meanRevOscillator

meanRevOscillator — 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 18 tagged calls, 0 off-format
Side panel 21 rows, padded to the 3-column grid
Chart marks reference-levels
Palette 8 / 8 canonical
GUI settings 59 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 three years on 7 markets it returns a median 6.1% against buy-and-hold's 39.2% — an edge of -42.8% — and is profitable on 5 of 7. On this window it is not a replacement for simply holding the asset, and nothing in these reports claims it is.

What it actually does

Models the spot price as an Ornstein-Uhlenbeck process — a mean-reverting random walk — and trades the deviation from the estimated mean rather than price direction.

dX = θ(μ − X)dt + σdW, with θ (reversion speed) and μ (long-run mean) recovered by OLS on the AR(1) discretisation of log price, and σ from the residual standard deviation. Half-life is ln(2)/θ, which sets the expected bars to revert halfway.

Every decision reads the z-score of the deviation, not the raw price:

Both of those refinements were dead before this audit: the ladder could never advance past its first rung, so dcaLevel was always 0 and the per-layer deepening and the dcaLevel > 0 guard could never fire.

Exposure is capped at 75% of equity (MAX_EXPOSURE) and enforced on the buy gate.

Read from the shipped strategy source, not from its documentation.

The measured constraint

Time in market is 90.1%, over 205 round trips across all 7 markets and three years. Everything in the performance report rests on that fraction of the bars.

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. Sweep MAX_DCA — the ladder works now and the shipped value was never measurable

Highest priority

MAX_DCA (5) and DCA_DECAY could not do anything before this audit, so the shipped values have never been tested. Enabling the ladder is not automatically good and this strategy shows why it needs measuring rather than assuming.

Measured at the strategy's own calibrated capital over 3 years × 7 markets: median return fell from 8.1% to 6.1%, median drawdown halved from 4.2% to 2.2%, and profitable markets went 7/7 → 5/7. Risk-adjusted that is better — return per unit of drawdown rises from 1.9 to 2.8 — but the hit rate is worse and the capital deployed roughly tripled.

That is a real trade-off, not a clear win, and the right value of MAX_DCA is an open question between 1 and 5. It should be swept alongside DCA_Z_STEP, since the two interact directly: a deeper per-layer threshold makes a longer ladder safer.

2. Now test the regime-break guard that has never run

High priority

DCA_REGIME_GUARD blocks adding a layer when the OU fit is deteriorating. It is gated on dcaLevel > 0, so it could never have fired — there was never a layer beyond the first.

This is the mechanism that should protect the ladder from exactly the failure mode that hurt the other averaging-down strategies in this pack: buying all the way down through a regime change. It is now live and completely unmeasured. Testing it with the guard on and off, at a few MAX_DCA values, would say whether it does the job it was written for — and this strategy is the only one in the pack that has such a guard, so the answer is worth knowing.

3. Check whether the OU fit is being refreshed sensibly

Worth testing

θ and μ are re-estimated by OLS over a rolling window, and everything downstream — entry thresholds, half-life, the regime-break test — depends on that fit. The window length is the single most consequential parameter in the strategy and it is not obvious from the code that it has been tuned.

Worth measuring: how often the fit changes materially, how the half-life estimate is distributed across markets, and whether the strategy's actual hold time matches the half-life it predicts. If realised holds are much longer than ln(2)/θ, the model is mis-specified for these markets and the thresholds are being set from a number that does not describe the data.

4. Measure it against cash, not buy-and-hold

Worth testing

It is profitable on 5 of 7 markets and loses to buy-and-hold on most, which over this window is the expected shape for anything that scales out into strength — buy-and-hold returned a median 39.2%.

The claim worth testing is the risk one: a 2.2% median drawdown while invested on 99% of bars is unusual, and that is what a mean-reversion model is for. Return on capital at risk and drawdown-adjusted return are the fair benchmarks; the buy-and-hold column answers a question this strategy was not built for.

5. The settings measurement does not cover this lineage

Read before acting on the others

Knob usage is proved by running the strategy 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.

So the report can say 59 settings are exposed in the GUI but cannot prove all 59 are read. Two settings on this strategy (MAX_DCA, DCA_DECAY) plus the per-layer threshold and the regime guard were all dead, and all of it was found by reading the control flow by hand. A proxy over P would catch this class automatically across the whole lineage.

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
18
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
21
Section headers
4
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

59 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_LIMITMR_TLMR_CAPITAL_ALLOCALLOC_PCTALLOC_CONFIRMEDALLOC_SPLIT_TOLALLOC_ENFORCE_SPLITALLOC_AUTO_REBALANCEALLOC_RESERVE_PCTMR_GRIDMR_GRID_LEVELSMR_GRID_SPAN_PCTMR_OU_LBMR_FRESHBAR_ONLYMR_BERTRAMMR_BERTRAM_CMR_BERTRAM_GAMMAMR_BERTRAM_MIN_HLMR_ENTRY_ZMR_EXIT_ZMR_MAX_DCAMR_DCA_DECAYMR_DCA_ZMR_GAINMR_COMPOUNDMR_MAX_HOLDMR_SPLOG_LEVELWARMUP_CYCLESMR_FEEMR_MAX_DDMR_MAX_EXPMR_DAILY_LOSSBE_GUARDNO_POST_ONLYNO_CLOSE_MARKETMR_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_ALLMIN_ORDER_QUOTEMR_PERIODMR_CONSEC_LOSS_MAXMR_DCA_REGIME_GUARDRESET_BREAKER_ONCERESET_STATS_ONCEQUANTRODUCTION_TRACESPREAD_PNL_JUMP_GUARD

Open presentation findings