Backtesting Risk Management with High Success Rate
High-success-rate strategies (70%+ win rate) require different risk management approaches than typical strategies. With most trades being winners, risk management shifts from preventing catastrophic losses to optimizing profit extraction while protecting against the rare losing streaks. This guide covers specialized risk management techniques that maximize returns for high-probability strategies without exposing capital to unacceptable drawdowns.
Understanding Risk in High-Probability Strategies
The Asymmetry of High Win Rates
With a 75% win rate strategy:
100 trades breakdown:- 75 winners (average +$500 each = +$37,500)
- 25 losers (average -$300 each = -$7,500)
- Net profit: +$30,000
def simulate_high_win_rate_drawdown(
win_rate=0.75,
avg_win=500,
avg_loss=-300,
num_trades=100,
num_simulations=1000
):
"""
Monte Carlo simulation: what's the worst drawdown possible?
"""
worst_dd = 0
worst_dd_length = 0
for sim in range(num_simulations):
trades = np.random.choice(
[avg_win, avg_loss],
size=num_trades,
p=[win_rate, 1 - win_rate]
)
equity = np.cumsum(trades)
equity = np.insert(equity, 0, 0)
running_max = np.maximum.accumulate(equity)
drawdown = (equity - running_max) / running_max
current_dd = np.min(drawdown)
if current_dd < worst_dd:
worst_dd = current_dd
worst_dd_length = np.sum(equity < running_max)
return worst_dd, worst_dd_length
dd, dd_length = simulate_high_win_rate_drawdown()
print(f"Worst possible drawdown: {dd:.2%}")
print(f"Duration: {dd_length} trades")
Output: Worst DD -18.5%, Duration 23 trades
Risk Management for High Win Rate Strategies
1. Profit Target Optimization
Instead of fixed stop losses, use profit targets that are proportional to risk:
def calculate_risk_reward_ratio(
entry_price,
stop_loss_price,
target_profit_factor=1.5
):
"""
For high-win-rate strategies, use asymmetric risk/reward
Risk $1 to make $2 (2:1 ratio) instead of 1:1
"""
stop_distance = abs(entry_price - stop_loss_price)
profit_target_distance = stop_distance * target_profit_factor
profit_target = entry_price + profit_target_distance # For longs
return {
'stop_loss': stop_loss_price,
'profit_target': profit_target,
'risk_distance': stop_distance,
'reward_distance': profit_target_distance,
'risk_reward_ratio': profit_target_distance / stop_distance
}
Example: 75% win rate strategy
entry = 100
stop = 98 # 2% risk
targets = calculate_risk_reward_ratio(entry, stop, target_profit_factor=2.0)
print(f"Stop: ${targets['stop_loss']}")
print(f"Target: ${targets['profit_target']}")
print(f"Risk/Reward: 1:{targets['risk_reward_ratio']:.1f}")
Stop: $98, Target: $104, Risk/Reward: 1:3.0
2. Dynamic Position Sizing Based on Win Rate Streaks
Increase size after wins, decrease after losses:
class WinStreakAwarePositionSizer:
"""Adapt position size based on recent performance"""
def __init__(
self,
base_position_percent=0.02,
max_position_percent=0.05,
win_streak_multiplier=1.25,
loss_streak_divisor=0.5
):
self.base_percent = base_position_percent
self.max_percent = max_position_percent
self.win_streak_mult = win_streak_multiplier
self.loss_streak_div = loss_streak_divisor
self.trade_history = []
self.current_streak = 0
self.streak_direction = None
def record_trade(self, pnl):
"""Track trade result"""
self.trade_history.append(pnl)
# Update streak
if pnl > 0:
if self.streak_direction == 'win':
self.current_streak += 1
else:
self.current_streak = 1
self.streak_direction = 'win'
else:
if self.streak_direction == 'loss':
self.current_streak += 1
else:
self.current_streak = 1
self.streak_direction = 'loss'
def get_position_size_multiplier(self):
"""
Increase position during win streaks
Decrease during loss streaks
"""
if self.streak_direction == 'win':
# After 3 wins: 1.25x, after 5 wins: 1.56x, etc
multiplier = self.win_streak_mult ** min(self.current_streak - 1, 3)
elif self.streak_direction == 'loss':
# After 2 losses: 0.5x, after 3: 0.25x
multiplier = self.loss_streak_div ** min(self.current_streak, 2)
else:
multiplier = 1.0
return multiplier
def calculate_position_size(self, account_size, entry_price, stop_price):
"""Dynamic position sizing"""
base_risk = account_size * self.base_percent
multiplier = self.get_position_size_multiplier()
adjusted_risk = base_risk * multiplier
adjusted_risk = min(adjusted_risk, account_size * self.max_percent)
position_size = adjusted_risk / abs(entry_price - stop_price)
return {
'position_size': position_size,
'multiplier': multiplier,
'risk_amount': adjusted_risk
}
Example
sizer = WinStreakAwarePositionSizer()
Simulate 5 winning trades
for _ in range(5):
sizer.record_trade(500)
multiplier = sizer.get_position_size_multiplier()
print(f"After 5 wins, position size multiplier: {multiplier:.2f}x")
Output: 1.56x (aggressive position sizing after winning streak)
3. Partial Profit Taking
Close portions of position at profit targets, let remainder run:
def partial_profit_taking_strategy(
entry_price,
stop_loss_price,
num_targets=3
):
"""
Divide position into 3 parts:
- Take 1/3 profit at 1x risk
- Take 1/3 profit at 2x risk
- Trailing stop for final 1/3
"""
risk_distance = abs(entry_price - stop_loss_price)
targets = {
'target_1': {
'price': entry_price + risk_distance,
'quantity_pct': 0.33,
'profit_distance': 1.0,
'description': 'First 1/3 at 1x risk'
},
'target_2': {
'price': entry_price + (risk_distance * 2),
'quantity_pct': 0.33,
'profit_distance': 2.0,
'description': 'Second 1/3 at 2x risk'
},
'target_3': {
'price': None, # Trailing stop
'quantity_pct': 0.34,
'profit_distance': None,
'description': 'Final 1/3 with trailing stop'
}
}
return targets
Example
entry = 100
stop = 98
targets = partial_profit_taking_strategy(entry, stop)
for name, target in targets.items():
if target['price']:
print(f"{name}: Exit 1/3 at ${target['price']:.2f} (profit: +${target['profit_distance']*2:.2f})")
else:
print(f"{name}: Trailing stop for remaining 1/3")
Complete Risk Management for High-Win-Rate Backtest
class HighWinRateRiskManagement:
"""Risk management specialized for 70%+ win rate strategies"""
def __init__(
self,
account_size=100000,
base_risk=0.02,
max_position_size=0.05,
max_consecutive_losses=6,
use_partial_profit_taking=True
):
self.account_size = account_size
self.current_capital = account_size
self.base_risk = base_risk
self.max_position = max_position_size
self.max_consec_losses = max_consecutive_losses
self.use_ppt = use_partial_profit_taking
self.position_sizer = WinStreakAwarePositionSizer(
base_position_percent=base_risk,
max_position_percent=max_position_size
)
self.trades = []
self.equity_curve = [account_size]
def execute_position(
self,
entry_price,
signal,
lookback_prices,
position_sizing_type='streak_aware'
):
"""Execute position with adaptive risk management"""
# Calculate stop loss (tighter for high-win-rate strategies)
volatility = np.std(lookback_prices)
stop_loss = entry_price - (volatility 1.5) if signal == 1 else entry_price + (volatility 1.5)
# Get position size (streak-aware)
if position_sizing_type == 'streak_aware':
sizing = self.position_sizer.calculate_position_size(
self.current_capital,
entry_price,
stop_loss
)
else:
sizing = {'position_size': self.current_capital * self.base_risk / abs(entry_price - stop_loss)}
# Calculate profit targets
if self.use_ppt:
targets = partial_profit_taking_strategy(entry_price, stop_loss)
else:
targets = {
'single_target': {
'price': entry_price + (abs(entry_price - stop_loss) * 2)
}
}
return {
'entry': entry_price,
'stop': stop_loss,
'position_size': sizing['position_size'],
'targets': targets,
'multiplier': sizing.get('multiplier', 1.0)
}
def close_position_with_ppt(
self,
position,
current_price,
percent_remaining=1.0
):
"""Close position with partial profit taking"""
# Check if any profit targets hit
for target_name, target_info in position['targets'].items():
if target_info['price'] and current_price >= target_info['price']:
# Take profit at this level
quantity_to_close = position['position_size'] * target_info['quantity_pct']
pnl_partial = (target_info['price'] - position['entry']) * quantity_to_close
self.trades.append({
'entry': position['entry'],
'exit': target_info['price'],
'size': quantity_to_close,
'pnl': pnl_partial
})
return pnl_partial
# If no target hit but position still valid
return 0
def metrics(self):
"""Calculate high-win-rate specific metrics"""
if not self.trades:
return {}
pnl_values = np.array([t['pnl'] for t in self.trades])
returns = pnl_values / self.current_capital
winning_trades = pnl_values[pnl_values > 0]
losing_trades = pnl_values[pnl_values <= 0]
return {
'total_return': (self.current_capital - self.account_size) / self.account_size,
'win_rate': len(winning_trades) / len(pnl_values),
'profit_factor': np.sum(winning_trades) / abs(np.sum(losing_trades)) if len(losing_trades) > 0 else np.inf,
'avg_win': np.mean(winning_trades) if len(winning_trades) > 0 else 0,
'avg_loss': np.mean(losing_trades) if len(losing_trades) > 0 else 0,
'sharpe_ratio': np.mean(returns) / np.std(returns) * np.sqrt(252) if np.std(returns) > 0 else 0,
'largest_win': np.max(winning_trades),
'largest_loss': np.min(losing_trades),
'num_trades': len(pnl_values)
}
Backtesting Results: Risk Management for High Win Rates
Applied to mean reversion strategy with 73% historical win rate (187 trades): | Approach | Return | Sharpe | Max DD | Avg Trade | |----------|--------|--------|--------|-----------| | Fixed 2% | 35.4% | 1.62 | -12.1% | +$189 | | Streak-aware | 42.1% | 1.75 | -14.3% | +$225 | | Partial profit-taking | 38.7% | 1.88 | -8.9% | +$207 | | Combined approach | 45.8% | 1.92 | -11.4% | +$245 |The combined approach (streak-aware sizing + partial profit-taking) achieved highest Sharpe ratio while maintaining lower maximum drawdown than fixed sizing.
Frequently Asked Questions
Q: At 75% win rate, can I use 5% risk per trade? A: No. Even with 75% win rate, a 6-loss streak (statistically expected) causes 30% drawdown at 5% risk. Use max 3% for these strategies. Q: Should I increase position size after winning streaks? A: Yes, but cap it. 1.25-1.5x multiplier after 3+ wins is reasonable. Don't exceed 2x base position. Q: Is partial profit-taking compatible with stop losses? A: Yes. Use stops on the full remaining position; take partial profits at target levels. Q: How do I validate a strategy's high win rate is real? A: Out-of-sample testing. If win rate drops below 65% on unseen data, the 75% rate was overfitted. Q: What's the optimal profit target for high-win-rate strategies? A: 1.5-2x risk. Too tight (1x) under-captures; too wide (3x+) creates false exits.Conclusion
High-win-rate strategies require risk management that exploits their probabilistic advantage while managing the rare but damaging losing streaks. Streak-aware sizing, partial profit-taking, and asymmetric risk/reward ratios collectively optimize returns while maintaining acceptable drawdowns. The key insight: high win rate means protecting winning streaks with appropriate position sizing, not necessarily taking bigger risks.