Educational content only — not financial advice. Trading involves substantial risk of loss.
Advanced Reference · ML/AI Libraries & Models · Algorithmic Trading

Advanced AI Trading Guide: The Complete ML Library, Model & Framework Reference for Quants

A practitioner-level map of the open-source and Hugging Face ecosystem for systematic trading — gradient boosting, deep time-series architectures, zero-shot foundation models, financial NLP, reinforcement learning, López de Prado's validation toolkit, backtesting engines, and MLOps — with architecture notes, licenses, benchmarks, and code for each.

5-Day Free Trial
No Card Required
Unlimited Trades
Instant Execution

Classical / tabular ML — the workhorses

Gradient-boosted decision trees (GBDTs) — not deep nets — are the default model family for every tabular classification/regression task in this pipeline: meta-labeling, regime features, factor scoring. A widely-cited 2024 comparison across 100–300+ tabular benchmarks found GBDTs match or beat deep learning on tabular data with far less tuning (arXiv:2402.03970). Financial tabular data is exactly the regime where this holds hardest: low signal-to-noise, mixed continuous/categorical/sparse features, heavy non-stationarity, and small effective sample sizes — conditions where deep nets' flexibility mostly buys variance, not signal.

Why GBDTs dominate tabular financial data

  • Bias-variance trade-off. With a few thousand effective (uniqueness-weighted) rows and a weak, noisy signal, a high-capacity net's low bias comes at a variance cost you cannot afford. Shallow trees with strong regularization (leaf count, min-data-in-leaf, bagging) sit at a far better point on that trade-off.
  • Mixed and sparse features natively. Trees split on raw feature values — no scaling, no imputation strategy, no embedding table for rarely-populated categoricals (sector, exchange, expiry-proximity flag). Feature engineering effort goes into signal, not preprocessing plumbing.
  • Monotonic constraints encode financial priors directly. LightGBM/XGBoost/CatBoost all support monotone_constraints: e.g. force P(win) to be non-decreasing in trend-strength and non-increasing in realized volatility. This regularizes toward domain-sane models and is unavailable, or far clunkier, in a standard MLP.
  • Missing values and non-stationarity. Trees route missing values natively (no leakage-prone imputation fit on the full dataset), and shallow, frequently-retrained trees adapt faster to regime shift than a large net that needs many epochs to unlearn stale patterns.
LibraryLicenseMaturity / stars*Categorical handlingSpeedBest fit here
LightGBMMITMature · moved microsoft/LightGBMlightgbm-org/LightGBM Mar 2026, still MITNative (histogram-based, leaf-wise growth)Fastest of the three on CPU, large-NDefault meta-label / primary classifier
XGBoostApache-2.0Mature, dmlc-maintained, huge ecosystemNeeds one-hot or target-encoding (or enable_categorical)Slightly slower than LightGBM on CPU histogram modeSecond opinion / ensembling partner for LightGBM
CatBoostApache-2.0Mature, Yandex-maintainedBest-in-class native categorical handling (ordered target stats)Slower to train, fast to scoreWhen you have many high-cardinality categoricals (symbol, sector, exchange)
scikit-learnBSD-3-ClauseFoundational, ~19+ yearsVia ColumnTransformer/OneHotEncoderN/A — utility layerCV splitters, metrics, pipelines, calibration, GMM — the glue, not the model

*Star counts are directional, checked mid-2026 — treat as "order of magnitude," not exact.

LightGBM

MIT

Mechanism: histogram-based gradient boosting with leaf-wise (best-first) tree growth instead of level-wise — converges faster and usually to lower loss than level-wise growth for a given leaf budget, at the cost of being more prone to overfitting on small data (control via num_leaves/min_data_in_leaf).

When to use: default choice for the meta-label filter and any tabular classification/regression in this stack — fastest CPU training, native missing-value routing, native monotonic constraints. When not to: very small samples (< ~500 rows) where leaf-wise growth overfits fast; prefer level-wise XGBoost or a simple logistic regression there.

Python · LightGBM meta-label training on triple-barrier labels
import lightgbm as lgb
from sklearn.model_selection import train_test_split

# X: one row per primary-strategy signal date. Columns e.g.
#   log_ret_5d, log_ret_20d, realized_vol_20d, atr_14, frac_diff_price,
#   ema_slope, rsi_14, bb_pctb, obv_z, dist_200ma
# y: triple-barrier label from mlfinpy -> 1 = profit barrier hit first, 0 = stop/time first
# w: sample weight from mlfinpy uniqueness/time-decay (NEVER plain 1s -- labels overlap)
X, y, w = features_df[feature_cols], labels_df["tb_label"], labels_df["sample_weight"]

X_train, X_val, y_train, y_val, w_train, w_val = train_test_split(
    X, y, w, test_size=0.2, shuffle=False              # never shuffle time series
)

params = {
    "objective": "binary", "metric": "auc",
    "num_leaves": 31, "learning_rate": 0.03,
    "feature_fraction": 0.7, "bagging_fraction": 0.7, "bagging_freq": 5,
    "min_data_in_leaf": 50,
    # financial priors: e.g. [trend, vol, ...] -> win-prob non-decreasing in trend,
    # non-increasing in realized vol, unconstrained (0) elsewhere
    "monotone_constraints": [1, 0, -1, 0, 0, 1, 0, 0, 0, -1],
    "verbosity": -1,
}
train_set = lgb.Dataset(X_train, label=y_train, weight=w_train)
val_set   = lgb.Dataset(X_val, label=y_val, weight=w_val, reference=train_set)

model = lgb.train(params, train_set, num_boost_round=2000,
                   valid_sets=[val_set], callbacks=[lgb.early_stopping(100)])

meta_label_proba = model.predict(X_new)  # P(this signal hits the profit barrier first)

XGBoost

Apache-2.0

Mechanism: level-wise (depth-wise) boosted trees with a regularized objective (L1/L2 on leaf weights) baked into the split-finding criterion itself, plus a well-optimized histogram/quantile-sketch split algorithm for large data.

When to use: as a second, differently-biased model for ensembling with LightGBM (level- vs leaf-wise growth decorrelates errors usefully); GPU training if you outgrow CPU. When not to: if you need first-class native categorical handling without preprocessing — CatBoost is better there.

Bash · install
pip install xgboost
# minimal use: xgboost.XGBClassifier(n_estimators=800, max_depth=4, learning_rate=0.03,
#   subsample=0.7, colsample_bytree=0.7, monotone_constraints="(1,0,-1,0,0,1,0,0,0,-1)")

CatBoost

Apache-2.0

Mechanism: ordered boosting (a permutation-driven scheme that avoids the target leakage classic greedy target-encoding suffers from) plus native ordered target statistics for categorical features — no manual encoding step, and less prone to prediction shift than naive target encoding.

When to use: cross-sectional stock-basket models with many categorical features (sector, industry, exchange, index-membership flags) where you'd otherwise hand-roll target encoding. When not to: tight CPU training-time budgets — CatBoost is typically the slowest of the three to fit, though fast to score.

Bash · install
pip install catboost
# CatBoostClassifier(cat_features=["sector","exchange"], iterations=1500,
#   depth=6, learning_rate=0.03, monotone_constraints="1:1,2:-1")

scikit-learn

BSD-3-Clause

Role here: not a competing model family but the connective tissue — Pipeline for leakage-safe in-fold preprocessing, GroupKFold/custom purged splitters, GaussianMixture for a lightweight regime baseline, CalibratedClassifierCV for turning GBDT scores into honest probabilities before you size positions off them (raw GBDT outputs are ranked well but poorly calibrated).

Calibrate before you size

A GBDT's predict_proba output is a good ranking signal but is usually not a well-calibrated probability out of the box — GBDTs tend to push predictions toward 0/1. If your position sizer consumes "confidence" directly (fractional-Kelly, meta-label-scaled size), wrap the model in sklearn.calibration.CalibratedClassifierCV (isotonic or Platt) fit inside the CV fold, or your sizing will be systematically overconfident.

Deep-learning time-series architectures

These are the from-scratch neural architectures you'd train yourself (typically via neuralforecast, Apache-2.0, which implements all of them), as opposed to the pretrained foundation models in §3. Every one of them was benchmarked into existence on M4/M3/electricity/traffic/ETT — none were designed against financial return series, and the honest verdict per architecture reflects that.

