Bitnomial DCM Edition (AIEX)

A temporary US-regulated edition of AX — list Architect products on Bitnomial's DCM and introduce customers to Bitnomial's FCM — built by forking the EP3 hot-path services onto a shared ax-bitnomial client crate, to be cut away once Architect's own DCM is live.

RFC: bitnomial-dcm-integration.md Epics: A-3470 · A-3489 · A-3448 · A-3311 · A-3486 · A-3348 Milestone: Integrations (Perpetuals exchange) — 32% Status snapshot: 2026-06-06 (refreshed — #2138 on main)

§0Principles

This edition is a stopgap, not a platform, and every design call follows from that. The four invariants below — plus one deliberate carve-out from the first — explain the whole shape of the work; everything in the tracker is mechanics.

It's temporary, so fork — don't abstract

The edition may run only a few months, so we build separate, copy-pasted hot-path services (aiex-risk-engine, aiex-marketdata-publisher, bitnomial-order-gateway, FIX fill ingestion) on a shared ax-bitnomial client crate, rather than paying for a generic EP3↔Bitnomial abstraction we'd then have to maintain and unwind. EP3 coupling is not isolated to one adapter — it lives in identity, provisioning, instruments, lifecycle, and settlement — so a "clean adapter" would create misleading compatibility.

§2a, §2g

Exception to (1): the api-gateway is switched, not forked

The one carve-out from "fork, don't abstract." The api-gateway's EP3 surface is a small part of the whole, so instead of forking it we route through a single AX_MATCHING_ENGINE / AX_VENUE switch that fails fast on misconfig and gates EP3-only admin surfaces (price-band edits, market-surveillance alerts) off under Bitnomial. This is the one place a thin abstraction earns its keep.

FIX drop copy is the authority; BTP replies are only a low-latency hint

The protocol split assigns each job to the right channel: BTP DMA for order entry and fast accept/reject feedback, FIX 4.4 drop copy as the authoritative order/fill lifecycle source, REST for product catalog / funding rates / startup backfill, and BTP pricefeed / WebSocket for market data. Lifecycle truth never comes from the BTP reply alone.

§2b, §2f

One omnibus connection multiplexes many accounts

Bitnomial confirmed a single order-entry connection can route to many clearing accounts via an "attributed open" message (a normal Open plus an account field) reserved for FCM-class partners. So AX trades through one connection, owns its own subaccounting, and maps each AX account_id → a Bitnomial clearing account — it does not need one connection per customer.

§2c, §2d

Positions are dead-reckoned exact; only margin is approximate

AX tracks positions by exact dead reckoning from its own order-fill stream and reconciles against the FCM at end of day — positions are not "approximate," they're precise and checked. What's genuinely approximate is margin: AX may ultimately apply a different risk model than the FCM, so the intraday margin number is an estimate, not a copy of the clearing figure. Until AX is a direct DCM entity, Bitnomial's authoritative margin/settlement state is reachable only through the FCM (Dorman, via GMI) on the daily cycle.

§2e, §3.3

§1Progress & Dependencies

This is not a linear sequence — it's nine parallel tracks, mostly one per forked service and one per owner, that converge on a working edition. The flat order would hide what actually matters: which tracks can run independently, and the handful of hard dependencies where one track genuinely blocks another. The dependency map shows those edges; the swimlanes below show progress per track. Snapshot 2026-06-06.

Done — 6 In progress — 6 Not started — 3 · 15 items across 9 tracks

Dependency map

How the tracks block one another. Solid arrows are hard blockers (the downstream work cannot finish first); dashed arrows are foundational or soft. Node colour is status. The critical path to a demoable edition runs drop-copy → risk-engine and accounts → order-gateway, both converging — with the api-gateway seam — on Edition works E2E; the external-dependency tail then gates go-live.

ax-bitnomial crateTrack A · done aiex infra / CITrack H · done FIX drop-copyTrack E · #2138 · done Market dataTrack C · #1944 · ababkin Accounts rekeyTrack G · X-cut · Andrew API-gw seamTrack B · A-3489 · Loc Risk-engine cutoverTrack D · Tin Order gatewayTrack F · Andrew · held Edition works E2Emilestone FCM reconciliationTrack I · external · blocked Customer onboardingTrack I · external · blocked Go-live / certificationTrack I · + commercial / LOI
done in progress not started externally blocked hard blocker soft / foundation
Process risk on the critical path: one unmerged integration branch remains. The drop-copy root #2138 (Track E, tin/bitnomial-fix-dropcopy) landed on main on 2026-06-07, taking its branch-local PRs (#2237, #2196, #2193) with it — so the risk-engine cutover stack (Track D) rooted on it can now rebase onto main. The remaining integration-branch root is ababkin/md-btp-fork-2-service (#1944, Track C), still open. Landing it, plus the accounts rekey (Track G) beneath the order-gateway (Track F), is what unblocks Edition works E2E.
AFoundation & shared client Loc · Michael · ababkin
3/4 done

The shared substrate every forked service builds on — done bar BTP codec clean-ups. Unblocks all venue tracks (soft edges in the map).

A1ax-bitnomial client crate — BTP codec/session, REST, FIX wrapperdone

The shared venue client all four services build on: the ax-btp wire crate (binary little-endian framing via binrw + tokio_util::codec, sequence IDs, heartbeats), the BTP order-entry bodies (Open/Modify/Reject/Closed/AttributedOpen), the authenticated REST client (products, orders, fills, funding, HMAC), and a FIX 4.4 wrapper. See §2b.

  • Merged to main
  • #1943 add ax-btp wire crate and ax-btp-mock scenario server
  • #1965 use binrw and tokio_util::codec for ax-btp
  • #2044 wrapper for Bitnomial's FIX protocol
  • #2131 ax_bitnomial::rest REST client (A-3491)
  • #2133 authenticated /orders endpoint
  • #2203 shared ax_bitnomial::instruments mapping helper
  • #2208 BTP order-entry Modify/Reject/Closed/AttributedOpen bodies (A-3545)
A2BTP codec hardening — heartbeats, body guards, frame & status handlingTinin progress

Make the wire layer robust against a real venue: validate heartbeat frames, bound body lengths, surface (not silently drop) corrupt order-entry frames, tolerate unknown order statuses, retry authed REST on 429, and guard order pagination. Mostly merged; a couple of clean-ups still open and two superseded spikes closed.

  • Merged to main
  • #2244 reject malformed BTP heartbeats
  • #2246 reject oversized BTP bodies
  • #2206 surface corrupt order-entry frames instead of dropping them (A-3542)
  • #2207 retry authed REST on 429 and guard order pagination (A-3543)
  • #2195 tolerate unknown Bitnomial order statuses
  • Open
  • #2245 preserve unknown BTP bodies (keep raw)
  • Superseded / closed
  • #2243 align fill layout with BTP docs (folded into other fixes)
  • #2247 inbound BTP sequence validator (superseded)
A3ax-btp-mock scenario serverababkindone

A TCP scenario-replay server that speaks BTP, so order-gateway / md-btp can be exercised without live venue credentials. Delivered (in revised form) inside the merged #1943; the original stacked draft #1922 was superseded. (A-3314 phase-4a crate was canceled in favor of this consolidated shape.)

  • Merged to main
  • #1943 ax-btp-mock scenario server (bundled with the wire crate)
  • Superseded / closed
  • #1922 original stacked TCP scenario-replay server (phase 4a)
A4SDK leak guard — forbid bitnomial / btp in the public SDKLocdone

A CI check that forbids the terms bitnomial and btp from leaking into the public rs/sdk — the venue is an internal implementation detail that must not surface in the customer-facing protocol. (A-3541.)

  • #2204 forbid bitnomial and btp terms in SDK leak check
BAPI Gateway venue seam Loc
1/2 done

The one deliberate non-fork (§2g) plus instrument/catalog loading. The convergence integration point — epic A-3489, the only Urgent epic in the set.

B1Matching-engine / venue selection seamLocin progress

A single AX_MATCHING_ENGINE selection seam routes the gateway to EP3 or Bitnomial and gates EP3-only admin surfaces off under Bitnomial. The seam and the EP3-surface gates are merged; the fail-fast-on-misconfig hardening (A-3515) and the data-plane parity verifier (A-3497) are still open. Epic A-3489 (P1/XL, In Progress).

  • Merged to main
  • #2132 AX_MATCHING_ENGINE selection seam (A-3494)
  • #2162 gate EP3 market-surveillance alert routes (A-3516)
  • #2197 gate off EP3 price-band edit admin surfaces (A-3517)
  • Open
  • #2153 single matching-engine switch with fail-fast on misconfig (A-3515)
  • #2240 Bitnomial data-plane parity verifier (A-3497)
B2Instruments & product catalog + price bandsLoc · ababkindone

Load Bitnomial products from REST /product/specs/ into the instruments table via a DB-driven product map (futures + perpetual futures only; product_id is the routing key, symbol is display identity). Seed hardening and authoritative price bands on the instrument spec followed. Price-band harmonization (A-3562) has one remaining sub-item — publishing bands in the md-btp ticker (A-3563) — but the instrument-spec surface is done.

  • Merged to main
  • #2148 load Bitnomial instruments via product mapping (A-3492)
  • #2191 repoint seed example to a real loadable product (A-3531)
  • #2192 warn-only row failures on BTP instrument seed (A-3532)
  • #2242 surface Bitnomial price bands on instrument spec (A-3564)
  • Funding rates (REST)
  • #1938 estimated funding-rate API + GUI (A-3345)
  • #2140 serve estimated funding rate over WebSocket
CMarket data — aiex-marketdata-publisher (md-btp) ababkin · Loc
in progress

Fork of marketdata-publisher onto the BTP pricefeed. Hard-feeds the risk engine (marks) and the live exchange. Lives on an unmerged integration branch (#1944).

C1aiex-marketdata-publisher (md-btp) forkababkin · Locin progress

Fork marketdata-publisher to consume the BTP pricefeed and publish the AX market-data protocol (top-10 aggregated depth, documented as such). The service fork lives on the long-lived ababkin/md-btp-fork-2-service branch (renamed aiex-marketdata-publisher); marks-to-Redis and a probe merged into that branch, not main. The earlier phase-by-phase md-core cutover (A-3310/3312/3313) was canceled in favor of this consolidated fork (epic A-3311 V3 "fork not abstract").

Root PR not yet on main. The whole md-btp stack hangs off #1944 (open). With the drop-copy root (#2138) now landed, this is the last integration-branch unblock called out in the dependency map.
  • Open — root + stacked on the fork branch
  • #1944 fork marketdata-publisher to ax-md-btp with BTP feed (root)
  • #1946 testcontainer integration suite (stacked)
  • #2178 probe Bitnomial market-data publisher (stacked)
  • Merged into the fork branch (not main)
  • #2199 mirror Bitnomial mark prices to Redis (A-3537)
  • Superseded / closed (phased md-core)
  • #1918 md-core scaffold (A-3310, canceled approach)
  • #1945 md-btp REST stats poller (superseded)
  • #1919 / #1920 / #1921 / #1923 phased md proposal/adapters (drafts, superseded)
DRisk engine — aiex-risk-engine fork Tin
in progress

Forked risk engine cut over to Bitnomial inputs — furthest-along service fork (A-3470, In Review). Drop-copy (E) landed on main, so the cutover stack can rebase off that branch; now gated mainly on market data (C).

D1aiex-risk-engine fork + cut over to Bitnomial inputsTinin progress

Fork ax-risk-engine and replace its EP3 venue I/O with Bitnomial inputs: a FIX ExecutionReport → calculator adapter, marks polled from md-btp's Redis (depends on Track C), REST open-orders bootstrap + ProductSpecIndex, a FIX drop-copy task bridging quickfix → tokio mpsc (depends on Track E), and an account map off logical replication (depends on Track G) — then cut run() over. The fork itself is merged (#2136); the cutover is a deep open stack that was rooted on the drop-copy branch and can now rebase onto main since #2138 landed (2026-06-07). The area saw ~7 abandoned fork attempts (#2125–#2135) before stabilizing.

Sub-stepPRLinear
FIX ExecReport → ToCalculator adapter#2160A-3471
Poll md-btp marks from Redis#2156A-3511
REST open-orders bootstrap + ProductSpecIndex#2157A-3513
FIX dropcopy task bridge (quickfix→mpsc)#2158A-3512
Cut run() over to Bitnomial inputs#2159A-3514
Index trading accounts off logical replication#2202A-3540
  • Merged to main
  • #2136 fork ax-risk-engineaiex-risk-engine (A-3472)
  • Open — cutover stack (was rooted on the now-merged drop-copy branch; rebasing onto main)
  • #2160 · #2156 · #2157 · #2158 · #2159 · #2202
  • Backlog: end-to-end risk smoke test (A-3475), risk-engine2 + Redis margin parity in OG (A-3473), contract-multiplier in margin/PnL (A-3544).
EDrop-copy / fill ingestion Tin · Michael · Loc
done

The authoritative lifecycle source (§2b). Root branch #2138 landed on main 2026-06-07, unblocking the risk-engine cutover (D).

E1FIX drop-copy ingestion / fill attributionTin · Michael · Locdone

A shared FIX drop-copy initiator, a dedicated FIX consumer, and a persist-first Sink + contiguous Watermark ingest contract. Fills are single-sided and attributed to AX accounts via the order-mapping table — not paired both-sides like EP3 trade ingestion. The root branch tin/bitnomial-fix-dropcopy (#2138) merged to main on 2026-06-07, bringing the dedicated consumer, persist-first Sink/Watermark, and REST-host parameterization with it; a hardened second iteration (batched ClickHouse inserts, MsgSeqNum validation, replay/recovery) continues on the -v1 branch.

Landed — critical-path blocker cleared. #2138 was repeatedly flagged as blocking the aiex PoC; with it on main, the risk-engine cutover stack (Track D) can rebase off the integration branch onto main.
  • Merged to main
  • #2138 shared FIX dropcopy initiator module (root branch, landed 2026-06-07)
  • #2237 dedicated FIX consumer for Bitnomial drop copy (rode in on #2138)
  • #2196 persist-first Sink + Watermark ingest contract (rode in on #2138)
  • #2193 parameterize Bitnomial REST host (rode in on #2138)
  • #2067 accept SPD SecurityType for spread fills (A-3413, groundwork)
  • Open / draft
  • #2086 port trade-engine to Bitnomial FIX 4.4 drop copy (A-3348)
  • Superseded / closed
  • #2184 require + validate FIX dictionary / file-store paths from env (closed)
FOrder gateway — bitnomial-order-gateway Andrew · ababkin
in progress · held

The biggest remaining technical piece (A-3448). End-to-end PoC worked 2026-06-04, then held pending the accounts rekey (G).

F1bitnomial-order-gateway fork — PoC → productionAndrew · ababkinin progress

Fork order-gateway: preserve the AX REST/WS order API, replace Ep3Identity with Bitnomial account/connection routing, persist the AX order_id → (connection, BTP orderId, ackId, modifyId, account) mapping, use BTP replies for fast feedback and FIX drop copy as the lifecycle authority. An end-to-end PoC worked on 2026-06-04 — a real user placed and cancelled a QQQ-PERP order via the trader GUI. The PoC base-stack PRs were then put on hold pending the account rekey (Track G).

Gated on Track G. The OG stack was told to "hold for now" until the account/identity rekey lands — see §2d and §3.8.
  • Open — refound stack
  • #2209 refound bitnomial-order-gateway crate (venue glue + tests)
  • #2211 order-gateway auth + users-replica (stacked)
  • #2212 order-gateway PoC-0 — connectable WS + deploy (stacked)
  • #2210 order-gateway open-orders cache (stacked)
  • Superseded / closed
  • #2075 earlier scaffold (superseded by the refound stack)
GAccounts / identity cross-cutting Andrew · Tin · Loc
in progress

The internal mega-blocker. Rides the multi-accounts rekey; gates the order gateway (F) and feeds the risk engine's account map (D).

G1Account / identity provisioning & rekeyAndrew · Tin · Locin progress

EP3 identity is jammed into users.ep3_username / trading_accounts.ep3_account; Bitnomial routing needs its own mapping (AX account_id → Bitnomial clearing account + connection). This rides on the separate multi-accounts rekey (splitting user_id into identity/ownership/membership) — without that abstraction the edition "is gonna really have a bad time." Bitnomial account columns + a provisioning loader are open; the gateway identity decouple (A-3495 → A-3499/A-3500) is still backlog.

  • Open
  • #2154 add trading_accounts Bitnomial account columns + map on load (A-3510)
  • #2198 provision account identity on trading accounts (A-3538, stacked)
  • Backlog: decouple gateway identity from EP3 (A-3495), nullable EP3 columns + Bitnomial mapping migration (A-3499), mint native IDs + load account map (A-3500). Account-key risk-engine internal state (A-3582, owner Andrew).
HInfrastructure & deploy Michael · Alanham · Loc
done

The substrate the forked services deploy onto — kept separate from the live AX stack.

H1aiex instances, terraform UAT, CI/releaseMichael · Alanham · Locdone

Standalone aiex-demo / aiex-prod instances, the bitnomial-uat terraform stack, the aiex build-and-release CI workflow (with TigerBeetle re-enabled), and a service deploy runbook.

  • Merged to main
  • #2047 add aiex-prod instance
  • #2060 add aiex-demo instance
  • #2090 aiex target build-and-release workflow
  • #2117 re-enable TigerBeetle step in aiex image builds
  • #1950 add bitnomial-uat terraform stack
  • #2083 commit bitnomial-uat lock file
  • #2190 aiex service deploy runbook (A-3529)
  • Open / draft
  • #2229 aiex GUI integration branch + build config
IExternal dependencies & go-live Brett · Michael · Kevin
not started

The external-dependency tail — blocked on Bitnomial (margin APIs, onboarding portal) and the commercial agreement. Gates go-live, parallel to the engineering tracks.

I1Position / margin reconciliation (DCO / FCM)TBDnot started

Bitnomial's authoritative margin / settlement APIs are gated behind contract execution — AX won't get direct access until it's the actual DCM / a direct entity; reconciliation otherwise routes through the FCM (Dorman) via GMI. The plan (§2e): positions are dead-reckoned exact and reconciled against the FCM end-of-day; margin is the approximate part (AX may run a different risk model). A read-only Clearing API hookup is scoped (A-3579) but backlog. The residual risk is concentrated in margin (§3.3).

  • No PRs — blocked externally on contract execution / FCM API access. Scoped: A-3579 (read-only Clearing API), A-3506 (reconcile client rate-limiting with Bitnomial's hard REST limits).
I2Customer onboarding / FCM IB flowsKevin · Brettnot started

As Introducing Broker, AX onboards customers to Bitnomial's FCM. Bitnomial's institutional onboarding portal and a programmatic onboarding/provisioning API are not yet delivered ("as soon as they can"); today it's a manual portal workflow. The current onboarding work collects customer emails to forward to Bitnomial. Account/connection setup is treated as an ops/admin workflow, with mappings stored in Postgres.

  • No PRs landed for the programmatic flow — blocked on Bitnomial's onboarding API/portal. Manual ops workflow in the interim.
I3Go-live, certification & eventual cutawayBrett · Michaelnot started

Bring the edition to production behind a real customer, then — once Architect's own DCM is live — cut away from Bitnomial "as soon as regulatorily possible." A soft go-live target of ~mid-June 2026 ("an exchange that sort of works, with rough edges and limited functionality") was floated, but it is not a committed date and is downstream of tracks D–G and I1.

Commercial / legal context (track, don't engineer). The Bitnomial LOI carries an exclusivity provision, monthly minimums, a per-contract listing fee, and a rebate pool — yet does not obligate Bitnomial to certify/list the contracts in any timeframe, and permits assignment to an affiliate (Kraken). Certification timing is therefore an external dependency outside engineering's control.
  • No PRs — gated on a working order-gateway (F), reconciliation (I1), and the commercial agreement.
Reading the tracks. A (foundation) and H (infra) are done and underlie everything. B (api-gateway) is the convergence integration point. The four service forks — C market-data, D risk, E drop-copy, F order-gateway — saw E land on main (2026-06-07); C is still pooled on an unmerged integration branch, D is in flight, and F is gated behind the cross-cutting accounts rekey (G). I is the external-dependency tail (reconciliation, onboarding, go-live), blocked on Bitnomial and the commercial agreement. The hard-dependency edges (map above) are: E→D, C→D, G→F, then D, F, B → Edition E2E → go-live.

Notebook

Reference design — the mechanics and reasoning behind the tracker and the open questions. Drawn from the RFC and the team's working decisions. Sections 2a–2h.

§2aThe "aiex" strategy — fork, don't abstract

AX is an exchange built over Connamara EP3. The Bitnomial Edition (nicknamed AIEX = ax + bitnomial) lets Architect operate a US-regulated venue now — listing Architect-licensed products on Bitnomial's DCM and introducing customers to Bitnomial's FCM — while Architect's own DCM spins up. Because the edition may run only a few months, the preferred shape is separate, copy-pasted services wherever that reduces abstraction work and risk.

The reason a generic EP3 adapter was rejected: EP3 coupling is not isolated to one seam. It appears in service-layer assumptions around identity, account provisioning, instruments, order lifecycle, market data, and settlement — order-gateway logs into EP3 at startup, loads instruments from EP3, converts requests into EP3 protobuf, and drives lifecycle off EP3 drop-copy semantics. Bitnomial is different enough at each point (binary BTP DMA not gRPC; ConnectionId+AuthToken not user impersonation; modify-in-place not two-order replace; FIX 4.4 drop copy not EP3 drop copy; top-10 aggregated depth not book entries) that forcing it through EP3 shapes would create misleading compatibility.

Throwaway-by-design, but reuse the commons. The fork rule applies to venue-shaped hot-path services. We still reuse: the public SDK protocols, auth/session machinery, DB/Redis/ClickHouse helpers, transaction-engine, and risk-engine2 internals. We fork: order-gateway, marketdata-publisher, risk-engine venue I/O, and fill ingestion.

§2bThe protocol split

Bitnomial exposes several protocols; each is assigned the job it's best at, and lifecycle truth is deliberately separated from the fast path.

ChannelUsed forAuthority?
BTP DMA order entry binary TCPopen, modify, cancel-as-modify, and fast accept/reject repliesLow-latency hint only — not the source of lifecycle truth
FIX 4.4 drop copyprivate order/fill lifecycle, reconciliation, recovery via gap-fill/resendAuthoritative for order & fill lifecycle
RESTproduct/spec catalog, funding rates, orders/fills backfill, reference data; HMAC authAuthoritative for reference data & backfill
BTP pricefeed / public WebSocketmarket data — top-10 aggregated levels, trades, market stateMarket data only (top-10 depth, documented as such)

Fills from drop copy are single-sided — we get our account's view, not both sides of the trade as EP3 trade ingestion pairs them — so fills are attributed to AX accounts through the order-mapping table rather than matched. REST /fills is the recovery source when the FIX session gaps. Ports in UAT: 11000 BTP order entry, 12000 BTP pricefeed, 10001 FIX drop copy.

§2cThe omnibus connection & attributed-open routing

The RFC's first and most important question was whether one BTP order-entry connection can route to multiple clearing accounts — public BTP Open carries only orderId, productId, side, price, quantity, timeInForce, with no documented account field. If the answer were no, AX would need one connection per customer.

Answered — yes. Bitnomial confirmed a single session can specify the account via an "attributed open" message — a normal Open plus an account field — a capability they reserve for FCM-class partners. Confirmed live in UAT. So AX trades through one omnibus connection, owns its own subaccounting, and the AttributedOpen body landed in #2208.

Self-match handling is "cancel on self-match" and is always enabled / not caller-configurable, so exercising both sides of a market in test requires distinct accounts (Bitnomial provisioned UBU00005UBU00008 for Architect). One consequence for identity: the mapping is AX account_id → Bitnomial clearing account, and Bitnomial account IDs may not be globally unique across FCMs — so the mapping must also carry the clearing firm.

§2dIdentity & account mapping at the seam

AX currently encodes user/account identity into EP3 participant/account names (users.ep3_username, users.ep3_account, trading_accounts.ep3_username, trading_accounts.ep3_account). Bitnomial needs its own mapping, and the cleanest place to put it is new nullable Bitnomial columns on trading_accounts plus a publication/replica into the risk engine — not a restructuring of the EP3 columns.

This is the internal critical path. The Bitnomial mapping rides on the broader multi-accounts rekey — splitting the conflated user_id into identity / ownership / membership and removing from_default_account_id shortcuts from the risk engine. The order-gateway fork (step 11) was explicitly put on hold pending this work: without the accounts abstraction the edition "is gonna really have a bad time." See the account-keying threads in the multi-accounts plan.

Provisioning, by contrast, is an ops/admin workflow, not an API: Bitnomial's public docs expose no programmatic user/account/connection creation, so AX stores Bitnomial account/connection mappings in Postgres and treats creation as a portal/manual step until Bitnomial ships an onboarding API.

§2eMargin, positions & reconciliation authority

Positions are a solved problem — exact dead reckoning, reconciled daily. What is intentionally unresolved is final authority over margin, and the reason is structural, not a missing feature.

No direct access to authoritative margin until AX is a direct DCM entity. Bitnomial's internal margin / settlement APIs sit behind auth tied to contract execution. As an IB in the interim, AX does not get those directly — the authoritative margin/settlement numbers are reached through the FCM (Dorman), via GMI on the daily cycle. There is also no confirmed public Position REST API, so the daily FCM reconciliation is also AX's position cross-check.

Working model, therefore — and note the deliberate split between positions and margin:

  • Positions are exact, not approximate. AX dead-reckons positions precisely from its own order/fill stream (the authoritative FIX drop-copy lifecycle) and reconciles them against the FCM record at end of day. The goal is an exact intraday position that ties out daily — not a best-effort estimate.
  • Margin is the approximate part. AX may ultimately run a different risk model than the FCM, so the intraday margin/equity figure is an internal estimate used for AX's own risk controls — it is not expected to be byte-identical to the clearing margin.
  • FCM / Bitnomial daily margin and balance state is authoritative for settlement; daily reconciliation corrects any position drift and is the only window onto the authoritative margin/settlement numbers (reached via the FCM, not a direct Bitnomial API).

The residual correctness risk is concentrated in margin, not positions: the intraday margin view has no independent intraday cross-check, and the contract-multiplier handling in margin/PnL (A-3544) is a known open gap on top of it.

§2fAX API semantics over BTP

We preserve AX's two-order replace model at the client/API boundary even though Bitnomial modifies orders in place. A replacement creates a new AX order_id; venue-side it's a BTP modify against the original Bitnomial orderId with a new modifyId.

EventAX behavior
Replace ack / FIX reportold AX order → terminal Replaced; new AX order → Accepted
Replace rejectreject the new AX replacement order; leave the old order live

Unsupported or unconfirmed AX features are rejected (or documented) rather than faked:

  • post_only — reject unless Bitnomial confirms BTP support.
  • GTC — reject for BTP unless confirmed; public BTP documents only Day and IOC.
  • Custom self-trade-prevention flags — reject/ignore with clear docs; Bitnomial SMP is always on and not caller-configurable.

Cancel-all is implemented by iterating known open orders per connection unless Bitnomial provides an admin cancel API. A reported (unanswered) concern: order state after partial fills, and after a replace mid-partial-fill, was historically hard to read on Bitnomial — see §3.4.

§2gService fork map & branch reality

The components and how each maps to an EP3 service — plus where the code actually lives today.

ComponentForked fromWhere it lives now
rs/bitnomial (ax-bitnomial / ax-btp)new shared cratemain (step 1)
api-gateway venue seamnot forked — single AX_MATCHING_ENGINE switchmain (step 5)
aiex-marketdata-publishermarketdata-publisherababkin/md-btp-fork-2-service (open #1944)
aiex-risk-engineax-risk-enginefork on main; cutover on the drop-copy branch
FIX drop-copy / fill ingestionnot EP3 trade-engine literally (single-sided)main (merged #2138)
bitnomial-order-gatewayorder-gatewayababkin/bitnomial-order-gateway stack (open, held)
The branch reality is the project's biggest process risk. Two long-lived integration branches — tin/bitnomial-fix-dropcopy and ababkin/md-btp-fork-2-service — absorbed many PRs that showed as "merged" but were not on main. The first root, drop-copy #2138, landed on main 2026-06-07 (with its branch-local PRs), so the 6-deep risk-engine cutover stack rooted on it can now rebase onto main; only the md-btp root #1944 remains off main. The api-gateway seam (epic A-3489) is the only Urgent epic and is the integration point they all converge on.

§2hConnectivity & environments

Network and environment facts that shape testing and deploy:

  • Order entry is private-only — BTP order entry and FIX drop copy run over PrivateLink / VPN / DirectConnect, not the public internet. Dev started on VPN/Tailscale; prod targets PrivateLink. Market data is available over the public WebSocket.
  • Session identity — SenderCompID is ARCHITECT; connection id 79 (parsed as hex, not decimal) plus an auth token. Env vars standardize on a BTNL_* prefix; the edition is selected by EXCHANGE_EDITION=aiex.
  • REST catalog is served (currently unauthenticated, internet-only, not yet on PrivateLink) from 85is.net: /product/specs/, /product/data/{id}. Bitnomial manages product provisioning on the test env.
  • Sandbox vs prod is murky — orders against sandbox product IDs were rejected ("account not found") while a prod product went through; the team was directed to the prod /product/specs path. Carries a real risk of accidentally trading against prod products in test (§3.6).
Migration safety. A near-miss was caught where Atlas/init.sql hardcoding the ax database would have dropped ~29 live aiex ClickHouse tables. Keep aiex schema/migrations isolated from the live AX database.

§3Open Questions

The questions the code can't answer — most are dependencies on Bitnomial. Each carries its current resolution.

  1. Can one BTP order-entry connection route to multiple clearing accounts? And where is the account specified?
    • Answered — yes Bitnomial confirmed a single connection can specify the account via an "attributed open" message (reserved for FCM-class partners), confirmed live in UAT. The AttributedOpen body shipped in #2208. AX runs one omnibus connection and owns subaccounting (§2c).
  2. Does BTP support GTC or post_only outside the public docs?
    • Not yet answered No answer from Bitnomial in any channel. Public BTP documents only Day and IOC. Current plan: reject both at the API boundary until confirmed (§2f).
  3. What is the authoritative margin/settlement API, and the recovery process after disconnect/crash/DR failover?
    • Answered — blocked Bitnomial's margin/settlement APIs are gated behind contract execution; AX gets no direct access as an interim IB — the authoritative numbers come through the FCM (Dorman) via GMI on the daily cycle. No confirmed public Position REST API. The accepted model: positions dead-reckoned exact + reconciled daily, margin approximate (possibly a different risk model than the FCM). The residual risk lives in margin, not positions (§2e, step 13).
  4. How does order state read after partial fills, and after a replace mid-partial-fill?
    • Not yet answered Raised from an ex-Bitnomial market-maker's report that this was historically hard to determine; the thread to Bitnomial went unanswered. An unmitigated protocol risk on the order-gateway lifecycle path — needs a definitive spec answer before production (§2f).
  5. Does Bitnomial expose programmatic account / connection / risk provisioning & onboarding APIs?
    • Partially — coming Not today. Bitnomial says they'll expose the onboarding API "as soon as they can"; the institutional portal link is also outstanding. Interim: a manual/portal ops workflow, mappings stored in Postgres (§2d, step 14).
  6. What are the sandbox limitations vs prod for production-like testing?
    • Partially answered Real confusion observed — sandbox product IDs rejected ("account not found") while a prod product went through; team directed to the prod /product/specs path. The sandbox/UAT/prod boundary remains murky, with a live risk of submitting against prod products in test (§2h).
  7. How long does the Bitnomial Edition run before cutaway to Architect's own DCM?
    • Strategic — open By design, "as soon as regulatorily possible" once Architect's own DCM is live. The fork-don't-abstract strategy (§2a) is the bet on this being short. The actual window depends on the DCM standup and the Bitnomial LOI (exclusivity, no certification-timing commitment, assignable to an affiliate) — outside engineering's control (step 15).
  8. Internal gate: must the account/identity rekey land before the order-gateway?
    • Answered — yes Yes. The order-gateway stack was explicitly put on hold pending the accounts rekey; without splitting identity from ownership and removing from_default_account_id shortcuts, the edition "is gonna really have a bad time." This is the internal critical path (§2d, steps 11–12).

§4Documentation

The reference material this project is built against — internal design docs, the in-repo BTP specs that are our source of truth for the wire, Bitnomial's public API docs, and the related AX plans.

Internal — design & specs

  • RFC: Bitnomial DCM Integration — the founding design (docs/rfc/bitnomial-dcm-integration.md); this plan tracks its execution.
  • BTP wire-format referencers/bitnomial/BTP_SPEC.md; the source of truth for every byte the ax-btp codec and ax-btp-mock emit/accept.
  • BTP REST catalog specrs/bitnomial/BTP_REST_SPEC.md; the authoritative reference for the GET /product/specs/ JSON used by instrument loading.
  • BTNL_FORK.md — fork reference doc. on develop-bitnomial — not on main

Bitnomial — public API docs

Linear — epics

EpicAreaOwnerStatus
A-3489 P1 / urgentAPI Gateway: Bitnomial venue supportLoc NguyenIn Progress
A-3470Risk-engine parity for Bitnomial editionTin ChungIn Review
A-3448bitnomial-order-gateway (BTP + FIX drop copy)Andrew LeeIn Progress
A-3311Multi-backend market data (EP3 + BTP)Alex BabkinBacklog
A-3486Load Bitnomial instruments into the API gatewayLoc NguyenIn Progress
A-3348BTP port of trade-engine2m.reesIn Progress

All under the Perpetuals exchange project · Integrations milestone. There is no standalone Bitnomial Linear project — issues carry the AIEx label.

Related AX plans

  • Multi-Accounts — the identity/ownership rekey the Bitnomial account mapping rides on (the internal critical path, §2d).
  • Position Feed Promotion — the EP3-side position-cache cutover; adjacent risk/margin work.