AlphaEngine
The research loop, on your machine.
Ask what is worth looking at, whether it holds up, what sizing the risk model implies,
and whether anything has crossed a line.
Computation runs locally; when you are signed in to quantOS, summary figures are reported to your account; full series are shared only if you opt in.
Research tool, not investment advice.
pip install alphaengine # library and one-shot commands
pip install "alphaengine[tui]" # the full-screen session
alphaengine demo # offline. no account, no data of yours
alphaengine # the session
alphaengine process # fit a DGP to the demo walk and stress it
If the shell cannot find alphaengine, this always works:
python -m alphaengine demo
python -m alphaengine
┌──────────┬─────────────────────────────────────┬──────────────────┐
│ ● maths │ Ask in plain English. │ sharpe │
│ always │ │ mean / std √252 │
│ ○ flows │ > which names rank highest on RSI? │ ▁▂▃▅▇█▇▅▃ │
│ ○ ask │ │ 0 0.0012 │
│ │ chose screen_universe │ 1 -0.0040 │
│ loaded │ │ │
│ sp500 │ │ │
└──────────┴─────────────────────────────────────┴──────────────────┘
There is one session, and it is this screen. A sentence picks a workflow.
run <name> follows that workflow exactly. The math pane opens when a run
has a statistic to show.
alphaengine
> login
> load prices.csv
> screen
Computation runs locally on this machine. When you are signed in to quantOS,
summary figures are reported to your account; full series are shared on
/traces only if you opt in (--share-series or ALPHAENGINE_SHARE_SERIES=1).
Where this sits
AlphaEngine computes locally on your machine against your own data. quantOS is the hosted research workspace that keeps the record and writes committee reports. Everything in AlphaEngine's math and sweep layer runs offline with no account; signing in connects local runs and sealed studies to your quantOS workspace.
What it answers
Eight questions, in the order a research week actually asks them. The command IS the question; only two of them need code of yours.
| Ask | Command | Workflow | Needs |
|---|---|---|---|
| Can I trust this data? | diagnose |
diagnose_data |
prices |
| What is worth a look? | screen |
screen_universe |
prices |
| Does this signal carry information? | signal |
evaluate_signal |
a signal panel + prices |
| Is this real, once you count the tries? | validate |
validate_study |
prices + your backtest |
| Where does it break? | stress |
stress_study |
a return series |
| Is it new, or my book again? | overlap |
check_overlap |
the candidate + the book |
| What sizing does the risk model imply? | size |
size_position |
a return series |
| Still inside the lines? | monitor |
monitor_sleeve |
a return series |
alphaengine diagnose --data prices.csv # before anything runs on it
alphaengine screen --universe sp500
alphaengine size --data returns.csv
The refusals are the point. A screen over a universe that mostly could not
be measured is refused as a survivorship artifact rather than ranked. A size on
a record shorter than its own minimum track length is refused outright, because
a small position is still a claim and the caveat does not travel with it into a
book. A monitor with no stated tolerances reports unchecked — never a green
light. A stop exits 0: "this did not clear the bar" is the system working.
Or say it in plain English and let your own model pick:
alphaengine
❯ which of my names rank highest on RSI?
That second path is EXPLORATORY: the model chooses a workflow and then chooses
each step from what the server permits. Two runs of the same question may
differ, and the run says so. run <workflow> is SCRIPTED and reproducible.
Both are legitimate; presenting one as the other is not.
The three rungs
Each is useful without the one above it. login lights the next one.
| Rung | What you need | What you get |
|---|---|---|
| The maths | nothing | alphaengine demo, alphaengine process, and the full Python API (import alphaengine, sweep, core), offline, forever. No account. |
| Workflows | a portal-issued ae_live_ key (login or QUANTOS_API_KEY; request access at https://qosai.app/access) |
The eight workflow commands (diagnose, screen, signal, validate, stress, overlap, size, monitor) plus workflows, runs, gaps, tonight, and run <workflow>. |
| Ask anything | your OWN model key (login anthropic) |
Plain English in. Runs under your account, not ours. |
The eight workflow commands (diagnose, screen, signal, validate,
stress, overlap, size, monitor) and workflows / runs require a
portal-issued ae_live_ key because the workflow graph and account record live
on the server (request access at https://qosai.app/access).
alphaengine demo, alphaengine process, and the Python library (import alphaengine)
work completely offline without an account or key.
How credentials are handled. QuantOS never receives your LLM provider key:
it goes directly from this process to your chosen provider. By default, keys can
be supplied via environment variables (QUANTOS_API_KEY, ANTHROPIC_API_KEY,
OPENAI_API_KEY, etc.), which touches nothing on disk. If you run login and
say yes to "Stay signed in on this machine?", auth.py writes an optional
~/.config/alphaengine/credentials.json (0600 permissions;
%APPDATA%\alphaengine\credentials.json on Windows) so you do not have to
re-paste in every terminal. Environment variables always win over that file.
To avoid disk storage entirely, set environment variables only (or answer n
at login). To delete the stored file at any time, run
alphaengine logout --purge (or alphaengine logout).
flowchart LR
A["demo / import<br/>offline maths"] --> B["login<br/>QuantOS key"]
B --> C["login anthropic<br/>your model"]
A -.->|"no account"| D["DSR · PBO · ICIR · HRP"]
B -.->|"ae_live_"| E["screen · validate · runs"]
C -.->|"BYOK"| F["plain English in"]
Getting your data in
One verb, three shapes. Nothing is ever fetched on your behalf.
load prices.csv # a local CSV
load sp500 # registered in the portal, with the closes you stored
load research.momentum # a module of yours (the only door that can carry a simulator)
From a shell the same doors are flags, for scripts:
alphaengine screen --data prices.csv
alphaengine screen --universe sp500
alphaengine validate --project research.momentum
--data reads three shapes, decided by the header and nothing else:
wide date,AAPL,MSFT,NVDA one column per name
long date,symbol,close one row per name per day
series date,close a single series, or one bare column of numbers
A file it cannot place raises and names all three rather than guessing. A loader that guesses eventually guesses wrong on somebody's book, and the failure is not an error: it is a screen that ranked returns as prices and produced a shortlist nobody can tell is wrong.
--project is the only door that can carry a simulator, because a simulator
is code. That is why validate_study needs it — and evaluate_signal needs it
for the scores it measures — while the rest run on prices alone.
--universe brings the closes you stored with it in the portal. That is your own
upload decrypted back to your own account, not us fetching market data, and the
distinction is the whole of the data boundary below.
Research OS quickstart
AlphaEngine works like git for quantitative research: initialize a local .alphaengine/ project once, and every dataset registration, hypothesis, workflow run, parameter sweep, and sealed study is recorded in an append-only content-addressed ledger on your disk (see project format, result envelope, and plugins). Both alphaengine and the short alias ae run the same entry point.
# 1. Initialize a local .alphaengine/ workspace
ae init --name equity-momentum
# 2. Register a local CSV (or Parquet) dataset and run hygiene checks
ae data add prices.csv --name equities --asof-col date
ae data show equities
# 3. Record a falsifiable hypothesis with an explicit kill rule
ae idea new "Cross-sectional 20d momentum holds net of costs" --kill-if "DSR < 0.9" --family mom_20d
# 4. Run a validation, declarative YAML pipeline, or Unix JSON pipe
ae validate --project alphaengine.demo --idea idea_1
ae run examples/screen_validate_size.yaml
ae screen --data equities --json | ae validate --stdin --json | ae size --stdin
# 5. Inspect the append-only project ledger, cumulative trials, and diffs
ae log --family mom_20d
ae show HEAD
ae rerun HEAD
# 6. Seal into a Schema 1.2 Study, verify against data, and export a committee memo
ae seal HEAD
ae verify .alphaengine/studies/HEAD.json
ae report memo HEAD --format md
What's new in 1.1.0
- Local-first project workspace (
ae init): Creates.alphaengine/(project.toml,ledger/,data/,studies/,ideas/,universes/, and SHA-256 content-addressedobjects/). Commands walk up from the current directory to find the project; outside a project, every command behaves as a standalone call. - Project-wide ledger & cumulative trial counting (
ae log,ae show,ae diff,ae rerun): Runs and sweeps inside a project automatically record parameter hashes, dataset hashes, and figures, accumulating trial counts across runs on the sameidea_idorfamily. - Dataset registry (
ae data add|list|show|lineage): Tracks local CSV and Parquet datasets by full SHA-256, row count, date range, column schema, and hygiene diagnostics (duplicate timestamps, missing/calendar gaps,--asof-collook-ahead hints, and survivorship hints). - Versioned
v1.0JSON result envelope,--stdinpipes, & YAML pipelines: Every command supports--jsonand--stdin(ae screen --json | ae validate --stdin | ae size --stdin), plus declarative multi-step recipes (ae run pipeline.yaml). - Hypotheses as objects (
ae idea new|list|status|show): Link runs with--idea <id>, evaluate--kill-ifrules against cumulative figures, and receive automatic prior-work warnings before running overlapping parameter sets. - Seal, verify, & cited committee memos:
ae seal <run|idea>,ae verify <study.json> [--data <file>], andae report memo <idea|run> --format md|pdf(where every figure cites[run_id]). - Research shell (
ae shell), plugins (ae plugin list), watcher (ae watch), live drift monitor (ae monitor <run> --live <file>), & opt-in sync (ae remote,ae push [--dry-run],ae pull).
What's new in 1.0.3
- Opt-in series sharing & 512-element wire guard: By default, workflow step traces (
/api/harness/runs/{id}/traces) report scalar figures only; full series are shared only when you pass--share-seriesor setALPHAENGINE_SHARE_SERIES=1. - Sealed studies (schema 1.2) & CLI reporting: Studies now carry a canonical-JSON SHA-256 seal (
content_sha256), a full 64-hexdata_hash, and an optional per-trial ledger (include_ledger=True). Usealphaengine seal <study.json>to seal or verify a study file, andalphaengine report <study.json> [--retail]to ingest into quantOS or the retail experiment ledger. - Math fixes:
SweepResult.verdict()passes cross-trial Sharpe dispersion intodeflated_sharpeper Bailey & López de Prado (2014);deflated_sharpe(n_trials=1)returnspsr_vs_zerowithsr0_expected_max = 0.0instead of deflating asN=2;combinatorial_oos_stabilityreplacescpcv_score(kept as a deprecated alias); andpbo_cscvvalidates thatn_splitsis an even integer $\ge 2$. - Interop loaders (
alphaengine.io):from_quantconnect(path)andfrom_vectorbt(portfolio_or_csv)build aStudywith returns and preserve trial-count provenance ("asserted"vs."not_recorded").
See CHANGELOG.md for 1.0.0–1.0.3 release notes.
What's new in 0.9.0
next_open fills wait for a prior close. The first bar of a next-open backtest
no longer trades that bar's own signal at that bar's open. Close fills are
unchanged, and the published goldens do not move.
The session is the full-screen terminal (pip install "alphaengine[tui]").
Each statistic keeps its formula, inputs, and series locally. By default,
/api/harness/runs/{id}/traces receives scalar figures only; full series are
uploaded only when you opt in (--share-series or ALPHAENGINE_SHARE_SERIES=1).
A portal that rejects the trace does not fail the run.
What's new in 0.8.0
Named processes, Grinold, and a frozen chart contract. Existing goldens
(deflated Sharpe, PBO, performance, screen) are byte-identical. New figures
are a public contract from this release. The study schema is 1.1: optional
process and charts fields; 1.0 studies still load.
| You have | You get | Module |
|---|---|---|
| A spread or a close | OU / GBM / jump / GARCH(1,1) calibration, then Monte Carlo stress under that law | core.process |
| An IC and a cross-section | Grinold alpha, breadth, transfer coefficient | core.active |
| A return panel | Detoned covariance and a PCA variance-explained table | core.covariance |
| Figures that already travel | {kind, key, title} hints a portal maps to a chart |
charts, figure contract |
The terminal boot is the README banner: tracked ALPHAENGINE, the subtitle,
a teal-left / amber-right mesh (plateau, valley, knife-edge ridge), and the
local-computation notice. alphaengine process fits a DGP offline, no account.
from alphaengine.core import ou_calibrate, garch_calibrate, dgp_stress, grinold_alpha
ou = ou_calibrate(spread) # kappa, theta, half_life; or not mean-reverting
g = garch_calibrate(returns) # omega, alpha, beta, persistence in (0, 1)
s = dgp_stress(close, dgp="ou") # n_trials = n_paths, source monte_carlo
a = grinold_alpha(panel, ic=0.05, vols=vols) # alpha stays here; missing vols are not treated as 1
What's new in 0.7.0
The daily modelling morning, and the overnight book, without a third dependency. Existing goldens (deflated Sharpe, PBO, performance, screen) are byte-identical. New figures are a public contract from this release.
| You have | You get | Module |
|---|---|---|
| A raw factor panel | Cross-sectional rank, z-score, winsorize, neutralize | core.panel |
| A signal and prices | ICIR, Newey-West t-stat, Fama-MacBeth λ, quantile book with one-way turnover | core.signals, core.cross_section |
| A return panel | EWMA / Ledoit-Wolf / Marchenko-Pastur covariance, HRP, risk parity, vol target | core.covariance, core.allocate |
The session is slimmer too: login lights a rung, load is the one data verb,
and boot paints the parameter surface this tool actually judges rather than a
command encyclopedia.
from alphaengine.core import cs_zscore, signal_icir, hrp_weights, fama_macbeth
z = cs_zscore(factor_panel) # skipped names are counted, not dropped
ic = signal_icir(signal, prices) # Spearman ICIR; Pearson is opt-in
fm = fama_macbeth(signal, prices) # λ_mean, t-stat, Newey-West
w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
Command reference
alphaengine commands prints this directory in the terminal, and
alphaengine commands run expands any one of them.
Get started
| Command | Does | Where |
|---|---|---|
init [--name NAME] |
initialize a local .alphaengine/ research project in this directory | shell + session |
demo |
run the built-in example offline, with no account and no data | shell + session |
process [ou | gbm | jump | garch] |
fit a named process to a series and stress it, offline | shell + session |
runs [--limit N] |
your own week: what ran, what it decided, what it filed | shell + session |
log [--idea ID] [--family FAMILY] [--since DATE] [--verdict VERDICT] |
list local project ledger runs, trial counts, and verdicts | shell + session |
show <run> |
inspect one recorded project run, its hashes, trials, and figures | shell + session |
diff <run-a> <run-b> |
compare data, parameter, trial, and figure changes between two runs | shell + session |
gaps |
what your record says is UNANSWERED, and what closes each one | shell + session |
tonight [--budget N] |
what would run unattended tonight, without running any of it | shell + session |
workflows |
what the server offers, what each needs, and which reproduce | shell + session |
login [quantos | anthropic | openai | gemini | groq | azure | openrouter | gateway] |
sign in, or login anthropic for a model key | shell + session |
key [quantos | anthropic | openai | gemini | groq | azure | openrouter | gateway] |
same as login: enter a credential, or see which rungs are lit | session |
commands [verb] |
this directory, or one command in full | shell + session |
models |
which model providers this machine can actually use | shell + session |
model [<provider[:name]>] |
pin the model for this session, or show the pin | session |
trace [run_id] |
local model/run events for a run, hashed prompts only | shell + session |
Do the work
| Command | Does | Where |
|---|---|---|
diagnose |
can I trust this data? | shell + session |
screen |
what is worth a look? | shell + session |
signal |
does this signal carry information? | shell + session |
validate |
is this real? | shell + session |
stress |
where does it break? | shell + session |
overlap |
is it new, or my book again? | shell + session |
size |
what sizing does the risk model imply? | shell + session |
monitor |
still inside the lines? | shell + session |
run <workflow> |
run one workflow exactly as written | shell + session |
rerun <run> |
reproduce a recorded project run from its inputs and check figures | shell + session |
watch <pipeline.yaml> [--interval SECONDS] |
re-run a declarative pipeline whenever a registered dataset file changes | shell + session |
seal <study.json | run | idea> |
write canonical content_sha256 into a study (or seal a project run/idea) | shell + session |
verify <study.json> [--data FILE] |
verify a study.json content_sha256 seal and optionally re-check figures | shell + session |
report <study.json> [--retail] | memo <idea | run> [--format md | pdf] |
send a study to quantOS, or export a cited committee memo (md/pdf) | shell + session |
<anything else> |
ask in plain English; your model picks a workflow and drives it | session |
status |
the current run, and what is loaded | session |
Bring your data
| Command | Does | Where |
|---|---|---|
book [<name> | status] |
show or load sleeves on the multi-strategy book | session |
load <file | module | universe> |
a CSV, a project module, or a portal universe | session |
idea new "<thesis>" --kill-if "<rule>" [--family FAMILY] | list | status <id> <state> | show <id> |
create, list, update, and evaluate local research hypotheses | shell + session |
thesis [<name> | clear] |
choose a portal thesis for the next run | session |
correlation |
the correlation of every loaded name with every other name | session |
covariance |
the sample covariance of every loaded name with every other name | session |
cointegration |
which loaded names are cointegrated | session |
portfolio |
weights for the names already loaded | session |
universe <name> |
same as load: a universe registered in the portal | session |
data add <file> [--name NAME] [--asof-col COL] | list | show <name> | lineage <run> |
register, inspect, and trace local CSV/Parquet datasets | shell + session |
project <module> |
same as load: a module with data and backtest_fn | session |
Session
| Command | Does | Where |
|---|---|---|
remote add <name> [url] | list |
configure or list remote sync endpoints in .alphaengine/project.toml | shell + session |
push [remote] [--dry-run] [--share-series] |
sync sealed studies, ledger runs, and ideas to quantOS (opt-in network) | shell + session |
pull [remote] |
pull team runs and ideas from quantOS into the local ledger (read-only) | shell + session |
plugin list |
list installed loader, method, and report template plugins | shell + session |
shell |
open the interactive research shell with project completion and status | shell |
logout [--purge] |
remove stored credentials from this machine | shell + session |
version |
print the version | shell |
help |
the short list: demo, login, load, then a question | session |
quit |
leave the session | session |
Data flags
| Flag | What it takes |
|---|---|
--data FILE |
a local CSV. Wide (date,AAPL,MSFT), long (date,symbol,close), or one series. |
--universe NAME |
a universe you registered in the portal, with the closes you stored. |
--project MODULE |
a Python module. The only door that can carry a simulator. |
Flags
| Flag | Does |
|---|---|
--url URL |
workflow server (default $QUANTOS_API_URL or the public API) |
--key KEY |
portal-issued ae_live_ key (default $QUANTOS_API_KEY) |
--project MODULE |
a module exposing data and backtest_fn |
--data FILE |
a local CSV: wide, long, or a single series |
--universe NAME |
a universe registered in the portal, with its stored closes |
--thesis NAME |
a thesis on this account; the run is attached to it |
--symbol TICKER |
one name out of a loaded universe; its closes become the return series |
--label TEXT |
what to call the artifact this run produces |
--input K=V |
a workflow input; repeatable |
--quiet |
only the result, no step narration |
--stream |
print the answer once the citation guard has passed |
--share-series |
opt in to uploading full series on run traces (default off; or $ALPHAENGINE_SHARE_SERIES=1) |
--limit N |
how many rows to show (default 25) |
--budget N |
how many runs a night is worth (default 3) |
--purge |
remove ~/.config/alphaengine/credentials.json and clean up its config directory |
--retail |
send the study to the retail experiment ledger (/api/retail/ledger/import) |
--name NAME |
name for the initialized project or registered dataset |
--idea ID |
attach the run or filter the log to a local hypothesis id |
--family FAMILY |
strategy or experiment family for cumulative trial counting |
--since DATE |
filter project log to runs on or after YYYY-MM-DD |
--verdict VERDICT |
filter project log by verdict (e.g. robust, marginal, likely_noise) |
--asof-col COL |
as-of column name or cutoff date for look-ahead checks in data add |
--json |
emit a versioned v1.0 JSON result envelope to stdout |
--stdin |
read an upstream JSON result envelope from standard input |
--kill-if RULE |
kill rule for a hypothesis, e.g. 'DSR < 0.9' |
--format FMT |
output format for report memo: md or pdf (default md) |
--dry-run |
print the exact sync payload without making a network call |
--live FILE |
live return series CSV to compare against a recorded backtest run |
--interval SECONDS |
polling interval in seconds for watch (default 1.0) |
--cycles N |
number of polling cycles for watch before exiting (0 means continuous) |
Examples
alphaengine diagnose --universe <name>
alphaengine screen --universe <name>
alphaengine signal
alphaengine validate
alphaengine stress
alphaengine overlap
alphaengine size
alphaengine monitor
alphaengine init
alphaengine init --name momentum
alphaengine demo
alphaengine process
alphaengine process ou
alphaengine runs
alphaengine runs --limit 50
alphaengine log
alphaengine log --family momentum --verdict likely_noise
alphaengine show HEAD
alphaengine diff run_a run_b
alphaengine gaps
alphaengine tonight
alphaengine tonight --budget 5
alphaengine workflows
alphaengine login
alphaengine commands
alphaengine run screen_universe --universe sp500
alphaengine run size_position --data returns.csv
alphaengine run validate_study --project research.momentum
alphaengine run examples/screen_validate_size.yaml
alphaengine rerun HEAD
alphaengine watch examples/screen_validate_size.yaml
alphaengine seal study.json
alphaengine seal HEAD
alphaengine verify study.json
alphaengine verify study.json --data prices.csv
alphaengine report study.json
alphaengine report study.json --retail
alphaengine report memo HEAD --format md
alphaengine models
alphaengine trace
alphaengine idea new "Cross-sectional momentum holds" --kill-if "DSR < 0.9" --family momentum
alphaengine idea list
alphaengine idea status idea_1 testing
alphaengine idea show idea_1
alphaengine run screen_universe --thesis momentum
alphaengine data add prices.csv --name equities
alphaengine data list
alphaengine data show equities
alphaengine data lineage HEAD
alphaengine remote add quantos
alphaengine remote add quantos https://qosai.app
alphaengine push --dry-run
alphaengine push
alphaengine pull
alphaengine plugin list
alphaengine shell
alphaengine logout
alphaengine logout --purge
alphaengine version
The library, without the CLI
The commands above are a wrapper. Everything they compute is importable, runs offline, and needs no account:
from alphaengine import sweep
r = sweep(backtest_fn, {"fast": [5, 10, 20], "slow": [50, 100, 200]}, data=prices)
r.surface() # is the result a broad plateau or a single lucky configuration?
r.verdict() # deflated for the 9 trials that were actually run
r.save() # study.json, on your disk
The trial count is derived from the grid that ran, never asserted. Omit it
and it is recorded as not_recorded rather than defaulting to a flattering 1,
and an unrecorded denominator cannot reach an edge verdict at all.
Writing your own backtest_fn
Two rules, both easy to get wrong the first time, and the reason the example above exists to copy:
Return a bare 1-D return series. Not a dict, not a stats object — the
per-period returns themselves. sweep does np.asarray(list(raw)), so a dict
of results iterates its keys and fails on the first string.
Return the same length for every combination. PBO splits the trial matrix into time blocks and compares configurations within each block, which only means anything if they line up in time. Ragged output is refused rather than truncated, because silently trimming produces a confident number over series that do not correspond. In practice: pick a warm-up long enough for the slowest window in your grid and start every configuration there.
WARMUP = 200 # covers the slowest `slow` in the grid
def backtest_fn(*, data, fast, slow):
close = data["close"]
return [
(close[i + 1] - close[i]) / close[i] * (1 if sma(close, fast, i) > sma(close, slow, i) else 0)
for i in range(WARMUP, len(close) - 1)
]
data is whatever you want it to be — a DataFrame, a dict of series, an array.
The package never inspects it; computation runs locally on your machine.
What it does
Runs your parameter grid. sweep() calls your backtest function once per
combination. It does not backtest anything itself, so the engine you already
trust stays the engine you trust.
Counts the trials for you. The statistics that correct a Sharpe ratio for
multiple testing need to know how many variants were tested. That number is
almost never recorded, because nobody counts what they discarded. Running the
grid makes it len(grid), so it never has to be asked for or asserted.
Refuses to flatter an unrecorded count. Since 0.2.0, omitting n_trials
means not_recorded — not 1. The trial count comes back null,
n_trials_source travels beside it, and a verdict of edge is unreachable
without a recorded denominator. A deflated Sharpe is a ratio; deflating by a
denominator nobody wrote down does not produce a weaker claim, it produces a
claim about nothing.
Shows you the neighbourhood. The output is whether your result sits on a broad plateau or a knife edge, and where the robust region is centred. A single spike surrounded by failures is a result fitted to its own parameters.
Produces a portable study. A JSON artifact holding what was tried, what came back, and a content hash of the data it ran on. Readable in a text editor, diffable, and versioned so it still parses in two years.
What is in it
| Module | Contents |
|---|---|
alphaengine.core |
deflated Sharpe, PSR, PBO via CSCV, CPCV, minimum track record length, performance and risk |
alphaengine.core.panel |
cross-sectional rank, z-score, winsorize, neutralize |
alphaengine.core.signals |
IC, ICIR, quantile returns, decay |
alphaengine.core.cross_section |
Fama-MacBeth, quantile book with turnover |
alphaengine.core.covariance |
EWMA, Ledoit-Wolf, Marchenko-Pastur denoise, detone, variance explained |
alphaengine.core.allocate |
HRP, risk parity, vol target (EWMA or GARCH) |
alphaengine.core.process |
OU, GBM, jumps, GARCH(1,1), DGP stress |
alphaengine.core.active |
Grinold alpha, breadth, transfer coefficient |
alphaengine.charts |
frozen {kind, key, title} hints; see figure contract |
alphaengine.sweep |
the grid runner and the sensitivity surface |
alphaengine.study |
the study artifact and its schema |
alphaengine.client |
the workflow client and the step executor |
alphaengine.cli |
the alphaengine terminal entry point |
Two runtime dependencies, numpy and scipy, both already present in a typical
research environment. import alphaengine makes no network call and needs no
account. Factor decomposition and cointegration testing need statsmodels and
are available as pip install 'alphaengine[factors]'.
Getting a study to somebody else
save() writes to your disk and needs no account. When the work has to reach
the PM who will act on it, report() sends the study — and only the study.
import os
from alphaengine import Study, sweep
os.environ["QUANTOS_API_KEY"] = "ae_live_..." # created in the portal
r = sweep(backtest_fn, grid, data=prices)
r.save() # yours, on your disk, always
Study.from_sweep(r, label="momentum, 9 configs").report()
What crosses is an explicit allowlist: the trial count and how it was obtained,
a content hash of the data, the verdict, the shape of the neighbourhood, the
performance figures. Computation runs locally; when you are signed in to
quantOS, summary figures are reported to your account, and full series are
shared only if you opt in (--share-series or ALPHAENGINE_SHARE_SERIES=1).
Without opt-in, a guard keyed on length (at most 512 elements) refuses to send
anything series-shaped whatever it is called.
Reporting is the only part of this package that touches a network, so it is the
only part that is not imported until you call it. import alphaengine still
makes no network call.
Where this sits in QuantOS
AlphaEngine is the open research layer of quantOS (GitHub). AlphaEngine computes locally on your machine, against your data, and is complete on its own: everything above works offline and forever, at no cost.
quantOS (https://qosai.app) is the hosted research workspace that keeps the record and writes committee reports. Studies produced here can be persisted to a firm's record, referenced when an idea becomes a position, and assembled into the reports that go to an investment committee or an allocator. The library computes; the platform remembers and reports.
The methods
Everything in core comes from the published literature. Nothing here is a
proprietary formula, which is deliberate: a referee whose reasoning you cannot
inspect is not a referee.
Deflated Sharpe Ratio, Probabilistic Sharpe Ratio, minimum track record length
Bailey, D. H., and López de Prado, M. (2012). "The Sharpe Ratio Efficient
Frontier." Journal of Risk 15(2), 3 to 44.
Bailey, D. H., and López de Prado, M. (2014). "The Deflated Sharpe Ratio:
Correcting for Selection Bias, Backtest Overfitting, and Non-Normality."
Journal of Portfolio Management 40(5), 94 to 107.
Implementation notes: Grid trials are treated as independent (N = len(grid)
with no effective-$N$ clustering adjustment). Cross-trial Sharpe dispersion
($\sigma_{\text{SR}}$) is computed across valid grid trials when at least two
exist, falling back to single-series Lo (2002) sampling error otherwise. At
n_trials=1, no multiple-testing deflation applies (sr0_expected_max = 0.0,
deflated_sharpe = psr_vs_zero). Verdict thresholds on deflated_sharpe:
< 0.5 is likely_noise, 0.5 to < 0.9 is marginal, and DSR >= 0.9 is
robust.
Probability of Backtest Overfitting via CSCV Bailey, D. H., Borwein, J., López de Prado, M., and Zhu, Q. J. (2017). "The Probability of Backtest Overfitting." Journal of Computational Finance 20(4), 39 to 69.
Combinatorial out-of-sample stability (combinatorial_oos_stability)
López de Prado, M. (2018). Advances in Financial Machine Learning. Wiley,
chapters 7 and 12.
Implementation note: Evaluates a single pre-computed return stream across all
$\binom{G}{k}$ held-out block combinations with purge and embargo trimmed
from each test block (no model re-fitting or stitched training predictions).
cpcv_score is retained as a deprecated alias.
Hierarchical Risk Parity
López de Prado, M. (2016). "Building Diversified Portfolios that Outperform
Out of Sample." Journal of Portfolio Management 42(4), 59 to 69.
Implementation note: hrp_weights uses the correlation metric distance
$d_{ij} = \sqrt{\frac{1}{2}(1 - \rho_{ij})}$ with single-linkage hierarchical
clustering (scipy.cluster.hierarchy.linkage(..., method="single")), followed
by quasi-diagonalization and recursive inverse-variance bisection.
Covariance shrinkage and spectral denoising Ledoit, O., and Wolf, M. (2004). "A Well-Conditioned Estimator for Large-Dimensional Covariance Matrices." Journal of Multivariate Analysis 88(2), 365 to 411. Laloux, L., Cizeau, P., Bouchaud, J.-P., and Potters, M. (1999). "Noise Dressing of Financial Correlation Matrices." Physical Review Letters 83(7), 1467 to 1470.
Multiple testing in asset pricing Harvey, C. R., Liu, Y., and Zhu, H. (2016). "... and the Cross-Section of Expected Returns." Review of Financial Studies 29(1), 5 to 68. Harvey, C. R., and Liu, Y. (2015). "Backtesting." Journal of Portfolio Management 42(1), 13 to 28.
Fama-MacBeth Fama, E. F., and MacBeth, J. D. (1973). "Risk, Return, and Equilibrium: Empirical Tests." Journal of Political Economy 81(3), 607 to 636.
Ornstein-Uhlenbeck / Vasicek Vasicek, O. (1977). "An Equilibrium Characterization of the Term Structure." Journal of Financial Economics 5(2), 177 to 188.
GARCH(1,1) Bollerslev, T. (1986). "Generalized Autoregressive Conditional Heteroskedasticity." Journal of Econometrics 31(3), 307 to 327.
Jump-diffusion Merton, R. C. (1976). "Option Pricing When Underlying Stock Returns Are Discontinuous." Journal of Financial Economics 3(1-2), 125 to 144.
The fundamental law of active management Grinold, R. C., and Kahn, R. N. (2000). Active Portfolio Management. 2nd ed. McGraw-Hill.
Downside deviation Sortino, F. A., and Price, L. N. (1994). "Performance Measurement in a Downside Risk Framework." Journal of Investing 3(3), 59 to 64.
Factor regression standard errors (in the factors extra)
Newey, W. K., and West, K. D. (1987). "A Simple, Positive Semi-Definite,
Heteroskedasticity and Autocorrelation Consistent Covariance Matrix."
Econometrica 55(3), 703 to 708.
Unit root testing for cointegration (in the factors extra)
Dickey, D. A., and Fuller, W. A. (1979). "Distribution of the Estimators for
Autoregressive Time Series with a Unit Root." Journal of the American
Statistical Association 74(366), 427 to 431.
Reproducibility
The values these functions return are treated as a public contract. A study written today has to reproduce in two years, so a change to a computed value is a breaking change requiring a version bump even when the signature is unchanged. While the leading digit is 0 the minor position carries that rule — 0.1 → 0.2 is what a changed figure costs — so every 0.2.x release produces identical numbers. CI fails if a pinned value moves.
Installing: "command not found"
The console script lives in your venv's Scripts/ (Windows) or bin/ (POSIX),
which joins your PATH only while that venv is activated. A fresh install
followed by alphaengine therefore says command not found, which reads as a
broken install and is not one.
.\.venv\Scripts\Activate.ps1 # Windows PowerShell
source .venv/bin/activate # macOS / Linux
alphaengine
Or skip activation entirely — python -m alphaengine always works, because
the interpreter that can import the package can always run it:
.venv/Scripts/python.exe -m alphaengine # Windows, unactivated
.venv/bin/python -m alphaengine # POSIX, unactivated
This is the price of in-process data access, and it is the one cost we will not
engineer around: a launcher that worked from anywhere would have to run in its
own environment, which is exactly the isolation that makes compute.* unable to
see your DataFrames.
The run narrates itself, because a loop you cannot watch is a loop you cannot trust. The server names an op, this machine executes it and hands back figures:
validate_study · <your workflow server>
server → compute.sweep
local · done
server → compute.deflated_sharpe
local · done
server → Stop: the surface is a ridge, not a plateau
--project names an ordinary module of yours exposing data and, if the
workflow sweeps, backtest_fn. Authenticate with a key from the portal:
export QUANTOS_API_KEY=ae_live_... # QUANTOS_API_URL for self-hosted or VPC
A stop exits 0. "This did not clear the bar" is the system working, not a broken build — a non-zero exit there would make every CI pipeline treat an honest refusal as a failure, which is exactly the pressure that gets honesty controls switched off. Only a run that could not execute a step exits non-zero.
Install this into the venv you do research in — not with
pipxoruv tool install. The compute steps execute in-process against your own DataFrames, so tool isolation, which is normally the right way to install a CLI, is the one thing that cannot work here. You cannot have isolation and in-process data access, and your data not moving is the point.
No LLM dependency in the core. AgentDriver takes a callable, so you bring
your own model by passing a function or setting your provider's environment
variable. LLM keys are never sent to QuantOS; if you choose to persist keys
locally at login, they live in ~/.config/alphaengine/credentials.json
(mode 0600) and are removed by alphaengine logout --purge.
Development
python -m venv .venv && .venv/bin/pip install -e '.[dev,factors]'
CI runs five gates. Run all of them before pushing — ruff check and
ruff format are different tools with different opinions, and passing one
says nothing about the other:
python -m ruff check src tests
python -m ruff format --check src tests
python -m mypy src
python -m pytest
python -m pytest tests -m golden -q # the frozen figures
Licence
Apache-2.0. See LICENSE.
Metadata
Release files for alphaengine 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| alphaengine-1.1.0.tar.gz | 3.4 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| alphaengine-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.8 MB
Release files / alphaengine-1.1.0.tar.gz
| Download URL | alphaengine-1.1.0.tar.gz |
|---|---|
| Size | 3.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
511661905772c4c4a748d40f900e75370aba6f630a8a388439b22425750c0ae3
|
|
BLAKE2b-256 checksum How to use checksums |
7dcb4728e091bde695282f2c7a74a87d5a9e5fdfd444310f6fda71013be7056e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logRelease files / alphaengine-1.1.0-py3-none-any.whl
| Download URL | alphaengine-1.1.0-py3-none-any.whl |
|---|---|
| Size | 399.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a27b853d24a69074ebe0c5880629ba72d3528a5f8b95d2c46154e4137ca6e0ff
|
|
BLAKE2b-256 checksum How to use checksums |
be1bb701b8348747c3d7f4d4399f81162c2c4c554958415ce42e47ae89f29f6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logSecurity for alphaengine 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for alphaengine-1.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.