quantred 2.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. quantred/__init__.py +24 -0
  2. quantred/alphas/__init__.py +53 -0
  3. quantred/alphas/base.py +221 -0
  4. quantred/alphas/combine.py +260 -0
  5. quantred/alphas/horizon.py +153 -0
  6. quantred/alphas/refine.py +300 -0
  7. quantred/alphas/scorers.py +87 -0
  8. quantred/analytics/attribution.py +354 -0
  9. quantred/analytics/bootstrap.py +353 -0
  10. quantred/analytics/charts.py +393 -0
  11. quantred/analytics/excursion.py +185 -0
  12. quantred/analytics/execution_quality.py +180 -0
  13. quantred/analytics/exposure.py +167 -0
  14. quantred/analytics/htmlreport.py +1208 -0
  15. quantred/analytics/information.py +197 -0
  16. quantred/analytics/metrics.py +502 -0
  17. quantred/analytics/performance.py +639 -0
  18. quantred/analytics/reporting.py +1672 -0
  19. quantred/analytics/screening.py +190 -0
  20. quantred/analytics/series_comparison.py +303 -0
  21. quantred/analytics/trade_analytics.py +353 -0
  22. quantred/brokers/alpaca/broker.py +415 -0
  23. quantred/brokers/alpaca/factory.py +33 -0
  24. quantred/brokers/alpaca/market_data.py +166 -0
  25. quantred/brokers/base.py +220 -0
  26. quantred/brokers/dryrun.py +164 -0
  27. quantred/brokers/errors.py +70 -0
  28. quantred/cli.py +8619 -0
  29. quantred/costs/__init__.py +11 -0
  30. quantred/costs/base.py +126 -0
  31. quantred/costs/parametric.py +131 -0
  32. quantred/data/__init__.py +29 -0
  33. quantred/data/compute.py +332 -0
  34. quantred/data/edges.py +81 -0
  35. quantred/data/features.py +120 -0
  36. quantred/data/panel.py +61 -0
  37. quantred/data/scan.py +85 -0
  38. quantred/data/store.py +269 -0
  39. quantred/demo/__init__.py +22 -0
  40. quantred/demo/scanners.py +104 -0
  41. quantred/demo/strategies.py +125 -0
  42. quantred/engine/backtest.py +1206 -0
  43. quantred/engine/barcheck.py +228 -0
  44. quantred/engine/live.py +539 -0
  45. quantred/execution/decision.py +192 -0
  46. quantred/execution/flatten.py +302 -0
  47. quantred/execution/halt.py +165 -0
  48. quantred/execution/ledger.py +758 -0
  49. quantred/execution/live_trader.py +587 -0
  50. quantred/execution/order_id.py +83 -0
  51. quantred/execution/sizing.py +114 -0
  52. quantred/indicators/indicators.py +158 -0
  53. quantred/marketdata/base.py +62 -0
  54. quantred/marketdata/client.py +59 -0
  55. quantred/marketdata/session.py +112 -0
  56. quantred/marketdata/synthetic.py +74 -0
  57. quantred/marketdata/timeframe.py +125 -0
  58. quantred/mcp/__init__.py +6 -0
  59. quantred/mcp/selftest.py +139 -0
  60. quantred/mcp/server.py +2419 -0
  61. quantred/optimization/causality.py +607 -0
  62. quantred/optimization/config_store.py +300 -0
  63. quantred/optimization/optimizer.py +431 -0
  64. quantred/optimization/parallel.py +345 -0
  65. quantred/optimization/param_space.py +309 -0
  66. quantred/optimization/walk_forward.py +1254 -0
  67. quantred/portfolio/allocator.py +135 -0
  68. quantred/portfolio/benchmark.py +109 -0
  69. quantred/portfolio/optimizer.py +786 -0
  70. quantred/portfolio/policy.py +335 -0
  71. quantred/portfolio/posterior.py +208 -0
  72. quantred/research/__init__.py +30 -0
  73. quantred/research/agent.py +868 -0
  74. quantred/research/demo_proposals.py +222 -0
  75. quantred/research/llm.py +311 -0
  76. quantred/research/proposer.py +234 -0
  77. quantred/research/provider_errors.py +242 -0
  78. quantred/research/sandbox.py +340 -0
  79. quantred/risk/__init__.py +60 -0
  80. quantred/risk/base.py +218 -0
  81. quantred/risk/conditional.py +474 -0
  82. quantred/risk/exposures.py +88 -0
  83. quantred/risk/factor.py +119 -0
  84. quantred/risk/sample.py +89 -0
  85. quantred/risk/streaming.py +214 -0
  86. quantred/scanners/base.py +141 -0
  87. quantred/scanners/symbol_scanner.py +148 -0
  88. quantred/services/__init__.py +51 -0
  89. quantred/services/analysis.py +6082 -0
  90. quantred/services/audit.py +574 -0
  91. quantred/services/campaign.py +381 -0
  92. quantred/services/configs.py +282 -0
  93. quantred/services/data.py +172 -0
  94. quantred/services/dryrun.py +206 -0
  95. quantred/services/glossary.py +130 -0
  96. quantred/services/maintenance.py +314 -0
  97. quantred/services/registry.py +351 -0
  98. quantred/services/setup.py +1023 -0
  99. quantred/services/sizing.py +150 -0
  100. quantred/services/smallreal.py +450 -0
  101. quantred/settings.py +571 -0
  102. quantred/store/__init__.py +0 -0
  103. quantred/store/bars.py +660 -0
  104. quantred/store/trials.py +1804 -0
  105. quantred/strategies/base.py +643 -0
  106. quantred/strategies/signals.py +28 -0
  107. quantred/utils/errors.py +48 -0
  108. quantred/utils/extras.py +330 -0
  109. quantred/utils/logging_config.py +33 -0
  110. quantred/utils/numeric.py +46 -0
  111. quantred/utils/streaming.py +474 -0
  112. quantred/utils/timeutils.py +148 -0
  113. quantred-2.4.0.dist-info/METADATA +632 -0
  114. quantred-2.4.0.dist-info/RECORD +117 -0
  115. quantred-2.4.0.dist-info/WHEEL +4 -0
  116. quantred-2.4.0.dist-info/entry_points.txt +8 -0
  117. quantred-2.4.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,632 @@
