Ecosystem 6.4.6

Pre-release

This version isn’t published on updates.mashdiv.com yet — the notes are available to preview, but it can’t be downloaded until it’s released.

23 August 2026

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

TRADINGORDER-BOOKMATCHING-ENGINEMARKET-MAKINGBUG-FIXES

Ecosystem v6.4.6

Release Date: August 23, 2026 Tags: TRADING, ORDER-BOOK, MATCHING-ENGINE, MARKET-MAKING, BUG-FIXES

Overview

A trader with orders resting on both sides of a market could not place a market order at all, and every cancellation had a few seconds in which it could leave its price level permanently short. Both faults come from the same place — three different parts of the engine answering "what is actually backing this price level?" and not all giving the same answer.

Placement priced a market order against the whole book, including the caller's own resting orders. The matching engine never does that: it steps over an order from the same account and looks further down for a real counterparty. So a two-sided market maker, whose own quote sits at the touch by construction, was refused for trading against itself on a market where somebody else's liquidity was one level down. Placement now prices against the depth the matcher will actually give it, on both buy and sell.

The second fault is quieter. Cancelling takes about three seconds to return the funds, and for that window the order is out of the engine's queue but still counted in the book. Two routines that rewrite price levels wrote the level without the order, and then let the cancellation subtract it a second time.

Read Upgrade Notes before you update — levels already short are not repaired by updating, and one refusal changes its wording. Requires Core v6.7.0.

Update Instructions

pnpm updator

Restart the backend afterwards. The matching engine holds order state in memory, so these take effect when the process restarts, not when the files land. There is no database change and no migration.


Upgrade Notes

Price levels that already went short stay short until you repair them

A level that lost depth to the double-subtraction described below does not come back on its own. It is short by exactly the size of a cancelled order and nothing records that it was ever wrong, so the only way to find it is to compare the book against the orders behind it.

The repair script is the one you already have, it is non-destructive, and it is a dry run unless you tell it otherwise:

cd backend
node scripts/rebuild-eco-orderbook.mjs                    # report, every symbol
node scripts/rebuild-eco-orderbook.mjs BTC/USDT           # report, one market
node scripts/rebuild-eco-orderbook.mjs BTC/USDT --execute # apply

Restart the backend after applying. It reads the orders table directly and fixes levels that are missing, short or unbacked; orders, trades, candles and balances are untouched. Markets with an active AI market maker are skipped unless you pass --include-ai, because their book is maintained without real order rows and rebuilding from orders would erase their quotes.

Some market orders are now refused for liquidity where they were refused for self-matching

Netting a trader's own depth out of the walk means a market order that only ever "filled" against that trader's own quotes now correctly finds there is not enough of anybody else's depth to fill against. That is a different refusal with a different code, and it is the honest one.

  • Changed the refusal a bot or an API client sees in that case from a self-match rejection (400) to insufficient liquidity (422). Anything that branches on the status code will see the change.
  • Changed the message to say how many of the price levels it walked were the caller's own resting orders, so the answer is actionable rather than contradicting a book the trader can see their own orders in.

Market order pricing and holds move for anyone quoting both sides

A market order is now sized and priced from the depth the matcher will actually give it. For a trader with no resting orders on the market, nothing changes at all. For one quoting both sides, the worst-case price and the funds held for a market buy are computed from other parties' liquidity only, so both figures can differ from what the same order would have produced before.


Fixed

A two-sided trader could not place a market order

The read that prices a market order took no account of who was asking. It walked every level of the book, including the caller's own, and the placement guard then compared the resulting price against that same caller's open orders and refused: "You already have an open SELL order at …".

For a market maker resting on both sides, its own quote is at or near the touch almost all the time, so this refused essentially every market order it tried — against its own liquidity, on a market where a real counterparty was sitting one level further down. The matching engine would have skipped straight past that order; only the placement layer did not know it.

  • Fixed market BUY being priced and sized against the caller's own resting depth. The sweep that computes the worst-case price and the funds to hold now nets the caller's own orders out of every level it walks.
  • Fixed the same fault on market SELL, which takes the best bid as its touch and was equally likely to find the caller's own bid sitting there.
  • Added a guard so this only relaxes the self-match check where it has been earned: depth the engine could not judge — levels beyond the loaded window on a very large market — has nothing netted out of it, and a walk that reaches one of those is still checked the old way. Nothing is waved through on assumption.
  • Changed the ghost-level warning to leave these levels alone. Depth held back because it belongs to the caller is healthy, funded, order-backed liquidity, and reporting it as unbacked would tell every two-sided trader that their own quotes need the repair script.

A cancelled order could leave its price level permanently short

Cancelling takes the order out of the engine's queue first and returns the funds afterwards — measured at three to four seconds on a live install, most of it the wallet refund. During that window the order is gone from the queue while its size is still counted in the aggregated book.

Two routines that rewrite price levels summed the queue alone during that window. Each wrote the level without the order being cancelled, and then the cancellation's own subtraction removed it a second time, from an already-reduced figure. The level ended short by exactly the cancelled size, and because both routines write the level's whole amount rather than a change to it, nothing was left to say it had ever been wrong.

  • Fixed the five-minute reconciler double-subtracting an order that is mid- cancellation. It runs over every symbol on a timer, so a trader who cancels continuously gave every round a fresh chance to land inside the window.
  • Fixed the stale-level repair doing the same thing.
  • Changed all three places that sum resident depth — pricing, the reconciler and the repair — to fold in orders that are mid-cancellation. They answer one question and now give one answer.

The stale-level repair could delete real depth on a very large market

A market with more than 25,000 orders on a side loads only the part of the book nearest the touch. The stale-level repair computed a level's whole amount from the orders it held in memory — which on such a market is not "what backs this level", it is "the part of what backs this level that happens to be loaded". Where the loaded part summed to zero, the repair deleted the level, taking real funded depth with it.

  • Fixed the repair by having it skip a market whose book is windowed. The five-minute reconciler owns that case and repairs those levels properly.
  • Applies only to markets past 25,000 orders on one side. Nothing changes on a market below that, which is every market on a normal install.