
    iJ                    &   U d Z ddlmZ ddlmZmZ ddlmZmZm	Z	m
Z
 ddlmZ ddlmZmZmZ ddlmZ 	 ddlmZ g d	d
dgddgg ddZded<    ed       G d d             Z ed       G d d             Z G d d      Zy# e$ r i ZY Tw xY w)u  
APEX V16 — Risk Manager.

Single source of truth for "can the bot open this specific trade right now?".

V15 had risk checks scattered across topstep_guards_status, correlation_block,
budget_pre_check, halt, _on_sl_hit, _check_daily_reset. V16 centralizes
all pre-trade gates here. Sizing-related budget pre-checks live in
trading/sizing.py and propagate via SizingDecision.skip; risk_manager
respects that result instead of re-deriving it.

Public API:
  - RiskManager(config, state, logger)
  - .check_entry(*, entry, sizing, symbol, active_trades, now_utc)
                                                    -> RiskCheckResult
  - .halt(reason)
  - .clear_halt(why)
  - .update_daily_pnl(delta_usd, *, is_win, brain)
  - .register_sl_hit(symbol, *, now_utc)            (cooldown + consecutive_sl)
  - .register_tp_hit(symbol)                         (resets consecutive_sl)

The RiskManager:
  - reads state.* and active_trades, never mutates active_trades.
  - mutates state.daily.*, state.brain.*, state.cooldown.*, state.halted.
  - does NOT call state_store.save() — orchestrator owns persistence
    (V15-BUG-9 discipline preserved; risk_manager mutates, orchestrator
    saves after each tick).

Halt semantics:
  - halt() sets state.halted=True. Persists until daily reset
    (load_state(auto_daily_reset=True)) or clear_halt().
  - profit_target REJECTS entries but does NOT halt — the bot stays
    alive to manage open positions (V15-parity, riga 666-668).
  - daily_loss_hard_stop AUTO-HALTS (V15-parity, riga 655-658).
  - daily_loss_soft_stop REJECTS entries but does NOT halt — the bot
    can resume opening if daily_pnl recovers above soft (V15-parity,
    riga 661-663).
    )annotations)asdict	dataclass)datetimetime	timedeltatimezone)Optional)EntryDecisionRiskRuleutc_now)SizingDecision)MAX_CONTRACTS_PER_TRADE)MESMNQMYMESNQYMMGCGCMCLCL)6E6B6A6J6C)EQUITY_INDICESMETALSENERGY	FX_MAJORSzdict[str, list[str]]CORRELATION_GROUPST)frozenc                      e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded<   ded	<   d
ed<   d
ed<   ded<   ded<   ded<   y)	RiskAudita  
    Diagnostic context emitted with every check_entry() result.

    Persisted to brain_log.jsonl for forensic review and calibration.
    Populated for both approvals and rejections so we can compute
    "how close was this approval to a gate" post-hoc.
    float	daily_pnldaily_loss_hard_stopdaily_loss_soft_stopdaily_profit_targetdaily_remaining_budgetpending_risk_usdrisk_vs_budget_pctintopen_trades_countconsecutive_sl_countOptional[str]cooldown_untilcorrelation_blockerstrnow_utcN)__name__
__module____qualname____doc____annotations__     +/home/work/apex_v16/trading/risk_manager.pyr&   r&   M   sP     !!!!&&Lr=   r&   c                  B    e Zd ZU dZded<   ded<   ded<   ded<   dd	Zy
)RiskCheckResultz
    Output of RiskManager.check_entry().
      approved: True iff trade may proceed.
      rule:     stable RiskRule code (machine-readable).
      reason:   human-readable explanation (logged).
      audit:    RiskAudit context.
    boolapprovedr5   rulereasonr&   auditc                ,    t        | j                        S N)r   rE   )selfs    r>   
