Risk Management Frameworks for Algorithmic Trading
Build comprehensive risk management systems with position limits, stop-losses, drawdown controls, and real-time monitoring for Indian markets.
Risk management isn’t optional—it’s what separates surviving traders from bankrupt ones. A single unmanaged risk event can wipe out months of profits. This guide implements production-grade risk frameworks for Indian algorithmic trading.
Core Risk Management Principles
The Three Pillars
- Position Limits: Never risk too much on single trade
- Stop Losses: Cut losses before they spiral
- Portfolio Limits: Control aggregate exposure
import pandas as pd
import numpy as np
from typing import Dict, List
from datetime import datetime
class RiskManager:
"""
Comprehensive risk management system
"""
def __init__(self, account_size: float, config: Dict):
self.account_size = account_size
self.config = config
self.positions = {}
self.daily_pnl = 0
self.peak_value = account_size
def check_position_limit(self, symbol: str, quantity: int, price: float) -> Dict:
"""Check if trade violates position limits"""
position_value = quantity * price
position_pct = position_value / self.account_size
max_position_pct = self.config.get('max_position_pct', 0.10) # 10% default
if position_pct > max_position_pct:
return {
'allowed': False,
'reason': f'Position size {position_pct:.1%} exceeds limit {max_position_pct:.1%}',
'max_quantity': int(self.account_size * max_position_pct / price)
}
return {'allowed': True, 'reason': 'Within position limits'}
def check_daily_loss_limit(self) -> Dict:
"""Check if daily loss limit reached"""
max_daily_loss = self.config.get('max_daily_loss', 0.02) * self.account_size
if self.daily_pnl < -max_daily_loss:
return {
'trading_allowed': False,
'reason': f'Daily loss limit reached: ₹{abs(self.daily_pnl):,.0f}'
}
return {'trading_allowed': True}
def check_drawdown_limit(self, current_value: float) -> Dict:
"""Check if drawdown limit reached"""
if current_value > self.peak_value:
self.peak_value = current_value
drawdown = (self.peak_value - current_value) / self.peak_value
max_drawdown = self.config.get('max_drawdown', 0.15) # 15% default
if drawdown > max_drawdown:
return {
'trading_allowed': False,
'reason': f'Drawdown {drawdown:.1%} exceeds limit {max_drawdown:.1%}'
}
return {'trading_allowed': True, 'current_drawdown': drawdown}
def calculate_position_size(
self,
signal_strength: float,
volatility: float,
account_value: float
) -> int:
"""
Kelly Criterion-based position sizing
Position Size = (Edge / Volatility) * Capital
"""
# Risk per trade
risk_per_trade = self.config.get('risk_per_trade', 0.02) # 2% default
# Adjust for signal strength (0-1 scale)
adjusted_risk = risk_per_trade * signal_strength
# Kelly fraction (fractional Kelly for safety)
kelly_fraction = 0.25 # Use 25% of full Kelly
# Position size
position_size = (adjusted_risk / volatility) * account_value * kelly_fraction
return max(1, int(position_size))
# Example usage
risk_config = {
'max_position_pct': 0.10, # 10% per position
'max_daily_loss': 0.02, # 2% daily loss limit
'max_drawdown': 0.15, # 15% max drawdown
'risk_per_trade': 0.01 # 1% risk per trade
}
risk_mgr = RiskManager(account_size=1000000, config=risk_config)
# Check position limit
check = risk_mgr.check_position_limit('RELIANCE', 100, 2500)
print(f"Position Check: {check}")
# Calculate position size
position_size = risk_mgr.calculate_position_size(
signal_strength=0.8,
volatility=0.02,
account_value=1000000
)
print(f"Recommended Position Size: {position_size}")