Futures Trading 6.1.6

11 August 2026

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

MONEYFUTURESMATCHING ENGINEMARK PRICELIQUIDATIONINSURANCE FUNDRISKADMINSETTINGSWEBSOCKETSPERFORMANCEBUG-FIXES

Futures v6.1.6

Release Date: August 11, 2026 Tags: MONEY, FUTURES, MATCHING ENGINE, MARK PRICE, LIQUIDATION, INSURANCE FUND, RISK, ADMIN, SETTINGS, WEBSOCKETS, PERFORMANCE, BUG-FIXES

Overview

Leveraged futures trading on this platform could pay out money that was never deposited. Measured on a single trade: two traders posted 60 each against 300 of notional, and closing both sides paid out 160. The 40 came from nowhere, and every request answered successfully while it happened.

That case had two causes, and both are closed here; an audit then found more paths besides. Positions were valued at the last trade printed on this platform's own book, so one order touching a thin level decided what every position on that market was worth. And when a position closed owing more than the margin behind it, nothing absorbed the difference — it was simply created. This release adds a fair mark price, liquidates earlier so a liquidation can actually fill, and puts an insurance fund behind the gap, funded by withholding a tenth of the futures fee the platform already collects, not by operator capital.

Your futures fee revenue and your traders' liquidation prices both change, and margin stranded on a market you deleted before today has to be returned by hand — read Upgrade Notes before comparing anything against last week.

The Ecosystem extension is required, and its 6.3.9 release carries the matching repairs to the spot engine this desk settles against. Requires Core v6.6.3.

Update Instructions

pnpm updator

The update creates one new table, the futures insurance ledger, while it applies the schema, and a fresh installation now imports it with the rest of the schema rather than waiting for the first start to add it. No seeder is required.

If an existing install is updating with schema sync switched off (DB_SYNC=none), start the backend once with it on before trading resumes. Without that table the fund cannot record anything: every settlement still succeeds and no trader sees an error, but nothing is written, the fund can never read as short so the forced close described below never fires, a per-market limit you have set never trips — and the share withheld from every futures fee still leaves the platform wallet with no ledger row saying where it went. It is not silent: every entry that cannot be written is logged as critical with what it would have said, and the admin futures dashboard reports the fund as unreadable rather than as empty.

Three new settings ship with defaults chosen so a fresh install is safe before anybody touches them. Two of them change behaviour on day one, and all three are in Upgrade Notes. None of the three has a field on an admin settings screen in this release — each is read from the platform's settings store, so changing one means writing that key there.


Highlights

Money was being created and nothing reported a problem

This release began as a single performance change to the matching engine and became the largest money-correctness release the futures product has had. What turned it was adopting one statement as the definition of correct and checking it after every single futures operation: every wallet balance, plus the margin posted on every open position, plus the margin held by every resting order, plus the insurance fund, adds up to the same number it did before. Counting wallets alone — which is all anyone had ever done — can only reveal a breach once both sides of a trade have closed, which is why none of this had ever surfaced.

With that in place, a 46-agent adversarial audit — read-only, five lenses, every finding put to two independent skeptics whose default answer is "refuted" — went looking for paths that could still create money on top of the fixes already made. It found more, and every one it confirmed is fixed here. Its most productive question was not "what is broken" but what would a broken version of this look like, and would anybody notice? — which is how the stop-loss path was caught. It had been creating money on ordinary price movement the whole time, and every check that had ever been pointed at it looked only at one trader's wallet, where the created money is invisible.

One finding is worth recording because the first fix for it was worse than the defect. Guarding a position's status with a lightweight transaction in the fast store made a closed position still read as open, and a flat round trip lost a whole margin. It was reverted and replaced with a uniqueness constraint in the main database, which is strongly consistent. Nothing here shipped on the strength of an agent's report; every fix was reproduced first.


Upgrade Notes

A tenth of every futures trading fee is now held in reserve instead of booked as profit

