Futures Trading 6.2.0

26 August 2026

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

FUNDINGORDER-BOOKMARGINMARK-PRICEPOSITIONSLIQUIDITYCORRECTNESS

Futures v6.2.0

Release Date: August 26, 2026 Tags: FUNDING, ORDER-BOOK, MARGIN, MARK-PRICE, POSITIONS, LIQUIDITY, CORRECTNESS

Overview

Perpetual contracts now actually fund, reduce-only means what it says, and a stop you set on a position survives a refresh. Positions never accrued funding at all before this release, and the endpoint professional counterparties read served a hardcoded zero rate to every one of them. The stop-loss and take-profit controls on the positions panel changed local state and made no request, so a trader set a stop, saw it, refreshed and had none.

The order book also learned who wrote each of its levels. Every path that prices money — the margin hold on a market order, the maker/taker decision, the mark price that liquidates — now reads only depth a trade can actually happen at. That was a precondition for the AI Market Maker quoting futures, and it lands here first, before anything can write a level it would have to be protected from.

Nothing changes on an install with no market maker on a futures pair. Funding is off on every market until you set a rate. Read Upgrade Notes — funding moves money between traders and needs a decision. Requires Core v6.7.2.

Update Instructions

pnpm updator

Boot once with database sync enabled: this release adds a funding-payment table. Then restart the backend and ship the frontend build.


Upgrade Notes

Funding is off until you set a rate, and it moves customer money

  • Every existing market starts with no funding configured, and a market with no rate settles nothing. Update is safe with no action.
  • To switch it on, set the rate and interval per market at Futures → Markets → (market) → Funding. A positive rate means longs pay shorts; a negative rate is the reverse.
  • The rate is a percentage of position value per interval, not per year, and it is capped at 0.75% per interval. That cap is not a style preference: the value is multiplied by every open position's notional, and a misplaced decimal on a market carrying 50x positions is an account-emptying event that looks exactly like a working settlement.
  • The interval is 1 to 24 hours and defaults to 8. Settlement happens on fixed UTC boundaries, so an interval change moves when the next one lands.
  • Each settlement writes a payment row per position. Customers see their own under the position; you see all of them in the funding history.

Reduce-only orders are now real, and your terminal has been sending the flag

  • The Pro terminal has put reduceOnly on every futures order for a long time and the route never read it. A trader turning it on got no protection and no error.
  • It now behaves as its name says: the order may only shrink an open position, and it is refused when there is nothing to reduce.
  • This is separate from a liquidation. The same column was previously written only by the liquidation engine and read by five places as meaning "this is a liquidation" — so a trader's reduce-only order would have been retired on restart, kept out of the book, and stamped the position LIQUIDATED on fill. Those five readers now key on liquidation, not on reduce-only.

Added

Perpetual funding

Until now, positions never accrued funding, and hb/perpetual/funding-rate served a constant lastFundingRate: "0" to every professional counterparty that asked — in a field they price against.

  • Added an operator-set funding rate and interval per market, settled on fixed UTC boundaries by a scheduled job.
  • Added a funding-payment record per position per settlement, and a funding history endpoint behind it.
  • Added funding fields to the market create and edit screens, with the cap and the interval bounds enforced there as well as at settlement.
  • Changed the Hummingbot funding-rate endpoint to report what the market is actually configured to charge and when it will next charge it. A market with no funding still answers zero — but that is now the truth about that market rather than the truth about the platform.
  • The rate here is operator-set, not premium-driven. This venue's book is its own price and it has no external index, so nothing here should be described as a funding rate calculated from a spot premium.

Reduce-only orders for traders

reduceOnly was not a new column. It existed all along, was written by exactly one caller — the liquidation engine — and was read by five places to mean "this is a liquidation": the matcher would not publish it as depth, a restart retired every open one, the fill decrement skipped it, the matched-price carve-out treated it as the aggressor, and the fill handler clamped its credit to margin and stamped the position LIQUIDATED.

