Skip to content

feat(library): SuperTrend flip strategy and the kernels behind it - #15

Merged
AetherAI3 merged 2 commits into
mainfrom
feat/supertrend-flip-long
Aug 19, 2026
Merged

feat(library): SuperTrend flip strategy and the kernels behind it#15
AetherAI3 merged 2 commits into
mainfrom
feat/supertrend-flip-long

Conversation

@AetherAI3

Copy link
Copy Markdown
Owner

What this implements

Two indicator kernels and one library strategy.

SUPERTREND and SUPERTREND_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), returning series<float>; SUPERTREND_DIR is the same signature returning series<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:

  • Seeding — the first warm bar has no prior trend to continue, so direction is read from where that bar closed inside its own range (close >= (high + low) / 2 is up). There is no flip on a seed bar.
  • Flipping — the trend changes only on a strict break of the active band. A close resting exactly on the band has not broken it.

Gap behavior follows the module's existing doctrine: like EMA, RSI, ATR and OBV, a None clears the state and the kernel re-seeds from the next full window rather than smoothing across the hole.

nano/library/trend/supertrend_flip_long.nano plus its pinned IR. It stays in the v0.1.0 subset per CONTRIBUTING.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:

Parameter Condition What it buys
volatility floor ATR_PCT(10) >= 0.05 a flip in a dead tape is noise, not a trend change
risk floor STOP_DISTANCE_ATR(10) >= 0.5 the stop must sit outside the noise it is meant to survive
reward:risk floor TARGET_R >= 2.33 the target must be worth the risk the stop already defines

with SUPERTREND_FLIP_BULL(10) >= 1 as the event itself. Feed conventions: SUPERTREND_FLIP_BULL is 1 on the bar SUPERTREND_DIR turned bullish; STOP_DISTANCE_ATR is (entry - protective stop) / ATR(10); TARGET_R is planned target distance over planned stop distance. All four are added to the signal-convention table in nano/library/README.md.

What the rule refuses to do

It proposes an Intent and nothing more. IntentNode carries 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 published STOP_DISTANCE_ATR and TARGET_R recomputes the bracket and owns it. The rule asserts only that on this bar a plan clearing all three floors existed. nano/library/README.md now 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.33 reward: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 published TARGET_R has 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.

python -m pytest -q   ->  415 passed, 2 skipped   (main: 401 passed, 2 skipped)
python -m ruff check nano tests  ->  All checks passed!

The CI package job 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).

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>
@AetherAI3

Copy link
Copy Markdown
Owner Author

Unblocked — #18 landed and conflicted with this branch on two count claims. I merged main in and resolved them; back to MERGEABLE/CLEAN with all six checks green.

Both conflicts were the same class: a strategy total 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 everything #18 added — its _ir.json was written in the library's one-node-per-line house format, and its comment header carries all five now-required fields plus a NOT line. check_contribution.py --all reports 35 entries ready for review., and the suite is 471 passed, 2 skipped.

Worth knowing for the next entry on this branch: python scripts/check_contribution.py --write <file>.nano now generates the IR partner in house format, so that step no longer has to be done by hand.

Leaving the merge decision to you — I have not reviewed the SuperTrend kernels or the strategy thesis, only unblocked the mechanics.

@AetherAI3
AetherAI3 merged commit ea5ee61 into main Aug 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants