o
    ̿Si4                     @   s  U ddl Z ddlZddlmZmZmZ ddlmZmZmZm	Z	m
Z
 ddlmZ ddlmZ ddlmZmZmZ e eZedd	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eeddZeeddZ eddZ!edd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'ed%d&pd&( ) Z*d'd(d'd)d)d*d+Z+ee,ef e-d,< d-ed.efd/d0Z.d-ed.ee, fd1d2Z/e/e!Z0e/e"Z1dd3d4e,d5e	e, d.e2fd6d7Z3d8ee d9ed.e	e fd:d;Z4d<eee,ef  d.ee fd=d>Z5d<eee,ef  d9ed.e	e fd?d@Z6dAedBed.e	e fdCdDZ7dEedFed.e	e fdGdHZ8dIed.ee,ef fdJdKZ9dLed.e	e fdMdNZ:dOdPdQeee,ef  dRe,dSed.eee,ef  fdTdUZ;d<eee,ef  dVedWe,dXe	e dYe	e dZe	e d.ee, fd[d\Z<d<eee,ef  d]ee,ef d.e	ee,ef  fd^d_Z=d<eee,ef  d]ee,ef d.e	ee,ef  fd`daZ>d4e,dWe,dbee,ef dceee,ef  d.ee,ef f
dddeZ?dIedfe,d.ee,ef fdgdhZ@dS )iT    N)datedatetime	timedelta)AnyDictListOptionalTuple)get_stock_history)ZerodhaClient)gpt_evaluate_candidatesgpt_limit_per_sidegpt_meta EARLY_MOVERS_SNAPSHOT_COLLECTIONearly_movers_snapshotsEARLY_MOVERS_MIN_DAILY_CANDLES240EARLY_MOVERS_MIN_PRICE30EARLY_MOVERS_MAX_PRICE3000EARLY_MOVERS_MIN_AVG_VOL_20D50000EARLY_MOVERS_MIN_AVG_DV_20D10000000EARLY_MOVERS_MIN_VOL_DAYS_20D20EARLY_MOVERS_MIN_ATR_PCTz0.15EARLY_MOVERS_EXCLUDE_KEYWORDSz LIQUID,ETF,BEES,GOLD,SILVER,CASHEARLY_MOVERS_EXCLUDE_SUFFIXESzADD,BETF,IETFEARLY_MOVERS_GPT_MIN_CONFIDENCE0EARLY_MOVERS_TOP_NEARLY_MOVERS_MIN_SCORE70%EARLY_MOVERS_CANDIDATE_LIMIT_PER_SIDE50EARLY_MOVERS_GPT_BLEND_WEIGHTz0.30EARLY_MOVERS_INDEX_SYMBOLNIFTY 50   
         )GAP_DAYHIGH_VOLATILITYVOLUME_SPIKEEXTENDED_BREAKOUTEXTENDED_BREAKDOWNBAD_ATRRISK_PENALTY_POINTSvreturnc                 C   s.   z| d u rW dS t | W S  ty   Y dS w )N        )float	Exception)r5    r:   :/var/www/html/Trade-python/app/v1/services/early_movers.py_safe_floatC   s   
r<   c                 C   sv   | d u rg S t | ttfrdd | D }nt| d}g }|D ]}t|p&d  }|r3|| q tt	|S )Nc                 S   s   g | ]}t |qS r:   )str).0xr:   r:   r;   
<listcomp>P       z$_parse_csv_words.<locals>.<listcomp>, )

