TL;DR

  • Layer 1 — Position limits: Cap the dollar amount and share count per position.
  • Layer 2 — Stop losses / take profits: Automatic exit at 2% loss or 4% gain (configurable).
  • Layer 3 — Daily loss cap: Bot stops trading for the day if cumulative losses hit your threshold.
  • Layer 4 — Kelly Criterion: Mathematically optimal position sizing based on signal confidence.
  • Layer 5 — Circuit breakers / kill switch: Auto-pause after consecutive losses; manual emergency stop.
  • All five layers are independent — each one works even if the others fail.

Why Multiple Risk Layers Matter

In any complex system, a single point of failure is a guarantee that something will eventually go wrong. Trading is no different. A stop loss protects you from a bad trade — but what if five bad trades happen in a row? A position limit protects you from oversizing — but what if the market gaps through your stop overnight?

Defense in depth is a principle borrowed from engineering and military strategy: build multiple independent barriers so that a failure in one layer is caught by the next. In slmaj, each risk layer operates independently. They do not depend on each other to function. If one layer fails to trigger for any reason, the others still protect you.

Consider this scenario: The bot opens a position in TSLA based on a high-confidence signal. Minutes later, an unexpected earnings revision is announced, and the stock drops 5% in seconds. Here is how each layer responds:

  • Layer 1 (Position limits) already capped the position at $10,000, limiting the maximum dollar exposure before the trade was even placed.
  • Layer 2 (Stop loss) triggers at -2%, closing the position and capping the loss at $200. But if the stock gaps past the stop price, the actual loss might be $300-$400.
  • Layer 3 (Daily loss cap) adds this loss to the day's running total. If the total crosses the daily limit, the bot stops opening new positions for the rest of the day.
  • Layer 4 (Kelly Criterion) had already sized this position smaller than the maximum because the signal confidence was moderate, not extreme — reducing the realized loss.
  • Layer 5 (Circuit breaker) counts this as a consecutive loss. If two more stops trigger in a row, the bot enters a cooldown period and pauses all trading.

No single layer would have been sufficient. The stop loss limited the per-trade damage but could not prevent the next trade from also losing. The daily cap eventually stopped the bleeding. The circuit breaker added a cooling-off period. Together, a potentially catastrophic day becomes a manageable loss.

Layer 1: Position Limits

Position limits are the simplest and most fundamental risk control. They cap how much capital you can commit to any single trade and how many trades you can have open at once.

Three parameters control this layer:

  • max_position_size — The maximum dollar amount the bot will allocate to a single position. If this is set to $10,000, the bot will never buy more than $10,000 worth of any single stock, forex pair, or crypto asset in one trade.
  • max_positions — The maximum number of concurrent open positions. If this is set to 5, the bot will not open a sixth position until one of the existing five closes.
  • max_shares — An optional hard cap on the number of shares per order. Useful as a secondary safety check to prevent a pricing error from creating an oversized position.
risk:
  max_position_size: 10000   # Maximum $10,000 per position
  max_positions: 5           # Maximum 5 open positions at once
  max_shares: 500            # Never more than 500 shares per order

How to set appropriate limits based on account size:

A common rule of thumb is to risk no more than 2-5% of your total account on any single position. For a $10,000 account, that means max_position_size of $200 to $500. However, this is the amount at risk (factoring in your stop loss), not the total position size. If your stop loss is 2%, then a $10,000 position risks $200 — which is 2% of a $10,000 account.

The relationship is: capital at risk = position size x stop loss percentage. So for a $10,000 account with a 2% stop loss and a goal of risking no more than 2% of account per trade:

# Target: risk 2% of $10,000 account per trade = $200 at risk
# With 2% stop loss: $200 / 0.02 = $10,000 max position
# This means max_position_size can be up to $10,000

# For a more conservative 1% risk per trade:
# $100 / 0.02 = $5,000 max position

For max_positions, consider your total account exposure. Five positions of $10,000 each means $50,000 in total exposure. If your account is $10,000, that is 5x leverage — which IBKR margin may allow but is extremely aggressive. A safer approach is to ensure total exposure stays at or below 100% of account value. For a $10,000 account with $2,000 positions, 5 positions equals $10,000 total — 100% exposure with no leverage.

Layer 2: Stop Losses and Take Profits

Stop losses and take profits are automatic exit orders placed immediately when a position is opened. They define the maximum loss and target gain for every trade, removing emotion from the exit decision.

