7 Aturan Manajemen Risiko Esensial untuk Trading Bots
📌 7 aturan kritis
- 1% maksimum risk per trade – non-negotiable
- Stop loss di setiap trade tanpa pengecualian
- Daily loss limit 3% – stop trading jika hit
- Maximum 5 concurrent positions
- Volatility-adjusted position sizing (ATR-based)
- Black swan protection: kill switch otomatis
- Weekly review untuk identify pattern
Daftar Isi
Mengapa bot trading butuh risk management ekstra
Trading manual: Anda dapat hentikan saat marah/emosional. Trading bot: akan terus eksekusi sampai disuruh berhenti.
Yang dapat salah dengan bot tanpa risk management:
- 🚨 Strategi failure: Logic bug eksekusi 100 trade buruk dalam menit
- 🚨 Internet outage: Bot tidak dapat tutup posisi saat market gap
- 🚨 Black swan: SNB CHF unpeg 2015, COVID March 2020 – bot terus trade tren ekstrem
- 🚨 API error: Broker API down, bot tidak tahu position state
- 🚨 Backtest overfit: Strategy yang bagus di history, terrible di live
Real example: Knight Capital Group lost $440 million dalam 45 menit di 2012 dari bot trading bug. Itu skala institutional. Untuk retail, bot tanpa risk management = recipe untuk total account loss.
Rule 1: 1% per trade maksimum
Mantra suci risk management. Tidak peduli signal Anda seberapa “yakin,” risk maksimum adalah 1% modal per trade.
Math why:
| Risk per trade | 10 consecutive losses | 20 consecutive losses |
|---|---|---|
| 1% | -9.6% (recoverable) | -18.2% (recoverable) |
| 3% | -26% (challenging) | -46% (devastating) |
| 5% | -40% (devastating) | -64% (akun tidak salvageable) |
| 10% | -65% (BUST) | -88% (BUST) |
Strategi terbaik di dunia memiliki losing streaks. 1% risk membuat Anda survive untuk capture winning streaks.
Implementasi:
def calculate_position_size(account_balance, risk_percent, stop_loss_pips, pip_value):
"""Hitung position size berdasarkan risk per trade."""
risk_amount = account_balance * (risk_percent / 100)
position_size = risk_amount / (stop_loss_pips * pip_value)
return position_size
# Example:
account = 10000 # USD
risk = 1.0 # 1%
stop_loss = 20 # pips
pip_value = 10 # USD per pip per standard lot
lots = calculate_position_size(account, risk, stop_loss, pip_value)
print(f"Position size: {lots:.2f} lots") # 0.50 lotsRule 2: Stop loss universal
Setiap trade harus memiliki stop loss. Tidak ada pengecualian.
Bot logic yang mandatory:
def place_order(symbol, side, lot_size, stop_loss_pips):
"""Wrapper yang memaksa stop loss di setiap order."""
if stop_loss_pips is None or stop_loss_pips <= 0:
raise ValueError("Stop loss MANDATORY untuk setiap trade!")
# Calculate stop loss price
if side == "BUY":
sl_price = current_price - (stop_loss_pips * pip_size)
else:
sl_price = current_price + (stop_loss_pips * pip_size)
# Place order with SL
order_result = broker.place_order(
symbol=symbol,
side=side,
size=lot_size,
stop_loss=sl_price
)
return order_resultMengapa stop loss di server, bukan client:
- Bot crash → stop loss tetap aktif di server broker
- Internet outage → stop loss eksekusi otomatis
- Computer power off → posisi terlindungi
Jangan pernah implementasi “mental stop loss” di bot. Selalu submit ke broker.
Rule 3: Daily loss limit
Jika bot loses 3% dalam 1 hari, stop trading sampai besok. No exceptions.
Mengapa 3%?
- 3 losing trades × 1% = 3% (within normal variance)
- 6 losing trades × 0.5% = 3% (also normal)
- Beyond 3%: indication strategy mungkin failing untuk market conditions hari ini
Implementation:
import datetime
class RiskManager:
def __init__(self, account_balance, daily_loss_limit_pct=3.0):
self.starting_balance = account_balance
self.current_balance = account_balance
self.daily_loss_limit = daily_loss_limit_pct
self.day_start_balance = account_balance
self.day_start_date = datetime.date.today()
def check_daily_loss(self):
"""Reset jika hari baru, check limit untuk hari ini."""
today = datetime.date.today()
if today != self.day_start_date:
self.day_start_balance = self.current_balance
self.day_start_date = today
daily_pnl_pct = ((self.current_balance - self.day_start_balance)
/ self.day_start_balance * 100)
if daily_pnl_pct <= -self.daily_loss_limit:
return False # STOP TRADING
return True
def can_trade(self):
if not self.check_daily_loss():
print("⛔ Daily loss limit hit. No more trades hari ini.")
return False
return TrueRule 4: Concurrent position limit
Maksimum 5 posisi simultan. Setiap position = exposure tambahan.
Math correlation risk:
Jika Anda buka 10 positions di pairs USD (EUR/USD, GBP/USD, AUD/USD, dll), itu effectively 1 trade besar terhadap USD. Correlation hampir 1.0.
USD weakness hari ini = semua 10 positions winning together. USD strength = semua 10 losing together. Tidak ada diversifikasi nyata.
Better approach:
- Max 5 positions concurrent
- Max 2 positions same currency exposure
- Diversifikasi across asset classes (forex + gold + indices)
def can_open_new_position(open_positions, max_positions=5):
if len(open_positions) >= max_positions:
return False, "Max concurrent positions reached"
# Check correlation (basic)
currencies_exposed = set()
for pos in open_positions:
for currency in pos['pair'].split('/'):
currencies_exposed.add(currency)
# If new position would over-expose to single currency
new_pair_currencies = set(new_pair.split('/'))
overlap = currencies_exposed & new_pair_currencies
if len(overlap) >= 2:
return False, "Too much correlation"
return True, "OK"Rule 5: Volatility-adjusted sizing
Fixed pip stop loss adalah suboptimal. Gunakan ATR (Average True Range).
Mengapa ATR?
- Pasar tenang: stop loss kecil aman
- Pasar volatile: stop loss kecil = stop hunt magnet
- ATR adapts to current volatility
def calculate_atr_stop_loss(prices, atr_period=14, atr_multiplier=2.0):
"""Stop loss berdasarkan ATR (volatility-adjusted)."""
import pandas as pd
df = pd.DataFrame(prices)
df['tr'] = pd.concat([
df['high'] - df['low'],
abs(df['high'] - df['close'].shift()),
abs(df['low'] - df['close'].shift())
], axis=1).max(axis=1)
df['atr'] = df['tr'].rolling(atr_period).mean()
current_atr = df['atr'].iloc[-1]
stop_distance_pips = current_atr * atr_multiplier / pip_size
return stop_distance_pips
# Lebih aman dari fixed 20 pip stopRule 6: Black swan kill switch
Black swan events akan terjadi: COVID March 2020, SNB CHF January 2015, Brexit June 2016. Bot Anda harus siap.
Kill switch logic:
- If total daily volatility > 3x normal: stop opening new positions
- If account drawdown > 10% intraday: close all positions
- If broker API errors > 5 dalam 5 menit: halt
- If spread > 5x normal: halt new positions
class BlackSwanProtection:
def __init__(self):
self.normal_volatility = None
self.max_drawdown_pct = 10.0
def check_volatility(self, current_atr, normal_atr):
if current_atr > normal_atr * 3:
print("⚠️ Extreme volatility detected. Halting new trades.")
return False
return True
def check_drawdown(self, current_balance, day_start_balance):
drawdown = ((current_balance - day_start_balance)
/ day_start_balance * 100)
if drawdown <= -self.max_drawdown_pct:
print("🚨 KILL SWITCH: Drawdown limit. Closing all.")
return "EMERGENCY_CLOSE_ALL"
return "OK"Rule 7: Weekly review process
Bot tanpa review = bot yang tidak improve. Setiap minggu (Sunday recommended):
Review checklist:
- Total P/L minggu ini
- Win rate vs backtest baseline
- Average win vs average loss
- Maximum drawdown intraweek
- Worst 3 trades: apa penyebabnya?
- Best 3 trades: apa pattern-nya?
- Risk rules violated? Berapa kali bot mendekati limits?
- Bug atau error? Log review.
Tracking di Google Sheets atau Notion. Identifikasi degradation pattern early.
💡 Yellow flag
Jika 4 minggu berturut-turut memburuk dari backtest baseline, strategy mungkin tidak working di current market regime. Pertimbangkan pause atau re-optimization.
Implementasi di Python bot
Combining semua 7 rules dalam Python class:
class TradingBotRiskManager:
def __init__(self, account_balance):
self.account = account_balance
self.day_start_balance = account_balance
self.max_risk_per_trade = 1.0 # %
self.daily_loss_limit = 3.0 # %
self.max_concurrent_positions = 5
self.max_intraday_drawdown = 10.0 # %
def can_open_trade(self, current_positions):
# Rule 1 & 4: Position limits
if len(current_positions) >= self.max_concurrent_positions:
return False, "Max positions reached"
# Rule 3: Daily loss limit
daily_pnl = ((self.account - self.day_start_balance)
/ self.day_start_balance * 100)
if daily_pnl <= -self.daily_loss_limit:
return False, "Daily loss limit hit"
# Rule 6: Black swan
if daily_pnl <= -self.max_intraday_drawdown:
return False, "KILL SWITCH activated"
return True, "OK"
def calculate_position(self, stop_loss_pips, pip_value):
# Rule 1: 1% maximum
risk_amount = self.account * (self.max_risk_per_trade / 100)
position_size = risk_amount / (stop_loss_pips * pip_value)
return position_size
def update_account(self, new_balance):
self.account = new_balanceFAQ
Bisa, tetapi survival rate menurun dramatically. 1% adalah industry standard untuk reasons matematis. Increase ke 2% hanya setelah 1+ tahun konsisten profit dengan 1%.
Concept sama. MQL5 memiliki AccountInfoDouble(ACCOUNT_BALANCE) untuk balance dan OrderSend() dengan SL/TP parameters. Class structure similar tetapi MQL syntax.
Tidak jika risk management ketat. Bot dengan kill switch dapat auto-halt. Tetapi review daily di awal dan weekly review wajib. Tidak ada bot yang “set and forget”.
