
    /j                         d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlmZ e
j                  j                  d e ee      j#                         j$                  j$                               ddlmZ de_        ddlmZmZmZmZmZmZ ddlmZm Z m!Z!m"Z"m#Z# ddlm$Z$ d	 Z% G d
 d      Z& G d d      Z' G d de      Z(ddZ)d Z*d Z+d Z,d Z-d Z.e/dk(  r& e0d        e,         e-         e.         e0d       yy)a(  
Phase C2b unit tests for TradeOpener.post_order_safety_check.

3 tests:
  1. orphan recovered with bracket re-attached -> stop_order_id and
     target_order_id populated, is_orphan_recovered=False, log warning
     "orphan_recovered_with_bracket_reattached".
  2. orphan recovered but SL re-attach fails -> stop_order_id=None,
     is_orphan_recovered=True, log critical "orphan_recovered_naked".
  3. no position present after sleep -> success=False, error_kind="not_found"

Run:
    cd ~/apex_v16
    python tests/test_trade_opener_safety_check.py
    )annotationsN)Pathg        )
BrokerBaseCancelResultClosedTradeOrderOrderResultPosition)	BrainName	DirectionEntryDecisionMarketStructureRegime)TradeOpenerc                     t        d|         y )Nz  ok  )print)labels    ;/home/work/apex_v16/tests/test_trade_opener_safety_check.py_okr   &   s    ug&'    c                      e Zd Zd Zd Zy)	JsonlSinkc                    g | _         y N)eventsselfs    r   __init__zJsonlSink.__init__.   s    br   c                B    | j                   j                  d|i|       y )Nevent)r   append)r   r    fieldss      r   writezJsonlSink.write/   s    dkk&8&8'59SF9S&Tr   N)__name__
__module____qualname__r   r#    r   r   r   r   -   s    (Tr   r   c                      e Zd Zd Zy)
FakeLoggerc                V    dd l }t               | _        |j                  d      | _        y )Nr   ztest.safety)loggingr   	brain_log	getLoggersystem)r   r+   s     r   r   zFakeLogger.__init__3   s     "''6r   N)r$   r%   r&   r   r'   r   r   r)   r)   2   s    7r   r)   c                      e Zd ZdZdZd Zd Zd Zd Zd Z	dd	Z
dd
ZddZd Zd Zd Zd Zd ZddZd Zd Zd Zd Zy)_Brokerz7In-memory BrokerBase. Configure positions + fail flags.
TestBrokerc                <    g | _         d| _        d| _        g | _        y )NF)	positionsfail_slfail_tpcallsr   s    r   r   z_Broker.__init__<   s    )+ "
r   c                   K   ywNTr'   r   s    r   connectz_Broker.connectB   s     D   c                   K   y wr   r'   r   s    r   
disconnectz_Broker.disconnectC   s     tr:   c                   K   ywr8   r'   r   s    r   is_connectedz_Broker.is_connectedD   s     r:   c                   K   yw)N     @r'   r   symbols     r   get_last_pricez_Broker.get_last_priceE   s     6r:   Nc                   K   | j                   j                  d       | j                  D cg c]  }||j                  |k(  s| c}S c c}w w)Npositions_get)r6   r!   r3   rB   )r   rB   ps      r   rE   z_Broker.positions_getF   s?     