Nothing extra is charged to any trader. The fee is collected exactly as before, and a share of it is then moved out of the platform's own wallet and recorded as a reserve, so that when a leveraged position closes owing more than its margin there is real money to fund the difference. This is what makes the fund cost the operator nothing to capitalise — but it does mean the futures fee revenue landing in the platform wallet drops by that share.

  • The share is futuresInsuranceFeeShare, a percentage, defaulting to 10. Setting it to 0 disables the withholding entirely and the fund stops growing.
  • The share is taken from the same platform wallet the fee was credited to, in the market's quote currency. Your futures fee income for a period will now read about a tenth lower than the same period before this update, and the difference is on the insurance ledger rather than gone.
  • If a market has been suffering deficits, raising this share is the lever that pays for them.

Traders are liquidated earlier, and the liquidation price on their screen moves with it

A liquidation places a real order and has to fill against whatever is resting. The gap between the liquidation point and total loss of margin is that order's entire budget for crossing the book, and on a thin white-label book a tenth of the margin was not enough — a liquidation that fails to fill falls back to settling against the mark, which is precisely how a shortfall is born.

  • Full liquidation now fires at 80% of posted margin lost, previously 90%. Partial liquidation fires at 60%, previously 70%.
  • The liquidation price shown on a trader's position, and the margin-health bands on the admin futures dashboard, are both derived from that threshold and move accordingly. An open position taken before the update will show a liquidation price nearer its entry price than it did yesterday. The position itself is unchanged; the point at which the platform will close it is.
  • The trade-off, stated plainly: a trader is closed somewhat sooner than before. An early liquidation returns the remainder of their margin to them, where a failed one takes all of it and leaves a debt besides.

A position in profit can now be closed early to cover somebody else's bankruptcy

When a position closes owing more than its own margin and the insurance fund cannot cover the difference, the platform closes the most profitable positions on the opposite side of that same market, ranked by profit and leverage, until the gap is covered. They are settled at the price at which the bankrupt account's margin ran out — a real price, at which the money genuinely exists — and the trader keeps every cent of it. What they do not receive is the rest of a move that nobody on the other side could ever have funded.

  • This is futuresDeficitPolicy, and it defaults to adl. The alternative, absorb, lets the fund go negative instead: the operator carries the debt knowingly, and the ledger shows exactly how much of it there is. Neither setting creates money.
  • Nothing is clawed back from profit a trader has already realised and been paid. Only currently open positions on the same market and the opposite side are eligible, and only those in profit.
  • Traders are not notified in the app when this happens. The event is on the insurance ledger with the position, the market and the price it settled at, and it is written to the server log.

Margin on markets you deleted before this update is still stranded, and this release cannot return it

Deleting a futures market was supposed to cancel every order resting on it and refund the margin. It never did — the cleanup was looking for the market under the base asset alone rather than the full pair, matched nothing, and reported success. Every resting order on a deleted market kept its margin held against a market that no longer existed: no book, no fill, no close and no route back to the trader. The orders, positions and candles all survived too, so re-creating the same pair inherited the previous market's book.

That is fixed for every deletion from now on, and the fix cannot reach backwards.

  • If you have ever deleted a futures market on this install, look for futures orders and positions whose market no longer exists and return that margin to those traders by hand. The customer has no way to see it and no way to ask for it back.
  • Re-creating a pair you previously deleted is safe again. Until now the new market could match an order left over from the old one, at the old market's prices, against traders who had walked away from that trade long ago.

Placing a second order at a different leverage on a position you already hold is now refused

A fill merges into an existing position on the same market and side, and the merge kept the first position's leverage while the new order's margin had already been taken at the second. The two then disagreed permanently, and the close paid out against the wrong one — money created in one direction, destroyed in the other, with nothing announcing either.

  • The order is rejected at placement with a message naming the leverage the held position is on, so the trader can either match it or close first. There is no change to a position already open.

A market can be made to stop opening new positions once its losses pass a limit

  • futuresMarketDeficitLimit is the cumulative shortfall, in the market's quote currency, past which that one market stops accepting new positions. Closing an existing position is always still allowed, so suspending a market never traps money already in it.
  • It is off by default (0), deliberately: a threshold nobody chose will suspend a healthy market one morning. The figures the admin futures dashboard now reports show what it would have caught, so a value can be picked from real numbers rather than guessed — they are in the dashboard's data and on the ledger, and the screen does not draw them yet.

Added

An insurance fund, recorded as a ledger rather than a balance

