External (A-book) execution

Hedging client flow at a real broker — the two supported venues, the enablement sequence, routing rules and the dry-run tester, the in-flight table, the alert inbox, orphan handling and how to drain back to a pure B-book.

10 min readUpdated 6 August 2026a-book, hedging, oanda, metaapi, routing

By default this addon is a B-book. Every client order fills against your balance sheet, you keep the spread, and you carry the market risk. That is profitable until a large client is right about a large move.

The A-book layer lets you mirror selected client orders at a real broker. The client's experience does not change — same terminal, same prices, same statement — but behind it, an equal and opposite position is opened in your account at OANDA or a MetaTrader broker. Your P&L on that flow becomes the spread and commission, and nothing else.

Everything else can be undone with a setting. An order routed to a broker is a real trade in a real account with real money, and the master switch is Super Admin only for that reason. Practise on a DEMO venue until the alert inbox, the reconciler and the drain procedure are boring.

Who carries the risk

Model You keep You risk When it fits
B-book (default) Spread, commission, swap, and client losses Client profits come out of your balance sheet Small tickets, retail flow, no broker relationship
A-book Spread and commission only Almost nothing on the hedged flow — but you must fund and maintain the hedge account Large tickets, professional flow, regulatory pressure
Hybrid Both, split by rule Whatever you chose not to hedge Most real desks

Hybrid is the point of the routing rules: hedge the accounts and sizes that scare you and keep the rest internal.

The two venues

The simpler of the two. Synchronous fill-or-kill market orders with a price bound, identical APIs on practice and live, and a gapless, replayable transaction ledger — which is what the reconciler relies on to prove that what the broker did and what you booked agree.

Needs APP_OANDA_API_KEY in .env and the account ID in the provider row.

A cloud REST bridge onto MetaTrader broker accounts. Use it when your broker is MT-only. It adds a third party between you and the venue, and it needs your broker's written approval for datacenter logins — brokers do disable accounts over this.

Needs APP_METAAPI_TOKEN.

cTrader Open API was the third bridge and it is gone — backend/seeders/20260804000001-forex-remove-ctrader.js retires it. Where no position, order or deal ever referenced it the row is deleted; where one did, deleting it would leave those rows pointing at a venue that does not exist, so the seeder keeps the row disabled and renames it cTrader Open API (retired). It cannot come back: hasCredentials and createBridgeAdapter in utils/engine/bridges/index.ts no longer know the name, so enabling it is refused and building its adapter is a 400. If you still have open cTrader hedges, close them at the broker directly.

Credentials are environment-only. They are never writable through the admin API and the value never crosses the wire; the provider list returns a boolean saying whether the variable is present, and nothing more.

Unlike market-data providers, multiple execution providers may be enabled at once. There is no single-active rule here.

Enabling a provider, in order

Admin → Forex Trading → Execution → Execution Desk (/admin/forex-trading/execution)

  1. Put the credentials in .env and restart. There are two — APP_OANDA_API_KEY and APP_METAAPI_TOKEN — and both are already listed in .env.example, blank. You only need the one belonging to the venue you are enabling; the other stays empty.

  2. Set the account reference and environment on the provider row. The account reference is the OANDA account ID or the MetaApi account UUID. Start with DEMO.

    Updates account reference, environment, proxy and the execution knobs
  3. Verify the connection. This builds the bridge adapter, runs a live connection test and persists a snapshot of the hedge account's balance, NAV, margin used and closeout percentage onto the provider row. It does not change the enabled status.

    Runs a live venue connection test and snapshots the account
  4. Import and link venue symbols. The venue spells symbols its own way. This pulls the account's instrument list, matches it against your catalog and writes the venue symbol under this provider's key — merging, so another provider's mapping is never clobbered. Read the unmatched report: an instrument with no venue symbol cannot be hedged and will fall through the fallback ladder.

    Imports venue symbols and links them to the catalog
  5. Tune the knobs if the defaults do not suit your venue. They are described below.

  6. Enable the provider.

    Enables or disables an execution provider, guarded both ways
  7. Turn on the master switch. fxTradingExternalRoutingEnabled, on the core system settings page, Super Admin only. Until this is on, no order is ever routed regardless of what the rules say.

The LIVE enable gate

Enabling a provider whose environment is LIVE runs one extra check that a DEMO provider skips: a real alert-delivery self-test. It fires a genuine self-test alert to every Super Admin over both in-app and email, waits for delivery, and refuses the enable with a 422 if it does not land.

That gate exists because the entire A-book safety model is alert-driven. Orphan detection, hedge-margin warnings, cursor stalls and basis drift are all alert-only — the system will never auto-trade to fix them. An operator who does not receive alerts has no safety model at all, only the appearance of one.

Make sure at least one Super Admin has a working email address before you try.

The execution knobs

Hedge-leg price tolerance passed to the venue as a price bound, in points.
Silence after which the watchdog starts reconciling the order by reference.
Hard cap. A ROUTING order becomes REJECTED with "Broker timeout" after this.
Pre-trade check: the hedge account's available margin minus the estimate must exceed NAV times this.
Alert when the hedge account's margin used over NAV exceeds this. Venue closeout is at 1.0.
Alert and auto-suspend new routing when the hedge sync is older than this many seconds.
Daily financing-basis alert threshold. Null disables it.
Optional wide broker-side stop attached to hedge opens, as a disaster backstop. Null disables it.
Comma-separated asset-class allowlist for this venue. Null means all classes.

The environment field cannot be changed while the provider is enabled, or while open external positions or in-flight routing orders reference it. Disable, edit, re-enable — which re-runs verification and, for LIVE, the alert self-test.

Routing rules

Execution → Routing Rules (/admin/forex-trading/execution/rules)

