src.core.expiry

🦉 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

Source: src/core/expiry.py. ExpirySelector picks the expiry (trade duration in seconds) for each trade by scoring every configured option against six inputs, and it learns over time which durations actually win.

class ExpirySelector

__init__(self, expiry_options: tuple = (60, 120, 180, 300))

ParameterTypeDefaultMeaning
expiry_optionstuple(60, 120, 180, 300)Candidate expiries in seconds. The bot passes BotConfig.expiry_options (default (120, 300, 600))

Sorts the options and initialises stats{expiry: {"wins": 0, "losses": 0}} per option.

select(self, regime: Regime, features: np.ndarray, confidence: float) -> int

ParameterTypeMeaning
regimeRegimeCurrent market regime
featuresnp.ndarrayFeature vector from FeatureEngine; reads indices 6 (volatility), 13 (RSI), 17 (ATR), 24 (ADX), with safe defaults if the vector is short
confidencefloatEnsemble confidence (fraction)

Returns the expiry in seconds with the highest score. Scoring per option (all constants from source):

  1. Regime — trending: ≥300s +3.0, ≥180s +2.0, ≥120s +1.0, else +0.5. Volatile: ≤60s +3.0, ≤120s +2.0, ≤180s +1.0, longer −1.0. Ranging: ≤120s +2.5, ≤180s +2.0, ≤300s +1.0, else +0.5.
  2. Volatility (return std, index 6) — >0.003: ≤120s +1.5, ≥300s −1.0. <0.001: ≥180s +1.0, ≤60s −0.5.
  3. ADX (index 24) — >30: ≥180s +1.5, ≥300s +0.5. <15: ≤120s +1.0.
  4. Confidence — ≥0.75: ≥180s +1.5. ≥0.65: ≥120s +0.5. Lower: ≤120s +1.0, ≥300s −1.0.
  5. RSI extremes (index 13) — RSI >75 or <25 (likely reversal): ≤120s +1.0, ≥300s −0.5.
  6. Past performance — once an option has ≥5 recorded results: score += (win_rate − 0.50) * 5.0 (60% WR → +0.5, 40% → −0.5).

Note the ATR value (index 17) is extracted but not used in any scoring rule. Ties resolve to whichever option max() sees first. No side effects.

record_result(self, expiry: int, result: str) -> None

Increments stats[expiry]["wins"] for "win" or ["losses"] for "loss"; "draw" or anything else is ignored. Unknown expiries get a fresh bucket. Called by _result_checker() in src/bot.py after each resolved trade.

status_line(self) -> str

Returns e.g. "120s:58%(12) | 300s:64%(25)" (win rate and count per option with data), or "no data yet". Used in trade logs.

save_state(self) -> dict / load_state(self, state: dict) -> None

save_state returns {"stats": {...}, "options": [...]}. load_state restores stats (keys coerced back to int — pickle round-trips through the bot keep them intact) but does not restore options; the option list always comes from the constructor/config. The bot persists this dict via pickle to <brain_path> with .pkl replaced by _expiry.pkl (e.g. athena_brain_expiry.pkl) whenever it saves the brain.

Usage example

from src.core.expiry import ExpirySelector
from src.utils.enums import Regime

sel = ExpirySelector(expiry_options=(120, 300, 600))
exp = sel.select(Regime.TRENDING_UP, features, confidence=0.68)  # e.g. 600
sel.record_result(exp, "win")
print(sel.status_line())   # "600s:100%(1)"

See also

💬 Unsure how this interacts with the rest of the configuration? Ask in the Chipa Discord — or prototype strategies no-code with ChipaEditor.