
    wj].                       d Z ddlmZ ddlmZmZmZ ddlmZmZ ddl	m
Z
 ddlmZmZ  G d dee
      Z G d	 d
ee
      Z G d dee
      Z G d dee
      Z G d dee
      Z G d dee
      Z ed       G d d             Ze G d d             Ze G d d             Ze G d d             Ze G d d             Ze G d d              Zd#d!Zy")$u\  
APEX V16 — Core data contracts (dataclasses).

Single source of truth for ALL structured data shapes in V16.
Replaces V15's dict-based init_data / brain context with typed,
immutable-where-appropriate dataclasses.

Rule: NO module outside this file defines its own dict for trade,
brain context, or brain decision data. Always import from here.
    )annotations)	dataclassfieldasdict)datetimetimezone)Enum)OptionalAnyc                      e Zd ZdZdZy)	DirectionBUYSELLN)__name__
__module____qualname__r   r        %/home/work/apex_v16/core/contracts.pyr   r      s    
CDr   r   c                      e Zd ZdZdZy)	BrainNameTFMRN)r   r   r   r   r   r   r   r   r   r      s    	B	Br   r   c                      e Zd ZdZdZdZdZy)TradeActionHOLDEXIT
PARTIAL_50MOVE_SLN)r   r   r   r   r   r   r   r   r   r   r   r   "   s    DDJGr   r   c                  H    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
ZdZdZdZdZdZy)RiskRulea  
    Stable rule codes for risk_manager.check_entry() rejections.

    These are part of the V16 contract: never rename existing codes
    (calibration analytics + JSONL forensics depend on them). New rules
    can be appended; deprecation is an explicit project decision.
    OKSIZING_SKIPHALTEDDAILY_LOSS_HARD_STOP_HITDAILY_LOSS_SOFT_STOP_HITDAILY_PROFIT_TARGET_REACHEDMAX_OPEN_TRADES_REACHEDMAX_DAILY_TRADES_REACHEDLAST_FRIDAY_CUTOFFFORCE_FLAT_TIME_REACHEDCOOLDOWN_ACTIVECORRELATION_BLOCKEDMAX_CONTRACTS_EXCEEDED!MAX_RISK_VS_DAILY_BUDGET_EXCEEDEDN)r   r   r   __doc__r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r   r   r   r!   r!   +   sY     )-B(5K(0F(B(B(E(A(B(<(A(9O(=(@(K%r   r!   c                  $    e Zd ZdZdZdZdZdZdZy)MarketStructureBULLISH_EXPANSIONBULLISH_COMPRESSIONBEARISH_EXPANSIONBEARISH_COMPRESSIONRANGINGUNKNOWNN)	r   r   r   r3   r4   r5   r6   r7   r8   r   r   r   r2   r2   C   s$    +/+/GGr   r2   c                      e Zd ZdZdZdZy)RegimeTRENDINGr7   r8   N)r   r   r   r;   r7   r8   r   r   r   r:   r:   L   s    HGGr   r:   T)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<   ded<   ded<   ded<   ded<   dZded<   dZded<   dZded<   dZded<   dZ	ded<   dZ
ded <   d#d!Zed$d"       Zy)%
TradeEntrya  
    Immutable snapshot of all data captured at the moment a trade is opened.

    Once created, NEVER modified. This is the contract that fixes V15-BUG-5
    (rsi_start saved but never propagated) and V15-BUG-2 (key name mismatch).

    All Brain decisions during the trade lifecycle reference THIS snapshot
    as the "ground truth at entry", not whatever tech_now happens to be.
    strsymbol
brain_name	directionint	contractsfloatentry_pricesl_pricetp_pricer   	opened_atrsi_m5_at_entryrsi_h1_at_entryrsi_h4_at_entryatr_ratio_at_entrymarket_structure_at_entryregime_at_entryh1_compat_at_entryconfidence_at_entryNOptional[str]entry_order_idstop_order_idtarget_order_idFboolis_paperis_orphan_recovered        orphan_entry_price_diffc                V    t        |       }| j                  j                         |d<   |S )z@Serialize to dict for state persistence (datetime -> isoformat).rI   )r   rI   	isoformat)selfds     r   to_dictzTradeEntry.to_dict   s&    4L113+r   c                    t        |      }t        |j                  d      t              rt	        j
                  |d         |d<    | di |S )z#Deserialize from dict (state load).rI   r   )dict
isinstancegetr?   r   fromisoformat)clsdatar^   s      r   	from_dictzTradeEntry.from_dict   sC     JaeeK(#.%33AkNCAkNxQxr   returnra   )rf   ra   ri   z'TradeEntry')r   r   r   r0   __annotations__rS   rT   rU   rW   rX   rZ   r_   classmethodrg   r   r   r   r>   r>   V   s     KONN OO  "" %)NM(#'M='%)O]) Hd %% &)U(  r   r>   c                      e Zd ZU dZdZded<   dZded<   dZded<   dZded	<   dZ	ded
<   dZ
ded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<   ddZedd       Zy)TradeRuntimeaw  
    Mutable state that changes during the trade lifecycle.
    Updated by orchestrator at each tracking iteration.

    current_sl_price tracks the broker-side stop after MOVE_SL or
    set_be_after_partial. entry.sl_price stays at the original SL
    (immutable snapshot at fill time). Brains use entry.sl_price for
    "SL at entry" comparisons; the orchestrator uses current_sl_price
    when emitting the next modify_stop. 0.0 means "still at entry SL".

    partial_pnl_usd captures the realized P&L from a partial close
    (separate from net_profit_usd which tracks the unrealized delta
    on the still-open portion).
    rY   rE   minutes_openprogress_pctnet_profit_usdFrV   partial_donepartial_pnl_usdcurrent_sl_pricersi50_partial_donelast_exit_eval_time r?   last_brain_actionlast_brain_reasonsl_price_ai_rawsl_ai_raw_hitc                    t        |       S )N)r   )r]   s    r   r_   zTradeRuntime.to_dict   s    d|r   c           
         | j                   D ch c]  }| }} | di |j                         D ci c]  \  }}||v s|| c}}S c c}w c c}}w )Nr   )__dataclass_fields__items)re   rf   fknownkvs         r   rg   zTradeRuntime.from_dict   sR      445q55Ctzz|Btq!qEzadBCC 6Bs   	A	AANrh   )rf   ra   ri   z'TradeRuntime')r   r   r   r0   rn   rj   ro   rp   rq   rr   rs   rt   ru   rw   rx   ry   rz   r_   rk   rg   r   r   r   rm   rm      s     L%L%NEL$ OU !e!$$!$$ss !OU M4 D Dr   rm   c                  0    e Zd ZU dZded<   ded<   ded<   y)	BrainContextuq  
    Bundle passed to Brain.manage_exit().

    Replaces V15's (tech, net_profit, trade_type, init_data) signature
    where init_data was a dict with hidden fallbacks (V15-BUG-3).

    entry: immutable snapshot — Brain compares "now vs entry" using this.
    runtime: current trade state.
    tech_now: TechSnapshot of current indicator state (typed dataclass —
              V15-BUG-3 fix at the indicator boundary). Annotated as
              `Any` here to avoid a circular import; concrete type is
              analysis.tech_snapshot.TechSnapshot. Brain code reads
              `ctx.tech_now.<field>` directly.
    r>   entryrm   runtimer   tech_nowN)r   r   r   r0   rj   r   r   r   r   r      s     Mr   r   c                  P    e Zd ZU dZded<   ded<   dZded<    ee      Zd	ed
