Monitoring a market
Answering "is this market actually being made" — the quoting meter and the gates it names, the price band, inventory skew, volume against budget, the lifecycle actions and what the analytics screen measures.
The status column answers "what did an operator last set", not "is this market being made". Those are different facts, and for most of this addon's life only the first one was on screen.
The dashboard at /admin/ai/market-maker is built around the second.
Quoting, not ACTIVE
The engine refuses to trade a market that fails any of its hard gates, and a market failing one keeps status ACTIVE throughout. The dashboard evaluates the same gates against the same rows the engine reads and reports the engine's verdict.
Every ACTIVE market lands in exactly one of five states, and the five counts add up to the active count so nothing hides between them:
| State | Meaning |
|---|---|
| In range | Quoting, and its price sits comfortably inside the configured band |
| At range edge | Quoting, but inside the outer 20% where the containment force engages |
| No price yet | Quoting, but the engine has not checkpointed a price for it |
| Outside range | Quoting, and the price has left the band you configured |
| Not quoting | Blocked by a gate, named on the row |
A Not quoting row names the gate that is closed — too few bots, unfunded pool, daily budget spent — so it states the remedy rather than the symptom. All three are closable from the market's own page.
The high-volatility guard is evaluated from price history held in the engine's own memory, which no query can reach. A market paused by volatility alone is still reported as quoting. That is deliberate: this screen will not claim a market passes a gate it cannot see. If a market reads as quoting and is manifestly silent, check the server log for the volatility warning — see Risk and emergency controls.
Your active-market count does not change when you look at this. The count of markets actually making a market may be considerably lower.
Where the price sits in its band
Each market carries a marker showing its last known price against the configured floor and ceiling, with the two edge zones drawn and the state stated in words — in range, at range edge, outside range — so it never depends on colour alone.
The reading is geometric, not linear. On a range of 1 to 100 the centre is 10, so a market sitting exactly on its anchor reads as centred.
No price yet is not an error. The engine checkpoints its price state roughly every two minutes, so a market started in the last couple of minutes has nothing to report and says so rather than guessing.
Inventory skew
Per market: what share of the pool is now held in the base asset, against the share it was funded with, and the difference in percentage points with its direction.
A positive figure means the pool holds more of the base asset than it was funded to hold — it has been absorbing sells and is long the asset it makes a market in.
Both sides are valued at the current price on purpose. This is not P&L, which is reported separately. Valuing the funded mix at its funding price would fold the market's own price move into the figure and make a pool that never traded look lopsided.
Volume today, against the budget that silences it
currentDailyVolume is a counter zeroed at the UTC daily reset. It is volume
today. A reading taken shortly after the reset legitimately looks empty, which
reads as a dead desk when it is nothing of the kind.
The dashboard labels it Volume today and sums it across every market whatever its status. The analytics screen's own tile still reports the old ACTIVE-only figure and is unchanged, so the two screens disagree by design — the dashboard's is the one that covers every market. If you kept a figure copied from an older build, do not compare it to today's: no pool balance changed, only what the screen was doing to it before showing you.
The figure is shown against the total of the markets' daily volume budgets as a percentage, because that budget is a hard trade gate. A market approaching 100% is a market about to go quiet until the reset — visible in advance, instead of discovered as a flat chart.
The trade counter beside it genuinely is a rolling 24 hours. The two measure different windows on purpose.
The masthead
Above the market table the page states what the feed is doing, when it was last read, and which engine process produced the numbers.
Two alerts are raised there:
- Silent markets, counted and broken down by cause — how many need active bots, how many have an unfunded pool, how many have spent their budget. Every counter links to the rows behind it.
- Engine short of markets — a comparison between the number of markets the running engine holds and the number the database says are ACTIVE. The engine loads exactly the active rows, so a shortfall means markets failed to initialise. This is only raised on the process that actually leads; a second process legitimately drives nothing.
The engine block also names what vouched for leadership. "none" means nothing is arbitrating it — neither Redis nor the database lease row could be reached — and every process that starts the engine believes it leads. That is the first thing to check when a market looks like it is being driven twice.
The table draws ten rows, ranked worst first: not quoting, then outside range, then at the edge, then no price yet, then healthy, then paused and stopped, with the largest pool first inside each rank. It says how many markets exist behind those ten.
The page refreshes every 30 seconds and skips the poll while the browser tab is hidden. A failed refresh keeps the last good numbers on screen — one bad request must not wipe a working dashboard — and says the refresh failed, gives the server's own reason, and shows the time the figures were actually read.
Lifecycle actions
From the market detail screen, four actions, with the transitions enforced server-side:
| From | Allowed |
|---|---|
STOPPED |
START |
ACTIVE |
PAUSE, STOP |
PAUSED |
RESUME, STOP |
START and RESUME first make sure the engine is actually ticking, then activate every bot and load the market. START additionally requires a funded pool and, if configured, the minimum quote liquidity.
STOP stops the market through the engine, which also clears any synthetic depth it left on the order book and cancels its own resting orders.
If this process is not the one holding engine leadership, the response reads "recorded as ACTIVE, but the market-making engine is not running in this process yet. It will begin on the next engine cycle." rather than reporting success. Older builds turned the badge green and said nothing while nothing ticked.
Note that pausing and stopping are not equivalent for the pool: pool withdrawals and rebalancing both refuse an ACTIVE market, so pausing is what unlocks them.
An operator's pause is never undone automatically. The job that resumes auto-paused markets reads the recent history rather than a single record and stops on any later decision, so an operator pause applied after an automatic one stands.
The analytics screen
/admin/ai/market-maker/analytics picks one market and reports on it.
Performance, over 1h, 24h, 7d or 30d: the live current price — the
price the market reached, not the target it was asked for — a price history, a
volume history, the measured target achievement rate, TVL and both P&L figures.
The query is bounded and says when it truncated: an aggressive market records
about 9,000 trades a day, and an unbounded 30-day pull fetched a quarter of a
million records to draw a few hundred points.
P&L: daily, weekly, monthly and all-time, realised and unrealised, ROI against the capital basis, and a breakdown of winning against losing fills with maker and taker counts.
All-time comes from each bot's lifetime accumulator, which has been correct since the addon shipped. Daily, weekly and monthly come from a per-fill ledger that had no writer until v6.1.0, and the individual fills before that were never captured so there is nothing to backfill. The response states when the ledger begins, and the screen labels it — so a large all-time figure beside three small ones reads as a short ledger, not as a collapse.
Trades: the market's trade history, filterable by date range and by bot. The filter is applied before paging, so a filtered request returns a full page of that bot's trades rather than whatever fraction of one page happened to belong to it.
The history trail
Every market keeps an immutable history: TRADE, START, STOP, PAUSE,
RESUME, AUTO_PAUSE, EMERGENCY_STOP, DEPOSIT, WITHDRAW, REBALANCE,
CONFIG_CHANGE, TARGET_CHANGE, PHASE_CHANGE, BIAS_CHANGE and
MOMENTUM_EVENT.
Per-trade rows are pruned on the retention window you configure. Daily summaries and lifecycle audit rows are never pruned.