← Changelog

Numerical Safety and Warmup Robustness Sweep

Patch7 June 2026· library-wide

Audit / Issue Found

A scan was performed for the class of bug a Gunbot dev would flag first on review: divisions by zero, indicator reads before warmup, parseFloat of undefined config without a default, and any path where bad market data silently produces a not-a-number that propagates into downstream order math. The strategies were checked for unguarded arithmetic on values that can legitimately be zero or undefined during a normal bot lifecycle.

Before the patch

Two real risk classes were found.

First, the two moon-phases strategies, both the spot and the futures version, were placing a grid of orders by dividing the total capital by the number of grid levels with no check that the number of levels was greater than zero. In a healthy run this is fine because the configuration always produces at least one level, but during a warmup tick where the reference price had not landed yet, or with a misconfigured zero-level setup, the divider would be zero. That makes the per-level amount infinite, then turns the order quantity into not-a-number, and then the strategy hands a not-a-number quantity to the exchange method. Some exchanges reject this loudly, some accept it and behave in unpredictable ways.

Second, four market-maker strategies, cartea-jaimungal, microprice, queue-reactive, and OFI flash, were computing a normalised inventory metric by dividing the open position by the trading limit over mid. With no guard on mid being greater than zero, a warmup tick where bid and ask are both zero would make the intermediate division infinite and silently zero out the normalised inventory. The strategy would then mistakenly think it was flat and bypass the position-cap gate that depends on this metric.

After the patch

The grid placement on both moon-phases versions now refuses to proceed if the grid calculation returned an empty array of levels, if the spendable capital is zero or negative, or if the reference price is zero or negative. Each refusal writes a warning to the log so you can see exactly why the strategy skipped a grid placement.

The four market-maker strategies now compute the normalised inventory only when mid and the trading limit are both positive. Otherwise the metric defaults to zero, which is the safe interpretation. The downstream gates that read this metric will not be fooled by a bad data tick.

Other findings from the scan were checked and confirmed clean. The codebase has good habits in most places: every parseFloat of a config field uses a default value, every reduce over an array uses an explicit initial value, and the dollar-cost-averaging average-cost math is correctly guarded by a positive-total check. Indicator reads have isFinite checks in the strategies that needed them.

Bottom line

The strategies are now safe against the kind of bad data that shows up during warmup, a brief exchange data hiccup, or an unusual setting. Orders cannot be sent with a not-a-number quantity, and the inventory gate cannot be silently bypassed.