DEV Community

Propfirmkey
Propfirmkey

Posted on

Building a Futures Trading Risk Calculator in Python

Risk management is the single most important factor separating profitable futures traders from those who blow their accounts. In this tutorial, we'll build a complete risk calculator in Python that handles position sizing, margin requirements, and max drawdown tracking.

Why Risk Calculation Matters

Most aspiring futures traders focus on entries and exits. But the math is clear: even a strategy with 60% win rate will fail if position sizes are too large. Let's build a tool that enforces discipline.

The Core Calculator

from dataclasses import dataclass
from typing import Optional

@dataclass
class FuturesContract:
    symbol: str
    tick_size: float
    tick_value: float
    margin: float

# Common futures contracts
CONTRACTS = {
    "ES": FuturesContract("ES", 0.25, 12.50, 15_400),
    "NQ": FuturesContract("NQ", 0.25, 5.00, 18_700),
    "MES": FuturesContract("MES", 0.25, 1.25, 1_540),
    "MNQ": FuturesContract("MNQ", 0.25, 0.50, 1_870),
    "CL": FuturesContract("CL", 0.01, 10.00, 6_600),
}

class RiskCalculator:
    def __init__(self, account_balance: float, max_risk_pct: float = 1.0):
        self.account_balance = account_balance
        self.max_risk_pct = max_risk_pct

    def max_risk_dollars(self) -> float:
        return self.account_balance * (self.max_risk_pct / 100)

    def position_size(self, symbol: str, stop_ticks: int) -> dict:
        contract = CONTRACTS[symbol]
        risk_per_contract = stop_ticks * contract.tick_value
        max_contracts = int(self.max_risk_dollars() / risk_per_contract)
        return {
            "symbol": symbol,
            "max_contracts": max_contracts,
            "risk_per_contract": risk_per_contract,
            "total_risk": max_contracts * risk_per_contract,
            "margin_required": max_contracts * contract.margin,
            "risk_pct": (max_contracts * risk_per_contract / self.account_balance) * 100,
        }

    def drawdown_tracker(self, trades: list[float]) -> dict:
        peak = self.account_balance
        balance = self.account_balance
        max_dd = 0
        for pnl in trades:
            balance += pnl
            peak = max(peak, balance)
            dd = (peak - balance) / peak * 100
            max_dd = max(max_dd, dd)
        return {
            "final_balance": balance,
            "peak": peak,
            "max_drawdown_pct": round(max_dd, 2),
            "total_pnl": sum(trades),
        }

# Example usage
calc = RiskCalculator(account_balance=50_000, max_risk_pct=1.5)
result = calc.position_size("ES", stop_ticks=8)
print(f"Max ES contracts with 8-tick stop: {result['max_contracts']}")
print(f"Total risk: ${result['total_risk']:.2f} ({result['risk_pct']:.1f}%)")
Enter fullscreen mode Exit fullscreen mode

Adding Drawdown Rules

Different prop firms have different drawdown rules. Some use End-of-Day (EOD) trailing drawdown, others use real-time. Let's model both:

def eod_trailing_drawdown(daily_closes: list[float], max_dd: float) -> dict:
    peak = daily_closes[0]
    for i, close in enumerate(daily_closes):
        peak = max(peak, close)
        dd = peak - close
        if dd >= max_dd:
            return {"blown": True, "day": i, "drawdown": dd}
    return {"blown": False, "max_observed_dd": max(peak - min(daily_closes[daily_closes.index(peak):]), 0)}

def realtime_trailing_drawdown(equity_curve: list[float], max_dd: float) -> dict:
    peak = equity_curve[0]
    for i, val in enumerate(equity_curve):
        peak = max(peak, val)
        dd = peak - val
        if dd >= max_dd:
            return {"blown": True, "tick": i, "drawdown": dd}
    return {"blown": False, "peak": peak}
Enter fullscreen mode Exit fullscreen mode

Putting It All Together

if __name__ == "__main__":
    calc = RiskCalculator(50_000, max_risk_pct=1.0)

    for symbol in ["ES", "MES", "NQ", "MNQ"]:
        pos = calc.position_size(symbol, stop_ticks=10)
        print(f"{symbol}: {pos['max_contracts']} contracts, "
              f"risk ${pos['total_risk']:.0f}, margin ${pos['margin_required']:,.0f}")

    # Simulate drawdown tracking
    sample_trades = [200, -150, 300, -400, 250, -100, 350, -200, 150, -50]
    dd_result = calc.drawdown_tracker(sample_trades)
    print(f"\nMax drawdown: {dd_result['max_drawdown_pct']}%")
    print(f"Final P&L: ${dd_result['total_pnl']:+,.0f}")
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  1. Never risk more than 1-2% per trade β€” the math protects you from ruin
  2. Know your contract specs β€” tick values vary dramatically between instruments
  3. Track drawdown continuously β€” not just at end of day

If you're evaluating prop trading firms for futures, PropFirmKey maintains a detailed comparison of major futures prop firms including Alpha Futures with current pricing, drawdown rules, and payout structures.

The full calculator code is on my GitHub. Questions or improvements? Drop them in the comments!

Top comments (0)