Two parameters control this layer:

  • stop_loss_pct — The percentage decline from entry price that triggers an automatic sell. Default is 0.02 (2%). If you buy a stock at $100, the stop loss triggers at $98.
  • take_profit_pct — The percentage gain from entry price that triggers an automatic sell. Default is 0.04 (4%). If you buy at $100, the take profit triggers at $104.
risk:
  stop_loss_pct: 0.02      # Exit at -2% loss
  take_profit_pct: 0.04    # Exit at +4% gain

Why the asymmetric ratio matters. The default configuration uses a 2:1 reward-to-risk ratio: risking 2% to gain 4%. This asymmetry is critical. With a 2:1 ratio, you only need to win 34% of your trades to break even. With the ML ensemble targeting win rates above 50%, a 2:1 ratio creates a meaningful statistical edge.

Here is the math: if you win 50% of trades with a 2:1 ratio, your expected profit per trade is (0.50 x 4%) - (0.50 x 2%) = +1.0% per trade. Over 100 trades, that is a cumulative 100% return before accounting for compounding and fees. Even at 45% win rate, you are still profitable: (0.45 x 4%) - (0.55 x 2%) = +0.7% per trade.

Trailing stops. slmaj also supports trailing stops, which move the stop loss upward as the position gains value. Instead of a fixed stop at $98 (2% below $100 entry), a trailing stop moves up with the price. If the stock reaches $105, the trailing stop moves to $102.90 (2% below the new high). This lets winning trades run further while locking in gains.

risk:
  stop_loss_pct: 0.02
  take_profit_pct: 0.04
  trailing_stop: true        # Enable trailing stops
  trailing_stop_pct: 0.02    # Trail 2% below the high

A word of caution: stop losses are not guaranteed exits. In fast-moving markets, a stock can gap past your stop price. If your stop is at $98 and the stock opens the next day at $95 due to overnight news, you get filled at approximately $95, not $98. This is called slippage, and it is why additional risk layers beyond stop losses are essential.

Layer 3: Daily Loss Caps

The daily loss cap is an account-level safeguard that stops all trading activity when cumulative losses for the day exceed a defined threshold. It prevents a bad day from becoming a catastrophic day.

risk:
  daily_loss_limit: 2000    # Stop trading after $2,000 in daily losses

How it works: The bot tracks the running total of realized losses (closed positions that lost money) and unrealized losses (open positions currently in the red) throughout each trading day. When the combined total hits daily_loss_limit, the bot takes the following actions:

  1. Stops generating new trade signals for the rest of the day.
  2. Does not open any new positions.
  3. Existing positions remain open with their stop losses and take profits intact. The bot does not close winning positions just because the daily cap was hit.
  4. Logs a clear warning message indicating the daily loss cap was triggered.

When it resets: The daily loss counter resets at the start of the next trading day (midnight ET for US stocks, or the start of the next forex session for currency pairs). The bot automatically resumes normal trading when the new day begins.

Recommended cap as percentage of account: Most risk management literature recommends a daily loss limit of 1-3% of total account value. For a $10,000 account, that means a daily loss limit of $100 to $300. The default value of $2,000 is appropriate for a $50,000+ account. Adjust this based on your account size:

# Conservative: 1% of account
# $10,000 account -> daily_loss_limit: 100
# $25,000 account -> daily_loss_limit: 250
# $50,000 account -> daily_loss_limit: 500

# Moderate: 2% of account
# $10,000 account -> daily_loss_limit: 200
# $25,000 account -> daily_loss_limit: 500
# $50,000 account -> daily_loss_limit: 1000

# Aggressive: 3% of account
# $10,000 account -> daily_loss_limit: 300
# $25,000 account -> daily_loss_limit: 750
# $50,000 account -> daily_loss_limit: 1500

The daily loss cap works in both paper trading and live trading. During paper testing, watch how often the cap triggers. If it triggers frequently, your position sizes or stop loss settings may be too aggressive for your account size.

Layer 4: Kelly Criterion Position Sizing

The Kelly Criterion is a mathematical formula that calculates the optimal fraction of your capital to allocate to a trade based on the probability of winning and the reward-to-risk ratio. It was developed by John Kelly at Bell Labs in 1956 and has been used by professional gamblers and traders ever since.

The formula:

Kelly % = W - (1 - W) / R

Where:
  W = Probability of winning (from ML model confidence)
  R = Win/loss ratio (take_profit_pct / stop_loss_pct)

Example:
  W = 0.55 (55% win rate from ML ensemble)
  R = 2.0  (4% take profit / 2% stop loss)

  Kelly % = 0.55 - (1 - 0.55) / 2.0
  Kelly % = 0.55 - 0.225
  Kelly % = 0.325 (32.5% of capital)

This means that for a signal with 55% confidence and a 2:1 reward-to-risk ratio, the mathematically optimal position size is 32.5% of your account. In practice, this is too aggressive for most traders.

Half-Kelly for safety. Professional traders almost universally use "Half-Kelly" — taking half the Kelly-recommended position size. This reduces volatility by 75% while only reducing expected returns by about 25%. slmaj implements Half-Kelly by default when Kelly sizing is enabled.

risk:
  use_kelly: true             # Enable Kelly Criterion sizing
  kelly_fraction: 0.5         # Half-Kelly (default)
  max_position_size: 10000    # Hard cap still applies

# With the example above:
# Full Kelly: 32.5% of $10,000 = $3,250
# Half Kelly: 16.25% of $10,000 = $1,625
# The position would be $1,625

How slmaj implements it: When a signal fires, the ML ensemble reports a confidence score (e.g., 0.78). This confidence score becomes the W (win probability) in the Kelly formula. The R (reward-to-risk ratio) comes from your stop loss and take profit settings. The formula calculates the Kelly fraction, which is then halved (Half-Kelly) and multiplied by your account value. The result is capped by max_position_size — Kelly never overrides the hard position limit.

When to use Kelly vs fixed sizing:

  • Use Kelly when your ML signals have varying confidence levels and you want to bet more on higher-confidence signals. Kelly naturally sizes up on strong signals and sizes down on weak ones.
  • Use fixed sizing (Kelly disabled) when you prefer uniform position sizes regardless of signal strength. Every trade gets the same dollar allocation up to max_position_size. This is simpler and may be preferable for beginners.
# Fixed sizing (Kelly disabled)
risk:
  use_kelly: false
  max_position_size: 5000    # Every trade gets up to $5,000

# Kelly sizing (variable based on confidence)
risk:
  use_kelly: true
  kelly_fraction: 0.5
  max_position_size: 10000   # Kelly-calculated size, capped at $10,000

During paper trading, try both approaches and compare the results. Kelly sizing typically produces better risk-adjusted returns but with more variable position sizes, which some traders find uncomfortable.

Layer 5: Circuit Breakers and Kill Switch

Circuit breakers and the kill switch are the final safety nets. They activate when the other layers have not been enough to prevent a serious losing streak, or when you need to manually intervene.

Circuit breaker: The circuit breaker monitors consecutive losing trades. When the number of consecutive losses reaches max_consecutive_losses, the bot enters a cooldown period and stops trading for cooldown_minutes.

risk:
  circuit_breaker:
    max_consecutive_losses: 3    # Pause after 3 losses in a row
    cooldown_minutes: 60         # Wait 60 minutes before resuming

Why this matters: Consecutive losses often indicate that market conditions have changed — a regime shift, unexpected volatility, or a news event the models were not trained on. Pausing gives the market time to stabilize and prevents the bot from chasing losses in unfavorable conditions. After the cooldown period, the bot resumes normal operation and the consecutive loss counter resets.

The circuit breaker is distinct from the daily loss cap. The daily cap measures total dollar losses; the circuit breaker measures consecutive trade outcomes. You could hit the circuit breaker without hitting the daily cap (three small losses in a row), or hit the daily cap without the circuit breaker (one large loss followed by a small win followed by another large loss). Both layers protect you in different scenarios.

Kill switch: The kill switch is a manual emergency stop. When activated, it immediately:

  1. Cancels all pending orders.
  2. Closes all open positions at market price.
  3. Halts the bot completely.
  4. Logs the kill switch activation with a timestamp.

You can activate the kill switch through the web dashboard at localhost:5555 (click the emergency stop button) or by pressing Ctrl+C in the terminal where the bot is running. The bot will gracefully shut down, closing positions before exiting.

# Kill switch is always available — no configuration needed.
# Activate via:
#   1. Web dashboard -> Emergency Stop button
#   2. Terminal -> Ctrl+C
#   3. Config file -> set kill_switch: true and restart

# The bot logs the event:
[2026-02-27 11:45:00] KILL    Kill switch activated
[2026-02-27 11:45:00] KILL    Cancelling 2 pending orders...
[2026-02-27 11:45:01] KILL    Closing 4 open positions at market...
[2026-02-27 11:45:03] KILL    All positions closed. Bot halted.

When to use the kill switch: Use it when something unexpected happens that is beyond the bot's ability to handle — a flash crash, a major geopolitical event, an IBKR connection issue, or any situation where you want to immediately flatten all positions and stop. There is no penalty for using it. Better to stop and reassess than to let positions run in conditions you do not understand.

