The Feature Lab

🦉 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

FeatureLab (in src/trading/feature_lab.py) is AthenaAI’s built-in feature-selection experiment. The feature engine computes 40 core features plus 17 experimental ones whose value is unproven; the Feature Lab watches live trade outcomes and turns off (masks) any experimental feature that shows no difference between winning and losing trades. Core features are never touched.

Data collection

Every resolved win or loss is fed in via record_trade(features, result) — from the result checker in src/bot.py, and replayed from the journal on restart (only for trades whose stored feature vector matches the current 57-feature layout). The lab keeps running sums of each feature’s value separately for wins and losses:

self._win_sums  += features   # when result == "win"
self._loss_sums += features   # when result == "loss"

Draws are ignored.

The review cycle

The bot constructs the lab with review_interval=50 (src/bot.py), so _review() runs every 50 recorded trades — but it returns immediately unless there are at least 20 wins and at least 20 losses accumulated. Below that, per-feature averages are too noisy to act on.

The importance metric

At review time the lab computes each feature’s average value across all winning trades and across all losing trades, and defines importance as the absolute gap:

win_avg  = self._win_sums  / self._win_count
loss_avg = self._loss_sums / self._loss_count
importance = np.abs(win_avg - loss_avg)

Intuition: if a feature’s average is the same whether the trade won or lost, it carries no information about outcomes. (A code comment mentions dividing by a standard deviation, but the implementation is the raw |win_avg − loss_avg| — the values are therefore scale-dependent: a price-difference feature measured in pips will naturally show smaller absolute importance than an oscillator in 0–100 units. Compare importance within a feature, over time, rather than across features of different units.)

Note the sums are cumulative for the life of the process (plus journal replay) — there is no decay or rolling window, so early trades weigh as much as recent ones.

Masking rule

Only experimental features (indices 40–56) are eligible. For each one:

  • importance < 1e-6 → set feature_mask[i] = 0.0 (masked — the feature is zeroed in every future feature vector),
  • otherwise → set feature_mask[i] = 1.0 (re-enabled, even if previously masked).

The threshold is effectively “shows literally zero difference” — constant-output features (e.g. a pattern flag that never fired, or volume_spike on a feed with no volume data) get pruned; anything with measurable signal stays. Because masking zeroes values rather than shortening the vector, the ensemble’s feature dimension never changes and no model reset occurs.

The mask lives only in memory: after a restart all features start active again and the lab rebuilds its statistics (partly from the journal replay).

Reporting

Each review logs, in order:

  1. 🔬 Feature Lab Review (after N trades):
  2. Top 5 most predictive features across the full 57-slot vector (core included), each with its importance and whether its average is higher on wins (↑WIN) or losses (↑LOSS): 🏆 Top 5 most predictive features:rsi: importance=3.2100 (↑WIN)
  3. Bottom 5 least predictive: 📉 Bottom 5 least predictive features:fib_dist_500: importance=0.000000
  4. Mask summary: 🧪 Experimental features: X active, Y masked

get_report() returns a compact status string such as features: 40+15/17exp (core count + active experimental / total experimental).

Tuning and limitations

  • The review interval (50) and the 20-win/20-loss floor are constructor/hard-coded values in src/trading/feature_lab.py and src/bot.py; there is no env var.
  • The 1e-6 threshold only removes dead features. If you want more aggressive pruning, raise it — but remember importance is unit-dependent (see above).
  • Importance here measures marginal separation, not model attribution: a feature can look unimportant alone yet matter in combination. That is why only the explicitly experimental features are candidates for masking.

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.