isinstancelisttupler=   splitstripupperappenddictfromkeys)r5   partsoutpsr:   r:   r;   _parse_csv_wordsL   s   
rQ   )namesymbolrR   c                 C   sx   | pd   }|pd   }|sdS tD ]}|r"||r" dS q| d|   }tD ]}|r9||v r9 dS q.dS )NrC   T F)rH   rI   _EXCLUDE_SUFFIXESendswith_EXCLUDE_KEYWORDS)rS   rR   symnmsufhaykr:   r:   r;   _is_excluded_instrument_   s   r]   valuesperiodc                 C   s:   |dkrd S t | |k rd S | | d  }t|t| S Nr   )lensumr8   )r^   r_   wr:   r:   r;   _smar   s   rd   candlesc                 C   s   g }d }| D ]9}t |d}t |d}t |d}|d u r&|| }nt|| t|| t|| }|t| |}q|S )Nhighlowclose)r<   getmaxabsrJ   r8   )re   rN   
prev_closechlcltrr:   r:   r;   _true_ranges{   s   
 rr   c                 C   s`   |dkrd S t | |d k rd S t| |d  d  }|| d  }|s&d S t|tt | S )Nr      )ra   rr   rb   r8   )re   r_   trsr:   r:   r;   _atr   s   ru   abc                 C   s   |dkrd S | | | d S )Nr         Y@r:   )rv   rw   r:   r:   r;   _pct   s   ry   latestpastc                 C   s   |dkrd S | | d d S )Nr         ?rx   r:   )rz   r{   r:   r:   r;   _return_pct   s   r}   zerodha_clientc                 C   s(  zt  }td | d ddditddddd	}|s5| d dddid
g diddddd	}|sL| d dddidddddddd	}t|trV|dnd}|du rstdt	t  | 
 d  dtddW S t  }|jdddddtdd d}|d}tdt||| |jt	|d||d}|stdt	t  | 
 d  d|dptddW S g }	|D ]}
t|
tsq|	t|
d qdd |	D }	t|	d k r	 t|	d!}t|	d"}t|	d#krt|	dd$ d!nd}t|	d krt|	dd$ d"nd}d%}|rK|rK|rK|rK|| }|| }||kr:|dkr:|dkr:d&}n||krK|dk rK|dk rKd'}d}t|	d(kr]t|	d) |	d* }||dpet|d}td+|d,|d-t	t  | 
 d  |W S  ty   dtdd Y S w ).zCompute a simple market regime tag from NIFTY.

        Keep it lightweight and robust: if index not available, return UNKNOWN.
        z"[EarlyMovers] market_context startstocksNSE$neN)exchangeinstrument_tokenrS   r   rs   )_idr   rS   $in)r)   NIFTY50NIFTYz^NIFTYi)z$regexz$optionsr   z@[EarlyMovers] market_context done | tag=UNKNOWN (no token) ms=%d  UNKNOWN)tagindexnifty_5d_return)hourminutesecondmicrosecond(   daysz%Y-%m-%dzA[EarlyMovers] market_context fetch start | token=%s from=%s to=%sday)interval	from_dateto_datez?[EarlyMovers] market_context done | tag=UNKNOWN (no data) ms=%drS   rh   c                 S      g | ]}|d kr|qS r   r:   r>   rm   r:   r:   r;   r@          z#_market_context.<locals>.<listcomp>7      2      SIDEWAYSBULLISHBEARISHr*   z<[EarlyMovers] market_context done | tag=%s nifty_5d=%s ms=%dr   r   )r   utcnowloggerinfofind_oneINDEX_SYMBOLrD   rK   ri   inttotal_secondsreplacer   strftimer=   get_historical_data_recordsrJ   r<   ra   rd   r}   r9   )dbr~   t0idxtokennowfrmtorawclosesr	sma20_now	sma50_now
sma20_prev
sma50_prevr   slope20slope50nifty_5drN   r:   r:   r;   _market_context   s   

	

	
	 "
 


"$r   dc                 C   sr   t | trt | ts| S t | tr|  S t | tr7|  r7zt|  dd W S  ty6   Y d S w d S )NZz+00:00)rD   r   r   r=   rH   fromisoformatr   r9   )r   r:   r:   r;   
_parse_day  s   
r      )limitdailiesmoder   c             
   C   s  | sg S i }g }| D ]I}t |d}|sq
|dkr+| \}}}	|dd|d}
n|dkr;|jdd|jd}
ng   S |
|vrLg ||
< ||
 ||
 | q
g }|D ]\}
||
p`g }|sdqXt|dd	 d
}t|d d}tdd |D }t	dd |D }t|d d}t
dd |D }t |d d}||r| n|
|||||d qX|rt |d d}dtdtfdd}d}t|tr|dkr| dk}n
|dkr|||k}|r|dd }|dkr|| d }|S )zAggregate daily candles into weekly/monthly OHLCV.

        mode: 'week' or 'month'
        Output candle uses: date (period end), open/high/low/close/volume
        r   week04dz-W02dmonth-c                 S   s   t | dp	tjS )Nr   )r   ri   r   minr?   r:   r:   r;   <lambda>:  rA   z"_aggregate_ohlcv.<locals>.<lambda>)keyr   openc                 s       | ]
}t |d V  qdS rf   Nr<   ri   r>   r   r:   r:   r;   	<genexpr><      z#_aggregate_ohlcv.<locals>.<genexpr>c                 s   r   rg   Nr   r   r:   r:   r;   r   =  r   r   rh   c                 s   r   )volumeNr   r   r:   r:   r;   r   ?  r   r   r   rf   rg   rh   r   r   r6   c                 S   sj   | j dkrt| jd dd}n
t| j| j d d}|tdd }| dkr3|tdd8 }| dks&|S )Nr   rs   r      )r   r   yearr   weekday)r   
next_monthlastr:   r:   r;   _last_business_day_of_monthG  s   
z5_aggregate_ohlcv.<locals>._last_business_day_of_monthF   N)r   ri   isocalendarr   r   rJ   sortedr<   rj   r   rb   	isoformatr   rD   r   )r   r   r   bucketskeys_in_orderrm   dtyrc   _r\   rN   rowsrows_sortedorn   ro   rp   r5   end_dtlast_dtr   	drop_lastr:   r:   r;   _aggregate_ohlcv  sZ   
&
r   	key_levelbiasatr10	atr_ratio	vol_ratioc                C   sl  g }| s|S | d pi }t |d}t |d}	t| dkr$| d nd }
t |
p*i d}|dkrFt|	| | d }|dkrF|d	 |d urS|d
krS|d |d ur`|dkr`|d |dkr|dkrt|| | d }|dkr|dkr|d n	|dkr|d |dkr||d kr|d |dkr||d k r|d |d ur|dkr|d |S )Nr   rh   r      r   rx         ?r.   gffffff?r/   ?r0   r   NEAR_RESISTANCEr   NEAR_SUPPORT)\(?r1   Gz?r2   r3   )r<   ri   ra   rk   rJ   )re   r   r   r   r   r   flagstodayrh   open_prevrl   gapdistr:   r:   r;   _risk_flags_basicb  s<   






