o
    ̿Sid                    @   s  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	 d dl
mZmZ e eZdZdZdZdZd	Zd d
ddededededef
ddZddddededededef
ddZdededefddZdefddZddee defddZdddee deeef fdd Zd!edefd"d#Zdeeef fd$d%Z dd&d'Z!dd(d)ed*ed+ee d,ed-ed.eeeef  ddfd/d0Z"dededefd1d2Z#defd3d4Z$defd5d6Z%defd7d8Z&defd9d:Z'defd;d<Z(defd=d>Z)d?ee defd@dAZ*dddee deeef fdBdCZ+dDedEe	edFf defdGdHZ,dIeeeef  dJedee fdKdLZ-dMeeef dIeeeef  dNede	eeeef f fdOdPZ.defdQdRZ/dSedee fdTdUZ0d)ed*ed+edVedWedXedYede	ee eeeef  f fdZd[Z1d)ed*ed-eddfd\d]Z2d)ed*ed!ed^ed_ed-eddfd`daZ3d)ed*edeeef fdbdcZ4ddedeedfedVedgeeef de	eeef fdhdiZ5ddedjedgeeef de	eef fdkdlZ6ddedgeeef de	eef fdmdnZ7defdodpZ8dqedredefdsdtZ9duedefdvdwZ:dxedefdydzZ;deeeef  fd{d|Z<d}ed~edee fddZ=dMeeef de	eeeef f fddZ>duedee fddZ?dedefddZ@duedee fddZAduedefddZBd}edee fddZCdMeeef defddZDdMeeef de	ee ee ee f fddZEdeeef dee fddZFdeeef deeef fddZGdIeeeef  dJede	eeeef  ee f fddZHdddZIdefddZJdeeef deeef deeef fddZK		dd+edeeef dee dMeeeef  def
ddZLd)ed*ededededededdfddZM					dd)ed*ee dee d+edMeeef dedee dee dedIeeeef  dNedee fddZNduedee fddZO			dd)ed*ee dee d+edMeeef dIeeeef  dedNedee dee dedeeef fddZPdddd)ed*ee d+edVedededfededee dee deeef fddZQdS )    N)datetime	timedeltatimezone)AnyDictOptionalTuple)confidence_at_leastnormalize_confidencepaper_tradespaper_accountspaper_balance_dailyzerodha_order_margin_cachepaper_trade_audit ʚ;	min_value	max_valuenamedefaultr   r   returnc                C   sn   t | }|d u st| dkrt|S z
tt| }W n ty,   t| Y S w tt|t||S N )osgetenvstrstripint	Exceptionmaxminr   r   r   r   rawv r$   ;/var/www/html/Trade-python/app/v1/services/paper_trading.py_env_int   s   
r&           g NgmCc                C   s   t | }|d u st| dkrt|S z
tt| }W n ty,   t| Y S w ||ks;|tdtdfv r?t|S tt|t||S )Nr   inf-inf)r   r   r   r   floatr   r   r    r!   r$   r$   r%   
_env_float   s   
r+   c                 C   sJ   t | }|d u rt|S t|  }|dv rdS |dv r!dS t|S )N>   1yonyestrueT>   0nnoofffalseF)r   r   boolr   r   lowerr   r   r"   sr$   r$   r%   	_env_bool*   s   
r:   c                   C   s    t dpt dpd pdS )N	MARKET_TZPAPER_TRADE_TZzAsia/Kolkata)r   r   r   r$   r$   r$   r%   _paper_tz_name6   s    r=   now_utcc                 C   s   ddl m} || S )zRReturn True only within PAPER_TRADE_START <= now < PAPER_TRADE_END (in MARKET_TZ).r   )is_paper_trade_creation_time)app.v1.utils.market_timer?   )r>   r?   r$   r$   r%    _is_paper_trade_creation_allowed;   s   rA   )r>   c                C   s  ddl m} t|pt }||sddddS t|  | t }ddddiiddidd	idd
igd}d}d}||ddddddddddddD ]:}|d7 }z$|dpWd	 	 p]d}	|dpdd	 	 }
t
|dppd}td|}t|d}t|d}|du r|}d}|dur|dur|
dkrt|t| }n|
dkrt|t| }t|t|pd }d|durt|nddt t|t|ddt d	}|d|did|i t| t|dpd	t|dpd	t|dt|dp|dpdp	dt|t dd z-t| t|dpd	t|dp&d	t|d p/d	d!dt|d|	d"d# W n
 tyI   Y nw zt| t|dpUd	t|dp^d	dd$ W n
 tyn   Y nw |d7 }W qH ty   td% Y qHw d&t
|t
|dS )'zForce-close OPEN intraday paper trades at PAPER_TRADE_END.

    Exit price: last candle close (trade.last_price_close; best-effort fallback to entry)
    Exit reason: INTRADAY_TIME_EXIT

    Returns:
      {"ran": bool, "scanned": int, "closed": int}
    r   )is_paper_trade_end_reachedFranscannedclosedOPENsettings.product$existsNr   MIS)status$or   )_iduser_id