Leverage lets a trader promise more than they deposited. When the price moves further than their deposit covers, the trader on the other side is owed money that does not exist. Liquidation is the first defence and closes the loser while their margin still covers what they owe; a genuine gap, or a book too thin for the liquidation to fill in, jumps straight past it. What is left over has to come from somewhere real, and until now it was created.

  • Added a futures insurance fund, held per quote currency, capitalised by withholding a share of the futures fee the platform was already taking. It requires no capital from the operator and takes nothing from any trader.
  • Added a permanent row for every movement through it, naming the position, the market, the price it settled at, the amount, and — separately — the part of a loss that ran past the position's own margin. The balance is the sum of those rows and nothing is ever edited in place, so a fund that looks wrong can be reconstructed. Two figures rather than one, because the balance also holds value passing through on behalf of open positions, and a healthy balance can hide a growing bill.
  • Added a negative balance as a legitimate, visible state. It means the operator is carrying a debt their reserve did not cover, which is exactly the thing they need to be able to see.

Every close now clears through the fund, not only the ones that go bankrupt

Recording only the bankrupt closes does not balance: a liquidation on a gapping price left 20 unaccounted for against a shortfall recorded as 15. A loser's realised loss is not credited to anybody at the moment it is realised — it is owed to a trader on the other side who has not closed yet — so it left the wallets and landed nowhere, and that winner was later paid out of nothing.

  • Added a clearing entry at every door a position can leave by: the manual close, a stop or take-profit, a full liquidation, a partial trim and a forced close. The fund receives what a losing trader realised, pays what a winning one is owed, and receives the whole margin of a trader who lost more than they had.

Auto-deleveraging, so the guarantee does not depend on the fund being big enough

  • Added the forced close described in Upgrade Notes, wired into both doors a bankrupt close can arrive through — the liquidation's fallback settlement, and a manual close on a market that gapped. Without it, closing a position by hand was the one remaining door through which money could still be created.

The fund's figures on the admin futures dashboard

  • Added the fund's balance per currency, the total shortfall that leverage has cost, and the markets producing it, to the figures the admin futures dashboard reports. A read failure returns nothing rather than zeros, because "I could not read this" and "there is nothing here" must not look the same on a risk console. The dashboard screen does not draw these yet — they are in its data, and on the ledger.

Changed

Live market prices on the markets list

  • Changed the futures matching engine to broadcast the markets-wide ticker snapshot once per matching cycle instead of once per traded symbol. That snapshot covers every market whether or not it traded, so the extra copies carried nothing the first one did not — on a cycle where several markets matched at once, the engine built and sent the same table several times over. Each market's own ticker is still sent per symbol, exactly as before, so a market page updates on the same tick it always did.

One account can no longer be both sides of a trade

The fill decision was made on price alone, and the check that used to make an account crossing itself impossible had been removed from order placement. Nothing was left to stop it. That is not a fairness nicety: a fill writes the market's last price and its current candle, two of the three readings the new mark price is built from, and the mark decides who is liquidated and what every close pays. On a market whose price limits are left open, the price such a trade could print at was unbounded.

  • Changed the matcher to skip a match where both sides belong to the same account, taking the older of the two out of contention so the queue keeps moving and the newer order stays available for a genuine counterparty.

Futures position updates travel on the order stream

  • Changed where futures position updates are published, from a market address that has no socket behind it to the futures order stream — the same one their sibling order updates already use, and where a client watching its own futures activity is already connected.
  • Removed a second, duplicate order broadcast to that same unreachable address. Order updates were never affected by it: they were always also sent to the working stream, which is the only reason nobody noticed.

Fixed

Every open futures position reported no profit or loss, for as long as it was held

The profit figure on an open position was a stored value refreshed only when a trade happened to touch that position — and a position is opened with its mark equal to its entry, so the value was stamped at zero the moment it was created and stayed there. The trading screen derived the mark price from that figure, so a frozen profit froze the mark price too, and with it the displayed distance to liquidation. Meanwhile the close endpoint settled against the live market, so the "Estimated PnL +0.00" in the close dialog was never what the trader was actually paid. A leveraged position could run all the way to liquidation with the panel showing nothing moving.

  • Fixed open positions are now valued against the live market each time they are read, and the price used is reported alongside the figure so the screen no longer has to work backwards from the profit to guess it.
  • Fixed closed positions keep their stored figure. That is their realised result, stamped at settlement, and re-valuing it would overwrite history.
  • The panel values an open position at the last price traded on that market, while a close settles at the fair mark described below, so the two can still disagree where the last print sits away from the resting book.