r  marketc           "      C   s  t | dk rd S | d }| dd }t |dk rd S t|d}t|d}t|d}t|d}t|d	}d
d | D }	t|	d}
|
rO|
dkrQd S ||
k rWd S tdd |D }|dkrfd S ||d krnd S t| d}|ry|dkr{d S |dkrtdkr|| d }|ttk rd S td|| }|d| krd S t|| }td|t|| }|| | d }|dkr|dkr||d krd S d}d}||krd}n!d|  krdkrn nd}nd|  k rdkrn d S d}nd S ||7 }|| }d}|dk rd}n|dk rd}n
|dkrd}nd}||7 }dd | dd D }|r2t	|t | nd}d}|dkr?|| nd }|d u rId}n2|dk rQd}n*|d krYd}n"|dkry|dkrmt|| | d nd}|d!k rvd"nd}nd}||7 }d}d }t | d#krt
t| d dt| d$ d}t|tr|d%nd }d }t|ttfrt|ttfr|| }|dk r|dkrd}n
|dkrd}nd}||7 }t|	d}|r|dkrd S t|| | d } d}!| dk rd}!n| d&k rd}!n| dkrd S ||!7 }tdtd'tt|}d(||t|d)t|d*t|d+t|ttfr9t|d+nd t|ttfrGt|d+nd t| d)t|trY|d,d-
S d d-
S ).N<   r   r   rh   r   rf   rg   r   c                 S      g | ]	}t |d qS rh   r   r   r:   r:   r;   r@         z"_score_bullish.<locals>.<listcomp>   r   c                 s   r   r   r   r   r:   r:   r;   r     r   z!_score_bullish.<locals>.<genexpr>gRQ?r+   rx   r7   r   r   r-            @ffffff?r   333333?r|   r   c                 S   ,   g | ]}t |d dkrt |d qS r   r   r   r   r:   r:   r;   r@        , 皙?333333?皙?r*   r   r          @d   r   r   r      r   
r   scorer   distance_to_level_pctr   r   r   relative_strength_5dsma20_dist_pctmarket_context)ra   r<   ri   rd   rj   ru   MIN_ATR_PCTr8   rk   rb   r}   rD   rK   r   r   round)"re   r  r  prev_windowrh   r  rf   rg   volr   sma200
resistancer   atr_pcttoday_rangebody
upper_wickdist_to_resr  prox_ptsr   vcp_ptsvolsavg20vol_ptsr   movers_ptsstock_5dr   rs_diffsma20sma_distsma_ptsr:   r:   r;   _score_bullish  s   








"& 





r:  c           "      C   s  t | dk rd S | d }| dd }t |dk rd S t|d}t|d}t|d}t|d}t|d	}td
d |D }	|	dkrLd S ||	d k rTd S t| d}
|
r_|
dkrad S |dkrwtdkrw|
| d }|ttk rwd S td|| }|d|
 krd S dd | D }t|d}|r|dkrd S ||krd S t	|| }tdt||| }||	 |	 d }|dkr|dkr||d krd S d}d}||	k rd}n!d|  krdkrn nd}nd|  k rdkrn d S d}nd S ||7 }||
 }d}|dk rd}n|dk rd}n
|dkrd}nd}||7 }dd | dd D }|r2t
|t | nd}d}|dkr?|| nd }|d u rId}n2|dk rQd}n*|dkrYd}n"|dkry|dkrmt	|| | d nd}|d k rvd!nd}nd}||7 }d}d }t | d"krtt| d dt| d# d}t|tr|d$nd }d }t|ttfrt|ttfr|| }|dkr|dk rd}n
|dk rd}nd}||7 }t	|| | d }d} |dk rd} n|d%k rd} n|dkrd S || 7 }t|d&}!|!r|!dkr||!k r|d7 }tdtd'tt|}d(||	t|d)t|
d*t|d+t|ttfr@t|d+nd t|ttfrNt|d+nd t|d)t|tr`|d,d-
S d d-
S ).Nr	  r   r
  r   rh   r   rf   rg   r   c                 s   r   r   r   r   r:   r:   r;   r   1  r   z!_score_bearish.<locals>.<genexpr>r   g\(\?r+   rx   r7   r   c                 S   r  r  r   r   r:   r:   r;   r@   H  r  z"_score_bearish.<locals>.<listcomp>r   r-   r  r  r  r   r  r|   r   c                 S   r  r  r   r   r:   r:   r;   r@   s  r  r  r  r  r  r*   r   r   r  r  r  r   r   r   r  r   r  )ra   r<   ri   r   ru   r#  r8   rj   rd   rk   rb   r}   rD   rK   r   r$  )"re   r  r  r%  rh   r  rf   rg   r&  supportr   r)  r*  r   r7  r+  
lower_wickdist_to_supr  r.  r   r/  r0  r1  r2  r   r3  r4  r5  r   r6  r8  r9  r'  r:   r:   r;   _score_bearish"  s   








"& 





r>  algodailyc           6         s  d}dt ttf dt ttf fdd dd |pg D }t|tr'|r'|d ni p*i }t|d	}t|d
}t|d}	t|d}
t|d}t|d}t|d}dtt	 dtt	 dtt	 fdd}|d}|||}|d}d}t|t
t	fr|dkrtt	|t	| d d}|d}|d}d}t|t
t	frt	|dk rd}nt	|dkrd }nd!}d"d t|dkr|d#d n|D }t|d$krt|d%d d& nd}t|d'krt|dd% t	t|dd%  nd}d}t|t
t	fr't|t
t	fr'|dkr't	|t	| }|d(k rd)}n
|d*kr%d+}nd,}d}t|t
t	fr<t	|d-kr:d.nd/}t|| }td0|	t|| }td0t|||
 }d}|dkr|pad1  d2krp||d3 k}n|ptd1  d4kr||d3 k}|d5} d}!t| t
t	frt	| d6k}!d}"|r|r|dkr||  kr|krn nd7}"n||  k r|k rn nd8}"nd9}"t|d:d;d<}#t|d=d;d<}$d>d |#pg D }%d?d |$pg D }&d@tt	 dtfdAdB}'|'|%}(|'|&})|)}*t|$dCkrXt|$d trXt|$d pi d	}+dDd |$dd D },tdEdF |,D d0dG}-tdHdF |,D d0dG}.|-dkrJ|+|-dI krJdJ}*n|.dkrX|+|.dK krXdL}*g }/t|t
t	frnt	|dMk rn|/dN |dOu rx|/dP g }0|!dOu r|0dQ t|t
t	fr|dkr|dkr|pd1  d2kr|t	|k r|0dR |pd1  d4kr|t	|kr|0dS | |t
|dTpd|dUpd|dVdW|"|(|*dXt	|dYpd0| |!||dZ||d[|||d\|/|0t|d]pg d^}1|rSt|d_kr|d`d n|}2 fdad|#p$g D }3 fdbd|$p0g D }4 fdcd|2p<g D }5|5|1dd< |3ded |1df< |4ded |1dg< |1S )ha2  Create compact payload for GPT.

        IMPORTANT: This payload is intentionally FEATURES-ONLY (no raw OHLC arrays)
        to reduce tokens and prevent the model from "reading charts".

        If you ever want to re-introduce OHLC summaries, see the optional block
        near the return dict.
        Frm   r6   c                 S   s   t | tsi S | d}t |tr|  }n|d ur$t|d d nd}|tt| ddtt| ddtt| ddtt| ddt	tt| d	d
}|S )Nr   r+   rC   r   r   rf   rg   rh   r   r   )
rD   rK   ri   r   r   r   r=   r$  r<   r   )rm   r   dsrN   r:   r:   r;   _compact_candle  s   


