AutoML hyperparameter optimizer for TA-Lib technical analysis signals with backtesting.
v0.2.1 — GUI + Developer SDK. Two new ways to use this tool:
- Plotly Dash GUI —
ta-automl-guiopens a browser at http://127.0.0.1:8050. Every option has a (?) button that explains the concept with a plain-English analogy. Includes a 🎓 Tutorial mode overlay (good for live workshops).- Developer SDK — write your own indicator + your own combination rule and let ta-automl validate the idea via backtest (no AutoML required). Scaffold with
ta-automl-dev new-indicator <name>/new-combiner <name>. Seedocs/EXTENDING.md,docs/DEVELOPER_GUIDE.md, and the tutorial / workshop docs.
Stage 1 — Screening: Loops over all ~158 TA-Lib indicators via talib.get_functions(), computes each (default mode: TA-Lib defaults; with --tune-screen: a small per-indicator Vizier/FLAML/random search picks better params and binarization method), binarizes the output to {-1, 0, +1}, and keeps those with sufficient signal density. Typically yields 100–150 indicator outputs as candidates. The tuned configs are then handed to Stage 2 as warm-start anchors so Vizier doesn't re-discover them.
Stage 2 — Optimization: Uses Google Vizier (GP-Bandit, in-process) or FLAML (BlendSearch) to search a high-dimensional parameter space: indicator-specific period/threshold hyperparameters + per-indicator combination weights + a global threshold. Each trial runs a full backtest on the held-out test set and returns the Sharpe ratio. Best combination is displayed as a traffic-light terminal table.
- Python 3.11
pip install ta-automl# Default: AMD, 2018–2024, 100 Vizier trials
ta-automl --symbol AMD
# Use FLAML if Vizier/JAX is not available
ta-automl --symbol AMD --optimizer flaml
# Parameter-aware Stage-1 screening (better quality survivors, slower)
ta-automl --symbol AMD --tune-screen --tune-trials 8
# Quick tuned screening with random search (fastest tuning option)
ta-automl --symbol AMD --tune-screen --tune-optimizer random --tune-trials 4
# Custom symbol and date range
ta-automl --symbol NVDA --start 2020-01-01 --end 2024-12-31 --trials 50
# Fewer trials for a quick test
ta-automl --symbol AMD --start 2022-01-01 --end 2023-12-31 --trials 5
# Long-only strategy, save HTML chart
ta-automl --symbol TSLA --no-short --save-html
# All options
ta-automl --help| Option | Default | Description |
|---|---|---|
--symbol |
AMD |
Ticker symbol |
--start |
2018-01-01 |
Start date |
--end |
2024-12-31 |
End date |
--trials |
100 |
Optimizer trial count |
--optimizer |
vizier |
vizier or flaml |
--loss |
sharpe |
Loss function name (see --list-losses) or module:fn |
--list-losses |
off | Print all registered losses and exit |
--metric |
(deprecated) | Legacy alias; prefer --loss |
--top-n |
8 |
Indicators shown in traffic light |
--lookback |
30 |
Recent days in traffic light |
--cash |
10000 |
Starting cash for backtest |
--commission |
0.002 |
Per-trade commission (0.2%) |
--train-ratio |
0.70 |
Train/test split (70% train) |
--no-short |
off | Long-only (default: long+short) |
--save-html |
off | Save interactive backtesting chart |
--p-threshold |
0.20 |
Stage-1 p-value cutoff (if --p-filter enabled) |
--min-sharpe |
-2.0 |
Stage-1 minimum quick Sharpe |
--no-bonferroni |
off | Disable Bonferroni correction |
--tune-screen |
off | Stage-1 hyperparameter search per indicator (Vizier/FLAML/random) |
--tune-trials |
8 |
Trials per indicator during Stage-1 tuning |
--tune-optimizer |
vizier |
Optimizer for Stage-1 tuning: vizier, flaml, random |
--tune-metric |
abs_sharpe |
Score: abs_sharpe, sharpe, neg_p_value |
--tune-method / --no-tune-method |
on | Also search binarization methods |
--output-dir |
results |
Where to save JSON results |
--cache-dir |
.cache |
Local OHLCV parquet cache |
Two extension points, both registry-based and CLI-discoverable:
| Concept | Doc | Flags |
|---|---|---|
| Loss function — what the optimizer maximizes | docs/CUSTOM_LOSS.md | --loss, --list-losses |
| Search strategy — how indicators are combined | docs/CUSTOM_SEARCH.md | --search-strategy, --list-searches |
| Parameter-aware Stage-1 screening — find good per-indicator hyperparameters before screening | docs/PARAMETER_AWARE_SCREENING.md | --tune-screen, --tune-trials, --tune-optimizer |
# List what's registered
ta-automl --list-losses
ta-automl --list-searches
# Built-in losses
ta-automl --symbol AMD --loss min_drawdown --trials 100
ta-automl --symbol AMD --loss calmar --trials 100
# Built-in search strategies
ta-automl --symbol AMD --search-strategy weighted --trials 100 # default — Vizier over weights
ta-automl --symbol AMD --search-strategy automl # FLAML AutoML black-box (no SHAP)
ta-automl --symbol AMD --search-strategy shap # AutoML + SHAP attributions
# Note: --optimizer / --trials only apply to 'weighted'. 'automl' and 'shap'
# use FLAML's internal time budget. See docs/CUSTOM_SEARCH.md.
# Mix and match — user-supplied loss/search via 'module:fn'
ta-automl --symbol AMD --search-strategy my_search:my_fn --loss my_losses:my_lossWhen to use which strategy:
weighted— fastest to interpret. Vizier's per-indicator weights ARE the answer; no SHAP needed. Best when a fixed linear blend is the right model. Respects--optimizerand--trials.automl— black-box tree model that captures non-linear interactions. Returns globalfeature_importances_for the picked model. Use when you want AutoML quality but don't need per-sample interpretability.shap—automl+ SHAP attributions. Required when indicators only matter on tail days / regime shifts (those don't show up in linear weights or coarse global importance). Pulls in theshapextra.
The Python API also accepts callables directly via
evaluate_trial(..., loss_fn=my_fn) and get_search(my_search_fn)(ctx).
Terminal: Rich color-coded traffic-light table showing the most recent lookback trading days, one column per top indicator, plus a combined signal column. Green = BUY, Red = SELL, Yellow = HOLD.
results/{SYMBOL}/results_{start}_{end}.json: Full optimization output — best parameters, metrics, survivor list.
results/{SYMBOL}/chart_{start}_{end}.html: Interactive backtesting chart (only with --save-html).
ta_automl/
├── compat.py # TA-Lib import guard, runtime patches
├── config.py # StudyConfig, ScreenConfig dataclasses
├── data/fetcher.py # yfinance download + parquet cache
├── signals/
│ ├── auto_discover.py # talib.get_functions() wrapper, param space
│ ├── binarizer.py # float series → {-1, 0, +1}
│ └── screener.py # Stage 1 quality filter
├── backtest/strategy.py # Dynamic backtesting.py Strategy factory
├── optimization/
│ ├── evaluator.py # params → signals → backtest → metrics
│ ├── study.py # Vizier in-process runner
│ └── flaml_search.py # FLAML BlendSearch runner
├── display/traffic_light.py # Rich terminal table
└── main.py # Click CLI
| Issue | Fix |
|---|---|
No module named 'jax' (Vizier) |
pip install "jax[cpu]" or use --optimizer flaml |
np.bool8 error (bokeh + numpy 2.x) |
Already fixed: numpy<2.0 pinned in pyproject.toml |
| Very few screener survivors | Normal — Vizier uses weights to select; adjust threshold for example to -99 by --min-sharpe -99 to pass all (well... that's a bit too much) |

