Claude-skill-registry drawdown-guardrails-pattern
Consistent drawdown control pattern for trading systems - backtests, live trading, and training
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/drawdown-guardrails-pattern" ~/.claude/skills/majiayu000-claude-skill-registry-drawdown-guardrails-pattern && rm -rf "$T"
manifest:
skills/data/drawdown-guardrails-pattern/SKILL.mdsource content
Drawdown Guardrails Pattern - Research Notes
Experiment Overview
| Item | Details |
|---|---|
| Date | 2025-12-16 |
| Goal | Implement consistent drawdown controls across all trading system components |
| Environment | Python 3.10, PyTorch, custom trading system |
| Status | Success |
Context
Trading systems need drawdown protection at multiple layers:
- Backtesting - Avoid selecting high-drawdown strategies during model evaluation
- Live Trading - Halt or reduce risk when equity drops
- Training - Penalize models that learn high-return/high-drawdown behavior
Without consistent guardrails, a model might backtest well but blow up in production.
Verified Workflow
1. Configuration (Single Source of Truth)
# Define limits once, reference everywhere MAX_DRAWDOWN_PCT = 0.15 # 15% - halt trading DRAWDOWN_WARNING_PCT = 0.10 # 10% - reduce position sizes DRAWDOWN_SIZING_SCALE = 0.5 # 50% position size in warning zone
2. Drawdown Tracking State
@dataclass class DrawdownState: peak_equity: float current_drawdown_pct: float = 0.0 drawdown_warning_triggered: bool = False drawdown_halt_triggered: bool = False # Once triggered, stays triggered def update_drawdown_tracking(self, current_equity: float): """Update drawdown state - call after every equity calculation.""" # Update peak (high water mark) if current_equity > self.peak_equity: self.peak_equity = current_equity # Calculate current drawdown if self.peak_equity > 0: self.current_drawdown_pct = (self.peak_equity - current_equity) / self.peak_equity # Update flags self.drawdown_warning_triggered = self.current_drawdown_pct >= DRAWDOWN_WARNING_PCT # Halt flag is sticky - once triggered, stays triggered until manual reset if self.current_drawdown_pct >= MAX_DRAWDOWN_PCT: self.drawdown_halt_triggered = True
3. Guardrail Check Function
def check_drawdown_guardrails(self) -> Tuple[bool, str, float]: """ Returns: (can_trade, reason, position_scale) """ # Check halt condition if self.drawdown_halt_triggered: return False, f"max_drawdown_{self.current_drawdown_pct:.1%}", 0.0 # Check warning condition if self.drawdown_warning_triggered: return True, f"drawdown_warning_{self.current_drawdown_pct:.1%}", DRAWDOWN_SIZING_SCALE # Normal operation return True, "ok", 1.0
4. Integration Points
Backtest Engine
for bar_idx in range(start_idx, end_idx): # Calculate equity BEFORE new trades equity = self._calculate_equity(current_price) # Update drawdown tracking self._update_drawdown_tracking(equity) can_trade, reason, position_scale = self._check_drawdown_guardrails() # Only process signals if allowed if can_trade: self._process_signal(..., position_scale=position_scale) # Even if halted, existing positions can still exit via stop-loss/TP
Live Trading Loop
while running: # Check drawdown before any trading can_trade, reason, position_scale = profit_tracker.check_trading_conditions( max_drawdown_pct=0.15 ) if not can_trade: logger.warning(f"TRADING HALTED: {reason}") # Allow exits but no new entries continue # Pass scale to trading function new_state = decide_and_trade(..., position_scale=position_scale)
Training Validation
# Compute fitness with drawdown penalty drawdown_penalty = config.drawdown_penalty_weight * max_drawdown fitness_score = sharpe_ratio * max(0.0, 1.0 - drawdown_penalty) # Early stopping on excessive drawdown if max_drawdown > config.max_drawdown_threshold: print(f"DRAWDOWN EARLY STOP: {max_drawdown:.1%}") should_stop = True
Failed Attempts (Critical)
| Attempt | Why it Failed | Lesson Learned |
|---|---|---|
| Only checking drawdown on trade entry | Positions can gap down overnight | Check on every bar/loop |
| Resetting halt flag when drawdown recovers | Creates "trading whipsaw" behavior | Halt should be sticky until manual reset |
| Using absolute dollar drawdown | Not comparable across account sizes | Always use percentage of peak |
| Linear position scaling 0-100% | Too aggressive reduction at small drawdowns | Use threshold-based (warning zone) |
| Not allowing exits when halted | Positions stuck, can't cut losses | Always allow stop-loss exits |
| Drawdown from initial capital | Misses profit drawdowns | Track from peak equity (high water mark) |
Final Parameters
# Recommended guardrail configuration @dataclass class DrawdownConfig: max_drawdown_pct: float = 0.15 # 15% - matches common hedge fund limits drawdown_warning_pct: float = 0.10 # 10% - early warning drawdown_sizing_scale: float = 0.5 # 50% in warning zone halt_on_max_drawdown: bool = True # Hard stop at max drawdown_penalty_weight: float = 2.0 # Training penalty multiplier
Key Insights
- Drawdown from peak, not initial - Track high water mark, not starting capital
- Halt flag should be sticky - Don't auto-resume trading after drawdown recovery
- Allow exits even when halted - Can still cut losses, just no new entries
- Position scaling is gradual - Warning zone reduces size, halt zone blocks entirely
- Consistent across all layers - Same 15% limit in backtest, live, and training
- Training penalty prevents selection - Models that drawdown heavily get lower fitness
- Log when triggered - Visibility into when guardrails activate
Testing Checklist
# Verify these scenarios: 1. [ ] Drawdown hits 10% -> position sizes reduced 50% 2. [ ] Drawdown hits 15% -> new entries blocked 3. [ ] Recovery to 5% drawdown -> halt flag STILL active (sticky) 4. [ ] Stop-loss orders execute even when halted 5. [ ] Training early-stops on 15%+ drawdown 6. [ ] Backtest metrics show drawdown correctly
References
- CLAUDE.md: "Max drawdown trigger: 15%"
- Hedge fund risk management: 15-20% typical max drawdown limits
- Kelly criterion: Fractional Kelly (0.25-0.5x) to reduce drawdown