From 6cadb7a67b666a37db73fde92a17eaea3bfd2906 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Sat, 11 Apr 2026 16:27:04 +0000 Subject: [PATCH] test: verify stop loss always results in loss Add test case that ensures when stop loss is triggered after multiple DCA buys with decreasing prices, the final balance is always less than the initial balance. --- src/backend/tests/test_backtest_engine.py | 74 +++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/backend/tests/test_backtest_engine.py b/src/backend/tests/test_backtest_engine.py index 6399e6e..a554c5e 100644 --- a/src/backend/tests/test_backtest_engine.py +++ b/src/backend/tests/test_backtest_engine.py @@ -378,6 +378,80 @@ def test_dca_multiple_buys(): return True +def test_stop_loss_always_results_in_loss(): + """Test that stop loss ALWAYS results in a loss, never a gain. + + This tests the scenario where: + - You start with $10,000 + - Price keeps dropping, triggering multiple buys + - Stop loss triggers, selling your entire position + - Final balance MUST be less than initial balance + """ + print("\n" + "=" * 60) + print("TEST 8: Stop Loss Always Results In Loss") + print("=" * 60) + + config = { + "bot_id": "test", + "strategy_config": { + "conditions": [{"type": "price_drop", "threshold": 2, "token": "TEST", "token_address": "0x123"}], + "actions": [{"type": "buy", "amount_percent": 20}], + "risk_management": {"stop_loss_percent": 5, "take_profit_percent": 5}, + }, + "initial_balance": 10000.0, + "ave_api_key": "test", + "ave_api_plan": "free", + } + + # Price scenario: drops each kline, triggering multiple buys + # Final drop triggers stop loss + # + # $0.60 -> $0.588 (2% drop) -> BUY 1 @ $0.588 + # $0.588 -> $0.576 (2% drop) -> BUY 2 @ $0.576 + # $0.576 -> $0.565 (2% drop) -> BUY 3 @ $0.565 + # $0.565 -> $0.535 (5.3% drop) -> STOP LOSS @ $0.535 (5% from weighted avg ~$0.576) + klines = [ + {"close": "0.60", "timestamp": 1000}, + {"close": "0.588", "timestamp": 2000}, # BUY 1 + {"close": "0.576", "timestamp": 3000}, # BUY 2 + {"close": "0.565", "timestamp": 4000}, # BUY 3 + {"close": "0.535", "timestamp": 5000}, # STOP LOSS + ] + + test = TestBacktestEngine() + engine, result = test._run_backtest(config, klines) + + print(f"\nSetup:") + print(f" Initial balance: $10,000") + print(f" Stop loss: 5%") + print(f" Each buy: 20% of current balance") + print(f"\nTrades:") + for i, trade in enumerate(engine.trades): + exit_info = f" ({trade.get('exit_reason', '')})" if 'exit_reason' in trade else "" + print(f" {i+1}. {trade['type']} @ ${trade['price']} - ${trade['amount']:.2f}{exit_info}") + + print(f"\nResults:") + print(f" Final balance: ${engine.current_balance:.2f}") + print(f" Total return: {result['total_return']:.2f}%") + print(f" Max drawdown: {result['max_drawdown']:.2f}%") + + # CRITICAL ASSERTION: Stop loss MUST result in loss + assert engine.current_balance < 10000.0, \ + f"BUG: Stop loss resulted in GAIN! Balance went from $10,000 to ${engine.current_balance:.2f}" + + # Also verify total return is negative + assert result['total_return'] < 0, \ + f"BUG: Total return is positive ({result['total_return']:.2f}%) after stop loss!" + + # Max drawdown should reflect the actual loss (close to stop loss %) + assert result['max_drawdown'] < 10, \ + f"Max drawdown ({result['max_drawdown']:.2f}%) seems too high" + + print(f"\n✓ PASSED: Stop loss correctly resulted in ${10000 - engine.current_balance:.2f} loss") + return True + + if __name__ == "__main__": run_tests() test_dca_multiple_buys() + test_stop_loss_always_results_in_loss()