Crypto Bots

Crypto Bot Risk Management: The 10 Rules That Separate Winners From Losers

Most crypto trading bots fail not because of bad strategies, but because of bad risk management. These 10 rules have been battle-tested by algorithmic traders who've survived multiple market crashes.

A
AI Agents Hubยท2026-04-02ยท7 min readยท1,212 words

Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.

Why Bots Blow Up

A bot running a profitable strategy can still lose everything. The most common causes:

  1. No position size limits โ€” one trade takes 50% of capital
  2. No drawdown circuit breakers โ€” bot keeps trading through a 60% loss
  3. Ignoring exchange risk โ€” FTX-style exchange insolvency
  4. Over-leveraging โ€” 10x leverage + 10% move = liquidation
  5. Correlated positions โ€” "diversified" portfolio that all falls together

Every rule below addresses a real failure mode that has blown up real bots.


Rule 1: Never Risk More Than 2% Per Trade

The Kelly Criterion tells you the mathematically optimal bet size. For most strategies, it's 1-5% of capital. Start at 1-2% until you have 100+ live trades of performance data.

def calculate_position_size(
    capital: float,
    win_rate: float,
    avg_win_pct: float,
    avg_loss_pct: float,
    max_risk_pct: float = 0.02  # 2% max risk
) -> float:
    """Calculate position size using Kelly Criterion, capped at max_risk"""
    
    # Kelly fraction
    b = avg_win_pct / avg_loss_pct  # Win/loss ratio
    p = win_rate
    q = 1 - p
    kelly = (b * p - q) / b
    
    # Use half-Kelly for safety
    half_kelly = kelly / 2
    
    # Cap at max_risk_pct
    safe_fraction = min(half_kelly, max_risk_pct)
    safe_fraction = max(safe_fraction, 0)  # Never negative
    
    return capital * safe_fraction

# Example: 60% win rate, 2% avg win, 1.5% avg loss
size = calculate_position_size(10000, 0.60, 0.02, 0.015)
print(f"Position size: ${size:.2f}")  # โ†’ ~$200 (2% of $10,000)

Rule 2: Implement a Daily Loss Limit

If your bot loses more than X% in a day, it stops trading automatically. This prevents a bad day from turning into a catastrophic week.

class RiskGuard:
    def __init__(self, daily_loss_limit_pct: float = 0.05):
        self.daily_limit = daily_loss_limit_pct
        self.starting_balance = None
        self.trade_date = None
        self.halted = False
    
    def check_daily_limit(self, current_balance: float) -> bool:
        """Returns True if trading should continue, False if halted"""
        today = datetime.date.today()
        
        # Reset at start of each day
        if self.trade_date != today:
            self.starting_balance = current_balance
            self.trade_date = today
            self.halted = False
            return True
        
        if self.starting_balance is None:
            self.starting_balance = current_balance
            return True
        
        daily_loss = (self.starting_balance - current_balance) / self.starting_balance
        
        if daily_loss >= self.daily_limit:
            if not self.halted:
                print(f"๐Ÿ›‘ DAILY LOSS LIMIT HIT: {daily_loss*100:.1f}% loss โ€” stopping for today")
                self.halted = True
            return False
        
        return True

Rule 3: Maximum Drawdown Circuit Breaker

If your total portfolio drops more than 20% from peak, the bot pauses and alerts you.

class DrawdownMonitor:
    def __init__(self, max_drawdown_pct: float = 0.20):
        self.max_drawdown = max_drawdown_pct
        self.peak_balance = 0
    
    def update(self, current_balance: float) -> bool:
        """Returns False if max drawdown exceeded"""
        self.peak_balance = max(self.peak_balance, current_balance)
        
        drawdown = (self.peak_balance - current_balance) / self.peak_balance
        
        if drawdown >= self.max_drawdown:
            print(f"๐Ÿšจ MAX DRAWDOWN EXCEEDED: {drawdown*100:.1f}% from peak")
            print(f"   Peak: ${self.peak_balance:,.2f} | Current: ${current_balance:,.2f}")
            send_telegram_alert(f"BOT HALTED โ€” {drawdown*100:.1f}% drawdown")
            return False
        
        return True

Rule 4: Diversify Across Exchanges (Not Just Coins)

FTX had $32B in customer deposits. In 72 hours it was bankrupt. Never keep more than 30% of your total bot capital on any single exchange.

EXCHANGE_LIMITS = {
    'binance': 0.30,    # Max 30% โ€” largest but still risky
    'coinbase': 0.25,
    'kraken': 0.20,
    'okx': 0.15,
    'bybit': 0.10,
}

def check_exchange_concentration(balances: dict, total: float) -> list[str]:
    """Alert if any exchange exceeds its allocation limit"""
    alerts = []
    for exchange, balance in balances.items():
        pct = balance / total
        limit = EXCHANGE_LIMITS.get(exchange, 0.20)
        if pct > limit:
            alerts.append(f"{exchange}: {pct*100:.0f}% (limit: {limit*100:.0f}%)")
    return alerts

Rule 5: Correlation Check Before Adding Positions

BTC at 60% of portfolio + ETH at 30% + SOL at 10% looks diversified. In a crypto crash, all three fall 50%+ simultaneously. True diversification requires low correlation assets.