z3_compact_candidate_payload.<locals>._compact_candlec                 S   r  r  r   r   r:   r:   r;   r@     r  z._compact_candidate_payload.<locals>.<listcomp>r   rh   r   rf   rg   r   r   r  r?   r   c                 S   sN   t | ttfrt |ttfr|dkrd S ttt| t| t| d dS )Nr   rx   r   )rD   r   r8   r$  rk   )r?   r   r:   r:   r;   	_dist_pct  s   $&z-_compact_candidate_payload.<locals>._dist_pctr!  r   Nr   rx   r   r   r   r   r  CONTRACTINGg?	EXPANDINGNEUTRALc                 S      g | ]}t |pi d qS r   r   r   r:   r:   r;   r@         r   r   g      @r-   r  	DRYING_UPgffffff?RISINGFLATr  	CONFIRMEDNOT_CONFIRMEDr7   rC   r   g      ?r   r  r   UPDOWNBASEr   r	  )r   r   r   c                 S   $   g | ]}t |trt|d qS r  rD   rK   r<   ri   r   r:   r:   r;   r@   %     $ c                 S   rS  r  rT  r   r:   r:   r;   r@   &  rU  r^   c                 S   sx   t | dk rdS t| d}t| d}| d }|r:|r:|dkr:||  kr)|kr*dS  ||  k r5|k r8dS  dS dS dS )	Nr+   r   r   r   r   rP  rQ  rR  )ra   rd   )r^   s10s20r   r:   r:   r;   _tf_dir(  s   

z+_compact_candidate_payload.<locals>._tf_dir   c                 S   s   g | ]	}t |tr|qS r:   rD   rK   r   r:   r:   r;   r@   <  r  c                 s   r   r   r   r   r:   r:   r;   r   =  r   z-_compact_candidate_payload.<locals>.<genexpr>)defaultc                 s   r   r   r   r   r:   r:   r;   r   >  r   r   
RESISTANCEr   SUPPORTg      ?TIGHT_RANGETNO_REJECTION_WICKSNEAR_KEY_LEVELBELOW_SMA200ABOVE_SMA200r  r"  r   )regimeindex_5d)r@  weeklymonthlyr   )r   r  near_key_levelr!  sma50_dist_pct)r)  state)	trend_20dbreakout_volumer   
risk_flags)rS   r   
algo_scorer  mtflocation
volatilityr   	structurer  rl  r+   r  c                       g | ]}t |tr |qS r:   rZ  r   rB  r:   r;   r@   n  rI  c                    rr  r:   rZ  r   rs  r:   r;   r@   o  rI  c                    rr  r:   rZ  r   rs  r:   r;   r@   p  rI  daily_last10iweekly_last12monthly_last12)r   r=   r   rD   rE   r<   ri   rd   r   r8   r   r$  ra   rb   rk   rj   r   rH   rI   r   r   rK   rJ   )6rS   r   r?  r@  INCLUDE_OHLC_IN_GPT_PAYLOADr   r  rh   r  rf   rg   r7  sma50r'  rC  
sma20_dist
sma50_distr   r)  r   r   	vol_statevols20v5v15	vol_trendr   rk  r+  r,  r<  no_rejection_wicksdist_to_levelrg  	daily_dirr   r   week_closesmonth_closesrX  
weekly_dirmonthly_ctxmonthly_taglast_m_closer  	prev_highprev_lowrq  r  rN   
last_dailyweek_compactmonth_compactdaily_compactr:   rs  r;   _compact_candidate_payload  s   
"


"




&$4*








$&
&


r  ist_datec           J      C   s  t  }td| t| |}t| d ddidddiddiddd	d
d
d
d}tdt| g }i }i }i }	|D ]9}
t|
	dpEd
 }|
	dpOd
  }|rX|sYq<|| |||< |||< |
	dpld
 }|ru||	|< q<i }|rt| d dd|iid	d
d
d}|D ]}t|	dpd
 }|	d}|rt|tsq|||< qtdt|t| g }g }d	}d	}d	}d	}d	}d	}d	}d	}t  }| D ]\}}|d
7 }t||		|dr|d
7 }q|	|}|rt|tk r|d
7 }qt|d pi 	d}|tk s|tkr|d
7 }q|dd }dd |D } dd | D }!t|!}"|!r;t|!t|! nd}#td	krL|"tk rL|d
7 }q|#tk rV|d
7 }qtd	krg }$|D ](}%t|%pfi 	d}&t|%ppi 	d}'|&d	kr|'d	kr|$|&|'  q_|$rt|$t|$ nd}(|(tk r|d
7 }q|d
7 }t||})|)rt|)	dpd	tkrt|t|)	d pdd!|)	d"durt|)	d"nd|)	d#durt|)	d#nd|)	d$durt|)	d$ndd%}*|d|i|)d&|*i t||}+|+ret|+	dpd	tkret|t|+	d p#dd'|+	d"dur4t|+	d"nd|+	d#durDt|+	d#nd|+	d$durTt|+	d$ndd%}*|d|i|+d&|*i |d( d	krtt  |  d) },td*||t|t||, q|jd+d, d-d. |jd/d, d-d. td0||||||||	 |dt d	t! }-|dt d	t! }.td1t|-t|.tt" t#rt" 	d2ndtt$  t$ }/g }0|/d	krS|-d|/ D ]3}1|1	d}|sq|	|}|r|	|pdnd}t|tr|r|0t%|d!|1|d3 q|.d|/ D ]3}1|1	d}|s+q|	|}|r;|	|p9dnd}t|trQ|rQ|0t%|d'|1|d3 qtd4t|0 t  }2t&||0d5\}3}4td6t|3t|4trwt|4nd	tt  |2  d)  i }5|3D ]}6|6	d|6	d7f}7|7d	 r|7d
 r|6|5|7< qd	}8|0D ]I}9t|9t#sq|9	dpd
  }|9	d7pd
  })|r|)d8vrՐq||)f}7|7|5v rq||)ddd9d:gd;d<|5|7< |8d