Creates a placement-time routing override

Rules are evaluated in ascending priority, first match wins, and a null dimension is a wildcard. The dimensions are:

Dimension Matches on
instrumentId One specific instrument
symbolGroupId Everything in a symbol group
assetClass FOREX, STOCK, COMMODITY, INDEX, CRYPTO
accountGroupId An account tier
accountId One specific customer account
side BUY or SELL only
minAmount / maxAmount A size band, in base units

The target is INTERNAL (keep it on the book) or EXTERNAL (hedge it), and an EXTERNAL rule names the execution provider.

Two properties are worth internalising:

Routing is decided once, at placement. A pending order stamps its routing decision when it is placed and honours it at trigger. Editing, disabling or deleting a rule affects new placements only and never re-routes an open position. That is why deleting a rule is safe — existing rows keep their stamped routing for audit.

Saving warns rather than blocks. A rule pointing at a provider that has no symbol mapping for instruments the rule can match is saved with a warning attached, because those orders will follow the fallback ladder rather than failing outright.

The dry-run tester

Runs the real routing resolver against a hypothetical order, without side effects

Describe an order — account, instrument, side, size, type — and the widget runs the actual resolver: master switch, environment guard, priority-ordered rules, global default, fallback policy. Nothing is journaled, no venue is called, no alert is fired. You get the decision plus an ordered rule-by-rule trace showing which rules were considered and why each did or did not match.

Use it before every rule change. It is much cheaper than discovering the answer with a customer's order.

When nothing matches, and when it goes wrong

Where an order goes when no routing rule matches it.
Applied when an order resolved EXTERNAL but no provider is usable.

The fallback fires when a provider is disabled, degraded, or the master switch went off between a pending order's placement and its trigger.

  • INTERNAL — fill it on the B-book and alert the desk. Recommended. Customers keep trading; you find out immediately and carry that one position yourself.
  • REJECT — refuse the order with an error. Choose this only if you would rather turn business away than carry unhedged risk, and understand it means a broker outage becomes a customer-visible outage.

An INTERNAL decision reached by fallback still records the rule that matched and why the fallback fired, so the audit trail does not lose it.

Monitoring the hedge

The Execution Desk shows, per provider: NAV, margin used, margin-use ratio, closeout percentage, sync age, a degraded flag and the unacknowledged alert count. The hedge monitor cron refreshes this every 60 seconds.

The in-flight table is the one to watch during an incident.

ROUTING orders, unresolved rejections and pending broker closes

It lists three things: orders journaled ROUTING and awaiting venue confirmation with their reserved margin and external reference; orders REJECTED at timeout but still flagged unresolved, which stay under a 24-hour ledger re-check window in case the fill turns up late; and open positions whose broker-side close has been requested but not confirmed.

A healthy desk has a near-empty in-flight table. Rows that persist are the ones that become orphans.

Per-provider, per-month A-book economics

The revenue report is where you find out whether hedging is actually paying: hedged volume, fill counts, price basis in points per leg, broker financing, dividends and commissions, hedge realised P&L from the replayed broker ledger, the client-leg P&L against it, and the combined net A-book margin. Broker-leg amounts are in the venue's account currency; client-leg amounts are converted to USD.

The alert inbox

The execution alert inbox, filterable by provider, severity and state
Acknowledges an alert

Every alert lands here and goes out by in-app notification and email to every Super Admin. Acknowledging is idempotent and records who and when.

What raises one: hedge margin breaching marginAlertRatio, repeated hedge sync failures, a stale sync cursor (which also auto-suspends new routing), broker timeouts, financing basis drifting past your threshold, ledger drift on a client account, unconvertible swap rollovers, and orphans in either direction.

Orphans

An orphan is a disagreement between your books and the broker's. The reconciler finds them by replaying the broker's transaction ledger every 60 seconds, and it never trades to fix one. It alerts, and you decide. Both remedies are confirm-gated and both write an alert recording who did what.

Closes an orphaned broker-side position at market

Use flatten when the broker holds a position you have no client position for — a hedge that was opened and whose client leg never booked. It closes that trade at market, either by venue trade id or by submitting an offsetting order for a symbol and net size. It refuses with a 409 if a matching open client position exists, because that hedge is not an orphan.

Books a late-discovered venue execution to the client

Use book late in the mirror case, in two forms. Either an order was rejected on timeout and the fill turned up afterwards — the order is re-journaled and the recorded venue fill is driven through the normal booking path, so the client gets their position at platform prices exactly as if it had confirmed in time. Or an open external position whose hedge the broker closed server-side, which is booked through the external-close path with the reason ADMIN.

The Execution Desk also serves the operator runbook — the enable checklist, an incident playbook for every failure mode that ends in an operator action, the funding and top-up policy, the drain procedure and the practice-to-live promotion. Read it before you enable a live venue; it ships with the backend and is more detailed than this page.

Draining back to a pure B-book

Turning the master switch off does not unwind anything. Existing external positions stay hedged at the broker and keep closing there normally; only new orders stop being routed. That is the correct behaviour — the alternative would be mass-flattening a live book on a settings change.

  1. Turn off fxTradingExternalRoutingEnabled. New flow now stays internal.

  2. Watch the live counts on the Execution settings tab and the Execution Desk: open external positions, in-flight routing orders, pending closes. This is the drain-warning data.

  3. Wait for them to reach zero. Positions close as clients close them, or you force-close them from the Positions screen.

  4. Then disable the providers. Disabling is refused with a 409 while any open external position, in-flight routing order or pending close still references the provider — the guard is there so you cannot orphan a hedge by switching something off.

  5. Leave the reconciler running until the counts are genuinely zero. It keeps working through the drain by design.

Next