Files
NexQuant/rdagent/components/backtesting/protections/low_performance.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

104 lines
3.5 KiB
Python

"""
Low Performance Filter
Blocks trading for factors/portfolios with consistently poor performance.
"""
from dataclasses import dataclass
from datetime import datetime
from .base import BaseProtection, ProtectionConfig, ProtectionResult, ProtectionType, ProtectionScope
@dataclass
class LowPerformanceConfig(ProtectionConfig):
"""Configuration for LowPerformance protection."""
min_sharpe_ratio: float = 0.5 # Minimum acceptable Sharpe
min_win_rate: float = 0.40 # Minimum 40% win rate
min_trades: int = 20 # Need at least this many trades to evaluate
class LowPerformanceProtection(BaseProtection):
"""
Filters out consistently underperforming factors.
Prevents wasting resources on factors that statistical analysis
shows are unlikely to become profitable.
"""
def __init__(self, config: LowPerformanceConfig):
super().__init__(config)
self.config: LowPerformanceConfig = config
@property
def scope(self) -> ProtectionScope:
return ProtectionScope.FACTOR
def check(
self,
returns: list[float],
timestamps: list[datetime],
current_equity: float,
peak_equity: float,
**kwargs
) -> ProtectionResult:
"""Check if performance is below minimum standards."""
self.record_check()
if not self.config.enabled:
return ProtectionResult(
should_block=False,
reason="Protection disabled",
protection_type=ProtectionType.LOW_PERFORMANCE
)
# Need minimum number of trades
if len(returns) < self.config.min_trades:
return ProtectionResult(
should_block=False,
reason=f"Insufficient data ({len(returns)} < {self.config.min_trades} trades)",
protection_type=ProtectionType.LOW_PERFORMANCE,
severity=0.0
)
# Calculate metrics
import numpy as np
returns_array = np.array(returns)
# Win rate
wins = int(np.sum(returns_array > 0))
win_rate = wins / len(returns)
# Sharpe ratio (annualized, assuming daily returns)
mean_return = float(np.mean(returns_array))
std_return = float(np.std(returns_array))
sharpe = (mean_return / std_return * np.sqrt(252)) if std_return > 0 else 0
# Check thresholds
reasons = []
severity = 0.0
if sharpe < self.config.min_sharpe_ratio:
reasons.append(f"Sharpe {sharpe:.2f} < {self.config.min_sharpe_ratio}")
severity = max(severity, (self.config.min_sharpe_ratio - sharpe) / self.config.min_sharpe_ratio)
if win_rate < self.config.min_win_rate:
reasons.append(f"Win rate {win_rate*100:.1f}% < {self.config.min_win_rate*100:.1f}%")
severity = max(severity, (self.config.min_win_rate - win_rate) / self.config.min_win_rate)
if reasons:
result = ProtectionResult(
should_block=True,
reason=" | ".join(reasons),
protection_type=ProtectionType.LOW_PERFORMANCE,
severity=severity
)
self.record_check(blocked=True)
return result
return ProtectionResult(
should_block=False,
reason=f"Performance acceptable (Sharpe: {sharpe:.2f}, Win rate: {win_rate*100:.1f}%)",
protection_type=ProtectionType.LOW_PERFORMANCE,
severity=severity
)