
    iH                        U d Z ddlmZ ddlZddlZddlmZ ddlmZ ddl	m
Z
mZ ddlmZmZmZmZmZmZ  ej&                  d      Zd	Zd
ed<    ed       G d d             Z G d d      Zy)u  
APEX V16 — Trade opener.

SINGLE POINT for opening a trade. Replaces V15's 4 duplicated paths
(paper, live success, duplicate-recovered, orphan-recovered) — that
duplication was V15-BUG-1, the root cause of subtle drift between
paths (e.g., one path saved rsi_start, others saved different fields).

Responsibilities:
  - Take an EntryDecision (Brain output) + current tech snapshot
  - Optionally place real broker order (or simulate for paper)
  - Build immutable TradeEntry + initial TradeRuntime
  - Return result to orchestrator

NOT responsibilities:
  - Risk checks (caller already validated via risk_manager)
  - Sizing (caller already computed via sizing.compute_contracts)
  - State persistence (caller saves via state_store)
  - Logging the trade_opened event (caller logs after success)

Failure modes:
  - Returns TradeOpenResult.success=False with structured error.
  - NEVER raises on broker errors (those are wrapped).
  - DOES raise on programmer errors (missing required fields).
    )annotationsN)	dataclass)Optional)
BrokerBaseOrderResult)	BrainName	DirectionEntryDecision
TradeEntryTradeRuntimeutc_nowtrade_openerg       @floatPOST_ORDER_SAFETY_DELAY_SECONDST)frozenc                  T    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<   d	Zd
ed<   y)TradeOpenResultzm
    Result of open_trade(). Either trade is set (success), or
    error/error_kind populated (failure).
    boolsuccessNzOptional[TradeEntry]entryzOptional[TradeRuntime]runtime strerror
error_kind)	__name__
__module____qualname____doc____annotations__r   r   r   r        +/home/work/apex_v16/trading/trade_opener.pyr   r   :   s7     M"&E&&*G#*E3OJr"   r   c                      e Zd ZdZdddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZedd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	       Z		 d	 	 	 	 	 	 	 	 	 	 	 	 	 dd
Z
	 	 	 	 	 	 	 	 	 	 	 	 ddZddddZy)TradeOpenerz
    Builds TradeEntry + TradeRuntime from a Brain EntryDecision.
    Optionally places broker order (live mode) or skips it (paper mode).

    Single-instance: typically held by orchestrator, instantiated once
    with broker + logger references.
    Nc                .    || _         || _        || _        y)z
        Args:
            broker: BrokerBase instance (used only in live mode)
            is_paper: True = simulation only, no broker call
            logger: LoggerBundle (optional but recommended)
        N)brokeris_paperlogger)selfr'   r(   r)   s       r#   __init__zTradeOpener.__init__T   s      r"   c                  K   |dk  rt        d|       |j                  j                         dvrt        d|j                        |j                  dk  s|j                  dk  s|j
                  dk  r2t        d|j                   d|j                   d|j
                         | j                  r| j                  |||||      S | j                  |||||       d{   S 7 w)	a"  
        Open a trade based on Brain decision.

        Args:
            symbol: e.g. "MES", "6B"
            brain_name: BrainName.TF.value or BrainName.MR.value
            decision: EntryDecision from Brain (direction, prices, confidence)
            contracts: from sizing.compute_contracts (already validated)
            tech_now: current tech indicators (used for entry snapshot)

        Returns:
            TradeOpenResult.success=True with entry+runtime on success,
            success=False with structured error on failure.
        r   zcontracts must be > 0, got )BUYSELLzinvalid direction: zprices must be > 0: entry=z, sl=z, tp=N)	
ValueError	directionupperentry_pricesl_pricetp_pricer(   _open_paper
_open_live)r*   symbol
brain_namedecision	contractstech_nows         r#   
open_tradezTradeOpener.open_tradec   s     . >:9+FGG##%_<283E3E2HIJJ1$(9(9Q(>(BSBSWXBX,X-A-A,B C''(h.?.?-@B  ==##FJ)XVVXyRZ[[[[s   C C)"C'#C)c                    | j                  ||||||j                  |j                  |j                  ddddd      }t	               }t        d||      S )z{
        Paper: no broker call, just build TradeEntry as if it filled
        immediately at decision.entry_price.
        NTFr7   r8   r9   r:   r;   actual_entry_priceactual_sl_priceactual_tp_priceentry_order_idstop_order_idtarget_order_idr(   is_orphan_recoveredr   r   r   )_build_trade_entryr2   r3   r4   r   r   )r*   r7   r8   r9   r:   r;   r   r   s           r#   r5   zTradeOpener._open_paper   sj     ''!'33$--$--  % ( 
 .t5'JJr"   c                  K   	 | j                   j                  |       d{   }|r:|d   }t        dd|j                   d|j
                   d	|j                   d
      S 	 | j                   j                  ||j                  ||j                  |j                         d{   }	|	j                  st        d|	j                  xs dd      S |	j                  xs |j                  }
