mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-28 16:07:46 +00:00
bd025e50dc
Implement automatic trading protection system to prevent excessive losses: PROTECTIONS (100% original code, NOT copied from Freqtrade): - Max Drawdown Protection: Blocks trading when DD > 15% (configurable) - Cooldown Period: 4h mandatory rest after 5% loss - Stoploss Guard: Detects stoploss clusters (>5 per day) - Low Performance Filter: Filters factors with Sharpe < 0.5, Win Rate < 40% ARCHITECTURE: - Base protection interface with common utilities - 4 specialized protection implementations - ProtectionManager orchestrates all active protections - Time-based blocking with automatic expiry TESTS (32 total, ALL PASS): - 25 unit tests in test/backtesting/test_protections.py - 7 integration tests in test/integration/test_all_features.py - Tests cover: normal operation, edge cases, error handling DOCUMENTATION: - Update QWEN.md with development guidelines for AI assistant * Mandatory rules: Update QWEN.md, README, requirements.txt, tests * Pre-commit checklist * Example workflow - Update README.md with protection system features - Update project structure with new modules All code is 100% original - NO license issues with Freqtrade GPLv3.
76 lines
2.5 KiB
Python
76 lines
2.5 KiB
Python
"""
|
|
Maximum Drawdown Protection
|
|
|
|
Blocks trading when portfolio drawdown exceeds threshold.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timedelta
|
|
from .base import BaseProtection, ProtectionConfig, ProtectionResult, ProtectionType, ProtectionScope
|
|
|
|
|
|
@dataclass
|
|
class MaxDrawdownConfig(ProtectionConfig):
|
|
"""Configuration for MaxDrawdown protection."""
|
|
max_drawdown_pct: float = 0.15 # Block if drawdown > 15%
|
|
|
|
|
|
class MaxDrawdownProtection(BaseProtection):
|
|
"""
|
|
Blocks trading when drawdown exceeds safe threshold.
|
|
|
|
This prevents the system from continuing to trade during a losing streak,
|
|
giving the market time to stabilize.
|
|
"""
|
|
|
|
def __init__(self, config: MaxDrawdownConfig):
|
|
super().__init__(config)
|
|
self.config: MaxDrawdownConfig = config
|
|
|
|
@property
|
|
def scope(self) -> ProtectionScope:
|
|
return ProtectionScope.GLOBAL
|
|
|
|
def check(
|
|
self,
|
|
returns: list[float],
|
|
timestamps: list[datetime],
|
|
current_equity: float,
|
|
peak_equity: float,
|
|
**kwargs
|
|
) -> ProtectionResult:
|
|
"""Check if drawdown exceeds threshold."""
|
|
self.record_check()
|
|
|
|
if not self.config.enabled:
|
|
return ProtectionResult(
|
|
should_block=False,
|
|
reason="Protection disabled",
|
|
protection_type=ProtectionType.MAX_DRAWDOWN
|
|
)
|
|
|
|
drawdown = self.calculate_drawdown(current_equity, peak_equity)
|
|
severity = abs(drawdown) / self.config.max_drawdown_pct if self.config.max_drawdown_pct > 0 else 0
|
|
|
|
if abs(drawdown) > self.config.max_drawdown_pct:
|
|
# Calculate block duration based on severity
|
|
block_hours = int(self.config.lookback_period_hours * severity)
|
|
block_hours = min(block_hours, 168) # Max 1 week
|
|
|
|
result = ProtectionResult(
|
|
should_block=True,
|
|
reason=f"Drawdown {abs(drawdown)*100:.1f}% exceeds max {self.config.max_drawdown_pct*100:.1f}%",
|
|
until=datetime.now() + timedelta(hours=block_hours),
|
|
protection_type=ProtectionType.MAX_DRAWDOWN,
|
|
severity=severity
|
|
)
|
|
self.record_check(blocked=True)
|
|
return result
|
|
|
|
return ProtectionResult(
|
|
should_block=False,
|
|
reason=f"Drawdown {abs(drawdown)*100:.1f}% within safe range",
|
|
protection_type=ProtectionType.MAX_DRAWDOWN,
|
|
severity=severity
|
|
)
|