Every one of those is correct for a liquidation and wrong for a trader closing their own position, which must rest, must be allowed to profit, and must not mark anything liquidated.

  • Added a separate flag for orders the liquidation engine places, so reduceOnly goes back to meaning only what its name says.
  • Added the refusal a reduce-only order needs: no position to reduce, no order.
  • The margin hold on a reduce-only order is sized as an exit rather than as new exposure.

Stop-loss and take-profit on an open position

The controls existed on the positions panel and did nothing but change what was drawn. A trader set a stop, watched it appear, refreshed the page and had none.

  • Added the route behind them, so a stop or target set on a position is stored and honoured.
  • Omitting a value keeps whatever is stored; sending an explicit empty value clears it. The write sets both columns, so collapsing those two cases would have silently deleted a stop the trader never touched.

A paged open-order read for one account

  • Added a single-partition read of one user's open futures orders, paged to the end. The two reads available before it answered a near-enough question at a cost nothing on a timer can pay: one walks the account's entire trading history and filters in memory, the other scans every user on the install.

Changed

Money paths price against order-backed depth only

  • Changed the market-order sweep to walk only order-backed levels, paging until it has enough of them rather than taking a flat window. 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. It also now distinguishes "the book genuinely cannot fill this" from "we stopped looking", because those are opposite instructions to a trader.
  • Changed the maker/taker decision to read the real touch. Off a book whose top is synthetic, an order that goes on to rest reads as crossing and is charged the taker rate for liquidity it is in fact providing.
  • Changed the mark price to take its book midpoint from backed depth only. The mark is what liquidates positions, and a mark taken from levels nothing can trade against liquidates 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 promoted to permanent order-backed depth by the first real fill at its price, with nothing to report it and no sweep able to remove it.

An orphaned level can no longer price an order

  • Added a check against the matching engine's own resting orders. The TTL filter answers "did the market maker write this level" and nothing else — an ORPHANED level, whose orders are gone but whose row survived a lost decrement or a mid-fill crash, carries no TTL either and read as perfectly real.
  • Changed the market-order sweep to count only depth the matcher actually holds, and to walk deeper when it meets a ghost. A level advertising more than is resting behind it stopped the walk early, which set the order's per-unit cap too low — and the deeper fills were then refused for insufficient margin, with the sweep half-done and the rest of the hold stranded.
  • Changed the maker/taker touch read to step over a ghost rather than price from it.
  • A disagreement between the book and the engine is never a REFUSAL: the level is skipped and the next one used. Where the engine cannot answer at all — a follower process, a symbol it never initialised — every level passes through exactly as before.

The level writer can mark what it writes

  • Changed the aggregated level writer to accept a TTL, which is the provenance marker the whole convention rests on. Order-backed callers pass nothing, exactly as before.

A market with a market maker on it cannot be bulk-deleted

  • Changed market deletion to refuse while an AI Market Maker still names the market. Unwinding a maker means cancelling its resting orders, waiting for its positions to close and paying its balance back — the market maker's own delete door does all of that in order and reports what it could not do. A bulk delete is not the place to liquidate a funded pool.

Fixed

Publishing a resting order no longer reads the whole market's depth

  • Fixed the read behind publishing a new resting order. It writes exactly one row and needs exactly one number to do it, and it was paging the entire symbol — both sides, every level, each converted through BigNumber — then indexing one key out of the result and discarding the rest, on the placement path of every order that rests. Two makers quoting one market did not merely add their own writes; they enlarged the read that every other writer on that market performed. It is now a single-row read naming the whole primary key.

Cancelling an order that never rested took somebody else's depth

The cancel path decremented the aggregated level unconditionally, without asking whether the cancelled order had ever been published into it. An order that never rested owns none of that size, so subtracting anyway deleted another trader's resting size at the same price.

  • Fixed the cancel path to decrement only what the order actually still has resting, using the same predicate the matcher publishes by.
  • This was reachable before reduce-only existed: a liquidation order carries the trader's own account id and an open status, so a trader could cancel their own in-flight liquidation and take a stranger's level with it.

An order that never rested no longer gives back depth it never had

  • Fixed the cancel path's level decrement where the level is synthetic. The order owns none of it, and subtracting anyway deleted display depth the market maker still believed it held while writing the remainder back without its TTL.