audit_dictzRiskCheckResult.audit_dictr   s    djj!!r=   N)returndict)r7   r8   r9   r:   r;   rI   r<   r=   r>   r@   r@   d   s#     N
IK"r=   r@   c                     e Zd ZdZdZdZdddZdd	 	 	 	 	 	 	 	 	 	 	 ddZddZddd	Z		 	 	 	 	 	 	 	 dd
Z
dd	 	 	 	 	 ddZddZddZ	 	 	 	 	 	 	 	 	 	 ddZedd       Z	 	 	 	 	 	 	 	 ddZe	 	 	 	 	 	 d d       Zed!d       Zy)"RiskManagerz3Centralized pre-trade risk gates + lifecycle hooks.i  i   Nc                .    || _         || _        || _        y rG   )configstatelogger)rH   rO   rP   rQ   s       r>   __init__zRiskManager.__init__   s    
r=   r6   c                  |xs
 t               }| j                  ||||      }|j                  r.| j                  t        j
                  d|j                   |      S | j                  j                  r9| j                  t        j                  | j                  j                  xs d|      S | j                  j                  }| j                  j                  j                  |k  rd| j                  j                  _        | j!                  d| j                  j                  j                  dd|d       | j                  t        j"                  d| j                  j                  j                  dd	|d|      S | j                  j$                  }| j                  j                  j                  |k  rG| j                  t        j&                  d| j                  j                  j                  dd
|d|      S | j                  j(                  }	| j                  j                  j                  |	k\  rbd| j                  j                  _        | j                  t        j,                  d| j                  j                  j                  dd|	d|      S t/        |      | j                  j0                  k\  rD| j                  t        j2                  t/        |       d| j                  j0                   d|      S | j                  j                  j4                  | j                  j6                  k\  rY| j                  t        j8                  | j                  j                  j4                   d| j                  j6                   d|      S | j;                  |      rK| j                  j<                  }
|j>                  |
k\  r&| j                  t        j@                  d|
dd|      S tC        | j                  jD                  | j                  jF                  tH        jJ                        }|jM                         |k\  r4| j                  t        jN                  d|jQ                  d       d|      S | j                  jR                  jT                  jW                  |      }|r`	 tY        jZ                  |      }||k  r$| j                  t        j\                  d| |      S | j                  jR                  jT                  |= | j                  jb                  rM| je                  ||      }|9| jg                  ||      }| j                  t        jh                  | d| d|      S tk        jV                  |tk        jV                  dd            }|jl                  |kD  r3| j                  t        jn                  |jl                   d| d| |      S | j                  jp                  }|jr                  d kD  rg|jr                  |z  }|jt                  |kD  rI| j                  t        jv                  d!|jt                  dd"|d#z  dd$|jr                  dd%|dd&	|      S ty        dt        jz                  j|                  d'|(      S # t^        t`        f$ r% | j                  jR                  jT                  |= Y w xY w))z
        Evaluate all pre-trade gates. First failing gate wins.
        Order: cheap binary checks first, calendar before intraday,
        budget arithmetic last.
        )symbolsizingactive_tradesr6   zsizing skip: haltedTzDaily loss hard stop: $z.2fz <= $zdaily P&L $z
 <= hard $z
 <= soft $zdaily target $z+.0fz >= $z.0f/z open positionsz trades todayzlast Friday of month, after 02dz:00 UTC)tzinfozafter z%H:%Mz UTC force-flat cutoffzcooldown until z
 blocked: z already open (correlated)default   z > cap z for r   zrisk $z > d   z% of remaining $z (allowed $) rB   rC   rD   rE   )?r   _build_auditskip_rejectr   SIZING_SKIPrD   rP   rX   HALTEDhalt_reasonrO   r)   dailyr(   daily_loss_hard_stop_hithaltDAILY_LOSS_HARD_STOP_HITr*   DAILY_LOSS_SOFT_STOP_HITr+   profit_target_hitDAILY_PROFIT_TARGET_REACHEDlenmax_open_trades_totalMAX_OPEN_TRADES_REACHEDexecuted_countmax_daily_tradesMAX_DAILY_TRADES_REACHED_is_last_friday_of_month$last_friday_of_month_cutoff_utc_hourhourLAST_FRIDAY_CUTOFFdtimeforce_flat_utc_hourforce_flat_utc_minuter	   utctimetzFORCE_FLAT_TIME_REACHEDstrftimecooldownr3   getr   fromisoformatCOOLDOWN_ACTIVE	TypeError
ValueErrorenable_correlation_correlation_blocker_with_correlationCORRELATION_BLOCKEDr   	contractsMAX_CONTRACTS_EXCEEDEDmax_risk_vs_daily_budgetr,   real_risk_usd!MAX_RISK_VS_DAILY_BUDGET_EXCEEDEDr@   OKvalue)rH   entryrV   rU   rW   r6   rE   hardsofttargetcutoff_hffcooldown_until_isocublockermax_ctcap_pctalloweds                     r>   check_entryzRiskManager.check_entry   s    &WY!!'	 " 
 ;;<<$$/  ::<<

