feat(sl-hunting): v5f, SLH-017, SLH-018 and the 17 SEP pre-open note - #172
Merged
Merged
Conversation
… is the receipt
16 Sep. Same pre-open note, same morning, opposite branch - and IH won.
The note shipped for today was conditional: gap up -> BUY the trapped sellers,
flat or gap down -> SELL with the market. NIFTY opened +82 points (+0.36%),
BankNIFTY +0.38%.
IH: "today we saw a FLAT opening, and with this kind of opening we are going to
follow the CONTINUED SELLING... if a VERY BIG gap up had opened we could not
have worked like this." He bought puts across all three indices and booked
around his 3-lakh target on a sharp simultaneous breakdown.
The agent: "NIFTY gapped +82pts (0.36%) ... after a 457pt breakdown day that
seated positional sellers, satisfying the pre-open note's GAP-UP->BUY branch."
Four entries, all LONG, inside 53 minutes, basket -Rs.863.75. Three lost; the
only winner was closed by an erroneous no-reason exit.
The proximate cause is a v4t VIOLATION rather than a knowledge gap. v4t already
calls the classification a judgement you must make and state, already calibrates
it ("a quarter of a percent is not a gap... something in the region of half a
percent is where the gap reading starts to earn itself"), and already says
hesitation resolves to flat. 0.36% is below that line. The number was computed
and written into all four entry rationales and never once tested against the
rule. This is the THIRD session decided by that one word: 31 Aug produced v4t,
01 Sep produced its v4u 3:15 sub-rule at a cost of Rs.1,488.25, this one cost
Rs.863.75, and nothing downstream was wrong on any of the three.
What IS net-new is the premise UNDERNEATH the branch, and it is why the agent
wanted the gap-up branch at all. All four rationales asserted the 457-point
breakdown had "seated positional sellers". IH argues the opposite with a
mechanism: "when do sellers REMAIN seated? When a retracement happens. When
there is no retracement, every trader keeps fearing that a retracement might
come" - so on continuous momentum "traders do come, but they also book their
target and leave", and "the chances of HOLDING are low here". A violent one-way
session ends with almost nobody carrying inventory, however large its range.
He then gives the falsification test, which is the part that runs BEFORE the
trade: "but if sellers had been seated in good quantity, what would the market
have opened as? We would have seen a straight gap up." The open is the receipt
for the premise. A small gap is not a weak version of that open; it is the
evidence against it.
Placed directly after the WHOLE v4t block - including its v4u sub-bullet, which
an earlier attempt split and orphaned - so the model reads "decide which word
describes the open", then "here is what the open's size is measuring". It is
v5c one scale up: there a retracement recruits a crowd inside the move, here a
retracement is what lets a crowd survive into the next session.
Negative-tested 14 ways, all 14 caught, plus a control mutation of unasserted
prose that correctly did not trip the test. One anchor initially matched zero
times because it began mid-line rather than at a line start; re-run
line-contiguously, it caught.
Recorded as open and NOT fixed here, both runtime changes kept out of a
knowledge-only PR:
1. The open classification should stop being prose. Three sessions, two
existing rules, same failure - which is exactly the corpus's own PROSE RULES
DON'T BIND / JUDGEMENT RULES GET TALKED PAST law. The gap percentage against
the 3:15 reference is computable before the first decision.
2. An EXIT carrying no reason executed and closed the day's only winner. The
tool logged "probable UNINTENDED order" and executed it anyway, closing a
+Rs.1,182 basket; the model's next decision called it erroneous itself, and
that trade's 23276 target printed at 09:18, about 100 seconds later. The
warning exists; the refusal does not.
Gates: 606 master, 28 market-data-health, 1497 pytest (+1), ruff 0.16.7, mypy
(80 files + master), compileall, bandit, coverage 73.7% policy pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ing it
Follow-up to v5f on this PR, and the arm of that session that had to become
code.
Three sessions in this book were decided by one word, with two knowledge rules
already in place specifically to get it right: 31 Aug produced v4t, 01 Sep
produced its v4u sub-rule at a cost of Rs.1,488.25, and 16 Sep cost Rs.863.75.
On 16 Sep the model computed the deciding number correctly - "+82pts (0.36%)" -
wrote it into all four entry rationales, and never compared it against v4t's own
calibration. That is exactly the shape the corpus already has a law for: PROSE
RULES DON'T BIND, and JUDGEMENT RULES GET TALKED PAST.
pivot_and_levels now returns an open_classification block carrying
previous_close, today_open, gap_points, gap_pct, threshold_pct, a verdict of
FLAT / GAP_UP / GAP_DOWN, and the reference it measured against.
Neither number is invented. The 0.5% threshold is v4t's own calibration ("a
quarter of a percent is not a gap... something in the region of half a percent
is where the gap reading starts to earn itself"), and sub-threshold opens read
FLAT in BOTH directions because v4t also says hesitation resolves to flat. The
reference is v4u's - the previous session's last candle rather than the official
15:30 close, because the closing auction carries settlement prints no crowd
traded around.
It reaches the model through the levels tool and, for BankNIFTY, through
bank_nifty. Run against 16 Sep's real numbers both come back FLAT (NIFTY 0.355%,
BankNIFTY 0.381%) - IH's reading of that morning, and the opposite of the one
the agent acted on.
WIRED, not merely added: v4t now carries an SLH-017 paragraph telling the model
to READ the verdict and state it, and not to recompute it or argue past it,
because an unwired fact is one the model re-derives its own way. The existing
v4t marker test was EXTENDED rather than rewritten, so its original assertions
still pin the original prose.
Scope kept narrow: this supplies the arithmetic and the threshold. It rejects no
orders and does not judge what the classification MEANS - that stays with the
model, which is where the actual reasoning is.
TDD: six tests RED first, then the implementation. Negative-tested 9 ways, all 9
caught, plus a control mutation of unasserted prose that correctly did not trip
the tests. One mutation - measuring from today's LAST price instead of its OPEN
- initially PASSED, because every fixture opened and closed at the same price.
That was a real defect the tests could not see: a flat open would silently
become a "gap up" by mid-morning and re-select the branch hours after the fact.
A dedicated drift test was added and the mutation then caught.
NOT BUILT, and recorded as such: refusing an EXIT that carries no reason. That
was the session's other item, and it reverses a standing operator decision.
SLH-007 says an EXIT is never refused over its wording, because bouncing it
would strand an open position; SLH-010 explicitly declined to reverse it and
added the warning instead; and
test_exit_with_no_reason_still_executes_but_warns_loudly exists so that "a later
tightening cannot quietly turn the warning into a refusal". Building it would
mean deleting a guard written to prevent it. The doc records the narrow
one-round-bounce form worth considering if it is ever revisited, and that it
needs an explicit operator decision.
Gates: 606 master, 28 market-data-health, 1504 pytest (+7), ruff 0.16.7, mypy
(80 files + master), compileall, bandit, coverage 73.8% policy pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follow-up to v5f / SLH-017 on this PR, and an explicit operator decision rather than a fix I could take on my own. On 16 Sep at 09:16:38 an EXIT carrying no reason executed and closed the day's only winner (+Rs.1,182 basket), about 100 seconds before that trade's 23276 target printed. The order tool logged it as "a probable UNINTENDED order" and honoured it anyway; the model's own next decision called the call erroneous. I raised this as a gate and then declined to build it, because refusing such an exit reverses a standing decision: SLH-007 says an EXIT is never refused over its wording, since bouncing it would strand an open position; SLH-010 explicitly declined to reverse that and added the warning instead; and test_exit_with_no_reason_still_executes_but_warns_loudly existed so that "a later tightening cannot quietly turn the warning into a refusal". The operator then took the decision explicitly, choosing the one-round form on the reasoning that it "would filter out the anomalies and let SLH-007 work on genuine wordless exit cases". SLH-007's guarantee is NARROWED, not reversed. THE CONTRACT. The first EXIT that resolves to the no-reason sentinel is sent back once, with a rejection naming both ways forward - state the reason, or send the EXIT again. Any repeat is honoured unconditionally whatever it carries. The worst case is ONE decision cycle, never a stranded position, and the stop, target, max-loss and 15:15 square-off are host-owned throughout and are never gated by any of this. WHERE THE STATE LIVES MATTERS. The armed flag sits on the EXECUTOR, not on the tool context. A context is rebuilt every bar, so context-local state would bounce the first attempt of every bar forever and could genuinely strand a position - the exact outcome SLH-007 forbids. The executor is created once per worker and outlives the pass. Set and cleared duck-typed under contextlib.suppress, because TradeExecutor is a Protocol and a third-party executor need not tolerate the attribute. SCOPED PER EPISODE: any accepted order clears the arm - the repeat exit, a restated reason, or a new entry - so a bounce spent on this morning's slip cannot buy a later position a free pass. Placeholder reasons bounce too: _safe_exit_reason maps "placeholder", "n/a" and friends to the same sentinel as an empty string, so they are the same class of anomaly. test_do_order_never_rejects_an_exit_for_a_bad_reason was rewritten as test_a_junk_exit_reason_bounces_once_then_exits_and_records_the_sentinel, keeping its real assertions: the position still comes out, and the junk still never becomes the permanent record. THE TOOL DESCRIPTION HAD TO CHANGE, or the model keeps a false contract. It said an EXIT "is NEVER rejected". It now states the single exception and the way out of it, and the test asserts the stale absolute is GONE rather than merely that the new wording is present. A model told exits can never be refused, that then receives a refusal, has been handed a contract its tool does not keep - the SLH-017 unwired-fact failure arriving from the other side. TDD: five tests RED first, then the implementation. Negative-tested 7 ways, all 7 caught, plus a control mutation of an unasserted comment that correctly did not trip the tests. The two that matter most are both caught: removing the bounce, and never disarming (which would strand a position by refusing the second attempt as well). Gates: 606 master, 28 market-data-health, 1508 pytest (+4), ruff 0.16.7, mypy (80 files + master), compileall, bandit, coverage 73.8% policy pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Source: 'Prediction For 17 SEP 2026' (SQIo4PisLw0, uploaded 2026-09-16 21:25 IST). Addendum to the v5f / SLH-017 / SLH-018 PR. The branch SHAPE repeats 16 Sep's note - gap up -> BUY, flat or gap down -> SELL, stated for all three indices - so what the test pins is the different reasoning underneath it. Today's selling broke down and then recovered WITHOUT crossing the higher point or the round number, which leaves one question open: which side did the session trap? flat / gap down -> SELL the sellers were only trapped, never seated gap up -> BUY a lone deliberate buyer may be, and only then His mechanism for the sell branch: "seeing the retracement, who comes here? SELLERS come. But because the market stays SIDEWAYS they leave again." The sideways is what removed them, so there is no seller crowd left to squeeze and the plan is simply to go with the selling. The buy branch keys off what price did NOT do - "it did not cross the round number, it did not cross its high point" - so "maybe some good trader slowly bought here and other people could not buy". Because "the higher point was not crossed, retail did not do much work", only a deliberate buyer may be seated, never a crowd, which is exactly why that branch needs the gap up to confirm one exists rather than assuming it. He also flags SENSEX expiry twice, as context for the triple-index read and not as a premise. Levels, with the ASR garbles resolved by the operator against the video: NIFTY R 23420 / 23280 S 23110 / 23000 BANKNIFTY R 56300 / 56650 S 55810 / 55500 SENSEX R 75000 / 74500 S 74000 / 73650 Three needed confirming. NIFTY's first support came through as "2310" and is 23110 - a dropped digit, not a dropped trailing zero, so my 23100 reading was wrong. BankNIFTY's second support came through as "5500" again and is 55500 again. SENSEX was the worst yet: both pairs arrived as single runs, "7574500" and "7473650", and resolve to round thousands - 75000/74500 and 74000/73650 - which neither reconstruction I offered had guessed. Recorded in the test comments so the next garble of the same shape reads round numbers first. Negative-tested 18 ways on the note itself, all 18 caught, plus a control mutation of unasserted prose that correctly did NOT trip the test - including guards on all three corrected levels, so a silent revert to my wrong readings fails. No doc section: notes have not carried one (PR #168 precedent); provenance lives in the test docstring and here. Gates: 606 master, 28 market-data-health, 1508 pytest, ruff 0.16.7, mypy (80 files + master), compileall, bandit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four commits: the knowledge rule for this session, the two code gates for the arms of it that had to stop being prose, and tomorrow's pre-open note.
Same note, same morning, opposite branch
Source:
TXiU5Odet04, IH's live session, uploaded 2026-09-16 10:09 IST.The pre-open note shipped for today (PR #171) was conditional: gap up → BUY the trapped sellers, flat or gap down → SELL with the market. NIFTY opened +82 points (+0.36%), BankNIFTY +0.38%.
IH: "today we saw a FLAT opening, and with this kind of opening we are going to follow the CONTINUED SELLING… if a VERY BIG gap up had opened we could not have worked like this."
Agent: "NIFTY gapped +82pts (0.36%) and BankNIFTY +213pts (0.38%) above prior close after a 457pt breakdown day that seated positional sellers, satisfying the pre-open note's GAP-UP→BUY branch."
The proximate cause is a v4t violation, not a knowledge gap
v4t already says the classification is a judgement you make and state, already calibrates it — "a quarter of a percent is not a gap… something in the region of half a percent is where the gap reading starts to earn itself" — and already says hesitation resolves to flat. 0.36% is below that line. The number was computed and written into all four entry rationales and never once tested against the rule.
This is the third session decided by that one word:
Nothing downstream was wrong on any of the three.
What v5f actually encodes
Not the branch — the premise underneath it, which is why the agent wanted the gap-up branch at all. All four rationales asserted the 457-point breakdown had "seated positional sellers". IH argues the opposite, with a mechanism:
— so on continuous momentum "traders do come, but they also book their target and leave", and "the chances of HOLDING are low here". A violent one-way session ends with almost nobody carrying inventory, however large its range.
Then the falsification test, which is the part that runs before the trade:
The open is the receipt for the premise. A small gap is not a weak version of that open — it is the evidence against it.
A ONE-WAY DAY LEAVES NOBODY SEATED, AND THE NEXT OPEN IS THE RECEIPT (v5f) sits directly after the whole v4t block — including its v4u sub-bullet, which an earlier attempt split and orphaned before I caught it — so the model reads "decide which word describes the open", then "here is what the open's size is measuring". It is v5c one scale up: there a retracement recruits a crowd inside the move, here a retracement is what lets a crowd survive into the next session.
Verification
Negative-tested 14 ways, all 14 caught, plus a control mutation of unasserted prose that correctly did not trip the test. One anchor initially matched zero times because it began mid-line rather than at a line start; re-run line-contiguously, it caught.
Gates: 606 master · 28 market-data-health · 1508 pytest (+12) · ruff 0.16.7 · mypy (80 files + master) · compileall · bandit · coverage 73.8%, policy pass.
Considered and not encoded
BNF_SPECIFIC.Commit 2 — SLH-017, the open is classified in code
The arm of this session that had to stop being prose.
pivot_and_levelsnow returns anopen_classificationblock:Neither number is invented. The 0.5% threshold is v4t's own calibration; sub-threshold opens read FLAT in both directions because v4t says hesitation resolves to flat. The reference is v4u's, for v4u's stated reason — the closing auction carries settlement prints no crowd traded around.
It reaches the model through
levelsand, for BankNIFTY, throughbank_nifty. Run against 16 Sep's real numbers, both come back FLAT (NIFTY 0.355%, BankNIFTY 0.381%) — IH's reading of that morning, and the opposite of the one the agent acted on.Wired, not merely added. v4t now carries an SLH-017 paragraph telling the model to read the verdict and state it, and not to recompute or argue past it — an unwired fact is one the model re-derives its own way. The existing v4t marker test was extended, not rewritten, so its original assertions still pin the original prose.
Scope kept narrow: this supplies the arithmetic and the threshold. It rejects no orders and does not judge what the classification means — that stays with the model.
TDD: six tests RED first, then the implementation. Negative-tested 9 ways, all 9 caught, plus a passing control. One mutation — measuring from today's LAST price instead of its OPEN — initially passed, because every fixture opened and closed at the same price. That was a real defect the tests could not see: a flat open would silently become a "gap up" by mid-morning and re-select the branch hours after the fact. A dedicated drift test was added; the mutation is now caught.
Commit 3 — SLH-018, one round of friction on a reasonless EXIT
The session's other item: an EXIT with no reason closed the day's only winner (+₹1,182), about 100 seconds before that trade's 23276 target printed.
I raised this as a gate and then declined to build it, because refusing such an exit reverses a standing decision:
test_exit_with_no_reason_still_executes_but_warns_loudlyexisted so "a later tightening cannot quietly turn the warning into a refusal".The operator then took the decision explicitly, choosing the one-round form — it "would filter out the anomalies and let SLH-007 work on genuine wordless exit cases". SLH-007 is narrowed, not reversed, and the tightening is neither quiet nor unilateral.
The contract. The first EXIT resolving to the no-reason sentinel is sent back once, with a rejection naming both ways forward — state the reason, or send the EXIT again. Any repeat is honoured unconditionally whatever it carries. Worst case is one decision cycle, never a stranded position; stop/target/max-loss/square-off are host-owned throughout and never gated.
Where the state lives matters. The armed flag sits on the executor, not the tool context. A context is rebuilt every bar, so context-local state would bounce the first attempt of every bar forever and could genuinely strand a position — the exact outcome SLH-007 forbids. Set and cleared duck-typed under
contextlib.suppress, sinceTradeExecutoris a Protocol.Scoped per episode: any accepted order clears the arm, so a bounce spent on this morning's slip cannot buy a later position a free pass.
Placeholder reasons bounce too —
_safe_exit_reasonmaps"placeholder"/"n/a"to the same sentinel, so they are the same class of anomaly. That guard test was rewritten to the two-step contract, keeping its real assertions.The tool description had to change, or the model keeps a false contract. It said an EXIT "is NEVER rejected". It now states the single exception and the way out, and the test asserts the stale absolute is gone rather than merely that the new wording is present — otherwise the model holds a contract its tool does not keep, which is the SLH-017 unwired-fact failure arriving from the other side.
TDD: five tests RED first. Negative-tested 7 ways, all 7 caught, plus a passing control. The two that matter most are both caught: removing the bounce, and never disarming (which would strand a position by refusing the second attempt too).
Commit 4 — the pre-open note for 17 Sep
From
SQIo4PisLw0, uploaded 2026-09-16 21:25 IST.The branch shape repeats 16 Sep's note — gap up → BUY, flat or gap down → SELL — so what the test pins is the different reasoning underneath. Today's selling broke down and then recovered without crossing the higher point or the round number, which leaves one question open: which side did the session trap?
Because "the higher point was not crossed, retail did not do much work", only a deliberate buyer may be seated, never a crowd — which is exactly why that branch needs the gap up to confirm one exists rather than assuming it. SENSEX expiry is flagged twice, as context for the triple-index read, not a premise.
Three levels needed operator confirmation against the video. NIFTY's first support arrived as
2310and is 23110 — a dropped digit, not a dropped trailing zero, so my 23100 reading was wrong. BankNIFTY's second support arrived as5500again and is 55500 again. SENSEX was the worst yet: both pairs came through as single runs,7574500and7473650, resolving to round thousands — 75000/74500 and 74000/73650 — which neither reconstruction I offered had guessed. Recorded in the test comments so the next garble of this shape reads round numbers first.Negative-tested 18 ways, all 18 caught, plus a passing control — including guards on all three corrected levels, so a silent revert to my wrong readings fails.
No doc section: notes haven't carried one (PR #168 precedent).
🤖 Generated with Claude Code