Ara.AI v8 ranks a universe of stocks by their expected next-day return relative to each other, and holds that view as a dollar-neutral long/short book. It is a gradient-boosted tree ensemble over ~40 scale-free daily features. It trains in about eleven seconds on a CPU, needs no GPU, and the whole GitHub Actions pipeline — fetch, four-fold walk-forward backtest, final fit, publish — finishes in a few minutes.
It replaces v7, a 433K-parameter transformer that predicted each stock's
absolute next-day return, retrained hourly, and had no measured edge:
50.23% direction accuracy against a 51.44% always-up baseline. The full
rationale, the diagnosis, and every number are in
docs/ARA_V8.md. The v7 stack is frozen under
legacy/ and still runs on demand.
Trained models: meridianal/ARA.AI.
Most of a stock's next-day return is the market's next-day return, which daily OHLCV cannot predict — so a model trained on absolute returns spends all its capacity on noise, and "always up" beats it because the market drifts up. v8 subtracts the universe's mean return out of the label and predicts only the residual: which names beat their peers. That target is forecastable, the baseline it must beat is a true 50%, and the natural output is a ranking rather than a price. Switching from a transformer to gradient-boosted trees followed from the same honesty: ~40 weak tabular features is the regime where trees win, and they cost seconds instead of minutes.
Expanding-window walk-forward, 4 folds over 2025-06-02 → 2026-08-06, retrained before each fold with a 1-day embargo. 99 symbols, median 70 names per day, 297 test days. Out-of-sample, reproducible with the command in Quick Start.
| metric | v8 | reference |
|---|---|---|
| mean daily rank IC | +0.0205 | 0.0 = no skill |
| IC t-statistic | +2.26 | > 2 is the significance bar |
| IC hit rate | 56.6% of days | 50% = no skill |
| long/short spread (top-5 vs bottom-5) | +18.6 bp/day | — |
| long/short Sharpe (annualized, pre-cost) | +1.58 | — |
| 1-day reversal baseline | IC +0.0025, −8.4 bp/day | v8 beats it |
Read this carefully. Positive in all four folds, stable across seed groups (IC +0.0204 to +0.0209, t 2.25–2.30), and it beats the naive reversal baseline. But t = 2.26 clears the conventional bar only just, on one window; fold 1 (+0.0405) carries much of the average while the other three sit near +0.013 with t < 1 individually. The long/short numbers are pre-cost and range +12.6 to +18.6 bp/day across seeds — ten names a day is far noisier than the IC it comes from. Trust the IC, treat the P&L as an illustration. A small measured edge, not a trading system.
Earlier versions of this table reported IC +0.0126 (t 1.08) on 50 symbols. Those numbers were measured on a randomly drawn half of the universe — the data fetcher shuffled its symbol list, so every run saw different names. That is fixed; docs/ARA_V8.md has the before/after and separates how much of the improvement is more data versus a more precise measurement.
Universe size is still the top lever: at 5 names per side there are few
independent bets. Extending the symbol list in
scripts/fetch_and_store_data.py is the next step.
Requirements: Python 3.9+. No GPU, no torch.
git clone https://github.com/MeridianAlgo/AraAI.git
cd AraAI
python -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt# Fetch daily bars into training.db
python scripts/fetch_and_store_data.py --db-file training.db --asset-type stock --limit 50
# The honest number: walk-forward backtest (~50 s)
python -m ara eval --db-file training.db --holdout-start 2025-06-01 --folds 4
# Fit on all data and save
python -m ara train --db-file training.db --output models/ara_v8_stocks.joblib
# Rank the most recent day
python -m ara predict --model-path models/ara_v8_stocks.joblibfrom ara import load, load_panel, make_dataset, predict
model = load("models/ara_v8_stocks.joblib")
data = make_dataset(load_panel("training.db", "stock"))
today = data[data["date"] == data["date"].max()].copy()
today["score"] = predict(model, today) # expected return vs the universe
print(today.sort_values("score", ascending=False)[["symbol", "score"]].head(10))score is a predicted residual return: +0.004 means "expected to beat the
universe average by ~40 bp tomorrow", not "expected to rise 0.4%".
daily OHLCV panel -> build_features() -> one row per (symbol, date):
~40 scale-free features + xs_* day-ranks
|
target = fwd_ret - universe_mean(fwd_ret), winsorized at 4σ
|
3x HistGradientBoosting (seed-averaged)
|
predicted residual return -> daily ranking
Every feature is a return, ratio, z-score, or within-day rank — no price or volume levels, which encode symbol identity rather than signal. Eight features additionally carry a cross-sectional percentile rank so the model can ask whether a name is stretched relative to its peers today.
The v7 pipeline fed a 30×44 tensor per sample; v8 feeds one row with lagged
returns as columns, since a tree reads ret_21 directly and never needed the
window. That is most of the 40× memory reduction and the speedup.
| v7 | v8 | |
|---|---|---|
| training time | ~7 min (2000 steps, CPU) | ~11 s |
| CI dependencies | torch, accelerate, comet-ml (~200 MB) | numpy, pandas, scikit-learn (~50 MB) |
| CI runs per day | 48 (hourly stocks + forex) | 1 |
| model | 433,059 parameters | 3 × 400 trees, 15 leaves |
Daily bars change once a day, so 23 of every 24 hourly runs retrained on identical data. The daily schedule is not a compromise; it is the correct one.
ara-v8.yml runs the walk-forward backtest before fitting the shipped
model and fails the run if mean IC is negative (--min-ic 0.0). Nothing
reaches Hugging Face unless it measured a positive out-of-sample signal — which
is precisely how v7 managed to publish an edgeless checkpoint every hour for
months.
ara/
features.py Vectorized panel feature engineering
model.py Dataset, training, evaluation, walk-forward, persistence
__main__.py CLI: python -m ara train|eval|predict
scripts/
fetch_and_store_data.py Market data ingestion into SQLite
push_to_hf.py Uploads models to Hugging Face
hf_download.py 429-aware model download
tests/
test_ara_v8.py Lookahead, symbol-isolation, target, roundtrip, planted-signal
.github/workflows/
ara-v8.yml Daily train + backtest gate + publish
lint.yml Formatting and lint
stocks.yml / forex.yml Legacy v7 pipelines, dispatch-only
legacy/ Frozen v7 transformer stack — see legacy/README.md
- Ara.AI v8 — design rationale, measured numbers, limits
- Legacy v7 — what it was, why it was retired, how to run it
- Local Benchmark Report — the v6/v7 audit that motivated v8
- Quick Start, FAQ, Model Card
- Changelog
v8 is stocks-only. A 22-pair cross-section is too thin to rank, and the source FX bars leak next-day information through day-t high/low — a plain regression on day-t OHL ratios "achieves" 81% sign accuracy on that data, none of it real. The v7 forex pipeline is frozen and dispatch-only rather than ported. Details in LOCAL_BENCHMARK_REPORT.md.
This software is for research and educational purposes only. It is not financial advice.
Trading financial instruments carries significant risk. Every prediction is a probabilistic forecast based on historical data, and past performance does not guarantee future results. Markets can behave in ways no model expects during sudden shocks, liquidity crises, or structural shifts. The performance figures above are pre-cost, pre-slippage, and not statistically significant.
You should never trade with money you cannot afford to lose. Any trading decision you make is yours alone. MeridianAlgo and its contributors are not liable for any financial loss that results from using this software.
The software is provided as is, without warranty of any kind. By using it you agree to hold MeridianAlgo and all contributors harmless from any claim that arises from your use of it. You are responsible for following all financial regulations that apply to you.
Released under the MIT License. See LICENSE for the full text.
Made with care by MeridianAlgo