account_idrK   symbol	directionentry_pricequantityreserved_amounttrade_valuelast_price_closerK   rR   rT   rS   rW   r'   LONGSHORTCLOSEDINTRADAY_TIME_EXIT	rK   
exit_priceexit_reason	closed_atrealized_pnl_per_unitrealized_pnlcurrent_unrealized_pnl_per_unitcurrent_unrealized_pnl
updated_atrN   $setrO   rP   rU   rV   rO   rP   trade_idrV   	pnl_total	closed_dtr^   rQ   TRADE_CLOSEDrg   status_fromrO   rP   rQ   eventreasondetailsrO   rP   ro   z/INTRADAY_TIME_EXIT close failed for paper tradeT)r@   rB   _to_utc_naive_now_utcensure_paper_game_indexesPAPER_TRADES_COLLECTIONfindgetr   upperr   r   _safe_floatr*   
update_one_apply_trade_close_to_accountr   _audit_eventr   _mark_intraday_equitylogger	exception)dbr>   rB   nowcolqrE   rF   trrl   rR   qtyentryexit_pxper_unitrealized_totalpatchr$   r$   r%   )close_paper_trades_for_intraday_time_exitB   s   



$
	
r   dt_utcc                 C   sf   z#ddl m} |t }|d}| jddj|d}||  W S  ty2   |    Y S w )z@Compute YYYY-MM-DD key in PAPER_TRADE_TZ (default Asia/Kolkata).r   ZoneInfoUTCNtzinfo)zoneinfor   r=   replace
astimezonedate	isoformatr   )r   r   tzutc	aware_utcr$   r$   r%   _paper_day_key   s   
r   c                   C   sx   t ddddt ddddt ddddtt d	d
dddt dddddt dddddtddtdp3d  p9ddS )NPAPER_ACCOUNT_STARTING_BALANCEg    .Ar'   )r   PAPER_ACCOUNT_MIN_TRADE_VALUE      Y@PAPER_ACCOUNT_MAX_TRADE_VALUEg     j@PAPER_ACCOUNT_MAX_QUANTITYg     @      ?g    eAr   PAPER_ACCOUNT_MAX_LOSS_PCTg      PAPER_ACCOUNT_MAX_PROFIT_PCTg     @@PAPER_ACCOUNT_EOD_EXITTPAPER_TRADE_PRODUCTrJ   )starting_balancemin_trade_valuemax_trade_valuemax_quantitymax_loss_pctmax_profit_pcteod_exitproduct)r+   r   r:   r   r   r   rx   r$   r$   r$   r%   _account_defaults   s   r   c                 C   s   z=| t  jddgddd | t jg dddd | t jg dd	dd | t jd
gddd | t jg ddd W d S  tyL   td Y d S w )NrO   rM   rP   rM   paper_accounts_user_accountT)r   unique)r   r   )dayrM   paper_balance_daily_user_day)r   r   )keyrM   zerodha_order_margin_cache_key)
expires_atrM   zerodha_order_margin_cache_ttlr   )r   expireAfterSeconds)r   r   )tspaper_trade_audit_user_tsr   z#Failed to ensure paper game indexes)PAPER_ACCOUNTS_COLLECTIONcreate_indexPAPER_BALANCE_DAILY_COLLECTION%ZERODHA_ORDER_MARGIN_CACHE_COLLECTIONPAPER_TRADE_AUDIT_COLLECTIONr   r~   r   )r   r$   r$   r%   rt      s8   rt   )rp   rO   rP   rQ   rn   ro   rp   c                C   s   zB|r|sW dS t |  t|t||rt|ndt|pdt|p"dt|tr*|ni t d}dd | D }| t | W dS  t	yL   Y dS w )z=Best-effort audit log to Mongo for skip/close/open decisions.Nr   )rO   rP   rQ   rn   ro   rp   r   c                 S      i | ]\}}|d ur||qS Nr$   .0kr#   r$   r$   r%   
<dictcomp>      z _audit_event.<locals>.<dictcomp>)
rt   r   _normalize_symbol
isinstancedictrs   itemsr   
insert_oner   )r   rO   rP   rQ   rn   ro   rp   docr$   r$   r%   r|      s"   


r|   c                 C   s6   t | }|d u rt|S t| }|r|S t|S r   )r   r   r   r   r8   r$   r$   r%   _env_str  s
   
r   c                   C   
   t ddS )NPAPER_MARGIN_REQUIREDTr:   r$   r$   r$   r%   _paper_margin_required&  s   
r   c                   C      t dddddS )NPAPER_LONG_PENDING_MAX_CANDLES   r     r   r&   r$   r$   r$   r%   _long_pending_max_candles+     r   c                   C   r   )NPAPER_LONG_OPEN_MAX_CANDLESr   r   r   r   r   r$   r$   r$   r%   _long_open_max_candles/  r   r   c                   C   r   )N"PAPER_EXIT_ON_OPPOSITE_VERY_STRONGTr   r$   r$   r$   r%   _allow_opposite_signal_exit3     
r   c                   C   r   )NPAPER_EOD_CUTOFF_HOUR   r      r   r   r$   r$   r$   r%   _paper_eod_cutoff_hour7  r   r   c                   C   r   )NPAPER_EOD_CUTOFF_MINUTE   r   ;   r   r   r$   r$   r$   r%   _paper_eod_cutoff_minute;  r   r   candle_ts_utcc                 C   s   | du rdS z4ddl m} |t }|d}| jddj|d}||}t }t }|j|kp9|j|ko9|j|kW S  t	yD   Y dS w )zReturn True if candle timestamp (UTC naive) is at/after EOD cutoff in PAPER_TRADE_TZ.

    Best-effort: if tz conversion fails, return False.
    NFr   r   r   r   )
