← kalmanGrid

kalmanGrid — Status & roadmap

ENGINEERING COMPLETEAudited 2026-09-12· hand-written (no shared engine) engine· quantroduction· spot

Where this strategy is today

ENGINEERING COMPLETE — the code is correct, uniform and shippable. That is a statement about engineering, not about profitability; the performance verdict is separate and sits below it.

Dimension State
Correctness 5 / 5 invariants pass
Console output 34 tagged calls, 0 off-format
Side panel 46 rows, padded to the 3-column grid
Chart marks reference-levels
Palette 8 / 8 canonical
GUI settings 66 exposed · 0 read at runtime · 0 untunable · 0 inert
Deployed to a live install Not yet — audited and staged, not yet shipped

Performance verdict. Over two years on 7 markets it returns a median -1.4% against buy-and-hold's -0.8% — an edge of 0.1% — and is profitable on 0 of 7.

What it actually does

Measured at 5m, and the open question on this page is now answered

This strategy declares a 5-minute timeframe and had previously been published on hourly bars. Re-measured at 5m over 210,400 bars and 7 markets, it is negative on all seven (median −1.40%, previously +0.3% and 4-of-7 profitable on the wrong timeframe).

The roadmap below asked why it almost never trades. The measurement separates two things that were being read as one:

round trips, 2 years, per market 15 to 69 — about one every 19 days
time holding a position 0.008% to 0.097% of bars
derived holding time per round trip ≈ 1.9 bars — ten minutes
wallet committed when it does hold 50% to 89%
win rate 8% to 33% (median 19%)
profit factor 0.05 to 0.26 (median 0.13)

So it is not a grid that sits in the market. It rests post-only bids and asks either side of the Kalman fair value, and a completed round trip is a bid fill followed within minutes by an ask fill. That is market-making, and the shape of the result is a market maker's failure mode rather than a grid's: it wins 19% of the time and its losses are roughly five times its wins in aggregate (profit factor 0.13). A resting bid that fills because price is falling does not get its matching ask, and the position it leaves behind is 50–89% of the account.

Both halves of "almost never trades" are therefore true and they are different problems. It enters rarely because the innovation gate pauses it, and it holds briefly because the two-sided quoting works as designed. The rare, large, mostly-losing entries are the thing to fix.

A spot grid whose levels are not fixed — they are anchored to a Kalman-filtered fair value that moves with the market.

The filter runs a two-state model, [price, velocity], observing the mid price. Grid rungs are placed at GRID_SPACING × ATR around the filter's price estimate rather than around the last trade, so the whole ladder drifts with the estimated fair value instead of being re-anchored by hand.

Two parts of it are more carefully built than the pack average, and both were verified against the code rather than taken from the header:

Safety: it pauses when the innovation z-score exceeds INNOV_PAUSE (6), which is the filter saying the observation no longer matches its model — a sensible condition to stop trading on.

Verified against the shipped strategy source, not its documentation.

The measured constraint

Time in market is 0.0%, over 236 round trips across all 7 markets and two years. Everything in the performance report rests on that fraction of the bars. A strategy out of the market 100% of the time is not therefore a low-risk one: the 1.4% drawdown happens entirely inside the time it is committed.

Settings, measured

This strategy does not use the engine config surface.

What to build next

Ordered by expected effect on the result, not by effort.

1. Re-tune the filter now that its settings are reachable

Highest priority

Until this audit the three parameters a Kalman filter is actually built on — process noise on price, process noise on velocity, and observation noise — could not be set from the Gunbot GUI. The GUI wrote KG_KF_Q_PRICE, KG_KF_Q_VEL and KG_KF_R; the filter read KG_Q_PRICE, KG_Q_VEL and KG_R. Turning them changed nothing.

They now work, and they matter a great deal. Raising observation noise to 3.0 on LINK takes the strategy from 32 buys and +20.4% to 9 buys and −0.7%. That sensitivity means the shipped values have never been measured against alternatives, because until now they could not be. A sweep over Q/R is the single highest-value experiment here — and because R adapts at runtime, what is being tuned is the starting R and the floor, which is a narrower and more tractable question than it first looks.

2. It enters rarely and holds briefly — and only the first of those is the problem

Highest priority

Measured at 5m: 15 to 69 round trips per market over two years, each held about 1.9 bars — ten minutes — with 50% to 89% of the wallet committed while it is on.

The brief holding is the two-sided quoting working: a resting bid fills, the matching resting ask fills minutes later, and the round trip closes. Nothing needs fixing there.

The rarity is the problem, and so is the size. One entry every nineteen days, at up to 89% of the account, with a 19% win rate and a profit factor of 0.13, means the strategy is making a handful of very large bets and losing most of them. A resting bid fills precisely when price is falling through it; if the matching ask never fills, what is left is an enormous adverse position.