<   y)BrainDecisionan  
    Brain's response to manage_exit().

    action: see TradeAction enum.
    reason: human-readable rationale (logged).
    move_sl_to: only set when action == MOVE_SL.
                metadata["sl_target"] should be "breakeven" or "trailing".
    metadata: free-form dict for Brain-specific signals (logged but
              not interpreted by orchestrator).
    r?   actionreasonNOptional[float]
move_sl_todefault_factoryra   metadata)	r   r   r   r0   rj   r   r   ra   r   r   r   r   r   r      s,    	 KK"&J&40Hd0r   r   c                  t    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<    ee      Zded<   y)EntryDecisionu  
    Brain's proposal to OPEN a new trade.
    None from evaluate_entry() means "no setup".

    TP variante γ:
      - Brain emits `rr_multiplier` (AI choice ∈ [0.17, 0.67]) and leaves
        `tp_price = 0.0` as sentinel "to be resolved post-sizing".
      - Orchestrator calls `resolve_tp_price(...)` after sizing/risk and
        replaces the decision via `dataclasses.replace(decision, tp_price=...)`.
      - The trade_opener still reads `decision.tp_price` as a finalized float;
        it never sees the 0.0 sentinel.
    r?   rB   rE   rF   rG   rH   rr_multiplierrC   
confidence	rationaler   ra   r   N)r   r   r   r0   rj   r   ra   r   r   r   r   r   r      s=     NOOON40Hd0r   r   c                  F    e Zd ZU dZded<   dZded<   dZded	<   dZd
ed<   y)EntryEvalResultuL  
    Outcome of Brain.evaluate_entry.

    decision:
        EntryDecision (open trade) or None (no setup / pre-val rejection
        / AI rejection / transient AI failure / candle gate skip).

    evaluated_candle_time:
        M5 candle timestamp (UTC unix seconds, float) on which the AI
        was called and either responded (any parse outcome) or returned
        a permanent error (credit/invalid). When set, the orchestrator
        updates SessionState.entry_eval_cache so subsequent iterations
        on the same candle skip the AI call. None when:
          - dedup short-circuited the AI call, OR
          - candle-state gate short-circuited (not_closed / stabilizing
            / too_old), OR
          - pre-val rejected before AI, OR
          - AI failed with a transient error (unknown/overload) — retry
            on next iteration.

    dedup_skipped:
        True when same-candle dedup short-circuited the AI call.
        Mutually exclusive with a non-None decision.

    reject_reason:
        Stable string code emitted by candle-state gates ("CANDLE_NOT_CLOSED"
        / "CANDLE_STABILIZING" / "CANDLE_TOO_OLD") and propagated by the
        orchestrator into brain_log.jsonl scan_skip events for forensics.
        None when not a candle-state skip (pre-val / AI / dedup paths use
        their own logging channels).
    zOptional['EntryDecision']decisionNr   evaluated_candle_timeFrV   dedup_skippedrR   reject_reason)r   r   r   r0   rj   r   r   r   r   r   r   r   r     s.    > ('-1?1M4#'M='r   r   c                 H    t        j                  t        j                        S )z-Single source of truth for timestamps in V16.)r   nowr   utcr   r   r   utc_nowr   C  s    <<%%r   N)ri   r   )r0   
__future__r   dataclassesr   r   r   r   r   enumr	   typingr
   r   r?   r   r   r   r!   r2   r:   r>   rm   r   r   r   r   r   r   r   r   <module>r      s  	 # 0 0 '   T 
T 
#t LsD L0c4 S$  $> > >J )D )D )D`   0 1 1 1* 1 1 16 #( #( #(T&r   