One order could decide what every position on the market was worth

Every place a position was valued read the last trade on this platform's own book. A market buy for 2 sweeping asks at 100 and 200 opened both sides at the correct 150 average — and then marked every position on that market at 200, a price that existed only because that order had reached it. The trader on the other side was instantly 100 down on 60 of posted margin, and closing both sides paid out 160 against the 120 ever deposited.

  • Fixed positions are now valued at the median of three readings of the book — the last trade, a weighted close of the current minute, and the midpoint between the best bid and the best ask — whenever a two-sided book exists to disagree with a single print. With two sides but no recent candle the mark sits halfway between the last trade and that midpoint, and with no two-sided book at all it is the last trade. On the case above the mark is 150, both sides settle flat, and it conserves to the cent with no fund involved at all.
  • Fixed the liquidation and stop sweep, the manual close, and the risk check the matching engine runs immediately after a fill all use that one valuation. The last of those was the fastest settlement path in the exchange and was the only one still deciding on a raw print.
  • This is not an index price and does not pretend to be. A real venue marks against other exchanges' prices, which nothing happening here can move; a market whose only venue is this install has no outside price to read. What this stops is a single order deciding the mark. It cannot stop a genuine move past what a losing trader deposited — that is what the fund and auto-deleveraging are for.

A stop-loss or take-profit created money every time it fired

Stop-loss and take-profit were the one full settlement path that never cleared through the fund. A close removes the position's margin from the platform's obligations and pays the trader their margin plus their profit, and that profit is owed by somebody who has not closed yet — so with nothing recording it, the difference was created or destroyed on the spot. On the worked example of a long of 2 at 150 on 5x taking profit at 200, that is 100 created, per exit, on ordinary price movement rather than on any edge case.

  • Fixed stop and take-profit exits clear through the fund like every other close, including the exits that pay nothing at all — a trader who loses everything forfeits their whole margin to the fund, and gating on the payout would have made the single largest loss the one case that went unrecorded.

A cancelled order went on filling for the life of the process

Cancelling a futures order marked it cancelled and refunded the trader, and then failed to take it out of the matching engine — the comparison that was supposed to find it in the queue was made between two different kinds of value and never matched anything. The matcher went on holding it as a live resting order, and went on filling it, for the life of the process. Measured: a two-unit market buy filled both units at 100, the second against an ask that had been cancelled earlier, so the position opened at 100 instead of the 150 it should have averaged and the counterparty took on exposure with no margin behind it.

  • Fixed a cancelled order now leaves the matcher when it is cancelled. The correction is made where orders are read, so every comparison downstream of it is made on comparable values rather than each one having to remember.
  • Fixed cancelling an order on a market that has no queued orders yet — a market created since the last restart, or one just deleted — returned a server error on a path that moves money. It now succeeds.

A market order sweeping two price levels left a position with no counterparty

The refund of unused slippage allowance was keyed on the size of the individual fill, which is identical for any two equal-sized fills of the same order — and a market order walking a thin book is exactly that. The second fill was rejected as a duplicate, the engine rolled its in-memory record back, and the position had already been written. The result was a long of 2 at an entry of 100, an order recording a single unit filled, both counterparty asks still open, and the trader debited for one unit while holding two.

  • Fixed the refund is keyed on the order's cumulative filled quantity, which is unique per fill and identical on a genuine retry.
  • Fixed the same collision in the reduce-only fill a liquidation produces, where the second fill of a sweep aborted before the position was shrunk and before its clearing entry was written.
  • Fixed everything that can fail during a match now happens before the position is written, and the trade broadcast that follows can no longer unwind a fill that has already moved money.

Two market orders meeting each other printed at the buyer's price

