@senpi/runtime 3.0.9-dev.20260710204324 → 3.0.9-dev.feat-strategy-compos.20260716233548

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/cli/composer-commands.d.ts +37 -0
  2. package/dist/cli/composer-commands.d.ts.map +1 -0
  3. package/dist/cli/composer-commands.js +396 -0
  4. package/dist/cli/composer-commands.js.map +1 -0
  5. package/dist/cli/senpi-commands.d.ts.map +1 -1
  6. package/dist/cli/senpi-commands.js +2 -0
  7. package/dist/cli/senpi-commands.js.map +1 -1
  8. package/dist/cli/validate-runtime-config.mjs +190 -0
  9. package/dist/composer/AUTHORING.md +435 -0
  10. package/dist/composer/__init__.py +29 -0
  11. package/dist/composer/authoring/__init__.py +59 -0
  12. package/dist/composer/authoring/answers.py +297 -0
  13. package/dist/composer/authoring/archetypes/__init__.py +26 -0
  14. package/dist/composer/authoring/archetypes/_common.py +410 -0
  15. package/dist/composer/authoring/archetypes/breakout.py +134 -0
  16. package/dist/composer/authoring/archetypes/classifier.py +109 -0
  17. package/dist/composer/authoring/archetypes/skeleton.py +76 -0
  18. package/dist/composer/authoring/archetypes/thesis_fund.py +103 -0
  19. package/dist/composer/authoring/archetypes/trend_momentum.py +132 -0
  20. package/dist/composer/authoring/builder.py +191 -0
  21. package/dist/composer/authoring/emit.py +264 -0
  22. package/dist/composer/authoring/gaps.py +115 -0
  23. package/dist/composer/authoring/generate.py +131 -0
  24. package/dist/composer/authoring/presets.py +168 -0
  25. package/dist/composer/authoring/runtime_emit.py +266 -0
  26. package/dist/composer/authoring/strategy_manifest.py +94 -0
  27. package/dist/composer/cli.py +487 -0
  28. package/dist/composer/compiler/__init__.py +22 -0
  29. package/dist/composer/compiler/compile.py +739 -0
  30. package/dist/composer/examples/breakout_swing.graph.yaml +121 -0
  31. package/dist/composer/examples/conviction_scorer.graph.yaml +216 -0
  32. package/dist/composer/examples/macro_thesis_basket.graph.yaml +247 -0
  33. package/dist/composer/examples/pre_listing_classifier.graph.yaml +204 -0
  34. package/dist/composer/examples/trend_momentum_majors.graph.yaml +133 -0
  35. package/dist/composer/generated/README.md +22 -0
  36. package/dist/composer/generated/catalog.md +118 -0
  37. package/dist/composer/generated/describe.md +1168 -0
  38. package/dist/composer/generated/graph.schema.json +1946 -0
  39. package/dist/composer/primitives/__init__.py +320 -0
  40. package/dist/composer/primitives/_candles.py +76 -0
  41. package/dist/composer/primitives/adapter.py +363 -0
  42. package/dist/composer/primitives/emitters.py +142 -0
  43. package/dist/composer/primitives/filters.py +71 -0
  44. package/dist/composer/primitives/indicators.py +326 -0
  45. package/dist/composer/primitives/rankers.py +40 -0
  46. package/dist/composer/primitives/scorers.py +100 -0
  47. package/dist/composer/primitives/sources.py +979 -0
  48. package/dist/composer/primitives/stateful.py +254 -0
  49. package/dist/composer/pure_fn/__init__.py +34 -0
  50. package/dist/composer/pure_fn/checks.py +39 -0
  51. package/dist/composer/pure_fn/models.py +177 -0
  52. package/dist/composer/pure_fn/purity.py +311 -0
  53. package/dist/composer/pure_fn/runtests.py +155 -0
  54. package/dist/composer/registry/__init__.py +40 -0
  55. package/dist/composer/registry/generate.py +425 -0
  56. package/dist/composer/registry/mcp-tools-snapshot.json +193 -0
  57. package/dist/composer/registry/mcp_closure.py +311 -0
  58. package/dist/composer/registry/models.py +417 -0
  59. package/dist/composer/registry/nodes.py +1297 -0
  60. package/dist/composer/requirements.txt +21 -0
  61. package/dist/composer/scaffold_path.py +118 -0
  62. package/dist/composer/templates/__init__.py +13 -0
  63. package/dist/composer/templates/scan.py.j2 +34 -0
  64. package/dist/composer/validator/__init__.py +29 -0
  65. package/dist/composer/validator/desugar.py +133 -0
  66. package/dist/composer/validator/errors.py +227 -0
  67. package/dist/composer/validator/graph_checks.py +443 -0
  68. package/dist/composer/validator/loader.py +110 -0
  69. package/dist/composer/validator/schema_checks.py +147 -0
  70. package/dist/composer/validator/validate.py +86 -0
  71. package/dist/composer/vendoring.py +103 -0
  72. package/dist/composer/verbs/__init__.py +49 -0
  73. package/dist/composer/verbs/_box.py +308 -0
  74. package/dist/composer/verbs/check.py +254 -0
  75. package/dist/composer/verbs/close.py +494 -0
  76. package/dist/composer/verbs/deploy.py +279 -0
  77. package/dist/composer/verbs/docs.py +30 -0
  78. package/dist/composer/verbs/fund.py +498 -0
  79. package/dist/composer/verbs/install.py +586 -0
  80. package/dist/composer/verbs/runtime_validate.py +422 -0
  81. package/dist/composer/verbs/smoke.py +134 -0
  82. package/dist/composer/verbs/status.py +383 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +2 -9
  85. package/dist/index.js.map +1 -1
  86. package/dist/runtime/load-runtime-config.d.ts +17 -0
  87. package/dist/runtime/load-runtime-config.d.ts.map +1 -1
  88. package/dist/runtime/load-runtime-config.js +2 -2
  89. package/dist/runtime/load-runtime-config.js.map +1 -1
  90. package/dist/runtime-api/config.d.ts +2 -2
  91. package/dist/runtime-api/config.js +4 -4
  92. package/dist/runtime-api/config.js.map +1 -1
  93. package/dist/runtime-api/handlers/ingest-signals.d.ts +3 -5
  94. package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
  95. package/dist/runtime-api/handlers/ingest-signals.js +5 -14
  96. package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
  97. package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
  98. package/dist/runtime-api/intake/intake-mount.js +2 -8
  99. package/dist/runtime-api/intake/intake-mount.js.map +1 -1
  100. package/dist/skills-manager/manifest.d.ts.map +1 -1
  101. package/dist/skills-manager/manifest.js +1 -3
  102. package/dist/skills-manager/manifest.js.map +1 -1
  103. package/dist/skills-manager/skills-manifest.json +1 -3
  104. package/package.json +4 -2
  105. package/dist/runtime-api/intake/ingest-mutex.d.ts +0 -16
  106. package/dist/runtime-api/intake/ingest-mutex.d.ts.map +0 -1
  107. package/dist/runtime-api/intake/ingest-mutex.js +0 -18
  108. package/dist/runtime-api/intake/ingest-mutex.js.map +0 -1
