Metadata-Version: 2.5
Name: sophostechne-quant
Version: 1.2.0
Summary: Evolve, test and optimise trading strategies.
Project-URL: Repository, https://github.com/sophostechne/quant
Project-URL: Issues, https://github.com/sophostechne/quant/issues
Author: Sophos Techne
License-Expression: MIT
License-File: LICENSE
Keywords: backtesting,evolutionary,quantitative,strategy,trading
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: <3.15,>=3.12
Requires-Dist: nautilus-trader>=1.230
Requires-Dist: pandas>=2.0
Description-Content-Type: text/markdown

# quant

Evolve, test and optimise trading strategies.

The engine behind the [Quant Workbench](https://github.com/sophostechne/quant-vsce) extension,
usable on its own from the command line. Everything it reports is JSON on stdout, with
diagnostics on stderr, so it composes with whatever you already run.

## Install

```sh
pip install sophostechne-quant
```

The distribution is `sophostechne-quant`; the import package and the command are both `quant`.
It pulls in [NautilusTrader](https://github.com/nautechsystems/nautilus_trader) and pandas, so
the install is not small.

## The idea

A backtest that reports only a return is a sales pitch. Three questions have to be answered
together, and `evaluate` always answers all three — there is no flag to omit the awkward two:

- **Did it make money?** Net return, drawdown, profit factor, win rate, time in drawdown.
- **Was the drawdown real?** A Monte Carlo resample of the strategy's own trades, so the
  observed drawdown can be placed in the distribution of drawdowns it could have had.
- **Was any of it better than chance?** A noise floor of random strategies on the same bars.
  A strategy that does not beat its noise floor did not find an edge, whatever it returned.

## Use

```sh
quant evaluate --strategy my.strategy --product BTC-USD --timeframe 6h --bars 2000
```

Searching emits NDJSON — one object per generation, then a `done` object carrying the
survivors. Every survivor is scored twice: on the bars it was fitted to, and on a held-out tail
the search never saw. The gap between the two is the entire result.

```sh
quant evolve --population 250 --generations 20 --survivors 5 --holdout 0.3
```

Walk-forward tests the *method* rather than any one strategy. The whole search re-runs in every
training window and its winner is applied, untouched, to the window after.

```sh
quant walkforward --train-bars 1200 --test-bars 400
```

Costs are charged on every path: `--fee` (default 0.001) and `--slippage` (default 0.0005).
`--seed` makes a run reproducible, and `--regimes` conditions the search on market regime.

### Where the bars come from

Symbols route by shape. An exchange pair — `BTC-USD`, `ETH-USD`, anything ending in a quote
currency — comes from Coinbase, which serves candles to anyone. A ticker like `AAPL` or `BF-B`
comes from a published bars service:

```sh
quant evolve --product AAPL --timeframe 1d
```

That works with no configuration: `QUANT_BARS_URL` defaults to `https://bars.sophostechne.com`.
Point it at your own service to use different data, or set it empty to refuse equities.

Equities need a service because every real-time equity feed is licensed per subscriber and
cannot be redistributed; a service built from IEX HIST captures can be, so the engine needs no
credentials of its own. With `QUANT_BARS_URL` set empty, a ticker fails with a message saying so
rather than silently doing something else. See
[quant-daemon](https://github.com/sophostechne/quant-daemon) for running one.

A timeframe the service does not publish is built by resampling a finer one, which is exact.
Buckets that no trade fell into stay absent rather than being carried forward — a gap is
information, and filling it puts bars into a backtest that nothing supports.

Everything is cached under `~/.cache/quant`, so a search that re-reads the same window across
folds hits the network once.

## Output

One JSON object per line, always with an `ok` field on terminal results. Failures are reported
as JSON too, so a caller parsing the stream never has to strip a traceback out of it:

```json
{"ok": false, "type": "error", "error": "FileNotFoundError: my.strategy"}
```

## Development

```sh
uv sync
uv run pytest
```

## License

MIT
