Risk Management in Algorithmic Trading: A Developer's Guide
The complete guide to risk management for automated trading systems. Position sizing, drawdown limits, correlation risk, and the code to implement it all.
Risk Management Is the Strategy
A mediocre strategy with good risk management will outperform a great strategy with poor risk management. This is the most counterintuitive truth in trading: your edge comes from how much you risk, not from how often you are right.
Max risk per trade
Max total portfolio risk
Max drawdown before halt
Position Sizing with ATR
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://tickatlas.com/v1"
class RiskManager:
def __init__(self, balance: float, max_risk_pct: float = 1.0,
max_positions: int = 5, max_drawdown_pct: float = 15.0):
self.initial_balance = balance
self.balance = balance
self.max_risk_pct = max_risk_pct
self.max_positions = max_positions
self.max_drawdown_pct = max_drawdown_pct
self.open_positions = []
self.peak_balance = balance
def calculate_position_size(self, symbol: str, entry: float,
stop_loss: float) -> float:
"""Calculate lot size based on fixed-percentage risk."""
risk_amount = self.balance * (self.max_risk_pct / 100)
stop_distance = abs(entry - stop_loss)
if stop_distance == 0:
return 0.0
pip_factor = 100 if "JPY" in symbol else 10000
stop_pips = stop_distance * pip_factor
pip_value = 10.0 # USD per pip per standard lot
lots = risk_amount / (stop_pips * pip_value)
return round(lots, 2)
def can_open_position(self) -> bool:
"""Check if we can open another position."""
if len(self.open_positions) >= self.max_positions:
return False
if self.current_drawdown() >= self.max_drawdown_pct:
return False
return True
def current_drawdown(self) -> float:
"""Calculate current drawdown from peak."""
self.peak_balance = max(self.peak_balance, self.balance)
dd = (self.peak_balance - self.balance) / self.peak_balance * 100
return round(dd, 2)
def total_risk_exposure(self) -> float:
"""Sum of risk across all open positions."""
return sum(p.get("risk_pct", 0) for p in self.open_positions)
ATR-Based Dynamic Position Sizing
def dynamic_position_size(self, symbol: str, timeframe: str,
direction: str, atr_multiplier: float = 2.0) -> dict:
"""Calculate position size using live ATR data."""
# Fetch ATR
resp = requests.get(f"{BASE_URL}/indicator",
headers={"X-API-Key": API_KEY},
params={"symbol": symbol, "indicator": "ATR_14", "timeframe": timeframe})
data = resp.json()["data"]
atr = data["values"]["atr"]
close = data["ohlcv"]["close"]
stop_distance = atr * atr_multiplier
if direction == "long":
stop_loss = close - stop_distance
else:
stop_loss = close + stop_distance
lots = self.calculate_position_size(symbol, close, stop_loss)
return {
"symbol": symbol,
"direction": direction,
"entry": close,
"stop_loss": round(stop_loss, 5),
"lots": lots,
"risk_amount": round(self.balance * self.max_risk_pct / 100, 2),
"atr": atr,
} Correlation-Aware Risk
Being long EURUSD, long GBPUSD, and long AUDUSD is not three separate bets -- it is essentially one large bet against the US dollar. Correlation-aware risk management counts correlated positions as partial duplicates.
CORRELATION_GROUPS = {
"USD_WEAK": ["EURUSD", "GBPUSD", "AUDUSD", "NZDUSD"],
"USD_STRONG": ["USDCAD", "USDJPY", "USDCHF"],
"SAFE_HAVEN": ["XAUUSD", "USDJPY"],
}
def check_correlation_risk(self, new_symbol: str, new_direction: str) -> bool:
"""Check if a new position would over-concentrate risk."""
# Find which group the new symbol belongs to
for group_name, symbols in CORRELATION_GROUPS.items():
if new_symbol in symbols:
# Count how many open positions are in the same group
same_group = [p for p in self.open_positions
if p["symbol"] in symbols
and p["direction"] == new_direction]
if len(same_group) >= 2:
return False # Too much exposure to this group
return True Drawdown Circuit Breaker
class DrawdownBreaker:
"""Halt trading when drawdown exceeds thresholds."""
def __init__(self):
self.thresholds = [
(10.0, "REDUCE"), # 10% DD: halve position sizes
(15.0, "PAUSE"), # 15% DD: stop opening new trades
(20.0, "FLATTEN"), # 20% DD: close everything
]
def evaluate(self, current_drawdown: float) -> str:
action = "NORMAL"
for threshold, level in self.thresholds:
if current_drawdown >= threshold:
action = level
return action
# In the main trading loop:
breaker = DrawdownBreaker()
rm = RiskManager(balance=10000)
action = breaker.evaluate(rm.current_drawdown())
if action == "FLATTEN":
close_all_positions()
elif action == "PAUSE":
pass # Do not open new trades
elif action == "REDUCE":
rm.max_risk_pct = 0.5 # Halve risk per trade
The Risk Checklist
Every trade has a defined stop loss
No exceptions. If you cannot define the stop before entry, do not take the trade. ATR-based stops are the most reliable approach.
Position size is calculated, not guessed
Use the fixed-percentage model. Risk 1% of account equity per trade. Let the stop distance determine the lot size, not the other way around.
Maximum portfolio heat is enforced
With 5 open positions at 1% risk each, you have 5% portfolio heat. Set a hard cap and enforce it in code, not in your head.
Drawdown triggers are automated
When you are losing money, your judgment is worst. Automate the circuit breakers so they fire without your emotional input.
Related Reading
Try this with live data
Every account gets $2.50 in free PAYG credits. No card required — paste your API key and run the code above against live broker data.
Keep reading
All articles- Guide 14 min read
Algorithmic Trading for Beginners: From Zero to First Bot
A beginner-friendly guide to algorithmic trading. Learn the core concepts, pick your first strategy, and build a working bot with Python and the TickAtlas API.
March 28, 2026
- Guide 13 min read
Building Autonomous Trading Agents with LLMs
A practical guide to building AI agents that autonomously monitor markets, analyze opportunities, and generate trading signals using LLMs and the TickAtlas API.
March 28, 2026
- Guide 10 min read
Bitcoin Technical Analysis API: RSI, MACD, and More for BTC
A developer's guide to analyzing Bitcoin with technical indicators via the TickAtlas API. Covers BTC-specific strategies, volatility management, and real code examples.
March 28, 2026