# Build on Backtrade, keep your strategy portable

Backtrade supplies historical data, simulated accounts, executions and results.
You need only Backtrade credentials to backtest. No Alpaca/IBKR account, broker
choice or external data subscription is required for historical replay.

## Start with your workflow

- Manual trading: open **Trade**.
- AI agent: connect the Backtrade MCP client through Quickstart.
- Python strategy: open **Agents → Code strategies** and use the local runner.
- Custom application: download a replay configuration in **Agents** and start the
  local bridge. It defaults to Backtrade replay; no broker profile is required.

```sh
node backtrade-bridge.mjs ./backtrade-replay.json
```

The bridge reads your account-linked Backtrade credential. Its generated local
token authenticates your application; it is not a broker API key.

## One application interface

The local REST base is `http://127.0.0.1:18900/backtrade/v1`. Authenticate with
`Authorization: Bearer <local configuration token>`.

```text
GET /capabilities
GET /account
GET /positions
GET /orders
GET /orders/{id}
POST /orders
DELETE /orders/{id}
GET /bars
```

```json
{"symbol":"SPY","side":"buy","qty":10,"type":"market","time_in_force":"day","client_order_id":"decision-001"}
```

Supported operations use stock/DAY market, limit, stop and stop-limit orders.
Unsupported fields fail explicitly. Broker contract identifiers are resolved by
the adapter; your strategy supplies the configured symbol. IBKR's portable adapter
currently accepts whole-share quantities. Fractional permissions vary by broker.
There is no claim of options, advanced order-class, extended-hours, TWS TCP or
exchange execution parity.

Accounts/positions use numbers; short positions have negative `qty`. Orders have
stable string IDs, numeric quantities, normalized side/type/status and cumulative
`filled_qty` / `filled_avg_price`. Missing timestamps remain null rather than
being fabricated. `GET /bars` returns completed 1m/5m bars for the configured
symbol. Replay never exposes its future; paper reads only closed intervals.

A downloadable Node client uses the same interface in either environment:

```javascript
import {TradingClient} from './backtrade-trading.mjs';
const client = new TradingClient('http://127.0.0.1:18900', LOCAL_TOKEN);
console.log(await client.account());
const order = await client.submit({
  symbol:'SPY', side:'buy', qty:1, type:'market',
  time_in_force:'day', client_order_id:'entry-001'
});
```

Download `/backtrade-trading.mjs` and its `.sha256` sidecar. Keep replay time
control (`/backtrade/advance`, `/play`, `/finish`) in your harness, outside the
strategy. Paper accounts do not have a controllable clock.

### Events

Connect to `ws://127.0.0.1:18900/backtrade/v1/events` with the local token in the
Authorization handshake header. The first message is a full `snapshot` with
`reset:true`, account, orders and completed bars. Later JSON messages include
`account`, `order`, `fill`, `bar` and `connection` with `schema_version:1` and
sequence numbers. Reconnect starts with a new snapshot; these are not durable
execution-log cursors.

The portable stream reconciles snapshots (roughly one second for replay, five
seconds for paper; paper bars refresh every 30 seconds). It does not claim to be
an exchange tick stream. Fills are deduplicated by cumulative quantity; `fill_qty`
is the newly observed quantity and `fill_price` is its inferred average price.
Several executions between snapshots can be combined. On `reconcile_required`,
pause decisions and read a fresh account/order snapshot. Native streams remain
available for applications that require their broker-specific event formats.

## Python strategy contract

The same `Strategy` class uses `on_start`, `on_bar`, `on_order`, `on_fill` and
`on_finish`. `Context` supplies account, orders, signed positions, completed
history, parameters, order submission/cancellation and logging.

```python
class Strategy:
    requirements = {
        "order_types": ["market", "limit"],
        "time_in_force": ["day"],
        "asset_classes": ["us_equity"]
    }

    def on_order(self, context, order):
        context.log(order["status"])

    def on_fill(self, context, fill):
        context.log("Newly filled: " + str(fill["fill_qty"]))
```

`filled_qty` is cumulative; `fill_qty` is this callback's increment. The runner
handles repeated observations and partial fills. An acknowledgement is not a
fill. Use `on_order` for rejection/cancellation state and reconcile through
`context.orders`; do not assume every requested order fills completely.
Declared requirements are checked before trading and displayed in experiment
logs. If declarations are absent, individual operations are still checked at
runtime; no automatic proof of future broker compatibility is claimed.

## Optional: external paper trading

Only when you choose **Connect to paper trading** do you select a broker and
configure its credentials. Historical backtesting stays independent.

Download a paper configuration, choose an immutable `versionId`, copy your tested
parameters, and start a read-only preview:

```sh
node backtrade-runner.mjs --paper ./backtrade-paper.json
```

Explicitly enable paper order submission after reviewing the preview:

```sh
node backtrade-runner.mjs --paper ./backtrade-paper.json --submit-orders
```

Alpaca uses your local environment's paper credentials and a pinned paper endpoint.
IBKR uses your local authenticated Client Portal Gateway and selected DU account;
see `IBKR-SETUP.md`. The paper runner uses the same isolated Python source and
callbacks, but wall-clock data drives decisions. It checks account/order snapshots
every five seconds and new completed bars every thirty seconds. Stale/missed
bars stop the runner instead of placing historical decisions in a current market.

Each run has explicit order, position, loss, callback and wall-time limits. Paper
preview never submits or cancels orders. In submission mode the strategy may
cancel only orders it submitted. Existing working orders in the configured symbol
must be reconciled before starting. A local state file records submitted IDs and
pending client IDs; after a crash, unresolved submissions must be reconciled
before a restart. The runner never blindly retries an uncertain paper order.

Stopping leaves positions and working orders at the broker; it does not liquidate
or alter unrelated orders. `on_finish` is read-only and best-effort on normal exit;
a forced termination may prevent it. Broker warning confirmations are returned
for explicit handling, not automatically accepted. Paper mode has no historical
Backtrade scorecard. Optional `publishPaper:true` displays broker-reported account
telemetry in Agents; it requires your Backtrade key and is labelled separately.

## Compatibility evidence

Backtrade's replay and actual Alpaca SDK/paper flows have been exercised. IBKR
native/portable contract mapping and local-gateway account boundaries are tested
with controlled responses; actual IBKR paper operation must be verified against
your authenticated Gateway and market-data entitlements. Same strategy logic
means the shared contract, not identical fills or identical results.

Existing broker-SDK applications can use **Advanced developer integrations**.
Those native endpoint formats are optional compatibility tools and never a
prerequisite for starting a Backtrade replay.

The portable adapter exposes one configured symbol per connection; native adapters retain their documented symbol support. Paper restart records are bound to account, strategy version, symbol and broker address. Use a separate `statePath` for each configuration. Reconcile any orders before removing an old state file. Strategy Lab shows a copyable saved version ID for the paper configuration.
