Architecture
π¦ 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 (src/bot.py, class AITradingBot) is an asyncio application built around three concurrent loops that share in-memory state: a candle stream feeding a deque of recent candles, a decision loop that turns candles into trades, and a result checker that resolves trades and feeds outcomes back into the models.
Component diagram
βββββββββββββββββββββββββββββββββ
β PocketOption (websocket) β
β via BinaryOptionsToolsV2 β
ββββββββ¬ββββββββββββββββ²βββββββββ
candle stream β β buy/sell, check_win
βΌ β
βββββββββββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββ
β AITradingBot (src/bot.py) β
β β
β _candle_stream βββΊ candles: deque(maxlen=lookback) β
β β β
β _trade_loop: βΌ β
β β FeatureEngine ββββ feature vector (core + experimental) β
β β RegimeDetector βββ trending / ranging / volatile β
β β EnsemblePredictorβ direction + confidence β
β β (SGD, PA, NB + GBM, RF, optional MLP) β
β β Signal gates βββββ conf band, alignment, confirmations, β¦ β
β β AdaptiveStrategy β blocked regimes/hours, conf adj, cooldown β
β β MoneyManager βββββ Kelly stake, daily loss limit β
β β ExpirySelector βββ best expiry from expiry_options β
β β buy()/sell() β
β _result_checker: βΌ β
β check_win βββΊ PerformanceTracker, MoneyManager, TradeJournal, β
β AdaptiveStrategy, ExpirySelector, FeatureLab, β
β EnsemblePredictor (online learning) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
athena_brain.pkl athena_brain_expiry.pkl trade_journal.db
(ensemble state) (expiry win stats) (SQLite journal)
Key modules:
| Module | Role |
|---|---|
src/core/feature_engine.py | Turns a candle window into a numeric feature vector (core + experimental features) β see Feature engine |
src/core/models.py | EnsemblePredictor: online + batch models, voting, persistence β see Ensemble |
src/core/regime.py | Market regime classification β see Regime detection |
src/core/expiry.py | ExpirySelector: per-expiry win statistics β see Expiry selection |
src/trading/strategy.py | AdaptiveStrategy: self-review every 25 trades β see Adaptive strategy |
src/trading/money_manager.py | Kelly staking + daily loss limit β see Risk management |
src/trading/journal.py | SQLite trade journal β see Persistence |
src/trading/feature_lab.py | Tracks experimental feature usefulness β see Feature lab |
Startup sequence
bot.start() runs, in order:
- Banner β prints asset, timeframe, expiry options, model roster, confidence band, trading hours, feature counts.
- Brain load or retrain β if
PO_RETRAIN=1, the saved brain file is deleted and retraining is forced. Otherwise_load_brain()triesbrain_path; on success it also loads<brain>_expiry.pkl(expiry stats) and logsβ Loaded saved brain β skipping dataset training!. - Fallback if no brain:
_reload_from_journal()β replays completed trades fromtrade_journal.dbinto the ensemble, restores win/loss stats, and feeds the adaptive strategy._load_dataset(dataset_path)β ifPO_DATASETis set, pre-trains on the CSV with a 70/30 walk-forward split, then saves the brain.
- Connect β creates
PocketOptionAsync(ssid=...), waits 3 seconds for the websocket handshake, logsConnected! Balance: $X.XX. - Warmup candles β fetches history via
get_candles(asset, timeframe, offset_seconds). The offset is seconds of history (effective_warmup Γ timeframe), and warmup is floored at 60 candles because features need at least 50 (SMA50). - Run β
asyncio.gather(_candle_stream(), _trade_loop(), _result_checker()).
The three concurrent loops
1. _candle_stream β market data in
Prefers subscribe_symbol_time_aligned(asset, timedelta(seconds=timeframe)), which delivers completed candles at interval boundaries, so features are always computed on closed candles rather than noisy in-progress ticks. If the installed BinaryOptionsToolsV2 version lacks it, it falls back to subscribe_symbol(asset, timeframe). Each incoming candle is appended to the deque if its timestamp is newer; a candle with the same timestamp replaces the last entry (same candle updating). On stream error the loop logs Candle stream error: ... and sets _running = False, stopping the whole bot.
2. _trade_loop β decisions
Ticks every poll_interval (2 s default) and walks a sequence of gates; the first failing gate skips the tick. A βΈ <reason> diagnostic is logged at most every 30 seconds. Gate order:
- Warmup (
< max(warmup_candles, 60)candles) - Max concurrent trades (
max_concurrent_trades, default 1) - Minimum wait between trades (
min_wait_between_trades, default 60 s) - Daily loss limit (
MoneyManager.can_trade()) - Consecutive-loss cooldown window
- Trading-hours filter (UTC hour must be in
trading_hours) - Adaptive extra cooldown (cold-streak penalty from
AdaptiveStrategy) - Feature computation (skip if
None) - Volatile-regime skip (if
skip_volatile_regime, off by default) - Confidence band:
min_confidence β€ conf β€ max_confidence - Low-range candle skip (candle range < 0.00005, ~0.5 pips on EURUSD)
- Indicator alignment (2-of-3 of RSI / MACD histogram / SMA cross must agree)
- Signal confirmation (
signal_confirmationsconsecutive candles agreeing; default 1) - 5-minute multi-timeframe trend confirmation (SMA5 vs SMA20 on every-5th-candle series)
- Adaptive strategy gate (blocked regimes/hours, direction preference, adjusted threshold)
If everything passes: MoneyManager.compute_stake() sizes the trade, ExpirySelector.select() picks the expiry, the bot logs βΆ TRADE ... and calls client.buy() or client.sell(). The trade is stored in pending_trades, journaled immediately, and its feature vector kept on the record for later learning. Gate details: Signal gates.
3. _result_checker β outcomes and learning
Every 3 seconds it scans pending_trades:
- A trade is only checked once
elapsed β₯ expiry + 2seconds β the 2 s pads for broker settlement. check_win(trade_id)responses are parsed defensively: dict (result/statuskeys) or string, lowercased; if the parsed value isnβtwin/loss/draw, it falls back to substring matching ("win","loss"/"lose","draw") anywhere in the raw response, else retries next cycle.- On a resolved trade: profit is computed (win =
stake Γ 0.85, loss =-stake, draw = 0), then fed to the performance tracker, money manager, journal, adaptive strategy, and expiry selector, and aβ WIN/β LOSS/β DRAWline is logged. - Online learning: the stored features are labeled (win β predicted direction was correct; loss β the opposite direction), added to the ensemble,
partial_fitruns everyretrain_every(10) new results, and batch models (GBM/RF/MLP) retrain on a sliding window of the last 500 samples once at least 200 are buffered (every 100 thereafter). - Every 10 completed trades: a performance snapshot row is written and the brain autosaved.
- Stale abandonment: if
check_winkeeps erroring and the trade is older thanexpiry Γ 5, it is dropped withAbandoning stale trade <id> after timeout(never resolved, never learned from).
Data flow summary
candles β FeatureEngine.compute β nan_to_num β RegimeDetector
β EnsemblePredictor.predict β (direction, confidence)
β gates β MoneyManager.compute_stake β ExpirySelector.select
β buy/sell β pending_trades + journal
β check_win β profit β perf/money/journal/adaptive/expiry stats
β label = win ? direction : 1 - direction β ensemble.add_sample
β partial_fit every 10 results; batch retrain on last 500 samplesFile artifacts
| File | Written by | Contents |
|---|---|---|
athena_brain.pkl (PO_BRAIN_PATH) | trainer, pre-training, autosave every 10 trades, shutdown | Pickled ensemble: scaler, online models, batch models (incl. optional MLP), fitted flags |
athena_brain_expiry.pkl (brain_path with .pkl β _expiry.pkl) | alongside every brain save | ExpirySelector per-expiry win/loss statistics |
trade_journal.db (BotConfig.db_path) | continuously | SQLite: trades and model_snapshots tables |
Full details and how to reset each: Persistence.
Shutdown
Ctrl+C raises KeyboardInterrupt in main.py, which calls bot.stop(): sets _running = False, saves the brain and expiry stats, and logs Bot stopped. Final stats: ....
See also
- Live trading β operating the bot and reading its logs
- Ensemble β how predictions are made
- Persistence β everything on disk
π¬ 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.