My TradeSight algo was generating 83% annual returns in backtests. Live paper trading? It burned through 8% of the portfolio in 3 days during a volatile session.
The problem wasn't the strategy. It was the absence of a circuit breaker.
What's a Circuit Breaker in Trading
In distributed systems, a circuit breaker stops cascading failures. If a service is down, you don't keep hammering it — you open the circuit, stop requests, and try again later.
Same idea applies to trading systems: if something is going wrong (drawdown, bad fills, market conditions), stop trading and wait.
The Failure Mode
My bot had no circuit breaker. When it hit bad trades in a choppy session, it kept entering positions, compounding losses. By the time I intervened manually, the damage was done.
The backtester never caught this because:
- Backtests don't simulate slippage accurately
- Backtests don't have intraday drawdown limits
- Backtests don't model cascading position entries
The Implementation
Here's the circuit breaker I added to TradeSight:
class CircuitBreaker:
def __init__(self, max_daily_loss_pct=0.02, max_consecutive_losses=3):
self.max_daily_loss_pct = max_daily_loss_pct
self.max_consecutive_losses = max_consecutive_losses
self.daily_loss = 0.0
self.consecutive_losses = 0
self.tripped = False
self.trip_time = None
self.cooldown_hours = 24
def record_trade(self, pnl_pct: float):
if pnl_pct < 0:
self.daily_loss += abs(pnl_pct)
self.consecutive_losses += 1
else:
self.consecutive_losses = 0
self._check_trip()
def _check_trip(self):
if self.daily_loss >= self.max_daily_loss_pct:
self._trip("Daily loss limit hit")
elif self.consecutive_losses >= self.max_consecutive_losses:
self._trip(f"{self.consecutive_losses} consecutive losses")
def _trip(self, reason: str):
self.tripped = True
self.trip_time = datetime.now()
logger.warning(f"CIRCUIT BREAKER TRIPPED: {reason}")
# Alert via webhook/WhatsApp
def is_open(self) -> bool:
"""Returns True if trading should be HALTED."""
if not self.tripped:
return False
# Auto-reset after cooldown
hours_since = (datetime.now() - self.trip_time).seconds / 3600
if hours_since >= self.cooldown_hours:
self.reset()
return False
return True
def reset(self):
self.tripped = False
self.daily_loss = 0.0
self.consecutive_losses = 0
logger.info("Circuit breaker reset")
Integration in the main trading loop:
async def trading_loop():
cb = CircuitBreaker(max_daily_loss_pct=0.02, max_consecutive_losses=3)
while True:
if cb.is_open():
logger.info("Circuit breaker open — skipping signal evaluation")
await asyncio.sleep(300)
continue
signal = await evaluate_signal()
if signal:
result = await execute_trade(signal)
cb.record_trade(result.pnl_pct)
await asyncio.sleep(60)
The Parameters That Actually Matter
Through paper trading, I found these thresholds work for a momentum strategy:
- Daily loss limit: 2% — beyond this, market conditions are probably hostile
- Consecutive losses: 3 — pattern signal, not random noise
- Cooldown: 24 hours — reset next market day, not same session
These are conservative. If you're running higher leverage, tighten them. If you're swing trading, loosen them.
Other Guards I Added
The circuit breaker was the main one, but I also added:
Stop-loss per position: Close any single position that hits -1.5%. No exceptions.
Max concurrent positions: Hard cap at 3 open positions. Prevents over-concentration during volatile sessions.
Market hours filter: Only trade 9:45 AM – 3:30 PM ET. Skip first and last 15 minutes — high volatility, poor fills.
Post stop-loss cooldown: After a stop-loss hit on ticker X, don't re-enter X for 7 days. Prevents the ADBE-style re-entry loops I was seeing.
The Result
After adding the circuit breaker, paper trading losses in volatile sessions dropped 80%. The bot still loses on bad days — that's expected — but it stops losing compounding amounts.
The strategy is the same. The risk management is what changed the outcome.
Open Source
TradeSight is open source: github.com/rmbell09-lang/tradesight
The circuit breaker is in tradesight/risk/circuit_breaker.py. Paper trading runs via Alpaca's sandbox API — no real money needed to test.
What other risk controls have you found essential for live algo trading? I'm still iterating on the drawdown recovery logic — would love to compare notes.
Top comments (0)