← KeelCONTENT HISTORY

Update to Keel

Snapshot Oct 24, 2026 · 13:41 UTC · version 1.0.0

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save Keel and check future changes in Following.

Up to 10 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Author a new Keel strategy from a natural-language thesis: fix path, universe and polarity; search every concept; batch-verify component types; draft the declarations and one Pipeline; dry-run until clean; save; and, when the user wants one, backtest and read the result. Use when the user says \"create / build / make me a strategy\", \"new strategy that ...\", or describes a trading idea and wants it implemented. Not for editing an existing strategy (strategy-fork-and-iterate) or for looking up one component (component-discovery).\n",
  "included_files": [],
  "name": "strategy-creation",
  "skill_md_contents": "---\nname: strategy-creation\ndescription: |\n  Author a new Keel strategy from a natural-language thesis: fix path, universe and polarity; search every concept; batch-verify component types; draft the declarations and one Pipeline; dry-run until clean; save; and, when the user wants one, backtest and read the result. Use when the user says \"create / build / make me a strategy\", \"new strategy that ...\", or describes a trading idea and wants it implemented. Not for editing an existing strategy (strategy-fork-and-iterate) or for looking up one component (component-discovery).\nreferences:\n  - step: 1\n    topic: \"strategy_paths\"\n    why: \"Path 1–4 chains, PSM vs ScalingPositionManager and when each applies, Path-2 direction and exits (the sizer table is in trading_domain)\"\n  - step: 1\n    topic: \"strategy_patterns\"\n    why: \"pattern snippets when the thesis names one: entry/exit, screen-select, pairs, beta hedge, DCA, regime\"\n  - step: 1\n    topic: \"trading_domain\"\n    why: \"the timeframe floor and the per-strategy timeframe table, carry and oscillator polarity, required component pairs, complexity ladder, the entry/exit sizer table\"\n  - step: 1\n    topic: \"pipeline_system\"\n    why: \"the type progression OHLCVDict → SignalSeries → … → WeightSeries\"\n  - step: 3\n    topic: \"reasoning_principles\"\n    why: \"the invariants to check on the mock: terminal WeightSeries, normalize only when needed, each transform once, path matches signal type, a discrete strategy needs a way out, universe before signal\"\n  - step: 4\n    topic: \"dsl_syntax\"\n    why: \"the declarations with one example; the Execution mode for each path; Parallel / Store / Load / variable / factory syntax\"\n  - step: 4\n    topic: \"composition_mechanics\"\n    why: \"Parallel semantics and branch isolation, the two mask systems, slot-reading components, loaders following Globals vs an own-clock branch\"\n  - step: 4\n    topic: \"universe_selection\"\n    why: \"Universe selectors, manual vs criteria modes, resolved semantics, groups — when the user names an asset scope\"\n  - step: 4\n    topic: \"data_loading\"\n    why: \"validated examples of every loader/clock shape — carry, own-clock smoothing, accrual, projection, two price clocks\"\n  - step: 5\n    topic: \"rule:<CODE>\"\n    why: \"the lesson behind any validation issue code the dry run returns\"\n  - step: 5\n    topic: \"mistakes\"\n    why: \"the structural mistakes by id — most now carry a validator code with its own fix; the few with none are checked by eye\"\n  - step: 5\n    topic: \"tool_usage\"\n    why: \"pipeline completeness: a backtest refuses a pipeline that does not end in weights, and names the missing step\"\ntools:\n  - keel_components_search\n  - keel_components_get_many\n  - keel_components_get\n  - keel_strategy_compose\n  - keel_backtest_run\n  - keel_backtest_summarize\n  - keel_backtest_compare\n  - keel_strategy_readiness\n---\n\n# References\n\nReference topics this workflow cites (keel_help topics):\n\n- Step 1 — `strategy_paths` — Path 1–4 chains, PSM vs ScalingPositionManager and when each applies, Path-2 direction and exits (the sizer table is in trading_domain)\n- Step 1 — `strategy_patterns` — pattern snippets when the thesis names one: entry/exit, screen-select, pairs, beta hedge, DCA, regime\n- Step 1 — `trading_domain` — the timeframe floor and the per-strategy timeframe table, carry and oscillator polarity, required component pairs, complexity ladder, the entry/exit sizer table\n- Step 1 — `pipeline_system` — the type progression OHLCVDict → SignalSeries → … → WeightSeries\n- Step 3 — `reasoning_principles` — the invariants to check on the mock: terminal WeightSeries, normalize only when needed, each transform once, path matches signal type, a discrete strategy needs a way out, universe before signal\n- Step 4 — `dsl_syntax` — the declarations with one example; the Execution mode for each path; Parallel / Store / Load / variable / factory syntax\n- Step 4 — `composition_mechanics` — Parallel semantics and branch isolation, the two mask systems, slot-reading components, loaders following Globals vs an own-clock branch\n- Step 4 — `universe_selection` — Universe selectors, manual vs criteria modes, resolved semantics, groups — when the user names an asset scope\n- Step 4 — `data_loading` — validated examples of every loader/clock shape — carry, own-clock smoothing, accrual, projection, two price clocks\n- Step 5 — `rule:<CODE>` — the lesson behind any validation issue code the dry run returns\n- Step 5 — `mistakes` — the structural mistakes by id — most now carry a validator code with its own fix; the few with none are checked by eye\n- Step 5 — `tool_usage` — pipeline completeness: a backtest refuses a pipeline that does not end in weights, and names the missing step\n\n# Method\n\nThe whole construction, in order: read the thesis → name its concepts →\ndiscover the components → mock the typed chain → draft from the skeleton →\ndry-run, reading each issue's fix → save → backtest when the user wants one\n→ read the result and reason about why.\n\n## Step 1 — Read the thesis; settle three decisions before any tool call\n\n1. **Path.** Continuous forecast (Path 1), discrete entry/exit (Path 2),\n   screen-select (Path 3), direct allocation (Path 4). \"buy when / sell when /\n   entry / exit / crosses\" → discrete; \"rank / weight / tilt / score /\n   forecast\" → continuous. Architecture ambiguity → ask ONE question.\n2. **Universe — choose the asset set.** Assets the user named →\n   `Universe(mode=\"manual\", symbols=[...])`. A scope they described (liquid\n   majors, a sector) → a criteria universe: `Universe(mode=\"top_volume\",\ntop_n=30, market=\"perp\")`, the convention, or `mode=\"category\"`. No hint\n   of scope at all → ask which assets, offering the top_volume convention.\n   Never write `resolved=`; the save resolves criteria and bakes the list.\n   \"HL perps\" is `market=\"perp\"`. Nothing injects a Universe: without one the\n   strategy fails validation (`MISSING_UNIVERSE`).\n3. **Polarity.** Trend-following unless the user says fade / overbought /\n   oversold / reversal / contrarian / mean reversion.\n\nClock: `1d` unless the user names one. An unsupported timeframe is offered as\nthe nearest supported value and changed only with consent — never silently.\n\nMatch complexity to specificity: vague → one question, then the simplest\nviable version; detailed (\"EWMAC 2/8 with FDM and vol sizing\") → exactly as\nstated, nothing simplified.\n\n## Step 2 — Search every concept\n\nFor each building block (data, signal, entry, exit, filter, sizing) and every\nconcept the user names (\"beta hedge\", \"trailing stop\", \"vol targeting\",\n\"regime\"): `keel_components_search(query=<concept>)`. Search even when you know\nthe indicator's name — the result gives the canonical name, its output type,\nand any adapter it needs (a raw indicator value wants `ThresholdCross` before\nan entry slot). A concept with no clean match is said out loud, not\napproximated silently; a hand-rolled substitute (a `ConstantForecast(-10)` for\na beta hedge) is a different strategy from the one asked for.\n\n## Step 3 — Mock the graph, then batch-verify\n\nWrite the chain as names with the type you expect at each arrow (no DSL yet):\n\n```\nPriceDataLoader → OHLCVDict · ROC → SignalSeries · CrossSectionalZScore → NormalizedSignal\n· ForecastScaler → ForecastSeries · ForecastCapper → ForecastSeries · ForecastWeightNormalizer → WeightSeries\n```\n\nThen ONE `keel_components_get_many(names=[...])` over every name,\nstandard components (`PriceDataLoader`, `Store`) included. Walk it pair-wise:\noutput type = next input type? required params covered? does a component\nread a slot that needs an earlier `Store`? did search surface a better\ncandidate you did not mock? A type gap means search for a bridging component\nor swap the pick — never force it. Only after the batch fits do you write\nDSL: a wrong shape costs one batch call here and a compile round-trip plus a\nredraft after.\n\nMulti-signal joins: one directional signal + the other as a filter through\n`ApplyMask`; `MaskAnd`/`MaskOr` are for exits and boolean filters (they lose\ndirection). Independent branches → a `Parallel` dict, not serial Store/Load;\neach branch sizes to WeightSeries before `WeightConcatenator`, and the book's\ngross exposure is the sum of the branch targets — split the target across\nbranches or cap after with `LeverageCap`.\n\n## Step 4 — Draft from the skeleton\n\nThe declarations, then exactly one `Pipeline(...)` — the skeleton\n`keel_strategy_compose`'s description carries:\n\n```\nGlobals(target_timeframe='1d')\nUniverse(mode='top_volume', top_n=30, market='perp')\nExecution(rebalance='buffered', buffer_threshold=0.2, buffer_mode='relative', rebalance_method='to_edge')\nPipeline([\n    PriceDataLoader(),\n    ROC(period=20),\n    ForecastScaler(avg_abs_target=10.0),\n    ForecastCapper(limit=20.0),\n    ForecastWeightNormalizer(target_leverage=1.0),\n], name='my_strategy')\n```\n\n`Globals(target_timeframe, bar_offset)` is the only place the clock lives.\n`bar_offset` is omitted unless the user asks for a different close: omitted,\nbars close on the timeframe's own boundary (00:00 UTC for `1d`). An offset\nmoves every bar and so changes every signal and result — `'12h'` closes the\ndaily bar at 12:00 UTC, and one daily strategy measured −7.1% with it against\n+44.6% without; the reply says so when one is declared. `Universe(...)` is\nrequired. `Execution(rebalance=...)`: `buffered` for Path 1 (continuous weights),\n`on_change` for Paths 2 and 3 (binary weights), `every_bar` only for a\nconstant-weight basket. The pipeline ends in a\nnormalizer or sizer producing WeightSeries. No imports; component names\nbare; keyword arguments only. A bare loader follows Globals with no resampler\nafter it; an explicit `timeframe=` on a loader is a branch on its own clock\nand comes back through `TargetSignalResampler` (finer) or\n`TargetSignalProjector` (coarser). A Parallel dict is consumed by a composer\nor `Extract`. Add what the user asked for plus the required glue, one\nsentence per addition.\n\n## Step 5 — Dry run until clean\n\nThe most important step. `keel_strategy_compose(dry_run=True, source=...)`.\nEvery issue carries a `code`, a `message` and a `suggestion` — the fix. Read\nthe code first; `keel_help(topic=\"rule:<CODE>\")` explains it. A parse failure\ncomes back the same way (its own code, the fix and the span), so an import\nline or a positional argument costs one round trip, not a guess. Apply the\nfix, re-run. A save does not wait for a clean validation — only parse and\ncompile errors block it, and a backtest run refuses a pipeline that is not\nbacktest-ready — so clean is the dry run's job: no error-severity issue\noutstanding before the save; warnings are read and either fixed or explained\nto the user. The few structural mistakes no validator code catches yet are in\nthe `mistakes` reference; check those by eye. Read the returned `universe`\nblock (mode, resolved count, preview) before moving on.\n\n## Step 6 — Save\n\n`keel_strategy_compose(source=..., name=<descriptive_snake_case>,\ndescription=<the thesis in the user's words>)`. Where the host draws the\nresult's card the user already sees it — explain it in your own words;\nelsewhere its markdown stands in. When the user asks where the strategy\nstands, `keel_strategy_readiness(strategy_id=<id>)` reports its evidence; its\n`next_recommended_action` is a field of that report, not an instruction.\nFurther edits are the `strategy-fork-and-iterate` workflow.\n\n## Step 7 — Backtest when the user wants one, then reason about the result\n\nA backtest uses one run of the plan's weekly allowance: run it when the user\nasked how the strategy performs, or agrees to a run.\n`keel_backtest_run(strategy_id=<id>)` with no dates runs the strategy clock's\ndefault window and cost model; pass a window only when the user named one.\n`keel_backtest_summarize` shows one finished run, `keel_backtest_compare`\nseveral variants in one call. Then read it and reason WHY — the mechanism, not the outcome: which exposure produced\nthe return, whether the costs, the carry or one window explain it, and how it\ncompares with the BTC hold reference over the same span. Prefer a principled\nfix over curve-fitting one bad window; the next change is one small edit\n(`strategy-fork-and-iterate`), saved as a new version of this strategy.\n\n# Decision points\n\n- Ask vs proceed: architecture ambiguity (trend vs MR, continuous vs discrete)\n  → one question; a parameter or minor choice → proceed with the default and\n  say so.\n- `EqualWeightAllocator` only on Path 3 (selection is the signal); continuous\n  forecasts keep conviction through `ForecastWeightNormalizer` or\n  `VolTargetWeightConverter` (+ `LeverageCap`) — the validator warns\n  `FORECAST_MAGNITUDE_DISCARDED` otherwise.\n- Path 3 is `TopNAssetSelector` → `EqualWeightAllocator` →\n  `WeightCadence(duration=…)` → `FillNaN(fill_value=0.0)`: the selection feeds\n  the allocator directly, the cadence is the hold and the exit, and `FillNaN`\n  keeps the terminal weights dense (`NONDENSE_TERMINAL_WEIGHTS` otherwise) —\n  the `screen_select_patterns` topic has the validated example. A\n  `PositionStateMachine` is followed by a sizer, never preceded by one.\n- Start simple: normalization, vol standardization and FDM are follow-ups,\n  not defaults, for a single-signal build.\n\n# Output shape\n\n1. The result's `view`: where the host draws it as a card the user already\n   sees it — explain it in your own words; elsewhere its markdown stands in.\n   The source is in the view; `include_source` fetches it on request.\n2. One-sentence thesis (\"Trend-following on top-30 HL perps with\n   regime-gated vol sizing\").\n3. The 3–5 composition choices and why.\n4. `strategy_id`, `hero_url`, and the declarations the strategy carries\n   (Universe, clock, Execution).\n5. If a backtest ran: what it says and why.\n\n# When NOT to use\n\n- Modifying an existing strategy → `strategy-fork-and-iterate`.\n- Looking up one component → `component-discovery`.\n- Repeated tool failures → `recover-from-error`.\n- Testing something already authored → `backtest-and-analyze`.\n\n# Test prompts\n\n1. \"Build me a momentum strategy on the top 20 HL perps with 1h bars.\"\n2. \"Create a new strategy that does EWMAC 8/32 trend following with\n   vol-targeted sizing and a beta hedge to BTC.\"\n3. \"Make a mean-reversion strategy on ETH using RSI overbought/oversold.\"\n"
}

SHA-256 of public snapshot: 16bce0d108de00a1766a88806a8ffac4e3ba8b42051ff5eb857b24f6c196e1d7