💡 Domestic note: Binance’s official site isn’t directly accessible from mainland China—you’ll need a VPN or similar network tool. Fiat deposits don’t support bank cards directly; most funding happens through C2C (USDT↔CNY) channels instead.

In futures trading, the difference between a profitable strategy and a losing one often isn’t about whether your signals are right—it’s about whether you’re actually implementing risk management.
A solid entry signal paired with poor risk control will give back every penny of gains in a single big swing. This article covers the framework for futures risk management: which layers you need to control, and how to set each one up properly.
Three Levels of Risk Control
Level 1: Position Sizing — how much to open each trade
Level 2: Stop-Loss Setup — maximum loss per individual trade
Level 3: Circuit Breaker — forced pause after consecutive losses
You need all three. Position sizing controls per-trade risk exposure, stop-loss controls your maximum loss on any single trade, and the circuit breaker prevents continuous bleeding when your strategy breaks.
Level 1: Position Sizing
Fixed Ratio Method (Recommended)
Position size each time = Account total equity × Fixed percentage
def calc_position_size(equity: float, risk_pct: float, leverage: int,
entry_price: float) -> float:
"""
equity: account total equity (USDT)
risk_pct: per-trade risk ratio, e.g., 0.02 = 2%
leverage: leverage multiplier
entry_price: entry price
returns: contract quantity (BTCUSDT each = 0.001 BTC)
"""
risk_amount = equity * risk_pct # actual amount willing to lose
notional = risk_amount * leverage # corresponding notional value
qty = notional / entry_price
return qty
Why fixed ratio instead of fixed amount?
Fixed amounts (e.g., 1000 USDT each time) become riskier as your account shrinks and safer as it grows—backwards. Fixed ratio auto-adjusts with your account size, keeping risk proportional.
Reasonable risk percentages by stage:
| Strategy Phase | Recommended Ratio |
|---|---|
| Testing (validating strategy) | 0.5% - 1% |
| Stable operation | 1% - 2% |
| High-confidence strategy | 2% - 3% |
| Not recommended above | 5% |
At 5%+ per trade, five consecutive losses means 25%+ drawdown—emotionally brutal and hard for a strategy to recover from.
Kelly Criterion (Advanced)
If you have enough historical data, the Kelly Criterion calculates the theoretically optimal position size:
def kelly_fraction(win_rate: float, avg_win: float, avg_loss: float) -> float:
"""
win_rate: historical win rate, e.g., 0.55
avg_win: average win (as ratio), e.g., 0.015 = 1.5%
avg_loss: average loss (as ratio, positive), e.g., 0.01 = 1%
"""
b = avg_win / avg_loss # win-to-loss ratio
p = win_rate
q = 1 - p
kelly = (b * p - q) / b
# use half-Kelly in practice to reduce volatility
return max(0, kelly * 0.5)
The Kelly Criterion gives the position size that theoretically maximizes compounded growth. In practice, use “half Kelly”—it controls drawdown while still preserving solid growth.
Level 2: Stop-Loss Setup
There are two main approaches to stops:
Price-Based Stop-Loss
Set your stop-loss price at entry; close when price hits it.
def set_stop_loss(symbol: str, side: str, entry_price: float,
stop_pct: float, client) -> dict:
"""
stop_pct: stop-loss distance, e.g., 0.02 = 2% from entry
"""
if side == "BUY":
stop_price = entry_price * (1 - stop_pct)
sl_side = "SELL"
else:
stop_price = entry_price * (1 + stop_pct)
sl_side = "BUY"
# place STOP_MARKET order (close at market when trigger is hit)
order = client.futures_create_order(
symbol=symbol,
side=sl_side,
type="STOP_MARKET",
stopPrice=round(stop_price, 2),
closePosition=True, # close entire position
timeInForce="GTE_GTC"
)
return order
How to set stop-loss distance:
Common approaches:
- ATR-based (Average True Range): stop-loss = 1–2x ATR, adapts to current volatility
- Support/resistance levels: place at technically meaningful spots
- Fixed percentage: simple and direct, easy to calculate
Recommend ATR because volatility varies wildly across market conditions. A fixed percentage is too loose in calm markets and too tight in volatile ones.
def calc_atr(highs, lows, closes, period=14):
"""calculate ATR"""
true_ranges = []
for i in range(1, len(closes)):
tr = max(
highs[i] - lows[i],
abs(highs[i] - closes[i-1]),
abs(lows[i] - closes[i-1])
)
true_ranges.append(tr)
return sum(true_ranges[-period:]) / period
Trailing Stop-Loss
Adjust your stop with price movement, locking in floating gains:
def set_trailing_stop(symbol: str, callback_rate: float, client) -> dict:
"""
callback_rate: callback percentage, e.g., 1.5 = 1.5%
triggers when price pulls back 1.5% from its peak
"""
order = client.futures_create_order(
symbol=symbol,
side="SELL",
type="TRAILING_STOP_MARKET",
callbackRate=callback_rate,
quantity=your_position_quantity,
reduceOnly=True
)
return order
Trailing stops suit trending markets; regular stops suit ranging markets.
Level 3: Circuit Breaker Mechanism
A circuit breaker forces a pause when your strategy hits consecutive losses, preventing account death-by-a-thousand-cuts.
class CircuitBreaker:
def __init__(self,
max_daily_loss_pct: float = 0.05, # max daily loss 5%
max_drawdown_pct: float = 0.15, # max drawdown 15%
max_consecutive_losses: int = 5): # max consecutive losses
self.max_daily_loss_pct = max_daily_loss_pct
self.max_drawdown_pct = max_drawdown_pct
self.max_consecutive_losses = max_consecutive_losses
self.consecutive_losses = 0
self.peak_equity = None
self.day_start_equity = None
def update(self, current_equity: float) -> bool:
"""
update state; returns True for normal, False for breaker triggered
"""
if self.peak_equity is None:
self.peak_equity = current_equity
if self.day_start_equity is None:
self.day_start_equity = current_equity
# update peak
if current_equity > self.peak_equity:
self.peak_equity = current_equity
# check max drawdown
drawdown = (self.peak_equity - current_equity) / self.peak_equity
if drawdown >= self.max_drawdown_pct:
self._trigger("max drawdown triggered", drawdown)
return False
# check daily loss
daily_loss = (self.day_start_equity - current_equity) / self.day_start_equity
if daily_loss >= self.max_daily_loss_pct:
self._trigger("daily loss triggered", daily_loss)
return False
# check consecutive losses
if self.consecutive_losses >= self.max_consecutive_losses:
self._trigger(f"{self.consecutive_losses} consecutive losses")
return False
return True
def record_trade(self, pnl: float):
"""record a trade result"""
if pnl < 0:
self.consecutive_losses += 1
else:
self.consecutive_losses = 0 # reset on profit
def _trigger(self, reason: str, value: float = 0):
print(f"[circuit breaker] {reason}: {value:.2%}")
# add Telegram alert here
Usage:
cb = CircuitBreaker(
max_daily_loss_pct=0.05,
max_drawdown_pct=0.15,
max_consecutive_losses=5
)
# check before each strategy run
while True:
equity = get_account_equity()
if not cb.update(equity):
send_telegram("strategy breaker triggered, pausing trades")
break
# execute strategy logic
signal = strategy.check_signal()
if signal:
pnl = execute_trade(signal)
cb.record_trade(pnl)
Complete Risk Management Integration Example
class RiskManager:
def __init__(self, client, equity: float):
self.client = client
self.equity = equity
self.circuit_breaker = CircuitBreaker()
def open_position(self, symbol: str, side: str, entry_price: float,
risk_pct: float = 0.01, leverage: int = 5):
"""open position with risk control"""
# 1. check circuit breaker
if not self.circuit_breaker.update(self.equity):
print("breaker triggered, rejecting position")
return None
# 2. calculate position size
qty = calc_position_size(self.equity, risk_pct, leverage, entry_price)
# 3. open position
order = self.client.futures_create_order(
symbol=symbol,
side=side,
type="MARKET",
quantity=qty
)
# 4. set stop-loss synchronously (based on ATR)
atr = self.get_atr(symbol)
stop_dist_pct = (atr * 1.5) / entry_price
set_stop_loss(symbol, side, entry_price, stop_dist_pct, self.client)
return order
Common Risk Control Mistakes
No stop-loss: Most fatal, no debate needed.
Stop too tight: 0.3x ATR means frequent triggers and a falsely inflated win rate.
Stop too loose: 5x ATR means single losses exceed expectation and wreck your risk ratio.
Fixed position size: After losses, you’re not reducing size—losses compound.
Manually overriding the breaker: “Just this once”—everyone thinks that every time. A breaker you manually disable is no breaker at all.
Risk management isn’t a leash on returns—it’s the infrastructure that keeps your strategy alive long enough to run. Build it right, stay in the game long enough, and let any edge you have compound its expected gains over time.
→ See Binance API from signup to first trade → See Quant toolkit: Binance API + Grafana + automated alerts → See Polaris strategy intro
📷 Cover image · Flux Schnell AI generated · panshi.io brand style