@@ -0,0 +1,204 @@
1
+ # pre_listing_classifier — new-listing/IPOP strategy (tier 2) whose direction CLASSIFIER
2
+ # is a per-graph pure_fn node (D2 kind 2).
3
+ #
4
+ # Why a pure_fn here (the slice-4 point): this strategy's pre-listing thesis is a CLASSIFIER
5
+ # PREDICATE — the 4h structure classifies tradeable direction (NEUTRAL is dropped), the 1h
6
+ # structure and smart money CONFIRM, and because SM data is sparse pre-listing its ABSENCE
7
+ # falls back to a trend-only assumed-aligned bonus. That "SM present -> must agree; SM absent
8
+ # -> assume aligned" branch is cross-feature conditional logic no registry scorer expresses.
9
+ # The fn body re-expresses the corpus package magpie's `build_thesis_pre_listing`
10
+ # (corpus: /Users/duncan/Senpi/skills/strategies/magpie/pre_listing/scanners/scoring.py) —
11
+ # cited in the body — statically purity-checked, with inline fixture tests.
12
+ #
13
+ # DYNAMIC UNIVERSE (slice 5, restored): pre_listing_classifier's real universe is discovered each tick off
14
+ # the LIVE market_list_instruments read by its IPOP funding signature (corpus
15
+ # magpie/pre_listing/scanners/scan.py _fetch_ipop_universe + scoring.ipop_passes_universe).
16
+ # The slice-4 whitelist stand-in is replaced by the discovery trio:
17
+ # instrument_list (live board records) -> classifier_predicate (funding + leverage +
18
+ # not-delisted, the ipop_passes_universe funding portion) -> universe_discover (xyz: prefix
19
+ # + leverage cap + min daily volume) -> the tradeable IPOP names.
20
+ # Chaining the two predicates reproduces ipop_passes_universe's full conjunction while using
21
+ # each registry node as declared (no registry change). emit_single_best is used because the
22
+ # discovered pre-listing universe is naturally tiny (recon: "magpie-implicit-via-small-N").
23
+
24
+ version: 1
25
+
26
+ metadata:
27
+ archetype: event_new_listing
28
+ strategy: pre_listing_classifier
29
+ corpus_origin: magpie # provenance only, for fidelity replay — not user-facing
30
+ exit_preset: protected_standard # slice 9: emitted as runtime.yaml exit: (magpie = balanced discovery)
31
+ # EDGE (verbatim, machine never interprets): "catch trade.xyz pre-IPO perps as they start
32
+ # trending into the listing — 4h sets the side, 1h and smart money confirm, and when the
33
+ # book is too new for smart-money data, trust the trend."
34
+
35
+ nodes:
36
+ # --- sources ---
37
+ - id: account
38
+ node: clearinghouse_state
39
+
40
+ - id: dedup_state
41
+ node: ttl_dedup_state
42
+ with:
43
+ ttl_seconds: 720 # pre_listing_classifier's 720s per-coin dedup window
44
+
45
+ # --- dynamic universe discovery (the IPOP funding signature; see header) ---
46
+ - id: board
47
+ node: instrument_list # live market_list_instruments records for the xyz venue
48
+ with:
49
+ dex: "xyz"
50
+
51
+ - id: ipop_funding
52
+ node: classifier_predicate # ipop_passes_universe: |funding| tiny, lev<=5, live
53
+ with:
54
+ max_funding_abs: 0.0000001 # magpie ipopFundingMaxAbs 1e-7
55
+ max_leverage: 5 # magpie ipopMaxLeverageCap
56
+ exclude_delisted: true
57
+
58
+ - id: ipop_universe
59
+ node: universe_discover # xyz: prefix + leverage cap + min daily volume floor
60
+ with:
61
+ name_prefix: "xyz:"
62
+ max_leverage_cap: 5
63
+ min_day_volume: 100000 # magpie ipopMinDailyVolUsd
64
+
65
+ # --- universe narrowing over the discovered IPOP set ---
66
+ - id: exclude_held
67
+ node: held_asset_exclusion
68
+
69
+ - id: exclude_recent
70
+ node: recent_signal_ttl_dedup
71
+
72
+ # --- candle + leaderboard reads over the discovered, narrowed universe ---
73
+ - id: candles
74
+ node: candles_fetch
75
+ with:
76
+ intervals: ["1h", "4h"] # universe is wired in (dynamic), not a static whitelist
77
+
78
+ - id: lean
79
+ node: sm_lean
80
+ with:
81
+ agree_threshold: 0.55 # magpie smTiltMinPct 55%
82
+
83
+ # --- trend structure at both intervals (the classifier's inputs) ---
84
+ - id: trend4h
85
+ node: trend_structure
86
+ with:
87
+ lookback: 6
88
+ interval: "4h"
89
+
90
+ - id: trend1h
91
+ node: trend_structure
92
+ with:
93
+ lookback: 6
94
+ interval: "1h"
95
+
96
+ # --- pure_fn classifier predicate (the bespoke, cross-feature edge; D2 kind 2) ---
97
+ - id: classify
98
+ kind: pure_fn
99
+ inputs:
100
+ - { name: trend4h, type: feature_map, doc: "4h trend structure (BULLISH/BEARISH/NEUTRAL)." }
101
+ - { name: trend1h, type: feature_map, doc: "1h trend structure (BULLISH/BEARISH/NEUTRAL)." }
102
+ - { name: sm, type: feature_map, doc: "Smart-money long lean percent [0,100]; may be absent." }
103
+ outputs:
104
+ - { name: scores, type: score_map, doc: "Per-asset {score, direction, reasons[]} for tradeable IPOPs." }
105
+ params:
106
+ - { name: min_score, type: float, default: 3, doc: "Minimum pre-listing score to emit." }
107
+ fn: |
108
+ def classify_pre_listing(trend4h, trend1h, sm, min_score):
109
+ # Magpie PRE-LISTING (tier 2): a CLASSIFIER PREDICATE, not a weighted scorer. The
110
+ # 4h structure classifies the side (NEUTRAL -> not tradeable, dropped); 1h + SM
111
+ # confirm; SM is SPARSE pre-listing, so its ABSENCE falls back to trend-only
112
+ # (assumed aligned) while a PRESENT-but-opposing SM read drops the candidate.
113
+ # Ported from magpie/pre_listing/scanners/scoring.py build_thesis_pre_listing.
114
+ scores = {}
115
+ assets = sorted(set(trend4h) | set(trend1h) | set(sm))
116
+ for asset in assets:
117
+ t4 = trend4h.get(asset, "NEUTRAL")
118
+ if t4 == "NEUTRAL":
119
+ continue
120
+ direction = "LONG" if t4 == "BULLISH" else "SHORT"
121
+ score = 3.0
122
+ reasons = ["4h_" + t4.lower()]
123
+ t1 = trend1h.get(asset, "NEUTRAL")
124
+ if (direction == "LONG" and t1 == "BULLISH") or (direction == "SHORT" and t1 == "BEARISH"):
125
+ score = score + 2.0
126
+ reasons.append("1h_confirms")
127
+ lean = sm.get(asset)
128
+ if lean is None:
129
+ score = score + 2.0
130
+ reasons.append("sm_sparse_assumed_aligned")
131
+ else:
132
+ tilt = lean if direction == "LONG" else 100.0 - lean
133
+ if tilt < 55.0:
134
+ continue
135
+ score = score + 2.0
136
+ reasons.append("sm_aligned")
137
+ if tilt >= 70.0:
138
+ score = score + 1.0
139
+ reasons.append("sm_strong")
140
+ if score < min_score:
141
+ continue
142
+ scores[asset] = {"score": round(score, 4), "direction": direction, "reasons": reasons}
143
+ return scores
144
+ tests:
145
+ - name: bullish_confirmed_long
146
+ inputs:
147
+ trend4h: { ACME: BULLISH }
148
+ trend1h: { ACME: BULLISH }
149
+ sm: { ACME: 60.0 }
150
+ expect:
151
+ scores:
152
+ ACME: { score: 7.0, direction: LONG, reasons: [4h_bullish, 1h_confirms, sm_aligned] }
153
+ - name: neutral_4h_not_tradeable
154
+ inputs:
155
+ trend4h: { ACME: NEUTRAL }
156
+ trend1h: { ACME: BULLISH }
157
+ sm: { ACME: 60.0 }
158
+ expect:
159
+ scores: {}
160
+ - name: sparse_sm_trend_only
161
+ inputs:
162
+ trend4h: { ACME: BULLISH }
163
+ trend1h: { ACME: NEUTRAL }
164
+ sm: {}
165
+ expect:
166
+ scores:
167
+ ACME: { score: 5.0, direction: LONG, reasons: [4h_bullish, sm_sparse_assumed_aligned] }
168
+
169
+ # --- ranker + emitter ---
170
+ - id: best
171
+ node: emit_single_best
172
+ with:
173
+ min_score: 0 # the pure_fn already applies pre_listing_classifier's score floor
174
+
175
+ - id: emit
176
+ node: signal_envelope
177
+ with:
178
+ margin_pct: 10 # PERCENT of withdrawable
179
+ leverage: 5 # pre_listing_classifier's IPOP venue max is <=5
180
+
181
+ edges:
182
+ # dynamic universe discovery: board -> funding signature -> prefix/leverage/volume
183
+ - { from: board.instruments, to: ipop_funding.candidates }
184
+ - { from: ipop_funding.kept, to: ipop_universe.instruments }
185
+ # universe narrowing chain over the discovered IPOP names
186
+ - { from: ipop_universe.universe, to: exclude_held.candidates }
187
+ - { from: account.held, to: exclude_held.held }
188
+ - { from: exclude_held.kept, to: exclude_recent.candidates }
189
+ - { from: dedup_state.recent, to: exclude_recent.recent }
190
+ - { from: exclude_recent.kept, to: candles.assets }
191
+ - { from: exclude_recent.kept, to: lean.assets }
192
+ # candles fan out to trend structure at both intervals
193
+ - { from: candles.candles, to: trend4h.candles }
194
+ - { from: candles.candles, to: trend1h.candles }
195
+ # each feature wires into ONE declared pure_fn input port (single writer, no merge)
196
+ - { from: trend4h.direction, to: classify.trend4h }
197
+ - { from: trend1h.direction, to: classify.trend1h }
198
+ - { from: lean.lean, to: classify.sm }
199
+ # classify -> rank -> emit
200
+ - { from: classify.scores, to: best.scores }
201
+ - { from: best.selected, to: emit.selected }
202
+ - { from: account.equity, to: emit.equity }
203
+ # close the dedup loop
204
+ - { from: emit.signals, to: dedup_state.emitted }
@@ -0,0 +1,133 @@
1
+ # trend_momentum_majors — parabolic trend-momentum strategy (tier 1, LONG-only) over a
2
+ # fixed 4-asset majors whitelist, as a typed dataflow DAG.
3
+ #
4
+ # Hand-authored from the corpus-recon inventory (stag section): a fixed 4-asset whitelist
5
+ # scanned on 4h candles, gated by five hard conditions (above SMA-200, +trend%, volume
6
+ # surge, acceleration, smart-money alignment), single-best emission, and the universal
7
+ # TTL signal-dedup on ctx.state. This is the slice-2 "validates clean" fixture and the
8
+ # slice-3 compile target. Re-expression of corpus package stag; semantics cited to
9
+ # skills/strategies/stag/... throughout.
10
+ #
11
+ # Shape (why it is a DAG, not a pipeline): candles fan out to five indicators whose
12
+ # per-asset features MERGE into the scorer; the universe is first narrowed by held-asset
13
+ # exclusion and TTL dedup. The ttl_dedup_state node closes the dedup loop through
14
+ # ctx.state (its `recent` output is prior-tick data, so it does not form a cycle).
15
+
16
+ version: 1
17
+
18
+ metadata:
19
+ archetype: trend_momentum
20
+ strategy: trend_momentum_majors
21
+ corpus_origin: stag # provenance only, for fidelity replay — not user-facing
22
+ exit_preset: protected_wide # slice 9: emitted as runtime.yaml exit: (stag = let the runner breathe)
23
+ # EDGE (verbatim, machine never interprets): "ride assets breaking to fresh highs with
24
+ # accelerating momentum, volume confirmation, and smart money leaning the same way."
25
+
26
+ nodes:
27
+ # --- sources ---
28
+ - id: account
29
+ node: clearinghouse_state # held-asset set + account equity
30
+
31
+ - id: dedup_state
32
+ node: ttl_dedup_state # ctx.state TTL dedup; `recent` = assets within TTL
33
+ with:
34
+ ttl_seconds: 240
35
+
36
+ # --- universe narrowing (held-exclusion then recent-signal dedup) ---
37
+ - id: exclude_held
38
+ node: held_asset_exclusion # candidates default to the whitelist universe
39
+
40
+ - id: exclude_recent
41
+ node: recent_signal_ttl_dedup
42
+
43
+ # --- candle + leaderboard reads over the narrowed universe ---
44
+ - id: candles
45
+ node: candles_fetch
46
+ with:
47
+ whitelist: ["BTC", "ETH", "SOL", "HYPE"]
48
+ intervals: ["4h"]
49
+
50
+ - id: lean
51
+ node: sm_lean
52
+ with:
53
+ agree_threshold: 0.6 # trend_momentum_majors requires >=60% SM alignment
54
+
55
+ # --- indicators (candles -> per-asset features) ---
56
+ - id: trend_sma
57
+ node: sma
58
+ with:
59
+ period: 200
60
+ interval: "4h"
61
+
62
+ - id: fresh_high
63
+ node: recent_high_bars_ago
64
+ with:
65
+ window: 60
66
+
67
+ - id: trend_pct
68
+ node: pct_change
69
+ with:
70
+ lookback: 42 # ~7d of 4h bars
71
+ interval: "4h"
72
+
73
+ - id: vol_surge
74
+ node: volume_surge_ratio
75
+ with:
76
+ recent_window: 6
77
+ baseline_window: 42
78
+
79
+ - id: accel
80
+ node: acceleration
81
+ with:
82
+ short_window: 3
83
+ long_window: 6
84
+
85
+ # --- scorer: five hard gates + weighted score (bespoke edge slot) ---
86
+ - id: gates
87
+ node: hard_gates_all
88
+ with:
89
+ # gate/weight keys are SOURCE NODE IDs (the compiler merges features keyed by the
90
+ # producing node id — collision-free even when two producers share a port name).
91
+ required_gates: ["trend_sma", "accel"] # above-SMA AND accelerating (both boolean)
92
+ # TODO(edge): tune these weights — the strategy's real edge lives here.
93
+ weights: { trend_pct: 1.0, vol_surge: 1.0, lean: 1.0 }
94
+ min_score: 3
95
+
96
+ # --- ranker + emitter ---
97
+ - id: best
98
+ node: emit_single_best
99
+ with:
100
+ min_score: 3
101
+
102
+ - id: emit
103
+ node: signal_envelope
104
+ with:
105
+ margin_pct: 5
106
+ leverage: 3
107
+
108
+ edges:
109
+ # universe narrowing chain
110
+ - { from: account.held, to: exclude_held.held }
111
+ - { from: exclude_held.kept, to: exclude_recent.candidates }
112
+ - { from: dedup_state.recent, to: exclude_recent.recent }
113
+ - { from: exclude_recent.kept, to: candles.assets }
114
+ - { from: exclude_recent.kept, to: lean.assets }
115
+ # candles fan out to indicators
116
+ - { from: candles.candles, to: trend_sma.candles }
117
+ - { from: candles.candles, to: fresh_high.candles }
118
+ - { from: candles.candles, to: trend_pct.candles }
119
+ - { from: candles.candles, to: vol_surge.candles }
120
+ - { from: candles.candles, to: accel.candles }
121
+ # per-asset features MERGE into the scorer (feature_map inputs merge)
122
+ - { from: trend_sma.above, to: gates.features }
123
+ - { from: fresh_high.bars_ago, to: gates.features }
124
+ - { from: trend_pct.pct, to: gates.features }
125
+ - { from: vol_surge.ratio, to: gates.features }
126
+ - { from: accel.accelerating, to: gates.features }
127
+ - { from: lean.lean, to: gates.features }
128
+ # score -> rank -> emit
129
+ - { from: gates.scores, to: best.scores }
130
+ - { from: best.selected, to: emit.selected }
131
+ - { from: account.equity, to: emit.equity }
132
+ # close the dedup loop: this tick's signals are recorded in ctx.state
133
+ - { from: emit.signals, to: dedup_state.emitted }
@@ -0,0 +1,22 @@
1
+ # composer/generated/ — registry-derived artifacts (DO NOT EDIT)
2
+
3
+ These files are generated from the node registry (`composer/registry/nodes.py`) — the
4
+ single source of truth (D4). They are committed so the agent skill, the TS rim, and CI
5
+ can read them without a build step, and so drift is caught by a golden-file diff.
6
+
7
+ | File | What it is |
8
+ |---|---|
9
+ | `graph.schema.json` | JSON Schema for the graph document format (D6). YAML is authored; this JSON Schema is canonical. Per-node param constraints derive via `if`/`then` on the node name. YAML anchors/merge keys are banned at the document level (noted in the schema; enforced in the validator, slice 2). |
10
+ | `catalog.md` | The ~1–2k-token progressive-disclosure index: node names, one-liners, param summaries. |
11
+ | `describe.md` | Full ports / params / example / cost per node (`composer describe <node>`). |
12
+
13
+ Regenerate after any registry change:
14
+
15
+ ```
16
+ python -m composer.registry.generate # rewrite the artifacts
17
+ python -m composer.registry.generate --check # CI-style: exit 1 if stale
18
+ ```
19
+
20
+ Generation is deterministic (stable ordering, sorted JSON keys, no timestamps): running
21
+ it twice produces byte-identical output. The golden-file tests in
22
+ `python-tests/composer/` regenerate and diff against these committed files.
@@ -0,0 +1,118 @@
1
+ # Strategy Composer — Node Catalog
2
+
3
+ Schema version: 1. Generated from composer/registry/nodes.py — do not edit by hand.
4
+
5
+ Progressive-disclosure index (D4): node names, one-liners, param summaries. `composer describe <node>` gives full ports/params/examples.
6
+
7
+ ## scoring
8
+
9
+ Bespoke scoring/classification is a `kind: pure_fn` node (typed ports, one `def`, math/statistics only, inline tests), never a primitive; `hard_gates_all` covers simple gate-and-weigh. Repeated shapes promote post-MVP.
10
+
11
+ ## source
12
+
13
+ - **account_history_fetch** — Own-account bucketed PnL / account-value history across timeframes.
14
+ - params: bucket_duration_ms (int=3600000, 60000..604800000)
15
+ - **arena_leaderboard_fetch** — Senpi Arena prize-competition leaderboard (AI agents, not HL traders).
16
+ - params: period_type (str='WEEK'); period_number (int=0, 0..); limit (int=100, 1..500); offset (int=0, 0..); qualified (bool=False)
17
+ - **asset_trading_limits_fetch** — Per-asset venue max-leverage limit for the own wallet.
18
+ - params: dex (str='')
19
+ - **candles_fetch** — Fetch multi-interval OHLCV candles for a universe of assets.
20
+ - params: whitelist (list[str]=[]); intervals (list[str]=[4h]); include_funding (bool=False); include_asset_context (bool=False)
21
+ - **clearinghouse_state** — Read account equity and the set of currently-held positions.
22
+ - **cross_asset_flows_fetch** — Leader move + correlated laggards that haven't followed yet.
23
+ - params: leader_asset (str='BTC'); min_move_pct (float=2.0, 0.0..); window (str='4h')
24
+ - **funding_history_fetch** — Per-asset extreme-funding history with direction + trend enums.
25
+ - params: asset (str=''); min_annualized_pct (float=20.0, 0.0..); min_persistence_hours (float=0.0, 0.0..)
26
+ - **funding_regime_fetch** — Market-wide funding regime (crowding) label plus its numeric companions.
27
+ - **instrument_list** — List live tradeable instruments with venue metadata.
28
+ - params: dex (str='')
29
+ - **leaderboard_top_fetch** — FAST 4h-window smart-money leaderboard (delta-PnL 'Predators').
30
+ - params: limit (int=100, 1..500)
31
+ - **leaderboard_trader_fetch** — One trader's live 4h-window leaderboard headline scalars.
32
+ - params: trader_id (str=required)
33
+ - **market_prices_fetch** — Latest per-asset mark prices for a venue.
34
+ - params: dex (str='')
35
+ - **mcp_read** — Escape hatch: read any MCP tool, emitting its unwrapped payload as `raw`.
36
+ - params: tool (str=required); args (map_str_json={}); timeout_seconds (float=0.0, 0.0..120.0)
37
+ - **momentum_events_fetch** — Recent momentum threshold-crossing events (traders crossing profit tiers).
38
+ - params: limit (int=50, 1..200); tier (int=0, 0..3); asset (str=''); from_time (str=''); to_time (str='')
39
+ - **open_orders_fetch** — Open resting orders for the strategy's own wallet.
40
+ - **open_positions_realized_pnl_fetch** — Realized (banked) PnL already taken on STILL-OPEN positions, batched.
41
+ - params: latest (bool=False)
42
+ - **own_return_24h** — Per-asset 24h return derived from markPx/prevDayPx asset_context.
43
+ - **sm_lean** — Smart-money net long/short lean per asset from the leaderboard.
44
+ - params: agree_threshold (float=0.55, 0.0..1.0)
45
+ - **strategy_list_fetch** — List strategy-metadata records (id, wallet, status, type, funded).
46
+ - params: status (str=''); strategy_type (str='')
47
+ - **strategy_pnl_snapshot** — Today's PnL / account-value / drawdown snapshot for the own wallet.
48
+ - params: period (str=''); drawdown_reset_on_day_rollover (bool=False)
49
+ - **top_strategies_fetch** — Ranked Senpi copy-trading strategies (paginated) sorted by total PnL.
50
+ - params: limit (int=20, 1..); offset (int=0, 0..); force_fetch (bool=False); max_pages (int=1, 1..)
51
+ - **top_traders_fetch** — Ranked best-track-record Hyperliquid traders (paginated).
52
+ - params: time_frame (str=required); sort_by (str='RETURN_ON_INVESTMENT'); bias (str='ALL'); limit (int=100, 1..1000); max_pages (int=1, 1..); open_position_filter (bool=False); activity_labels (list[str]=[]); consistency (list[str]=[]); risk_labels (list[str]=[])
53
+ - **trader_history_fetch** — Closed-trade history for an arbitrary trader address.
54
+ - params: trader_address (str=required); limit (int=100, 1..1000)
55
+ - **trader_positions_fetch** — One leaderboard trader's per-position 4h PnL breakdown.
56
+ - params: trader_id (str=required)
57
+ - **trader_state_fetch** — An arbitrary trader's current book: held set + account equity.
58
+ - params: trader_address (str=required)
59
+ - **trader_states_fetch** — Full current books for a batch of traders (chunked at 50).
60
+ - params: wallets (list[str]=[]); latest (bool=False); include_position_age (bool=False); sort_open_positions_by (str=''); sort_open_positions_direction (str='DESC')
61
+ - **universe_discover** — Derive a tradeable universe by filtering live instruments on a predicate.
62
+ - params: min_day_volume (float=0.0, 0.0..); max_leverage_cap (float=5.0, 1.0..); name_prefix (str='')
63
+
64
+ ## indicator
65
+
66
+ - **acceleration** — Whether a short-window move is at least half a long-window move.
67
+ - params: short_window (int=3, 1..); long_window (int=6, 2..); interval (str='4h')
68
+ - **aggregate_bias** — Cross-trader net/gross cohort bias per coin.
69
+ - **breakout_detect** — Directional breakout of the prior-window high/low.
70
+ - params: window (int=168, 2..); interval (str='1h')
71
+ - **pct_change** — Percent change of close over a lookback window.
72
+ - params: lookback (int=42, 1..); interval (str='4h')
73
+ - **recent_high_bars_ago** — Bars elapsed since the highest high in a window (fresh-high check).
74
+ - params: window (int=60, 2..); interval (str='4h')
75
+ - **rsi** — Relative Strength Index over the last N bars.
76
+ - params: period (int=14, 2..100); interval (str='1h')
77
+ - **sma** — Simple moving average of close and an above/below flag.
78
+ - params: period (int=200, 1..1000); interval (str='4h')
79
+ - **threshold_gate** — Compare a per-asset feature against a threshold, emitting a 1.0/0.0 gate.
80
+ - params: op (str='lt'); value (float=0.0)
81
+ - **trend_structure** — Higher-lows / lower-highs trend direction over a lookback.
82
+ - params: lookback (int=6, 2..); interval (str='4h')
83
+ - **volume_surge_ratio** — Recent-window mean volume divided by a baseline-window mean.
84
+ - params: recent_window (int=6, 1..); baseline_window (int=42, 1..); interval (str='4h')
85
+
86
+ ## filter
87
+
88
+ - **classifier_predicate** — Gate universe membership on a set of instrument-field predicates.
89
+ - params: max_funding_abs (float=1e-07, 0.0..); max_leverage (float=5.0, 1.0..); exclude_delisted (bool=True)
90
+ - **held_asset_exclusion** — Drop candidates already held in the account.
91
+ - **live_instrument_validation** — Keep only candidates still live on the venue board.
92
+ - **recent_signal_ttl_dedup** — Drop candidates signaled within the dedup TTL.
93
+
94
+ ## scorer
95
+
96
+ - **hard_gates_all** — Require all named gates to pass, then score the survivors.
97
+ - params: required_gates (list[str]=[]); weights (map_str_float={}); min_score (float=0.0, 0.0..)
98
+
99
+ ## ranker
100
+
101
+ - **emit_single_best** — Sort by score and select the single best candidate.
102
+ - params: min_score (float=0.0)
103
+ - **rank_pool_cap_to_open_slots** — Sort by score and take the top (max_slots - held) candidates.
104
+ - params: max_slots (int=5, 1..50); min_score (float=0.0)
105
+
106
+ ## emitter
107
+
108
+ - **signal_envelope** — Build the scan() signal list from selected candidates.
109
+ - params: margin_pct (float=5.0, 0.0001..100.0); leverage (float=3.0, 1.0..50.0); clamp_leverage_to_venue (bool=True); valid_for_seconds (int=0, 0..); signal_type (str='')
110
+
111
+ ## stateful
112
+
113
+ - **cohort_cache_state** — Cache a cohort in ctx.state behind a TTL + version.
114
+ - params: ttl_hours (float=24.0, 0.0..); version (int=1, 0..)
115
+ - **snapshot_diff_state** — Rolling per-key snapshots; growth vs the window's oldest.
116
+ - params: value_field (str=''); bucket_seconds (int=86400, 1..); max_snapshots (int=12, 2..)
117
+ - **ttl_dedup_state** — Track recently-signaled assets in ctx.state with a TTL and pruning.
118
+ - params: ttl_seconds (int=240, 1..); prune_factor (int=4, 1..)