Signal Gates
🦉 AthenaAI is an AI-powered ensemble machine-learning trading bot for PocketOption, built on BinaryOptionsToolsV2 → Get 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
-
Warmup — fewer than
max(warmup_candles, 60)candles collected (default 60). The feature engine and SMA50-based features need history before predictions mean anything. -
Max concurrent trades —
len(pending_trades) >= max_concurrent_trades(default 1). One open position at a time by default. -
Minimum wait between trades — less than
min_wait_between_tradesseconds (default 60) since the last order. -
Daily loss limit —
MoneyManager.can_trade()returns false once the day’s losses reachmax_daily_loss(default $300). Trading stops for the day. -
Cooldown — a previously triggered consecutive-loss cooldown (
_cooldown_until, see gate 15) has not yet expired. -
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. -
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.
-
Feature availability —
FeatureEngine.compute()returnedNone(fewer thanmax(feature_window, 26)candles). The surviving vector is sanitized withnp.nan_to_num. -
Volatile-regime skip — regime is
VOLATILEandskip_volatile_regimeis true. Off by default (Falseinsrc/config.py— “let ML decide”).The ensemble now predicts a direction and confidence; the remaining gates judge that signal.
-
Minimum confidence —
confidence < min_confidence(default 0.63,PO_MIN_CONF). Also clears the signal-confirmation history, so a weak candle restarts any confirmation streak. -
Maximum confidence —
confidence > 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.) -
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. -
Indicator alignment — with
require_indicator_alignmenttrue (default),_check_indicator_alignmentdemands that at least 2 of 3 classic indicators agree with the ML direction:Indicator (feature index) CALL vote if PUT vote if RSI zone (14) rsi < 70(not overbought)rsi > 30(not oversold)MACD histogram (12) macd_hist > 0macd_hist < 0SMA cross, SMA5−SMA20 (10) sma_cross > 0sma_cross < 0Note the RSI check reads feature index 14, which is the −1/0/+1
rsi_zonevalue rather than the raw RSI (index 13) — in practice the zone value always satisfies< 70and> 30, so this vote effectively always passes and alignment hinges on MACD and SMA agreeing. -
Signal confirmation —
_check_signal_readyrequires the lastsignal_confirmationsconsecutive 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 calland 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. -
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. -
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 comparesSMA5vsSMA20of 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. -
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
| # | Gate | Condition to pass | Config knob (default) | Diagnostic log when blocked |
|---|---|---|---|---|
| 1 | Warmup | len(candles) >= max(warmup_candles, 60) | warmup_candles (60) | ⏸ Warming up (n/60 candles) |
| 2 | Max concurrent | open trades < limit | max_concurrent_trades (1) | ⏸ Max trades open (1/1) |
| 3 | Min wait | ≥ N s since last trade | min_wait_between_trades (60 s) | ⏸ Wait between trades (Ns left) |
| 4 | Daily loss | daily loss < limit | max_daily_loss ($300, PO_MAX_DAILY_LOSS) | ⏸ Daily loss limit reached |
| 5 | Cooldown | past _cooldown_until | cooldown_seconds (300 s) | ⏸ Cooldown (Ns left) |
| 6 | Trading hours | UTC hour in whitelist | trading_hours (12 allowed hours) | ⏸ Outside trading hours (HH UTC) |
| 7 | Adaptive cooldown | ≥ extra s since last trade | learned: 120/300 s | ⏸ Adaptive cooldown (Ns) |
| 8 | Features | compute() not None | feature_window (20) | ⏸ Feature compute returned None |
| 9 | Volatile skip | regime not VOLATILE (if enabled) | skip_volatile_regime (False) | ⏸ Volatile regime — skipping |
| 10 | Min confidence | conf >= min_confidence | min_confidence (0.63, PO_MIN_CONF) | ⏸ Low confidence: … |
| 11 | Max confidence | conf <= max_confidence | max_confidence (0.85, PO_MAX_CONF) | ⏸ Overconfident: … |
| 12 | Candle range | high − low >= 0.00005 | none (hard-coded) | ⏸ Low range candle (…) — skip |
| 13 | Indicator alignment | ≥2 of 3 indicators agree | require_indicator_alignment (True, PO_REQUIRE_ALIGNMENT) | ⏸ Indicators misaligned (conf=…) |
| 14 | Signal confirmation | N consecutive candles agree | signal_confirmations (1) | ⏸ Signal confirmation (k/N) … |
| 15 | Consec-loss trigger | streak < max_consec_losses | max_consec_losses (3), cooldown_seconds (300 s) | Hit N consecutive losses → cooldown |
| 16 | 5-min trend | SMA5 vs SMA20 of 5-min samples agrees with direction | none (hard-coded, needs ≥100 candles) | ⏸ 5-min trend DOWN vs CALL — skip |
| 17 | Adaptive strategy | should_trade() returns true | learned (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_confidenceorsignal_confirmationsproduces fewer, stronger signals. - The learned gates (7, 17) tighten automatically after losses and relax after wins — see Adaptive strategy.
See also
- Ensemble predictor — where direction/confidence come from
- Adaptive strategy, Regime detection, Expiry selection
- Configuration guide and per-field pages under reference/config
💡 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.