summaryrefslogtreecommitdiff
path: root/services/order-executor/tests/test_risk_manager.py
diff options
context:
space:
mode:
Diffstat (limited to 'services/order-executor/tests/test_risk_manager.py')
-rw-r--r--services/order-executor/tests/test_risk_manager.py51
1 files changed, 51 insertions, 0 deletions
diff --git a/services/order-executor/tests/test_risk_manager.py b/services/order-executor/tests/test_risk_manager.py
index 4bd5761..a8fe37f 100644
--- a/services/order-executor/tests/test_risk_manager.py
+++ b/services/order-executor/tests/test_risk_manager.py
@@ -235,3 +235,54 @@ def test_var_calculation():
positions = {"BTCUSDT": Position(symbol="BTCUSDT", quantity=Decimal("1"), avg_entry_price=Decimal("100"), current_price=Decimal("100"))}
var = rm.calculate_portfolio_var(positions, Decimal("10000"))
assert var >= 0 # Non-negative
+
+
+# --- Drawdown position scaling tests ---
+
+
+def test_drawdown_position_scale_full():
+ rm = RiskManager(max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), drawdown_reduction_threshold=0.1, drawdown_halt_threshold=0.2)
+ rm.update_balance(Decimal("10000"))
+ scale = rm.get_position_scale(Decimal("10000"))
+ assert scale == 1.0 # No drawdown
+
+
+def test_drawdown_position_scale_reduced():
+ rm = RiskManager(max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), drawdown_reduction_threshold=0.1, drawdown_halt_threshold=0.2)
+ rm.update_balance(Decimal("10000"))
+ scale = rm.get_position_scale(Decimal("8500")) # 15% drawdown (between 10% and 20%)
+ assert 0.25 < scale < 1.0
+
+
+def test_drawdown_halt():
+ rm = RiskManager(max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), drawdown_reduction_threshold=0.1, drawdown_halt_threshold=0.2)
+ rm.update_balance(Decimal("10000"))
+ scale = rm.get_position_scale(Decimal("7500")) # 25% drawdown
+ assert scale == 0.0
+
+
+def test_consecutive_losses_pause():
+ rm = RiskManager(max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), max_consecutive_losses=3, loss_pause_minutes=60)
+ rm.record_trade_result(False)
+ rm.record_trade_result(False)
+ assert not rm.is_paused()
+ rm.record_trade_result(False) # 3rd loss
+ assert rm.is_paused()
+
+
+def test_consecutive_losses_reset_on_win():
+ rm = RiskManager(max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), max_consecutive_losses=3)
+ rm.record_trade_result(False)
+ rm.record_trade_result(False)
+ rm.record_trade_result(True) # Win resets counter
+ rm.record_trade_result(False)
+ assert not rm.is_paused() # Only 1 consecutive loss
+
+
+def test_drawdown_check_rejects_in_check():
+ rm = RiskManager(max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), drawdown_halt_threshold=0.15)
+ rm.update_balance(Decimal("10000"))
+ signal = Signal(strategy="test", symbol="BTC/USDT", side=OrderSide.BUY, price=Decimal("50000"), quantity=Decimal("0.01"), reason="test")
+ result = rm.check(signal, Decimal("8000"), {}, Decimal("0")) # 20% dd > 15%
+ assert not result.allowed
+ assert "halted" in result.reason.lower()