The matcher asked only whether the seller was taking liquidity and treated every other case as the seller resting — including the case where both sides were aggressing. Two market orders therefore printed at the deepest price the buyer's sweep was allowed to reach, which can be above what the seller's own reservation covers. The position opened carrying more margin than the seller had ever posted: money the close would pay out and nothing put in.

  • Fixed the buyer's price is used only when the buyer is the one resting. Otherwise the seller's is, which the match condition guarantees is covered by both sides' holds.

Deleting a market refunded the notional, in the wrong currency, from the wrong wallet

Once market deletion was reaching the refund at all — see Upgrade Notes — that refund turned out to carry four defects at once. It refunded the full value of the order rather than the margin actually posted, so at 20x it paid back twenty times what had been taken. On a sell it paid a quantity of the base asset into a base-asset wallet, where a futures order is always margined in the quote whichever side it is on. It searched for the trader's wallet without saying which kind of wallet, which fails outright and aborted the entire market cleanup at an error — stranding every remaining order's margin. And it used an identifier no other cancellation path shares, so cancelling an order and then deleting its market paid the refund twice.

  • Fixed market deletion now refunds exactly what the ordinary cancel path refunds — the unfilled share of the margin and its fee, in the quote currency, from the futures wallet — under the identifier the other two cancellation doors already share, so all three agree and an order that leaves through two of them is paid once.
  • Fixed deleting several markets at once had the same market-lookup defect as deleting one, fixed in one place and left in the other. Both now clean up properly and both tell the matching engine to forget the market, so its book cannot outlive it in memory.

A position could be settled twice, in full

Stops and take-profits are decided from a snapshot of open positions taken at the start of a sweep, and any of those positions can be settled by a manual close or a liquidation before the sweep reaches it. The payment made no attempt to re-check, and it paid under an identifier neither of those other paths consumes — so the wallet's own duplicate protection did not overlap and a position settled mid-sweep was paid in full a second time.

  • Fixed the position is re-read immediately before it is paid, and a position no longer open is left alone. That narrows the race to the instant between the re-read and the payment rather than closing it outright; the ledger constraint below is what makes the fund side of it impossible.

Two settlement paths racing the same position could also each hand the fund that position's whole margin, because a payout of zero — which is exactly the total-loss case — consumes no duplicate protection at all. That reports a market as having cost several times what it really did, and would drive the new per-market limit off a multiplied figure.

  • Fixed one clearing entry per position is now enforced by the database itself, and a second write being turned away is recognised as the constraint doing its job rather than logged as a fault.

A stop whose write failed left the position open with the money already paid

The recovery pass that replays a settlement whose second write failed knew about manual closes and liquidations but had never heard of a stop or take-profit — the one exit that fires on ordinary price movement rather than an operator action. A position in that state stayed open forever with its money already in the trader's wallet, and the trader could close it a second time.

  • Fixed stop and take-profit settlements are recognised and replayed like any manual close.

A cancelled order's fee refund left the platform's books short

The whole fee is credited to the platform when a futures order is placed, and a share of it is then withheld into the insurance fund — so only part of it was still in the treasury when the order was cancelled and the fee refunded to the trader in full. The loss the platform books on that refund is capped at what the treasury actually holds, so on a currency the platform has only just started trading the cap bit by exactly the withheld share and the platform's own records stopped agreeing with each other.

  • Fixed the two cancellation paths that book the refunded fee against the platform — cancelling one order, and cancelling all of a trader's orders — now return the withheld share to the treasury first, in proportion to the part of the order being refunded, so a cancel is a true reversal.

A single failure could stop futures matching for the entire process

Matching locks the orders it is about to update, and that lock is all-or-nothing — it refuses the whole batch if a single order in it is already locked. The unlock ran only on the success path, while three steps that can genuinely fail sat above it. One failure therefore stalled not just that order but every future batch containing it, permanently, with nothing louder than a warning in the log.

  • Fixed the unlock always runs, whatever happens in between.

The futures markets page advertised "up to 100x" for markets that offered nothing of the kind

A market whose configuration carries no leverage levels was given 100x as a fallback, on the public markets page, so a default became a claim about the product — both on that page's headline figure and on every such market's card.

  • Fixed a market that publishes no leverage shows a dash, is excluded from the headline figure and no longer counts itself as high-leverage.