|	j                  xs |j                  }|	j                  xs |j                  }| j                  ||||||
|||	j                  |	j                  |	j                   dd      }t#               }t        d||      S 7 R# t        $ r}t        dd| d      cY d}~S d}~ww xY w7 # t        $ r}t        dd| d      cY d}~S d}~ww xY ww)a  
        Live: place market bracket via broker, then build TradeEntry
        from the actual filled prices and order IDs.

        Pre-check: if a position already exists at broker for this
        symbol, return duplicate error (orchestrator decides what to do).
        NFzpositions_get failed: broker_failurer   r   r   r   z#position already exists at broker:  zct @ 	duplicate)r7   r0   r:   r3   r4   z place_market_bracket exception: z'place_market_bracket failed (no detail)r>   TrF   )r'   positions_get	Exceptionr   r0   r:   	avg_priceplace_market_bracketr3   r4   r   r   r2   rG   entry_idstop_id	target_idr   )r*   r7   r8   r9   r:   r;   existingeposorder_resultactual_entry	actual_sl	actual_tpr   r   s                  r#   r6   zTradeOpener._open_live   s     	![[66v>>H  1+C"9}}oQs}}oU3==/K ' 	.2kk.N.N",,#!**!** /O / )L ##""((U,U+  $//G83G3G ))>X->->	 ))>X->->	''!+%%'00&..(22 % ( 
 .t5'JJE ? 	".qc2+ 	.)  	"8<+ 	s   GE7 E4E7 <G$AF  %F&F  *C
G4E7 7	F FFGFGF   	G)F?9G:G?GGg        )orphan_entry_price_diffc                \   t        d!i d| d|d|j                  j                         d|d|d|d|dt               d	t	        |j                  d
d            dt	        |j                  dd            dt	        |j                  dd            dt	        |j                  dd            dt        |j                  dd            dt        |j                  dd            dt	        |j                  dd            dt        |j                        d|d|	d|
d|d|d |S )"uK  
        SINGLE point of TradeEntry construction. All paths funnel here.
        Snapshot every relevant tech indicator from tech_now.

        This eliminates V15-BUG-5 (rsi_start saved by some paths but not
        propagated to Brain) — V16's TradeEntry carries every snapshot
        field that any Brain might need.
        r7   r8   r0   r:   r2   r3   r4   	opened_atrsi_m5_at_entryrsig      I@rsi_h1_at_entryrsi_h1rsi_h4_at_entryrsi_h4atr_ratio_at_entry	atr_ratiog      ?market_structure_at_entrymarket_structureRANGINGregime_at_entryregimeh1_compat_at_entryh1_compatibilityconfidence_at_entryrB   rC   rD   r(   rE   r[   r!   )	r   r0   r1   r   r   getr   int
confidencer7   r8   r9   r:   r;   r?   r@   rA   rB   rC   rD   r(   rE   r[   s                 r#   rG   zTradeOpener._build_trade_entry	  sy   4  

 "
 ((..0	

  
 +
 %
 %
 i
 "(,,ud";<
 "(,,x">?
 "(,,x">?
   %X\\+s%CD!
" '*(,,7I9*U&V#
$  Xy AB%
&  %X\\2Dc%JK'
( !$H$7$7 8)
, *-
. (/
0 ,1
4 5
6 !47
8 %<9
 	
r"   c                P  K   t        j                  t               d{    	 | j                  j	                  |       d{   }|s%| j                  d||dd        t        dd	d
      S |d   }	t        |	j                        }
t        |j                        }|
|z
  }t        |	j                        j                         dk(  rdnd}| j                  ||t        |	j                         |j"                  |j$                         d{   \  }}}|duxr |du}| }| j'                  |||t        |	j                         ||
|j"                  |j$                  d||d||      }t)               }|j"                  |_        |r2| j                  dd||
||t        |	j                         |||dd 
       n0| j                  dd||
||t        |	j                         ||dd 	       t        d||      S 7 7 # t
        $ r}t        dd| d      cY d}~S d}~ww xY w7 w)u  
        Called after place_market_bracket has raised or returned failure.
        Determines whether the position actually opened broker-side
        despite the failure (SDK timeout / WS race / partial response)
        and brings it under V16 management if so.

        V15 parity: _post_order_safety_check (riga 1828-1900) — sleep,
        positions_get, register orphan with stop_id=None.

        V16 IMPROVEMENT: when a position is found, attempt SL/TP re-attach
        via place_stop_order + place_limit_order BEFORE registering. If
        both succeed, the orphan trade is registered with valid order IDs
        and is_orphan_recovered=False (not naked). If either re-attach
        fails, fall back to V15 behavior (is_orphan_recovered=True,
        stop_order_id=None) and emit a critical-level log so the operator
        flattens manually or the watchdog catches it.

        Returns:
          - success=True with entry+runtime if a position was found and
            registered (with or without bracket re-attach).
          - success=False, error_kind="not_found" if no orphan exists.
          - success=False, error_kind="broker_failure" if positions_get
            itself raised (degraded mode — caller can retry next tick).
        NFz*positions_get failed during safety check: rI   rJ   safety_check_no_position   )r7   error_contextzno orphan position found	not_foundr   r-   r.   )r7   protective_sider:   r3   r4   rq   (orphan_recovered_with_bracket_reattachedwarning)	levelr7   r?   intended_entry_priceentry_price_diffr:   rR   rS   ru   orphan_recovered_nakedcritical)rz   r7   r?   r{   r|   r:   reattach_errorru   TrF   )asynciosleepr   r'   rM   rN   r   _log_safetyr   rO   r2   r   r0   r1   _reattach_protectionro   r:   r3   r4   rG   r   current_sl_price)r*   r7   r8   r9   r:   r;   ru   	positionsrU   rV   rX   intendeddiffrw   sl_idtp_idr   bracket_attached	is_orphanr   r   s                        r#   post_order_safety_checkz#TradeOpener.post_order_safety_checkF  sm    B mm;<<<	"kk77??I 7+8#+>  @"0&  lS]]+--.h&$'$6$<$<$>%$G&U-1-F-F+#--(&&&& .G .
 (
$un "-FE4E((	''!#--(+$--$--! )$( ( 
  .#+#4#4 :#/%-!%cmm,+DS1   ( #/%-!%cmm,-+DS1  
 t5'JJe 	= @ 	"B1#F+ 	,(
s^   H&G6H&G< G9G< CH&H#C)H&9G< <	H HH H&H  H&c                  K   d}d}g }	 | j                   j                  ||||       d{   }	|	j                  r|	j                  xs |	j                  }n|j                  d|	j                          	 | j                   j                  ||||       d{   }|j                  r|j                  xs |j                  }n|j                  d|j                          ||dj                  |      fS 7 # t        $ r}
|j                  d|
        Y d}