/*>>TafnF@RTTTs   *AA
A
Ac                   K   g S wr   r'   rA   s     r   pending_ordersz_Broker.pending_ordersI   s
        c                   K   g S wr   r'   )r   rB   sincelimits       r   recent_tradesz_Broker.recent_tradesJ   s
     RirI   c                $   K   t        dd      S w)NFzsimulated bracket failuresuccesserrorr	   )r   kws     r   place_market_bracketz_Broker.place_market_bracketK   s     50KLLs   c                   K   | j                   j                  d       | j                  rt        dd      S t        d|dd      S w)Nplace_stop_orderFzSL re-attach failedrO   TSL-NEW-1)rP   sl_pricestop_identry_id)r6   r!   r4   r	   )r   rB   side	contracts
stop_prices        r   rV   z_Broker.place_stop_orderM   sF     

,-<<u4IJJ4*#-
D 	D   AAc                   K   | j                   j                  d       | j                  rt        dd      S t        d|dd      S w)Nplace_limit_orderFzTP re-attach failedrO   TTP-NEW-1)rP   tp_price	target_idrZ   )r6   r!   r5   r	   )r   rB   r[   r\   limit_prices        r   r`   z_Broker.place_limit_orderS   sF     

-.<<u4IJJ4+%/*F 	Fr^   c                "   K   t        d      S wNT)rP   )r   )r   rB   order_ids      r   cancel_orderz_Broker.cancel_orderY   s     <PT;U4U   c                   K   yw)Nr   r'   rA   s     r   cancel_all_for_symbolz_Broker.cancel_all_for_symbolZ   s     !r:   c                "   K   t        d      S wrf   rR   )r   rB   r\   s      r   close_positionz_Broker.close_position[   s     ;W[C\<\ri   c	                ,   K   t        dddd||      S w)NTzC-FAKEzS2-FAKEzT2-FAKE)rP   rZ   rY   rc   rX   rb   rR   )	r   rB   	directioncontracts_to_closeresidual_contractsnew_sl_pricenew_tp_priceold_stop_order_idold_target_order_ids	            r    partial_close_via_opposite_orderz(_Broker.partial_close_via_opposite_order\   s#      8Y)!L
 	
   c                "   K   t        d      S wrf   rR   )r   rB   rg   rr   s       r   modify_stopz_Broker.modify_stopd   s     4((ri   c                   K   yw)Ng     j@r'   r   s    r   get_account_balancez_Broker.get_account_balancef   s     r:   c                :   K   dd l }|j                  g d      S w)Nr   )openhighlowclosevolume)columns)pandas	DataFrame)r   rB   	timeframenpds        r   
fetch_barsz_Broker.fetch_barsg   s     ||$N|OOs   r   )NN2   )r$   r%   r&   __doc__namer   r9   r<   r>   rC   rE   rH   rM   rT   rV   r`   rh   rk   rm   rv   ry   r{   r   r'   r   r   r0   r0   9   s\    AD# )+-9U ;OMDF V;\
)7Pr   r0   c           	     &    t        d| ddddd      S )NBUYg     @g     @g      ?F   test)ro   entry_pricerX   rb   rr_multiplier
confidence	rationale)r   entrys    r   make_decisionr   l   s    UVfrV r   c                 v    ddddt         j                  j                  t        j                  j                  ddS )Ng     K@g      J@g      I@g      ?)rsirsi_h1rsi_h4	atr_ratiomarket_structureregimeh1_compatibility)r   BULLISH_EXPANSIONvaluer   TRENDINGr'   r   r   tech_nowr   s   s5    tt+==CC//'' r   c                ,    t        j                  |       S r   )asynciorun)coros    r   r   r   }   s    gkk$''r   c            
        t               } t        dddd      g| _        t               }t	        | d|      }t        |j                  dt        j                  j                  t        d	      dt               d
            }g }|j                  }|}|r|j                  }d}||u}	|	}|sLddt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      dz  }
|j'                  |
       |rt!        j(                  d	fdf      dt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      t!        j$                  |      dz  }dd|iz  }|j'                  |       t!        j*                  |d      i z  }dd|iz  }t-        t!        j.                  |            dx}x}x}x}x}	}|j                  }|j0                  }d}||u }|st!        j(                  d|fd||f      dt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      t!        j$                  |      t!        j$                  |      dz  }t!        j2                  d      dz   d|iz  }t-        t!        j.                  |            dx}x}x}}|j                  }|j4                  }d}||k(  }|st!        j(                  d|fd ||f      dt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      t!        j$                  |      t!        j$                  |      dz  }d!d|iz  }t-        t!        j.                  |            dx}x}x}}|j                  }|j6                  }d"}||k(  }|st!        j(                  d|fd#||f      dt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      t!        j$                  |      t!        j$                  |      dz  }d!d|iz  }t-        t!        j.                  |            dx}x}x}}|j                  }|j8                  }d}||k(  }|st!        j(                  d|fd$||f      dt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      t!        j$                  |      t!        j$                  |      dz  }t!        j2                  d%      dz   d|iz  }t-        t!        j.                  |            dx}x}x}}|j                  }|j:                  }d&}||z
  }t=        |      }	d'}|	|k  }|s_t!        j(                  d(|fd)|	|f      d*t        j                         v st!        j"                  t<              rt!        j$                  t<              nd*dt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      t!        j$                  |      t!        j$                  |      t!        j$                  |	      t!        j$                  |      d+z  }t!        j2                  d,|j                  j:                         d-z   d.|iz  }t-        t!        j.                  |            dx}x}x}x}x}	x}}d/ |j>                  j@                  D        }tC        |      }|sd0d1t        j                         v st!        j"                  tB              rt!        j$                  tB              nd1t!        j$                  |      t!        j$                  |      d2z  }
t-        t!        j.                  |
            dx}}tE        d3       y)4z@Position found + SL re-attach OK + TP re-attach OK -> not naked.MESr      g    @@rB   ro   r\   	avg_priceFis_paperloggerr@   r   zbracket exception)rB   
brain_namedecisionr\   r   error_contextN$%(py4)s
{%(py4)s = %(py2)s.success
}respy2py4is notz2%(py8)s
{%(py8)s = %(py6)s.entry
} is not %(py11)spy6py8py11%(py13)spy13r   assert %(py16)spy16iszV%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.entry
}.is_orphan_recovered
} is %(py7)spy0r   r   py7z/with bracket re-attached, NOT flagged as orphan
>assert %(py9)spy9rW   ==)zP%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.entry
}.stop_order_id
} == %(py7)szassert %(py9)sra   )zR%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.entry
}.target_order_id
} == %(py7)s)zN%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.entry
}.entry_price
} == %(py7)sz%entry_price reflects broker avg_priceg      ?g&.>)<)z%(py10)s
{%(py10)s = %(py0)s((%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.entry
}.orphan_entry_price_diff
} - %(py7)s))
} < %(py13)sabs)r   py1py3py5r   py10r   zorphan_entry_price_diff=z
>assert %(py15)spy15c              3  ,   K   | ]  }|d    dk(    yw)r    (orphan_recovered_with_bracket_reattachedNr'   .0es     r   	<genexpr>z@test_orphan_recovered_with_bracket_reattached.<locals>.<genexpr>   s"      1 zGG 1rw   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr   r   r   zForphan recovered + SL+TP re-attached: stop_id/target_id set, NOT naked)#r0   r
   r3   r)   r   r   post_order_safety_checkr   TFr   r   r   rP   r   @py_builtinslocals
@pytest_ar_should_repr_global_name	_safereprr!   _call_reprcompare_format_boolopAssertionError_format_explanationis_orphan_recovered_format_assertmsgstop_order_idtarget_order_idr   orphan_entry_price_diffr   r,   r   r   r   )brokerr   openerr   @py_assert1@py_assert3@py_assert0@py_assert7@py_assert10@py_assert9@py_format5@py_format12@py_format14@py_format15@py_format17@py_assert6@py_assert5@py_format8@py_format10@py_assert2@py_assert4@py_assert8@py_assert12@py_assert11@py_format16s                            r   -test_orphan_recovered_with_bracket_reattachedr     s+   YF g F \F%?F
f,,!3!3V,+> -  C
 13;;0;0;3990D09D000000030003000;0000;0009D000000300030009000D0000000000000099 :9(( :E :(E1 :(9(9:(E : :396: :!9!9  : :09	  : :09	  : :09	 ) : :09	 -2 : :(9(99: : :&9&9: : :9909""0j0"j0000"j000000300030009000"000j00000009929$$2
2$
2222$
222222322232229222$222
222222299T9  TGT G+TTT GTTTTTT3TTT3TTT9TTT TTTGTTT-TTTTTTTTyy Gy00 G4 G047 G378 G4 G84? G G5F5FG84 G G@FG G.F.F  G G=FY  G G@FG G.F.F  G G=FY  G G=FY  G G=FY 1 G G=FY 48 G G=FY 9 G G=FY <@ G G5F5F
"399#D#D"EFG G G3F3FG G G1((//1 13 1 1 1 1 1*0&1 100  1 1'0y  1 1'0y1 1 1'0y1 1 1 1001 1PQr   c            
     	   t               } t        dddd      g| _        d| _        t	               }t        | d|      }t        |j                  dt        j                  j                  t               dt               	            }g }|j                  }|}|r|j                  }d
}||u}	|	}|sLddt        j                          v st#        j$                  |      rt#        j&                  |      ndt#        j&                  |      dz  }
|j)                  |
       |rt#        j*                  d	fdf      dt        j                          v st#        j$                  |      rt#        j&                  |      ndt#        j&                  |      t#        j&                  |      dz  }dd|iz  }|j)                  |       t#        j,                  |d      i z  }dd|iz  }t/        t#        j0                  |            d
x}x}x}x}x}	}|j                  }|j2                  }d}||u }|st#        j*                  d|fd||f      dt        j                          v st#        j$                  |      rt#        j&                  |      ndt#        j&                  |      t#        j&                  |      t#        j&                  |      dz  }t#        j4                  d      dz   d|iz  }t/        t#        j0                  |            d
x}x}x}}|j                  }|j6                  }d
}||u }|st#        j*                  d|fd||f      dt        j                          v st#        j$                  |      rt#        j&                  |      ndt#        j&                  |      t#        j&                  |      t#        j&                  |      dz  }t#        j4                  d      dz   d|iz  }t/        t#        j0                  |            d
x}x}x}}d |j8                  j:                  D        }t=        |      }|sdd t        j                          v st#        j$                  t<              rt#        j&                  t<              nd t#        j&                  |      t#        j&                  |      d!z  }
t/        t#        j0                  |
            d
x}}t?        d"       y
)#zBPosition found + SL re-attach FAILS -> registered as naked orphan.r   r   r   r@   r   TFr   rB   r   r   r\   r   Nr   r   r   r   r   r   r   r   r   r   r   r   r   r   znaked orphan flag MUST be setr   r   )zP%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.entry
}.stop_order_id
} is %(py7)sznaked: no stop_order_idc              3  ,   K   | ]  }|d    dk(    yw)r    orphan_recovered_nakedNr'   r   s     r   r   zEtest_orphan_recovered_naked_when_sl_reattach_fails.<locals>.<genexpr>   s"      1 z55 1rw   r   r   r   zKorphan + SL re-attach fails: naked orphan, stop_order_id=None, log critical) r0   r
   r3   r4   r)   r   r   r   r   r   r   r   r   rP   r   r   r   r   r   r   r!   r   r   r   r   r   r   r   r,   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                      r   2test_orphan_recovered_naked_when_sl_reattach_failsr     s!   YF f F FN\F%?F
f,,!3!3A
 -  C 13;;0;0;3990D09D000000030003000;0000;0009D000000300030009000D0000000000000099Q9((QDQ(D0QQQ(DQQQQQQ3QQQ3QQQ9QQQ(QQQDQQQ2QQQQQQQQ99E9""EdE"d*EEE"dEEEEEE3EEE3EEE9EEE"EEEdEEE,EEEEEEEE1((//1 13 1 1 1 1 1*0&1 100  1 1'0y  1 1'0y1 1 1'0y1 1 1 1001 1UVr   c            
     J   t               } t               }t        | d|      }t        |j	                  dt
        j                  j                  t               dt                           }|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      t        j                   |      d	z  }d
d|iz  }t#        t        j$                  |            dx}x}}|j&                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      t        j                   |      d	z  }d
d|iz  }t#        t        j$                  |            dx}x}}d}	| j(                  }|	|v}
|
st        j                  d|
fd|	|f      t        j                   |	      dt        j                         v st        j                  |       rt        j                   |       ndt        j                   |      dz  }t        j*                  d      dz   d|iz  }t#        t        j$                  |            dx}	x}
}d |j,                  j.                  D        }t1        |      }|sddt        j                         v st        j                  t0              rt        j                   t0              ndt        j                   |      t        j                   |      dz  }t#        t        j$                  |            dx}}t3        d       y)zGNo position present after sleep -> success=False, error_kind=not_found.Fr   r   r   r  r   )z/%(py2)s
{%(py2)s = %(py0)s.success
} is %(py5)sr   )r   r   r   zassert %(py7)sr   N	not_foundr   )z2%(py2)s
{%(py2)s = %(py0)s.error_kind
} == %(py5)srV   )not in)z1%(py1)s not in %(py5)s
{%(py5)s = %(py3)s.calls
}r   )r   r   r   z&no position -> never attempt re-attachz
>assert %(py7)sc              3  ,   K   | ]  }|d    dk(    yw)r    safety_check_no_positionNr'   r   s     r   r   z<test_no_orphan_position_returns_not_found.<locals>.<genexpr>   s"      1 z77 1rw   r   r   r   zCno orphan position: success=False not_found, no re-attach attempted)r0   r)   r   r   r   r   r   r   r   r   rP   r   r   r   r   r   r   r   r   
error_kindr6   r   r,   r   r   r   )r   r   r   r   r   r   r   @py_format6r   r   r   r   s               r   )test_no_orphan_position_returns_not_foundr     s   YF\F%?F
f,,!3!3A
 -  C ;;%;%;%33;%>>([(>[((((>[((((((3(((3(((>((([((((((( 1V\\ 1\1 1001\ 1 1'0y  1 1*0&1 100 &, 1 1'0y &, 1 1'0y &2 1 10001 1 1001 11((//1 13 1 1 1 1 1*0&1 100  1 1'0y  1 1'0y1 1 1'0y1 1 1 1001 1MNr   __main__z!test_trade_opener_safety_check.pyzALL 3 TESTS PASSED)r@   )1r   
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   syspathlibr   pathinsertstr__file__resolveparenttrading.trade_openertrade_opener_opener_modPOST_ORDER_SAFETY_DELAY_SECONDSbroker.broker_baser   r   r   r   r	   r
   core.contractsr   r   r   r   r   r   r   r   r)   r0   r   r   r   r  r  r  r$   r   r'   r   r   <module>r%     s     #    
  3tH~--/66==> ? +.1 +   - (U U
7 70Pj 0Pf (R6W,O, z	
-.1368-/	
 r   