&&2(  {{//::%%-8<DJJ5II)$***:*:*D*DS)ItTWjY <<11djj..88=ZSzR  {{//::%%-<<11djj..88=ZSzR  00::%%/15DJJ.<<44 !1!1!;!;D AvclS  }!B!BB<<00}%&a(I(I'J/Z  ::**dkk.J.JJ<<11::##2231T[[5Q5Q4RR_`  ((1{{GGH||x'||//28C.H  KK++KK--<<

 >>r!<<00W-..DE  "ZZ00??CCFK?++,>?R<<< 00)*<)=>  JJ''66v> ;;))//FG"..ug>||00hj	1KL  ),,+//	1=
 f$<<//##$GF85A  ++66 ''!+22W<G##g-||>> !5!5c :#"3;s++;E<X<XY\;] ^%%,SM4   ""	
 	
] z* ?JJ''66v>?s   )=Z
 '!Z
 
0Z>=Z>c                :   | j                   j                  ryd| j                   _        || j                   _        | j                  G	 | j                  j	                  d|       | j                  j
                  j                  d|        yy# t        $ r Y yw xY w)zEActivate kill switch. No new entries until daily reset or clear_halt.NTrj   )rD   zHALT: )rP   rX   rg   rQ   log_session_eventsystemwarning	Exception)rH   rD   s     r>   rj   zRiskManager.halt=  s    :: 

!'

;;"--fV-D""**VF8+<= #  s   AB 	BBc                   | j                   j                  sy| j                   j                  }d| j                   _        d| j                   _        | j                   	 | j                  j	                  d||       yy# t
        $ r Y yw xY w)zDClear halt flag. Use sparingly; daily reset handles the common case.NFr`   halt_cleared)whywas)rP   rX   rg   rQ   r   r   )rH   r   olds      r>   
clear_haltzRiskManager.clear_haltJ  sz    zz  jj$$!

!#

;;"--n#3-O #  s   A= =	B	B	c                   | j                   j                  xj                  |z  c_        | j                   xj                  |z  c_        |dk(  rV|r*| j                   j                  xj
                  dz  c_        y| j                   j                  xj                  dz  c_        y|dk(  rV|r*| j                   j                  xj                  dz  c_        y| j                   j                  xj                  dz  c_        yy)z
        Apply a P&L delta and update brain win/loss counters in one call.
        Brain string is the stable BrainName value ("TF" or "MR").
        TFr]   MRN)	rP   rh   r(   session_pnlbraintf_wins	tf_lossesmr_wins	mr_losses)rH   	delta_usdis_winr   s       r>   update_daily_pnlzRiskManager.update_daily_pnlW  s     	

""i/"

)+D=

  ((A-(

  **a/*d]

  ((A-(

  **a/*	 r=   c          
        |xs
 t               }| j                  j                  j                  }| j                  j                  j                  di       }t        |j                  |d            }|dz   }|||<   |dk\  r| j                  }d}n| j                  }d}|t        |      z   j                         }	|	| j                  j                  j                  |<   |j                         | j                  j                  j                  |<   | j                  3	 | j                  j                  j                  d	| d
| d| d|	        yy# t         $ r Y yw xY w)a  
        Anti-overtrading post-SL (V15 _on_sl_hit, riga 2117).
          - prior consecutive_sl >= 2 -> 2h cooldown (circuit breaker)
          - else                       -> 30min cooldown
        Increments state.cooldown.last_trade_close_at and consecutive_sl.
        r1   r   r]      2h30min)secondsNz[SL] z: SL #z -> cooldown z until )r   rP   r   last_trade_close_atmetadata
setdefaultr/   r   COOLDOWN_CONSECUTIVE_SL_SECONDSCOOLDOWN_FIRST_SL_SECONDSr   	isoformatr3   rQ   r   infor   )
rH   rU   r6   priorcountersprev	new_countr   labelr3   s
             r>   register_sl_hitzRiskManager.register_sl_hito  sF    &WY

##77 ::&&112H"M8<<*+1H	$19::GE44GE!Ig$>>IIK5C

**62:A:K:K:M

//7;;"""''F86)M%P^O_` #
  s   1E 	EEc                    | j                   j                  j                  di       }d||<   t               j	                         | j                   j
                  j                  |<   y)z?Reset consecutive_sl counter for symbol. Cooldown not extended.r1   r   N)rP   r   r   r   r   r   r   )rH   rU   r   s      r>   register_tp_hitzRiskManager.register_tp_hit  sN    ::&&112H"M:A):M:M:O

