Skip to content

docs(prd): what keel can learn from Jesse, which is not its strategies - #530

Merged
eaitbrahim merged 2 commits into
mainfrom
docs-jesse-prd
Aug 24, 2026
Merged

docs(prd): what keel can learn from Jesse, which is not its strategies#530
eaitbrahim merged 2 commits into
mainfrom
docs-jesse-prd

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

PRD from comparing keel's Rule ABC against the Jesse framework's Strategy API 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_breakout negative 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_trials in research/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's hyperparameters() 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 and pullback_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/strategies is Cloudflare-protected and could not be read; the analysis is from jesse-ai/example-strategies and 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.

eaitbrahim and others added 2 commits August 23, 2026 10:59
…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
@eaitbrahim
eaitbrahim merged commit 7c2455a into main Aug 24, 2026
5 checks passed
@eaitbrahim
eaitbrahim deleted the docs-jesse-prd branch August 24, 2026 05:48
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.

1 participant