docs(prd): what keel can learn from Jesse, which is not its strategies - #530
Merged
Conversation
…rrent one ratchets away from it (#520) v12 adds positions.initial_stop, written at tranche open and never rewritten. The break-even arm of exit_policy.next_stop computes its threshold from the trade's ORIGINAL per-unit risk: entry + be_roll_rr * (entry - initial_stop). Live state carried entry_fill (the ledger) and open_stop:<product_id> (the CURRENT, already-ratcheted stop) and nothing else -- so the number that threshold is most sensitive to was simply absent. Substituting the current stop is not an approximation, it is a DIFFERENT POLICY. The current stop rises on every ratchet, shrinking (entry - stop), so the threshold creeps toward entry and the arm fires earlier each time, drifting further from the measured policy the longer a trade runs. Live and sim would then encode two different break-even rules while appearing to share exit_policy's functions -- the exact failure sharing them was meant to prevent. NULL means UNKNOWN and readers must switch the break-even arm OFF for that tranche rather than guess. Zero would be a real number -- a stop 100% below entry -- and a catastrophic one to compute a threshold from, so the column is nullable and _position_row_to_dict is careful not to let _text_to_dec invent a zero. The trailing arm is unaffected either way: it needs no original risk, so a tranche without one keeps trailing and simply never break-even-rolls. NO BACKFILL, deliberately: the honest value for every pre-v12 tranche is NULL. Idempotent by the v8/v11 PRAGMA table_info guard, because a database stamped at v11 got positions from v4's DDL and CREATE TABLE IF NOT EXISTS never adds a column. DCA passes None and that is legitimate, not a degraded case -- it carries no stop by design, and requiring one would refuse a real tranche. The three schema-version tripwires were bumped 11 -> 12 consciously, which is what they exist to force. Gates: pytest 4559 passed / 3 skipped; ruff check keel tests packages clean; mypy clean across 354 source files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NyeggYtojNXCTHeD3JHxb6
#528, #529) Compares keel's Rule ABC against the Jesse framework's Strategy API and its published example strategies (jesse-ai/example-strategies -- jesse.trade is Cloudflare-protected, and reading the source is the better record anyway: what matters is what the framework lets a strategy SAY, not what its strategies claim to earn). The conclusion is deliberately narrow. keel is cost-bound, not signal-bound -- round-trip friction ~2.5%, no shipped rule family net-positive, turtle_breakout negative on all 24 assets measured -- so porting DUAL_THRUST, KDJ, Bollinger and the rest is the known dead end. A rule negative on 24 assets is not fixed by a 25th signal. What Jesse's API can express that keel's cannot: conditional entry at a chosen price, a per-bar update_position hook, pyramiding, partial exits, fill-event hooks, per-strategy state, and a declared hyperparameter space. Four of those are ALREADY keel's open issues (#333, #447, #502) -- an external framework independently re-deriving the same gaps is corroboration of the roadmap, not an addition to it, and the PRD explicitly declines to re-file them. Two things are genuinely new, and are filed: - #528: Jesse declares each strategy's parameter space. keel already consumes n_trials in research/deflate.py and reasons about the trials budget in source comments, but the number itself is hand-recorded. Declaring the space makes the input to keel's own overfitting correction derivable rather than remembered. Explicit non-goal: no optimiser -- running one against a cost-bound engine manufactures exactly what the deflated-Sharpe machinery exists to detect. - #529: Jesse strategies name their entry price and assume the fill. keel priced that assumption when #258 corrected the fill model and pullback_continuation's gross PF fell 0.92 -> 0.77. Any foreign strategy ported naively inherits an optimism this project has already measured. The document also records what keel does that Jesse does not -- rails, a detect() with no account access, fill fidelity, trials discipline -- so the comparison reads as a trade rather than a wishlist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NyeggYtojNXCTHeD3JHxb6
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.
PRD from comparing keel's
RuleABC against the Jesse framework'sStrategyAPI and its published example strategies.Milestone: Strategy-API expressiveness (Jesse comparison). Issues: #528, #529.
The conclusion is deliberately narrow
keel is cost-bound, not signal-bound — round-trip friction ~2.5%, no shipped rule family net-positive,
turtle_breakoutnegative on all 24 assets measured. So porting DUAL_THRUST, KDJ, Bollinger, SMACrossover and the rest is the known dead end: a rule negative on 24 assets is not fixed by a 25th signal.The lesson is the API, not the strategies.
Four of the gaps are already filed — that is the finding
Jesse can express: a conditional entry at a chosen price, a per-bar
update_position()hook, pyramiding, partial exits, fill-event hooks, per-strategy state, and a declared hyperparameter space.Four of those are already keel's open issues — #333 (conditional entry), #447 (pluggable rules), #502 (stop management, partial exits). An external framework independently re-deriving the same gaps is corroboration of the roadmap, not an addition to it, and the PRD explicitly declines to re-file them.
Two things are genuinely new
#528 — declare each rule's parameter space. keel already consumes
n_trialsinresearch/deflate.py::expected_max_sharpe, reasons per-choice in source comments about whether something "increments the trials budget (§73.12)", and keeps a ledger with explicit provenance. But the number itself is hand-recorded. Jesse'shyperparameters()makes it a property of the strategy. This is the one place Jesse's design is straightforwardly better for a discipline keel cares about more than Jesse does.Explicit non-goal: no optimiser. Running one against a cost-bound engine manufactures exactly what the deflated-Sharpe machinery exists to detect.
#529 — document the fill-model hazard. Jesse strategies name their entry price and assume the fill (
self.buy = qty, entry). keel priced that assumption when #258 corrected the fill model andpullback_continuation's gross PF fell 0.92 → 0.77 with double the trade count. Any foreign strategy ported naively inherits an optimism this project has already measured — and compounds it with the cost regime.Method note
jesse.trade/strategiesis Cloudflare-protected and could not be read; the analysis is fromjesse-ai/example-strategiesand the API surface those strategies exercise. Reading the source is the better record anyway: what matters is what the framework lets a strategy say, not what its strategies claim to earn.What it records in the other direction
The document lists what keel does that Jesse does not — 18 un-overridable rails, a
detect()with no account access so a rule physically cannot size its own position, compliance screening with attested provenance, fill fidelity, and trials discipline — so the comparison reads as a trade rather than a wishlist.Rule's statelessness is called out as a deliberate property to protect, not a deficiency to fix.Docs-only. No code change.