AI Market Maker 6.1.8
11 August 2026
This release has upgrade notes. Read them before updating — they describe behaviour changes that need your attention.
AI Market Maker v6.1.8
Release Date: August 11, 2026 Tags: MARKETS, POOL, WITHDRAWALS, ADMIN, RELIABILITY, RISK-MONITORING, VOLATILITY, EMERGENCY-STOP, ANALYTICS, ORDER-BOOK, CANDLES, BOTS, STATISTICS, PERFORMANCE, BUG-FIXES
Overview
The addon's money and teardown paths, audited end to end.
Two pool withdrawals arriving together paid the admin wallet twice and debited the pool once, in real balance, and deposits lost money the same way with the sign flipped. Deleting a market maker paid out a balance read before its shutdown and could then fail with the pool still funded — payable a second time. Both teardown doors reported cancelling the maker's quotes while every one of them stayed resting in the public book.
The risk layer failed the same way: a market could be auto-paused for ordinary movement, a process merely standing down took every market on the install out of service, and neither state appeared anywhere an operator looks. Figures move as well — every trade total now counts fills against real customers.
Read Upgrade Notes before comparing any figure against last week, and go and look at any market you deleted or paused while it was live.
Requires Core v6.6.3.
Update Instructions
pnpm updatorNothing to configure and no data migration. Several of the fixes below change what existing records should have said, so the checks in Upgrade Notes are worth the ten minutes.
Highlights
Almost every defect in this release reported success. The emergency stop answered
200 with a count of orders cancelled; the delete logged a clean teardown; the
pool paid out and the route returned the withdrawal it had made; the analytics
pages drew a confident number. In each case the underlying step had done nothing,
done half of itself, or done it to somebody else's money.
That is why so much of the work here is about what a market maker reports rather than what it does. The engine now names the markets it has stopped ticking even though their status still reads active, the emergency stop says how many quotes it could not pull, the teardown refuses rather than destroying a pool it could not pay out, and the trade list and the per-market performance screen carry a statement of what they could not read. A number that is a lower bound now says so.
Upgrade Notes
A pool withdrawal could pay out twice, and a deposit could be charged twice
Every balance write on the pool was an absolute value computed from a read taken outside the transaction and without a lock, and nothing else collapsed duplicate requests — the wallet ledger key is fresh on every request and the admin screen mints a new one per click. So two withdrawals arriving together — a double-clicked button, two tabs, a client retrying after a timeout — both passed the available-balance check, both credited the admin wallet in full, and the second overwrote the first's balance write. The pool was debited once and paid out twice, and the difference is real balance. Deposits are the same race inverted: both admin wallets debited, the pool grown by one deposit, which loses the operator's own money rather than creating any, and is presumably why nobody reported it.
- Compare each pool's balance and its funded capital basis against the deposits and withdrawals recorded in its history. A duplicate shows up as two admin wallet movements of the same size against one movement in the pool.
- The same check applies to any pool you topped up twice in quick succession — there the pool will be short, not the wallet.
A market maker deleted while it was live may have paid out the wrong balance, or none
The delete read the pool at the top of the handler, then waited up to ten seconds for the engine to stop and cancelled the resting orders — and every fill that lands in that window commits a real change to the same pool. The payout therefore credited the balance the pool held before those trades. Worse, the credits ran outside any transaction and the pool row was never zeroed; it was destroyed later, in a step that waits on rows another process is still writing and can time out. When it did, the route answered an error with the admin already credited the whole pool and the pool row still carrying the whole balance — which could then be paid out a second time through the pool withdrawal screen. A credit that failed outright was logged and stepped over, and the teardown destroyed the pool anyway, taking the capital with it.
- For any market maker you have deleted, reconcile the admin wallet credits against the pool's last known balance. A delete that returned an error is the one to look at first.
- Deleting a market maker also wiped every aggregated depth level on that ecosystem pair, including levels backed by genuine customer limit orders. Those customers' orders stayed open with their funds held while their depth vanished from the chart. On any pair where you have deleted a maker, look for open ecosystem orders that no longer appear in the book.
Quotes may still be resting from an emergency stop or a delete
Both teardown doors "cancelled" the maker's live quotes with a step that only evicts an order from the matching engine's in-memory queue — it writes no terminal status and removes no depth. The emergency stop reported every quote it found as cancelled while all of them were still open, still resting, still publicly tradeable against the pool, and re-adopted by the next resync.
- Check the public book on any pair where you have run an emergency stop or deleted a maker for bot quotes that are still standing, and cancel them.
- The emergency stop now reports how many quotes it could not pull and says so in its warning. A non-zero count is the thing to act on.
Markets stopped or paused by the system do not come back on their own
Two separate faults took funded markets out of service, and neither heals itself, because the resume path only ever restarts a market whose status already reads active.
- A process merely standing down — a lost leadership key, a lost lease, a deploy, or a single settings read that errored — wrote stopped onto every market maker on the install. Look for markets reading stopped that you did not stop, and start them.
- The volatility guard could auto-pause a healthy market. Look for markets reading paused with an automatic high-volatility entry in their history, satisfy yourself the market is sound, and start them again.
The volatility threshold means something different now
The guard, which began working for the first time in 6.1.7, was measuring something the threshold beside it was never expressed in. The threshold you set on a market is a daily percentage — the same unit as that market's own base volatility — and the figure compared against it was annualised, roughly nineteen times larger for the same market. Separately, restarting a market re-seeds its price from the last published close, and that step was counted as a market move: one recorded case paused a market 51 seconds after it started.
- If you raised a market's volatility threshold to stop it pausing, lower it back to what you actually intend. Read it against the market's configured base volatility, in percent per day.
Every trade figure now includes fills against real customers
The trade list, the dashboard's recent-trade count and the whole of the per-market performance screen were built from the simulated bot-to-bot trade record alone. Fills against real customers settle elsewhere and were counted nowhere, so a maker doing nothing but genuine business rendered as idle.
- Trade counts, volumes and averages will read higher than they did last week on any market with real customer activity. Nothing traded that had not traded before.
- The per-fill customer record only exists from the release that began writing it. For periods before that, the merged figures are a lower bound. The trade list and the per-market performance screen now state when that applies rather than drawing the shortfall as a quiet market; the dashboard's recent-trade count carries no such statement, and under-counts in silence when the customer record cannot be read.
Added
A census of the markets that have stopped ticking
A market that pauses itself after a run of failed ticks deliberately never writes its status row — writing it would make the supervisor correct the mismatch every few seconds and turn a bounded backoff into a flap. The cost was that nothing outside the engine could tell: the dashboard reported the market as active and quoting while its engine had stopped, for as long as the backoff lasted, which escalates to 32 minutes. A market the risk monitor paused fell through the same gap from the other direction.
- Added two lists to the engine's status, carried onto the analytics overview: the markets the engine has paused itself on — with when it paused, when it will retry and the last error — and the markets it has loaded but is not ticking because their status is paused. Between them they account for every loaded market the engine is skipping.
- Added a single automatic-pause entry to a market's own history the first time an engine-level pause begins, naming the failure, the retry time and the fact that the market's own stored status still disagrees. One entry per incident, not one per retry.
- Changed the description of the engine's active-market count, which said it was driving those markets. It counts the markets it has loaded, and that number does not fall when one of them stops being made.
The customer side of the trade feed
- Added fills against real customers to the trade list, tagged as customer fills against simulated house prints, and carrying the realised profit, the fee, whether the maker was the passive side, and the counterparty. The two stores are merged by time and paged together.
- Added a coverage statement to the trade list and the performance screen: how far back the customer-fill record was read, when it begins, how many bots could not be read, and whether the page is therefore complete. A store that could not be read is no longer indistinguishable from a market that never traded with anyone.
Changed
Pausing a market now retires its resting quotes
Withdrawing a pool is refused only while a market is active, and pausing is precisely what unlocks that money — but pausing used to do nothing except flip a flag. Every one of the pool's real quotes stayed live in the public book with nothing reserved against them, so an operator could pause, withdraw the pool to zero, and leave customers filling bids the pool no longer had the funds to honour. The periodic sweep was no substitute: a paused market's price is frozen, so a quote that was not already stale can never become so, leaving the one-hour expiry as the only thing that retired anything — a window up to an hour wide.
- Changed a pause to cancel the market's resting real quotes as it pauses, after the market has stopped placing new ones. The synthetic depth is left alone, so a resume republishes as before.
- Fixed a cancel-everything sweep that forgot the orders it had failed to cancel. Those orders stayed live and publicly resting while the maker no longer knew they existed, so nothing could ever retire them — at exactly the two moments that exist to make the pool safe to touch.
The candles behind a market's chart
Every tick a market maker publishes updates thirteen timeframes of the chart, from one minute to one week.
- Changed those thirteen writes to go out together rather than one after another. The candles written are identical; a tick now waits for the slowest single write instead of for all thirteen in sequence. A write that fails is still isolated to its own timeframe and leaves the other twelve alone.
Per-bot trade counts and volume
A market's per-bot statistics are assembled from the last seven days, each day held separately.
- Changed the seven reads to run as one wave rather than in sequence. The counts and volumes are the same figures.
- Changed what happens when one of those seven days cannot be read. The tally stopped where it stood, so every day older than the failure was left out of the figures on the page without saying so. The unreadable day is now logged and skipped, and every day that can be read still counts.
The risk monitor's three checks
Every active market is examined every ten seconds for volatility, the day's profit and loss, and how long it has been since its last trade.
- Changed the three questions to be asked together rather than one after another, so a market's check takes as long as its slowest question rather than the sum of all three. The daily-loss thresholds and the alerts are unchanged; the volatility figure is corrected below.
- Changed the last-trade lookup to read only the timestamp it needs. It previously loaded the whole history record, including a stored detail blob nothing on this path reads — once for every active market, six times a minute.
The pool figures on the performance screen
- Changed the pool's locked value, unrealised profit and realised profit to leave the server as numbers. They went out as text under a contract that declared them numbers, so any client doing arithmetic on them was joining strings rather than adding.
- Changed the target-achievement figure to report nothing rather than zero when there was nothing to measure, and to carry a plain statement of what it does and does not mean: it compares every trade in the period against the target as it stands now, and the target can be moved inside the period. Zero percent claims every trade missed the target, which is a measurement nobody made.
Fixed
Two pool withdrawals at once paid twice
Covered in Upgrade Notes, where the records to check are listed.
- Fixed the pool withdrawal to re-read the balance and re-run its availability check inside the transaction that writes it, against a locked row. The second request now waits and then sees what the first actually left.
- Fixed the same race on deposits, which credited the pool once for two debited wallets.
Deleting a market maker could pay out the wrong balance, or lose it
- Fixed the payout to read the pool after the engine has stopped and the book is cancelled — the point at which no further fill is possible — instead of from a snapshot taken before a shutdown that takes up to ten seconds.
- Fixed the payout and the pool's zeroing to be one transaction, so a later failure can no longer leave a funded pool row behind money that has already been paid out and is payable again.
- Fixed a failed payout being logged and stepped over while the teardown destroyed the pool regardless. A pool that cannot be paid out now leaves the market maker intact, with the balance still recoverable by retrying the delete or withdrawing it directly.
- Applies to a pool that holds a balance. An admin wallet is resolved for a currency the pool actually holds and for no other, so deleting an empty market maker never has to resolve one — as before, but now stated by the code rather than left to a swallowed error.
An emergency stop that left the quotes live
The step both teardown doors used only drops an order from the matching engine's in-memory queue and redraws the book. It is the last step of a cancel, and on its own it succeeds having changed nothing on disk.
- Fixed the emergency stop and the market delete to perform the real cancel, writing the terminal status and removing the depth, and to cancel only what is actually left of a partially filled quote rather than its original size.
- Added a count of the quotes that could not be cancelled to the emergency stop's answer, and to the warning it returns. A quote that survived a stop is still tradeable against the pool, and it is the thing an operator has to act on.
Deleting a market maker wiped the whole order book on that pair
The aggressive cleanup the delete runs removed every aggregated depth level on the ecosystem pair, not just the ones the bot had written. On a live pair, the resting orders of every other participant vanished from the depth chart and from the sizing of incoming market orders while those orders stayed open and their funds stayed held.
- Fixed the cleanup to remove only the levels the market maker itself wrote, leaving every level backed by a genuine customer order in place, and to republish the book so the cached depth matches what is actually there.
Stale quotes could be arbitraged out of the pool
A real quote is placed a fraction either side of the market and then never repriced. Its only removals were a fill and an hourly expiry sweep, so a bid placed at 99.90 while the market was at 100 was still the best bid an hour later with the market at 90 — and a customer market-selling into it would take 99.90 out of the pool for something worth 90. The exposure stood open for the full hour, and up to 500 such quotes can rest at once.
- Fixed the sweep to also retire any quote that has ended up on the wrong side of the market's own reference price. A market maker never intends to bid at or above its own reference, so such a quote can only ever be taken at a loss to the pool. Quotes that never reach the public book are untouched, and a market that has not yet priced itself has nothing swept.
A customer's fill was invisible to the running market maker
A quote a customer had partly taken still read as fully available in the maker's own memory, because nothing told the running engine about the fill. Cancelling one therefore asked the book to remove its original size from a depth level shared with every other participant at that price — taking liquidity that was never that order's, and deleting the level outright when it went to zero. A fully consumed quote never left the maker's books at all, so it occupied one of its 500 slots for the rest of the run.
- Fixed the running market maker to be told about a customer fill as the matcher settles it, so a cancel removes only what is genuinely still resting and a consumed quote is retired.
- Fixed the same arithmetic after a restart, where re-adopted quotes were assumed to be untouched no matter how much of them a customer had already taken.
A bot quote a customer had consumed still read as resting
The maker's own record of its live quotes was only ever written when a quote was cancelled — a quote that was filled was never written at all. Anything reading that record to ask what the maker currently has on the book over-counted by the whole of its traded volume.
- Fixed the record to be written on every customer fill, and marked filled once the quote is fully consumed. A partly filled quote deliberately continues to read as resting, because that is what it is and it still has to be retired.
- Fixed the teardown labelling a quote a customer had taken as cancelled. It now records which way the order actually went.
- Fixed a quote being made fillable a moment before it was recorded. A fill landing in that gap wrote a record the placement then overwrote back to resting, and a crash in the same gap left a quote in the public book with nothing tracking it.
The volatility guard paused markets that were behaving
Covered in Upgrade Notes, with the threshold to re-check.
- Fixed the volatility measurement to be a daily percentage, the unit the threshold you set is expressed in, instead of an annualised one roughly nineteen times larger.
- Fixed it to weight each price sample by the time that actually elapsed before it. The samples are irregular — seconds to minutes apart — and every one of them was previously treated as an hour.
- Fixed a market restart being read as a price move. The price is re-seeded from the last published close on every start, so the step across a restart is an artefact of the process starting; the pair of samples spanning one is now discarded rather than pooled.
- Timestamps are stored only to the second, so two samples inside one second are now treated as one second apart rather than none. That floor is part of the new arithmetic rather than a repair of the old one: without it the division above would report unbounded volatility and pause every market on the install.
A process standing down took every market out of service
Shutting the engine down on one process wrote stopped onto every loaded market maker's status row — and that row is the operator's standing declaration of what they want running. Not one path that reaches a shutdown is an operator: leadership moving to another process, a lost lease, a deploy, the global enable or maintenance switches, and those last two also fire when a settings read merely errors. Nothing healed it, because the restart path only ever re-activates a market that already reads active. One brief interruption took every funded market maker on the install out of service until a human pressed start on each.
- Fixed the shutdown to release its in-memory markets without touching a single status row. Everything the process genuinely owns still comes down: the real quotes are cancelled and the depth it published is cleared. A deliberate stop-all still declares itself, as before.
A market's candle close pinned to a value from a market that no longer existed
The right to publish a minute's closing price is claimed per pair and per minute, held briefly, and tied to nothing else — not to the market it was published for, nor to that market continuing to exist. Delete a market and re-list the same pair inside that window, which is what happens whenever an operator corrects a listing, and the new market maker inherits a claim from an engine that is gone. Every one-minute candle it published then carried the dead engine's number for the rest of that minute: the price series advanced, trades printed, the high, low and volume all moved, and only the close stood still — so nothing anywhere reported a fault.
- Fixed a market to release that pair's outstanding claims as it takes over publishing the pair, so a close published before it existed has no authority over its candles.
Every trade figure counted the house trading with itself
The trade list, the dashboard's 24-hour trade count, and the per-market performance screen's trade count, average size, volume chart, price points and target-achievement rate were all built from the simulated bot-to-bot record alone. Fills against real customers are settled by the exchange matcher and recorded separately, so a market doing nothing but real business rendered as idle on the one screen an operator opens to decide whether it is earning.
- Fixed the dashboard's 24-hour count and every trade-derived figure on the per-market performance screen to draw on both records; what the trade list itself gained is under Added above. The trade list and the performance screen share one merge, so those two pages cannot disagree with each other about the same market — the dashboard sums the two counts on its own. In all three the customer half is read best-effort and never allowed to fail a page.
- Only the trade list and the performance screen say so when that half falls short, and state the figure as a lower bound rather than drawing it as a quiet market. A dashboard count that could not read the customer record under-counts in silence.
- Fixed the volume chart and price chart to distinguish house prints from customer fills, and the price chart to stop labelling a trade's own execution price as the target it was supposedly tracking — a claim neither record supports.
Three of the four figures on the market analytics card were always zero
The screen read field names the endpoint has never returned, and the contract described so little of the payload that nothing contradicted it.
- Fixed "Total trades", which drew a confident 0 on every market on the platform, and now shows the merged count with the house and customer split beside it.
- Fixed the two volume figures beside it, which were zero for the same reason. The card now shows traded volume over the period being viewed, and the target-achievement rate, which reports a dash rather than zero when there was nothing to measure.
- Added a line under the card whenever the figures above it are known to be incomplete, stating what was missing.
Creating a market could report a failure that named nothing
The create saves the market and then reads it back to answer with. If that read stumbled, the error handler tried to undo a change that had already been committed — an attempt that fails on its own, and its failure is what reached the screen: an internal server error naming no cause, on a market that had in fact been created. An operator who believes the market was not created creates it again.
- Fixed what an operator is shown when something goes wrong after the change has been written. The request still ends in an error; what changed is that the error now describes the real failure instead of being replaced by a complaint about undoing work that was already done.
- The market delete carries the same guard. Its only work after the change is written is assembling the answer, so there is no known case of an operator seeing this on a delete.