Files
NexQuant/rdagent/components/backtesting/protections/max_drawdown.py
T
TPTBusiness bd025e50dc feat: Add Trading Protection System with 4 protections + comprehensive tests
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.
2026-04-03 13:01:56 +02:00

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
)