Futures Trading 6.1.0

29 July 2026

CRITICAL FIXESSETTLEMENTLEVERAGEMARGINLIQUIDATIONVALIDATIONKYCRECONCILIATIONPERFORMANCEADMINUI

Futures v6.1.0

Release Date: July 29, 2026 Tags: CRITICAL FIXES, SETTLEMENT, LEVERAGE, MARGIN, LIQUIDATION, VALIDATION, KYC, RECONCILIATION, PERFORMANCE, ADMIN, UI

Overview

A full end-to-end review of the futures addon.

Leverage was not implemented. It multiplied the size of the order and the position, but never divided the margin — the trader's wallet was charged the full un-leveraged value, and closing the position paid back the leveraged amount. Opening and immediately closing a position at the same price, at 20x, turned 200 into 4,000. This was reproduced on the first attempt.

This release implements the isolated-margin model the product's own interface has always described, and fixes the settlement, liquidation, matching and validation defects found alongside it.

It also brings the addon onto the platform's shared foundations. Futures order placement can now be gated behind KYC verification, using the per-level switch the KYC level builder has always offered but no futures screen ever read. The public markets page and the admin desk follow the operator's own theme instead of a fixed orange-and-amber identity. And the background job that keeps position records in step is now bounded, honours the position store's own kill switch, and retires work it can never complete rather than failing on it forever.

Requires Core v6.5.8 and the Ecosystem extension.

Update Instructions

pnpm updator

No migration or seeder is required.

Before updating, close any open futures positions. Positions opened under the old behaviour carry a size that was multiplied by leverage and a margin that was not, so they cannot be settled correctly by either the old code or the new. There is no automatic conversion, because there is no way to tell what the trader intended.


Added

Order placement can now require KYC verification

The KYC level builder has always carried a Futures Trading switch, but nothing on the futures side ever read it — a level with that feature switched off traded futures anyway.

  • Added enforcement of that switch. With Enforce KYC Feature Access turned on, placing a futures order requires a verified user whose level lists Futures Trading. Anyone else is refused, with a message naming what is missing, instead of the order going through.
  • Applies to order placement only. Closing a position and cancelling an order stay open to everyone, so a change in verification status can never trap an open position or strand its margin.

Closing a position returns the settlement figures

  • Added the price it settled at, the margin that was posted, the realised profit or loss and the exact amount credited, returned alongside the confirmation message when a position is closed.

Changed

The public futures page follows the operator's theme

  • Changed the public futures markets page, which was built in a fixed orange-and-red identity that ignored the operator's brand colour and read poorly in one of the two modes. It now takes the platform's own colours and reads correctly in both light and dark.
  • Changed the three headline market figures to use the same stat card as the rest of the platform, instead of three hand-built tiles with their own colour rules.
  • Changed leverage figures to a monospaced, tabular style, so they line up down the column instead of shifting with each row.

Admin futures dashboard restyled, with a proper time-range picker

  • Changed the dashboard, which was drawn in fixed amber and red down to the hard-coded colours of its background animation. It now follows the operator's theme throughout.
  • Changed the time-range control to the platform's own dropdown — styled, and usable from the keyboard — instead of the browser's native one.
  • Changed position status chips to take their colour from the platform-wide status palette, so a futures status looks the same as that status everywhere else in admin, and counters to use a tabular figure style so they stay aligned as they tick.

Admin positions table takes its status colours from the platform

  • Changed status chips in the admin futures positions table, which used a futures-only colour mapping of their own. They now use the platform's central status colours. Cancelled in particular changes from an amber warning tone to the red the rest of the platform uses for that status.

Fixed

Leverage inflated the position instead of dividing the margin

Three parts of the system each assumed a different model, and together they printed money. A flat round trip returned the margin multiplied by the leverage — 20x turned a 200 charge into a 4,000 credit, with no price movement and no counterparty to fund it. The higher the leverage on offer, the larger the multiplier; at the 125x the markets advertise, a round trip returned 125×.

  • Fixed the model is now the standard quote-settled perpetual on isolated margin:

    position value  = amount × price
    initial margin  = position value ÷ leverage
    charged at open = margin + fee
    profit / loss   = (mark − entry) × amount   long
                      (entry − mark) × amount   short
    paid at close   = margin + profit/loss, never below zero
  • Fixed the amount a trader enters is the exposure itself — which is also what the market's size limits apply to and what the order book matches. Leverage no longer changes it.

Closing a position paid out the full position value, at a stale price

