A QuantConnect equities algorithm lab built risk-first. Every strategy is
pinned to the local stage: pytest, black-swan replays, and backtests only.
Before any universe, indicator, or order code loads, the config must clear a
pre-flight risk gauntlet with hardcoded caps. The point is not to maximize
returns; it is to make unsafe configurations unrunnable.
- 🛑 Pre-flight risk gauntlet. Three hardcoded caps checked before anything else runs.
- 🏷️ Lifecycle stages. Promotion criteria live in YAML; a human flips the stage, never code.
- 🌪️ Black-swan replay. Committed 2008 and COVID fixtures for stress runs.
- 🎲 Monte Carlo drawdown. Stdlib-only, deterministic per seed.
- 📓 Veto ledger. Every blocked trade appended to a JSONL audit trail.
- 🧪 126 tests. Kelly sizing, price math, gauntlet contract, YAML schema.
flowchart LR
A[strategy.yaml] --> B[schema check]
B --> C[risk gauntlet]
C -->|caps hold| D[backtest]
C -->|cap violated| E[RiskGauntletError]
git clone https://github.com/wranngle/tradingbot.git
cd tradingbot
pip install pytest pydantic pyyaml- Run the suite:
python3 -m pytest -qprints126 passed. A committed QuantConnect stub means no account is needed. - Validate the strategy config:
python3 scripts/validate_strategy.pyprintsOK stage=local notional_cap=1000.0 version=0.1.0. - Watch the gauntlet work:
python3 demo_gauntlet.py unsaferejects,python3 demo_gauntlet.py safepasses. The same contract runs intests/test_risk_gauntlet.pyfor all three caps.
Every algorithm load is gated by a pre-flight check against three hardcoded caps. The caps are deliberately not configurable: the gauntlet is the boundary, not a preference.
| You try | The gauntlet says |
|---|---|
leverage: 3.0 |
leverage_cap violated (actual=3.0, allowed<=2.0) |
max_drawdown_pct: 0.31 |
max_drawdown_cap violated (allowed<=0.30) |
max_position_pct: 0.25 |
position_concentration_cap violated (allowed<=0.20) |
| default config | passes silently |
Violations raise RiskGauntletError before any order, indicator, or
universe code is touched. Unsafe configurations are not warned about. They
do not run.
What a rejection looks like
RiskGauntletError: risk gauntlet: leverage_cap violated (actual=3.0, allowed<=2.0)
Every strategy carries a stage in strategy.yaml. Promotion is a human
decision recorded in YAML, gated by written criteria, and it has never
happened: no strategy in this repo has been promoted past local.
| Stage | What runs there | Status |
|---|---|---|
local |
pytest, black-swan replay, backtests, notional cap 1000 | every strategy is here |
paper |
forward-testing on simulated fills, no money | defined, unreached |
live |
gated on paper promotion criteria | defined, unreached |
Standalone tools that run without a QuantConnect account:
python3 monte_carlo.py --seed 7 # drawdown distribution
python3 scripts/replay_blackswan.py # 2008 and COVID stress replay
python3 compare.py # two-strategy diff harness
python3 risk_digest.py # deterministic markdown risk digest
scripts/bin/vetoes --tail 5 # recent blocked tradesRunning the full algorithm requires a QuantConnect project: create one in
the dashboard, copy these Python files into its root, edit config.py for
dates, cash, and universe, then run the backtest.
| Next | Why |
|---|---|
| Kelly position-sizing test coverage | top gap named in the test suite |
| pytest wired into CI | the suite currently runs locally |
| single cap table | one duplicate cap copy still drifts |
MIT. See LICENSE.
