
    +j$                        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
mZmZmZmZ  ej                  d      Z G d d	e	      Zy)
u~  
APEX V16 — DryRunBroker.

Composable wrapper that turns any BrokerBase implementation into a
"dry run" broker: real market data, real account balance, real positions
— but every WRITE method is a no-op that logs the intent and returns a
synthetic OrderResult. Useful for pre-LIVE validation against real
Topstep data without putting capital at risk.

Usage:
    real = TopstepXBroker(instruments=[...])
    await _connect_with_retry(real, ...)
    broker = DryRunBroker(real, logger=logger)   # writes intercepted
    # broker is a drop-in BrokerBase

Method classification (this is the contract):

  READ / LIFECYCLE — DELEGATED to wrapped broker:
    connect            # wrapped.connect — real WebSocket session
    disconnect         # MUST delegate. Otherwise zombie WS at shutdown.
    is_connected       # real connection state
    get_last_price     # real market price
    positions_get      # real broker-side positions
    pending_orders     # real broker-side pending orders (Reconciler)
    recent_trades      # real broker-side trade history (Reconciler case ii)
    get_account_balance# real account equity
    fetch_bars         # real OHLCV bars

  WRITE — INTERCEPTED with no-op + structured log + synthetic success:
    place_market_bracket
    place_stop_order      # used by orphan SL re-attach (C2b)
    place_limit_order     # used by orphan TP re-attach (C2b)
    cancel_order
    cancel_all_for_symbol
    close_position
    modify_stop

The synthetic OrderResult includes "DRY-..." order IDs so downstream
state mutations are tracked and the JSONL forensics show exactly which
trades would have happened on a real run.

NOTE on the disconnect classification (explicit per reviewer ask):
disconnect is LIFECYCLE not WRITE. Intercepting it would leave the
underlying WebSocket alive after shutdown, leaking a connection per
session. Real shutdown of the upstream socket is required.
    )annotationsN)datetime)Optional)
BrokerBaseCancelResultClosedTradeOrderOrderResultPositiondry_run_brokerc                  v   e Zd ZdZdZdddZddZddZddZddZ	ddd	Z
dd d
Z	 	 	 d!	 	 	 	 	 	 	 d"dZd#dZd$dZddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d%dZ	 	 	 	 	 	 	 	 	 	 d&dZ	 	 	 	 	 	 	 	 	 	 d'dZd(dZd)dZ	 d	 	 	 	 	 d*dZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d+dZ	 	 	 	 	 	 	 	 d,dZed-d       Zd.dZy)/DryRunBrokerz>BrokerBase wrapper. See module docstring for read/write split.DryRunNc                     || _         || _        y)z
        Args:
            wrapped: a connected (or about-to-connect) real BrokerBase.
            logger:  LoggerBundle. If provided, write-method intercepts
                     emit structured events to brain_log.
        N)_wrapped_logger)selfwrappedloggers      ,/home/work/apex_v16/broker/dry_run_broker.py__init__zDryRunBroker.__init__D   s          c                R   K   | j                   j                          d {   S 7 wN)r   connectr   s    r   r   zDryRunBroker.connectR   s     ]]**,,,,   '%'c                T   K   | j                   j                          d {    y 7 wr   )r   
disconnectr   s    r   r   zDryRunBroker.disconnectU   s     mm&&(((s   (&(c                R   K   | j                   j                          d {   S 7 wr   )r   is_connectedr   s    r   r!   zDryRunBroker.is_connectedY   s     ]]//1111r   c                T   K   | j                   j                  |       d {   S 7 wr   )r   get_last_pricer   symbols     r   r#   zDryRunBroker.get_last_price\   !     ]]11&9999   (&(c                T   K   | j                   j                  |       d {   S 7 wr   )r   positions_getr$   s     r   r)   zDryRunBroker.positions_get_   s!     ]]008888r'   c                T   K   | j                   j                  |       d {   S 7 wr   )r   pending_ordersr$   s     r   r+   zDryRunBroker.pending_ordersb   r&   r'   c                X   K   | j                   j                  |||       d {   S 7 wr   )r   recent_trades)r   r%   sincelimits       r   r-   zDryRunBroker.recent_tradese   s'      ]]00FFFF   !*(*c                R   K   | j                   j                          d {   S 7 wr   )r   get_account_balancer   s    r   r2   z DryRunBroker.get_account_balancem   s     ]]668888r   c                X   K   | j                   j                  |||       d {   S 7 wr   )r   
fetch_bars)r   r%   	timeframens       r   r4   zDryRunBroker.fetch_barsp   s%     ]]--fiCCCCr0   ATR)sl_absolute_pricesl_tickstp_ticks	sl_sourcec               $  K   d }
	 | j                   j                  |       d {   }
| j                  d      }| j	                  d||||||	|d   |d   |d   
       t        d|
|
nd|||d   |d   |d   	      S 7 X# t        $ r d }
Y cw xY ww)
Nplacedry_place_market_bracketr         )	r%   	direction	contractssl_pricetp_pricer;   synthetic_entry_idsynthetic_stop_idsynthetic_target_idTg        )successentry_pricerC   rD   entry_idstop_id	target_id)r   r#   	Exception_dry_ids_log_dryr
   )r   r%   rA   rB   rC   rD   r8   r9   r:   r;   entry_pxidss               r   place_market_bracketz!DryRunBroker.place_market_bracketw   s      %)	!]]99&AAH mmG$&Y)I"1v!!f #A 	 	
 $,$8cVF!f
 	
 B 	H	s8   BA? A=A? AB=A? ?B
BBBc                   K   dt        j                         j                  d d  }| j                  d|||||       t	        d|||      S w)NzDRY-attach-S-   dry_place_stop_order)r%   siderB   
stop_pricerF   T)rH   rC   rK   rJ   uuiduuid4hexrO   r
   )r   r%   rV   rB   rW   oids         r   place_stop_orderzDryRunBroker.place_stop_order   s`      djjl..r234"	j! 	 	

 :sS
 	
   AAc                   K   dt        j                         j                  d d  }| j                  d|||||       t	        d|||      S w)NzDRY-attach-T-rT   dry_place_limit_order)r%   rV   rB   limit_pricerG   T)rH   rD   rL   rJ   rX   )r   r%   rV   rB   ra   r\   s         r   place_limit_orderzDryRunBroker.place_limit_order   s`      djjl..r234#	{ # 	 	

 ;#
 	
r^   c                ^   K   | j                  d||       t        dt        |            S w)Ndry_cancel_order)r%   order_idT)rH   re   )rO   r   str)r   r%   re   s      r   cancel_orderzDryRunBroker.cancel_order   s*     ((KD3x=AAs   +-c                2   K   | j                  d|       yw)Ndry_cancel_all_for_symbol)r%   r   )rO   r$   s     r   cancel_all_for_symbolz"DryRunBroker.cancel_all_for_symbol   s     1&As   c                J   K   | j                  d||       t        d      S w)Ndry_close_position)r%   rB   T)rH   )rO   r
   )r   r%   rB   s      r   close_positionzDryRunBroker.close_position   s.     
 	 Y 	 	
 4((s   !#c	                   K   | j                  d      \  }	}
}| j                  d||||||||	       t        ddt        j                         j
                  d d  |
|||      S w)NPARTIAL$dry_partial_close_via_opposite_order)r%   rA   contracts_to_closeresidual_contractsnew_sl_pricenew_tp_priceold_stop
old_targetTzDRY-PARTIAL-CLOSE-rT   )rH   rJ   rK   rL   rC   rD   )rN   rO   r
   rY   rZ   r[   )r   r%   rA   rq   rr   rs   rt   old_stop_order_idold_target_order_id_sts               r    partial_close_via_opposite_orderz-DryRunBroker.partial_close_via_opposite_order   s      --	*1a2Y11%L&3F 	 	
 )$**,*:*:2A*>)?@!L	
 	
s   A#A%c                b   K   | j                  d|||       t        d|t        |            S w)Ndry_modify_stop)r%   re   rs   T)rH   rC   rK   )rO   r
   rf   )r   r%   re   rs   s       r   modify_stopzDryRunBroker.modify_stop   s=      	H< 	 	
 <X
 	
s   -/c                r    t        j                         j                  d d }d|  d| d|  d| d|  d| fS )NrT   zDRY-z-E-z-S-z-T-)rY   rZ   r[   )prefixtokens     r   rN   zDryRunBroker._dry_ids  sT    

  !$6(#eW%6(#eW%6(#eW%
 	
r   c                    | j                   )	  | j                   j                  j                  |fi | y t        j                  d| j                  ||       y # t        $ r Y y w xY w)Nz
[%s] %s %s)r   	brain_logwriterM   loginfoname)r   eventfieldss      r   rO   zDryRunBroker._log_dry
  s\    <<#,&&,,U=f= HH\499eV<  s   'A 	A%$A%r   )r   r   returnNone)r   bool)r   r   )r%   rf   r   Optional[float])r%   Optional[str]r   zlist[Position])r%   r   r   zlist[Order])NN2   )r%   r   r.   zOptional[datetime]r/   intr   zlist[ClosedTrade])r   float)r%   rf   r5   rf   r6   r   )r%   rf   rA   rf   rB   r   rC   r   rD   r   r8   r   r9   Optional[int]r:   r   r;   rf   r   r
   )
r%   rf   rV   rf   rB   r   rW   r   r   r
   )
r%   rf   rV   rf   rB   r   ra   r   r   r
   )r%   rf   re   rf   r   r   )r%   rf   r   r   )r%   rf   rB   r   r   r
   )r%   rf   rA   rf   rq   r   rr   r   rs   r   rt   r   rw   r   rx   r   r   r
   )r%   rf   re   rf   rs   r   r   r
   )r   rf   r   ztuple[str, str, str])r   rf   r   r   )__name__
__module____qualname____doc__r   r   r   r   r!   r#   r)   r+   r-   r2   r4   rR   r]   rb   rg   rj   rm   r|   r   staticmethodrN   rO    r   r   r   r   ?   s%   HD-)2:9:
 !%$(	GG "G 	G
 
G9D .2"&"&&
&
 &
 	&

 &
 &
 +&
  &
  &
 &
 
&
P

 
 	

 
 

"

 
 	

 
 

"B $(	)	) !	) 
		)

 
  	

  
 
 
 )
 +
 

6

 
 	

 

$ 
 
=r   r   )r   
__future__r   loggingrY   r   typingr   broker.broker_baser   r   r   r	   r
   r   	getLoggerr   r   r   r   r   <module>r      sI   -^ #     
 g()R=: R=r   