# Backtrade broker bridge

The bridge provides a local Alpaca Trading API or IBKR Web API subset for a
historical Backtrade replay, and can connect the same client to a paper broker.
Your broker credentials stay in the local process. Use Node 22 or later.

1. Sign in to Backtrade. For a broker-neutral replay, use **Agents** and
   [the portable interface](PORTABLE-TRADING.md). Existing native broker clients
   use **Agents → Advanced developer integrations**.
2. Link the Backtrade MCP client to your account, or create a key under Account →
   API keys and save the documented private credential file.
3. For an existing native client, choose its API format, a recorded session and
   data source. No external broker account is required for replay.
4. Download `backtrade-bridge.mjs`, its checksum, and your configuration.
5. Run `sha256sum -c backtrade-bridge.mjs.sha256`, then:

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

Use the `token` in your local config as the bridge secret. For Alpaca clients,
use any local key ID and that token as the secret. IBKR Web API clients pass
`Authorization: Bearer <local token>` or `X-Backtrade-Token` in each request and
WebSocket handshake. The bridge listens on loopback only and rejects browser
origins. Do not expose it directly to the internet.

## Alpaca Python SDK

```python
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest
from alpaca.trading.enums import OrderSide, TimeInForce

client = TradingClient("backtrade", LOCAL_TOKEN,
                       url_override="http://127.0.0.1:18900", paper=True)
print(client.get_account())
order = client.submit_order(MarketOrderRequest(
    symbol="SPY", qty=1, side=OrderSide.BUY, time_in_force=TimeInForce.DAY))
print(order.id, order.status)
```

The replay is paused until its driver advances it. This control belongs in the
harness, outside the strategy:

```sh
curl http://127.0.0.1:18900/backtrade/advance \
  -H 'Authorization: Bearer <local token>' -H 'Content-Type: application/json' \
  -d '{"milliseconds":60000}'
```

Tick replay uses `milliseconds` (up to 60,000 per request). Minute replay uses
`until` in UTC or `events`. `/backtrade/play` accepts `{"speed":1}` through 60;
zero pauses. `/backtrade/finish` advances to completion and records results.
`/backtrade/status` reports its watch URL and current state.

For native streams, connect to `/v2/iex` (market data) and `/stream` (order
updates), authenticate with the local token, and subscribe using Alpaca messages.
Historical `/v2/stocks/SPY/bars` exposes only bars already seen in the replay.
Choose the bar interval in configuration. Broker rehearsal uses market prices and sizes with an account-linked key.
Set `pricePolicy: "indexed"` for blind research; SDK data models that require
volume/sizes cannot consume that redacted mode.

## IBKR Web API

Use `http://127.0.0.1:18900/v1/api`. Resolve a replay conid first:

```text
GET /iserver/secdef/search?symbol=SPY
GET /portfolio/accounts
POST /iserver/account/DU_REPLAY/orders
{"orders":[{"conid":1,"side":"BUY","quantity":1,"orderType":"MKT","tif":"DAY","cOID":"entry-1"}]}
```

Use returned contract identifiers; replay conids are local, not real IBKR IDs.
Resolve the same symbol again when switching to IBKR paper. Supported vocabulary:
`MKT`, `LMT`, `STP`, `STP LMT`, `DAY`; orders, cancellation, modification,
positions, account summary and causal market-data history/snapshot.
`/v1/api/ws` supports `smd+<conid>+{"fields":["31","84","86"]}`, `sor+{}` and
`tic`. This is Web API support; TWS's proprietary TCP protocol is not emulated.

## Paper environments

For Alpaca, change `environment` to `alpaca-paper`, keep `profile: "alpaca"`,
and provide `ALPACA_API_KEY` and `ALPACA_API_SECRET` to the bridge process (for
example via `node --env-file=/private/paper.env ...`). The bridge pins trading
to `https://paper-api.alpaca.markets`. Market data uses Alpaca's data stream and
your own entitlements. The bridge supports both JSON and the Alpaca Python
SDK's MessagePack encoding. With `barInterval: "5Min"`, it aggregates incoming
minute bars into completed five-minute observations in paper mode as well. Streaming clients authenticate the local handshake with
the local token; the bridge supplies broker authentication upstream.