r   r   r=   r   r   r   r   hourminuter   )r   r   r   r   r   lochmr$   r$   r%   _is_eod_cutoff_reached?  s   

 r   c                C   s$  t |pt }t|sddddS t|  | t }dddgiddd	diidd
iddiddigd}d}d}||ddddddddddddddddD ];}|d7 }z%|dpZd  p`d}|dpgd  }	t	|dpsd}
|
dkr{d}
t
|d}t
|d}|d
u r|}d}|d
ur|d
ur|	dkrt|t| }n|	dkrt|t| }t|t|
pd }d|d
urt|nd
dt t|t|ddt d	}|d|did|i t| t|dpdt|dpdt|dt
|dp	|d p	dpdt|t dd! z-t| t|dp!dt|dp*dt|d"p3dd#dt|d|d$d% W n
 tyM   Y nw zt| t|dpYdt|dpbddd& W n
 tyr   Y nw |d7 }W qK ty   td' Y qKw dt	|t	|dS )(a  Force-close any remaining intraday paper trades after EOD cutoff.

    This is a safety net to ensure intraday-only paper trades do not remain OPEN
    just because a candle update didn't arrive after the cutoff.

    Returns:
      {"ran": bool, "scanned": int, "closed": int}
    Fr   rC   $inrG   PENDINGTrH   rI   Nr   rJ   )rK   zsettings.eod_exitrL   rM   )rN   rO   rP   rK   rQ   rR   rS   rT   rU   rV   rW   	opened_at
created_atsnapshot_idsettingsrK   rR   rT   rS   rW   r'   rX   rY   rZ   EOD_EXITr\   rN   re   rO   rP   rU   rV   rf   rQ   rj   rk   rm   rq   z EOD close failed for paper trade)rr   rs   r   rt   ru   rv   rw   r   rx   r   ry   r*   rz   r{   r   r|   r   r}   r~   r   )r   r>   r   r   r   rE   rF   r   rK   rR   r   r   r   r   r   r   r$   r$   r%   close_paper_trades_for_eodU  s   



$
	
r   dpath.c                 C   s,   | }|D ]}t |ts d S ||}q|S r   )r   r   rw   )r   r   curr   r$   r$   r%   	_safe_get  s   
r   market_datapreferred_tfc                 C   s*   t | |d\}}|sd S tt|dS )Nr   close)_pick_latest_candlery   _normalize_candlerw   )r   r   
candle_raw_r$   r$   r%   $_extract_last_close_from_market_data  s   r  analysispreferred_timeframec                   s  t ||}d dD ]}t| |}t|tr!| r!|    nq	d}t trDt fdddD r7d}nt fdddD rDd	}d}| d
}t|trS|}n<t	| d}	|	du ret	t| d}	t	| d}
|
du rwt	| d}
|
du r}|}
|	dur|
durtt
|
t
|	k}d}t	| d}|du rt	t| d}|du rt	t| d}t	| d}|du r|}|dur|durtt
|t
|k}dd |||fD }d}|rtdd |D } ||||d}|sd|d< n|rdnd|d< t||fS )a  LONG-only gate: allow when trend-up OR above VWAP OR above HTF support.

    Deterministic, best-effort: only blocks when at least one gate is evaluable and all evaluable gates are False.
    If nothing is evaluable, allow and record that it was unknown.
    N))regimetrend)r  bias)marketr  )contextr  )r  )market_trend)index_trend)sector_trendc                 3       | ]}| v V  qd S r   r$   r   x
trend_hintr$   r%   	<genexpr>
      z&_long_regime_allows.<locals>.<genexpr>)UPBULLBULLISHPOSITIVERISINGTc                 3   r  r   r$   r  r  r$   r%   r    r  )DOWNBEARBEARISHNEGATIVEFALLINGF
above_vwapvwap)
indicatorsr   
last_pricer   htf_support)levelsr#  )r
  supportc                 S   s   g | ]}|d ur|qS r   r$   r  r$   r$   r%   
<listcomp>-  s    z'_long_regime_allows.<locals>.<listcomp>c                 s   s    | ]}t |V  qd S r   )r6   r  r$   r$   r%   r  0  r  )r  trend_upr  above_support
last_closeUNKNOWN_ALLOWregime_evalALLOWBLOCK)r  r   r   r   r   rx   anyrw   r6   ry   r*   )r  r   r  r)  pr#   r'  r  explicit_above_vwapr   pricer(  r%  price2	evaluableallowedrp   r$   r  r%   _long_regime_allows  sj   






r5  c                   C   r   )NPAPER_MARGIN_CACHE_SECONDSi     iQ r   r   r$   r$   r$   r%   _margin_cache_secondsA  r   r8  objc           	      C   s   | du rdS t | ttfrt| S t | tsdS dD ]}| |}t|}|dur3|dkr3t|  S q| d}t |ttfrDt|S t |trod}d}| D ]}t|}|du r\qQ|t|7 }d}qQ|ro|dkrot|S dS )zTBest-effort parse of Kite order_margins response to a single required margin number.N)requiredrequired_marginfinaltotal_marginr   totalr'   FT)r   r   r*   r   rw   ry   values)	r9  r   r#   fr>  r9   any_numvvffr$   r$   r%   _parse_zerodha_required_marginE  s6   



rD  rR   r   r1  r   c                C   s"  |du rdS |r|r|r|dks|dkrdS t |  |pd  dkr&dnd}	|p+d  p1d}| d	|	 d	| d	| d	tt|d
 	}
| t }t }|t|t||
d}|rt	|
dtr|
d|krt|
dt	|
dtr|
dfS dfS zd||	|dt|t|ddg}||}W n
 ty   Y dS w d}t	|tr|rt	|d tr|d nd}nt	|tr|}t|}|tt d }z4|jt|t||
ddt|t||
||	|t|t||durt|nd|||didd W ||fS  ty   Y ||fS w )zPReturn (required_margin, raw_response). Uses Mongo cache to respect rate limits.NNNr   r   rX   BUYSELLrJ   |r   )rO   rP   r   r   r;  r"   NSEMARKETregular)exchangetradingsymboltransaction_typer   
order_typerT   r1  variety)secondsre   )rO   rP   r   rQ   txr   r   r1  r;  r"   rd   r   Tupsert)rt   r   rx   roundr*   r   rs   find_oner   r   rw   r   ry   r   r   get_order_marginsr   listrD  r   r8  rz   )r   zerodha_clientrO   rP   rQ   rR   r   r1  r   rR  r   r   r   cachedordersrespraw0r:  expiresr$   r$   r%   !_get_required_margin_from_zerodhag  sx   *".
r_  c                C   s  |r|sdS t |  t }t|}| t }|t|t||dddi}t|tr/|dnd}t|t	r@|| 
 dk r@dS t| t|t|d}	t|	dpSd}
t|	d	p\d}d}| t t|t|d
dddiD ]}t|d}|dur|t|7 }qqt|
t| }tdt|
t| }|jt|t||dt|t||t|
dg |dt|
t|t|t|t|||dd|dt|pddiddd dS )z_Write mark-to-market info into today's `paper_balance_daily` without changing realized pnl_day.NrO   rP   r   last_mark_tsrM   <   rO   rP   balancer'   reserved_balancerG   )rO   rP   rK   rc   rO   rP   r   balance_openpnl_dayeventsr   )balance_closeequity_closeunrealized_closereserved_closeavailable_closera  rd   ri  MARK)r   typero   )$setOnInsertre   $pushTrS  )rt   rs   r   r   rV  r   r   r   rw   r   total_secondsget_or_create_paper_accountry   ru   rv   r*   r   rz   )r   rO   rP   ro   r   r   	daily_colr   	last_markaccrd  reservedunrealtuequity	availabler$   r$   r%   r}     s\    
	
r}   rd  deltac           
   
   C   s   t |}| t }t }	z=|jt|t||dt|t||t|| dg |	dt||	ddt|id|	t|t|p<ddid	d
d W d S  tyW   td Y d S w )Nr`  r'   rf  )rj  rd   rh  ri  r   )r   r~  ro   )rq  re   $incrr  TrS  zFailed to upsert daily balance)	r   r   rs   rz   r   r*   r   r~   r   )
r   rO   rP   r   rd  r~  ro   r   r   r   r$   r$   r%   _upsert_daily_balance  s.   
	