1
+ Metadata-Version: 2.5
2
+ Name: quantred
3
+ Version: 2.4.0
4
+ Summary: A layered, broker-agnostic algorithmic-trading research engine that is honest about how hard making money is.
5
+ Project-URL: Homepage, https://tradeflow.mk-dir.com/
6
+ Project-URL: Documentation, https://tradeflow.mk-dir.com/
7
+ Project-URL: Source, https://github.com/makedirectory/quantred
8
+ Project-URL: Issues, https://github.com/makedirectory/quantred/issues
9
+ Project-URL: Changelog, https://tradeflow.mk-dir.com/changelog
10
+ Author-email: Andrew Schwartz <andrew@mk-dir.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: alpaca,backtesting,mcp,quantitative-finance,trading,walk-forward
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Financial and Insurance Industry
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Office/Business :: Financial :: Investment
25
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
26
+ Requires-Python: >=3.10
27
+ Requires-Dist: alpaca-py>=0.35
28
+ Requires-Dist: numpy>=1.26
29
+ Requires-Dist: pandas>=2.0
30
+ Requires-Dist: pytz>=2024.1
31
+ Provides-Extra: ai
32
+ Requires-Dist: anthropic>=0.40; extra == 'ai'
33
+ Provides-Extra: dev
34
+ Requires-Dist: duckdb>=1.0; extra == 'dev'
35
+ Requires-Dist: hatchling>=1.21; extra == 'dev'
36
+ Requires-Dist: matplotlib>=3.8; extra == 'dev'
37
+ Requires-Dist: mcp<2,>=1.22; extra == 'dev'
38
+ Requires-Dist: ortools>=9.8; extra == 'dev'
39
+ Requires-Dist: polars>=1.25; extra == 'dev'
40
+ Requires-Dist: pyarrow>=14; extra == 'dev'
41
+ Requires-Dist: pytest>=8.0; extra == 'dev'
42
+ Requires-Dist: ruff>=0.6; extra == 'dev'
43
+ Requires-Dist: scikit-learn>=1.4; extra == 'dev'
44
+ Provides-Extra: mcp
45
+ Requires-Dist: mcp<2,>=1.22; extra == 'mcp'
46
+ Provides-Extra: openai
47
+ Requires-Dist: openai>=1.40; extra == 'openai'
48
+ Provides-Extra: optimize
49
+ Requires-Dist: scikit-learn>=1.4; extra == 'optimize'
50
+ Provides-Extra: portfolio
51
+ Requires-Dist: ortools>=9.8; extra == 'portfolio'
52
+ Provides-Extra: store
53
+ Requires-Dist: duckdb>=1.0; extra == 'store'
54
+ Requires-Dist: polars>=1.25; extra == 'store'
55
+ Requires-Dist: pyarrow>=14; extra == 'store'
56
+ Provides-Extra: viz
57
+ Requires-Dist: matplotlib>=3.8; extra == 'viz'
58
+ Description-Content-Type: text/markdown
59
+
60
+ # QuantRed
61
+
62
+ [![CI](https://github.com/makedirectory/quantred/actions/workflows/ci.yml/badge.svg)](https://github.com/makedirectory/quantred/actions/workflows/ci.yml)
63
+ [![Docs](https://img.shields.io/badge/docs-tradeflow.mk--dir.com-blue)](https://tradeflow.mk-dir.com/)
64
+
65
+ A small, **layered**, **broker-agnostic** algorithmic-trading **research
66
+ engine** — and an honest one, which mostly means it's very good at telling you
67
+ your brilliant strategy is actually noise. It scans markets, tests strategies,
68
+ validates them out-of-sample, allocates portfolios, and exposes the whole
69
+ research surface to AI agents **without ever handing them order-execution
70
+ authority**. It ships with an [Alpaca](https://alpaca.markets) adapter, but
71
+ everything above the broker layer is vendor-neutral.
72
+ It scans a universe of symbols, runs a strategy over them, and either **backtests**
73
+ on history or **trades live** (paper by default) — with optional **parameter
74
+ optimization**, **walk-forward validation**, and **constraint-solver portfolio
75
+ allocation**.
76
+
77
+ Full docs — usage guide, engineering wiki, and
78
+ [changelog](https://tradeflow.mk-dir.com/changelog) — live at
79
+ **[tradeflow.mk-dir.com](https://tradeflow.mk-dir.com/)**.
80
+
81
+ > Making money in markets is genuinely hard. This project won't change that — but
82
+ > it will at least stop you from fooling yourself quite so quickly, which is most
83
+ > of the battle.
84
+
85
+ Designed to be easy to try and easy to read:
86
+
87
+ - **No TA-Lib / no native build step** — indicators are pure pandas/numpy, so
88
+ `uv sync` is all you need and the Docker image carries no compiler toolchain.
89
+ - **Broker-agnostic** — everything is written against a `Broker` /
90
+ `MarketDataProvider` interface. Alpaca is the first implementation; dropping in
91
+ another venue means writing one adapter, nothing else.
92
+ - **Strict separation of concerns** — each layer does one job (see below).
93
+
94
+ > ⚠️ Educational software. Trading is risky; use paper trading. No warranty.
95
+
96
+ ![QuantRed demo — an in-sample equity curve that looks tradeable, beside the walk-forward verdict that refuses to promote it](https://raw.githubusercontent.com/makedirectory/quantred/main/docs/static/img/demo.png)
97
+
98
+ *`make demo` runs the whole pipeline on synthetic data (no keys, no network) and
99
+ renders this: a strategy that looks profitable in-sample, and the out-of-sample
100
+ verdict that calls it noise. The refusal is the product. The same panels render
101
+ for any run — pass `--chart PATH` to `backtest` or `walkforward`.*
102
+
103
+ ## The mental model: two clocks
104
+
105
+ The one idea that explains everything else — QuantRed runs on **two clocks that
106
+ never touch**:
107
+
108
+ - **Research clock** (offline, slow, exploratory): backtest → optimize →
109
+ walk-forward, plus the optional AI agent. Non-determinism and LLMs are allowed
110
+ here. It only ever *proposes* — writing provenance-stamped configs to disk.
111
+ - **Trade clock** (live, deterministic, LLM-free): `live bar → signal → order`.
112
+ No model sits in the order path, so there's nothing to prompt-inject and nothing
113
+ non-deterministic to debug when real money is at stake.
114
+
115
+ **Promotion is a manual human step** — automation never flips `PAPER_TRADE` or
116
+ places an order. The [MCP server](#agent-integration-mcp) enforces this
117
+ *structurally*: it builds only a data client, so it physically cannot trade. See
118
+ the [architecture docs](https://tradeflow.mk-dir.com/engineering/architecture)
119
+ for the full picture.
120
+
121
+ ## Requirements
122
+
123
+ You need **either** of these — not both:
124
+
125
+ - **[uv](https://docs.astral.sh/uv/getting-started/installation/)** — the Python
126
+ package manager used to run everything locally, **or**
127
+ - **[Docker](https://docs.docker.com/get-docker/)** — to build and run the app in
128
+ a container (no local Python or uv needed).
129
+
130
+ The `Makefile` targets run through **uv** (e.g. `make backtest` → `uv run …`), and
131
+ there are separate **Docker** targets (`make docker-build`, `make docker-run`).
132
+ Pick whichever you prefer.
133
+
134
+ Either way you'll need free Alpaca **paper-trading** API keys from the
135
+ [Alpaca dashboard](https://app.alpaca.markets/) → *Paper Account → API Keys*.
136
+
137
+ ## Quickstart (install it)
138
+
139
+ No clone needed — install the command and run the offline demo:
140
+
141
+ ```bash
142
+ uv tool install 'quantred[mcp,viz]' # or: python -m pip install 'quantred[mcp,viz]'
143
+ quantred demo # full pipeline on synthetic data, no keys
144
+ quantred init # your free Alpaca paper keys, and optionally a model
145
+ # provider for `research` (skipping it is fine)
146
+ quantred init --check # what is ready, for which task, and the next step
147
+ quantred init --probe-model --provider anthropic --for research # optional: one tiny billed request
148
+ quantred verdict --scanner none --symbols AAPL,MSFT,NVDA,AMZN,GOOGL,META,AVGO,JPM,XOM,UNH,COST,WMT \
149
+ --start 2024-01-01 --end 2024-12-31
150
+ claude mcp add quantred -- quantred mcp # connect an MCP client; it launches the server
151
+ quantred mcp --self-test # confirm discovery works without a chat client
152
+ ```
153
+
154
+ Optional capabilities are extras (charts, the MCP server, the model SDKs). Adding one
155
+ later with `uv tool` replaces the environment, so list every extra you want — the
156
+ [extras guide](https://tradeflow.mk-dir.com/usage/extras) has the table and the exact
157
+ commands. `quantred init --check --for <task>` prints the one you need for that task, and a
158
+ command that needs a missing extra prints it with your current extras filled in.
159
+
160
+ **→ [Getting started](https://tradeflow.mk-dir.com/usage/getting-started)** walks the
161
+ whole path: install → keys → your first real result → Claude connected. Six steps,
162
+ the first two with no keys and no network.
163
+
164
+ The distribution and the command are both **`quantred`**; the importable package is
165
+ `quantred`; no `tradeflow` command is installed with it. If you installed the older
166
+ `tradeflow-engine`, its final release (2.3.1) is a transition package that depends on
167
+ `quantred`, so upgrading it moves you across without breaking old scripts, MCP configs or
168
+ packs importing `tradeflow` (the alias raises a `DeprecationWarning`, shown under `-W default` or in test runs). A private pack needs its imports
169
+ changed from `tradeflow` to `quantred` before you drop it. The steps are in
170
+ [migrating to QuantRed](https://tradeflow.mk-dir.com/usage/migrating-to-quantred).
171
+
172
+ State (the research journal, trial store, bar cache, promoted configs) lives in
173
+ `~/.tradeflow` — for an installed copy and for a checkout alike. `quantred --version`
174
+ prints which copy is running and where its state is; override with `TRADEFLOW_HOME`.
175
+
176
+ ## Quickstart (from a checkout)
177
+
178
+ ```bash
179
+ # 1. Install uv: https://docs.astral.sh/uv/
180
+ # 2. Install dependencies
181
+ make install # uv sync --inexact
182
+
183
+ # 3. See it work — no keys, no network
184
+ make demo # full pipeline on synthetic data + verdict
185
+
186
+ # 4. Point it at real data: guided setup for your free Alpaca paper keys
187
+ make init # writes .env, checks the keys, says what's next
188
+ # (make check re-runs the diagnostics any time)
189
+
190
+ # 5. Try it (preconfigured combos)
191
+ make scan # which symbols are flagged right now?
192
+ make backtest # scan -> demo strategy -> report
193
+ make live # paper-trade the scanned universe
194
+ ```
195
+
196
+ ## Docker (a local dev stack, not an easier install)
197
+
198
+ Docker is itself a prerequisite, so `uv tool install` above is the simpler way to
199
+ just try this. What compose adds is a long-running MCP server and docs site with
200
+ state on named volumes that survives container replacement:
201
+
202
+ ```bash
203
+ make up # MCP + persistent state. Never starts trading.
204
+ make down # stop; your trial history survives
205
+
206
+ docker compose run --rm demo # offline, no keys
207
+ docker compose run --rm verdict --scanner none \
208
+ --symbols AAPL,MSFT,NVDA,AMZN,GOOGL,META,AVGO,JPM,XOM,UNH,COST,WMT \
209
+ --start 2024-01-02 --end 2024-12-31
210
+ ```
211
+
212
+ `live` is profile-gated and is never started by `up` — turning the machine on must
213
+ not turn trading on. See [Running in Docker](https://tradeflow.mk-dir.com/usage/docker).
214
+
215
+ Run `make help` to see every target. Anything is overridable inline:
216
+
217
+ ```bash
218
+ make backtest SYMBOLS=AAPL,MSFT,NVDA START=2024-06-01 END=2024-09-01 CAPITAL=50000
219
+ ```
220
+
221
+ Or call the CLI directly:
222
+
223
+ ```bash
224
+ uv run python main.py backtest --strategy demo_trend --scanner demo_volume \
225
+ --symbols NVDA,META,TSLA --start 2024-01-02 --end 2024-04-01
226
+ ```
227
+
228
+ ## See it run
229
+
230
+ `make demo` runs the entire pipeline on a seeded random walk — no keys, no
231
+ network — and ends in a promotion verdict. The point isn't a winning strategy;
232
+ it's that the **refusal to promote a noise strategy is the product**:
233
+
234
+ ```text
235
+ $ make demo
236
+
237
+ ======================================================================
238
+ QuantRed demo — synthetic data, no API keys, no network
239
+ (a seeded random walk: realistic-looking, no actual edge)
240
+ ======================================================================
241
+
242
+ 1) In-sample backtest of every registered strategy
243
+ In-sample, almost anything looks tradeable. That's the trap.
244
+
245
+ STRATEGY RETURN SHARPE TRADES
246
+ ---------------------------------------
247
+ demo_trend 14.74% 0.48 45
248
+
249
+ 2) Walk-forward validation of 'demo_trend' (the honest scorecard)
250
+ Optimize in-sample, score out-of-sample across folds, then gate it.
251
+
252
+ OOS Sharpe (median): -0.27 efficiency (OOS/IS): -0.19 OOS trades: 25
253
+
254
+ Promotion gates:
255
+ [FAIL] oos_sharpe: -0.27 (threshold 1.20)
256
+ [FAIL] oos_profit_factor: 0.74 (threshold 1.30)
257
+ [FAIL] walk_forward_efficiency: -0.19 (threshold 0.40)
258
+ [PASS] oos_drawdown_vs_is: 10.59% (threshold 15.41%)
259
+ [FAIL] min_oos_trades: 25 (threshold 100)
260
+ [FAIL] deflated_sharpe: <0.001 (threshold 0.500)
261
+
262
+ Verdict: NOT promotable
263
+
264
+ No edge in a random walk → the gates refuse to promote it. That refusal
265
+ is the product. Point QuantRed at real data:
266
+ quantred init add your free Alpaca paper keys
267
+ quantred verdict then run the whole pipeline on real market data
268
+ ```
269
+
270
+ That is one strategy because a bare install ships one. Anything you install
271
+ alongside it appears in the same table, scored the same way.
272
+
273
+ Notice the arc: `demo_trend` looks tradeable in-sample (+14.7%, Sharpe 0.48), but
274
+ once it's optimized in-sample and scored **out-of-sample** the edge evaporates
275
+ (median OOS Sharpe −0.26) and all but one promotion gate fails. That's
276
+ [walk-forward validation](https://tradeflow.mk-dir.com/engineering/walk-forward)
277
+ doing its job.
278
+
279
+ ### Watching the agent get told "no"
280
+
281
+ `make demo` makes the point on synthetic data, where the refusal is easy — there
282
+ was never any edge to find. The harder and more useful question is what happens
283
+ when a proposal *does* look good. `make demo-agent` runs one full research
284
+ session on **real market data** and narrates every guardrail as it fires:
285
+
286
+ ```text
287
+ $ make demo-agent
288
+
289
+ ── Round 1 ────────────────────────────────────────────────────
290
+ Hypothesis Caching the processed universe to local disk lets the
291
+ strategy reuse warm state across sessions...
292
+ Sandbox REJECTED — generated code may not import 'os'
293
+ ↳ no bars loaded, no backtest run, no trial consumed
294
+
295
+ ── Round 2 ────────────────────────────────────────────────────
296
+ Sandbox REJECTED — MultiFactorConfluenceStrategy has 8 searchable params (cap 5)
297
+
298
+ ── Round 4 ────────────────────────────────────────────────────
299
+ Proposal [code] new strategy implementation
300
+ Sandbox ADMITTED — imports clean, contract valid, params within cap
301
+ Walk-forward in-sample Sharpe 2.04 → out-of-sample 1.39 (efficiency 0.76)
302
+ Multiple-testing correction applied over 37 trials
303
+ Promotion gates:
304
+ [PASS] oos_sharpe 1.39 threshold 1.00
305
+ [PASS] oos_profit_factor 1.92 threshold 1.30
306
+ [PASS] walk_forward_efficiency 0.76 threshold 0.40
307
+ [FAIL] oos_drawdown_vs_is 18.40% threshold 13.50%
308
+ [FAIL] min_oos_trades 52 threshold 100
309
+ [PASS] deflated_sharpe 0.580 threshold 0.500
310
+ Verdict NOT promotable — discarded
311
+ ```
312
+
313
+ That last round is the one worth dwelling on. The strategy is *not* noise: it
314
+ survives out-of-sample with a Sharpe of 1.39 and clears four of six gates. It is
315
+ refused anyway, because 52 out-of-sample trades is too small a sample to
316
+ distinguish skill from luck and its drawdown degraded relative to in-sample. A
317
+ research engine that only rejects obvious garbage is not doing anything for you;
318
+ the interesting behavior is rejecting things that look good.
319
+
320
+ By default the proposals are **replayed** from a fixed set, so the run is
321
+ deterministic and needs no LLM API key — only Alpaca market-data keys. Point it
322
+ at a live model to watch it improvise instead:
323
+
324
+ ```bash
325
+ make demo-agent # replayed proposals, deterministic
326
+ make demo-agent-live # live Claude proposer (needs ANTHROPIC_API_KEY)
327
+
328
+ uv run python main.py demo-agent --symbols NVDA,AAPL,META --provider ollama
329
+ ```
330
+
331
+ Real market data means real variance: the exact Sharpes and which gates fail will
332
+ differ with the universe and date range you point it at. The shape — proposals
333
+ rejected before evaluation, survivors gated on out-of-sample evidence, nothing
334
+ promoted automatically — is what stays constant.
335
+
336
+ ## What it does
337
+
338
+ | Command | What happens |
339
+ |---------|--------------|
340
+ | `demo` | Run the whole pipeline on **synthetic data** — no keys, no network — ending in an honest promotion verdict |
341
+ | `demo-agent` | Narrate one AI research session on **real market data**: proposal → sandbox → walk-forward → gates → holdout |
342
+ | `init` | Guided first-run setup: write a valid `.env` with hidden prompts, validate the keys against Alpaca via the data-only client, confirm paper trading, optionally warm the cache. `--check` is a doctor that writes nothing |
343
+ | `scan` | Run the universe scanner and print flagged symbols |
344
+ | `verdict` | The whole cross-sectional pipeline in one command — scan → alphas → portfolio → information over one universe, one window, one cost model — ending in one gate-derived verdict (read-only) |
345
+ | `backtest` | Scan → run a strategy over history → performance report |
346
+ | `live` | Scan → warm up indicators → stream bars → place paper/live orders. Bar-quality guards (staleness, spikes, inconsistent OHLC, out-of-order) veto bad bars — rejecting, never repairing — and a position ledger records intent vs. observed fills |
347
+ | `live --dry-run` | What this contract **would** try to do right now: the orders it would send, the caps that would bind, the signals it would skip. The broker has no order methods that work, so trading is a capability it does not have rather than a rule it was told. No credentials, no fills, nothing journaled |
348
+ | `small-real` | **Places real orders.** Trades a validated config at reduced capital to measure fills, slippage and fees — the caps scaled with the book rather than shrunk by hand, since a ceiling small enough to force fills biases the sample toward low-priced names. Mandatory preflight, paper by default, telemetry to its own ledger, and no trial journaled |
349
+ | `reconcile` | Check the position ledger against the broker's actual account state. Reports divergence; never corrects it (read-only) |
350
+ | `halt` / `resume` / `halts` | Stop opening new positions, and say so durably — a running engine sees it on the next bar. Blocks entries, never exits, so it can't trap the book |
351
+ | `flatten` | Emergency: halt, cancel every order, close every position. Goes straight to the broker, so it works when the engine is wedged |
352
+ | `backtest --causality` | Probe whether each decision could have been made when it was made: the execution clock, same-bar ranking, benchmark alignment, and the scanner's as-of clock. A **different class** from the leakage probe, which tests for future data and cannot see a one-bar look-ahead at all |
353
+ | `screen` | Sweep a parameter space cheaply to ask whether a family holds anything at all — one process, one data fetch, N evaluations, and **nothing journaled**. Leads with the distribution and what the best of N draws is worth under the null, because the best of N is the maximum of N draws; `--confirm` records exactly one point as a real trial |
354
+ | `optimize` | Search strategy parameters by backtest objective (grid / random / Bayesian); `--workers N` evaluates candidates in parallel — wall-clock only, same trials and same winner |
355
+ | `allocate` | Weight a portfolio: scalar-score sizing (OR-Tools), or `--objective utility` for mean-variance construction from alpha + Σ |
356
+ | `alphas` | Rank a universe by continuous alpha — a comparable, annualized residual-return forecast per name; `--combine` blends several signals, `--neutralize-factors` regresses out the risk model's factor exposures (read-only) |
357
+ | `risk` | Estimate the universe covariance Σ (Ledoit–Wolf shrinkage) and summarize its risk structure (read-only) |
358
+ | `info` | Information report: measure IC, breadth, and predicted-vs-realized IR — skill vs luck (read-only) |
359
+ | `--html PATH` | On `verdict`/`backtest`/`walkforward`/`info`: write a self-contained HTML report of the run — inline charts, zero external requests, provenance and honesty warnings first-class |
360
+ | `horizon` | Measure alpha decay / half-life; recommend rebalance cadence + current/lagged blend (read-only) |
361
+ | `walkforward` | Out-of-sample validation: optimize in-sample, score out-of-sample across folds, with a sacred holdout and promotion gates |
362
+ | `trials` | Browse the campaign's memory: `list` (filters, sorting, paging), `show` (one trial in full), `best` (a DSR-ranked leaderboard that always shows the family's `n_trials`) — read-only; `archive` retires a whole era by moving the journal and its index aside **together** (a store left beside a fresh journal keeps reporting a trial count for evidence that is gone); `mark-contaminated` quarantines a suspect subset with an appended event — history intact, reason on the record |
363
+ | `mcp` | Serve QuantRed over MCP so an agent (Claude Code / Desktop) can drive verdict/scan/backtest/optimize/walk-forward/alphas/risk/portfolio/info — read-only, no live trading |
364
+
365
+ One strategy ships, and it is a demonstration rather than a candidate:
366
+
367
+ - **`demo_trend`** — long-only EMA trend follower: the normalized fast−slow gap,
368
+ whose sign crossings are the golden / death cross (daily). Paired with the
369
+ `demo_volume` scanner.
370
+
371
+ It exists so `quantred demo` has something to run and so the interface has a
372
+ worked example, not because it is an edge — the demo above spends its whole output
373
+ showing that it is not. **The strategies you would actually trade go in your own
374
+ package**, installed beside the engine and discovered by entry point, which keeps
375
+ them out of this repository entirely.
376
+
377
+ Each strategy defines a single continuous **score** (its conviction); the trade
378
+ clock's `BUY/SELL/HOLD` and the
379
+ [continuous alpha](https://tradeflow.mk-dir.com/engineering/alphas) are both
380
+ derived from it — one source of truth.
381
+
382
+ `example` is a complete working pack to copy — `quantred init
383
+ --example-pack ./my-signals` writes it out. See
384
+ [Your own strategies](https://tradeflow.mk-dir.com/usage/private-strategies)
385
+ and [Extending](https://tradeflow.mk-dir.com/engineering/extending).
386
+
387
+ ### Using it from your own code
388
+
389
+ `quantred.services.*` is the supported surface — the same JSON-returning functions
390
+ the CLI renders and the MCP server exposes, so you get identical numbers by
391
+ construction:
392
+
393
+ ```python
394
+ from quantred.services.analysis import run_verdict
395
+ from quantred.services.data import build_data_client
396
+
397
+ result = run_verdict(build_data_client(), "demo_trend", ["NVDA", "AAPL"], start, end)
398
+ print(result["verdict"]["summary"])
399
+ ```
400
+
401
+ Everything outside `services/` is internal and moves without notice. See
402
+ [Using QuantRed as a library](https://tradeflow.mk-dir.com/engineering/embedding).
403
+
404
+ ### Optional features
405
+
406
+ Capabilities are optional extras so the base install stays lean:
407
+
408
+ Installed: see the [extras guide](https://tradeflow.mk-dir.com/usage/extras). In a
409
+ checkout:
410
+
411
+ ```bash
412
+ make install-optimize # scikit-learn, for `optimize --method bayesian`
413
+ make install-portfolio # Google OR-Tools, for `allocate`
414
+ uv sync --inexact --extra mcp # the MCP SDK, for `python main.py mcp`
415
+ ```
416
+
417
+ ## Project status
418
+
419
+ This is an evolving research project, not a production trading platform. To keep
420
+ that honest, here's what's load-bearing versus what's still maturing:
421
+
422
+ | Capability | Status |
423
+ |------------|--------|
424
+ | Broker / market-data abstractions | ✅ Stable |
425
+ | Offline backtesting + analytics | ✅ Stable |
426
+ | Pure pandas/numpy indicators | ✅ Stable |
427
+ | Universe scanning | ✅ Stable |
428
+ | Offline test suite (in-memory fakes) | ✅ Stable |
429
+ | Parameter optimization — grid / random | ✅ Stable |
430
+ | Walk-forward validation + promotion gates | 🧪 Experimental |
431
+ | Parameter optimization — Bayesian | 🧪 Experimental |
432
+ | Portfolio allocation (OR-Tools) | 🧪 Experimental |
433
+ | Live paper trading | 🧪 Experimental |
434
+ | Bar-quality guards + position reconciliation | 🧪 Experimental |
435
+ | MCP server | 🧪 Experimental |
436
+ | Research agent | 🧪 Experimental |
437
+
438
+ "Experimental" means the interfaces and gate thresholds may still change — not
439
+ that the code is untested. Everything ships with offline tests.
440
+
441
+ ## Agent integration (MCP)
442
+
443
+ > **AI-assisted research without AI-controlled trading.**
444
+
445
+ `quantred mcp` exposes QuantRed's deterministic capabilities to any MCP
446
+ client (Claude Code / Claude Desktop) as tools: discovery, `run_verdict`,
447
+ `run_scan`, `run_backtest`, `run_optimization`, `run_walk_forward`, the research
448
+ chain (`compute_alphas`, `construct_portfolio`, `compute_information`, ...),
449
+ `render_report`, read-only trial-store access (`list_trials`, `get_trial`,
450
+ `best_trials`), `get_metrics_glossary`, and `save_config`/`load_config`/
451
+ `list_configs`. Every call is appended to the audit log (`logs/mcp_audit.jsonl` under the state root) for replay.
452
+
453
+ **The safety model is structural.** The server constructs only a *data* client —
454
+ no trading client, no broker — so it is incapable of placing an order. There is
455
+ no `place_order`, `start_live`, `cancel`, or `set_paper_trade` tool; promoting a
456
+ config to live is a manual human step outside MCP. The capability simply isn't
457
+ wired in, so it can't be prompt-injected around. The agent works on the
458
+ *research clock* (offline, exploratory); the live order path stays LLM-free.
459
+
460
+ Register it with a client. The server needs the `mcp` extra, which the quickstart above
461
+ installs; on a copy that lacks it, `quantred init --check --for mcp` prints the one command
462
+ that adds it and keeps your other extras and packs:
463
+
464
+ ```bash
465
+ claude mcp add quantred -- quantred mcp # Claude Code
466
+ ```
467
+
468
+ ```json
469
+ { "mcpServers": { "quantred": { "command": "quantred", "args": ["mcp"] } } }
470
+ ```
471
+
472
+ A desktop client often has a smaller `PATH` than a terminal, so `quantred init` prints
473
+ this block with the absolute command for your copy, and `quantred mcp --self-test`
474
+ runs exactly that command (printing the block again when it passes). From a checkout it is the checkout's interpreter and the
475
+ absolute `main.py`:
476
+
477
+ ```json
478
+ { "mcpServers": { "quantred": {
479
+ "command": "/path/to/quantred/.venv/bin/python",
480
+ "args": ["/path/to/quantred/main.py", "mcp"] } } }
481
+ ```
482
+
483
+ An installed copy and a checkout share one state root, `~/.tradeflow`, unless
484
+ `TRADEFLOW_HOME` is set, so an agent and you see the same campaign history. If you set
485
+ `TRADEFLOW_HOME`, the config block printed by `init` carries it too; a desktop client does
486
+ not inherit your shell's exports.
487
+
488
+ ## Research agent (optional)
489
+
490
+ `quantred research` runs a bounded, offline loop that proposes hypotheses,
491
+ validates them out-of-sample with walk-forward, and writes a shortlist of
492
+ provenance-stamped candidate configs to `configs/` for a human to review. It never
493
+ promotes anything to live trading.
494
+
495
+ The proposer is **provider-agnostic** — choose with `--provider`:
496
+
497
+ | Provider | Install | Default model | Credential |
498
+ |----------|---------|---------------|------------|
499
+ | `anthropic` (default) | extra `ai` | `claude-opus-4-8` | `ANTHROPIC_API_KEY` |
500
+ | `openai` | extra `openai` | `gpt-4o` | `OPENAI_API_KEY` |
501
+ | `ollama` (local) | none | `llama3.1` | none |
502
+
503
+ `quantred init --check --for research --provider anthropic` prints the exact command that
504
+ adds the extra to your copy. Set the credential in `.env` (alongside your Alpaca keys; `quantred init`
505
+ writes it) or as the standard environment variable. Ollama runs locally and needs no key.
506
+
507
+ ```bash
508
+ quantred research --provider ollama --model llama3.1 \
509
+ --symbols NVDA,AAPL --start 2024-01-01 --end 2025-12-31 \
510
+ --goal "improve OOS Sharpe without raising max drawdown" --holdout-days 60
511
+ ```
512
+
513
+ See the [docs](https://tradeflow.mk-dir.com/) (Usage → *AI agents*) for the full
514
+ tool surface, guardrails, and provider setup.
515
+
516
+ *Contributors, from a checkout:* the same commands run as `python main.py mcp` and
517
+ `python main.py research`, and extras are added with `uv sync --inexact --extra ai`
518
+ (`--inexact` keeps the dev tools and any editable pack).
519
+
520
+ ## Architecture
521
+
522
+ The codebase is organized into single-responsibility layers. Nothing above the
523
+ broker layer imports a vendor SDK. The two clocks never touch — automation only
524
+ ever proposes a config; a human promotes it:
525
+
526
+ ```mermaid
527
+ flowchart LR
528
+ subgraph research["Research clock — offline · LLM-allowed"]
529
+ direction TB
530
+ H[hypothesis] --> B[backtest] --> O[optimize] --> W[walk-forward]
531
+ W --> C[(provenance-stamped<br/>config)]
532
+ end
533
+ subgraph trade["Trade clock — live · deterministic · LLM-free"]
534
+ direction TB
535
+ Bar[live bar] --> Sig[signal] --> Ord[broker order]
536
+ end
537
+ C -. "human promotes<br/>(nothing auto-flips)" .-> Bar
538
+ ```
539
+
540
+ The layers themselves:
541
+
542
+ ```
543
+ brokers/ Broker interface + domain types ── alpaca/ (AlpacaBroker, AlpacaMarketData)
544
+ marketdata/ MarketDataProvider interface, Timeframe, MarketDataClient
545
+ indicators/ Pure pandas/numpy technical indicators
546
+ strategies/ Strategy base + signals (score -> BUY/SELL/HOLD, sizing, risk)
547
+ scanners/ ScannerStrategy base + SymbolScanner (universe resolution)
548
+ demo/ The one strategy + scanner a bare install ships, for `quantred demo`
549
+ execution/ LiveTrader (signals -> broker orders)
550
+ analytics/ Performance metrics + reporting
551
+ engine/ BacktestEngine + LiveEngine (orchestration only)
552
+ optimization/ ParameterSpace + ParameterOptimizer (tune params via backtest)
553
+ portfolio/ PortfolioAllocator (OR-Tools MIP position weighting)
554
+ utils/ logging, numeric, time helpers
555
+ ```
556
+
557
+ Data flows the same way in both modes:
558
+
559
+ ```
560
+ marketdata → strategy.process_data → strategy.generate_signals
561
+ → engine (simulate fills | route to execution) → analytics
562
+ ```
563
+
564
+ To work on them locally:
565
+
566
+ ```bash
567
+ make docs # serve the Docusaurus site at http://localhost:3000
568
+ ```
569
+
570
+ ## Docker
571
+
572
+ ```bash
573
+ make docker-build
574
+ make docker-run # paper live-trading; mounts your .env
575
+ ```
576
+
577
+ ## Tests
578
+
579
+ ```bash
580
+ make test # offline suite — no API keys or network required
581
+ ```
582
+
583
+ The whole stack is testable offline because every layer depends on the
584
+ broker/data abstractions; tests inject in-memory fakes.
585
+
586
+ Lint and format with ruff (what CI runs):
587
+
588
+ ```bash
589
+ uv run ruff check . # lint
590
+ uv run ruff format --check . # format
591
+ ```
592
+
593
+ ## Contributing
594
+
595
+ **Contributions and ideas are very welcome** — whether it's a bug fix, a new
596
+ strategy or scanner, an additional broker/data adapter, an LLM provider, docs, or
597
+ just a feature request or suggestion. If you have an idea, open an issue to start
598
+ the conversation; if you have a fix, open a PR. No contribution is too small, and
599
+ feedback on what would make QuantRed more useful is always appreciated.
600
+
601
+ And hey — don't be greedy: share your algos, let's make money together. 📈 (Worst
602
+ case, we lose money together, which is basically friendship.)
603
+
604
+ See **[CONTRIBUTING.md](CONTRIBUTING.md)** for setup, the pre-push checks, and the
605
+ coding standards (layering rules, separation of concerns, no vendor SDK above the
606
+ broker layer). CI runs ruff lint + format + the test suite on every PR.
607
+
608
+ ## Stopping, and account utilities
609
+
610
+ `Ctrl-C` stops the process but records nothing — restart the engine and it trades
611
+ again. To stop in a way that sticks:
612
+
613
+ ```bash
614
+ make halt REASON="feed looks wrong" # refuse new entries; exits still allowed
615
+ make halts # what is currently halted
616
+ make resume # lift it
617
+ make flatten REASON="why" # halt, cancel everything, close everything
618
+ ```
619
+
620
+ Halt state is a file under the state root, not a database — the order path holds no
621
+ connection to anything that can be down, so the switch still works when the engine
622
+ does not. See [Stopping trading](https://tradeflow.mk-dir.com/usage/stopping).
623
+
624
+ ## ☕ Coffee?
625
+
626
+ If this code somehow makes you money, I'd genuinely love to hear about it. If it
627
+ *loses* you money — we've never met, and this is the first you're hearing of it.
628
+
629
+ Either way, if it saved you some time, you can buy me a coffee:
630
+
631
+ **[buy me a coffee →](https://venmo.com/u/Andrew-Schwartz-92)**
632
+