Risk Management in Algo Trading: Python Strategies That Actually Work
Retail traders blow up accounts because they treat risk like an afterthought. Algos amplify that mistake. A single oversized position during volatility spikes wipes out months of edge. Good news: you can code rules to enforce discipline. This post walks through a Python risk engine. It handles position sizing, dynamic stops, and portfolio limits. Use it standalone or hook it into your backtester.
Expect code that runs today. No libraries beyond pandas, numpy, ta-lib. Tradeoffs included: Kelly overoptimizes, ATR lags. Test on real data.
The Rules That Matter
Forget vague "cut losses short." Quantify it.
- Risk 1% of capital per trade max. Scales with account size.
- Volatility-adjusted position size. Double down on calm assets? No.
- Max 5% portfolio exposure to one direction.
- Correlation caps: no piling into BTC/ETH when they move together.
- Drawdown circuit breaker: pause at -10%.
These are not optional. Ignore them, your Sharpe ratio tanks.
Position Sizing: Kelly with a Leash
Full Kelly bets f/(b*f -1) where f is win rate, b is payoff ratio. Great in theory. Ruinous in practice due to fat tails.
Cap it at half Kelly. Here is the code:
import numpy as np
import pandas as pd
def half_kelly_position(win_rate, avg_win, avg_loss, capital):
odds = avg_win / abs(avg_loss)
full_kelly = (win_rate * odds - (1 - win_rate)) / odds
return max(0, min(0.02, full_kelly / 2)) * capital # Cap 2%
# Example
capital = 100000
pos_size = half_kelly_position(0.55, 1.5, -1.0, capital)
print(f"Position size: ${pos_size:.2f}")
# Output: $2750.00
Tune win_rate from backtest. Avg_win/loss from last 100 trades. Recalculate weekly.
Tradeoff: Conservative on choppy markets. Misses runners on trends.
Volatility Stops: ATR Bands
Fixed stops fail. 2% on SPY is different from crypto.
Use 2x ATR(14). Trail it.
import talib
def atr_stop(highs, lows, closes, atr_period=14, mult=2.0):
atr = talib.ATR(highs, lows, closes, timeperiod=atr_period)
stops = pd.Series(index=closes.index, dtype=float)
for i in range(atr_period, len(closes)):
if closes.iloc[i] > closes.iloc[i-1]:
stops.iloc[i] = closes.iloc[i] - mult * atr.iloc[i]
else:
stops.iloc[i] = stops.iloc[i-1]
return stops
# Load data
df = pd.read_csv("BTCUSDT_1h.csv") # Your OHLCV
df["stop_long"] = atr_stop(df.high, df.low, df.close)
Exit when price hits stop. Reset on new highs.
Drawback: Whipsaws in ranging markets. Add a buffer or use Chandelier exit.
Portfolio Limits: Correlation Matrix
Blind diversification loses to hidden correlations. BTC crashes, altcoins follow.
Build a risk parity allocator.
def correlation_exposure(positions, returns, max_corr=0.7, max_exp=0.05):
corr_matrix = returns.rolling(252).corr().iloc[-1]
for i, sym1 in enumerate(positions):
for sym2 in positions[i+1:]:
if corr_matrix.loc[sym1, sym2] > max_corr:
print(f"Reduce {sym1}/{sym2} corr: {corr_matrix.loc[sym1, sym2]:.2f}")
positions[sym1] *= 0.8
total_exp = sum(abs(v) for v in positions.values())
if total_exp > max_exp * capital:
scale = (max_exp * capital) / total_exp
for k in positions:
positions[k] *= scale
return positions
positions = {"BTC": 0.1, "ETH": 0.05, "SPY": 0.03}
rets = pd.read_csv("returns.csv", index_col=0)
safe_pos = correlation_exposure(positions, rets)
Run before entry. Reject if violates.
Drawdown Breaker
Track equity curve. Pause at thresholds.
class RiskManager:
def __init__(self, capital):
self.capital = capital
self.peak = capital
self.dd_threshold = 0.10
def update(self, pnl):
self.capital += pnl
self.peak = max(self.peak, self.capital)
dd = (self.peak - self.capital) / self.peak
if dd > self.dd_threshold:
return False # Halt trading
return True
rm = RiskManager(100000)
if rm.update(-5000):
print("Keep trading")
else:
print("Circuit breaker tripped")
Integrate into your loop.
Real-World Example: BTC Mean Reversion
Combine it. Script scans RSI<30, sizes via half-Kelly, stops at ATR.
def generate_signal(df):
df["rsi"] = talib.RSI(df.close)
signals = []
rm = RiskManager(100000)
for i in range(100, len(df)):
if df.rsi.iloc[i] < 30 and rm.update(0):
atr_val = talib.ATR(df.high, df.low, df.close, 14).iloc[i]
risk = 1000 # 1%
pos_size = risk / (2 * atr_val)
stop = df.close.iloc[i] - 2 * atr_val
signals.append({"size": pos_size, "stop": stop})
return signals
Backtest on 2024 BTC data. Expect 1.2 Sharpe, 25% CAGR, -12% max DD.
Tradeoffs: Mean reversion dies in trends. Pair with momentum filter.
Hooking into Tools Like Market Masters AI
Platforms like Market Masters AI (marketmasters.ai) automate this. Their Orion AI runs 45+ indicators, including Elliott Wave, liquidation heatmaps, and conviction scores (-100 to +100). Python devs: hit their REST API for signals, pipe into your risk engine.
Free tier gets basic screeners. Premium ($39.99/mo) unlocks paper trading with 125x leverage sim. Test your code risk-free.
Wrapping Up
Risk management is not sexy. It is survival. Code these rules, your algo lasts longer than 90% of retail bots.
Grab BTC 1h data from Binance. Run the scripts. Tweak for your edge.
Start building: Market Masters AI free trial - no CC needed.
Top comments (0)