7 }8q|8rt|4tr|4d=|8  d>td?t't d@tfdAdB}:d>td?t't d@tfdCdD};dEt(t d@tfdFdG}<g }=|-D ]}1|1	d}t|1	dp<d	}>|5	|d!f}6t|6t#rZ|6	dHdurZt|6	dHnd}?t|6t#r|tt)pgd	d	kr||?du szt|?tt)k r|q-t|1	d&pg }@t|6t#r|6	d&pg D ]}A|A|@vr|@|A q|<|@}B|:|>|?}C|;|>|?}D|=i |1|>t|6t#r|6ndt d	t|Ct|B t*t dt|Dt|B dI|@t|BdJ q-g }E|.D ]}1|1	d}t|1	dpd	}>|5	|d'f}6t|6t#r|6	dHdurt|6	dHnd}?t|6t#r4tt)pd	d	kr4|?du s2t|?tt)k r4qt|1	d&p<g }@t|6t#rZ|6	d&pKg D ]}A|A|@vrX|@|A qL|<|@}B|:|>|?}C|;|>|?}D|Ei |1|>t|6t#rv|6ndt d	t|Ct|B t*t dt|Dt|B dI|@t|BdJ q|=jdKd, d-d. |EjdLd, d-d. dMt+tt,f d@tfdNdO}F|=D ]D}1|1-dP|F|1 |1-dQt|1	dRpd	 |1-dS|1	dTdurt|1	dTn	t|1	dQpd	 |1-dUt|1	d>pd	 q|ED ]D}1|1-dP|F|1 |1-dQt|1	dRpd	 |1-dS|1	dTdur+t|1	dTn	t|1	dQp3d	 |1-dUt|1	d>pAd	 q|=dt d
t. }G|Edt d
t. }H|t  |||ttttttt/t0t1t)dVt!t|-t|.dWi t" t|0t|3|4dX|G|HdYdZ}I| t2 j3d[|i|Id\t  id]d-d^ td_|t|Gt|Htt  |  d)  d-||t|-t|.t|I	d`i 	dapg t|I	d`i 	dbpg t|0t|3t|4dc
S )dzECompute and persist the early movers snapshot for the given IST date.z&[EarlyMovers] snapshot start | date=%sr   r   Fr   NEQ)	is_activer   r   stock_idinstrument_typer   rs   )r   rS   r  rR   z)[EarlyMovers] universe loaded | stocks=%dr  rC   rS   rR   stock_historyr   )r   r  r   r   z3[EarlyMovers] stock_history loaded | ids=%d docs=%d)rS   rR   r   rh   rJ  c                 S   rG  rH  r   r   r:   r:   r;   r@     rI  z/build_early_movers_snapshot.<locals>.<listcomp>c                 S   r   r   r:   )r>   r5   r:   r:   r;   r@     r   r7   r   r  r   r   r   r   r   )r   r   r   r   r   rl  r   i  r   zP[EarlyMovers] scan progress | considered=%d passed_hard=%d bull=%d bear=%d ms=%dc                 S       t | dpd| dpdfS Nr  r   rS   rC   r   ri   r   r:   r:   r;   r          z-build_early_movers_snapshot.<locals>.<lambda>T)r   reversec                 S   r  r  r  r   r:   r:   r;   r   	  r  zu[EarlyMovers] hard filters summary | considered=%d passed=%d excluded=%d candles=%d price=%d vol=%d vol_days=%d dv=%dzL[EarlyMovers] candidates | bull=%d bear=%d gpt_mode=%s gpt_limit_per_side=%dr   )rS   r   r?  r@  z)[EarlyMovers] gpt call start | payload=%d)r  
candidatesz8[EarlyMovers] gpt call done | results=%d errors=%d ms=%dr   )r   r   BADNO_GPT_OUTPUTzJGPT did not return an analysis item for this symbol in the batch response.)rS   r   
confidencebias_oksetup_qualityrl  noteszmissing_items_filled=rm  gpt_confr6   c                 S   sD   |d u rt | S tdtdtt}t t| d|  t||  S Nr7   r|   )r   rj   r   r8   GPT_BLEND_WEIGHTr$  rm  r  rc   r:   r:   r;   
_blend_intd  s    z/build_early_movers_snapshot.<locals>._blend_intc                 S   s@   |d u rt | S tdtdt t}t | d|  t ||  S r  )r8   rj   r   r  r  r:   r:   r;   _blend_floatj  s   z1build_early_movers_snapshot.<locals>._blend_floatr  c                 S   s@   d}| pg D ]}|t tt|  d7 }qtdt |S r`   )r   r4   ri   r=   rH   rI   rj   )r  ptsfr:   r:   r;   _risk_penaltyp  s   "z2build_early_movers_snapshot.<locals>._risk_penaltyr  r   )rm  gptfinal_scorefinal_score_floatrl  risk_penaltyc                 S   r  Nr  r   rS   rC   r  r   r:   r:   r;   r     r  c                 S   r  r  r  r   r:   r:   r;   r     r  itc                 S   s2   |  dpd  }|dkrdS |dkrdS dS )Nr   rC   r   BREAKOUTr   	BREAKDOWNr   )ri   rH   rI   )r  rw   r:   r:   r;   _strategy_for  s   z2build_early_movers_snapshot.<locals>._strategy_forstrategyoverall_scorer  overall_score_floatr  early_mover_score)
consideredpassed_hard_filtersmin_daily_candles	min_price	max_pricemin_avg_vol_20dmin_avg_dv_20dmin_vol_days_20dmin_atr_pctexcluded_keywordsexcluded_suffixesgpt_min_confidence)candidate_limit_per_sidebullish_candidatesbearish_candidates)payload_countresult_counterrors)bullishbearish)r   generated_atr  universer?  r  topr   
created_at)z$setz$setOnInsert)upsertzJ[EarlyMovers] snapshot saved | date=%s top_bull=%d top_bear=%d total_ms=%dr  r  r  )
okr   r  r  r  bullish_topbearish_topgpt_payloadgpt_results
gpt_errors)4r   r   r   r   r   rE   findra   r=   ri   rH   rI   rJ   rD   itemsr]   MIN_DAILY_CANDLESr<   	MIN_PRICE	MAX_PRICErb   MIN_VOL_DAYS_20DMIN_AVG_VOL_20DMIN_AVG_DV_20Dr:  r   	MIN_SCOREr  r8   r>  r   sortrj   CANDIDATE_LIMIT_PER_SIDEr   rK   r   r  r   r   r   GPT_MIN_CONFIDENCEr$  r   r   
setdefaultTOP_Nr#  rW   rU   r   
update_one)Jr   r~   r  t_allr  r   	stock_idssymbols_by_idids_by_symbolnames_by_symbolrP   sidrX   rY   history_by_iddocsr   r   bullish_algobearish_algor  r  filtered_by_candlesfiltered_by_pricefiltered_by_volfiltered_by_vol_daysfiltered_by_dvfiltered_by_excludet_loopre   rh   last20r0  pos_volsvol_daysr1  dvsrm   rp   r5   avg_dv20rw   r  s2msbull_candidatesbear_candidates	gpt_limitr  r  t_gpt	gpt_itemsr  gpt_by_symbolgir\   missingrO   r  r  r  bullish_finalrm  confriskrfpenaltyblended_intblended_floatbearish_finalr  r  r  snapr:   r:   r;   build_early_movers_snapshotx  s  

$






    
    		






	
. 




. 



	: : #	r  )Aloggingosr   r   r   typingr   r   r   r   r	   app.v1.services.stock_historyr
   app.v1.services.zerodha.clientr    app.v1.services.early_movers_gptr   r   r   	getLogger__name__r   getenvr   r   r  r8   r  r  r  r  r  r#  EXCLUDE_KEYWORDS_CSVEXCLUDE_SUFFIXES_CSVr  r  r  r  r  rH   rI   r   r4   r=   __annotations__r<   rQ   rW   rU   boolr]   rd   rr   ru   ry   r}   r   r   r   r  r:  r>  r  r  r:   r:   r:   r;   <module>   sv    

	 "	&c8BM6* 6 : "B