r  c           
      C   s:  t |  | t }t|t|d}||}|r|S t }t }t|d }t|t|| d| t|t|dt|t|d t|d t|d t|d t|d	 t|d
 t|	dpaddg ||d}	z|
|	 W n ty   ||}|r| Y S  w t| t|t||t|ddd ||p|	S )Nrc  r   :r'   r   r   r   r   r   r   r   rJ   )r   r   r   r   r   r   r   )rO   rP   paper_account_idr   rd  re  available_balancer   ri  r   rd   ACCOUNT_CREATEDrO   rP   r   rd  r~  ro   )rt   r   r   rV  rs   r   r*   r   r6   rw   r   r   r  )
r   rO   rP   r   r   rw  r   r   balr   r$   r$   r%   rt    sL   






	
"rt  r   sltargetr   c                 C   s  t |pi d}t |pi d}t|}t|}|pd  }	|durM|dkrM|	dkr<| d|d   }
t||
}n|	d	krM| d|d   }
t||
}|durx|dkrx|	dkrg| d|d   }t||}n|	d	krx| d|d   }t||}t|t|t| fS )
zCApply optional max loss/profit % caps (tighter SL / nearer target).r   r   r   Nr   rX   r   r   rY   )ry   rw   r*   r   rx   r   r    )r   r  r  rR   r   r   r   eff_sl
eff_targetr   override_sl
override_tr$   r$   r%   _apply_overrides?  s(   

r  r  c           	      C   s  t |pi d}t |pi d}t|pi dpd}|d u r#d}|d u s+|dkr/t|}|dkr5d}| dks=|dkr?dS tt|t|}|t|k rPdS t|t|  }tdt||}|dkrfdS t| t| }|t|k s||t|d kr~dS ||fS )	Nr   r   r   r   r'   r   r   r'   &.>)ry   rw   r   r*   r    r   )	r   r  r   min_tvmax_tvmax_qtyrV   r   tvr$   r$   r%   _compute_quantity[  s,   r  c                 C   s   t |pi d}t |pi d}t|pi dpd}|du r#d}|du s+|dkr-dS |dkr3d}| dkr9dS t|t|k rCdS tt|t|  }tdt||}|dkr[dS t| t| }|t|k sq|t|d	 krsdS ||fS )
zOSize by min/max trade value + max quantity (does not cap by available balance).r   r   r   r   Nr'   r  r   r  )ry   rw   r   r*   r   r    )r   r   r  r  r  r   r  r$   r$   r%   _compute_quantity_by_limitsy  s*   r  c                  C   s   t dpd  } t| S )NPAPER_TRADE_MIN_STRENGTHLOW)r   r   r   rx   r
   r#   r$   r$   r%   _min_strength_required  s   r  actualminimumc                 C   s
   t | |S r   )r	   )r  r  r$   r$   r%   _strength_at_least  r   r  r#   c                 C   s2   t | }g d}z||W S  ty   Y dS w )z.Map normalized confidence to an integer level.r  MEDIUMHIGHVERY_STRONGr   )r
   indexr   )r#   r9   orderr$   r$   r%   _strength_level  s   r  levelc                 C   s0   g d}t | } tdt| t|d } ||  S )Nr  r   rM   )r   r   r    len)r  r  r$   r$   r%   _strength_from_level  s   r  c                 C   sR   z| d  ddiddipi }|d}t|tr|W S dW S  ty(   Y dS w )z3Fetch latest market intelligence payload (DB-only).market_intelligence_summaryrp  latestrN   r   payloadN)rV  rw   r   r   r   )r   r   r  r$   r$   r%   _market_intelligence_latest  s   
