feat(library): SuperTrend flip strategy and the kernels behind it - #15
Conversation
Adds SUPERTREND and SUPERTREND_DIR to nano/indicators/, and a trend strategy that reads a host-published flip. The kernels share one recursion because the line and the direction are two readings of the same state: the line is whichever final band the direction selects, and the direction changes only on a strict break of the band the line sits on. Two conventions are pinned rather than left to the caller, since an unpinned convention is where two runtimes silently disagree about which bar a flip happened on: the first warm bar seeds from where it closed inside its own range, and a close resting exactly on a band has not broken it. ATR and TR already existed and are unchanged. trend/supertrend_flip_long stays in the v0.1.0 subset, so the flip arrives as a host-published 0/1 series rather than a computed call. Its three floors are named in the header: a volatility floor so a flip in a dead tape is not tradeable, a risk floor so the stop sits outside the noise it must survive, and a reward:risk floor so the target pays for the stop. An intent carries an action, an asset, and a confidence and nothing else, so the bracket cannot be output. It is expressed as preconditions the host must already have measured, and the host recomputes and owns the bracket. nano/library/README.md now records that boundary. No performance claim is made anywhere; the rule proposes, and the host gate disposes. Tests: kernel fixtures are hand-computed six-bar tapes with the arithmetic written beside each assertion, covering the seed tie, the exact-band rest, and a gap that re-seeds rather than smoothing across the hole. Every strategy no-fire case is paired with a positive control per PR #14. 415 passed, 2 skipped.
#18 landed the contribution checker, the watchdog library category, and drift guards on the library counts, which conflicted with this branch's own count updates. Both conflicts were the same class: a strategy total that each side had bumped independently. Resolved to main's table shape with trend/ at 4 to include supertrend_flip_long, and the library README total at 35. The entry itself needed no changes. It already passes the checks #18 added: its _ir.json was written in the library's one-node-per-line house format, and its comment header carries all five required fields plus a NOT line. The full suite is 471 passed, 2 skipped and check_contribution.py --all reports 35 entries ready for review. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Unblocked — #18 landed and conflicted with this branch on two count claims. I merged Both conflicts were the same class: a strategy total each side had bumped independently. Resolved to main's table shape with The entry itself needed no changes. It already passes everything #18 added — its Worth knowing for the next entry on this branch: Leaving the merge decision to you — I have not reviewed the SuperTrend kernels or the strategy thesis, only unblocked the mechanics. |
What this implements
Two indicator kernels and one library strategy.
SUPERTRENDandSUPERTREND_DIR(nano/indicators/compute.py,nano/indicators/registry.py). ATR and TR already existed and are untouched — only SuperTrend was missing. Both kernels share one recursion, because the line and the direction are two readings of the same state: the line is whichever final band the direction currently selects. Running the recursion twice would let the two answers drift apart on a gap.Signature:
SUPERTREND(series<float> high, series<float> low, series<float> close, int period, float mult), returningseries<float>;SUPERTREND_DIRis the same signature returningseries<bool>. Warm-up is_first_period()— the kernel seeds on the first bar ATR exists, adding no lag of its own.Two conventions are pinned in the kernel rather than left to the caller, since an unpinned convention is exactly where two runtimes silently disagree about which bar a flip happened on:
close >= (high + low) / 2is up). There is no flip on a seed bar.Gap behavior follows the module's existing doctrine: like EMA, RSI, ATR and OBV, a
Noneclears the state and the kernel re-seeds from the next full window rather than smoothing across the hole.nano/library/trend/supertrend_flip_long.nanoplus its pinned IR. It stays in the v0.1.0 subset perCONTRIBUTING.md, so the flip arrives as a host-published 0/1 series rather than a computed call — a computed call would emit v1.0 IR and move the library's pinned fixtures.Parameters
Every number in the rule is one of three named floors, stated in the source header so it can be argued with rather than absorbed:
ATR_PCT(10) >= 0.05STOP_DISTANCE_ATR(10) >= 0.5TARGET_R >= 2.33with
SUPERTREND_FLIP_BULL(10) >= 1as the event itself. Feed conventions:SUPERTREND_FLIP_BULLis 1 on the barSUPERTREND_DIRturned bullish;STOP_DISTANCE_ATRis(entry - protective stop) / ATR(10);TARGET_Ris planned target distance over planned stop distance. All four are added to the signal-convention table innano/library/README.md.What the rule refuses to do
It proposes an
Intentand nothing more.IntentNodecarries an action, an asset and a confidence — there is no field for a stop price, a target price, or a quantity, in baseline IR or in v1.0. So the bracket cannot be output; it is expressed as preconditions the host must already have measured, and the host that publishedSTOP_DISTANCE_ATRandTARGET_Rrecomputes the bracket and owns it. The rule asserts only that on this bar a plan clearing all three floors existed.nano/library/README.mdnow records that boundary under "Intent boundary", since it applies to any future rule with a bracket.No performance claim is made
Nothing here has been backtested and no return, win rate, expectancy, or drawdown figure appears anywhere in the diff. The
2.33reward:risk floor is a parameter of the rule, not a measured result — it is the ratio from the reference setup this was written against, and it is a threshold the host must clear, not an outcome anyone achieved.CALIBRATED ON:in the header follows the existing library convention of stating what does and does not transplant across instruments and timeframes; it is not a claim that the rule works. The library remains a conformance corpus.Tests
Kernel fixtures are hand-computed six-bar tapes with the arithmetic written beside each assertion — TR, ATR(2), the bands, and the resulting line and direction — not self-comparisons. They cover the seed tie (
close == hl2), the exact-band rest, a bear flip and a bull flip, and a gap whose post-hole values differ from the ungapped tape, which is what proves the recursion re-seeded instead of smoothing through.Strategy tests cover the three no-fire cases — no flip, flip in a tape below the volatility floor, and a missing signal — each paired with a positive control that does fire on an otherwise identical tape, per the convention from #14. The missing-data case asserts
RuntimeError_rather than a defaulted value: a host that has not publishedTARGET_Rhas not decided the target is unacceptable, it has decided nothing, and defaulting either way would be the runtime inventing the risk decision it exists to relay.The CI
packagejob was reproduced locally: both new library files land in the wheel.Doc counts corrected alongside: 33 -> 35 kernels (
README.md,docs/status.md), 26 -> 27 strategies and the trend row 3 -> 4 (README.md,nano/library/README.md).