← dipHunter

dipHunter — Fixes

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

Every defect the audit found and how it was repaired — the same measurements as the engineering record, without the code.

3 issues found and repaired — 2 defects, 1 warning.

# Issue Severity Surface
1 87% of this strategy's exits happened without it noticing DEFECT accounting
2 Seventeen settings could not be set to zero DEFECT inputs
3 Four panel rows had no explanation WARNING side panel

Every fix was verified by re-running the full backtest across all 7 markets and comparing the result record against the pre-fix baseline. Where a fix is stated to change no behaviour, the two records are identical to the byte.


87% of this strategy's exits happened without it noticing

Severity DEFECT · Surface accounting · Sites 4

Every cycle it holds a position, this file publishes ledger.customSellTarget = breakEven × (1 + TP_PCT/100) — a resting limit order at the take-profit price. Gunbot fills that order the moment price trades through it, and does not call back into the strategy to say so.

The file has exactly three places that account for a sale — the stop loss, TP1 and TP2 — and all three sit behind method.sellMarket. None of them runs when the resting order fills. So totalPnL, dailyPnL, tradeCount, wins, losses and fillLog — every number in the PERFORMANCE section of the side panel — simply did not see those exits.

Why it matters. This is not a rare edge. The resting target is priced at break-even + 2% and TP1 is priced at break-even + 2%: the same level. The resting order is checked against the bar's high before the strategy is even asked to decide, so it wins almost every time. Measured over three years on seven markets, the resting order took 1,799 of 2,072 exits — 86.8% — against 91 for TP1 and 182 for the stop.

So the operator watching the panel saw a position vanish with no log line, no notification, no chart mark and no change to the trade count, while the strategy's own win rate and P&L were computed from the 13.2% of trades it happened to perform itself.

Exits by path, 3 years × 7 markets — before the repair, only the last two rows were counted anywhere

exit path count share mean gain was it booked?
Resting target fill 1,799 86.8% +2.00% no — invisible
Stop loss 182 8.8% −16.07% yes
TP1 (sell half) 91 4.4% +3.25% yes
TP2 (sell all) 0 0% — yes, but never fired

The identity was confirmed rather than inferred: the count of unexplained exits equals the engine's restingFills counter exactly on all seven markets (263/263 ADA, 158/158 BTC, 262/262 DOGE, 269/269 ETH, 300/300 LINK, 320/320 SOL, 227/227 XRP).

The fix. Added a RECONCILE block at the top of the cycle that detects a drop in the holding the strategy did not itself ask for, and books it: realised P&L, trade count, win/loss, fill log, a log line and a panel notification, plus a state reset when the position is now flat.

Detection is by quantity rather than by order feed, so it does not depend on what a particular venue reports. expectQty — recorded at each of the three explicit sell sites — is what the strategy expects to be left after its own sale, so its own exits are not counted twice. The fill is priced at the resting target the file published on the previous cycle (lastTarget), which is where such an order fills by definition.

Effect on live behaviour. None. Re-ran the full 3-year backtest on all seven markets and diffed against the pre-fix baseline: buys, sells, return and max drawdown are identical to the last decimal on every market. The repair changes what the strategy reports, not what it does.

One latent difference is worth stating plainly rather than leaving to be discovered. The circuit breaker trips on consecutiveLosses >= 3, and that counter now also sees the resting exits — which are profitable, so they reset it. On this data it made no difference because the circuit breaker never fired at all, in either version, on any of the seven markets (verified by counting its log line across every bar). It could differ on data where three stops land in one day.

Scope beyond this strategy. Found here first. 373 of the 429 strategies in the pack publish a resting customSellTarget, so the same shape has to be checked on each of them as the audit reaches it — the evidence tool now reports it automatically as a share of unexplained exits.


Seventeen settings could not be set to zero

Severity DEFECT · Surface inputs · Sites 16

Every tunable was read through a fallback idiom meaning use the operator's value, or the baked default. In JavaScript zero is falsy, so an operator who set a value to zero silently got the shipped default instead.

Why it matters. Zero is how most of these features are turned off. DH_MAX_DCA: 0 was meant to disable the DCA ladder and instead ran it at depth 1; DH_SP: 0 was meant to mean whole units. The GUI accepted the value, the settings readout echoed it back, and the strategy ignored it — the worst of the three possible behaviours, because it looks like it worked.

Settings that can now hold 0

count
settings repaired 16 sites, 17 keys
including DH_MAX_DCA, DH_DCA_DROP, DH_DCA_DECAY, DH_TP_PCT, DH_TP2_PCT, DH_RSI_BUY, DH_MFI_BUY, DH_MAX_EXP, DH_FEE, DH_SP, DH_MAX_DD, WARMUP_CYCLES, MIN_ORDER_QUOTE

The fix. Rewritten to a reader that falls back only when the setting is genuinely absent or unparseable. The fixer skips any file where an affected knob is used as a divisor, so a zero cannot turn a working strategy into a division by zero.

Effect on live behaviour. None at the shipped defaults — verified by the byte-identical re-run. It changes behaviour only for an operator who had actually set one of these to zero, which is the point.

Scope beyond this strategy. 1,743 call sites across 70 files pack-wide; this is this file's share.


Four panel rows had no explanation

Severity WARNING · Surface side panel · Sites 4

Total PnL, Today PnL, Trades and Cycle rendered with a value and no tooltip, so the number had no explanation anywhere in the GUI.

Why it matters. These four are exactly the rows the silent-exit defect above was corrupting. A Trades count with no tooltip gives the operator nothing to check it against — and it was wrong by a factor of seven.

The fix. Filled from the shared label glossary, which refuses to invent text for a label it does not know rather than guessing.

Effect on live behaviour. None — presentation only.

Scope beyond this strategy. 1,433 rows across 392 distinct labels pack-wide.