ArchitectureYear / venueCore ideaProblem vs. plain LSTMFinancial-data verdict
LSTM / GRU1997 / 2014Gated recurrent units carry a cell state across time steps to fight vanishing gradients.— (baseline)Still a reasonable baseline for sequence classification (e.g. regime state); rarely beats GBDT on tabular-framed return prediction and is slower to train/tune.
TCNBai et al. 2018Stacked causal, dilated 1-D convolutions (WaveNet-style) give an exponentially growing receptive field with fully parallel training.Parallelizable (no sequential recurrence) and avoids vanishing/exploding gradients over long lookbacks.Solid, fast baseline in neuralforecast; no documented financial edge over GBDT, but useful when you specifically need a long causal receptive field cheaply.
N-BEATSOreshkin et al. 2020 (ICLR)Pure fully-connected stacks with backward/forward residual "basis expansion" blocks; interpretable trend/seasonality decomposition, no recurrence or attention.Much simpler and more parallel than RNN/attention stacks; beat the M4-competition winner by ~3% and a statistical benchmark by ~11%.Benchmarks are M3/M4/tourism — non-financial. Useful as a fast, interpretable univariate baseline; not evidence of edge on returns.
N-HiTSChallu et al. 2023 (AAAI)Extends N-BEATS with hierarchical interpolation and multi-rate input sampling so different blocks specialize in different frequency bands.Cuts long-horizon compute and reportedly beats Transformer baselines by >25% on long-horizon benchmarks.Same caveat as N-BEATS — a strong long-horizon forecaster on benchmark data, unproven on financial returns specifically.
Temporal Fusion Transformer (TFT)Lim et al. 2021 (Int. J. Forecasting)LSTM encoder for local processing + interpretable multi-head self-attention for long-range dependencies + a variable-selection gating network that also yields feature-importance-like interpretability.Natively handles static covariates, known-future inputs (e.g. expiry date), and produces quantile forecasts with built-in interpretability — none of which vanilla LSTM offers.The most finance-plausible of the deep architectures because of native covariate support; still no peer-reviewed evidence of consistent OOS financial-return edge over GBDT meta-labeling.
InformerZhou et al. 2021 (AAAI, best paper)ProbSparse self-attention selects only the top-scoring queries, cutting attention cost from O(L²) to O(L log L) for very long sequences.Makes long-sequence (thousands of steps) transformer forecasting computationally tractable.Designed for long-horizon benchmark forecasting, not noisy short-memory financial returns; see the DLinear caveat below.
AutoformerWu et al. 2021 (NeurIPS)Replaces standard attention with an auto-correlation mechanism plus built-in series decomposition (trend/seasonal) between blocks.Captures periodicity more directly than dot-product attention; more robust on strongly seasonal series.Financial returns have weak/no reliable seasonality at daily/swing frequency — decomposition assumption is a weaker fit than on electricity/traffic data.
FEDformerZhou et al. 2022 (ICML)Performs attention in the frequency domain (via Fourier/wavelet transforms) with a mixture-of-experts decomposition for trend.Frequency-domain attention scales sub-quadratically and captures global periodic structure.Same seasonality-dependence caveat as Autoformer; frequency-domain priors are a mismatch for near-random-walk return series.
PatchTSTNie et al. 2023 (ICLR)Splits each series into subseries "patches" as transformer tokens (like ViT for time series) and treats each channel independently, sharing one transformer backbone across all channels.Patch tokenization retains local semantics and cuts attention cost quadratically vs. point-wise tokens; channel-independence avoids spurious cross-channel overfitting. Reported ~21% MSE reduction over prior transformer SOTA on long-horizon benchmarks.The architectural basis for several foundation models below (see §3); channel-independence is a genuinely useful inductive bias, but the MSE gains are on ETT/weather/traffic, not equities.
iTransformerLiu et al. 2024 (ICLR spotlight)"Inverts" the standard transformer: each variate's whole time series becomes one token, and attention runs across variates instead of across time steps, with a per-series FFN handling temporal encoding.Captures multivariate correlations directly via attention without modifying core transformer components; generalizes to unseen variate counts.Notably, in a 2026 financial-return benchmark (arXiv:2606.27100, see §3), iTransformer beat the pretrained foundation models on META — a rare case of a from-scratch architecture outperforming zero-shot foundation models on a specific liquid single-name equity.
The DLinear caveat — read before reaching for any transformer

Zeng et al., "Are Transformers Effective for Time Series Forecasting?" (AAAI-23 Oral; code: cure-lab/LTSF-Linear) showed that a single linear layer with trend/seasonal decomposition (DLinear) beat Informer, Autoformer, and FEDformer on almost every standard long-horizon benchmark, arguing their reported gains came mostly from the non-autoregressive direct-multistep training strategy, not from attention itself. Hugging Face's follow-up analysis pushed back somewhat (parameter-matched transformers do better), but the practical lesson holds: always benchmark any deep architecture against a linear/ARIMA baseline (Nixtla statsforecast, §11) before trusting a complexity-driven accuracy gain. This is doubly true on financial data, where noise dominates signal far more than on electricity or traffic data.

Time-series foundation models on Hugging Face

Pretrained, zero-shot time-series forecasters are real and useful — for volatility, not price direction. A June 2026 study built specifically to test this, "Pretrained Time-Series Foundation Models for Financial Return Forecasting" (Noguer i Alonso & Pereira Franklin, AI Finance Institute), benchmarked TimeGPT, TimesFM-2.5, Moirai-2.0, Chronos, and Chronos-2 against from-scratch NBEATS/NHITS/PatchTST/iTransformer/KAN on five liquid US equities (AAPL, AMZN, GOOG, JPM, META) with a rolling-origin protocol against a random-walk benchmark. The result: across ten asset/task combinations, only two showed statistically significant outperformance of the random walk — Chronos on AMZN and Moirai-2.0 on GOOG — and the authors state plainly that "gains over the random-walk benchmark are small and sparse," concluding TSFMs are "useful practical priors that reduce model-development costs," but explicitly "not universal engines for statistically reliable alpha generation." Treat every model below accordingly: excellent, CPU-runnable engines for volatility/vol-forecast inputs to a sizing layer; weak-to-random-walk for price direction.

ModelOrgArchitecture familyParamsContextLicense
Chronos / Chronos-BoltAmazon ScienceT5-style encoder-decoder over quantized/tokenized values (Chronos) or direct patch-based multi-step regression heads (Bolt)Bolt-base 205M (mini→large family)Model-dependent, typically up to ~2048 ptsApache-2.0
TimesFMGoogle ResearchDecoder-only transformer over fixed-length input/output patches (32-in / 128-out patch config)200M (v1.0) / 500M (v2.0)Up to 2048 pts trained contextApache-2.0
Moirai / Moirai-MoESalesforceMasked-encoder transformer with "any-variate" attention (flattens multiple series into one token sequence so it natively handles arbitrary channel counts and covariates)Small/Base/Large (14M–311M)Flexible, any positive lengthCC BY-NC-4.0
Lag-LlamaServiceNow / Morgan Stanley / Mila et al.LLaMA-style decoder-only transformer fed engineered lag features (not raw tokenized values) as its "vocabulary"~2.45M (deliberately tiny)Recommended 32–1024 pts, tunableApache-2.0
MOMENTAuton Lab, CMUTransformer trained multi-task (forecast + classify + detect anomalies + impute) on the "Time-series Pile" corpusSmall/Base/Large (~40M–341M)Task-dependentMIT
IBM Granite-TimeSeries (PatchTSMixer / PatchTST / TTM)IBM ResearchPatchTSMixer = all-MLP mixer over patches (no attention); PatchTST variant = patch-transformer; TTM = "Tiny Time Mixer," a very small pretrained mixerPatchTSMixer ~1M (task head), TTM sub-5M512-in / 96-out typical configsApache-2.0

Amazon Chronos / Chronos-Bolt

Apache-2.0 · 205M

Model card: huggingface.co/amazon/chronos-bolt-base (family also includes chronos-t5-{tiny,mini,small,base,large} and the newer amazon/chronos-2).

Architecture: the original Chronos tokenizes scaled, quantized time-series values into a fixed vocabulary and runs them through a standard T5 encoder-decoder — i.e. it treats forecasting as language modeling over a discretized numeric alphabet. Chronos-Bolt replaces autoregressive token-by-token decoding with direct multi-step patch regression, reported as up to ~250× faster and ~20× more memory-efficient at comparable or better accuracy (measured via WQL/MASE across 27 benchmark datasets), trained on order ~100B time-series observations.

Verdict: best all-round pick for a CPU-only volatility-forecast input — Apache-2.0, fast, zero-shot, and the one model in the 2606.27100 benchmark that reached significance (on AMZN). Still: one significant result out of ten trials is not evidence of a repeatable directional edge.

Python · zero-shot volatility forecast with Chronos-Bolt
pip install chronos-forecasting torch

import torch
from chronos import BaseChronosPipeline

pipeline = BaseChronosPipeline.from_pretrained(
    "amazon/chronos-bolt-base",     # T5-style encoder-decoder, 205M params, Apache-2.0
    device_map="cpu",               # CPU is fine for daily-bar, small-batch inference
    torch_dtype=torch.bfloat16,
)

# Feed REALIZED VOLATILITY, not raw price -- this is a vol forecaster, not an oracle
context = torch.tensor(realized_vol_20d.values[-512:])

quantiles, mean = pipeline.predict_quantiles(
    context=context,
    prediction_length=10,                   # 10-day-ahead vol forecast
    quantile_levels=[0.1, 0.5, 0.9],
)
vol_forecast_median = quantiles[0, :, 1]    # feed straight into GARCH/vol-target sizing (see §10)

Google TimesFM

Apache-2.0 · 200M/500M

Model card: huggingface.co/google/timesfm-2.0-500m-pytorch (ICML 2024). Architecture: decoder-only transformer trained to consume fixed-length input patches (32 points) and emit longer output patches (128 points) directly — closer to a language model's next-token objective than Chronos's tokenized-value approach, but operating on continuous patch embeddings rather than a discretized vocabulary.

Verdict: along with Moirai-2.0, had the strongest average rank across the five-equity benchmark (leading on AAPL/JPM/GOOG/AMZN) — but per the paper's own framing, strong rank ≠ significant edge; none of TimesFM's wins cleared the random-walk significance bar. Good, well-maintained, Apache-2.0, fine for volatility; not a green light for price direction.

Bash · install
pip install timesfm
# tfm = timesfm.TimesFm(context_len=512, horizon_len=10, backend="cpu")
# tfm.load_from_checkpoint(huggingface_repo_id="google/timesfm-2.0-500m-pytorch")
# point_fc, quantile_fc = tfm.forecast([realized_vol_20d.values], freq=[0])

Salesforce Moirai / Moirai-MoE

CC BY-NC-4.0

Model card: huggingface.co/Salesforce/moirai-1.0-R-large. Architecture: masked-encoder transformer with "any-variate attention" — instead of a fixed per-channel embedding, it flattens an arbitrary number of variates (targets + dynamic/static covariates) into one token sequence, so a single pretrained model natively handles any multivariate forecasting task without per-dataset retraining. Moirai-MoE swaps the dense FFN for a mixture-of-experts layer for better specialization at similar inference cost.

License blocker — read before you touch this in a live system

Moirai and Moirai-MoE weights are released under CC BY-NC 4.0 — non-commercial only. Using them (even just for inference) inside a system that manages your own trading capital is arguably a commercial use and is not covered by this license without a separate agreement from Salesforce. Chronos, TimesFM, Lag-Llama, and MOMENT are Apache-2.0/MIT and carry no such restriction — prefer those for anything touching live capital. Moirai is fine for personal research/backtesting only.

Verdict: Moirai-2.0 produced the benchmark's other significant result (GOOG) and tied TimesFM for the strongest average rank — technically interesting, but licensing rules it out of a live pipeline regardless.

Lag-Llama

Apache-2.0 · ~2.45M

