Skip to content

About

Institutional-grade quantitative strategy finder. The 9-step protocol as code: bootstrap EV, Monte Carlo, IS/OOS gauntlet. Educational research only.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

Repository files navigation

quant-finder — institutional protocol as code

The 9-Step Institutional Protocol, as code.

Automates steps 1–7. Refuses to trade. Every strategy must survive the gauntlet.

Python Tests License Costs Status Steps 8–9

Educational research only — not financial advice. This software never trades and never will.


The pipeline

flowchart LR
    A["1 · IDEA<br/><i>falsifiable thesis</i>"] --> B["2 · DATA<br/><i>yfinance · cached</i>"]
    B --> C["3 · ENCODE<br/><i>fn(df) → trades</i>"]
    C --> D["4 · BACKTEST<br/><i>4 bps costs always</i>"]
    D --> E["5 · ANALYZE<br/><i>metrics in R</i>"]
    E --> F["6 · OPTIMIZE<br/><i>≤ 2 params · wide steps</i>"]
    F --> G["7 · OVERFIT CHECK<br/><i>IS/OOS gauntlet</i>"]
    G --> H{{"VERDICT"}}
    H -->|PASS| I["8 · LIVE SETUP<br/><i>human-only</i>"]
    H -->|FAIL| J["record reasons<br/><i>propose ONE new hypothesis</i>"]
Loading

validation pipeline


The validation gauntlet

A hypothesis earns PASS only if all six gates hold — no exceptions, no partial credit:

# Gate Threshold
Ⅰ Sample size ≥ 100 trades
Ⅱ Expectancy Average trade > 0 after 4 bps round-trip costs — a backtest without costs is void
Ⅲ Split integrity In-sample / out-of-sample 80/20, ordered by time, never overlapping
Ⅳ Bootstrap EV 10,000 resamples in R-multiples: P(EV < 0) < 5% OOS and < 20% IS
Ⅴ Overfitting signal IS and OOS 95% CIs must overlap
Ⅵ Order-luck Monte Carlo 1,000 runs: ≤ 10% losing simulations

Plus one discipline the software enforces structurally: every filtered variant is reported next to its unfiltered BASE CASE. If the filter can't beat the dumb version, the filter is decoration.

Survivors get the risk yardstick table — simulated percentiles of P&L and drawdown after 10/20/40 trades. This is the future live kill-switch map: 75th percentile DD → cut size · 95th percentile DD → full stop. A human always decides.

Doctrine: "If you haven't completed every single step, don't trade. Save your money." Process over prediction. A FAIL is progress — record reasons, move on.


First run — the honest scoreboard

verdict hypothesis family trades avg trade OOS P(EV<0) MC losing
✗ donchian20-atr-regime breakout (filtered) 33 +217.5 bps 13.5% 0.4%
✗ donchian20-base breakout (base) 52 +197.9 bps 15.3% 0.0%
✗ gapdown2-revert mean-reversion (base) 29 −0.4 bps 59.0% 42.9%
✗ overnight-base overnight (base) 2,935 +1.8 bps 15.9% 15.9%
✗ overnight-sma20 overnight (filtered) 1,992 +0.4 bps 54.4% 53.8%
✗ turn-of-month-3d seasonality (base) 140 +25.2 bps 44.6% 45.2%

0 PASS / 6 FAIL — and that is the system working. Donchian shows a real edge (+198 bps/trade, PF 4.15) but dies on sample size; the overnight anomaly exists but can't clear 4 bps of costs. The gauntlet exists to say no before your account does. Full reasoning per hypothesis in artifacts/00-index.md.


Architecture

Layer Implementation
Data yfinance daily bars, 2015 → today, parquet-cached, MultiIndex flattened
Hypotheses {id, family, falsifiable thesis, symbol, fn(df) → trades DataFrame}
Backtester Next-bar fills, no look-ahead (unit-tested), costs on every trade
Validation Bootstrap EV in R · shuffle MC · time-ordered 80/20 split · CI overlap
Risk Percentile yardstick map with kill-switch thresholds (75th/95th DD)
Determinism Hypothesis id → SHA-256 seed → identical verdicts every run
Automation Weekly cron: engine → verdict diff → summary with iteration proposals