Putting It All Together

Here is a complete risk configuration for a $10,000 account using moderate settings:

risk:
  # Layer 1: Position Limits
  max_position_size: 5000     # $5,000 max per position (50% of account)
  max_positions: 4            # 4 concurrent positions max
  max_shares: 200             # Hard share cap

  # Layer 2: Stop Losses and Take Profits
  stop_loss_pct: 0.02         # 2% stop loss
  take_profit_pct: 0.04       # 4% take profit (2:1 ratio)
  trailing_stop: true         # Enable trailing stops
  trailing_stop_pct: 0.02     # 2% trailing distance

  # Layer 3: Daily Loss Cap
  daily_loss_limit: 200       # $200/day (2% of account)

  # Layer 4: Kelly Criterion
  use_kelly: true
  kelly_fraction: 0.5         # Half-Kelly

  # Layer 5: Circuit Breakers
  circuit_breaker:
    max_consecutive_losses: 3
    cooldown_minutes: 60

Walking through a bad trading day with this configuration:

Imagine the bot opens the day with four positions. The market turns sharply against all of them.

  1. 9:45 AM — Position 1 (NVDA, $3,200 via Kelly sizing) hits the 2% stop loss. Loss: $64. Daily total: -$64.
  2. 10:15 AM — Position 2 (AAPL, $4,100 via Kelly sizing) hits the 2% stop loss. Loss: $82. Daily total: -$146. Consecutive losses: 2.
  3. 10:30 AM — Position 3 (MSFT, $2,800 via Kelly sizing) hits the 2% stop loss. Loss: $56. Daily total: -$202. Consecutive losses: 3.
  4. 10:30 AMCircuit breaker triggers (3 consecutive losses). Bot enters 60-minute cooldown. No new trades until 11:30 AM.
  5. 10:30 AMDaily loss cap triggers ($202 > $200 limit). Bot stops trading for the rest of the day, regardless of when the circuit breaker cooldown ends.
  6. Position 4 (TSLA, $4,800) remains open with its stop loss intact. If it is in profit, the trailing stop protects the gain. If it hits the stop, the loss is $96 — but no new positions will open.

Result: The worst-case daily loss is approximately $298 (all four stops hit) — about 3% of the account. The daily cap stopped the bot from opening new losing positions after the third stop. The circuit breaker provided an additional pause. At no point was the account at risk of a catastrophic loss.

Conservative vs aggressive presets compared:

Parameter Conservative Moderate Aggressive
max_position_size 20% of account 50% of account 100% of account
max_positions 3 5 8
stop_loss_pct 1.5% 2.0% 3.0%
take_profit_pct 3.0% 4.0% 6.0%
daily_loss_limit 1% of account 2% of account 3% of account
kelly_fraction 0.25 (Quarter) 0.50 (Half) 0.75 (Three-Quarter)
max_consecutive_losses 2 3 5
cooldown_minutes 120 60 30

The setup wizard lets you choose between these presets. You can also customize individual parameters after initial setup. Start conservative and loosen settings only after consistent paper trading results prove the strategy works for your account.

Configuring Risk for Your Account Size

Your account size fundamentally changes how you should configure risk parameters. What works for a $50,000 account can destroy a $2,000 account, and what is appropriate for a $2,000 account would be absurdly conservative for $100,000.

Small accounts: $1,000 - $5,000

Small accounts have very little room for error. A few bad trades can consume a significant percentage of your capital. The priority is survival — staying in the game long enough for the strategy's edge to play out.

# Recommended config for $1,000 - $5,000 accounts
risk:
  max_position_size: 1000     # $1,000 max (20-100% of account)
  max_positions: 2            # Only 2 positions at once
  max_shares: 50
  stop_loss_pct: 0.015        # Tight 1.5% stop
  take_profit_pct: 0.03       # 3% target (2:1 ratio)
  daily_loss_limit: 50        # $50/day (1-5% of account)
  use_kelly: true
  kelly_fraction: 0.25        # Quarter-Kelly for safety
  circuit_breaker:
    max_consecutive_losses: 2
    cooldown_minutes: 120

Key considerations for small accounts: You may be limited by IBKR's minimum order sizes and commissions. Commissions that are negligible on a $10,000 position become significant on a $500 position. Consider focusing on a small number of highly liquid instruments (SPY, QQQ, AAPL) to minimize spread costs. PDT (Pattern Day Trader) rules apply to US stock accounts under $25,000 — you are limited to 3 day trades in a 5-day rolling window unless you have a margin account above $25,000.