Model card: huggingface.co/time-series-foundation-models/Lag-Llama. Architecture: a LLaMA-style decoder-only transformer, but instead of feeding it raw values or patches, each input token is a vector of classical lag features (values at t-1, t-2, t-7, t-30, ...) plus date/time covariates — i.e., it puts a transformer on top of a feature-engineered representation reminiscent of classical autoregressive modeling, deliberately kept tiny (~2.45M params) versus the other entries here.

Verdict: not part of the 2606.27100 benchmark directly, but architecturally the closest to a "transparent" autoregressive baseline of the group — a reasonable second opinion for volatility forecasting when you want something smaller/faster than Chronos-Bolt/TimesFM, still Apache-2.0-clean.

MOMENT

MIT · ~341M

Model card: huggingface.co/AutonLab/MOMENT-1-large (ICML 2024; Auton Lab, Carnegie Mellon). Architecture: a single transformer backbone multi-task pretrained across forecasting, classification, anomaly detection, and imputation on the curated "Time-series Pile," making it the most general-purpose of this group rather than a forecasting specialist.

When to use: its anomaly-detection and imputation heads are arguably more differentiated for a trading pipeline than its forecasting head — e.g. flagging anomalous intraday bars in a data-quality gate, or imputing gaps in an alt-data feed — rather than as the volatility forecaster itself. License: MIT, no restriction.

IBM Granite-TimeSeries

Apache-2.0

Model card: huggingface.co/ibm-granite/granite-timeseries-patchtsmixer (also see ibm-granite/granite-timeseries-patchtst and the "Tiny Time Mixer" TTM line). Architecture: PatchTSMixer replaces attention entirely with an MLP-Mixer over patches — no quadratic attention cost at all — reporting 2–3× lower memory/runtime than patch-transformers at comparable or better accuracy on public long-horizon benchmarks (e.g. 0.37 MSE on ETTh1 96-step forecasting).

When to use: the cheapest-to-run option here by a wide margin (sub-5M-parameter TTM variants exist) — a good fit if you want a lightweight, CPU-trivial vol-forecast component and don't need Chronos/TimesFM's zero-shot generality. License: Apache-2.0.

The one-sentence policy for this whole section

Use any Apache-2.0/MIT model here as a volatility/covariate input to the deterministic sizing layer in §10; do not use any of them, including the significant Chronos/Moirai results, as a standalone entry signal — the 2606.27100 benchmark's own authors call the effect "small and sparse," and iTransformer (a from-scratch architecture, §2) beat every foundation model on META, underscoring that pretraining is not a universal advantage on financial series.

Financial NLP & LLMs

Financial sentiment/NLP belongs in the risk layer (a veto/regime feature) not the alpha layer — per §0's source research, standalone news-sentiment edge is largely a look-ahead artifact that dies OOS. Here are the three models actually worth knowing.

FinGPT

MIT (code)

Repo: github.com/AI4Finance-Foundation/FinGPT. Architecture: not a model trained from scratch — a LoRA (Low-Rank Adaptation) fine-tuning recipe applied to open base LLMs (LLaMA2-7B/13B for FinGPT v3.2/v3.3, ChatGLM2-6B for v3.1). LoRA freezes the base weights and trains small low-rank adapter matrices instead, cutting trainable parameters from ~6.17B to ~3.67M — practical to fine-tune on a single consumer GPU.

