""" Tests for sim/reporter.py — PnL breakdown and stats. """ from sim.reporter import PnLReporter, PnLBreakdown, SimulationStats class TestPnlReporter: def test_initial_equity(self): r = PnLReporter(initial_equity=10000.0) assert r.net_pnl() == 0.0 def test_maker_fill_bid(self): r = PnLReporter(initial_equity=10000.0) size = 0.001 fee = size * 50000.0 * 0.0002 # 0.01 r.record_maker_fill(side="bid", price=50000.0, size=size, mid_price=50001.0, fee=fee) bd = r.breakdown() assert bd.spread_capture > 0 # bought below mid assert bd.maker_fees < 0 def test_maker_fill_ask(self): r = PnLReporter(initial_equity=10000.0) size = 0.001 fee = size * 50001.0 * 0.0002 r.record_maker_fill(side="ask", price=50001.0, size=size, mid_price=50000.0, fee=fee) bd = r.breakdown() assert bd.spread_capture > 0 # sold above mid def test_toxic_fill(self): r = PnLReporter(initial_equity=10000.0) size = 0.001 fee = size * 50000.0 * 0.0002 r.record_maker_fill(side="bid", price=50000.0, size=size, mid_price=50000.5, fee=fee, is_toxic=True) bd = r.breakdown() assert bd.adverse_selection_cost > 0 def test_inventory_tracking(self): r = PnLReporter(initial_equity=10000.0) r.record_maker_fill(side="bid", price=50000.0, size=0.002, mid_price=50001.0, fee=1.0) assert r.position == 0.002 r.record_maker_fill(side="ask", price=50001.0, size=0.002, mid_price=50000.0, fee=1.0) assert abs(r.position) < 1e-10 def test_equity_curve(self): r = PnLReporter(initial_equity=10000.0) r.record_equity_update(sim_time=1.0, mid_price=100000.0) curve = r.equity_curve assert len(curve) >= 2 assert curve[0]["v"] == 10000.0 def test_max_drawdown(self): r = PnLReporter(initial_equity=10000.0) r.record_maker_fill(side="bid", price=100000.0, size=0.001, mid_price=99900.0, fee=1.0) # loss assert r.stats().max_drawdown > 0 def test_stats_includes_all_components(self): r = PnLReporter(initial_equity=10000.0) r.record_maker_fill(side="bid", price=50000.0, size=0.001, mid_price=50001.0, fee=10.0) r.record_maker_fill(side="ask", price=50001.0, size=0.001, mid_price=50000.0, fee=10.0) r.record_spread(1.5) r.record_latency(25.0) stats = r.stats() assert stats.total_trades == 2 assert stats.pnl.spread_capture > 0 assert stats.pnl.net_pnl != 0 assert stats.avg_spread_bps > 0 def test_breakdown_all_fields(self): r = PnLReporter(initial_equity=10000.0) bd = r.breakdown() assert bd.spread_capture == 0 assert bd.inventory_pnl == 0 assert bd.maker_fees == 0 assert bd.taker_fees == 0 assert bd.net_pnl == 0 def test_cancel_counting(self): r = PnLReporter(initial_equity=10000.0) r.record_cancel() r.record_cancel() assert r.stats().cancels == 2 def test_downtime(self): r = PnLReporter(initial_equity=10000.0) r.record_downtime(60.0) r.record_equity_update(sim_time=100.0, mid_price=100000.0) stats = r.stats() assert stats.uptime_pct < 100.0