Medium accounts: $5,000 - $25,000

Medium accounts can afford more diversification and slightly wider stops. The strategy starts to work as intended at this level because position sizes are large enough for the ML signals to generate meaningful returns while commissions remain a small fraction of trade value.

# Recommended config for $5,000 - $25,000 accounts
risk:
  max_position_size: 5000     # $5,000 max per position
  max_positions: 4            # Up to 4 concurrent positions
  max_shares: 200
  stop_loss_pct: 0.02         # 2% stop loss
  take_profit_pct: 0.04       # 4% take profit
  daily_loss_limit: 250       # $250/day (1-5% of account)
  use_kelly: true
  kelly_fraction: 0.5         # Half-Kelly
  circuit_breaker:
    max_consecutive_losses: 3
    cooldown_minutes: 60

Key considerations for medium accounts: At $25,000+, you clear the PDT threshold and can day trade freely. Below that, be mindful of the 3-trade limit. Forex and crypto are not subject to PDT rules, so those asset classes provide unrestricted trading for smaller accounts. This is a good range for testing all features including multi-asset diversification.

Large accounts: $25,000+

Larger accounts benefit from full diversification across asset classes, wider stops that reduce the chance of being stopped out by normal volatility, and the ability to run the full data source suite for maximum signal quality.

# Recommended config for $25,000+ accounts
risk:
  max_position_size: 10000    # $10,000 max per position
  max_positions: 6            # Up to 6 concurrent positions
  max_shares: 500
  stop_loss_pct: 0.02         # 2% stop loss
  take_profit_pct: 0.04       # 4% take profit
  trailing_stop: true
  trailing_stop_pct: 0.02
  daily_loss_limit: 750       # $750/day (1-3% of account)
  use_kelly: true
  kelly_fraction: 0.5         # Half-Kelly
  circuit_breaker:
    max_consecutive_losses: 3
    cooldown_minutes: 60

Key considerations for large accounts: With more capital, you can trade across all supported asset classes simultaneously — stocks, forex, crypto, and commodities. Trailing stops become more useful at this level because wider positions can capture larger moves. Consider enabling more data sources (API-key sources like Alpha Vantage, Reddit, Finnhub) for higher-quality signals. The additional cost of API subscriptions is negligible relative to the account size.

Rules of thumb that apply to all account sizes:

  • Never risk more than 2% of your account on a single trade (position size x stop loss percentage).
  • Never lose more than 5% of your account in a single day (daily loss limit).
  • Total open exposure should not exceed 100% of account value unless you fully understand margin risk.
  • Start with the conservative preset and tighten only after 4+ weeks of profitable paper trading.
  • When in doubt, make positions smaller. You can always increase later.

Frequently Asked Questions

Can I disable risk controls?

Technically, you can set very permissive values (e.g., daily_loss_limit: 999999), but you cannot fully disable the risk engine. This is intentional. The risk controls exist to protect your capital, and removing them entirely would be reckless. If you find the defaults too restrictive, adjust individual parameters incrementally rather than disabling layers.

What happens if IBKR disconnects while positions are open?

Stop losses and take profits are submitted as orders to IBKR when positions are opened. These orders live on IBKR's servers, not on your machine. If your internet drops or the bot crashes, the stop loss and take profit orders remain active at IBKR and will execute normally. When the bot reconnects, it syncs with the current account state and resumes operation.

Does the Kelly Criterion work with all asset classes?

Yes. The Kelly formula uses signal confidence and your risk/reward ratio, both of which are independent of asset class. Whether the trade is in AAPL stock, EUR/USD forex, or BTC-USD crypto, the math is the same. The ML ensemble produces a confidence score for every signal regardless of instrument type, and Kelly uses that score to size the position.

How do I know if my risk settings are too aggressive?

Three warning signs: (1) the daily loss cap triggers more than once per week during paper testing, (2) the circuit breaker activates multiple times per week, (3) max drawdown exceeds 10% of account value. If any of these occur, reduce max_position_size, tighten stop_loss_pct, or lower daily_loss_limit. See the paper trading guide for evaluation criteria.

Can I set different risk parameters for different asset classes?

Yes. slmaj supports per-asset-class risk overrides in the config file. For example, you might use tighter stops on crypto (which is more volatile) and wider stops on forex (which moves in smaller increments). The global risk settings serve as defaults, and per-asset settings override them. See the features page for more on multi-asset configuration.