o
    ̿Si5                  
   @   s  d dl Z d dlZd dlZd dlmZ d dlmZmZmZmZm	Z	 d dl
mZ eeZeddp2d  Zdefdd	Ze Zeed
dZeeddZeeddZeeddZeeddZdededeeef fddZdedee fddZdedeeeef  fddZdeeef deeeef  defd d!Z deeef deeeef  de	eeeef  ee f fd"d#Z!defd$d%Z"deeef fd&d'Z#dS )(    N)datetime)AnyDictListOptionalTuple)get_openai_clientEARLY_MOVERS_GPT_MODEoffreturnc                  C   s.   dD ]} t | dpd }|r|  S qdS )N)EARLY_MOVERS_GPT_MODEL
CHAT_MODELOPENAI_CHAT_MODELOPENAI_MODEL )osgetenvstrip)kv r   >/var/www/html/Trade-python/app/v1/services/early_movers_gpt.py_pick_model   s   r   EARLY_MOVERS_GPT_TEMPERATUREz0.0EARLY_MOVERS_GPT_MAX_TOKENS1200EARLY_MOVERS_GPT_TIMEOUT45EARLY_MOVERS_GPT_MAX_CALLS10EARLY_MOVERS_GPT_LIMIT_PER_SIDE25model
max_tokensc                 C   s2   | pd   }|drdt|iS dt|iS )Nr   zgpt-5max_completion_tokensr#   )r   lower
startswithint)r"   r#   mr   r   r   _max_tokens_kwarg"   s   
r)   textc                 C   sL  | sd S |   }|dr/|d}|dkr/||d d  }|d}|dkr/|d |   }|d}|d}|dkrk|dksE||k rk|d}|dkrk||krk|||d  }zt|W S  tyj   Y d S w |dkr|d}|dkr||kr|||d  }zt|W S  ty   Y d S w zt|W S  ty   Y d S w )	Nz```
   {[]})r   r&   findrfindjsonloads	Exception)r*   snls2end	start_obj	start_arrchunkr   r   r   _extract_json)   sF   






r>   itemc              	   C   s  t | tsd S | dpd  }| dpd  }|r#|dvr%d S | d}zt|}W n ty;   d }Y nw |d u rBd S tdtd|}| d}d	}t |t	r[t	|}n|d urtt
|  }|d
v rnd}n|dv rtd	}| d}|d urt
|  nd}	|	dvr|dkrd}	n	|dkrd}	nd}	g }
| d}t |tr|D ]}t |t
r| r|
|   q| d}|d urt
|nd}||t|t	||	|
|d d dS )Nsymbolr   bias)BULLISHBEARISH
confidenceg        g      Y@bias_okT)false0noF)true1yessetup_qualityBADOKAYGOODK   rP   7   rO   rN   
risk_flagsnotesi  r@   rA   rD   rE   rL   rS   rT   )
isinstancedictgetr   upperfloatr6   maxminboolstrr%   listappend)r?   r@   rA   confconf_fbias_ok_rawrE   r7   setup_quality_rawrL   rS   rfxrT   notes_sr   r   r   _validate_itemR   sd   









rh   market
candidatesc           	      C   s
  ddg dddidddgdd	d
ddddidg dddddidddiddd}ddgd|id}g }|p:g D ])}t |trd|dpHd  }|dpSd  }|rd|rd|||d q;| |tt|plg |ddddddddd|d}tj	|d d!S )"zoSingle-call GPT prompt for multiple candidates.

    Candidates must already be compact and features-only.
    arrayobjectrU   typestringrB   rC   )rm   enumnumberr   d   )rm   minimummaximumbooleanrM   )rm   items)rm   required
propertiesru   r@   r   rA   )r@   rA   )countkeyszGEvaluate each candidate for early breakout/breakdown watchlist quality.zReturn ONLY ONE valid JSON OBJECT with a single key 'items' (no markdown). items must be a JSON array matching the schema contract. You MUST return exactly one item per input candidate (expected.count). Do not omit any symbol. Do not add extra symbols.zN0-100, higher means higher probability of follow-through in next 1-5 sessions.zQtrue if the provided bias is supported by the provided features; otherwise false.zMBAD/OKAY/GOOD based on structure + context; do not use any outside knowledge.z Use short upper snake-case tags.zUDo not infer raw OHLC/candlestick details unless provided as explicit features/flags.)taskoutputrD   rE   rL   rS   strict)ri   rj   expectedinstructionsoutput_schemaF)ensure_ascii)
rV   rW   rX   r   rY   r`   r'   lenr4   dumps)	ri   rj   items_contractcontractcandidate_keyscr7   bpayloadr   r   r   _build_prompt   sT   
	