r  decisionmarket_biasc                 C   sl   | pd   }|pd   }|dkr"|drdS |dr"dS |dkr4|dr-dS |dr4dS d S )Nr   rF  r  Tr  FrG  )r   rx   
startswith)r  r  r   mbr$   r$   r%   _market_bias_alignment  s   



r  c                 C   s  t |}d|i}tdds||fS ztddddd}t| }|s,dd	d
|d< ||fW S t|d}|du rCddd
|d< ||fW S t |  d }|t	|kr`dd|d|d< ||fW S |d}t
t|ddt|pqd}	d| d ||	d|d< |	du rtddddd}
tt|t|
 }t|
|d d< ||d< ||fW S ||d< ||fW S  ty   ddd
|d< ||f Y S w )zCompute effective strength for paper-trade creation.

    By default this equals the signal's normalized confidence.
    Optionally boosts 1 level when market bias aligns with the trade direction.
    base_strengthPAPER_MARKET_BIAS_BONUS_ENABLEDF!PAPER_MARKET_BIAS_MAX_AGE_MINUTESrb  rM   i  r   missing_payload)usedro   micaptured_atNmissing_captured_atg      N@stale)r  ro   age_minutesr  r  r   TZ)r  r  r  aligned PAPER_MARKET_BIAS_STRENGTH_BONUSr   r   bonuseffective_strengthr   )_signal_strengthr:   r&   r  _safe_dtrw   r   utcnowrs  r*   r  r   r   r  r  r   r   )r   r  baserp   max_ager  r  age_minr  r  r  boostedr$   r$   r%   #_effective_strength_for_paper_entry  sJ   








r  c                 C   s>   z| d u rW d S t | trW d S t| W S  ty   Y d S w r   )r   r6   r*   r   r  r$   r$   r%   ry     s   

ry   dtc                 C   s^   z| j du s| j | du r| jddW S | tjjddW S  ty.   | jdd Y S w )zReturn a UTC-naive datetime for consistent DB/storage comparisons.

    - If dt is tz-aware, convert to UTC then drop tzinfo.
    - If dt is tz-naive, assume it's already UTC and keep as-is.
    Nr   )r   	utcoffsetr   r   r   r   r   )r  r$   r$   r%   rr     s   rr   c                 C   s^   | d u rd S t | trt| S t | tr-zt| dd}t|W S  ty,   Y d S w d S )Nr  z+00:00)r   r   rr   r   fromisoformatr   r   )r#   parsedr$   r$   r%   r  "  s   


r  c                 C   s   | pd   S r   r   rx   r  r$   r$   r%   r   0  r   r   c                 C   s,   | pd   }|dkrdS |dkrdS d S )Nr   rF  rX   rG  rY   r  )r  r   r$   r$   r%   _direction_from_decision4  s   r  c                 C   s    t | d| d| ddS )N
confidencedecision_probabilityscore)r  r  )r
   rw   )r  r$   r$   r%   r  =  s
   r  c                 C   s   t | d}t | d}t | d}|d u r t | d}|d u r8| d}t|ttfr8|r8t |d }|d u rX| d}t|trXt |d}|d u rXt |d}|||fS )	NrS   	stop_lossprice_targetr  targetsr   r   exit)ry   rw   r   rX  tupler   )r  r   r  r  raw_targetstargets_objr$   r$   r%   _extract_trade_planE  s    



r  candlec                 C   s(   dD ]}t | |}|r|  S qd S )N)r   rz  	timestamptime)r  rw   )r  r   r   r$   r$   r%   
_candle_ts\  s   r  c                 C   sh   t | t| dp| dt| dp| dt| dp#| dt| dp.| dd	}|S )
Nopenohighr   lowlr   c)r   r  r  r  r   )r  ry   rw   )r  outr$   r$   r%   r   e  s   r   c                 C   s   t | tsdS | dpi }t |tsdS |pd }g }|r$|| |g d |D ]}||}t |trJ|rJ|d }t |trJ||f  S q-| D ]\}}t |trk|rkt |d trk|d t|f  S qOdS )NrE  candlesr   )15minute30minute5minute60minuter   r   )	r   r   rw   r   appendextendrX  r   r   )r   r   candles_by_tftfstfarrlastr$   r$   r%   r   q  s,   




r   c                 C   s`   z | t  }|jg ddd |jg ddddddd	id
 W d S  ty/   td Y d S w )N)r   r   )rQ   rM   )rK   rM   )r   r   paper_trades_user_symbol_statusr   )r   r   )	signal_idrM   paper_trades_unique_signalTr  string)rI   z$type)r   r   partialFilterExpressionz%Failed to ensure paper_trades indexes)ru   r   r   r~   r   )r   r   r$   r$   r%   ensure_paper_trades_indexes  s   r  c                   C   s   t  S r   )r   r  r$   r$   r$   r%   rs     s   rs   tradec                 C   s  t | d}t | d}t | d}| dpd  }t|}|d}|d}|d}	|d	}