import numpy as np
import pandas as pd

def check_portfolio_correlation(returns: pd.DataFrame, threshold: float = 0.8) -> list:
    """Warn if any two holdings are too correlated"""
    corr_matrix = returns.corr()
    alerts = []
    
    for i in range(len(corr_matrix.columns)):
        for j in range(i+1, len(corr_matrix.columns)):
            corr = corr_matrix.iloc[i, j]
            if corr > threshold:
                coin1 = corr_matrix.columns[i]
                coin2 = corr_matrix.columns[j]
                alerts.append(f"{coin1}/{coin2}: {corr:.2f} correlation (>0.8 = highly correlated)")
    
    return alerts

Rule 6: Use Stop Losses on Every Position

No exceptions. A stop loss at -5% feels painful until the day you're down 50% on a position and wishing you'd had one.

def place_order_with_stop(exchange, symbol: str, direction: str, size: float, 
                           entry: float, stop_pct: float = 0.05):
    """Place order and immediately set stop loss"""
    
    # Enter position
    order = exchange.create_market_order(symbol, direction, size)
    fill_price = order['average']
    
    # Calculate stop loss price
    if direction == 'buy':
        stop_price = fill_price * (1 - stop_pct)
        stop_side = 'sell'
    else:
        stop_price = fill_price * (1 + stop_pct)
        stop_side = 'buy'
    
    # Place stop loss immediately
    stop_order = exchange.create_order(
        symbol, 'stop_market', stop_side, size,
        params={'stopPrice': stop_price, 'reduceOnly': True}
    )
    
    print(f"Entry: ${fill_price:.2f} | Stop: ${stop_price:.2f} ({stop_pct*100:.0f}% risk)")
    return order, stop_order

Rule 7: Never Trade More Than 3 Correlated Coins Simultaneously

If BTC drops 10% in an hour, your long ETH, SOL, and AVAX positions are also in trouble. Cap correlated simultaneous positions.


Rule 8: Log Everything

You can't improve what you don't measure:

import json
from pathlib import Path

class TradeLogger:
    def __init__(self, log_file: str = "trades.jsonl"):
        self.log_file = Path(log_file)
    
    def log_trade(self, trade: dict):
        trade['timestamp'] = datetime.datetime.now().isoformat()
        with open(self.log_file, 'a') as f:
            f.write(json.dumps(trade) + '\n')
    
    def get_stats(self) -> dict:
        trades = [json.loads(l) for l in self.log_file.read_text().strip().split('\n') if l]
        pnls = [t['pnl'] for t in trades if 'pnl' in t]
        wins = [p for p in pnls if p > 0]
        losses = [p for p in pnls if p < 0]
        
        return {
            'total_trades': len(trades),
            'win_rate': len(wins) / len(pnls) if pnls else 0,
            'total_pnl': sum(pnls),
            'avg_win': sum(wins)/len(wins) if wins else 0,
            'avg_loss': sum(losses)/len(losses) if losses else 0,
            'profit_factor': abs(sum(wins)/sum(losses)) if losses else float('inf'),
        }

Rule 9: Paper Trade for 30 Days Minimum Before Going Live

No exceptions for new strategies. Past backtest performance does not guarantee future results. Paper trade:

class PaperTrader:
    def __init__(self, starting_balance: float = 10000):
        self.balance = starting_balance
        self.positions = {}
        self.trade_log = []
    
    def buy(self, symbol: str, usd_amount: float):
        price = self.get_price(symbol)
        qty = usd_amount / price
        self.positions[symbol] = {'qty': qty, 'entry': price, 'usd': usd_amount}
        self.balance -= usd_amount
        print(f"[PAPER] BUY {qty:.4f} {symbol} @ ${price:,.2f}")
    
    def sell(self, symbol: str):
        if symbol not in self.positions:
            return
        pos = self.positions[symbol]
        current_price = self.get_price(symbol)
        current_value = pos['qty'] * current_price
        pnl = current_value - pos['usd']
        self.balance += current_value
        del self.positions[symbol]
        print(f"[PAPER] SELL {symbol} | P&L: ${pnl:+.2f}")

Rule 10: Have an Emergency Stop Button

Build a way to immediately stop all bots and cancel all orders:

async def emergency_shutdown(exchanges: list, reason: str):
    """IMMEDIATELY stop all bots and cancel all open orders"""
    
    print(f"๐Ÿšจ EMERGENCY SHUTDOWN: {reason}")
    send_telegram_alert(f"๐Ÿšจ EMERGENCY SHUTDOWN\nReason: {reason}")
    
    for exchange in exchanges:
        try:
            # Cancel all open orders
            markets = exchange.load_markets()
            for symbol in markets:
                try:
                    exchange.cancel_all_orders(symbol)
                except Exception:
                    pass
            print(f"โœ… {exchange.id}: all orders cancelled")
        except Exception as e:
            print(f"โŒ {exchange.id}: shutdown failed โ€” {e}")
    
    # Write stop flag
    Path("EMERGENCY_STOP").write_text(reason)
    print("Emergency stop flag written. Restart manually after investigation.")

Risk management isn't exciting. No one brags about the losses they avoided. But every experienced algorithmic trader will tell you the same thing: the strategy gets you into trades, risk management keeps you in the game long enough to profit from them.

Related Articles