r   c              
      s   g }t dkr
g g fS ts|d g |fS zt  W n  ty< } z|dt|  g |fW  Y d}~S d}~ww dttttf  dtt	ttf  fdd}d	ttttf  dt	ttttf  tt f f fd
d}||}t
|}i d}t|pg g}	|	rS|tdttk rS|	d}
|
sq||
}|rtfdd|D rq|d7 }||
\}}|r|| |D ]$}|dpd  |dpd  f}|d r|d r||< qg }|
D ]-}t|tsq|dpd  |dpd  f}||v r|vr|| q|rGt|dkr,|D ]	}|	|g q!ntdt|d }|	|d|  |	||d  |	rS|tdttk sg }|D ]}|}|rf|| qW|rt|t|kr|dt| dt| d |d|  ||fS )zZEvaluate candidates via GPT in a single call.

    Returns: (validated_items, errors)
    r
   zGPT model not configuredzOpenAI unavailable: Nrowsr   c                 S   sd   g }| pg D ])}t |tsq|dpd  }|dpd  }|r/|r/|||f q|S )Nr@   r   rA   )rV   rW   rX   r   rY   r`   )r   ksr   r7   r   r   r   r   _candidate_key_list   s   
z4gpt_evaluate_candidates.<locals>._candidate_key_listbatchc              
      s  g }t | d}dddd|dg}d}z@z jjjd|ttddid	tt}W n tyG    jjjd|ttd
tt}Y nw |j	d j
jpPd }W n  tyt } z|dt|  g |fW  Y d }~S d }~ww t|}t|tr|d}t|tr|}t|trdD ]}	||	}t|tr|} nqt|tr|dr|dr|g}t|ts|d d dd}
|d |d|
  g |fS g }|D ]}t|}|r|| q|s|d ||fS )N)ri   rj   systemzYou are a disciplined swing trader and risk manager. You only use the provided structured data. Output ONLY valid JSON. No markdown. No commentary.)rolecontentuserr   rm   json_object)r"   messagestemperaturetimeoutresponse_format)r"   r   r   r   r   zOpenAI call failed: ru   )resultsr{   datar@   rA   i  r+    z"GPT response not a JSON items listz	raw_head=zGPT returned no valid itemsr   )r   chatcompletionscreater   r   r)   r   r6   choicesmessager   r   r`   r^   r>   rV   rW   rX   r_   replacerh   )r   local_errorspromptr   r   resperawr   r   headoutit)clientri   r"   r   r   
_call_once   s~   













z+gpt_evaluate_candidates.<locals>._call_oncer   r-   c                 3   s    | ]}| v V  qd S )Nr   ).0r   )results_by_keyr   r   	<genexpr>M  s    z*gpt_evaluate_candidates.<locals>.<genexpr>r@   r   rA         zGPT returned z of z itemsz
gpt_calls=)r	   r   r`   r   r6   r^   r   r   r   r   setr_   r[   r'   r   popallextendrX   r   rY   rV   rW   r   )ri   rj   errorsr   r   r   wanted_keys
wanted_setcallsqueuer   
batch_keysru   errsr   r   missingr   r(   midr   r   r   )r   ri   r"   r   r   gpt_evaluate_candidates   s   

*>K

,
,
(

 r   c                   C   s   t dttS )Nr   )r[   r'   r    r   r   r   r   gpt_limit_per_side{  s   r   c                   C   s   t ttttt t dS )N)moder"   r   r#   r   limit_per_sidegenerated_at)r	   r   r   r   r   r   r   utcnowr   r   r   r   gpt_meta  s   r   )$r4   loggingr   r   typingr   r   r   r   r   app.v1.services.gpt_enginer   	getLogger__name__loggerr   r   r%   r	   r^   r   r   rZ   r   r'   r   r   r   r    r)   r>   rh   r   r   r   r   r   r   r   r   <module>   s.    

)*?BD '