For a first-time IBKR connection, follow [the account and Gateway setup guide](IBKR-SETUP.md).

For IBKR, use `environment: "ibkr-paper"`, `profile: "ibkr"`, set `brokerBase`
to your local authenticated Client Portal Gateway, including `/v1/api`, and
`paperAccount` to its actual DU account ID. Configure the gateway's certificate
trust with Node's normal CA settings. The bridge permits order mutations only
for the configured paper account; order-reply warnings are returned unchanged. Explicitly accept or decline a
pending warning with `POST /iserver/reply/{id}` and `{"confirmed":true}` or
`false`. Only replies from this bridge’s configured paper account are accepted. Authentication/market-data permissions are still enforced by IBKR.

The official IBKR MCP connector produces human-reviewed instructions; it is not
this automation adapter. The bridge does not enable live-money trading.

## Compatibility

Replay supports US equities, market/limit/stop/stop-limit DAY orders, numeric
strings, fractional quantity, cancel and replace, account/positions, and streams.
Unsupported TIF (IOC/FOK/GTC), notional orders, advanced order classes, and
extended-hours flags are rejected. Replay can contain extended-hours data;
that does not imply full broker extended-hours order semantics.

Alpaca and IBKR paper fill models differ from the historical simulator. Broker rehearsal uses nominal prices and is explicitly non-predictive; blind
research is a separate indexed mode. Paper trading uses nominal prices and
cannot pause time. Maintain explicit sizing, latency and data assumptions.

## Connection doctor

```sh
node backtrade-bridge.mjs --doctor ./backtrade-broker.json
```

The doctor checks replay credentials and date coverage, or the actual configured
paper account. It does not place an order. A failed check exits nonzero.

## Portable MCP tools

Run the same local bridge as an MCP server. Let MCP start it; do not also run
the standalone command on the same port. Its REST and WebSocket endpoints remain
available to your other local clients:

```json
{"mcpServers":{"broker":{"command":"node","args":["/absolute/path/backtrade-bridge.mjs","--mcp","/private/path/backtrade-broker.json"]}}}
```

`get_account`, `get_positions`, `get_orders`, `resolve_contract`, `place_order`,
`cancel_order` and `get_bars` use the selected broker in either environment.
IBKR also exposes `reply_to_order_warning`.
Replay additionally exposes `advance` and `end_session`. Changing environment
keeps the trading tools; paper mode cannot advance a historical clock.

Set `publishPaper: true` and provide your Backtrade credential file to display
paper state in the same Agents workspace. Reports are labelled as supplied by
your local connector, not independently verified Backtrade executions. The local
connector reports positions/equity/orders; Alpaca also reports fills and bars.
IBKR paper telemetry reads positions, orders and historical bars, and consumes
trade updates from the connected gateway stream. Keep its trades subscription
active for ongoing fill telemetry; actual gateway/data entitlement checks are
required before treating a connection as ready. P&L starts at connection equity.

## Recovery and observation semantics

The bridge saves a non-secret session handle beside its configuration. Restart it
with the same file to resume; use `--new` to deliberately create a new run after a
completed session. Transient replay disconnects pause playback and attempt bounded
recovery. Reconcile orders after reconnecting. A lost POST response is retried
with the same client order ID.

The historical tick engine executes against the full recorded tape. The bridge's
quote/trade observations are latest snapshots at each advance, tagged
`replay_snapshot`; they are not a claim to deliver every recorded tick. Timestamps
and visible sizes come from the actual last event, never the polling clock.
Completed 1m/5m bars are streamed separately. Bar trade counts/VWAP remain null
when the archive does not contain those fields; no values are invented.

Paper observation shows recent activity. Reports compact broker payloads to fit
the API request limit, preserve account/working-order state, and expose counts
of older records omitted in `account.observer_omissions`. Use the native broker
API to request additional history. Paper reports remain distinct from Backtrade's
complete replay execution ledger.