Two experiments, in this order. First cap the entry: size each rung against equity rather than letting one rung take most of the account, and re-read profit factor — a 19% win rate is survivable at small size and is not at 89%. Then widen the innovation gate (the INNOV_PAUSE setting) to let more entries through, and check whether the win rate holds up when the filter is allowed to be less certain.

3. Judge it on LINK-like markets, not on the median

High priority

The per-market spread is the widest of any strategy audited so far: +20.4% on LINK against −0.0% on BTC, where buy-and-hold returned +130.8%. Profit factor ranges from 0.00 to 7.71 and win rate from 0% to 76%.

With one round trip on BTC the numbers there carry no information at all. The honest reading is that this strategy has conditions it suits and conditions in which it simply does not act, and the median across seven markets hides both. Worth doing: classify the markets by whether the filter's innovation stayed inside the pause threshold, and report the strategy only on markets where it actually traded enough to be measured.

4. Consider whether the ladder should ever cut a loss

Worth testing

Like the other accumulate-and-scale strategies in this pack, the sell paths require a gain. Unlike them, the win rate here is genuinely mixed (0-76%) rather than a structural 100%, which means positions are being closed at a loss somewhere — worth confirming which path does that and whether it is intended, because the design does not obviously include one.

If there is no deliberate loss-cutting path, then the mixed win rate is coming from fee drag on scratch trades rather than from risk management, and that is a different thing to know.

5. The settings measurement does not yet cover this lineage

Read before acting on the others

The audit proves which settings a strategy reads by running it with a recording proxy over its config object. That works for the 348 Gunbot Quant files, which read through one shared config accessor, and not for the 79 Quantroduction files including this one — they build a plain settings object straight from the GUI values.

The three dead Kalman knobs were found here by diffing the settings-display block against the live P block by hand. A proxy over P would have caught them automatically, and would cover all 79 files at once. This is the third strategy in a row where that gap hid a real defect, which makes it the highest-value improvement to the audit tooling itself.

Operator output, measured

Everything below was read out of the strategy file by the presentation audit, not taken from its documentation. These are the surfaces an operator actually watches: the terminal log, the side panel, the chart, and the settings the GUI offers.

Terminal log
Tagged, aligned, coloured
34
Raw and off-format
0
Exempt (object and error dumps)
12

Every non-exempt line carries the strategy name, an event tag and the palette colour for that tag, so one terminal running many pairs stays readable.

Side panel
Data rows
46
Section headers
9
Rows with no tooltip
0
Padded to the 3-column grid
yes
Chart marks
Model
reference-levels

draws price levels into customChartTargets and no per-trade marks, so there is no intent-vs-fill distinction to get wrong

Colour palette
Non-canonical colours
0

One colour per meaning, identical in the log, the panel and the chart — the eight canonical roles.

Settings the GUI exposes

66 settings appear in the Gunbot strategy editor for this strategy. Which of them the code reads has not yet been measured for this lineage: it does not use the shared config surface, so there is nothing to wrap with a recording proxy.

PERIODTRADING_LIMITKG_TLKG_CAPITAL_ALLOCALLOC_PCTALLOC_CONFIRMEDALLOC_SPLIT_TOLALLOC_ENFORCE_SPLITALLOC_AUTO_REBALANCEALLOC_RESERVE_PCTKG_GRIDKG_GRID_LEVELSKG_GRID_SPAN_PCTKG_GRID_SPACINGKG_GAINKG_MAX_DCAKG_DCA_DECAYKG_COMPOUNDKG_Q_PRICEKG_Q_VELKG_RKG_INNOV_PAUSEKG_CALIBKG_EM_ENABLEDKG_EM_INTERVALKG_EM_WARMUPKG_EM_LRKG_R_FLOORKG_Q_FLOORKG_MAX_EXPKG_MAX_DDKG_FEEKG_MAKER_FEEKG_SPLOG_LEVELWARMUP_CYCLESMIN_ORDER_QUOTEBE_GUARDNO_POST_ONLYKG_TRACEVERBOSE_LOGSVERBOSE_INTERVAL_MINBE_GUARD_BLOCK_MARKET_SELLSSCRATCH_LIVENESS_MINCONSEC_RESET_CYCLESDRIFT_ATR_FRACSKEW_QTY_MAXSKEW_QTY_TARGETPORTFOLIO_INCLUDEPORTFOLIO_EXP_BUDGETPAIR_EXP_BUDGETDISABLE_BREAKER_WINDDOWNTRACE_ALLKG_DCA_FROM_HOLDINGKG_KF_Q_PRICEKG_KF_Q_VELKG_KF_RKG_PERIODSTRICT_LITERATURENO_CLOSE_MARKETQUANTRODUCTION_TRACERESET_BREAKER_ONCERESET_STATS_ONCEKG_COUNT_WINDOW_CYKG_PENDING_REAP_CYSPREAD_PNL_JUMP_GUARD

Open presentation findings