Skip to main content
Glama
README.md
# Backtrader MCP

**English** | [**中文**](#-中文文档)

Backtrader MCP is an independent, local-first MCP server for building and
running reproducible Backtrader strategies. It turns confined CSV files into
immutable datasets, typed strategy intent into private drafts, and reviewed
drafts into bounded subprocess runs with durable status and reports.

P0 is deliberately offline and backtest-only. It does not expose brokers,
stores, credentials, live orders, arbitrary Python execution, or network
transports.

Full documentation (English / 中文):
<https://cloudquant.github.io/backtrader-mcp/> (also buildable on Read the
Docs; sources under `docs/`).

## Distribution contract

- Python 3.10 or newer.
- MCP Python SDK `>=2.0.0,<2.1` (validated at `2.0.0`), using `MCPServer` and
  local stdio.
- Independent wheel/source distribution; the only accepted Backtrader runtime is
  [`cloudQuant/backtrader`](https://github.com/cloudQuant/backtrader), pinned in
  package metadata to commit `3c967ed61be184c0099ba5bef55d4bed09ad0b4a`.
- SQLite/WAL state, content-addressed CSV data, private draft files, HMAC
  capabilities, filesystem locks, idempotency records, and startup recovery.
- Product-owned `prepare_strategy_run`, `start_strategy_run`,
  `get_run_status`, `cancel_strategy_run`, and `get_run_result` tools. MCP SDK
  v2.0.0 does not provide the Tasks extension, so this product does not claim
  it.
- Observability tools: `list_jobs` (state-filtered job enumeration),
  `get_run_logs` (bounded, path-sanitized job log tails), and
  `list_target_tree` (read-only target preimages for exact change reviews);
  `get_run_status` reports `log_uri`, `elapsed_seconds`, and `eta_bound`.
- All 30 tools carry readOnlyHint/destructiveHint/idempotentHint/openWorldHint
  annotations. Tool errors cross the MCP boundary as structured
  `[code] message` text with an optional `Suggestion:` next step; absolute
  filesystem paths in error text and job logs are redacted.

The wheel includes seven JSON Schema contracts under
`backtrader_mcp/schemas/` and the deterministic comparison policy under
`backtrader_mcp/policies/`. It also includes its own immutable full metadata
snapshot: 1,155 unique records covering 1,152 functional tests, 1,035
three-file strategy packages, and 1,032 verified mappings. The fourteen
current-fork template entries (seven archetypes by two output profiles) remain
separate from those corpus records.

## Install without changing the base environment

From this directory, create and activate a dedicated virtual environment, then
install the package. `python` may be any supported Python 3.10+ interpreter:

```bash
python -m venv .runtime
. .runtime/bin/activate
python -m pip install -c constraints/requirements-v2.txt .
python -m backtrader_mcp --help
```

The package dependency installs that pinned CloudQuant source. For a source
checkout or an environment where the dependency was skipped, run the explicit
installer instead:

```bash
backtrader-mcp install-backtrader | python -m json.tool
```

It installs only when Backtrader is absent. If another Backtrader distribution
is already installed, it leaves that distribution untouched and returns the
machine-readable `installed_backtrader_untrusted` warning.

Register only absolute, trusted roots in the host environment:

```text
BACKTRADER_MCP_STATE_ROOT=/absolute/private/state
BACKTRADER_MCP_SOURCE_ROOTS={"market_data":"/absolute/read-only/csv","functional_corpus":"/absolute/read-only/tests/functional/strategies","package_corpus":"/absolute/read-only/strategies"}
BACKTRADER_MCP_TARGET_ROOTS={"strategies":"/absolute/generated/strategies"}
BACKTRADER_MCP_RUNTIMES={"default":"/absolute/cloudquant-backtrader"}
```

Root maps are JSON objects. MCP callers receive only root IDs and relative
paths; they cannot submit absolute paths or executable paths. A runtime root
must contain `backtrader/__init__.py` and have Git `origin` resolving to
`github.com/cloudquant/backtrader`; another fork or the public PyPI package is
rejected before a strategy run. If `BACKTRADER_MCP_RUNTIMES` is omitted, a
verified installed CloudQuant distribution is registered as `default`.

Before adding a host, export the same values in the installation shell and run
the read-only diagnostic. Quoting the JSON values prevents the shell from
interpreting them:

```bash
export BACKTRADER_MCP_STATE_ROOT='/absolute/private/state'
export BACKTRADER_MCP_SOURCE_ROOTS='{"market_data":"/absolute/read-only/csv"}'
export BACKTRADER_MCP_TARGET_ROOTS='{"strategies":"/absolute/generated/strategies"}'
export BACKTRADER_MCP_RUNTIMES='{"default":"/absolute/cloudquant-backtrader"}'
backtrader-mcp doctor | python -m json.tool
```

`doctor.status` must be `passed`. The report is stable JSON and includes the
installed product and dependency versions, installed-Backtrader provenance,
configured root checks, supported adapters/run profiles, and the actual
Backtrader `module_file`, version, Git commit, branch, provenance, and runtime
capabilities. An existing non-CloudQuant installed package is reported as a
warning; a configured non-CloudQuant runtime is an error. The CLI diagnostic
itself does not create the state root or write to a source/target root; normal
MCP server startup initializes its private state root before tools are
available.

## Catalog modes

`get_catalog_snapshot` returns the slim header by default: counts, hashes,
provenance, and `extensions.entry_count`, without the 1,155-record entry list.
Set `include_entries=true` to page through entries with `limit` (1-100) and
`offset`; the response reports `pagination.total`/`has_more`/`truncated`.
Every record has `source_available=false`: search and provenance are
available, but `inspect_strategy` does not pretend that the original source
bytes were shipped. `list_strategy_templates` (tool and resource) independently
returns all 14 current-fork archetype/profile templates.
`search_strategy_catalog` reports `total`/`has_more`/`offset` pagination
metadata and actionable empty-result suggestions.

For an explicit source-attached rebuild, register the functional and package
corpora as two read-only IDs in `BACKTRADER_MCP_SOURCE_ROOTS`, then call:

```json
{
  "tool": "refresh_strategy_catalog",
  "arguments": {
    "source_root_id": "functional_corpus",
    "package_root_id": "package_corpus"
  }
}
```

The server scans metadata and hashes only; it never imports, executes, or
modifies a corpus file. The result reports fresh
`functional_tests`/`strategy_packages`/`mapped` counts, a content hash, and a
diagnostic if they differ from the verified 1,152/1,035/1,032 baseline.
Source-attached records use `source_available=true`; subsequent
`inspect_strategy` detects changed functional or package bytes. Supplying only
`source_root_id` preserves the smaller AST-only refresh for a registered
strategy target root.

## Host setup

Replace every `/ABSOLUTE/PATH` placeholder in the matching file.

### Claude Desktop / Claude Code

Copy `examples/hosts/claude-desktop.json` into the host's MCP configuration,
or replace every placeholder and run this complete Claude Code command:

```bash
claude mcp add-json --scope project backtrader '{
  "type": "stdio",
  "command": "/ABSOLUTE/PATH/backtrader-mcp/.runtime/bin/backtrader-mcp",
  "args": ["serve"],
  "env": {
    "BACKTRADER_MCP_STATE_ROOT": "/ABSOLUTE/PATH/.backtrader-mcp-state",
    "BACKTRADER_MCP_SOURCE_ROOTS": "{\"market_data\":\"/ABSOLUTE/PATH/data\"}",
    "BACKTRADER_MCP_TARGET_ROOTS": "{\"strategies\":\"/ABSOLUTE/PATH/generated-strategies\"}",
    "BACKTRADER_MCP_RUNTIMES": "{\"default\":\"/ABSOLUTE/PATH/cloudquant-backtrader\"}"
  }
}'
claude mcp list
```

Restart Claude Desktop after editing its JSON. Claude Code can verify the
project-scoped server with `claude mcp list` and its interactive `/mcp` view.

### Codex

Merge `examples/hosts/codex-config.toml` into `~/.codex/config.toml` or a
trusted project's `.codex/config.toml`, then restart the Codex client. The
Codex app, Codex CLI, and Codex IDE extension share this configuration.
Codex's own `approval_policy` governs the host, but it does not replace either
of this product's trusted local approval records.

The equivalent CLI registration is:

```bash
codex mcp add \
  --env BACKTRADER_MCP_STATE_ROOT=/ABSOLUTE/PATH/.backtrader-mcp-state \
  --env 'BACKTRADER_MCP_SOURCE_ROOTS={"market_data":"/ABSOLUTE/PATH/data"}' \
  --env 'BACKTRADER_MCP_TARGET_ROOTS={"strategies":"/ABSOLUTE/PATH/generated-strategies"}' \
  --env 'BACKTRADER_MCP_RUNTIMES={"default":"/ABSOLUTE/PATH/cloudquant-backtrader"}' \
  backtrader -- /ABSOLUTE/PATH/backtrader-mcp/.runtime/bin/backtrader-mcp serve
codex mcp list --json
```

### OpenCode

Merge `examples/hosts/opencode.json` into the global or project OpenCode
configuration. The current configuration places each named local server
directly below `mcp`; the command is an argument vector and `enabled` is true.
Run `opencode mcp list` and require the `backtrader` server to be connected
before starting a strategy request.

### OpenClaw

Edit and run `examples/hosts/openclaw-add.sh`, then keep the successful
`openclaw mcp doctor backtrader --probe` output as setup evidence.

### First host verification

All four adapters start the same stdio server. A successful connection performs
MCP `initialize`; the host then discovers `tools/list`, `resources/list`, and
`prompts/list`. Use the host's MCP view/logs to confirm those discovery calls,
then submit this non-mutating first request:

```text
Use only the backtrader MCP server. Call doctor, then call
get_catalog_snapshot. Return doctor.status, the default runtime's module_file,
version and commit, plus snapshot.extensions.entry_count. Do not create a
draft, write a target, or start a run.
```

Expected evidence is `doctor.status=passed`, a `module_file` below the
registered runtime, the expected Backtrader version/commit, and catalog
`entry_count=1155`. Use these host-specific discovery checks:

| Host | Registration check | Interactive discovery |
| --- | --- | --- |
| Claude Code | `claude mcp list` | `/mcp` shows `backtrader`, then run the first request |
| Codex | `codex mcp list --json` | Start/restart Codex, inspect its MCP tools, then run the first request |
| OpenCode | `opencode mcp list` | Require `backtrader` connected, then run the first request |
| OpenClaw | `openclaw mcp doctor backtrader --probe` | Inspect the workspace MCP tools, then run the first request |

For raw protocol evidence independent of host UI wording, the isolated v2
protocol test performs `initialize`, `tools/list`, `resources/list`,
`prompts/list`, and a typed `get_catalog_snapshot` call.

Host configuration references:
[Claude MCP](https://code.claude.com/docs/en/mcp),
[Codex MCP](https://learn.chatgpt.com/docs/extend/mcp),
[OpenCode MCP](https://opencode.ai/docs/mcp-servers/), and
[OpenClaw MCP](https://docs.openclaw.ai/cli/mcp).

## Upgrade and uninstall

For a compatible `0.2.x` upgrade, stop every connected host, back up the
private state root, activate the dedicated environment, and reinstall:

```bash
. .runtime/bin/activate
python -m pip install --upgrade -c constraints/requirements-v2.txt .
backtrader-mcp doctor | python -m json.tool
```

Restart the host and repeat its registration check and first request. Do not
reuse draft validation tokens, change/run tokens, or approvals across an
incompatible release. This product does not migrate pre-P0 state.

To uninstall, first remove the `backtrader` MCP registration from each host
(or delete only its matching configuration entry), stop active runs, then:

```bash
. .runtime/bin/activate
python -m pip uninstall backtrader-mcp
```

Uninstalling the wheel intentionally leaves the configured state, datasets,
generated strategies, and source files untouched. Archive or remove those
paths separately only after reviewing their contents. If `.runtime` was
dedicated solely to this product, it can be removed with the platform's file
manager after deactivation.

## Closed-loop workflow

1. `inspect_dataset` reads headers and a bounded sample from a configured
   source root.
2. `register_dataset` requires an explicit canonical column map and writes a
   normalized immutable CSV to the CAS. Registration fails if the source
   changes while read.
3. `preview_dataset` reads a bounded CAS preview.
4. `derive_tabular_dataset` runs only `identity`, `dropna`, `returns`, or
   `sma` with typed parameters and an exact source-manifest hash. It creates a
   new dataset ID; no DataFrame, callable, pickle, or in-memory object crosses
   the protocol. `returns`/`sma` drop their warmup rows and register the
   derived column as a `pandas_custom_lines` feature line, so a derived
   dataset can feed `precomputed_ml` strategies directly.
5. `search_strategy_catalog` selects one of seven archetypes.
6. `create_strategy_draft` renders either `single_test` or `python_bundle`.
   All seven archetypes support both profiles. The spec may declare an
   allowlisted analyzer set (`extensions.analyzers`: sqn/calmar/vwr/
   timereturn) whose typed metrics flow into the result's `extra_metrics`,
   and an optional canonical `seed` that freezes into the run manifest and
   seeds the candidate's random/numpy state for reproducible strategies.
7. `update_strategy_draft` requires the current revision and file hash.
8. `validate_strategy_draft` parses and compiles AST without importing the
   candidate in the server. It classifies direct Strategy classes separately
   from cooperative Indicator/LineIterator/Observer/Analyzer objects. A direct
   Strategy does not have a global `super().__init__()` requirement; a custom
   cooperative line object does.
9. Read the current target tree with `list_target_tree` (relative path to
   sha256), then `prepare_strategy_changes` with the validation token, exact
   target preimage hashes, and an idempotency key. It returns a signed change
   token and a complete create/replace/delete review.
10. Review the change, then run the printed command locally:

    ```bash
    backtrader-mcp approve \
      --change-set CHANGE_ID \
      --change-token 'SIGNED_TOKEN' \
      --yes
    ```

    The approval record is created in the private local database. There is no
    MCP tool for approval and no `approved=true` parameter.

11. `apply_strategy_changes` requires that approval ID, the signed change
    token, and a new idempotency key. It rechecks draft and target hashes,
    stages the complete managed directory, and uses a journaled rename
    transaction.
12. `prepare_strategy_run` requires a fresh validation token, immutable
    dataset ID, registered runtime ID, timeout, one of the fixed run profiles
    (`runonce`, `runnext`, `runonce_runnext_compare`, `fixed_tests`, or
    `parameter_sweep`), and an idempotency key. `parameter_sweep` freezes a
    typed `param_grid` (StrategySpec parameter names to value lists, at most
    64 combinations) under the same single approval and ranks the
    per-combination results by `return_rate`. It freezes the exact draft, artifact, validation, dataset,
    runtime, profile, and timeout hashes and returns a signed run token.
13. Review those frozen inputs, then create a separate execution approval
    locally:

    ```bash
    backtrader-mcp approve \
      --run-plan RUN_PLAN_ID \
      --run-token 'SIGNED_RUN_TOKEN' \
      --yes
    ```

    Change approvals and run approvals have different subject types and cannot
    be reused for one another.

14. `start_strategy_run` accepts only that run plan ID, signed run token,
    execution approval ID, and a new idempotency key. Poll `get_run_status`
    every 2-5 seconds until a terminal state (it reports `log_uri`,
    `elapsed_seconds`, and `eta_bound`); optionally call `cancel_strategy_run`;
    read the normalized JSON and Markdown report with `get_run_result`. Use
    `list_jobs` to recover job IDs across sessions. On
    FAILED/TIMED_OUT/ORPHANED, read the bounded sanitized tails with
    `get_run_logs` before changing the strategy.

Job states are `QUEUED`, `RUNNING`, `CANCEL_REQUESTED`, `CANCELLED`,
`SUCCEEDED`, `FAILED`, `TIMED_OUT`, and `ORPHANED`. Every transition is a
compare-and-swap write with one arbitration rule: **a terminal state, once
persisted, is never overwritten, and a visible `CANCEL_REQUESTED` suppresses
`SUCCEEDED`/`FAILED`/`TIMED_OUT`**. Cancelling a job that already finished
returns `already_terminal` instead of touching it.

A server-owned watchdog (started only by `serve`, never by CLI commands)
consumes the worker heartbeat, enforces the wall-clock deadline with a grace
period, orphans jobs whose worker died, and cleans up detached candidate
process groups. Jobs report a structured `error_kind`
(`user_strategy`/`resource_limit`/`timeout`/`validation`/`infrastructure`/
`cancelled`/`orphaned`) so clients can distinguish a strategy bug from a
resource cap or a supervision decision.

The concurrency cap rejects instead of queueing: `start_strategy_run` fails
with an actionable suggestion when `max_concurrent_jobs` is reached.
Retention: `backtrader-mcp clean --kind
jobs|cas|drafts|approvals|nonces --before YYYY-MM-DD` removes finished job
records, unreferenced CAS objects, unreferenced drafts, consumed/expired
approvals, and consumed token nonces respectively. Dataset
registration streams row-by-row (bounded memory), deduplicates identical
sources without re-parsing, and catalog refresh reuses an (mtime,size)
fingerprint cache.

Successful results contain exactly eleven canonical metrics:
`bar_num`, `buy_count`, `sell_count`, `win_count`, `loss_count`, `trade_num`,
`final_value`, `sharpe_ratio`, `annual_return`, `max_drawdown`, and
`return_rate`. `sharpe_ratio` and `annual_return` are nullable. The bundled
`comparison-profile-v1` defines deterministic integer equality and floating
point tolerances for run comparison.

## Execution semantics

- Default sizer is `bt.sizers.FixedSize(stake=1)`: `self.buy()` without a size
  trades exactly one unit. Templates demonstrate explicit sizing where it
  matters (order_risk uses a risk fraction).
- Commission is a fixed percentage applied to both sides
  (`cerebro.broker.setcommission(percabs=True)`).
- There is no cheat-on-close; market orders fill at the next bar's open.
- `SharpeRatio` assumes riskfree rate 0.01 and population standard
  deviation; the annualization factor follows the data timeframe (252/52/12).
- `max_drawdown` is reported as a positive percent.
- The yahoo adapter stores raw close prices (`adjclose=False`); no
  adjustment metadata is applied.
- The CloudQuant fork resamples with `bar2edge=True` by default, which
  differs from upstream backtrader.
- `parameter_sweep` runs each grid combination once (runonce) with the
  params passed through `cerebro.addstrategy(..., **override)`; one approval
  covers the whole frozen grid (at most 64 combinations).

## Typed data adapters and bar operations

`register_local_dataset` accepts six independent typed adapters:
`generic_csv`, `backtrader_csv`, `yahoo_csv`, `mt5_csv`, `pandas`, and
`pandas_custom_lines`. Every source is parsed and normalized into an immutable
canonical CSV object before execution. The controlled worker then constructs
the named Backtrader adapter for each feed; it does not silently route every
format through `GenericCSVData`.

Pandas inputs must use `source_type=materialized_dataframe` and reference a
confined `.csv` file. Pickles, arbitrary Python objects, and caller-supplied
constructors are rejected. `pandas_custom_lines` also requires every custom
line to be declared in both `lines` and `columns`. MT5 feeds reject
sub-minute timeframes (the adapter would otherwise silently truncate
precision), and `alignment.mode` accepts only `intersection`.

Registration enforces a data-quality gate: non-positive OHLC prices and
inconsistent bars (high below low, high below max(open,close), low above
min(open,close)) are rejected with row-numbered errors. Markets where zero or
negative prices are legitimate can opt out per feed with
`adapter_options.allow_non_positive_prices=true`; OHLC consistency is always
enforced.

Each feed may declare a typed `extensions.bar_operation`:

```json
{"mode": "direct"}
```

or:

```json
{"mode": "resample", "timeframe": "minutes", "compression": 5}
```

`mode` may also be `replay`. Resample and replay are applied with
`Cerebro.resampledata` and `Cerebro.replaydata`, respectively. Successful
fixed-test results include per-mode `feed_runtime` evidence with the requested
format, actual adapter class, bar operation, source row count, and output bar
count.

## Security model

- Stdio writes protocol frames only to stdout. Candidate stdout/stderr are
  redirected to per-job private log files.
- Source, target, draft, CAS, and job paths are confined. Symlinks and parent
  traversal are rejected at caller-controlled boundaries.
- Validation and change tokens use a random 256-bit local secret, random
  nonces, expirations, and HMAC-SHA256 over canonical hash bindings.
- Apply authorization comes only from the trusted local CLI record.
- Target application replaces the entire managed strategy directory. Callers
  must provide the exact hash of every pre-existing file, including files that
  will be deleted.
- Candidate code is never imported by the MCP process. A worker launches it
  with a fixed interpreter, fixed entrypoint, minimal environment, separate
  process group, timeout, captured output, and validated result contract.
- Every run manifest fingerprints the runtime's git HEAD commit, the
  runtime's version-file hash, and the resolved pandas/numpy versions
  (best-effort: a pip-installed runtime without a git checkout records a
  null commit).

Process control uses a POSIX session and resource-limit pre-exec hook on
POSIX, while non-POSIX startup omits those options, uses a Windows process
group when available, and preserves only the required `SystemRoot` launch
variable. The automated suite exercises both branch contracts, but a real
Windows fourteen-cell host run has not yet been recorded.

Static AST policy and a subprocess are not an OS sandbox. Reviewed candidate
code still runs with the local user's filesystem permissions. P0 is intended
for trusted local strategy development; run it in a container or restricted
OS account for hostile code. SQLite state is single-host, and the journaled
directory swap is crash-recoverable but not a multi-host distributed
transaction. Cancellation is process-based, not an MCP Tasks capability.
Watchdog cleanup records PIDs without process start-time binding; on a
long-lived host a reused PID could in theory be signalled, and the heartbeat
staleness check is the primary defence.

**Approval host assumption.** Change and run approvals are created only by the
trusted local CLI, but the human-vs-agent separation holds only while the host
does not grant the agent local command execution: an agent with shell access
could run the printed `approve` command itself. Every approval record and its
audit entry carry the OS identity of the local approver; for stronger
separation, gate the `approve` CLI behind sudo/another OS account or an
approval daemon outside the agent's reach. Signed tokens now carry one-time
nonces consumed at the authorization landing point (apply/start), and
replayed, expired, or clock-skewed tokens are rejected. On Windows the lock
layer falls back to `msvcrt` byte-range locking, but a real Windows host run
has still not been recorded.

## Related resources

The CloudQuant Backtrader ecosystem:

- [`cloudQuant/backtrader`](https://github.com/cloudQuant/backtrader) — the
  pinned Backtrader runtime fork this product executes (commit
  `3c967ed61be184c0099ba5bef55d4bed09ad0b4a`).
- [`cloudQuant/backtrader-mcp`](https://github.com/cloudQuant/backtrader-mcp) —
  this MCP server.
- [`cloudQuant/backtrader-skills`](https://github.com/cloudQuant/backtrader-skills) —
  companion skills for Backtrader workflows.
- [`cloudQuant/backtrader_web`](https://github.com/cloudQuant/backtrader_web) —
  companion web product.
- [`cloudQuant/backtrader-agent`](https://github.com/cloudQuant/backtrader-agent) —
  companion agent product.
- [`cloudQuant/fincore`](https://github.com/cloudQuant/fincore) — FinCore,
  companion financial infrastructure.

## Development and acceptance

Run all commands from this directory:

```bash
python -m pip install -e ".[test]"
PYTHONPATH=src python -m pytest -q
ruff check src tests scripts
ruff format --check src tests scripts
PYTHONPATH=src python -m mypy src/backtrader_mcp
# With the four BACKTRADER_MCP_* root variables from the install section:
PYTHONPATH=src python -m backtrader_mcp doctor
PYTHONPATH=src python -m backtrader_mcp audit-independence
python scripts/run_acceptance.py --matrix all \
  --require-no-skills --require-no-agent
```

The project dependency pins `cloudQuant/backtrader` at commit
`3c967ed61be184c0099ba5bef55d4bed09ad0b4a`; no public PyPI Backtrader fallback
is accepted. Test runtime resolution is explicit
`BACKTRADER_MCP_TEST_RUNTIME_ROOT`, then a sibling checkout, then the installed
package. Every candidate is provenance-checked against CloudQuant; an invalid
or untrusted explicit override fails closed. Ruff is the only formatter and
mypy is a required quality gate. The current branch-coverage gate is 80%; its
exact configured value is the release criterion.

Protocol tests install `mcp==2.0.0` only into a temporary target directory.
They must not upgrade or remove the user's base-environment `mcp==1.20.0`.
The fixed acceptance entrypoint consumes a structured 14-cell artifact rather
than inferring success from pytest progress dots. It first builds a temporary
wheel, installs the wheel's `[test]` dependency closure under the repository
constraints into a clean temporary target, and runs pytest from a separate
directory outside this source checkout. That target must contain the pinned
CloudQuant Backtrader distribution and a matching direct-URL provenance record,
so it does not borrow the active environment's product dependencies.
`backtrader_mcp` is imported only from the installed wheel target.

The matrix executes all seven archetypes with both output profiles as real
runonce/runnext child-process backtests, covers all six adapters plus
resample/replay, and records inspect/register/preview, draft/validate,
prepare/apply, run, and compare evidence. Its JSON output also records the
wheel SHA-256, installed module origin,
`source_checkout_on_sys_path=false`, sibling-product absence, and the
independence audit. Callers cannot supply an arbitrary pytest target.
The wheel acceptance additionally verifies the exact full-snapshot SHA-256 and
imports/searches it from a clean temporary site directory outside this
repository, with no sibling AI product on `PYTHONPATH`.

---

# 📖 中文文档

[**English**](#backtrader-mcp) | **中文**

---

Backtrader MCP 是一个独立、本地优先的 MCP 服务器,用于构建和运行可复现的
Backtrader 策略。它把受限的 CSV 文件转换为不可变数据集,把 typed 策略意图转换为
私有草稿,把经过审查的草稿转换为带超时边界的子进程运行,并持久化运行状态与报告。

P0 版本刻意设计为离线、仅回测。它不暴露 broker、store、凭证、实盘订单、任意
Python 执行或网络传输。

完整文档(English / 中文):
<https://cloudquant.github.io/backtrader-mcp/>(亦可在 Read the Docs 构建;
源文件位于 `docs/`)。

## 分发契约

- Python 3.10 及以上。
- MCP Python SDK `>=2.0.0,<2.1`(以 `2.0.0` 验证),使用 `MCPServer` 与本地
  stdio。
- 独立的 wheel / 源码分发;唯一可接受的 Backtrader 运行时是
  [`cloudQuant/backtrader`](https://github.com/cloudQuant/backtrader),包元数据固定到
  commit `3c967ed61be184c0099ba5bef55d4bed09ad0b4a`。
- SQLite/WAL 状态、内容寻址的 CSV 数据、私有草稿文件、HMAC 能力令牌、文件系统
  锁、幂等性记录以及启动恢复。
- 产品自有的 `prepare_strategy_run`、`start_strategy_run`、
  `get_run_status`、`cancel_strategy_run` 和 `get_run_result` 工具。MCP SDK
  v2.0.0 不提供 Tasks 扩展,因此本产品也不声称支持。
- 可观测性工具:`list_jobs`(按状态过滤的作业枚举)、`get_run_logs`(有界、
  绝对路径脱敏的作业日志尾部)与 `list_target_tree`(只读目标树原像,用于
  精确变更评审);`get_run_status` 返回 `log_uri`、`elapsed_seconds` 和
  `eta_bound`。
- 全部 30 个工具都带有 readOnlyHint/destructiveHint/idempotentHint/
  openWorldHint 注解。工具错误以结构化 `[code] 消息` 文本跨越 MCP 边界,可选
  附带 `Suggestion:` 下一步建议;错误文本与作业日志中的绝对文件系统路径会被脱敏。

wheel 在 `backtrader_mcp/schemas/` 下包含七个 JSON Schema 契约,在
`backtrader_mcp/policies/` 下包含确定性比较策略。它还内置自己的不可变完整元数据
快照:1,155 条唯一记录,覆盖 1,152 个功能测试、1,035 个三文件策略包和 1,032 个
已验证映射。当前 fork 的十四条模板条目(七个 archetype × 两种输出 profile)与
这些语料记录分开存放。

## 不改动基础环境的安装

在本目录下创建并激活一个专用虚拟环境,然后安装本包。`python` 可以是任意受支持的
Python 3.10+ 解释器:

```bash
python -m venv .runtime
. .runtime/bin/activate
python -m pip install -c constraints/requirements-v2.txt .
python -m backtrader_mcp --help
```

包依赖会安装该固定的 CloudQuant 源码。若从源码检出运行或此前跳过了依赖安装,可改用
显式安装入口:

```bash
backtrader-mcp install-backtrader | python -m json.tool
```

它只会在 Backtrader 缺失时安装;若已存在其他 Backtrader 发行版,会保持原环境不变并
返回机器可读的 `installed_backtrader_untrusted` 警告。

只在宿主环境中注册绝对、可信的 root:

```text
BACKTRADER_MCP_STATE_ROOT=/absolute/private/state
BACKTRADER_MCP_SOURCE_ROOTS={"market_data":"/absolute/read-only/csv","functional_corpus":"/absolute/read-only/tests/functional/strategies","package_corpus":"/absolute/read-only/strategies"}
BACKTRADER_MCP_TARGET_ROOTS={"strategies":"/absolute/generated/strategies"}
BACKTRADER_MCP_RUNTIMES={"default":"/absolute/cloudquant-backtrader"}
```

Root 映射是 JSON 对象。MCP 调用方只能拿到 root ID 和相对路径,不能提交绝对路径或
可执行路径。运行时 root 必须包含 `backtrader/__init__.py`,且 Git `origin` 必须解析为
`github.com/cloudquant/backtrader`;其他 fork 或公开 PyPI 包会在启动策略前被拒绝。若未
设置 `BACKTRADER_MCP_RUNTIMES`,已验证的已安装 CloudQuant 分发会自动注册为
`default`。

新增宿主之前,先在安装 shell 中导出同样的值并运行只读诊断。给 JSON 值加引号可以
避免被 shell 解释:

```bash
export BACKTRADER_MCP_STATE_ROOT='/absolute/private/state'
export BACKTRADER_MCP_SOURCE_ROOTS='{"market_data":"/absolute/read-only/csv"}'
export BACKTRADER_MCP_TARGET_ROOTS='{"strategies":"/absolute/generated/strategies"}'
export BACKTRADER_MCP_RUNTIMES='{"default":"/absolute/cloudquant-backtrader"}'
backtrader-mcp doctor | python -m json.tool
```

`doctor.status` 必须为 `passed`。报告是稳定的 JSON,包含已安装产品及依赖版本、已安装
Backtrader 的溯源、已配置的 root 检查、支持的 adapter / run profile,以及实际的
Backtrader `module_file`、版本、Git commit、分支、溯源和运行时能力。已有的非
CloudQuant 已安装包会显示 warning;已配置的非 CloudQuant 运行时则是 error。CLI 诊断
本身不会创建 state root,也不会写入 source / target root;正常 MCP 服务器启动时才会
在工具可用之前初始化自己的私有 state root。

## Catalog 模式

`get_catalog_snapshot` 默认只返回精简 header:计数、哈希、溯源以及
`extensions.entry_count`,不含 1,155 条记录列表。设置 `include_entries=true` 可
以用 `limit`(1-100)与 `offset` 分页获取条目;响应会报告
`pagination.total`/`has_more`/`truncated`。全部记录的
`source_available=false`:搜索和溯源可用,但 `inspect_strategy` 不会假装原始源
码字节随包分发。`list_strategy_templates`(工具与资源两种形态)独立返回当前
fork 的全部 14 条 archetype / profile 模板。`search_strategy_catalog` 返回
`total`/`has_more`/`offset` 分页元数据以及可操作的空结果建议。

若要显式重建带源码的快照,把 functional 和 package 两个语料以两个只读 ID 注册到
`BACKTRADER_MCP_SOURCE_ROOTS`,然后调用:

```json
{
  "tool": "refresh_strategy_catalog",
  "arguments": {
    "source_root_id": "functional_corpus",
    "package_root_id": "package_corpus"
  }
}
```

服务器只扫描元数据和哈希,绝不导入、执行或修改任何语料文件。结果会报告最新的
`functional_tests`/`strategy_packages`/`mapped` 计数、内容哈希,以及与已验证的
1,152/1,035/1,032 基线不一致时的诊断信息。带源码的记录使用
`source_available=true`,后续 `inspect_strategy` 可检测 functional 或 package 字节
是否变化。只提供 `source_root_id` 时,则保留针对已注册策略 target root 的更小
AST-only 刷新。

## 宿主配置

请替换对应文件中每一个 `/ABSOLUTE/PATH` 占位符。

### Claude Desktop / Claude Code

把 `examples/hosts/claude-desktop.json` 复制进宿主的 MCP 配置,或替换全部占位符后
运行下面这条完整的 Claude Code 命令:

```bash
claude mcp add-json --scope project backtrader '{
  "type": "stdio",
  "command": "/ABSOLUTE/PATH/backtrader-mcp/.runtime/bin/backtrader-mcp",
  "args": ["serve"],
  "env": {
    "BACKTRADER_MCP_STATE_ROOT": "/ABSOLUTE/PATH/.backtrader-mcp-state",
    "BACKTRADER_MCP_SOURCE_ROOTS": "{\"market_data\":\"/ABSOLUTE/PATH/data\"}",
    "BACKTRADER_MCP_TARGET_ROOTS": "{\"strategies\":\"/ABSOLUTE/PATH/generated-strategies\"}",
    "BACKTRADER_MCP_RUNTIMES": "{\"default\":\"/ABSOLUTE/PATH/cloudquant-backtrader\"}"
  }
}'
claude mcp list
```

编辑 Claude Desktop 的 JSON 后需重启。Claude Code 可用 `claude mcp list` 及其交互
式 `/mcp` 视图验证项目级服务器。

### Codex

把 `examples/hosts/codex-config.toml` 合并到 `~/.codex/config.toml` 或可信项目的
`.codex/config.toml`,然后重启 Codex 客户端。Codex App、Codex CLI 和 Codex IDE
扩展共用这份配置。Codex 自身的 `approval_policy` 管理宿主,但不替代本产品的任一
可信本地审批记录。

等价的 CLI 注册命令:

```bash
codex mcp add \
  --env BACKTRADER_MCP_STATE_ROOT=/ABSOLUTE/PATH/.backtrader-mcp-state \
  --env 'BACKTRADER_MCP_SOURCE_ROOTS={"market_data":"/ABSOLUTE/PATH/data"}' \
  --env 'BACKTRADER_MCP_TARGET_ROOTS={"strategies":"/ABSOLUTE/PATH/generated-strategies"}' \
  --env 'BACKTRADER_MCP_RUNTIMES={"default":"/ABSOLUTE/PATH/cloudquant-backtrader"}' \
  backtrader -- /ABSOLUTE/PATH/backtrader-mcp/.runtime/bin/backtrader-mcp serve
codex mcp list --json
```

### OpenCode

把 `examples/hosts/opencode.json` 合并到全局或项目级 OpenCode 配置。当前配置把每个
具名的本地服务器直接放在 `mcp` 下;`command` 是参数向量,`enabled` 为 true。运行
`opencode mcp list`,并要求 `backtrader` 服务器在发起策略请求前已连接。

### OpenClaw

编辑并运行 `examples/hosts/openclaw-add.sh`,然后保留成功的
`openclaw mcp doctor backtrader --probe` 输出作为安装证据。

### 宿主首次验证

四个 adapter 启动的是同一个 stdio 服务器。连接成功会执行 MCP `initialize`,随后
宿主发现 `tools/list`、`resources/list` 和 `prompts/list`。用宿主的 MCP 视图 / 日
志确认这些发现调用,然后提交下面这个非变更的首个请求:

```text
Use only the backtrader MCP server. Call doctor, then call
get_catalog_snapshot. Return doctor.status, the default runtime's module_file,
version and commit, plus snapshot.extensions.entry_count. Do not create a
draft, write a target, or start a run.
```

预期证据是 `doctor.status=passed`、位于已注册运行时之下的 `module_file`、预期的
Backtrader 版本 / commit,以及 catalog `entry_count=1155`。各宿主的发现检查如下:

| 宿主 | 注册检查 | 交互式发现 |
| --- | --- | --- |
| Claude Code | `claude mcp list` | `/mcp` 显示 `backtrader`,然后运行首个请求 |
| Codex | `codex mcp list --json` | 启动 / 重启 Codex,查看其 MCP 工具,然后运行首个请求 |
| OpenCode | `opencode mcp list` | 要求 `backtrader` 已连接,然后运行首个请求 |
| OpenClaw | `openclaw mcp doctor backtrader --probe` | 查看工作区 MCP 工具,然后运行首个请求 |

如需独立于宿主 UI 措辞的原始协议证据,隔离的 v2 协议测试会执行 `initialize`、
`tools/list`、`resources/list`、`prompts/list` 和一次 typed
`get_catalog_snapshot` 调用。

宿主配置参考:
[Claude MCP](https://code.claude.com/docs/en/mcp)、
[Codex MCP](https://learn.chatgpt.com/docs/extend/mcp)、
[OpenCode MCP](https://opencode.ai/docs/mcp-servers/) 和
[OpenClaw MCP](https://docs.openclaw.ai/cli/mcp)。

## 升级与卸载

兼容的 `0.2.x` 升级:停止所有已连接宿主、备份私有 state root、激活专用环境并重装:

```bash
. .runtime/bin/activate
python -m pip install --upgrade -c constraints/requirements-v2.txt .
backtrader-mcp doctor | python -m json.tool
```

重启宿主并重复其注册检查和首个请求。不要跨不兼容版本复用草稿校验令牌、change/run
令牌或审批。本产品不迁移 pre-P0 state。

卸载时,先从每个宿主移除 `backtrader` MCP 注册(或只删除其对应配置项),停止活动
运行,然后:

```bash
. .runtime/bin/activate
python -m pip uninstall backtrader-mcp
```

卸载 wheel 时有意保留已配置的 state、数据集、生成的策略和源文件不动。请在审查内容
后再单独归档或删除这些路径。若 `.runtime` 仅专用于本产品,可在 deactivate 后用平
台文件管理器删除。

## 闭环工作流

1. `inspect_dataset` 从已配置的 source root 读取表头和有界样本。
2. `register_dataset` 需要显式的规范列映射,并把归一化的不可变 CSV 写入 CAS。读取
   期间源文件发生变化则注册失败。
3. `preview_dataset` 读取有界的 CAS 预览。
4. `derive_tabular_dataset` 只运行 `identity`、`dropna`、`returns` 或 `sma`,带
   typed 参数和精确的 source-manifest 哈希。它创建新的 dataset ID;任何
   DataFrame、callable、pickle 或内存对象都不会穿越协议。
5. `search_strategy_catalog` 在七个 archetype 中选择一个。
6. `create_strategy_draft` 渲染 `single_test` 或 `python_bundle`。七个 archetype
   都支持这两种 profile。
7. `update_strategy_draft` 需要当前 revision 和文件哈希。
8. `validate_strategy_draft` 解析并编译 AST,但不在服务器中导入候选项。它把直接
   Strategy 类与协作式 Indicator/LineIterator/Observer/Analyzer 对象分开判定。
   直接 Strategy 没有全局 `super().__init__()` 要求;自定义协作式 line 对象则需要。
9. `prepare_strategy_changes` 需要校验令牌、精确的 target 原像哈希和一个幂等键。它
   返回一个签名 change token 和完整的 create/replace/delete 审查。
10. 审查 change 后,在本地运行打印出的命令:

    ```bash
    backtrader-mcp approve \
      --change-set CHANGE_ID \
      --change-token 'SIGNED_TOKEN' \
      --yes
    ```

    审批记录写入私有本地数据库。没有用于审批的 MCP 工具,也没有 `approved=true`
    参数。

11. `apply_strategy_changes` 需要该审批 ID、签名 change token 和一个新的幂等键。它
    会重新检查草稿和 target 哈希,暂存完整的受管目录,并使用带日志的 rename 事务。
12. `prepare_strategy_run` 需要一个新的校验令牌、不可变 dataset ID、已注册的运行时
    ID、超时、固定 run profile 之一(`runonce`、`runnext`、
    `runonce_runnext_compare` 或 `fixed_tests`)以及一个幂等键。它冻结确切的草稿、
    artifact、校验、数据集、运行时、profile 和超时哈希,并返回签名 run token。
13. 审查这些冻结输入后,在本地单独创建一个执行审批:

    ```bash
    backtrader-mcp approve \
      --run-plan RUN_PLAN_ID \
      --run-token 'SIGNED_RUN_TOKEN' \
      --yes
    ```

    change 审批和 run 审批的 subject type 不同,不能互相复用。

14. `start_strategy_run` 只接受该 run plan ID、签名 run token、执行审批 ID 和一个新
    的幂等键。每 2-5 秒轮询一次 `get_run_status` 直到终态(响应含 `log_uri`、
    `elapsed_seconds` 与 `eta_bound`);可选调用 `cancel_strategy_run`;用
    `get_run_result` 读取归一化的 JSON 和 Markdown 报告。跨会话找回 job ID 用
    `list_jobs`。FAILED/TIMED_OUT/ORPHANED 时,先用 `get_run_logs` 读取有界脱敏
    日志尾部,再修改策略。

作业状态为 `QUEUED`、`RUNNING`、`CANCEL_REQUESTED`、`CANCELLED`、`SUCCEEDED`、
`FAILED`、`TIMED_OUT` 和 `ORPHANED`。每个迁移都是 compare-and-swap 写入,仲裁
规则唯一:**终态一旦持久化永不被覆写,可见的 `CANCEL_REQUESTED` 会抑制
`SUCCEEDED`/`FAILED`/`TIMED_OUT`**。取消已结束的作业会返回 `already_terminal`
而不是触碰它。

服务器自有的 watchdog(仅由 `serve` 启动,CLI 命令从不启动)消费 worker
心跳、以宽限期强制执行墙钟截止、把 worker 已死亡的作业判定为孤儿,并清理
脱离的候选进程组。作业报告结构化的 `error_kind`
(`user_strategy`/`resource_limit`/`timeout`/`validation`/`infrastructure`/
`cancelled`/`orphaned`),让客户端能区分策略 bug、资源封顶与监督决策。

并发上限是"拒绝而非排队":达到 `max_concurrent_jobs` 时
`start_strategy_run` 失败并附可操作建议。保留策略:`backtrader-mcp clean
--kind jobs|cas|drafts|approvals|nonces --before YYYY-MM-DD` 分别删除已结束的
作业记录、未被引用的 CAS 对象、未被引用的草稿、已消费或已过期的审批、已消费
的令牌 nonce。数据集注册逐行
流式处理(有界内存)、相同源免重解析去重,目录刷新复用 (mtime,size) 指纹缓存。

成功结果恰好包含 11 个规范指标:`bar_num`、`buy_count`、`sell_count`、
`win_count`、`loss_count`、`trade_num`、`final_value`、`sharpe_ratio`、
`annual_return`、`max_drawdown` 和 `return_rate`。`sharpe_ratio` 和
`annual_return` 可为空。内置的 `comparison-profile-v1` 定义了运行比较时确定性的整
数相等判定和浮点容差。

## 执行语义

- 默认 sizer 为 `bt.sizers.FixedSize(stake=1)`:不带 size 的 `self.buy()` 恰好
  成交 1 单位。模板在关键处演示显式 sizing(order_risk 使用风险比例)。
- 佣金为双边固定百分比(`cerebro.broker.setcommission(percabs=True)`)。
- 无 cheat-on-close;市价单在下一根 bar 的开盘价成交。
- `SharpeRatio` 假设无风险利率 0.01、总体标准差;年化因子随数据 timeframe
  (252/52/12)。
- `max_drawdown` 以正数百分比报告。
- yahoo adapter 存储未调整收盘价(`adjclose=False`),不应用调整元数据。
- CloudQuant fork 默认 `bar2edge=True` 重采样,与上游 backtrader 不同。
- `parameter_sweep` 每个网格组合以 runonce 执行一次,参数经
  `cerebro.addstrategy(..., **override)` 传入;一次审批覆盖整个冻结网格
  (最多 64 个组合)。

## Typed 数据 adapter 与 bar 操作

`register_local_dataset` 接受六个独立的 typed adapter:`generic_csv`、
`backtrader_csv`、`yahoo_csv`、`mt5_csv`、`pandas` 和 `pandas_custom_lines`。每个
源在执行前都被解析并归一化为不可变的规范 CSV 对象。受控 worker 随后为每个 feed 构
造具名的 Backtrader adapter,而不会把所有格式都悄悄走 `GenericCSVData`。

Pandas 输入必须使用 `source_type=materialized_dataframe` 并引用一个受限的 `.csv`
文件。pickle、任意 Python 对象和调用方提供的构造器都会被拒绝。`pandas_custom_lines`
还要求每条自定义 line 同时在 `lines` 和 `columns` 中声明。

每个 feed 可声明一个 typed `extensions.bar_operation`:

```json
{"mode": "direct"}
```

或:

```json
{"mode": "resample", "timeframe": "minutes", "compression": 5}
```

`mode` 也可为 `replay`。resample 和 replay 分别通过 `Cerebro.resampledata` 和
`Cerebro.replaydata` 应用。成功的 fixed-test 结果包含按 mode 记录的
`feed_runtime` 证据:请求格式、实际 adapter 类、bar 操作、源行数和输出 bar 数。

## 安全模型

- stdio 只把协议帧写入 stdout。候选项的 stdout/stderr 被重定向到按作业私有的日志
  文件。
- source、target、draft、CAS 和作业路径都被限定。符号链接和父目录穿越在调用方控制
  的边界处被拒绝。
- 校验和 change token 使用随机 256 位本地密钥、随机 nonce、过期时间,以及对规范哈
  希绑定的 HMAC-SHA256。
- apply 授权只来自可信的本地 CLI 记录。
- target 应用会替换整个受管策略目录。调用方必须提供每个既有文件的精确哈希,包括即
  将被删除的文件。
- 候选代码绝不被 MCP 进程导入。worker 用固定解释器、固定入口、最小环境、独立进程
  组、超时、捕获输出和已校验的结果契约来启动它。

进程控制在 POSIX 上使用独立 session 与 resource-limit pre-exec hook;在非 POSIX 上
不会传入这两个参数、可用时使用 Windows process group,并只保留启动所需的
`SystemRoot` 环境变量。自动化套件覆盖两类分支契约,但尚未记录真实 Windows 的十四格
host 运行结果。

静态 AST 策略加子进程并不是 OS 沙箱。经审查的候选代码仍以本地用户的文件系统权限运
行。P0 面向可信的本地策略开发;对恶意代码请在容器或受限 OS 账户中运行。SQLite 状态
是单主机的,带日志的目录交换可崩溃恢复,但不是多主机分布式事务。取消是基于进程的,
不是 MCP Tasks 能力。watchdog 清理只记录 PID 而不绑定进程启动时间;在长期运行的宿主
上,被复用的 PID 理论上可能被误发信号,心跳失速判定是主要防线。

**审批的宿主假设。** change/run 审批只由可信本地 CLI 创建,但"人机分离"
只在宿主不给 Agent 本地命令执行能力时成立:有 shell 权限的 Agent 可以自行
运行打印出来的 `approve` 命令。每条审批记录及其审计行都携带本地审批者的 OS
身份;需要更强隔离时,请把 `approve` CLI 置于 sudo/另一 OS 账户或 Agent 触达
范围之外的审批守护进程之后。签名令牌现在携带一次性 nonce,在授权落地点
(apply/start)原子消费;重放、过期或时钟偏移超窗的令牌一律被拒绝。Windows
上锁层回退到 `msvcrt` 字节范围锁,但真实 Windows 宿主运行仍未记录。

## 相关资源

CloudQuant Backtrader 生态:

- [`cloudQuant/backtrader`](https://github.com/cloudQuant/backtrader) —— 本产品
  执行的固定 Backtrader 运行时 fork(commit
  `3c967ed61be184c0099ba5bef55d4bed09ad0b4a`)。
- [`cloudQuant/backtrader-mcp`](https://github.com/cloudQuant/backtrader-mcp) ——
  本 MCP 服务器。
- [`cloudQuant/backtrader-skills`](https://github.com/cloudQuant/backtrader-skills) ——
  Backtrader 工作流的配套 skills。
- [`cloudQuant/backtrader_web`](https://github.com/cloudQuant/backtrader_web) ——
  配套 Web 产品。
- [`cloudQuant/backtrader-agent`](https://github.com/cloudQuant/backtrader-agent) ——
  配套 Agent 产品。
- [`cloudQuant/fincore`](https://github.com/cloudQuant/fincore) —— FinCore,配套
  金融基础设施。

## 开发与验收

所有命令在本目录下运行:

```bash
python -m pip install -e ".[test]"
PYTHONPATH=src python -m pytest -q
ruff check src tests scripts
ruff format --check src tests scripts
PYTHONPATH=src python -m mypy src/backtrader_mcp
# 配合安装章节中的四个 BACKTRADER_MCP_* root 变量:
PYTHONPATH=src python -m backtrader_mcp doctor
PYTHONPATH=src python -m backtrader_mcp audit-independence
python scripts/run_acceptance.py --matrix all \
  --require-no-skills --require-no-agent
```

项目依赖固定 `cloudQuant/backtrader` 的 commit
`3c967ed61be184c0099ba5bef55d4bed09ad0b4a`,不接受公开 PyPI Backtrader fallback。
测试运行时依次解析显式 `BACKTRADER_MCP_TEST_RUNTIME_ROOT`、相邻 checkout、已安装包;
每个候选都必须通过 CloudQuant 溯源校验,显式路径无效或不可信时会 fail closed。Ruff 是
唯一 formatter,mypy 是 required 质量门禁。当前分支覆盖率门槛为 80%,以配置中的精确值
作为发布标准。

协议测试只把 `mcp==2.0.0` 安装到一个临时目标目录,绝不升级或移除用户基础环境的
`mcp==1.20.0`。固定的验收入口消费一个结构化的 14 格 artifact,而不是从 pytest 进
度点推断成功。它先构建临时 wheel,再根据仓库 constraints 把 wheel 的 `[test]` 依赖
闭包安装到干净的临时目标,并从本源码检出之外的另一个目录运行 pytest。该 target 必须
包含固定的 CloudQuant Backtrader 分发及匹配的 direct-URL 溯源记录,因此不会借用活动
环境中的产品依赖;`backtrader_mcp` 本身只从已安装的 wheel target 导入。

矩阵把全部七个 archetype × 两种输出 profile 作为真实的 runonce/runnext 子进程回测
执行,覆盖全部六个 adapter 加 resample/replay,并记录 inspect/register/preview、
draft/validate、prepare/apply、run 和 compare 证据。其 JSON 输出还记录 wheel
SHA-256、已安装模块来源、`source_checkout_on_sys_path=false`、sibling 产品缺失以及
独立性审计。调用方不能提供任意 pytest 目标。wheel 验收还会验证完整快照的确切
SHA-256,并从本仓库之外的干净临时 site 目录导入 / 搜索它,且 `PYTHONPATH` 上没有
sibling AI 产品。