Use cases: financial sentiment analysis, robo-advisor style Q&A, and forecasting experiments using LLM-engineered signals (also the basis of FinRL Contest's "LLM-engineered signals" track). License nuance: the FinGPT training/inference code is MIT, but the underlying LLaMA base weights carry Meta's own separate community license terms — check those independently before any commercial deployment of a fine-tuned checkpoint.

FinBERT-tone

Apache-2.0 · ~110M

Model card: huggingface.co/yiyanghkust/finbert-tone (Huang, Wang & Yang, Contemporary Accounting Research 2022). Architecture: also BERT-base (12 layers / 768 hidden / 12 heads), but trained on a different corpus lineage — pretrained on 10-K/10-Q/earnings-call text, then fine-tuned on 10,000 manually annotated analyst-report sentences, giving it a somewhat different tone-classification behavior than ProsusAI's FinBERT despite the similar name.

When to use: a second, differently-trained sentiment opinion for ensembling with FinBERT on analyst-report or earnings-call text specifically (rather than general news headlines).

Reinforcement learning for trading

RL is the most hyped and least reliable item on this whole page for a core trading engine. The tooling is genuinely good; the honest verdict, backed by the RL community's own benchmark contest, is to confine it to narrow, tightly-validated sub-problems (e.g. execution timing) rather than a primary or portfolio-level engine.

LibraryLicenseRoleStatus
FinRLMITFinancial RL framework: environments, agents, DRL algorithm wrappers for stock/crypto/order-execution tasksActive; ~15k★, runs the annual FinRL Contest
FinRL-MetaMITDynamic market-environment and dataset layer for FinRL (hundreds of market simulators)Active, smaller community (~1–2k★)
Stable-Baselines3MITReliable, well-tested implementations of the underlying RL algorithms (PPO, SAC, DQN, A2C, TD3, DDPG, HER) that FinRL wrapsActively maintained, the de facto standard PyTorch RL library
TensorTradeApache-2.0RL trading-environment framework (Gym-style)Maintenance stalled — a community fork, TensorTrade-NG, was created explicitly because the original "needed a lot of refactoring, was outdated, and looked not really maintained." Treat as unmaintained for planning purposes.

The four algorithm families (one line each)

  • PPO (Proximal Policy Optimization) — on-policy, discrete or continuous actions; clips the policy-update step to stay near the old policy, trading some sample efficiency for training stability. The default first choice in most FinRL examples.
  • SAC (Soft Actor-Critic) — off-policy, continuous actions only; maximum-entropy objective (rewards exploration explicitly) with a replay buffer, generally more sample-efficient than PPO but more sensitive to hyperparameters.
  • DQN (Deep Q-Network) — off-policy, discrete actions only; learns an action-value function via a replay buffer and target network. Needs a discretized action space (e.g. buy/hold/sell), which is a poor fit for continuous position sizing.
  • A2C (Advantage Actor-Critic) — on-policy, discrete or continuous; a simpler, synchronous cousin of A3C, generally the weakest of the four on financial tasks in FinRL's own benchmarks.
Why RL is concretely fragile for trading — not just "hard"

Non-stationary reward landscape: an RL agent's value function is learned against a specific historical regime; when the regime shifts (as financial markets constantly do), the learned policy is optimizing for a reward surface that no longer exists — unlike a re-trained GBDT meta-labeler, there's no simple "refit on the latest window" fix for a policy network without risking catastrophic forgetting. Sample inefficiency: deep RL typically needs millions of environment interactions to converge; you have exactly one non-replayable history of the market, so "more data" means either simulation (which encodes your own assumptions as ground truth) or years of additional wall-clock waiting. Reward-hacking on backtests: an agent optimizing cumulative simulated PnL will happily learn to exploit simulator quirks (unrealistic fill assumptions, zero market impact, exact backtest-engine rounding) rather than a genuine market edge — precisely the failure mode the FinRL Contests paper (arXiv:2504.02281) documents: submitted agents with a high probability of backtest overfitting are explicitly rejected at a 10% significance level by the contest's own organizers — the people building the RL trading ecosystem consider most submitted RL trading agents statistically indistinguishable from overfit noise by default.

Practical scope, if you use RL at all: a narrow sub-problem with a naturally MDP-shaped reward (e.g. order-execution timing within a fixed parent order, VWAP-tracking), evaluated with the same PBO/DSR discipline as everything else in §14 — never as the primary entry/exit/sizing engine.

Full quant ML pipelines — Microsoft Qlib

Qlib is the closest thing to a turnkey AI-quant research platform in open source — a data layer, feature-engineering DSL, model zoo, and walk-forward backtest/portfolio-analysis harness, wired together via YAML workflow configs. MIT-licensed (microsoft/qlib), tens of thousands of GitHub stars, MIT since its September 2020 open-sourcing, actively released (v0.9.7, August 2025) and now paired with Microsoft's RD-Agent for automated research-loop experimentation.

Alpha158 vs. Alpha360 — the two built-in feature sets

Alpha158Alpha360
What it is~158 hand-engineered technical/formulaic alpha features (returns, moving averages, volatility ratios, volume ratios, etc.)Raw normalized OHLCV history flattened over a 60-day lookback (6 fields × 60 days = 360 columns), essentially no feature engineering
Feature relationshipsLow spatial/temporal structure between columns — a classic tabular feature matrixStrong spatial-temporal structure — designed for models that learn their own representation from raw sequences
Best-paired modelsLightGBM, XGBoost, CatBoost, linear/MLP modelsGRU, LSTM, Transformer-family, ALSTM, GATs — sequence models that benefit from raw structure

Workflow/config system and built-in model zoo

Qlib experiments are typically driven by a single YAML file consumed by the qrun CLI, declaring the data handler (Alpha158/360 or custom), the model, the dataset split, the portfolio strategy, and the backtest parameters — this makes swapping LightGBM for a GRU or a TFT wrapper a one-line config change, not a rewrite. The built-in model zoo spans classical and deep approaches: LGBModel (LightGBM), XGBModel, CatBoostModel, MLP, GRU/LSTM/ALSTM (recurrent), Transformer, TFT wrapper, TabNet, HIST, IGMTF, and several graph/attention research models (GATs, KRNN, Localformer) — all trained and evaluated through the same workflow harness with a shared walk-forward backtest/IC-analysis report.

YAML · minimal Qlib workflow config (LightGBM on Alpha158)
qlib_init:
  provider_uri: "~/.qlib/qlib_data/custom_nse"   # see note below on pointing Qlib at NSE data
  region: in                                       # generic region tag; India isn't a stock Qlib region out of the box

market: &market custom_nse_500
benchmark: &benchmark NIFTY500

data_handler_config: &data_handler_config
  start_time: 2010-01-01
  end_time: 2025-12-31
  fit_start_time: 2010-01-01
  fit_end_time: 2019-12-31
  instruments: *market

task:
  model:
    class: LGBModel
    module_path: qlib.contrib.model.gbdt
    kwargs:
      loss: binary
      num_leaves: 31
      learning_rate: 0.03
      monotone_constraints: [1, 0, -1, 0]
  dataset:
    class: DatasetH
    module_path: qlib.data.dataset
    kwargs:
      handler:
        class: Alpha158
        module_path: qlib.contrib.data.handler
        kwargs: *data_handler_config
      segments:
        train: [2010-01-01, 2019-12-31]
        valid: [2020-01-01, 2021-12-31]
        test:  [2022-01-01, 2025-12-31]

# run with:  qrun this_config.yaml
Pointing Qlib at custom (e.g. NSE) data

Qlib ships US and China data providers out of the box; NSE/BSE requires you to build your own binary data store with scripts/dump_bin.py, feeding it point-in-time OHLCV Parquet/CSV files you've already assembled (per the survivorship-bias-free data-plan discipline). You'll also need a custom trading calendar (NSE holidays differ from NYSE/China) and to register your instrument universe file — Qlib's data layer is format-agnostic once converted, so Alpha158/360 and the full model zoo work unmodified on NSE data once the dump step is done.

López de Prado financial-ML methods — mlfinpy & skfolio

López de Prado's Advances in Financial Machine Learning toolkit used to mean a paid mlfinlab license — it no longer does. Two free, permissively-licensed packages now cover triple-barrier labeling, meta-labeling, fractional differentiation, sample-uniqueness weighting, CPCV, and HRP.

PackageLicenseCovers
mlfinpyMITTriple-barrier labeling, meta-labeling, fractional differentiation, sample-weight/uniqueness utilities — a modern Pythonic re-implementation of the AFML code snippets
skfolioBSD-3-Clausescikit-learn-compatible portfolio optimization and model selection: CombinatorialPurgedCV out of the box, plus HRP, HERC, risk-budgeting, CVaR/CDaR optimization
mlfinlab (paid)CommercialThe original, packaged, paid version — only worth it if you specifically want Hudson & Thames' maintained extras beyond what the two free packages cover
Python · mlfinpy triple-barrier labeling
pip install mlfinpy

from mlfinpy.labeling import get_events, add_vertical_barrier, get_bins
from mlfinpy.util import get_daily_vol

# causal, EWMA-based daily volatility -- uses data strictly up to t, no leakage
daily_vol = get_daily_vol(close=prices, lookback=50)

vertical_barriers = add_vertical_barrier(
    t_events=signal_dates, close=prices, num_days=10       # time-limit barrier
)

triple_barrier_events = get_events(
    close=prices,
    t_events=signal_dates,             # your primary (Pine) strategy's signal dates
    pt_sl=[2.0, 1.5],                  # profit-take / stop-loss as multiples of daily_vol
    target=daily_vol,
    min_ret=0.0005,
    num_threads=4,
    vertical_barrier_times=vertical_barriers,
)

labels = get_bins(triple_barrier_events, prices)
# labels['bin']    -> 1 = profit barrier hit first, 0 = stop/time barrier hit first
# labels['ret']    -> realized return at the barrier that was hit
# labels['t1']     -> the barrier-touch timestamp (needed for purging downstream)
Python · skfolio CombinatorialPurgedCV for the meta-label model
pip install skfolio

from skfolio.model_selection import CombinatorialPurgedCV, cross_val_predict
from sklearn.pipeline import Pipeline
import lightgbm as lgb

meta_label_pipeline = Pipeline([
    ("model", lgb.LGBMClassifier(num_leaves=31, learning_rate=0.03,
                                  min_child_samples=50)),
])

# n_folds=10, n_test_folds=2  ->  C(10,2) = 45 combinatorial train/test paths
# purged_size / embargo_size sized to the max holding horizon (t1 - t0), NOT a flat 1%
cv = CombinatorialPurgedCV(n_folds=10, n_test_folds=2,
                            purged_size=10, embargo_size=10)

# returns an empirical DISTRIBUTION of out-of-sample paths, not one score --
# use it to compute PBO and the Deflated Sharpe Ratio, not a single train/test split
oos_paths = cross_val_predict(estimator=meta_label_pipeline, X=X, y=y,
                               cv=cv, sample_weight=w)

What each technique buys you

  • Fractional differentiation (mlfinpy.features.frac_diff / fixed-width FFD variant) — finds the minimum differencing order d (often 0.2–0.5) that passes an ADF stationarity test while retaining >0.9 correlation to the level series, giving a stationary input that keeps memory — strictly better for tree/linear models than raw returns (zero memory) or raw price (non-stationary).
  • Sample-uniqueness weighting — triple-barrier labels overlap in time (a 10-day holding period means adjacent signals share label windows), violating the IID assumption most ML theory relies on. Weight each sample by its average uniqueness (inverse of concurrent-label count) plus time-decay, or your effective sample size is a fraction of your row count and every downstream significance test is overstated.
  • Combinatorial Purged CV (CPCV) — generates many train/test path combinations (not one walk-forward path), purges training rows whose label window overlaps the test fold, and embargoes a buffer after each test fold. Produces a full distribution of OOS Sharpe ratios (feeding PBO and DSR) instead of one point estimate — this is why it's structurally more reliable than plain walk-forward.
  • HRP (Hierarchical Risk Parity) (skfolio.optimization.HierarchicalRiskParity) — allocates via hierarchical clustering of the correlation matrix instead of inverting a noisy covariance matrix (which mean-variance optimization does), making it far more robust to estimation error on a basket of correlated equities.

Regime detection — hmmlearn & ruptures

This is the section where the single most common silent leakage bug in retail regime-gating lives — and where showing the wrong pattern next to the right one is more useful than describing it in prose.

LibraryLicenseMaturityRole
hmmlearnBSDMature, scikit-learn-style API, ~3k★Gaussian/GMM/Categorical HMMs for a discrete calm/stormy regime state
rupturesBSD-2-ClauseMature, academic (ENS Paris-Saclay/CNRS)Offline changepoint detection — PELT (exact, linear-cost, unknown number of changes), binary segmentation (BinSeg, greedy/approximate), and sliding-window methods
The #1 silent look-ahead bug: full-sample smoothed HMM decoding

The default, easiest way to call hmmlearn — fit GaussianHMM on your entire history, then call .predict() for regime labels — uses Viterbi/smoothed decoding under the hood. The regime label at time t is chosen using the entire sequence, including everything after t. The gate effectively "knows" a crisis is coming before it happens. Every eye-popping published HMM drawdown-reduction number (56%→24% max DD, etc.) that doesn't explicitly control for this is very likely built this way. Below: the leaky pattern, and the causal, walk-forward, filtered-posterior pattern that is actually deployable.

Python · WRONG — leaky full-sample smoothed decoding (do not deploy)
from hmmlearn.hmm import GaussianHMM

model = GaussianHMM(n_components=2, covariance_type="diag", n_iter=1000)
model.fit(X_full)                    # fit on the ENTIRE history -- future leaks into the fit
regimes = model.predict(X_full)      # Viterbi = globally-smoothed decode over the whole path
# regimes[t] used information from t+1 ... T. This is look-ahead bias.
# Any backtest of a regime gate built this way is invalid and will not reproduce live.
Python · CORRECT — causal, walk-forward, filtered-posterior regime gate
from hmmlearn.hmm import GaussianHMM
import numpy as np

regime_at_t = np.full(len(X), np.nan)
model, refit_every, min_train = None, 63, 500     # e.g. refit quarterly

for t in range(min_train, len(X)):
    if model is None or t % refit_every == 0:
        model = GaussianHMM(n_components=2, covariance_type="diag", n_iter=200)
        model.fit(X[:t])                          # ONLY data strictly before t

    # forward-algorithm FILTERED posterior using data up to and including t --
    # no smoothing, no future information, no re-fit on data that includes t's label use
    posteriors = model.predict_proba(X[:t + 1])
    regime_at_t[t] = posteriors[-1].argmax()       # today's regime, knowable today

# regime_at_t is safe to merge into the feature frame for both backtest and live use.
# Expect a MUCH smaller, laggier drawdown benefit than the full-sample-smoothed version --
# that gap IS the size of the leakage bug you just closed.

ruptures is the complementary tool when you want changepoints rather than a persistent regime state — e.g. flagging a volatility-regime break in an EWMA series to trigger an HMM refit, or as an alternative regime-boundary detector to cross-check the HMM's transitions. Pelt (linear-cost exact search, unknown number of breakpoints, needs a penalty parameter) is the default; Binseg trades exactness for speed on longer series; window-based methods are cheapest but least precise on subtle breaks. Like the HMM, run it in an expanding/rolling window for live use — ruptures' standard API is also an offline, full-series method by default.

Backtesting & execution engines

No single engine is right for every stage. Use a vectorized engine to triage thousands of parameter combinations, then re-validate every survivor on an event-driven engine with realistic fills before risking capital — conflating the two stages (trusting a vectorized-engine backtest as your final answer) is a common, expensive mistake.

EngineSpeedFill realismLicenseMaintenanceBest-fit use case
vectorbt (community)Extremely fast (Numba-vectorized, thousands of configs at once)Weak — vectorized fills, easy to fool yourselfApache-2.0 + Commons ClauseCommunity edition maintenance-frozen; active dev moved to paid vectorbtPROFast first-pass parameter sweep / triage only
backtraderModerate (pure Python, event-driven)Moderate (configurable slippage/commission, but manual)GPL-3.0Stalled ~2021 — open PRs/issues unmerged for yearsLegacy strategies only; don't start new work here
Zipline-reloadedModerateGood (Quantopian-derived fill/slippage models)Apache-2.0Community-maintained fork (stefan-jansen), modest but real activityResearch backtests wanting the original Zipline/Quantopian API and pipeline model
NautilusTraderFast (Rust core)High — same strategy code path for backtest and liveLGPL-3.0-or-laterVery active, ~24k★, production-gradeFinal realistic-fill re-validation and the bridge to live — the one tool built for research-to-live parity
QuantConnect LEANFast (C#/.NET core, Python API)High — institutional-grade fill/slippage/margin modelsApache-2.0Very active, ~19k★, backs a commercial cloud platformFull-featured alternative to NautilusTrader, especially with QuantConnect's hosted data/cloud/live options
btModerateModerate (higher-level "Algo" abstraction over vectorized fills)MITActively maintained (releases into 2026)Fast, readable portfolio-level (multi-asset allocation) backtests, less suited to per-trade microstructure realism
backtesting.pyFast (single-asset, vectorized-ish)Basic (simple slippage/commission model)AGPL-3.0Active, small footprint, ~2.4k★Quick single-asset prototyping only — AGPL is a real constraint if you build a hosted/SaaS product on it

Why NautilusTrader's architecture actually matters

Most "great backtest, disappointing live" failures are not model failures — they're research/production skew bugs: your backtest engine and your live execution code are two separate codebases that quietly drift apart (a fill-price assumption here, an off-by-one bar-timing bug there), and nothing forces them to stay consistent. NautilusTrader's Rust-core, event-driven design runs the same strategy code — the same classes, the same event handlers — in backtest, paper, and live modes; only the data/execution adapters change. That architectural choice eliminates an entire class of bugs by construction rather than by discipline, which is why it's the right tool for the "final realistic-fill re-validation and bridge to production" role rather than a nice-to-have.

License note — LGPL-3.0, not GPL

NautilusTrader is LGPL-3.0-or-later, not the stricter GPL-3.0 that governs backtrader and freqtrade. LGPL permits linking/using the library from proprietary code without open-sourcing your strategy code, as long as modifications to NautilusTrader itself (if any) are shared back — materially less restrictive than a straight GPL dependency for a closed-source trading system. Still confirm current license terms against the repository before a commercial deployment.

Portfolio construction & risk

This is where "drawdown control" is actually implemented — as deterministic math consuming volatility forecasts and meta-label confidence, not as another prediction task.

LibraryLicenseRole
PyPortfolioOptMITClassical mean-variance / efficient frontier, Black-Litterman, shrinkage covariance estimators, and HRP — the most approachable API of the three
Riskfolio-LibBSD-3-ClauseDeeper risk-based allocation: HRP/HERC, ~24 risk measures including CVaR/CDaR/EVaR, risk-parity/risk-budgeting, built on CVXPY convex optimization
archNCSAGARCH/EGARCH/GJR-GARCH/TARCH volatility-family models — the standard econometric volatility forecaster feeding vol-targeted sizing
Python · GJR-GARCH volatility forecast → vol-targeted position size (arch)
pip install arch

from arch import arch_model
import numpy as np

returns_pct = 100 * log_returns.dropna()      # arch expects % returns for numerical stability
# GJR-GARCH(1,1,1) with Student-t errors -- captures the leverage effect (vol reacts more to
# negative returns than positive ones), which plain GARCH(1,1) misses
am = arch_model(returns_pct, vol="GARCH", p=1, o=1, q=1, dist="t")
res = am.fit(disp="off", last_obs=train_end_date)   # fit only on data up to the current date

fcast = res.forecast(horizon=10, reindex=False)
vol_forecast_daily = np.sqrt(fcast.variance.values[-1]) / 100   # back to decimal daily vol

target_vol_daily = 0.10 / (252 ** 0.5)        # ~10% annualized portfolio vol target
position_scale = target_vol_daily / vol_forecast_daily[0]
position_size = base_size * min(position_scale, max_leverage_cap)   # always cap leverage

PyPortfolioOpt

MIT

When to use: quick HRP or shrinkage-covariance allocation across a basket without pulling in a full convex-optimization dependency stack. When not to: if you need CVaR/CDaR objectives or more exotic risk measures — that's Riskfolio-Lib's job.

Python · HRP allocation
from pypfopt import HRPOpt
hrp = HRPOpt(returns=daily_returns_df)   # NOT prices -- pass returns
weights = hrp.optimize()                 # hierarchical-clustering-based, no covariance inversion

Riskfolio-Lib

BSD-3

When to use: whenever the objective is explicitly tail-risk-aware (CVaR/CDaR) rather than variance — a better match for a "least drawdown" mandate than mean-variance.

Python · CVaR-optimal portfolio
import riskfolio as rp
port = rp.Portfolio(returns=daily_returns_df)
port.assets_stats(method_mu="hist", method_cov="hist")
weights = port.optimization(model="Classic", rm="CVaR", obj="MinRisk", rf=0, l=0)

Feature engineering & forecasting utilities

Before trusting any GBDT or foundation-model result, benchmark it against the boring baseline. These two libraries cover automated feature extraction and the classical forecasting baselines you should never skip.

LibraryLicenseRole
tsfreshMITAutomated extraction of hundreds of statistical/signal-processing time-series features, with a built-in hypothesis-testing-based relevance filter (the "FRESH" algorithm) to control false-discovery rate on the feature explosion
Nixtla statsforecastApache-2.0Fast classical/econometric baselines — AutoARIMA, AutoETS, AutoCES, Theta — the mandatory benchmark before believing any ML "edge"
Nixtla mlforecastApache-2.0Machine-learning forecasting at scale (pandas/polars/Spark/Dask/Ray backends) with the same lag-feature-engineering plumbing regardless of backend
Nixtla neuralforecastApache-2.030+ neural architectures under one API — every model in §2 (NBEATS, NHITS, TFT, Informer, Autoformer, FEDformer, PatchTST, iTransformer, TimeLLM, ...) available without hand-rolling each paper's code
Python · tsfresh automated + filtered feature extraction
pip install tsfresh
from tsfresh import extract_relevant_features

# long-format df: columns [id, time, value]; y indexed by id (e.g. triple-barrier label)
features = extract_relevant_features(
    long_format_df, y=labels_by_id, column_id="id", column_sort="time",
    fdr_level=0.01,           # false-discovery-rate control -- do NOT skip this on financial data
)
# re-run the relevance filter INSIDE each CV fold in production, never on the full dataset
Python · mandatory baseline before trusting any model's "edge"
pip install statsforecast
from statsforecast import StatsForecast
from statsforecast.models import AutoARIMA, AutoETS

sf = StatsForecast(models=[AutoARIMA(), AutoETS()], freq="D", n_jobs=-1)
sf.fit(train_df)
baseline_forecast = sf.predict(h=10)
# your GBDT / foundation-model forecast must beat THIS, net of cost, or there is no edge

MLOps for a live trading system

MLOps tooling here isn't about scale — it's about honesty. The single most load-bearing MLOps function in this whole stack is trial-count discipline for the Deflated Sharpe Ratio: every backtest configuration you ever try, tracked, forever, so the multiple-testing correction is computed against your real trial count, not the flattering subset you remember.

ToolLicenseRoleWhen to adopt
MLflowApache-2.0Experiment tracking (params/metrics/artifacts) + model registry with stage transitionsEarly — every single backtest run, from day one, feeds the DSR trial count
Evidently AIApache-2.0Input-feature and prediction-distribution drift detection (KS-test, PSI, Wasserstein, 100+ built-in metrics)Once a strategy is running on a schedule against live data
NannyMLApache-2.0Label-free live performance estimation — CBPE for classifiers, DLE for regressors — estimates real-world accuracy before ground-truth labels are even availableOnce paper/live trading is running and labels arrive with a lag (exactly the triple-barrier situation)
PrefectApache-2.0Pythonic pipeline orchestration: scheduling, retries, caching, event-driven triggers for the nightly ingest→feature→predict→size→alert DAGOnce a plain cron script becomes unwieldy — not needed for an MVP
DagsterApache-2.0Alternative orchestrator with stronger data-asset lineage/typing — heavier but better observability at scaleIf you outgrow Prefect's simplicity and want asset-level lineage across many pipelines
Python · MLflow logging that actually enforces the DSR trial-count discipline
pip install mlflow
import mlflow

with mlflow.start_run(run_name="lgbm_meta_label_v37"):
    mlflow.log_params(params)                                   # every hyperparameter tried
    mlflow.log_param("cv_scheme", "CombinatorialPurgedCV(10,2)")
    mlflow.log_param("feature_set_hash", feature_set_hash)       # so re-used feature sets don't double count
    mlflow.log_metric("oos_sharpe_median", oos_sharpe_median)    # across the CPCV path distribution
    mlflow.log_metric("pbo", pbo_value)
    mlflow.log_metric("trial_number_in_family", trial_counter)   # THIS feeds the Deflated Sharpe N
    mlflow.lightgbm.log_model(model, "model")

# Query the full run history to compute N for the Deflated Sharpe Ratio -- include prior
# Pine-Script optimization trials too (see §14), not just the ML runs.
Why this is non-negotiable, not nice-to-have

The Deflated Sharpe Ratio needs an honest count of every configuration you've ever tried against this data — not just the final model. Without a system-of-record, trial count is reconstructed from memory at the exact moment you're most motivated to undercount it. MLflow (or an equivalent) turns "how many things did I actually try" from a self-reported guess into a queryable fact.

Data & alternative datasets on Hugging Face

Beyond model hubs, Hugging Face's Datasets Hub carries a growing set of finance-relevant datasets worth knowing about for prototyping and sentiment work — treat all of them as prototyping/backup sources, not survivorship-bias-free production data (see the Data Plan discipline in the source research report).

DatasetTypeNotes
mito0o852/OHLCV-1mUS equities, minute OHLCVThousands of US stocks, 1992–2026, sourced from Finnhub.io
123olp/binance-futures-ohlcv-2018-2026Crypto futures OHLCVBinance futures history 2018–2026 — good CCXT-pipeline-test alternative/backup
TimKoornstra/financial-tweets-sentimentSocial sentimentLabeled Twitter/X financial and crypto sentiment — useful for fine-tuning or validating a FinBERT-style veto filter
huggingface.co/datasets?search=financeHub searchThe Hub's finance-tagged dataset list changes frequently — always re-search rather than trusting a static list; verify licensing per-dataset before any commercial use

Numerai — a different game, not a data source for your own system

Numerai is best understood as a modeling-skill marketplace, not a dataset to plug into your own pipeline. The core tournament ships free, pre-engineered, obfuscated features — PCA'd, z-scored, and time-sliced so you can model world-class financial data without ever knowing which stocks or dates you're looking at (each weekly "era" has IDs that don't map across eras, by design). Numerai Signals is the complementary product: you submit predictions on your own universe/features, aiming for signals orthogonal to what Numerai's existing model stack already captures. All submitted models feed a stake-weighted meta-model that in turn drives Numerai's own hedge fund. Frame it as optionality for monetizing modeling skill — evaluated on multi-year, drawdown-inclusive performance, not a fast track to income — and as an interesting adjacent ecosystem rather than a component of the pipeline in §14.

Reference architecture — full pipeline

How an advanced user wires every library above into one pipeline, end to end. Data ingestion → Qlib/tsfresh features → fractional differentiation (mlfinpy) → LightGBM meta-label model validated with nested Combinatorial Purged CV (skfolio) → causal hmmlearn regime gate → GARCH-based (arch) vol-targeted sizing → NautilusTrader backtest → MLflow logging → PickMyTrade webhook for execution.

Reference pipeline: data → features → labels → meta-label CV → regime/sizing → NautilusTrader → MLflow → PickMyTrade 1 · Data ingestion Point-in-time OHLCV + constituent/delisting table; Parquet/DuckDB no survivorship bias 2 · Features Qlib Alpha158 + tsfresh + mlfinpy fractional differentiation (FFD) fit inside-fold only 3 · Triple-barrier mlfinpy: causal EWMA vol → PT/SL/vertical barriers + sample weight uniqueness-weighted 4 · Meta-label model LightGBM inside skfolio CombinatorialPurgedCV → PBO + Deflated Sharpe reject if PBO > 50% 5 · Regime + sizing Causal filtered hmmlearn gate + GARCH (arch) vol- targeted fractional Kelly deterministic math 6 · NautilusTrader One strategy class, backtest → paper → live (only adapters swap) research/live parity 7 · MLflow → PickMyTrade Every run logged; webhook JSON → broker bracket order execute, don't build
Every arrow is a data hand-off between an independently swappable library — no stage depends on a specific choice in another.
Python · end-to-end reference pipeline skeleton
"""
Reference pipeline skeleton: data ingestion -> PickMyTrade execution.
Every preprocessing step is fit INSIDE the CPCV training fold only -- never on
the full dataset. Illustrative structure; wire in your own data store / config.
"""
import numpy as np
import pandas as pd
import lightgbm as lgb
import mlflow
import requests
from hmmlearn.hmm import GaussianHMM
from arch import arch_model
from mlfinpy.labeling import get_events, add_vertical_barrier, get_bins
from mlfinpy.util import get_daily_vol
from mlfinpy.features import frac_diff_ffd
from skfolio.model_selection import CombinatorialPurgedCV, cross_val_predict
from sklearn.pipeline import Pipeline
from qlib.contrib.data.handler import Alpha158
from tsfresh import extract_relevant_features
from nautilus_trader.backtest.engine import BacktestEngine
from nautilus_trader.trading.strategy import Strategy


# 1. DATA INGESTION -- point-in-time, survivorship-bias-free universe only
def load_point_in_time_universe(as_of_date, min_adv_usd=2_500_000):
    """Never filter by TODAY's liquidity -- that imports survivorship bias."""
    raw = data_store.query_asof(as_of_date=as_of_date)          # Parquet/DuckDB store
    return raw[raw["adv_20d_asof"] > min_adv_usd]


# 2. FEATURES -- Qlib Alpha158 for technicals, tsfresh + mlfinpy for the rest
def build_features(prices: pd.DataFrame) -> pd.DataFrame:
    alpha158 = Alpha158(instruments=prices["symbol"].unique().tolist(),
                         start_time=prices.index.min(), end_time=prices.index.max())
    tech_features = alpha158.fetch()

    tsfresh_features = extract_relevant_features(
        prices.reset_index(), y=None, column_id="symbol", column_sort="date", fdr_level=0.01,
    )

    d = find_min_ffd_order(prices["close"], adf_pvalue=0.05, min_corr=0.9)   # search d in [0.1, 1.0]
    frac_diff_price = frac_diff_ffd(prices["close"], d=d, threshold=1e-4)    # stationary, RETAINS memory

    return tech_features.join(tsfresh_features, how="inner").assign(frac_diff_price=frac_diff_price)


# 3. LABELS -- causal EWMA vol -> ATR-scaled triple barrier (mlfinpy)
def label_events(prices: pd.Series, signal_dates: pd.DatetimeIndex) -> pd.DataFrame:
    daily_vol = get_daily_vol(close=prices, lookback=50)
    vertical_barriers = add_vertical_barrier(t_events=signal_dates, close=prices, num_days=10)
    events = get_events(close=prices, t_events=signal_dates, pt_sl=[2.0, 1.5], target=daily_vol,
                         min_ret=0.0005, num_threads=4, vertical_barrier_times=vertical_barriers)
    return get_bins(events, prices)          # -> bin (0/1), ret, t1 (needed for purging)


# 4. META-LABEL MODEL -- LightGBM inside nested Combinatorial Purged CV
def train_meta_label_model(X: pd.DataFrame, y: pd.Series, w: pd.Series, trial_counter: int):
    pipeline = Pipeline([("model", lgb.LGBMClassifier(
        num_leaves=31, learning_rate=0.03, min_child_samples=50,
        monotone_constraints=MONO_CONSTRAINTS))])

    cv = CombinatorialPurgedCV(n_folds=10, n_test_folds=2, purged_size=10, embargo_size=10)
    oos_paths = cross_val_predict(estimator=pipeline, X=X, y=y, cv=cv, sample_weight=w)

    pbo = compute_pbo(oos_paths)
    dsr = compute_deflated_sharpe(oos_paths, n_trials=get_trial_count_from_mlflow() + trial_counter)
    if pbo > 0.50 or dsr.p_value > 0.05:
        raise ValueError(f"Model rejected: PBO={pbo:.2f}, DSR p={dsr.p_value:.3f}")

    final_model = pipeline.fit(X, y, model__sample_weight=w)
    return final_model, pbo, dsr


# 5. REGIME GATE -- causal, walk-forward, FILTERED (never smoothed) HMM posterior
def causal_regime_state(vol_return_features: np.ndarray, t: int, model_cache: dict,
                         refit_every: int = 63, min_train: int = 500) -> int:
    if t < min_train:
        return 1  # default "calm" until enough history exists
    if t % refit_every == 0 or "model" not in model_cache:
        hmm = GaussianHMM(n_components=2, covariance_type="diag", n_iter=200)
        hmm.fit(vol_return_features[:t])                       # only data strictly before t
        model_cache["model"] = hmm
    posteriors = model_cache["model"].predict_proba(vol_return_features[: t + 1])
    return int(posteriors[-1].argmax())                        # filtered state at t, no look-ahead


# 6. VOL-TARGETED SIZING -- GJR-GARCH forecast -> fractional-Kelly-scaled size
def size_position(returns_pct: pd.Series, meta_label_confidence: float, regime_state: int,
                   target_vol_annual: float = 0.10, kelly_fraction: float = 0.5,
                   max_leverage: float = 1.5) -> float:
    if regime_state == 0:                                       # 0 = "stormy" -- stand aside
        return 0.0
    res = arch_model(returns_pct, vol="GARCH", p=1, o=1, q=1, dist="t").fit(disp="off")
    vol_fc = np.sqrt(res.forecast(horizon=1, reindex=False).variance.values[-1, 0]) / 100
    vol_scale = (target_vol_annual / (252 ** 0.5)) / vol_fc
    kelly_scale = kelly_fraction * max(2 * meta_label_confidence - 1, 0)   # confidence -> edge proxy
    return float(np.clip(vol_scale * kelly_scale, 0, max_leverage))


# 7. BACKTEST -- NautilusTrader (same strategy class runs backtest AND live)
class MetaLabelStrategy(Strategy):
    def __init__(self, model, hmm_cache, config):
        super().__init__(config)
        self.model, self.hmm_cache = model, hmm_cache

    def on_bar(self, bar):
        feats = self.build_live_features(bar)                   # same code path as build_features()
        confidence = self.model.predict_proba(feats)[:, 1][0]
        regime = causal_regime_state(self.vol_history, self.clock.timestamp_ns, self.hmm_cache)
        size = size_position(self.returns_pct_history, confidence, regime)
        if confidence > 0.55 and size > 0:
            self.submit_bracket_order(side="BUY", size=size, sl_atr_mult=1.5, tp_atr_mult=2.0)


def run_backtest(historical_bars, final_model, strat_config):
    engine = BacktestEngine()
    engine.add_data(historical_bars)
    engine.add_strategy(MetaLabelStrategy(model=final_model, hmm_cache={}, config=strat_config))
    return engine.run()      # the SAME strategy object later promotes to paper/live -- adapters swap only


# 8. MLFLOW LOGGING -- every trial, forever (feeds the Deflated Sharpe trial count)
def log_run(params, pbo, dsr, backtest_results, final_model, trial_counter):
    with mlflow.start_run(run_name=f"pipeline_v{trial_counter}"):
        mlflow.log_params(params)
        mlflow.log_metric("pbo", pbo)
        mlflow.log_metric("dsr_p_value", dsr.p_value)
        mlflow.log_metric("backtest_sharpe", backtest_results.sharpe_ratio)
        mlflow.log_metric("backtest_max_dd", backtest_results.max_drawdown)
        mlflow.log_metric("trial_number_in_family", trial_counter)
        mlflow.lightgbm.log_model(final_model, "meta_label_model")


# 9. EXECUTION -- PickMyTrade webhook JSON (paper first, then tiny live)
def send_to_pickmytrade(symbol, side, qty, sl_price, tp_price, webhook_url, secret):
    payload = {
        "secret": secret, "symbol": symbol, "action": side,     # "buy" / "sell"
        "quantity": qty, "stopLoss": sl_price, "takeProfit": tp_price, "orderType": "market",
    }
    resp = requests.post(webhook_url, json=payload, timeout=5)
    resp.raise_for_status()
    return resp.json()
Every arrow is a swap point, by design

Notice that no stage is hard-wired to a specific choice from earlier sections: swap LightGBM for CatBoost, Qlib's Alpha158 for a hand-built feature set, NautilusTrader for QuantConnect LEAN, or PickMyTrade for a different execution bridge, and nothing else in the pipeline needs to change. That decoupling is what makes the §15 decision matrix below actionable rather than prescriptive.

Claude Fable as pair-engineer for this stack

Claude Fable is Anthropic's frontier model (model id claude-fable-5), available via the Claude app and Claude Code. On a stack this deep, its value isn't "write me a trading bot" — it's the unglamorous, error-prone middle: getting CPCV fold arithmetic right, writing a causal (not leaky) HMM decoding loop on the first pass, wiring a Qlib data handler for a market it doesn't ship out of the box, and reading a multi-stage pipeline end to end for the one leak that would otherwise only surface as a live/backtest gap months later.

Where it earns its keep on this page's specific stack:

  • CPCV fold logic — deriving purge/embargo sizes from your label horizon and slowest feature lookback (§7), instead of guessing them and finding out from an inflated backtest Sharpe.
  • Causal regime inference — writing the filtered-posterior, walk-forward loop instead of the leaky full-sample decode()/Viterbi call shown as the "wrong" example in §8.
  • Qlib configuration — generating and adapting Alpha158/Alpha360 workflow YAML to a custom data handler for a region Qlib doesn't ship, like the NSE example in §6.
  • Pipeline-wide leakage review — reading the §14 skeleton stage by stage for a fit-outside-the-fold bug before it ever touches capital.
  • Pine Script → Python conversion — porting a TradingView strategy's entry logic into the primary-signal function that feeds the meta-labeling model.
  • Execution glue — drafting the webhook payload/idempotency-key code covered in §14.6, and the MLflow logging that keeps your Deflated Sharpe trial count honest (§12).

Ready-to-adapt prompts for each of those — specific to this page's stack, not generic "build me a bot" asks:

Prompt 1 · CPCV purge/embargo review
Review this skfolio CombinatorialPurgedCV setup [paste code]: 10 folds, 2 test folds, on triple-barrier-labeled
signals where barriers can resolve up to 10 trading days after the signal (get_events num_days=10). Check whether
purged_size and embargo_size are large enough to cover that full label-resolution window plus my slowest feature's
lookback. Flag any fold where a training sample's resolution window overlaps a test sample's, explain the leak, and
give me corrected purge/embargo values with the reasoning shown.
Prompt 2 · Causal HMM regime gate, not leaky
Here is my hmmlearn regime-detection code [paste code]. Confirm it only calls predict_proba on data up to and
including index t when producing the label used AT time t, and that it never calls hmm.decode() or reads a Viterbi
path computed over the full series. If it's using the full-sample smoothed posterior, rewrite it as a walk-forward
loop that refits every N bars on strictly past data and returns the filtered (not smoothed) posterior at each step,
matching the causal pattern, not the leaky one.
Prompt 3 · Qlib Alpha158 on a non-default market
I want Microsoft Qlib's Alpha158 handler running on NSE (India) equities, not one of Qlib's built-in regions.
Here's my current workflow YAML [paste config]. Write a custom data handler/provider that points Alpha158's feature
computation at my own point-in-time OHLCV store instead of Qlib's default region data, keeping the Alpha158 feature
set intact. Explain specifically what breaks -- calendar, instrument universe, trading-day alignment -- if I just
change the region tag without supplying compatible replacements.
Prompt 4 · Full-pipeline leakage audit
Here is my end-to-end pipeline: data ingestion, feature engineering, triple-barrier labeling, LightGBM meta-label
training inside CombinatorialPurgedCV, HMM regime gate, GARCH-based sizing, and NautilusTrader backtest [paste code].
Walk it stage by stage and flag every point where a transform -- scaler, feature selector, frac-diff order search, HMM
fit -- is fit on data outside the current CV training fold, or a feature could see information unavailable at signal
time. Rank findings by how much each would inflate the backtest Sharpe if left uncorrected.
Prompt 5 · Pine Script → primary-signal function
Here is my TradingView Pine Script strategy [paste script]. Convert its entry logic into a Python function that
takes a pandas DataFrame of OHLCV bars and returns a boolean signal series, matching the Pine version bar-for-bar on
identical historical data. This becomes the primary-model signal feeding my meta-labeling filter, so flag anything --
security() calls with lookahead, use of the current bar's close before it closes -- that would make the two versions
diverge.
Prompt 6 · Webhook payload + idempotency key
Write a Python function that takes a meta-label confidence score, ATR-based stop/target multiples, and a
position size from my vol-targeted sizing calc, and builds the JSON payload for a PickMyTrade webhook POST -- symbol,
side, quantity, stopLoss, takeProfit. Add a deterministic idempotency key (hash of symbol + signal timestamp + bar
index) as a payload field, and wrap the POST in retry-with-backoff logic that treats a timeout as "unknown, reconcile
before retrying" rather than blindly resubmitting.
Fable drafts, you merge

None of the prompts above replace reading the diff yourself. Claude Fable is fast at the mechanical parts — CV fold arithmetic, boilerplate retry logic, converting Pine to pandas — but the purge/embargo sizes, the PBO/DSR rejection thresholds, and the decision to route capital through a given webhook stay yours to sign off on, every time, before anything touches a paper or live account.

Execution bridge — build vs. buy for the last hop

The same build-vs-buy call any systems team makes, applied to the last hop of the §14 pipeline: do your scarce engineering hours go into order-routing plumbing, or into the model layers that are actually yours? This section makes the case for treating live execution as a purchased, narrow-interface dependency — and shows what the interface looks like from the pipeline's side.

1 · Order-routing plumbing has a large blast radius for silent bugs

Retry logic, broker session re-auth, partial-fill reconciliation, OCO bracket state machines, webhook dedup — each is a well-understood problem in isolation, and each is exactly the kind of code where a bug doesn't throw. It quietly duplicates an order, drops a stop, or leaves a state machine in the wrong node. A crashed listener holding an open futures position overnight isn't a stack trace; it's a call to your broker's desk.

2 · A silent execution bug is a P&L event, not a caught exception

Your model's mistakes show up as a lower Sharpe over thousands of trades — noisy but bounded and diagnosable. An execution-layer mistake shows up as one specific missing stop or duplicate fill, realized in dollars, usually the one time volatility spikes and you aren't watching the terminal. Unit tests catch the bugs you thought to write a test for; they rarely catch a session token expiring mid-day or a webhook delivered twice during a network blip.

3 · The hours worth spending are the ones only you can spend

Retry/backoff, OCO state machines, and broker-specific order translation are the same problem for every trader who routes to Rithmic or IB — which is exactly why they're commoditized. Your meta-labeling model, regime gate, and CPCV validation are not solved anywhere else; they're the only layer where an extra hour can actually move your Sharpe. An hour hardening a homemade order router against edge cases someone else already hardened against is an hour not spent there.

4 · Separation of concerns is a reliability pattern, not a nicety

A narrow, dumb HTTP interface — one POST, one payload shape — between strategy and broker means your research code can be as experimental as you want without ever holding a live session token. If the execution vendor's infrastructure has an incident, that's their on-call rotation, not a 2am page for you and not a bug in your model. It's the same reason platform teams don't let application code open raw sockets to a database.

Where this sits in the §14 pipeline

The reference pipeline ends at "MLflow logging → PickMyTrade webhook for execution" because that's the natural trust boundary. Your model emits a signal → a TradingView alert fires, or — since an advanced setup may skip TradingView entirely — your own Python service constructs and POSTs the JSON directly to PickMyTrade's webhook endpoint → PickMyTrade owns OCO bracket placement, broker-specific order translation, multi-account fan-out, and the reconnect/retry loop against whichever venue you route to. That division of labor means your service only has to be reliable up to "send one idempotent HTTP POST" — it never has to maintain a stateful, multi-week connection to a broker's session API, refresh auth tokens on a schedule, or reconcile order state after a network partition. That entire category of problem moves off your plate.

JSON · illustrative webhook payload shape (generic — verify field names against current vendor docs)
{
  "idempotencyKey": "SIG-MES-2026-07-03T14:32:00Z-000481",
  "symbol": "MES",
  "side": "buy",
  "quantity": 2,
  "orderType": "market",
  "stopLoss": 5320.25,
  "takeProfit": 5364.75,
  "meta": {
    "metaLabelConfidence": 0.62,
    "atr14": 14.8,
    "slAtrMult": 1.5,
    "tpAtrMult": 2.0,
    "regimeState": 1
  }
}

Deliberately generic — not a verified PickMyTrade schema (see the working send_to_pickmytrade() call in §14 for the field names already used in this page's reference pipeline). quantity comes straight out of the §11/§14 vol-targeted sizing calc; stopLoss/takeProfit are entry ± ATR × the barrier multiples already validated in triple-barrier labeling (§7); idempotencyKey — absent from the minimal §14 example — is what turns an at-least-once webhook delivery into an effectively-once order.

The failure modes worth designing for up front

Webhook delivery is at-least-once, not exactly-once — a retried POST after a timeout can otherwise duplicate an order; an idempotency key is what makes retries safe. Broker API rate limits and auth-token refresh cycles are per-broker trivia (Rithmic session tokens, IB Gateway reconnects, Tradovate OAuth refresh) a shared execution layer has already hit thousands of times across other users' accounts. Order-state reconciliation after a network partition — did that bracket order actually get placed, or did the connection drop before the ack? — means querying broker order state and diffing it against what you believe you sent, on every reconnect. Underneath all of it: your research code has zero SLA requirements — a crashed kernel costs you a re-run — while your execution code needs five-nines-adjacent reliability, because "the listener was down for four minutes" is indistinguishable, in its consequences, from "the stop-loss didn't exist."

Keep the research environment's blast radius at zero

Your backtest, retrain, and MLflow-tracked experiment code (§12) should have no code path that can place a live order — not a feature flag, not a dry_run=False default waiting to be flipped by mistake. Routing every live order through one external, narrow HTTP boundary enforces that separation structurally: the only way research code could place a live trade is by deliberately constructing the exact same webhook POST your live strategy makes. It's the same reason CI runners don't get production database credentials — here it costs a subscription instead of a second on-call rotation.

Commoditized infrastructure, consumed via API

Treat execution the way you already treat compute: you don't rack your own servers to get a GPU, and there's no alpha in racking your own order router either. PickMyTrade is infrastructure you call, not a component of your edge — the model, the regime gate, and the validation are the parts of this stack worth defending.

Decision matrix — which tool for which job

A single lookup table for "I need to… → use this → because…".

I need to…UseWhy
Filter trade quality (meta-label)LightGBM + mlfinpy triple-barrierHighest-ROI, lowest-risk ML addition; GBDT dominates tabular financial data
Detect market regime causallyhmmlearn (filtered posterior) + ruptures cross-checkCheap, interpretable; ruptures independently confirms structural breaks the HMM finds
Size positionsarch (GARCH) vol forecast + fractional KellyDeterministic math owns sizing — this is where drawdown control actually lives
Get a zero-shot volatility forecastChronos-Bolt or TimesFMApache-2.0, CPU-runnable, no training needed, legitimately useful for vol (not direction)
Validate without leakageskfolio CombinatorialPurgedCV + PBO + Deflated SharpeProduces an OOS performance distribution, not one lucky split; lower PBO than plain walk-forward
Track every experiment / enforce trial-count honestyMLflowThe DSR correction is only as honest as your trial count — MLflow makes it a queryable fact, not a memory
Fast first-pass parameter sweepvectorbt (community)Numba-vectorized triage of thousands of configs in seconds — never trust it as the final answer
Re-validate with realistic fills before capitalNautilusTraderOnly engine here with true research-to-live parity — same strategy code, real fill/slippage modeling
Build a full quant research pipeline fastMicrosoft QlibData handler + Alpha158/360 + model zoo + walk-forward harness, all wired via one YAML config
Extract automated time-series featurestsfreshHundreds of candidate features with a built-in FDR-controlled relevance filter
Benchmark before trusting any "edge"Nixtla statsforecast AutoARIMA/AutoETSMandatory sanity check — DLinear/AAAI-23 showed complexity alone proves nothing
Allocate across a correlated basketHRP via PyPortfolioOpt, skfolio, or Riskfolio-LibRobust to noisy covariance estimates, unlike mean-variance optimization
Optimize for tail risk specifically (CVaR/CDaR)Riskfolio-LibPurpose-built convex-optimization risk measures beyond variance — matches a "least drawdown" mandate
Detect live input/prediction driftEvidently AIStatistical drift tests (KS, PSI, Wasserstein) across features and predictions
Estimate live performance before labels arriveNannyML (CBPE/DLE)Label-free — critical since triple-barrier outcomes only resolve after the barrier is hit
Use financial sentiment as a risk vetoFinBERT (ProsusAI)Apache-2.0, CPU, sub-second — defensive filter, not an alpha engine
Cheaply fine-tune an LLM on financial textFinGPT (LoRA on LLaMA)~3.67M trainable params vs. 6.17B full fine-tune — fits on one consumer GPU
Orchestrate the nightly pipelinePrefect (Dagster at scale)Scheduling/retries/caching once a cron script becomes unwieldy — not needed pre-MVP
Avoid a non-commercial license trapPrefer Chronos / TimesFM / Lag-Llama / MOMENT over MoiraiMoirai is CC BY-NC-4.0 — not usable in a system that manages your own capital
Find structural breaks / changepointsruptures (PELT)Linear-cost exact search, no assumed number of breakpoints
Use RL without it blowing up your backtestStable-Baselines3 (PPO) on a narrow sub-problem, PBO-testedFinRL's own contests reject overfit agents at 10% significance — confine RL accordingly
Execute live ordersPickMyTrade webhookExecution is a solved, hardened problem — your edge lives in the model layers, not order plumbing
Draft / review pipeline code quicklyClaude Fable (claude-fable-5)Pair-engineer for CPCV fold math, causal HMM decoding, and leakage review — see §14.5; every merge is still a human decision
Bridge a model signal to a live broker orderPickMyTrade (webhook execution bridge)One idempotent POST replaces weeks of broker auth, retry, and OCO state-machine engineering — see §14.6

Licensing appendix

Every tool and model covered on this page, with its license and a plain flag. Safe = permissive, no meaningful restriction on commercial/live-trading use. Caution = usable but read the terms (copyleft obligations, source-available-but-restricted clauses, or a maintenance-status concern rather than a licensing one). Restricted = a real blocker for an unrestricted commercial/live system without a separate agreement.

Tool / modelLicenseFlagNote
LightGBMMITSafe
XGBoostApache-2.0Safe
CatBoostApache-2.0Safe
scikit-learnBSD-3-ClauseSafe
neuralforecast / statsforecast / mlforecast (Nixtla)Apache-2.0SafeCovers §2's from-scratch DL architectures via one API
Nixtla TimeGPTProprietary / paid APICautionClosed, vendor lock-in; prefer open Chronos/TimesFM instead
Amazon Chronos / Chronos-BoltApache-2.0Safe
Google TimesFMApache-2.0Safe
Salesforce Moirai / Moirai-MoECC BY-NC-4.0RestrictedNon-commercial only — do not use in a system managing your own capital without a separate license
Lag-LlamaApache-2.0Safe
MOMENTMITSafe
IBM Granite-TimeSeries (PatchTSMixer/PatchTST/TTM)Apache-2.0Safe
FinBERT (ProsusAI)Apache-2.0Safe
FinBERT-toneApache-2.0Safe
FinGPT (code)MITCautionCode is MIT, but fine-tuned checkpoints inherit the base LLaMA weights' separate Meta community license — check that independently
FinRL / FinRL-MetaMITSafe
Stable-Baselines3MITSafe
TensorTradeApache-2.0CautionLicense is permissive, but maintenance has stalled enough that a community fork (TensorTrade-NG) exists specifically to address it — plan accordingly
Microsoft QlibMITSafe
mlfinpyMITSafe
skfolioBSD-3-ClauseSafe
mlfinlab (paid)CommercialCautionPaid license required — the two free packages above already cover the essentials
hmmlearnBSDSafe
rupturesBSD-2-ClauseSafe
vectorbt (community)Apache-2.0 + Commons ClauseCautionCommons Clause blocks selling a product/service whose value derives primarily from the software itself; fine for internal use, and maintenance-frozen
backtraderGPL-3.0RestrictedStrong copyleft; also stalled since ~2021 — avoid for new work on both grounds
Zipline-reloadedApache-2.0Safe
NautilusTraderLGPL-3.0-or-laterCautionPermits linking from closed-source strategy code; modifications to NautilusTrader itself must be shared back — verify current terms before commercial deployment
QuantConnect LEANApache-2.0Safe
btMITSafe
backtesting.pyAGPL-3.0RestrictedNetwork-use copyleft — a real constraint if you build a hosted/SaaS product on it; fine for private, non-distributed use
freqtradeGPL-3.0RestrictedCrypto-only bot; strong copyleft restricts closed-source commercial derivatives — flagged since it's a common trap
PyPortfolioOptMITSafe
Riskfolio-LibBSD-3-ClauseSafe
archNCSASafePermissive, commercial-OK
tsfreshMITSafe
MLflowApache-2.0Safe
Evidently AIApache-2.0Safe
NannyMLApache-2.0Safe
PrefectApache-2.0Safe
DagsterApache-2.0Safe
PickMyTradeProprietary / paid SaaSCautionExecution API, not an installable library — no open-source or self-hosted option; a subscription gates access (see §14.6), a vendor-dependency call rather than an OSS-license one
This is not legal advice

Licenses and their interpretation can change, and "commercial use" boundaries (especially for CC BY-NC and AGPL) are genuinely fact-specific. Treat this table as a starting map for due diligence, not a substitute for reading the current license text yourself — or a lawyer's opinion — before any deployment that touches real capital or is distributed to others.

Important financial disclaimer

Educational purposes only. All content on this page is provided for general educational and informational purposes and does not constitute investment, financial, legal, or tax advice, nor a recommendation or solicitation to buy or sell any security, futures contract, or other financial instrument.

Substantial risk of loss. Trading futures, derivatives, and other leveraged products involves substantial risk of loss and is not suitable for all investors. You can lose more than your initial investment. Only risk capital — money you can afford to lose without affecting your lifestyle — should ever be used for trading.

Past performance is not indicative of future results. Hypothetical and backtested performance results have inherent limitations: they are prepared with the benefit of hindsight, do not involve financial risk, and cannot account for all factors — including the ability to withstand losses — that affect actual trading. Every benchmark number and star/download count cited on this page reflects a point-in-time snapshot taken during mid-2026 research and may have changed by the time you read it — verify current figures against the primary source (GitHub repo, Hugging Face model card, or cited paper) before relying on them.

No guarantees. Nothing on this page guarantees profit or protection from loss. Machine-learning models — including the time-series foundation models discussed in §3 — can be wrong, market conditions change, and automated systems can malfunction.

Not a registered advisor. The author and publisher of this page are not a registered investment advisor, broker-dealer, commodity trading advisor (CTA), or financial planner. Before making any financial decision, consult a licensed financial advisor who can evaluate your individual circumstances.

Not legal advice on licensing. The licensing appendix in §16 is provided for convenience and reflects a good-faith reading of publicly available license files at research time; it is not legal advice. Confirm current license terms directly with each project before any commercial or production use.

About the tools mentioned. PickMyTrade is a third-party execution-automation tool that routes alerts to brokers; it does not provide trading advice, signals, or strategies. All libraries, models, and frameworks named (Hugging Face, Amazon, Google, Salesforce, IBM, Microsoft, Meta/LLaMA, AI4Finance Foundation, and every open-source project referenced) are the property of their respective owners; this page is independent educational content and is not endorsed by or affiliated with any of them. Factual details (licenses, star counts, benchmark figures) were taken from official repositories, model cards, and cited papers at research time and may change — always verify on the primary source.

↑ Back to top

Automate the last hop with PickMyTrade

Your edge lives in the model, regime gate, and validation layers. Let PickMyTrade own the execution plumbing — one idempotent webhook to 60+ brokers and prop firms, with bracket orders attached.