|d
u sL|d
u sL|d
u sL|d
u sL|d
u rS|	|
t dS t | dp[d}t | dpdd}|dkrtd|| }td|| }t||}t||}||kr|}|| }d|dt t|d|||	|
t dS ||kr|}|| }d|dt t|d|||	|
t dS |	d
ur|	| nd
}|||d
urt|nd
|	|
t dS |dkrJtd|| }td|| }t||}t||}||kr|}|| }d|dt t|d|||	|
t dS ||kr,|}|| }d|dt t|d|||	|
t dS |	d
ur5||	 nd
}|||d
urBt|nd
|	|
t dS |	|
t dS )zReturn patch dict for DB update based on deterministic candle rules.

    Deterministic rule: Stop-loss is checked first (worst-case) when both SL/target occur in same candle.
    rS   r  r  rR   r   r  r  r   r   NrW   last_candle_tsrd   max_favorable_mover'   max_adverse_moverX   rZ   	STOP_LOSS)rK   r]   r^   r_   r`   rb   r  r  rW   r  rd   TARGET)r  r  rb   rW   r  rd   rY   )ry   rw   r   rx   r   rs   r   r*   )r  r  r   r  r  rR   r  r  r  r   	candle_tsprev_mfeprev_mae	favorableadversemfemaer]   realizedry  r$   r$   r%   _maybe_close_trade_with_candle  s   



(


	



r  candle_timeframec           $      C   s	  t |}|rt|tsdS | t }t|}|d}|dddgid}t }	d}
d}t|tr<|	r<t|d}
t|}d}|	|i d	d
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
d
d
dD ]d}zQt
|d}|r|r||krW q||dpd  pd}|dpd  }t|dtr|dni }|d}|durt|nd}i }|dkr#t|d}|du r|d |t d!}n4t|dpd}td|d
 }t }|d"}|d }|duot|t|k}|duot|t|k}|d#krn|s(|rndt |t|d|r5d$nd%|d |t d&	}|rmt||}|| |dd'krmd|d< d|d(< d|d)< d|d*< d|d+< d|d,< nt||d |t d-}|	r|
r|rtt|d.r|d/v r|
d/v r|
|kr|d dur|d'|d d0t d1d1d2 n'|d#kr|dkr||kr|d dur|d'|d d3t d1d1d2 |dd'kr|rt|d4pd5  pd5d5kr|rt|r|d dur|d'|d d6t d1d1d2 |r!||d7< ntt||}|r/||d7< |dd'krt|d8p?d}td|d
 }t||d8< |	r|
r|rtt|d.r|d/v r|
d/v r|
|kr|d durt|d}t|d }d1}|dur|dur|d#krt|t| }n|d9krt|t| }|d'|d0t t|d1d2 nK|d#krt }|dkr||kr|d durt|d}t|d }d1}|dur|durt|t| }|d'|d3t t|d1d2 |dd'kr|r|dr|rt|d4p"d5  p*d5d5krt|rt
|d} t|d }| r|durzCt| t|krt|d}d1}|dur||d#krot|t| }n|d9kr|t|t| }|d'|d6t t|d1d2 W n
 ty   Y nw t|dpd}!|!dk rd}!t|d,}"t|d:}#|#dur|!dkrt|#t|! |d;< |"dur|!dkrt|"t|! |d+< |dd'krTt| t|dpdt|dpdt|d	t|dp|dpd1pd1t|d+pd1t t|d(p%d'd< t| t|dp3dt|dp<d|d=t|d(pGd't|d	|d>d? |dkr|ddkrz9| t t|dpndt|dpwdd@dAt dBt|d	|t|dCpddDidEt idF W n
 ty   Y nw |d	|d	 idG|i t | t|dpdt|dpddH| dI |d
7 }W q| ty   t!"dJ| Y q|w |S )KzWEvaluate all ACTIVE paper trades (OPEN + PENDING) for symbol using the provided candle.r   r   r   rG   r   rQ   rK   Nr  rN   rM   rO   rP   rS   r  r  rR   rT   rV   rU   r   
pending_atr   r  r  r  pending_candle_count)open_candle_countrK   rK   r   r   Fr   r  r  rX   OPEN_ABOVE_ENTRYCLOSE_AT_OR_ABOVE_ENTRY)	rK   r   opened_candle_tsr  r	  confirm_reasonrW   r  rd   rZ   r^   r]   r_   ra   r`   )r  rW   r  rd   r  >   rX   rY   OPPOSITE_SIGNALr'   )rK   r]   r^   r_   r`   rb   	TIME_EXITr   rJ   r   last_candle_timeframer	  rY   rb   rc   rf   rj   rk   rm   rc  ri  TRADE_PENDING_TO_OPENr  )r   rp  rg   rQ   r  rd   )rr  re   re   CANDLE_rq   z'Paper trade update failed for symbol=%s)#r   r   r   ru   r   rw   r   r  r  rv   r  r   rx   r6   ry   rs   r   r   r   r*   r  updater  r   r   r   r   r   r{   r|   r   rz   r}   r~   r   )$r   rQ   r  r  r  r   normr  r   opposite_exit_enabledanalysis_diranalysis_strengthupdatedr   	last_seenrK   rR   r   r   r   r   pending_cntpending_maxcopenccloseconfirm_by_openconfirm_by_closepatch2open_cntclose_pxr   max_candlesr   r   per_unit_realper_unit_unrealr$   r$   r%   #update_open_paper_trades_for_symbol/  sb  	
	








	


.(






$(




$
(	r&  rg   rV   rh   ri   r^   c                 C   sN  |r|sd S t | t|t|d}| t }	t|t|d}
t|dp&d}t|dp/d}t|p4d}t|p:d}td|| }|| }td|}|| }td|| }|	|
t|t|t|t ddt dt|t|t|t|t|p{dt|t|d		id
 t	| t|t||t|t|dt|pd
  d d S )Nrc  rd  r'   re  )rd  re  r  rd   ri  rj   r   )	r   rp  rg   rV   pnlpnl_appliedr^   balance_beforebalance_after)re   rr  TRADE_rZ   r  )rt  r   r   ry   rw   r*   r   rz   rs   r  rx   )r   rO   rP   rg   rV   rh   ri   r^   rw  r   r   rd  rx  r  r'  reserved_afterraw_new_balancenew_balanceapplied_deltar}  r$   r$   r%   r{     sX   



r{   r  stock_idsourcer   snapshot_timestamprY  c                 C   s   t |tsdS dS )zCreate OPEN/PENDING paper trade if signal passes filters and BUY/SELL.

    Returns paper_trade_id (string) or None if skipped.
    N)r   r   )r   rO   rP   r0  rQ   r  r1  r   r2  rY  r   r  r$   r$   r%   create_paper_trade_from_signal  s   
r3  c           ;      C   sj   t | }|du r
dS ||ks|tdtdfv rdS dt|  kr&dkr1n t|S t|d S t|S )zNormalize various score/probability formats to 0-100.

    Accepts:
    - 0..1 probability -> 0..100
    - 0..100 score -> 0..100
    Nr(   r)   r'   r   r   )?ry   r*   r   r   r   r   r   r  rw   r  rt  r   rO   r   r   r   rX  r1  rx   setr   r    _score_to_pctr~   infosortedkeysr|   rs   rA   r@   format_windowlocal_time_strpaper_trade_windowr  r  r  r  r  r5  r   r  rY  r  r_  r   r   r  ru   rV  r  r2  r0  r   r6   r   r   rz   modified_countr   inserted_idr}   r   );r#   r  rP   rQ   rR   settings_objacc0allowed_sourcesallowed_decisionsd0	min_scorer"   r  r   r9  r:  r;  strengthstrength_detailsr   r  r  rw  r   r}  r  rx  planned_entry