Closing paid back the whole position value regardless of the margin actually posted. And the profit figure was only recalculated when a trade happened to touch the position, so on a quiet market a close settled against whatever the price was at the last trade, which could be arbitrarily old.

  • Fixed the position is valued at the live price at the moment it is closed, and the payout is the margin plus profit or loss, never below zero. Nothing is charged on close — under isolated margin the loss is already limited to the margin, and taking more would reach into funds the position never held.

Being liquidated cost nothing

Liquidation handed the trader their entire position back at the moment they lost it. A liquidation was a full refund.

  • Fixed a liquidation returns what is left of the margin.
  • Fixed the refund was going to whichever wallet the system happened to find first — typically the ecosystem wallet, which the position had never taken money from. It now settles to the futures wallet in the correct currency.
  • Changed liquidation to trigger once 90% of the margin is lost rather than waiting for 100%. Closing a position only once its margin is entirely gone leaves nothing to pay the counterparty with.
  • Removed partial liquidation. It created money — see below.

Cancelling a partly-filled order refunded the whole order

The refund was the full amount no matter how much had already traded, so cancelling the remainder of a partly-filled order also handed back the margin of the part that filled.

  • Fixed only the still-reserved share is refunded.
  • Fixed a cancelled sell order refunded into the wrong currency's wallet — crediting a wallet that had never been charged and stranding the margin in the one that had.

The "counter order" shortcut refunded more than it took

Placing an order that mirrors one of your own resting orders cancels that order and refunds it. On any market priced above 1 the refund was larger than what had ever been taken.

  • Fixed it refunds exactly what the order reserved. Its message no longer claims to have closed a position, which it never did.

Matching engine

The futures matching engine is an early copy of the ecosystem one and had none of the hardening that engine has since received.

  • Fixed an unrecognised order type could send the engine into an endless loop and pin the server's CPU at 100%.
  • Fixed a closed order left in the queue could re-match as a zero-amount fill.
  • Fixed a failed settlement no longer records a fill. Both orders used to be marked as matched regardless — recording a trade that moved no money.
  • Fixed a failure to open the position now stops the trade instead of being ignored. The position is the trade; ignoring the failure left the trader charged, the order marked filled, and no position anywhere.
  • Fixed zero and negative fills are refused.
  • Fixed an order book level of exactly zero was handled incorrectly.

Order placement

  • Fixed side, type, amount, price and leverage are all checked. None of them were. An unrecognised side or type was charged for and written to the book, where nothing could ever match it.
  • Fixed leverage is checked against the list the market publishes. Any value at all was accepted — 1000x, zero, negative — and silently changed the size of the position.
  • Fixed an order sent with no price sailed past the balance check and then failed deep inside the engine. A price is now required, and market orders are refused explicitly rather than by accident.
  • Fixed minimum and maximum order size were only enforced on sell orders, so a buyer could open any size at all.
  • Fixed a market the operator has disabled now refuses new orders. Exits stay open, so disabling a market never traps money.
  • Fixed real error messages are preserved on order creation, cancellation and position close. Every rejection used to be rewritten as "server error", so the trader could never see what they needed to change.

Cost limits were only enforced on buy orders

A market's minimum and maximum order cost ran on buys only, so a seller could place an order whose value sat outside the limits the operator had set. This is the mirror image of the size-limit defect above, which ran on sells only.

  • Fixed both limits now apply to both sides of the book, measured against the value of the trade — amount × price.
  • Applies to markets where cost limits have actually been configured. Both are unset by default.

Two more orders that should never have been accepted

  • Fixed an order on a market whose maker or taker fee was never configured is now refused with "Market metadata is missing valid fee rates", instead of quietly charging a fee computed from nothing.
  • Fixed a trade so small that its margin rounds away to zero at the market's price precision is refused, rather than opening a position backed by nothing.

Positions silently failed to open

After any backend restart, orders could fill without a position ever being created. The trader was charged, their order showed as filled, and no position existed anywhere — nothing to close, nothing to liquidate, and no way to get the margin back.

  • Fixed positions open correctly, and a failure to open one now rolls the trade back rather than being logged and ignored.

Stop loss and take profit did nothing

The order ticket carries a full stop-loss / take-profit calculator — percentages, estimated loss, estimated profit, a risk/reward ratio — and the levels reached both the order and the position. Nothing enforced them. Even in the one place they were read, no position was ever closed and no money ever moved. A trader who set a stop had no stop.

  • Fixed stop loss and take profit are enforced against the position, on every price the market prints, and settle exactly like a manual close or a liquidation. A trader's own stop is checked before the exchange's liquidation, so when a single price reaches both, the one they asked for wins.
  • Fixed the exit pays out first and updates the position afterwards, so a failure part-way through cannot cost the trader money, and a repeat cannot pay twice.

