# Model decisions in Python strategies

Backtrade can record a model decision during historical replay. This is optional: ordinary backtesting needs no model-provider or brokerage account.

Use Strategies → Strategy library → Model decisions. Save an Anthropic key in Provider settings; the browser seals it before sending it to Backtrade's private service. Adding a key does not permit spending. Fresh runs need a separate allowance tied to the saved setup and the account API key running the job. Backtrade never provides pooled inference credit.

Fresh provider admission is restricted to the explicitly enabled pilot. A disabled pilot does not silently substitute another provider. The supported model is Claude Haiku 4.5 (`claude-haiku-4-5-20251001`); this version uses text-only requests without provider tools or streaming.

```python
class Strategy:
    def on_start(self, ctx):
        answer = ctx.infer(
            system="Use only the supplied historical context. Reply BUY or HOLD.",
            messages=[{"role": "user", "content": "HOLD unless you have enough evidence."}]
        )
        ctx.log(answer["call"]["id"])
        if answer["text"].strip() == "BUY":
            ctx.submit_order(side="buy", qty=1)
```

The current downloaded runner is required. Python receives the text and a call receipt, never a provider key or Backtrade lease token. Failed requests raise `ModelDecisionError`, a `RuntimeError` subclass with `code` and `call` attributes. A strategy can explicitly hold on a failure. Unresolved or undelivered model calls cannot authorize orders in that callback.

Replay time pauses while the model responds. Actual service latency is recorded separately. This is a research clock, not a simulation of live-broker latency. Orders still require a subsequent executable market event to fill. Each date job starts with independent capital; this is not a continuous multi-day portfolio. `ctx.infer` is a Backtrade research extension; a live Alpaca/IBKR deployment needs its own provider adapter.

## Spending and failures

Before approval, review the experiment's aggregate attempt, token, time and estimated-dollar limits. Token counting is an estimate; the provider's own billing and account controls determine the invoice. A timeout may still have cost money. Backtrade reserves the attempt conservatively, records an unknown outcome and never automatically resends it. A late response remains undelivered. Actual usage above reservation stops further model calls across this experiment. An otherwise valid, on-time response is still delivered; you do not lose that paid decision solely because the token estimate was low.

A Backtrade server restart during an open model callback interrupts that job. The runner fails with `stale_frame`; any already-sent provider attempt remains accounted for and is never automatically resent. Create a new experiment to continue.

An allowance expires at its fixed UTC expiry, at most24 hours after approval. It cannot renew itself or approve a changed setup. Canceling or revoking prevents new calls but cannot undo a request already sent.

## Recorded reruns

Duplicate a model experiment to start in **Recorded** mode. It reuses explicitly linked source responses when code, effective prompt, model settings, data provenance, calendar window and context match. A miss stops with `cache_miss`; there is no paid fallback. Recorded reruns make zero new inference or token-count requests and are not independent model samples.

Results and the live agent view show model calls next to the existing trade/fill workflow. Open Request & response for the text, or Raw record/export for protocol detail. Server receipts attest what Backtrade transmitted/received, not hidden reasoning, historical truth of arbitrary supplied context, or profitability. Declared `research_setup` remains code-generation metadata and is never executed as a trading prompt.

Requests/responses are encrypted and normally retained30 days, within8 MiB/job and128 MiB/account body limits. Explicit body deletion frees that capacity and prevents further replay from those bodies. Immutable receipt metadata has a separate4 MiB/account pilot cap; it remains until its parent jobs/account are removed. A storage cap can stop admission before any provider request. Private text may contain sensitive user-supplied context: exports are explicit owner actions.

## API and MCP

- `POST /v1/inference/preview`: normalize a setup and obtain its approval digest without a model call.
- The signed-in browser approves an allowance. `GET /v1/inference/allowances` lists existing approvals; an ordinary simulation key cannot mint spending authority.
- `POST /v1/experiments`: submit the inference config and, for fresh mode, the matching `allowance_id`.
- `GET /v1/inference/evidence?job_id=...`: bounded server call receipts. Add `call_id=...` for one body's detail, or `export=1` for the bounded job export.
- MCP tools: `preview_strategy_inference`, `model_allowances`, `run_strategy`, `model_call_evidence`, `model_call_body`.

Runner-reported execution evidence and server model evidence have separate ID/sequence spaces. Use both exports when auditing a run. Missing evidence is not a match. Provider usage, consumed allowance, Messages attempts and token-count requests are distinct quantities.

## Enrollment verification

The enrollment UI displays the private service signing fingerprint. Compare it with the deployment record when available. The service signs the enrollment public key and one-use owner/session challenge; the browser verifies that signature before sealing. This protects passive exposure in the public API/loopback path. It does not protect an actively compromised browser application or a verification fingerprint delivered through that same compromised path.

The deployed signing fingerprint is published with the release verification record. No provider credentials belong in a strategy, API export, command argument or support screenshot.
