
    Զj3              
      
   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
mZ e	j                  j                  d e ee      j!                         j"                  j"                               ddlmZ dddd	dd
ddddddd
ddddddd
ddddddd
dgZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zedk(  rO e         e         e         e         e         e         e         e         e         e         e d       yy) z
Unit tests for dashboard_writer pure helpers: realized-events accounting,
asset_stats day filter, and the daily roll-up that feeds the chart.

Run:
    cd ~/apex_v16
    python -m pytest tests/test_dashboard_writer.py -q
    )annotationsN)Path)DashboardWriter2026-05-13T15:00:00+00:006Efull     j@   tssymbolkindbalance_postpartial_pnl_usd	contractsz2026-05-13T17:30:00+00:00g    q@z2026-05-14T09:30:00+00:00partial      D@z2026-05-14T10:15:00+00:006Bg     o@c                    t        j                  t              } | D cg c]  }|d   	 }}ddg}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}| d	   d
   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}| d	   d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}| d	   d   }d}||u }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}| d   d
   }d}| }||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}x}}| d   d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}| d   d   }d}||u }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}| D cg c]  }|d   	 }}ddg}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}t        d       y c c}w c c}w )Nr   r   r   ==z%(py1)s == %(py4)spy1py4assert %(py6)spy6r   pnl_usd      ^@r   winhas_partialFisz%(py1)s is %(py4)sr
         T@z%(py1)s == -%(py4)sassert %(py7)spy7losscumulative_pnlr   zMtrade_history emits one row per completed trade (in-flight partials excluded))	r   _trade_historyEVENTS
@pytest_ar_call_reprcompare	_safereprAssertionError_format_explanationprint)
rowsr@py_assert0@py_assert3@py_assert2@py_format5@py_format7@py_assert5@py_format6@py_format8s
             2/home/work/apex_v16/tests/test_dashboard_writer.py0test_trade_history_fuses_partial_with_full_closer>   +   s   
 ))&1D!%&AAhK&6&4,6&,6666&,666&666,666666679&&&&&&&&&&&&&&&&&&&76?#e#?e####?e###?###e#######7=!*U*!U****!U***!***U*******79&$&$&&&&&&&&&&&$&&&&&&&76?$f$?f$$$$?f$$$?$$$f$$$$$$$7=!*U*!U****!U***!***U*******)-.AA.?.5$-?.-????.-???.???-???????	
YZ ' /s   QQc                    t        j                  t              } t        |       }ddh}||k(  }|st	        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                  |      dz  }dd	|iz  }t        t	        j                  |            d x}x}}| d   d
   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}| d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}| d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}| d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}| d   d
   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}| d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}| d   d   }d}| }||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}x}}t        j                  t        d      }t        |      }dh}||k(  }|st	        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                  |      dz  }dd	|iz  }t        t	        j                  |            d x}x}}|d   d
   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   d   }d}| }||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}x}}t        j                  t        d      }i }||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }d d!|iz  }	t        t	        j                  |	            d x}}t        d"       y )#Nr   r   r   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sset	all_statspy0r   py3r   assert %(py8)spy8tradesr
   r   r   r   r   winslossesr   pnlr   r%   r&   r'   r(   
2026-05-14	ts_prefixtodayz
2026-05-15z%(py0)s == %(py3)semptyrD   rE   zassert %(py5)spy5zKasset_stats filters on the trade's full-close ts (in-flight trades omitted))r   _asset_statsr,   rA   r-   r.   @py_builtinslocals_should_repr_global_namer/   r0   r1   r2   )rB   r7   r:   @py_assert4r9   @py_format9r5   r6   r8   r;   r<   rO   rQ   @py_assert1@py_format4s                  r=   /test_asset_stats_today_filters_by_full_close_tsr\   ?   s%     ,,V4Iy>)dD\)>\))))>\))))))3)))3))))))y)))y)))>)))\)))))))T?8$))$))))$)))$))))))))))T?6"))"))))")))"))))))))))T?8$))$))))$)))$))))))))))T?5!--!----!---!----------T?8$))$))))$)))$))))))))))T?8$))$))))$)))$))))))))))T?5!---!----!---!---------- ((<HEu:$::33uu:;x %A% A%%%% A%%% %%%A%%%%%%%;x %A% A%%%% A%%% %%%A%%%%%%%;u)T)TE)E))))E))))))T))))))) ((<HE5B;5B55B	
WX    c                    t        j                  t              } t        j                  |       }|D cg c]  }|d   	 }}ddg}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d	   d
   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d	   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   d
   }d}| }||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}x}}|d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   d   }|d   d   }||k  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}t        d       y c c}w )Ndate