~
d}
~
ww xY w7 # t        $ r}
|j                  d|
        Y d}
~
dd}
~
ww xY ww)u#  
        Attempt to attach SL + TP to an orphan position. Returns
        (sl_id_or_None, tp_id_or_None, error_summary). If SL fails, TP
        is still attempted so a naked orphan keeps at least the TP if
        achievable — V15 behavior in place_market_bracket riga 1213-1216.
        Nzsl: zsl raised: ztp: ztp raised: z; )r'   place_stop_orderr   rR   rQ   appendr   rN   place_limit_orderrS   join)r*   r7   rw   r:   r3   r4   r   r   errorssl_resrU   tp_ress               r#   r   z TradeOpener._reattach_protection  s<      $#		-;;77H F ~~9&//V\\N34		-;;88H F ~~((;FOOV\\N34 eTYYv...+  	-MMKs+,,	-  	-MMKs+,,	-sv   E!C7 C5A	C7 6!D# D!A	D# !E5C7 7	D DEDE!D# #	E
,E EE

Einfo)rz   c                  | j                   	  | j                   j                  j                  |fi | 	 |dk(  r(| j                   j                  j                  d||       nV|dk(  r(| j                   j                  j                  d||       n(| j                   j                  j                  d||       y y y  t        t        |dk7  r|nd      d||       y # t        $ r Y w xY w# t        $ r Y y w xY w)Nr~   z[safety] %s %sry   )
r)   	brain_logwriterN   systemr~   ry   r   getattrlog)r*   eventrz   fieldss       r#   r   zTradeOpener._log_safety  s    ;;"+%%++E<V<J&KK&&//0@%Pi'KK&&../?OKK&&++,<eVL P Q GGC%:"5:F %    s#   'C BC) 	C&%C&)	C54C5)N)r'   r   r(   r   returnNone)r7   r   r8   r   r9   r
   r:   ro   r;   dictr   r   )r7   r   r8   r   r9   r
   r:   ro   r;   r   r?   r   r@   r   rA   r   rB   Optional[str]rC   r   rD   r   r(   r   rE   r   r[   r   r   r   )r   )r7   r   r8   r   r9   r
   r:   ro   r;   r   ru   r   r   r   )r7   r   rw   r   r:   ro   r3   r   r4   r   r   z(tuple[Optional[str], Optional[str], str])r   r   rz   r   r   r   )r   r   r   r   r+   r<   r5   r6   staticmethodrG   r   r   r   r!   r"   r#   r%   r%   K   s?   	%\%\ %\  	%\
 %\ %\ 
%\VKK K  	K
 K K 
KDSKSK SK  	SK
 SK SK 
SKr   *-6
6
 6
  	6

 6
 6
 "6
 6
 6
 &6
 %6
 '6
 6
 "6
 "'6
  
!6
 6
F  sKsK sK  	sK
 sK sK sK 
sKj'/'/ '/ 	'/
 '/ '/ 
2'/R 7= r"   r%   )r   
__future__r   r   loggingdataclassesr   typingr   broker.broker_baser   r   core.contractsr   r	   r
   r   r   r   	getLoggerr   r   r    r   r%   r!   r"   r#   <module>r      ss   4 #   !  6  g' *-  , $	 	 	 k kr"   