Risk management

🦉 AthenaAI is an AI-powered ensemble machine-learning trading bot for PocketOption, built on BinaryOptionsToolsV2Get it on GitLab
💬 Need help? Join the Chipa Discord · ⚡ Go no-code with ChipaEditor

AthenaAI’s money management lives in MoneyManager (src/trading/money_manager.py), with additional loss-control gates in the trade loop (src/bot.py) and the adaptive strategy (src/trading/strategy.py). This page walks through each mechanism with the exact formulas and constants from the code.

Stake sizing: fractional Kelly with a confidence multiplier

MoneyManager.compute_stake(confidence, win_rate, payout=0.85) is called before every trade (the bot passes the current overall win rate from its performance tracker and a payout of 0.85). The full formula:

  1. Kelly edge — with p = win rate, q = 1 − p, b = payout:

    edge = p·b − q

    If edge ≤ 0 (or payout ≤ 0), the function returns base_stake immediately — no edge, minimum stake.

  2. Kelly fraction of bankroll analog:

    kelly    = edge / b
    fraction = kelly × kelly_fraction          # kelly_fraction default 0.50
  3. Map onto the configured stake range:

    stake = base_stake + fraction × (max_stake − base_stake)
  4. Confidence multiplier (v6 “gentler scaling”):

    stake ×= max(0.5, (confidence − 0.45) × 2.0)

    That is: 60% confidence → ×0.50, 65% → ×0.65, 70% → ×0.80, 75% → ×0.95, and ×1.0 only at 97.5% (which PO_MAX_CONF would reject anyway). The multiplier never exceeds ~0.8 in the default 63–85% confidence band, so it always shrinks the Kelly stake.

  5. Clamp and round:

    stake = clamp(stake, base_stake, max_stake)   # then rounded to cents

Because the multiplier is below 1 in practice, the final clamp means the stake only rises above base_stake when the win rate is well above breakeven.

Worked example

Config: base_stake = $25, max_stake = $100, kelly_fraction = 0.50. Trade: win rate 75%, confidence 80%, payout 0.85.

edge     = 0.75 × 0.85 − 0.25          = 0.3875
kelly    = 0.3875 / 0.85               = 0.4559
fraction = 0.4559 × 0.50               = 0.2279
stake    = 25 + 0.2279 × (100 − 25)    = $42.10
mult     = max(0.5, (0.80 − 0.45) × 2) = 0.70
stake    = 42.10 × 0.70                = $29.47
clamp(25, 29.47, 100)                  → stake = $29.47

Same trade at a 60% win rate: edge = 0.11, kelly = 0.129, stake = 25 + 0.0647 × 75 = $29.85, ×0.70 = $20.90 → clamped up to $25.00 (base). Early on — the performance tracker starts around 50% — nearly every stake is exactly base_stake, so treat PO_BASE_STAKE as your effective per-trade risk.

Note: stake sizing uses payout 0.85 as a hardcoded argument in src/bot.py, not a live payout quote; results are also booked at 0.85.

Daily loss limit

MoneyManager accumulates realized profit into daily_pnl. Before every trade, can_trade() requires:

daily_pnl > −max_daily_loss

Once cumulative losses reach PO_MAX_DAILY_LOSS ($300 default), the trade loop logs ⏸ Daily loss limit reached and stops opening trades. The counter resets when the UTC calendar date changes (New day — resetting daily P&L tracker) — not at your local midnight. The counter is in-memory only: restarting the bot resets it, so don’t restart to “unlock” trading after a bad day.

Consecutive-loss cooldown

Tracked by the performance tracker and enforced in the trade loop: after max_consec_losses (default 3) losses in a row, the bot logs Hit N consecutive losses → cooldown 300s and blocks trading for cooldown_seconds (default 300 s = 5 minutes), then resets the streak counter. Both values are config-only fields (max-consec-losses, cooldown-seconds).

Adaptive cooldowns

Independently of the fixed cooldown, AdaptiveStrategy reviews the last trades every 25 results and sets an extra minimum wait between trades based on the rolling last-10 win rate:

Recent-10 win rateExtra cooldownLog
< 30%300 s🥶 Recent WR X% — adding 5min cooldown
< 40%120 s😐 Recent WR X% — adding 2min cooldown
≥ 40%0 s(cleared; 🔥 On fire! above 65%)

While active you’ll see ⏸ Adaptive cooldown (Ns) diagnostics. The adaptive layer can also block whole regimes and UTC hours and shift the confidence threshold — see Adaptive strategy.

Concurrency and pacing limits

  • Max concurrent trades: max_concurrent_trades (default 1) — no new trade while one is pending. Diagnostic: ⏸ Max trades open (1/1).
  • Minimum wait between trades: min_wait_between_trades (default 60 s) from the moment the last trade was placed. Diagnostic: ⏸ Wait between trades (Ns left).

Both are config-only fields (max-concurrent-trades, min-wait-between-trades).

Trading-hours filter

trading_hours (config-only, trading-hours) whitelists UTC hours. Default: (0, 1, 2, 3, 4, 16, 17, 19, 20, 21, 22, 23) — the code comment attributes the exclusion of 08–15 UTC (and 5–7, 18) to a 546-trade analysis showing consistent losses in those hours. Outside the whitelist the bot logs ⏸ Outside trading hours (HH UTC) and does nothing. Set trading_hours to an empty tuple to trade around the clock (the banner then shows Hours: all).

Layered defense summary

per trade   : stake = fractional-Kelly × confidence, clamped [base, max]
per streak  : 3 losses → 300 s cooldown; cold last-10 WR → +120/300 s
per day     : stop at −$max_daily_loss (UTC-day reset)
always      : ≤1 open trade, ≥60 s between trades, UTC-hours whitelist

Risk note

These mechanisms limit — but cannot eliminate — losses. Binary options risk the entire stake on every trade; a full losing day still costs max_daily_loss, and the daily limit resets on restart because it is not persisted. Past performance, including any win-rate statistics the bot itself reports, does not guarantee future results. Size PO_BASE_STAKE, PO_MAX_STAKE, and PO_MAX_DAILY_LOSS to amounts you can afford to lose.

See also

💬 Questions about running AthenaAI? Ask in the #trading-bots channel on the Chipa Discord — and if you’d rather design strategies without touching Python, try ChipaEditor.