2026-05-13rL   r   r   r   r   r   r   pnl_dayr   r*   r
   r%   r&   r'   r(   r   cumulative_pnl_net)<)z%(py1)s < %(py4)sz?trade_history_daily rolls up per-trade rows into per-day points)
r   r+   r,   _trade_history_dailyr-   r.   r/   r0   r1   r2   )r3   dailydr5   r6   r7   r8   r9   r:   r;   r<   s              r=   ,test_trade_history_daily_buckets_by_utc_daterg   [   sd   ))&1D006E  %%!AfI%E%,)EE%)EEEEE%)EEEE%EEE)EEEEEEEE 8I'%'%''''%''''''%'''''''8$%..%....%...%.......... 8I'4'4%'%''''%''''''4'''''''8$%--%----%---%---------- 8()FE!H5E,FF),FFFFF),FFFF)FFF,FFFFFFFF	
KL &s   M$c                 .   t         j                  } g } | |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x} x}x}x}}t        d       y )Nr   )zZ%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s._trade_history_daily
}(%(py4)s)
} == %(py9)sr   )rD   py2r   r   py9zassert %(py11)spy11z-trade_history_daily on empty input returns [])r   rd   r-   r.   rU   rV   rW   r/   r0   r1   r2   )rZ   r6   r:   @py_assert8@py_assert7@py_format10@py_format12s          r=   +test_trade_history_daily_empty_when_no_rowsrp   p   s    //99/39r93r99993r999999?999?999/9999993999r9999999	
9:r]   c            	        ddddd ddddddd ddd	ddd
d ddg} t        j                  | d      }t        j                  | d      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}t        d       y )Nr   r   r   r	   r
   r   z2026-05-13T23:59:59+00:00     m@z2026-05-14T00:00:00+00:00g    `n@r`   rM   rL   rH   r   r   r   r   r   rK         I@g      4@rI   z5ts_prefix splits cleanly on the UTC-midnight boundary)r   rT   r-   r.   r/   r0   r1   r2   )events	yesterdayrO   r5   r6   r7   r8   r9   s           r=   9test_asset_stats_ts_prefix_respects_utc_midnight_boundaryrv   u   s,   
 +d q	2 +d q	2 +d q	2
F  ,,V|LI,,V|LE T?8$))$))))$)))$))))))))))T?5!)T)!T))))!T)))!)))T))))))) ;x %A% A%%%% A%%% %%%A%%%%%%%;u%%%%%%%%%%%%%%%%%%%;v#!#!####!######!#######	
ABr]   c            	     F
   ddddd dddddd d	ddd
dddd ddg} t        j                  | d      }|d   d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j
                  d      dz   d|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j
                  d      dz   d|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d	}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}t        j                  |       }t        |      }d}||k(  }	|	st        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                  |      dz  }dd |iz  }
t        t        j                  |
            d x}x}	}|d   d!   }d"}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d#   }d	}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d$   }d%}||u }|slt        j                  d&|fd'||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}t        d(       y ))N2026-05-14T08:00:00+00:00MGCr   r	   r
   r   2026-05-14T09:00:00+00:00r   rs   2026-05-14T10:00:00+00:00rr   rL   rM   rH   r   r   r   z%partial+BE must collapse into 1 trade
>assert %(py6)sr   rI   u   partial+BE total +$50 → WINrJ   r   r   rK   r@   lenr3   rC   rF   rG   r   r    r   r!   Tr"   r$   z=partial profit + BE residual aggregates to one WIN, not 1W+1S)r   rT   r-   r.   r/   _format_assertmsgr0   r1   r+   r}   rU   rV   rW   r2   )rt   statsr5   r6   r7   r8   r9   r3   r:   rX   rY   s              r=   /test_partial_plus_be_residual_counts_as_one_winr      ss    +e q	2 +eD q	2 +e q	2F ((<HE<!OQO!Q&OOO!QOOO!OOOQOOO(OOOOOOOO<GQGQ&GGGQGGGGGGQGGG(GGGGGGGG<!&Q&!Q&&&&!Q&&&!&&&Q&&&&&&&<)T)T))))T))))))T))))))) ))&1Dt99>933tt976?#e#?e####?e###?###e#######79%%%%%%%%%%%%%%%%%%%7=!)T)!T))))!T)))!)))T)))))))	
IJr]   c            
        ddddd dd ddddddd dd	d
dg} t        j                  |       }t        |      }d}||k(  }|st        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                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d	}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }t        t        j                  |            d x}x}}t        j                  | d      }
|
d   d   }d	}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }t        t        j                  |            d x}x}}|
d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }t        t        j                  |            d x}x}}t        d        y )!Nrx   r   r   r	   r
    r   r   r   r   r   r   
broker_pnl
pnl_sourcer{        R@brokerr   r@   r}   r3   rC   rF   rG   r   r   r   r   z*broker value must win over the stale deltar|   r   r   r    r   rL   rM   rK   rI   z@broker_pnl (pnl_source=broker) overrides the stale balance delta)r   r+   r}   r-   r.   rU   rV   rW   r/   r0   r1   r~   rT   r2   )rt   r3   r7   r:   rX   r9   rY   r5   r6   r8   r   s              r=   -test_broker_pnl_overrides_stale_balance_deltar      sW    +d q2	/ +d q8	5F ))&1Dt99>933tt979VV%VVVVVVVVVVVV*VVVVVVVV76?&&?&&&&?&&&?&&&&&&&&&&((<HE;u&$&$&&&&$&&&&&&$&&&&&&&;v#!#!####!######!#######	
LMr]   c                    ddddd dd ddddd	d d
ddddddddd dd ddg} t        j                  |       }t        |      }d}||k(  }|st        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                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }t        t        j                  |            d x}x}}|d   d    }d!}||u }|slt        j                  d"|fd#||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }t        t        j                  |            d x}x}}t        d$       y )%Nrx   r   r   r	   r
   r   r   rz   r   g     E@r   r   r{   g    n@r   r@   r}   r3   rC   rF   rG   r   r   r   r   z)broker $75 must override tick-math $43.75r|   r   r   r    r   r!   Tr"   r$   z6broker_pnl on partial overrides the closer's tick mathr   r+   r}   r-   r.   rU   rV   rW   r/   r0   r1   r~   r2   
rt   r3   r7   r:   rX   r9   rY   r5   r6   r8   s
             r=   +test_broker_pnl_overrides_partial_tick_mathr      s
    +d q2	/
 +dD!8	5
 +d q2	/F  ))&1Dt99>933tt979UU%UUUUUUUUUUUU*UUUUUUUU76?&&?&&&&?&&&?&&&&&&&&&&7=!)T)!T))))!T)))!)))T)))))))	
BCr]   c            
        ddddd dd dddddd	d dd
ddg} t        j                  |       }t        |      }d}||k(  }|st        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                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }t        t        j                  |            d x}x}}t        d       y )Nrx   r   r   r	   r
   r   r   r{   g    l@g      )r   r@   r}   r3   rC   rF   rG   r   r   r   r   r   z9tick-math discrepancy must NOT override the balance deltar|   r   z>non-broker pnl_discrepancy does not override the balance deltar   r   s
             r=   0test_broker_pnl_ignored_when_source_is_tick_mathr      sG    +d q2	/ +d qB	0F ))&1Dt99>933tt979bb%bbbbbbbbbbbb'bbbbbbbb	
JKr]   c                    ddddd dddddd d	ddg} t        j                  | d
      }i }||k(  }|st        j                  d|fd||f      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}}t        j                  |       }g }||k(  }|st        j                  d|fd||f      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}}t        d       y )Nrx   ry   r   r	   r
   r   rz   r   g      >@rL   rM   r   rP   r   rR   z1in-flight partial must not appear in closed statsz
>assert %(py5)srS   r3   z2in-flight partial must not appear in trade_historyzEin-flight partial without full close is excluded from completed stats)r   rT   r-   r.   rU   rV   rW   r/   r~   r0   r1   r+   r2   )rt   r   r7   rZ   r[   r;   r3   s          r=   5test_in_flight_partial_without_full_close_is_excludedr   	  s$   
 +e q	2 +eD q	2	F ((<HEK5B;KKK5BKKKKKK5KKK5KKKBKKKKKKKKKK))&1DK42:KKK42KKKKKK4KKK4KKK2KKKKKKKKKK	
QRr]   __main__z!ALL DASHBOARD_WRITER TESTS PASSED)!__doc__
__future__r   builtinsrU   _pytest.assertion.rewrite	assertionrewriter-   syspathlibr   pathinsertstr__file__resolveparentdashboard_writerr   r,   r>   r\   rg   rp   rv   r   r   r   r   r   __name__r2    r]   r=   <module>r      s;   #   
  3tH~--/66==> ? , '$Y1. '$Y1. '$1. '$Y1.
 [(Y8M*;
C: KFN<D:L2S( z463502/1=?3513/1469;	
-. r]   