FIL_Grid_Engine — Status & roadmap
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 | 7 rows, padded to the 3-column grid |
| Chart marks | reference-levels |
| Palette | 8 / 8 canonical |
| GUI settings | 23 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 -2.8% against buy-and-hold's 49.6% — an edge of -53.4% — and is profitable on 0 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
The return figures on this page are cut short, and by a lot
This strategy halts permanently the first time price falls 3% below the bottom of its grid, and nothing in the file ever clears that state. Across a three-year test that means it trades for a while, stops, and the rest of the window is a flat line — so time in market reads 0% on all seven markets, and the trade counts are 15 to 61 over 111,044 bars.
Run with the halt disabled, the same strategy on the same markets takes 88 to 2,546 trades, and the seven returns spread over 89.9 percentage points instead of 4.1, with three of seven profitable and SOL at +57.4%. The median hardly moves — −2.80% becomes −2.22% — which is the point: as shipped this cannot lose much and cannot win at all, because it stops before either can happen.
Read every number below as the result up to the halt. The fixes report carries the full comparison.
A resting-limit-order grid. It picks a price range, cuts it into 36 levels, arms a buy limit at every level below the current bid, and gives each filled level its own take-profit sell. A level that sells is re-armed as a buy. That is the whole engine; everything else is a guard on top of it.
The range comes from one of five bounds modes (GQ_FG_BOUNDS):
| mode | range |
|---|---|
Rolling (default) |
highest high / lowest low over the last 300 candles |
ATR |
last close ± 10 × ATR(14) |
Bollinger |
100-period mean ± 2.5 σ |
Percent |
last close ± 25% |
Manual |
GQ_FG_HIGH / GQ_FG_LOW |
and the levels are spaced by one of eight functions — Geometric (default), Arithmetic, Quadratic, Cubic, Power, Sigmoid, Logarithmic, Fibonacci. Take-profit is NextGrid by default: each slot sells at the level above the one it bought at.
The guards are a trend guard that pauses new buys while price is below a falling 200-EMA, a cap of 200 concurrent filled slots, and a hard stop that market-closes the whole basket if price falls 3% below the grid floor — after which halted is set and the strategy never trades again until the pair is reset by hand.
The range is computed once, at initialisation, and never rebuilt. hi and lo are assigned on exactly one line in the file, inside if (!isInitialized), and no other code path touches them. Everything measured about this strategy follows from that.
Read from the shipped strategy source, not from its documentation.
The measured constraint
Time in market is 0.1%, over 225 round trips across all 7 markets and three 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 2.8% 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. Rebuild the grid when price leaves it and no slot is held
Highest priority
This is the whole strategy in one decision. The range is fixed at initialisation, so the grid is a bet that price returns to a window chosen in the strategy's first hour — and over three years it usually did not.
The measured relationship is almost mechanical: the share of bars price spent above the frozen grid predicts how much the strategy traded.
| market | grid (frozen) | price afterwards | bars above grid | buys |
|---|---|---|---|---|
| BTC | 26,808 – 35,280 | 33,596 – 126,011 | 99% | 0 |
| SOL | 21.66 – 33.90 | 31.50 – 286 | 100% | 4 |
| DOGE | 0.0574 – 0.0752 | 0.0661 – 0.4751 | 94% | 17 |
| ETH | 1,542 – 1,866 | 1,419 – 4,935 | 91% | 18 |
| ADA | 0.2394 – 0.2980 | 0.1389 – 1.30 | 79% | 580 |
| XRP | 0.4769 – 0.5866 | 0.3979 – 3.65 | 77% | 85 |
| LINK | 7.21 – 11.79 | 7.03 – 30.36 | 70% | 445 |
On BTC it placed not one order in three years while reporting itself Active. The two markets it worked on are the two where price came back down.
A rebuild is safe under one condition, and the file already tracks it: gridSellOrders holds the filled-but-unsold slots, so when that is empty the grid holds no inventory and the bounds can be recomputed without orphaning anything. The rule to test is rebuild when the slot map is empty and price is outside [lo, hi]. Measure it against the frozen version on all seven markets before adopting it — a grid that chases a trend up can also buy every step of the way down, which is exactly the failure the trend guard exists for, so the two interact and need testing together.
This was deliberately not changed in this audit: it is the strategy's defining behaviour, not an oversight, and changing it is a new strategy rather than a repair. What the audit did instead was make the condition visible — see the fixes.
2. Three of the five bounds modes describe a band that tracks price, then never track it
High priority
ATR is last close ± 10 × ATR. Percent is last close ± 25%. Bollinger is a 100-period mean ± 2.5 σ. All three are defined relative to where price is now, and all three are evaluated once and frozen forever.
For Manual that is exactly right, and for Rolling it is at least arguable — a fixed historical range is a coherent thing to trade. For the other three it contradicts the mode's own definition. An operator choosing Percent is asking for a band around the current price and gets a band around the price on the day they started.
So this is not the same question as the rebuild above. Even if the frozen design is kept, these three modes should either recompute or be withdrawn, because as shipped they do not do what their names say. Cheapest test: run all five modes on all seven markets with the range recomputed each cycle, and compare against frozen. If frozen wins for Rolling and loses for the other three, that settles both questions at once.
3. The hard stop halts the pair permanently and nothing clears it
High priority
The code behind this is in the engineering record, which this site does not publish.
Once the basket stop fires, every later cycle returns at if (halted) with HALTED after stop-loss. Re-arm by resetting the pair. There is no cooldown and no automatic recovery — the operator must intervene.
The generated FILGrid_* variants, which are the same engine from the Gunbot Quant generator, handle this differently: they carry a cooldown setting (8 bars by default) and make the halt itself optional via haltOnSl. This file has neither. Worth porting both, because a permanent halt that needs a human is a strategy that stops working the first weekend nobody is watching — and it did not fire once in this three-year run, so its behaviour is entirely unmeasured.
The wider lesson from this pack applies here: a latch that only a human can clear is the same shape as the circuit breaker that silently disabled btcRegimeScaler for 88% of its run.
4. Measure it where a grid is supposed to work
Worth testing
Median return over the three years is 0.0% against a buy-and-hold of 39.2%, and time in market is 4.1%. Those numbers say almost nothing about the strategy, because on five of seven markets it barely traded — they are mostly a measurement of a stranded ladder.
The two markets where it did trade are the interesting ones: LINK returned 9.0% on 445 round trips with a 36.4% drawdown, and ADA returned −2.1% on 573 round trips with 22.7%. Both ranged rather than trended. That is the regime a grid is for, and it is where the design should be judged.
So the honest next measurement is a regime-conditional one: split every market into trending and ranging stretches (the 200-EMA slope the trend guard already computes is a serviceable classifier) and report the strategy separately on each. A grid that makes money in ranges and merely sits out trends is a usable component; a grid that loses in both is not. The current single median cannot tell those apart.
5. The same freeze is in all 15 fil-grid files, and the generated ones document it
Read before acting on the others
The 14 FILGrid_* strategies are the Gunbot Quant generator's version of this engine — a different implementation with an F_ settings prefix — and they freeze the grid the same way, in if (neither fil nor ...). Their header says so outright: "A FIXED grid of N levels between bounds computed once from a lookback window."
This file's header does not. It lists the five bounds modes and never mentions that they are evaluated once, which is why the behaviour reads as a defect here and as a documented constraint there. The wording has not been changed, because the right fix is the rebuild rather than a better description of not having one — but if the frozen design is kept, this file's header should say so as plainly as the generated ones do.
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.
- Tagged, aligned, coloured
- 18
- Raw and off-format
- 0
- Exempt (object and error dumps)
- 6
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.
- Data rows
- 7
- Section headers
- 1
- Rows with no tooltip
- 0
- Padded to the 3-column grid
- yes
- Model
- reference-levels
draws price levels into customChartTargets and no per-trade marks, so there is no intent-vs-fill distinction to get wrong
- 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
23 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.
GQ_FG_LEVELSGQ_FG_SPACINGGQ_FG_POWERGQ_FG_SIGMOID_KGQ_FG_BOUNDSGQ_FG_RANGE_LBGQ_FG_HIGHGQ_FG_LOWGQ_FG_ATR_LENGQ_FG_ATR_BANDGQ_FG_BB_LENGQ_FG_BB_STDGQ_FG_PCT_WIDTHGQ_FG_ATR_MULTGQ_FG_TREND_LENGQ_FG_TP_MODEGQ_FG_TP_PCTGQ_FG_LADDER_NGQ_FG_SL_ENABLEGQ_FG_SL_OFFSETGQ_FG_TREND_GUARDGQ_FG_TREND_SLOPEGQ_FG_MAX_SLOTS
Open presentation findings
- NOTE · console — emits the tagged format inline at 18 call site(s) rather than through a shared emitter — the output is right, but the format is copied rather than defined once