Signal Gates

🦉 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 is deliberately reluctant to trade. Every iteration of _trade_loop in src/bot.py (running every poll_interval seconds, default 2.0) pushes the current market state through a fixed pipeline of gates, in code order. A single failed gate means continue — no trade this cycle. Only when every gate passes does the bot place an order (log line ▶ TRADE ...).

Blocked cycles are quiet by default: a diagnostic reason is logged at most once every 30 seconds (the ⏸ ... lines), so a silent bot is usually just a gated bot — see Troubleshooting.

The pipeline, in code order

  1. Warmup — fewer than max(warmup_candles, 60) candles collected (default 60). The feature engine and SMA50-based features need history before predictions mean anything.

  2. Max concurrent tradeslen(pending_trades) >= max_concurrent_trades (default 1). One open position at a time by default.

  3. Minimum wait between trades — less than min_wait_between_trades seconds (default 60) since the last order.

  4. Daily loss limitMoneyManager.can_trade() returns false once the day’s losses reach max_daily_loss (default $300). Trading stops for the day.

  5. Cooldown — a previously triggered consecutive-loss cooldown (_cooldown_until, see gate 15) has not yet expired.

  6. Trading hours — the current UTC hour is not in trading_hours (default (0,1,2,3,4,16,17,19,20,21,22,23); the v6 comment notes 08–15 UTC lost money consistently over a 546-trade analysis). An empty tuple disables this gate.

  7. Adaptive cooldown — the adaptive strategy has imposed an extra cooldown (120 s or 300 s) after a cold streak, and not enough time has passed since the last trade.

  8. Feature availabilityFeatureEngine.compute() returned None (fewer than max(feature_window, 26) candles). The surviving vector is sanitized with np.nan_to_num.

  9. Volatile-regime skip — regime is VOLATILE and skip_volatile_regime is true. Off by default (False in src/config.py — “let ML decide”).

    The ensemble now predicts a direction and confidence; the remaining gates judge that signal.

  10. Minimum confidenceconfidence < min_confidence (default 0.63, PO_MIN_CONF). Also clears the signal-confirmation history, so a weak candle restarts any confirmation streak.

  11. Maximum confidenceconfidence > max_confidence (default 0.85, PO_MAX_CONF). Overconfident signals are treated as overfit noise. Also clears the signal history. (The ensemble already clamps confidence to 0.78, so this gate only fires if you lower the cap.)

  12. Low-range candle — the latest candle’s high − low < 0.00005 (about half a pip on EURUSD). Doji/indecision candles produce near-random outcomes. Hard-coded threshold.

  13. Indicator alignment — with require_indicator_alignment true (default), _check_indicator_alignment demands that at least 2 of 3 classic indicators agree with the ML direction:

    Indicator (feature index)CALL vote ifPUT vote if
    RSI zone (14)rsi < 70 (not overbought)rsi > 30 (not oversold)
    MACD histogram (12)macd_hist > 0macd_hist < 0
    SMA cross, SMA5−SMA20 (10)sma_cross > 0sma_cross < 0

    Note the RSI check reads feature index 14, which is the −1/0/+1 rsi_zone value rather than the raw RSI (index 13) — in practice the zone value always satisfies < 70 and > 30, so this vote effectively always passes and alignment hinges on MACD and SMA agreeing.

  14. Signal confirmation_check_signal_ready requires the last signal_confirmations consecutive candles (default 1 = instant) to agree on the same direction. One signal is recorded per unique candle timestamp; a repeat check on the same candle returns false (wait for the next candle). With N > 1 you’ll see 📡 Signal building: k/N candles agree on call and finally ✅ Signal CONFIRMED: call xN candles avg_conf=…%. Mixed directions reset the wait. History is cleared after a confirmed signal and after any confidence-gate failure.

  15. Consecutive-loss cooldown trigger — if perf.consec_losses >= max_consec_losses (default 3), set _cooldown_until = now + cooldown_seconds (default 300 s), reset the counter, and skip. Future cycles then fail gate 5 until the cooldown expires. Log: Hit N consecutive losses → cooldown 300s.

  16. 5-minute multi-timeframe trend — if at least 100 candles exist, the loop samples every 5th candle (candle_list[::5]) to synthesize a 5-minute view, and when at least 20 such samples exist compares SMA5 vs SMA20 of the sampled closes. A CALL is rejected if the 5-minute trend is down (sma5_5m <= sma20_5m); a PUT is rejected if it is up. No config knob; skipped entirely with fewer than 100 candles.

  17. Adaptive should_trade — the adaptive strategy’s final veto: blocked regime, blocked hour, non-preferred direction without +0.08 extra confidence, or confidence below min_confidence + confidence_adj. Log: 🧠 Adaptive skip: <reason>.

After gate 17 the bot sizes the stake (Kelly-based, see Risk management), asks the expiry selector for a duration, and places the order.

Summary table

#GateCondition to passConfig knob (default)Diagnostic log when blocked
1Warmuplen(candles) >= max(warmup_candles, 60)warmup_candles (60)⏸ Warming up (n/60 candles)
2Max concurrentopen trades < limitmax_concurrent_trades (1)⏸ Max trades open (1/1)
3Min wait≥ N s since last trademin_wait_between_trades (60 s)⏸ Wait between trades (Ns left)
4Daily lossdaily loss < limitmax_daily_loss ($300, PO_MAX_DAILY_LOSS)⏸ Daily loss limit reached
5Cooldownpast _cooldown_untilcooldown_seconds (300 s)⏸ Cooldown (Ns left)
6Trading hoursUTC hour in whitelisttrading_hours (12 allowed hours)⏸ Outside trading hours (HH UTC)
7Adaptive cooldown≥ extra s since last tradelearned: 120/300 s⏸ Adaptive cooldown (Ns)
8Featurescompute() not Nonefeature_window (20)⏸ Feature compute returned None
9Volatile skipregime not VOLATILE (if enabled)skip_volatile_regime (False)⏸ Volatile regime — skipping
10Min confidenceconf >= min_confidencemin_confidence (0.63, PO_MIN_CONF)⏸ Low confidence: …
11Max confidenceconf <= max_confidencemax_confidence (0.85, PO_MAX_CONF)⏸ Overconfident: …
12Candle rangehigh − low >= 0.00005none (hard-coded)⏸ Low range candle (…) — skip
13Indicator alignment≥2 of 3 indicators agreerequire_indicator_alignment (True, PO_REQUIRE_ALIGNMENT)⏸ Indicators misaligned (conf=…)
14Signal confirmationN consecutive candles agreesignal_confirmations (1)⏸ Signal confirmation (k/N) …
15Consec-loss triggerstreak < max_consec_lossesmax_consec_losses (3), cooldown_seconds (300 s)Hit N consecutive losses → cooldown
165-min trendSMA5 vs SMA20 of 5-min samples agrees with directionnone (hard-coded, needs ≥100 candles)⏸ 5-min trend DOWN vs CALL — skip
17Adaptive strategyshould_trade() returns truelearned (base: min_confidence)🧠 Adaptive skip: …

Tuning notes

  • The static, money-related brakes (gates 2–6, 15) are your primary risk controls — see Risk management. Risk note: loosening them increases the money at risk per day; binary options losses are the full stake.
  • The signal-quality gates (10–14, 16) trade frequency for selectivity. Raising min_confidence or signal_confirmations produces fewer, stronger signals.
  • The learned gates (7, 17) tighten automatically after losses and relax after wins — see Adaptive strategy.

See also

💡 Want to discuss how AthenaAI makes decisions — or design your own logic like this without writing code? Join the Chipa Discord and give ChipaEditor a try.