canli-backtest-mcp
v0.3.0
Published
MCP server for backtests that refuse lookahead: point-in-time factors from SEC filings, cross-sectional backtests of any signal on your own prices, costs on actual trades, and every variant counted in a trial ledger.
Maintainers
Readme
canli-backtest-mcp
Backtests an AI agent can trust. An MCP server that builds point-in-time factors from SEC filings, runs cross-sectional backtests of any signal on your own prices with no lookahead (enforced in code), charges costs on the actual trades, and records every variant in a trial ledger, so the best of a search is judged against the whole search instead of alone.
Local stdio only: your prices, signals and ledgers never leave your machine.
Why
Three mistakes make most agent-run backtests look better than they are:
- Lookahead. Using a number before it was public: an annual figure in January that was filed in
February, or a value that was later restated.
pit_factoronly uses what had been filed with the SEC by each date, as it stood then;backtest_signalonly trades on signal rows dated at leastlag_daystrading days earlier, and stops if anything reaches past that. - Free trading. Ignoring turnover. Positions drift with prices between rebalances, and costs are charged on the trades from those drifted positions to the new targets.
- Counting only the winner. An agent tries 200 variants and reports the best. With
ledger, every run is recorded (in canli-validation-mcp's hash-chained ledger), and each reply gives the best run's deflated Sharpe after counting all of them, and the Sharpe that luck alone reaches.
Tools
| tool | what it does |
|---|---|
| list_factors | The factors pit_factor builds and what each measures. |
| pit_factor | A factor (roa, gross_margin, operating_margin, revenue_growth, asset_growth, accruals, or level:<measure>) for your tickers, monthly, as known on each date. Writes a signal CSV. |
| backtest_signal | Rank on the signal, long the top quantile (and short the bottom), rebalance weekly, monthly, quarterly or every N days, with lag_days, cost_bps and an optional ledger. |
Breadth lab, sweeps and labelled data
| tool | what it does |
|---|---|
| price_panel | A price, dollar-volume and industry-group panel built from a fund's holdings (IVV for the S&P 500), ready for the tools below |
| breadth_signals | The 24 cross-sectional signals the lab builds sleeves from, each with its reference |
| breadth_book | Every signal inside every group of names as a sleeve, traded as one netted account: the book's Sharpe after costs, the netting saved, and its effective independent bets; writes the sleeves as a CSV with their specs |
| breadth_sweep | A grid of book settings run in parallel on every core, or as shards other machines run and merge back (inputs, grid, gaps and duplicates checked); the best Sharpe is set against what the best of that many trials reaches by luck, and with split_date the chosen settings are scored after it |
| effective_bets | How many independent bets a set of return series holds (the participation ratio of their correlations), by series and by family |
| annotation_packet, annotation_agreement, annotation_gold | Blind labelling packets of figures (or of a draft's figures) with guidelines and an answer key; agreement between labellers (Cohen's kappa); and a gold set with its datasheet |
Quick start
npx -y canli-backtest-mcpClaude Code:
claude mcp add canli-backtest -- npx -y canli-backtest-mcpThen ask: "Build roa for these 200 tickers from 2015 to 2024, backtest it monthly on prices.csv with 10 bps costs, try quantiles 0.1, 0.2 and 0.3 in the ledger 'roa-search', and tell me whether the best one survives the search."
Files
Both files are CSV with a date column (YYYY-MM-DD) and one column per ticker:
- prices: closing prices, one row per trading day. Bring your own; adjust for splits and dividends if you want total returns. The server does not supply prices (vendor licences forbid passing them on).
- signal: one row per date the values became known.
pit_factorwrites this; your own signal works too, as long as each row is dated by when it was available.
Empty cells are missing values, never zero.
What a result does not establish
- Prices are yours: survivorship, splits, dividends and data errors in them pass straight into the result.
- A backtest measures one path of history under these costs; it does not establish that the signal keeps working.
- Costs are a flat rate per unit traded; market impact, short-borrow fees and financing are not charged unless included in
cost_bps. - SEC values are what companies reported in XBRL, as filed; late filers, non-XBRL filers and tag changes can be missing.
Configuration
| variable | default | meaning |
|---|---|---|
| CANLI_LEDGER_DIR | ~/.canli/ledgers | where ledgers are kept (shared with canli-validation-mcp) |
| CANLI_CACHE_DIR | ~/.cache/canli-fundamentals | SEC data cache |
Part of the Canli Capital MCP family
canli-validation-mcp (validators and the trial ledger), canli-fundamentals-mcp (SEC fundamentals point in time), canli-research-mcp (the open research record). Source: github.com/arhancanli/canlicapital. MIT licensed. Built by Arhan Canli.