planned_slplanned_targetr4  rp   r   rV   rU   r;  
margin_rawr   qty0tv0req
scaled_qtyreq2raw2tv2r  r  r   existingr   snap_tsr  r   acc_colqaccupdacc_updrespaper_trade_idr$   r$   r%   r5    s   r5  c                 C   s   t |  t|  t|p|pi d}t||d\}}d}|r_t| ||||d}|	r_z%|dd}|r8t||d< |r@t||d< | t |d	d
t|	ii W n t	y^   t
d| Y nw t| |||||||	|
|||d}||dS )zXCore loop: update OPEN trades using latest candle, then create new trade if VERY_STRONG.rQ   r   r   )rQ   r  r  r  rG   r  rO   rP   re   last_snapshot_idz/Failed to update last_snapshot_id for symbol=%s)rO   rP   r0  rQ   r  r1  r   r2  rY  r   r  )updated_open_tradescreated_paper_trade_id)r  rt   r   rw   r   r&  r   ru   update_manyr   r~   r   r3  )r   rO   rP   r0  rQ   r  r   r1  r  r   r2  rY  r  r  r  r   
created_idr$   r$   r%    process_signal_and_market_update  sH   

r^  )r0  r   rS   r  rT   c       
   %      C   s  t |  t|  t|pd }|sdddS |dur't| r't| nd}|s6tdp1d p5d}t|}|sAdddS |pDd  }|d	v rPd
}n|dv rVd}|dvr_dddS zt|}t|}t|}W n t	y{   ddd Y S w t
|pd}|dkrdddS |dks|dks|dkrdddS | t }||||dddgidddd}|rzt| |||dddt|dpdid W n	 t	y   Y nw dddS t| ||d}t|dtr|dni }|d }z|durt|t
|}W n
 t	y   Y nw |dkrdddS t|t| }t|}t|d!}|du rJt|d"p8d#}t|d$pBd#}td#|| }t|d% t|k ryzt| |||dd&t|t|d'd W n
 t	ys   Y nw dd(dS t }| t }||d)t|id*}t|t| d+d,t id-t d.|t|t|t
|d/id0} ||| }!|!jdkrdd(dS i d1d2d3|d4|d5|	durt|	ndd6|d7|d8t|d9t|d:t|d;t
|d<t|d=t|d>dd?d@dddA|dB|i dCddDddEddFddGddHddIddJd#dKd#dLddMddNddO|
r2t|
ndd,|dP|d
kr?dQndRt|t|t|d@dSdTt|t|t|dU}"dVdW |" D }"zI||"}#t|#j}$|dX|#jidYdZ|$ii zt| |||d[d\|$|t
|t|d]d W n
 t	y   Y nw t| ||d^| d_ d`|$daW S  t	y   tdb| ddcd Y S w )dzCreate a manual paper trade with explicit plan + quantity.

    This bypasses GPT score/strength filters and creates an OPEN trade immediately.
    Returns {ok, paper_trade_id?, reason?}.
    r   FUSER_ID_MISSING)okro   NPAPER_DEFAULT_ACCOUNT_IDr   SYMBOL_MISSING>   rF  rX   rX   >   rG  rY   rY   >   rX   rY   DIRECTION_INVALIDPRICE_INVALIDr   QTY_INVALIDPRICE_NON_POSITIVEr   rG   r   )rO   rP   rQ   rK   rM   )rN   rK   TRADE_SKIPPEDSYMBOL_ALREADY_ACTIVEexisting_statusrK   rm   rc  r   r   r  rd  r'   re  r  LIMITS_OR_BALANCE)r}  r:  INSUFFICIENT_BALANCEz$gte)rO   rP   r  )re  r  rd   ri  TRADE_MANUAL_RESERVED)r   rp  rQ   rx  r   r   )r  re   rr  
trade_typePAPERrO   rP   r0  rQ   rR   rS   r  r  rT   rV   rU   r;  r1  MANUALr   r   r_   r]   r^   ra   r`   rc   rb   r  r  rW   r  r  r   signalrF  rG  )r  rS   r  r  r  effective_plan)rS   r  r  c                 S   r   r   r$   r   r$   r$   r%   r   	  r   z-create_manual_paper_trade.<locals>.<dictcomp>rN   re   rX  TRADE_CREATEDMANUAL_OPEN)rg   rR   r   rx  MANUAL_rq   T)r`  rX  z*Failed to create manual paper trade for %sDB_INSERT_FAILED)r  rt   r   r   r   r   r   rx   r*   r   r   ru   rV  r|   rw   rt  r   r   r    ry   r   rs   r   rz   r<  r   r   r=  r}   r~   r   )%r   rO   rP   rQ   rR   rS   r  r  rT   r0  r   uidacctsymr   r   r  tgtr   r   rQ  rw  r   r  rV   rU   r}  r  rx  r   rS  rT  rU  rV  r   rW  rX  r$   r$   r%   create_manual_paper_trade  s  
$




	






	

	
 '+


	rz  r   )r   NrE  )NNNNr  )NNN)Rloggingr   r   r   r   typingr   r   r   r   app.v1.utils.confidencer	   r
   	getLogger__name__r~   ru   r   r   r   r   r   r   r&   r*   r+   r6   r:   r=   rA   r   r   r   rt   r|   r   r   r   r   r   r   r   r   r   r   r  r5  r8  rD  r_  r}   r  rt  r  r  r  r  r  r  r  r  r  r  ry   rr   r  r   r  r  r  r  r   r   r  rs   r  r&  r{   r3  r5  r^  rz  r$   r$   r$   r%   <module>   s   
&&$~
*	
!$ &	

X"	

T&;4.*&

*6	0"	:
. 

  W	
H
	

   Z
	


D	

