@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.
- package/dist/cli/composer-commands.d.ts +37 -0
- package/dist/cli/composer-commands.d.ts.map +1 -0
- package/dist/cli/composer-commands.js +396 -0
- package/dist/cli/composer-commands.js.map +1 -0
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +2 -0
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/cli/validate-runtime-config.mjs +190 -0
- package/dist/composer/AUTHORING.md +435 -0
- package/dist/composer/__init__.py +29 -0
- package/dist/composer/authoring/__init__.py +59 -0
- package/dist/composer/authoring/answers.py +297 -0
- package/dist/composer/authoring/archetypes/__init__.py +26 -0
- package/dist/composer/authoring/archetypes/_common.py +410 -0
- package/dist/composer/authoring/archetypes/breakout.py +134 -0
- package/dist/composer/authoring/archetypes/classifier.py +109 -0
- package/dist/composer/authoring/archetypes/skeleton.py +76 -0
- package/dist/composer/authoring/archetypes/thesis_fund.py +103 -0
- package/dist/composer/authoring/archetypes/trend_momentum.py +132 -0
- package/dist/composer/authoring/builder.py +191 -0
- package/dist/composer/authoring/emit.py +264 -0
- package/dist/composer/authoring/gaps.py +115 -0
- package/dist/composer/authoring/generate.py +131 -0
- package/dist/composer/authoring/presets.py +168 -0
- package/dist/composer/authoring/runtime_emit.py +266 -0
- package/dist/composer/authoring/strategy_manifest.py +94 -0
- package/dist/composer/cli.py +487 -0
- package/dist/composer/compiler/__init__.py +22 -0
- package/dist/composer/compiler/compile.py +739 -0
- package/dist/composer/examples/breakout_swing.graph.yaml +121 -0
- package/dist/composer/examples/conviction_scorer.graph.yaml +216 -0
- package/dist/composer/examples/macro_thesis_basket.graph.yaml +247 -0
- package/dist/composer/examples/pre_listing_classifier.graph.yaml +204 -0
- package/dist/composer/examples/trend_momentum_majors.graph.yaml +133 -0
- package/dist/composer/generated/README.md +22 -0
- package/dist/composer/generated/catalog.md +118 -0
- package/dist/composer/generated/describe.md +1168 -0
- package/dist/composer/generated/graph.schema.json +1946 -0
- package/dist/composer/primitives/__init__.py +320 -0
- package/dist/composer/primitives/_candles.py +76 -0
- package/dist/composer/primitives/adapter.py +363 -0
- package/dist/composer/primitives/emitters.py +142 -0
- package/dist/composer/primitives/filters.py +71 -0
- package/dist/composer/primitives/indicators.py +326 -0
- package/dist/composer/primitives/rankers.py +40 -0
- package/dist/composer/primitives/scorers.py +100 -0
- package/dist/composer/primitives/sources.py +979 -0
- package/dist/composer/primitives/stateful.py +254 -0
- package/dist/composer/pure_fn/__init__.py +34 -0
- package/dist/composer/pure_fn/checks.py +39 -0
- package/dist/composer/pure_fn/models.py +177 -0
- package/dist/composer/pure_fn/purity.py +311 -0
- package/dist/composer/pure_fn/runtests.py +155 -0
- package/dist/composer/registry/__init__.py +40 -0
- package/dist/composer/registry/generate.py +425 -0
- package/dist/composer/registry/mcp-tools-snapshot.json +193 -0
- package/dist/composer/registry/mcp_closure.py +311 -0
- package/dist/composer/registry/models.py +417 -0
- package/dist/composer/registry/nodes.py +1297 -0
- package/dist/composer/requirements.txt +21 -0
- package/dist/composer/scaffold_path.py +118 -0
- package/dist/composer/templates/__init__.py +13 -0
- package/dist/composer/templates/scan.py.j2 +34 -0
- package/dist/composer/validator/__init__.py +29 -0
- package/dist/composer/validator/desugar.py +133 -0
- package/dist/composer/validator/errors.py +227 -0
- package/dist/composer/validator/graph_checks.py +443 -0
- package/dist/composer/validator/loader.py +110 -0
- package/dist/composer/validator/schema_checks.py +147 -0
- package/dist/composer/validator/validate.py +86 -0
- package/dist/composer/vendoring.py +103 -0
- package/dist/composer/verbs/__init__.py +49 -0
- package/dist/composer/verbs/_box.py +308 -0
- package/dist/composer/verbs/check.py +254 -0
- package/dist/composer/verbs/close.py +494 -0
- package/dist/composer/verbs/deploy.py +279 -0
- package/dist/composer/verbs/docs.py +30 -0
- package/dist/composer/verbs/fund.py +498 -0
- package/dist/composer/verbs/install.py +586 -0
- package/dist/composer/verbs/runtime_validate.py +422 -0
- package/dist/composer/verbs/smoke.py +134 -0
- package/dist/composer/verbs/status.py +383 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -9
- package/dist/index.js.map +1 -1
- package/dist/runtime/load-runtime-config.d.ts +17 -0
- package/dist/runtime/load-runtime-config.d.ts.map +1 -1
- package/dist/runtime/load-runtime-config.js +2 -2
- package/dist/runtime/load-runtime-config.js.map +1 -1
- package/dist/runtime-api/config.d.ts +2 -2
- package/dist/runtime-api/config.js +4 -4
- package/dist/runtime-api/config.js.map +1 -1
- package/dist/runtime-api/handlers/ingest-signals.d.ts +3 -5
- package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
- package/dist/runtime-api/handlers/ingest-signals.js +5 -14
- package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
- package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-mount.js +2 -8
- package/dist/runtime-api/intake/intake-mount.js.map +1 -1
- package/dist/skills-manager/manifest.d.ts.map +1 -1
- package/dist/skills-manager/manifest.js +1 -3
- package/dist/skills-manager/manifest.js.map +1 -1
- package/dist/skills-manager/skills-manifest.json +1 -3
- package/package.json +4 -2
- package/dist/runtime-api/intake/ingest-mutex.d.ts +0 -16
- package/dist/runtime-api/intake/ingest-mutex.d.ts.map +0 -1
- package/dist/runtime-api/intake/ingest-mutex.js +0 -18
- 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..)
|