Positions opened at the wrong price

  • Fixed the entry price is the price the trade actually filled at, not the limit price on the order. A buy limit at 110 crossing a resting sell at 100 opened at 110 — an instant paper loss and a liquidation price moved against the trader from the moment the position existed. Adding to an existing position had the same defect.

Partial liquidation created money, and has been removed

Positions in this engine settle against a reference price rather than being traded out against the order book, so nothing reduces the counterparty. Trimming 80% off one side paid that side out for the slice, while the other side stayed open at full size and was later paid for the very same price movement on the very same quantity. Measured on a 1-unit long at 20x, trimmed by a 4% adverse move: the closes returned 3.2 more than every margin still posted, with the price flat. That is money created out of nothing, not ordinary gap risk.

  • Removed liquidation is now always a full close. Both sides then stay the same size, their profit and loss cancel exactly, and nothing is created.

Reconciliation

A background job walks recent settlement records and makes sure each one's position is recorded as closed or liquidated in both places the platform stores it. Three defects made it a permanent source of errors and load.

It retried work that could never succeed

A record whose reference could not be read — malformed, or an identifier the position store rejected outright — failed on every single run, forever. It was never retired, so the backlog never drained and the futures reconciliation log filled with the same handful of rows repeating indefinitely.

  • Fixed rows that can never be acted on are now marked handled, with the reason recorded against them, so the backlog actually drains. Genuine connection failures are still left alone and retried.
  • Fixed one concrete source of those permanent failures. A partial-liquidation reference carries a trailing amount, which used to be read as part of the position's identifier and produced identifiers the store rejected on every run. The amount is now split off before the identifier is read — which matters for the breadcrumbs left behind by partial liquidations placed before the feature was removed.

It ran on installs with the position store switched off

On an install started with SCYLLA_ENABLED=false, the reconciler still scanned the ledger on every run and then fired queries at a store it had never connected to. Those failures look temporary, so they were retried forever — an endless stream of errors and pointless database work on a platform that may not use futures at all.

  • Fixed the reconciler now reads that switch itself and stops before doing any work.

It read the whole ledger on every run

Each run looked for its own records by searching for text inside a free-form details field, in a way the database cannot narrow down. Every run therefore read every completed settlement in the rolling window, so the cost of a run grew with the desk's volume rather than with the work actually waiting.

  • Fixed it now matches on the record's own reference, which the database can look up directly, so only rows that belong to the reconciler are read. The per-run cost is bounded rather than growing with the ledger.
  • Added FUTURES_RECONCILER_BATCH_SIZE caps how many rows one run may take, defaulting to 500. The scan is oldest-first and handled rows are stamped, so a backlog drains across runs instead of being restarted from scratch each time.

The order ticket could not place an order

Every order submitted from the futures ticket was refused with "Invalid symbol". A stop set on the ticket never reached the server either — which is the second reason stops did nothing — and market orders were sent without a price.

  • Fixed the ticket sends what the platform actually expects.
  • Fixed it was also submitting twice. It now submits once.
  • Fixed the ticket showed a position size the platform never opened and a "margin" figure that was in fact the full position value. It now shows the real exposure and the real margin.
  • Fixed the estimated profit and loss on the stop-loss / take-profit calculator were multiplied by leverage on top of a size that already included it.
  • Fixed the leverage slider's maximum was 1x on every market, while its starting value was 10. It now moves between the leverages the market actually offers, and starts on one of them.

The admin dashboard showed an empty desk when it could not load

When the futures dashboard failed to load its figures, the failure was swallowed and the page drew its zero-filled defaults instead. A broken load looked exactly like a healthy desk with no markets, no open positions, no volume and no profit or loss — on the one screen an operator opens to find out whether anything is wrong.

  • Fixed the dashboard now shows Failed to load dashboard, the reason it failed, and a Try Again button.
  • Fixed the page refreshes itself every 30 seconds, and one failed refresh no longer blanks a dashboard that had already loaded. The last good figures stay on screen.

Market creation wizard headings and helper text

  • Fixed the Precision and Limits headings in the market creation wizard were tagged with a text size that does not exist, so they had been silently rendering at body-text size and did not read as headings at all. They now render at their intended size.
  • Changed helper text across all three steps of the wizard follows the operator's theme instead of a fixed grey.