From 5cee0686e421b1f21484c23e413692616e9e2ffa Mon Sep 17 00:00:00 2001 From: TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> Date: Wed, 1 Apr 2026 18:25:29 +0900 Subject: feat(backtester): Phase 1 complete — realistic backtesting engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Slippage modeling (configurable per-trade, buy higher/sell lower) - Trading fee deduction (maker/taker configurable) - Stop-loss and take-profit auto-execution per position - Short selling support (allow_short flag) - Walk-forward analysis engine (in-sample/out-of-sample, efficiency ratio) - Daily equity curve Sharpe/Sortino with risk-free rate adjustment - Recovery factor, consecutive win/loss streaks, fee-aware PnL - 312 tests passing --- services/backtester/tests/test_metrics.py | 68 ++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 11 deletions(-) (limited to 'services/backtester/tests/test_metrics.py') diff --git a/services/backtester/tests/test_metrics.py b/services/backtester/tests/test_metrics.py index 34314b3..582309a 100644 --- a/services/backtester/tests/test_metrics.py +++ b/services/backtester/tests/test_metrics.py @@ -111,11 +111,11 @@ def test_consecutive_losses(): """Consecutive loss tracking should count streaks correctly.""" trades = [ _make_trade("BUY", "100", 0), - _make_trade("SELL", "110", 10), # win + _make_trade("SELL", "110", 10), # win _make_trade("BUY", "110", 20), - _make_trade("SELL", "105", 30), # loss + _make_trade("SELL", "105", 30), # loss _make_trade("BUY", "105", 40), - _make_trade("SELL", "100", 50), # loss + _make_trade("SELL", "100", 50), # loss ] metrics = compute_detailed_metrics(trades, Decimal("10000"), Decimal("10005")) assert metrics.max_consecutive_losses >= 1 @@ -126,12 +126,44 @@ def test_risk_free_rate_affects_sharpe(): """Higher risk-free rate should lower Sharpe ratio.""" base = datetime(2025, 1, 1, tzinfo=timezone.utc) trades = [ - TradeRecord(time=base, symbol="BTCUSDT", side="BUY", price=Decimal("100"), quantity=Decimal("1")), - TradeRecord(time=base + timedelta(days=1), symbol="BTCUSDT", side="SELL", price=Decimal("110"), quantity=Decimal("1")), - TradeRecord(time=base + timedelta(days=2), symbol="BTCUSDT", side="BUY", price=Decimal("105"), quantity=Decimal("1")), - TradeRecord(time=base + timedelta(days=3), symbol="BTCUSDT", side="SELL", price=Decimal("115"), quantity=Decimal("1")), - TradeRecord(time=base + timedelta(days=4), symbol="BTCUSDT", side="BUY", price=Decimal("110"), quantity=Decimal("1")), - TradeRecord(time=base + timedelta(days=5), symbol="BTCUSDT", side="SELL", price=Decimal("108"), quantity=Decimal("1")), + TradeRecord( + time=base, symbol="BTCUSDT", side="BUY", price=Decimal("100"), quantity=Decimal("1") + ), + TradeRecord( + time=base + timedelta(days=1), + symbol="BTCUSDT", + side="SELL", + price=Decimal("110"), + quantity=Decimal("1"), + ), + TradeRecord( + time=base + timedelta(days=2), + symbol="BTCUSDT", + side="BUY", + price=Decimal("105"), + quantity=Decimal("1"), + ), + TradeRecord( + time=base + timedelta(days=3), + symbol="BTCUSDT", + side="SELL", + price=Decimal("115"), + quantity=Decimal("1"), + ), + TradeRecord( + time=base + timedelta(days=4), + symbol="BTCUSDT", + side="BUY", + price=Decimal("110"), + quantity=Decimal("1"), + ), + TradeRecord( + time=base + timedelta(days=5), + symbol="BTCUSDT", + side="SELL", + price=Decimal("108"), + quantity=Decimal("1"), + ), ] m1 = compute_detailed_metrics(trades, Decimal("10000"), Decimal("10018"), risk_free_rate=0.0) m2 = compute_detailed_metrics(trades, Decimal("10000"), Decimal("10018"), risk_free_rate=0.10) @@ -154,8 +186,22 @@ def test_fee_subtracted_from_pnl(): """Fees should be subtracted from trade PnL.""" base = datetime(2025, 1, 1, tzinfo=timezone.utc) trades_with_fees = [ - TradeRecord(time=base, symbol="BTC", side="BUY", price=Decimal("100"), quantity=Decimal("1"), fee=Decimal("1")), - TradeRecord(time=base + timedelta(minutes=10), symbol="BTC", side="SELL", price=Decimal("110"), quantity=Decimal("1"), fee=Decimal("1")), + TradeRecord( + time=base, + symbol="BTC", + side="BUY", + price=Decimal("100"), + quantity=Decimal("1"), + fee=Decimal("1"), + ), + TradeRecord( + time=base + timedelta(minutes=10), + symbol="BTC", + side="SELL", + price=Decimal("110"), + quantity=Decimal("1"), + fee=Decimal("1"), + ), ] # PnL should be 10 - 1 - 1 = 8 metrics = compute_detailed_metrics(trades_with_fees, Decimal("10000"), Decimal("10008")) -- cgit v1.2.3