Every trade is generated by next-bar execution and verified by regression tests — including one born from a real bug: a Donchian exit window that included the current bar could never be breached by its own close (0 trades forever). It's now a named test so it can never come back.


Run it

git clone https://github.com/Ferry737/quant-finder && cd quant-finder
uv sync                             # .venv · Python 3.12 · numpy · pandas · yfinance · pyarrow
uv run python -m pytest tests/ -q   # 49 tests
uv run python finder.py             # full engine run, ~2–5 min (bootstrap dominates)
engine run complete: 6 hypotheses, 0 PASS, 0 ERROR
global index: artifacts/00-index.md
  [ FAIL] donchian20-base          trades=52    avg=197.9bps
  ...

Deliverables per run: artifacts/00-index.md (global, PASS-first) and one artifacts/<id>/ directory per hypothesis — 00-index.md (thesis, verdict, reasons, metrics, yardsticks), verdict.json (machine-readable), trades.csv.

The engine snapshots verdicts to .last_run/verdicts.json and diffs every run against the previous one — verdict changes are headline news.


Adding a hypothesis

Edit build_bank() in finder.py:

("my-hypothesis-id", "family",
 "Falsifiable question: does X beat costs under Y?",
 "SPY", base=True, variant_of=None,
 lambda df, r: run_my_logic(df, r)),

fn(df, risk_R) → trades DataFrame with columns [entry_date, exit_date, gross_ret, risk_R]. The id seeds the RNG, so the same id always produces the same verdict. Then: add a unit test → run the suite → run the engine → commit.

Iteration discipline: never mutate a failed hypothesis — propose one concrete change as a new hypothesis. Never stack filters to dodge a drawdown; that is overfitting — re-size or redesign instead. Max 2 parameters optimized, wide steps; consistency across top rows beats max net profit.


🔬 Strategy Lab — mine research papers with a local LLM

A desktop app on top of the same gauntlet. It reads the papers Wall Street actually cites, converts their documented claims into testable hypotheses with a local LLM (no cloud, no API keys), and lets the gauntlet judge the output.

uv run python run_lab.py                              # dashboard → http://127.0.0.1:8517
uv run python run_lab.py --headless --paper mop-2012-tsmom --n 2
uv run python run_lab.py --list-papers
Paper library 10 curated papers — Lou/Polk/Skouras overnight tug-of-war, Moskowitz/Ooi/Pedersen time-series momentum, George/Hwang 52-week high, Jegadeesh/Titman momentum, Asness value & momentum everywhere, and more. Two honest tiers: open-access (arXiv full text — the LLM reads the actual paper) and abstract-tier (paywalled classics — the documented claim is embedded, never invented)
Local model Qwen2.5-14B-Instruct Q4_K_M (~9GB VRAM — fits a 12GB GPU) via llama.cpp. Download once: huggingface.co/bartowski/Qwen2.5-14B-Instruct-GGUF → models/ (gitignored)
Safety rail The LLM can only choose among 6 whitelisted rule types with clamped parameters — it proposes, it never executes. The gauntlet remains the only judge
Artifacts Every run persisted to artifacts/lab/<run_id>/run.json + per-hypothesis 00-index.md

First live runs on the developer's RTX 5070: the LLM read the TSMOM claim and proposed a 12-month momentum rule (FAIL: 20 trades < 100 — the gauntlet said no); it read the 52-week-high claim and proposed a 50-day Donchian breakout with a 20-day-low exit (FAIL: sample). The pipeline works precisely because it refuses to flatter the papers.


Doctrine hard rules

  1. No live trading, ever — the human is the only executor of steps 8–9.
  2. A FAIL is progress — record reasons, move on.
  3. Filters must earn their place against the base case, or they are decoration.
  4. Re-size or redesign — never optimize your way around a drawdown.

Built by an autonomous agent under human command. The human holds the only keys to steps 8–9.

⚖️ Educational research — not financial advice. No warranties, expressed or implied.

About

Institutional-grade quantitative strategy finder. The 9-step protocol as code: bootstrap EV, Monte Carlo, IS/OOS gauntlet. Educational research only.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages