AI Market Maker 6.2.2

26 August 2026

This release has upgrade notes. Read them before updating — they describe behaviour changes that need your attention.

MARKET-MAKINGFUTURESORDER-BOOKLIQUIDITYRISKMOBILECOMPLIANCE

AI Market Maker v6.2.2

Release Date: August 26, 2026 Tags: MARKET-MAKING, FUTURES, ORDER-BOOK, LIQUIDITY, RISK, MOBILE, COMPLIANCE

Overview

The market maker can now quote perpetual futures markets, not only ecosystem ones. Until this release the add-on could describe nothing but an ecosystem market: its market reference was a foreign key into ecosystem_market and its engine wrote to the ecosystem keyspace unconditionally. An operator who bought Futures, listed a pair and pointed a market maker at it had no way to do so.

A maker is now created against a venue as well as a market. On Futures it posts real margin from its own account, and a fill opens a real position that is marked, funded and — read this twice — liquidated on the same terms as any trader's. That money is the operator's either way, and the alternative is an unliquidatable position accruing a loss nothing bounds.

One decision comes with this release: the market maker and a published mobile app are now mutually exclusive. There is also a one-off migration to run. Read Update Instructions, then Upgrade Notes. Requires Futures v6.2.0 for the futures venue and Core v6.7.2; an install without Futures is unaffected and every existing market keeps working exactly as it did.

Update Instructions

pnpm updator

Then, once, on an install that has ever run a market maker:

node backend/scripts/ai-mm-polymorphic-market.mjs --apply

Restart the backend first so the model sync adds the new columns, then run the script, then restart again. It drops the foreign key that used to bind a market maker to ecosystem_market — an automatic schema sync cannot remove it, and while it is there the first attempt to create a futures market maker fails with a foreign-key violation on a futures_market.id that certainly exists. The script is a dry run by default and reports exactly what it would change.

Existing markets need no data migration. Every market maker created before this release is an ecosystem one by definition, and the new venue column defaults to that.


Upgrade Notes

The market maker will not run on a platform that ships a mobile app

An order book showing depth nobody will honour is "functionally deceptive" under Google Play's Deceptive Behavior policy and Apple 3.1.2(a). Unlike most findings that is not a rejection of a new submission — it removes an app that has already been approved, from your developer account, along with everything else you publish there.

So the two are made mutually exclusive by construction rather than by advice.

  • This platform ships a mobile app is a new switch at AI Market Maker → Settings → Trading → Status, and it ships off. An install that leaves it off keeps its market maker exactly as it is today.
  • Turning it on makes the engine refuse to start, asserted at boot before it even takes leadership, with the reason written to the log. There is no ordering of switches that produces both and no admin permission that overrides it.
  • Turn it on only when you have decided which of the two you are keeping. If you are already running makers, unwind them through Markets → (market) → Delete first — that door cancels the resting orders, clears the published depth and pays the pool back. Flipping the switch stops the engine; it does not unwind anything.

Leverage is a risk control, and it is refused where it means nothing

  • Futures leverage is accepted between 1x and 125x and ships at 1x. A value outside that range is refused with a reason rather than silently corrected — an operator who typed 0.5 believed something about how the market would be margined and needs to be told it was wrong.
  • It is refused outright on an ecosystem market maker, and hidden on that screen. A maker there funds its orders from pool balances rather than posting margin, so a stored leverage would be a control that looks meaningful and is not.
  • A maker at 125x is liquidated by a move of well under one percent, and it quotes both sides, so it is always holding some inventory when that move happens.

Added

A market maker can be created on a futures market

  • Added a venue step to the create wizard, offered only when the install has both. It decides which markets the picker lists, because a market id names a row in one table or the other.
  • Added a futures leverage setting, shipping at 1x. At 1x the pool posts the full notional of every order and the position cannot be liquidated by anything short of the price reaching zero; at 10x a 10% adverse move takes the whole allocation. It is hidden on ecosystem markets and refused there, because a maker on that venue funds its orders from pool balances rather than posting margin.
  • Added a Venue column to the market list, filterable. The same pair can exist on both venues with separate books and separate money, so a symbol alone no longer identifies which market a row is quoting.
  • Fixed filtering the market list by pair, which silently dropped every futures market maker from the rows and the count. A market is joined from one of two tables, and a column filter promoted one of those joins to an inner join — so the list quietly answered a different question from the one it was asked. Both aliases are now declared as one polymorphic group and the filter is applied across both.

The market maker refuses to start where a mobile app is published

  • Added a boot assertion: with This platform ships a mobile app switched on, the engine stops before it takes leadership and logs why. Synthetic order-book depth and a published app cannot coexist, and the finding that results removes a live app rather than rejecting a new one.
  • Added the switch itself, at Settings → Trading → Status, shipping off. An install with no app is unaffected.
  • Asserted in the engine rather than in the job that starts it, so the engine cannot run whatever starts it, and there is no admin who can turn it off.

Realised P&L is measured on futures too

  • Added a fill sink for futures. Realised P&L, the count of fills against customers, the profitable count and the per-trade ledger behind the daily, weekly and monthly windows had exactly one writer — the ecosystem matcher — so a futures maker reported a structural zero for all of them. Zero is the same number a maker that has never traded reports, and the same one a maker losing money reports, so nothing in the output said it was not a measurement.
  • Added a botId mark on futures orders, written only by the maker's own placement path. This engine settles the maker as an ordinary funded account, so without a mark on the order there was nothing to say a fill was the maker's.
  • Changed the accounting to ONE implementation both venues call. It used to live inside the ecosystem matcher; it now lives in the add-on that owns the tables, so "realised P&L" means the same thing on either venue. The arithmetic is pure and unit-tested.
  • A futures maker that was running before this release traded for real and recorded none of it. The analytics say so rather than reporting those periods as zero, and firstRealTradeAt gives the date its ledger actually starts.

The maker posts margin at the leverage its position will actually settle at

  • Added leverage adoption. A fill MERGES into an existing position and the merged position keeps its OWN leverage, so an operator raising leverage from 10x to 50x while a position is open would have had the maker post notional / 50 for exposure the close pays out at notional / 10. The maker now posts at the held position's leverage and takes the new setting once it is flat.

Futures quotes close a position before they open the opposite one

  • Added the split that hedge mode requires: a quote larger than the inventory held becomes a reduce-only exit leg plus an opening leg, exit first. Without it a maker quoting both sides accumulates a long and a short, each holding margin, both growing, neither closing — capital exhaustion with no losing trade anywhere in the history to explain it.
  • Added an exposure budget, measured against gross position value rather than net. A perfectly hedged book consumes twice the capital and pays funding on both sides; measuring the net would report it as risk-free. It counts resting opening orders as well as filled positions — measured against fills alone it could never bind, because the maker would rest an order, see the position unchanged, and rest another.
  • Added a refusal to quote at all when the maker cannot read what it is holding. The resting-order read refuses on failure by design, because "nothing is resting" is the answer that permits an over-close; quoting on a guess is worse than skipping a heartbeat.
  • Added exits-only degradation. When the budget is spent or the pool cannot fund the opening leg, the exit leg is still placed. Refusing the whole quote would strand the maker holding inventory it had just stopped quoting a way out of.

A futures pool holds margin in a wallet

  • Added a dedicated system account the maker trades from — no password, no wallet address, an address in the reserved .invalid domain, and no way to sign into it. It exists so the futures engine sees an ordinary funded trader, which is what makes margin, funding, ADL and liquidation work on the maker's positions without a second implementation of any of them.
  • Added a free-balance check on withdrawal, with the figure in the message. Margin behind an open position or a resting order cannot be withdrawn, and paying out against the allocation column alone would credit the operator from a wallet that does not hold it.

The maker notices its own fills

  • Added reconciliation against the futures engine's open orders. Nothing told the maker that a pool order had filled — the only fill signal in the codebase is published by the ecosystem matcher — so a filled quote stayed tracked for ever and the resting-order count never fell. On a busy market it reached the 500-order ceiling and the maker stopped quoting, with the log saying only that the ceiling had been reached.

The futures order book distinguishes real depth from display depth

  • Added a provenance-filtered reader for the futures book, matching the one the ecosystem has had since synthetic depth was introduced. A level written with a TTL is the market maker's display depth; one written without is backed by a real order.

Changed

Futures money paths now price against depth that can actually fill

  • Changed the market-order sweep to walk only order-backed levels. It sizes a trader's margin hold, and a hold sized against depth nothing can fill is an under-hold: the position ends up worth more than the margin posted for it.
  • Changed the maker/taker decision to read the real touch. Off the display book, an order resting behind synthetic depth reads as crossing and is charged the taker rate for liquidity it goes on to provide.
  • Changed the mark price to take its book midpoint from backed depth only. The mark is what liquidates positions, and the market maker quotes a fraction of a percent off the last price — so its levels sit on the touch by construction. A mark read from them would liquidate real traders at a price that never existed.
  • Changed the matching cycle's level ledger to exclude synthetic levels. Every write-back on that path is an absolute SET amount = ? inside a batch, with no way to carry a TTL — so a synthetic level that entered the ledger would be silently promoted to permanent order-backed depth by the first real fill at its price, and nothing would ever remove it.
  • Changed publishing a resting order to read ONE level rather than paging the whole symbol, and to take its base from backed depth. Reading a synthetic amount as the base advertised roughly twice what could fill and stripped the TTL on write-back.

Pool operations follow the venue

  • Changed deposits and withdrawals on a futures market to move the money as well as the column, between the operator's futures wallet and the maker's.
  • Changed a BASE deposit on a futures market to be refused with the reason. Futures margin is posted in the quote currency whichever way the maker is facing, so there is no base balance for it to become.
  • Changed pool rebalancing to be refused on futures markets. It moves a pool between base and quote to reach a target ratio; a futures pool has one currency and its exposure is a position, so there is no ratio to correct.

Money moves through the ledger, on the right venue

  • Changed the maker's trading fee to be credited to the platform treasury and to fund the insurance reserve, as the user route does. It was debited from the pool and credited nowhere — money removed from the wallet system with no matching entry.
  • Changed the cancel refund to go through the wallet ledger with an idempotency key. It was a raw balance increment: no transaction row to explain the movement, and nothing to stop a retried cancel paying twice.
  • Changed deleting a futures maker to pay out of the maker's own futures wallet. It credited the operator from an ECOSYSTEM wallet the money was never in, while the balance stayed in the futures wallet the delete then orphaned.
  • Fixed a defect in the reduce-only work shipped alongside this: the in-memory order handed to the matching engine omitted the liquidation flag that was written to its row. An absent value reads as "this IS a liquidation", so a trader's own reduce-only exit was treated as one until the next restart — not published as depth, and its credit capped at the margin the closed slice posted, which silently confiscates the whole gain on a profitable close. The row was always correct, so the symptom disappeared on restart.

Deleting a market no longer orphans its maker

  • Added a refusal. aiMarketMaker.marketId used to carry an ON DELETE CASCADE and a polymorphic reference cannot, so deleting an ecosystem or futures market would have left the maker's funded pool behind an orphan. Bulk market delete now refuses and names the door that unwinds a maker properly — cancelling its resting orders and paying the pool back — rather than liquidating real money as a side effect of a market delete.

Teardown reaches the right venue

  • Changed the emergency stop and the market delete to run the sweep that matches the venue. The ecosystem sweep against a futures market found nothing and reported zero cancelled — which reads as "this market had no quotes" rather than "this stop did not reach them", and the delete then paid the pool out on the strength of it.
  • Changed the expired-order sweep to cancel pool futures orders through the futures engine. They carry both the real-liquidity and the pool-futures flags, and the sweep tested only the first — so every expired futures quote was cancelled against the ecosystem keyspace, found nothing, and rested in the book while the retry counter climbed.
  • Changed the Scylla teardown to be venue-scoped. Two of its five steps are keyed by SYMBOL rather than market id, so deleting a futures maker reached into the ecosystem's data for the same pair — clearing a different, live market maker's order book.
  • Changed restart adoption on futures to read the engine's own open orders rather than a tracking table. Every order the maker places there belongs to one account, so the engine's view is complete — including anything a crash left behind mid-write.