//7r=   c                    t        | j                  j                  j                  di       j                  |d            S )Nr1   r   )r/   rP   r   r   )rH   rU   s     r>   _consecutive_sl_forzRiskManager._consecutive_sl_for  s3    4::&&**+A2FJJ6STUVVr=   c               *   | j                   j                  }t        || j                  j                  j
                  z
        }|dkD  r|j                  |z  nd}| j                  j                  j                  j                  |      }t        | j                  j                  j
                  || j                   j                  | j                   j                  ||j                  |t        |      | j                  |      |d |j                               S )Nr   g      ?)r(   r)   r*   r+   r,   r-   r.   r0   r1   r3   r4   r6   )rO   r)   absrP   rh   r(   r   r   r3   r   r&   r*   r+   ro   r   r   )	rH   rU   rV   rW   r6   r   	remainingrisk_vs_budgetr3   s	            r>   rb   zRiskManager._build_audit  s     {{//tzz//999:	09AF  9,3 	 ,,;;??Gjj&&00!%!%!A!A $ ? ?#,#11-!-0!%!9!9&!A) $%%'
 	
r=   c                8    t        |       }||d<   t        di |S )z4Return a new RiskAudit with correlation_blocker set.r4   r<   )r   r&   )rE   r   ds      r>   r   zRiskManager._with_correlation  s$     5M#*
 ~1~r=   c                4    t        d|j                  ||      S )NFra   )r@   r   )rH   rC   rD   rE   s       r>   rd   zRiskManager._reject  s!     	
 	
r=   c                n    t         j                         D ]  }| |vr|D ]  }|| k7  s	||v s|c c S    y)z
        Returns the symbol of a correlated open trade that blocks `symbol`,
        or None if no group-level blocker applies.
        N)r#   values)rU   rW   membersothers       r>   r   z RiskManager._correlation_blocker  sN     *002 	!GW$  !F?u'= L!	! r=   c                v    | j                         dk7  ry| t        d      z   j                  | j                  k7  S )z9True iff `now_utc` falls on the last Friday of its month.   F   )days)weekdayr   monthrS   s    r>   ru   z$RiskManager._is_last_friday_of_month  s5     ??!)++22gmmCCr=   rG   )rJ   None)r   r   rV   r   rU   r5   rW   rK   r6   Optional[datetime]rJ   r@   )rD   r5   rJ   r   )manual)r   r5   rJ   r   )r   r'   r   rA   r   r5   rJ   r   )rU   r5   r6   r   rJ   r   )rU   r5   rJ   r   )rU   r5   rJ   r/   )
rU   r5   rV   r   rW   rK   r6   r   rJ   r&   )rE   r&   r   r5   rJ   r&   )rC   r   rD   r5   rE   r&   rJ   r@   )rU   r5   rW   rK   rJ   r2   )r6   r   rJ   rA   )r7   r8   r9   r:   r   r   rR   r   rj   r   r   r   r   r   rb   staticmethodr   rd   r   ru   r<   r=   r>   rM   rM   z   s   = &*&*#  '+m
 m
 	m

 m
 m
 $m
 
m
f00 	0
 0 
08 '+	++ $	+
 
+ZPW
 
 	

 
 
 

:  

 
 	

 

  
   D Dr=   rM   N)r:   
__future__r   dataclassesr   r   r   r   ry   r   r	   typingr
   core.contractsr   r   r   trading.sizingr   core.config_futuresr   ImportErrorr#   r;   r&   r@   rM   r<   r=   r>   <module>r      s   %N # ) A A  ; ; )!;  >dmdm4	, (  $  , $" " "*yD yDO  ! !s   B BB