@senpi/runtime 3.0.9-dev.feat-strategy-compos.20260721003344 → 3.0.9-dev.feat-strategy-compos.20260722213012
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.map +1 -1
- package/dist/cli/composer-commands.js +193 -41
- package/dist/cli/composer-commands.js.map +1 -1
- package/dist/cli/composer-live-read.d.ts +68 -0
- package/dist/cli/composer-live-read.d.ts.map +1 -0
- package/dist/cli/composer-live-read.js +126 -0
- package/dist/cli/composer-live-read.js.map +1 -0
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +8 -10
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/cli/senpi-strategy-tool.d.ts +56 -0
- package/dist/cli/senpi-strategy-tool.d.ts.map +1 -0
- package/dist/cli/senpi-strategy-tool.js +158 -0
- package/dist/cli/senpi-strategy-tool.js.map +1 -0
- package/dist/composer/authoring/__init__.py +2 -0
- package/dist/composer/authoring/answers.py +52 -0
- package/dist/composer/authoring/archetypes/__init__.py +2 -0
- package/dist/composer/authoring/archetypes/contrarian_fade.py +493 -0
- package/dist/composer/authoring/archetypes/relative_value.py +280 -23
- package/dist/composer/cli.py +57 -6
- package/dist/composer/examples/contrarian_fade_crowd.graph.yaml +356 -0
- package/dist/composer/examples/rotation_cross_sectional.graph.yaml +389 -0
- package/dist/composer/generated/catalog.md +2 -1
- package/dist/composer/registry/generate.py +2 -1
- package/dist/composer/validator/errors.py +2 -0
- package/dist/composer/verbs/__init__.py +6 -0
- package/dist/composer/verbs/_box.py +96 -5
- package/dist/composer/verbs/_layout.py +37 -0
- package/dist/composer/verbs/_live.py +50 -6
- package/dist/composer/verbs/close.py +51 -6
- package/dist/composer/verbs/fund.py +144 -27
- package/dist/composer/verbs/install.py +25 -6
- package/dist/composer/verbs/portfolio.py +650 -0
- package/dist/composer/verbs/review.py +724 -0
- package/dist/composer/verbs/status.py +129 -2
- package/dist/composer/verbs/update.py +9 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/composer-managed-write.d.ts +133 -0
- package/dist/runtime/composer-managed-write.d.ts.map +1 -0
- package/dist/runtime/composer-managed-write.js +369 -0
- package/dist/runtime/composer-managed-write.js.map +1 -0
- package/openclaw.plugin.json +5 -0
- package/package.json +2 -1
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# contrarian_fade strategy — machine-generated anchor (composer new).
|
|
2
|
+
#
|
|
3
|
+
# Fade an overextended move: detect an RSI extreme confirmed by a recent price run and enter the
|
|
4
|
+
# OPPOSITE direction (SHORT an overbought rip, LONG an oversold flush), betting on mean reversion.
|
|
5
|
+
# fade_crowd additionally requires the crowd (smart-money lean) to be piling in AND the market-wide
|
|
6
|
+
# funding regime to permit the fade; fade_price fades pure price. A hysteresis-free reversion exit
|
|
7
|
+
# closes when RSI crosses back through the midline or the move invalidates the fade, with
|
|
8
|
+
# held-exclusion built in. Edit ONLY the pure_fn body (the EDGE slot) to move it toward your edge;
|
|
9
|
+
# every intermediate state stays valid (`composer check`).
|
|
10
|
+
|
|
11
|
+
version: 1
|
|
12
|
+
|
|
13
|
+
metadata:
|
|
14
|
+
archetype: contrarian_fade
|
|
15
|
+
strategy: fade_crowded_majors
|
|
16
|
+
generated:
|
|
17
|
+
by: "composer new"
|
|
18
|
+
content_hash: "ecafe30f1dfa5bbe64cb08ac68e0a6c75993ea4bc2a7a9d3e73fb749c16fdb72"
|
|
19
|
+
structure_hash: "841ef7ce490e1e6e733135f0b01af847315948f80e7a88272bf58356277c9fc5"
|
|
20
|
+
spec:
|
|
21
|
+
version: 2
|
|
22
|
+
archetype: contrarian_fade
|
|
23
|
+
strategy: fade_crowded_majors
|
|
24
|
+
thesis: "fade the crowd: when a liquid major rips 3%+ in ~4h and smart money is piled in on that side, the late entrants are maximally exposed — short the overbought rip / buy the oversold flush and ride th..."
|
|
25
|
+
universe:
|
|
26
|
+
whitelist: ["BTC", "ETH", "SOL", "HYPE"]
|
|
27
|
+
long_basket: []
|
|
28
|
+
short_basket: []
|
|
29
|
+
discover: false
|
|
30
|
+
dex: ""
|
|
31
|
+
name_prefix: ""
|
|
32
|
+
max_leverage_cap: 5.0
|
|
33
|
+
min_day_volume: 0.0
|
|
34
|
+
intervals: ["4h"]
|
|
35
|
+
max_positions: 1
|
|
36
|
+
dedup_seconds: 240
|
|
37
|
+
exit_preset: protected_wide
|
|
38
|
+
unmanaged: false
|
|
39
|
+
protection:
|
|
40
|
+
seeded_from: protected_wide
|
|
41
|
+
interval_seconds: 60
|
|
42
|
+
dsl_preset:
|
|
43
|
+
hard_timeout:
|
|
44
|
+
enabled: true
|
|
45
|
+
interval_in_minutes: 20160
|
|
46
|
+
weak_peak_cut:
|
|
47
|
+
enabled: false
|
|
48
|
+
interval_in_minutes: 240
|
|
49
|
+
dead_weight_cut:
|
|
50
|
+
enabled: false
|
|
51
|
+
interval_in_minutes: 480
|
|
52
|
+
phase1:
|
|
53
|
+
enabled: true
|
|
54
|
+
max_loss_pct: 25.0
|
|
55
|
+
retrace_threshold: 15
|
|
56
|
+
consecutive_breaches_required: 2
|
|
57
|
+
phase2:
|
|
58
|
+
enabled: true
|
|
59
|
+
tiers:
|
|
60
|
+
- trigger_pct: 15
|
|
61
|
+
lock_hw_pct: 0
|
|
62
|
+
- trigger_pct: 30
|
|
63
|
+
lock_hw_pct: 30
|
|
64
|
+
- trigger_pct: 60
|
|
65
|
+
lock_hw_pct: 55
|
|
66
|
+
- trigger_pct: 120
|
|
67
|
+
lock_hw_pct: 72
|
|
68
|
+
- trigger_pct: 250
|
|
69
|
+
lock_hw_pct: 85
|
|
70
|
+
guard_rails:
|
|
71
|
+
daily_loss_limit_pct: 15
|
|
72
|
+
max_consecutive_losses: 4
|
|
73
|
+
cooldown_seconds: 3600
|
|
74
|
+
drawdown_halt_pct: 25
|
|
75
|
+
drawdown_reset_on_day_rollover: false
|
|
76
|
+
per_asset_cooldown_seconds: 1800
|
|
77
|
+
risk:
|
|
78
|
+
margin_pct: 5.0
|
|
79
|
+
leverage: 3.0
|
|
80
|
+
drawdown_halt_pct: null
|
|
81
|
+
max_consecutive_losses: null
|
|
82
|
+
cooldown_seconds: null
|
|
83
|
+
max_entries_per_day: null
|
|
84
|
+
edge: "fade the crowd: when a liquid major rips 3%+ in ~4h and smart money is piled in on that side, the late entrants are maximally exposed — short the overbought rip / buy the oversold flush and ride the mean reversion; step aside when RSI reverts through the midline."
|
|
85
|
+
requires: []
|
|
86
|
+
exit_signals:
|
|
87
|
+
interval_seconds: 300
|
|
88
|
+
enter_threshold: 3.0
|
|
89
|
+
exit_threshold: 2.0
|
|
90
|
+
reentry_cooldown_seconds: 1800
|
|
91
|
+
contrarian_fade:
|
|
92
|
+
use_crowd: true
|
|
93
|
+
overbought_rsi: 70.0
|
|
94
|
+
oversold_rsi: 30.0
|
|
95
|
+
min_extension_pct: 3.0
|
|
96
|
+
crowd_threshold: 0.15
|
|
97
|
+
# EDGE (verbatim, machine never interprets): "fade the crowd: when a liquid major rips 3%+ in ~4h and smart money is piled in on that side, the late entrants are maximally exposed — short the overbought rip / buy the oversold flush and ride the mean reversion; step aside when RSI reverts through the midline."
|
|
98
|
+
|
|
99
|
+
nodes:
|
|
100
|
+
# sources: account equity for sizing. NO held-exclusion at the universe level —
|
|
101
|
+
# it lives in the pure_fn (fade only when flat, exit only when held).
|
|
102
|
+
- id: account
|
|
103
|
+
node: clearinghouse_state
|
|
104
|
+
# open positions (held + direction): in-fn held-exclusion + the exit tail's REAL
|
|
105
|
+
# held-direction stamp. Kept even in the entry-only variant (no-pyramiding).
|
|
106
|
+
- id: positions
|
|
107
|
+
node: open_positions
|
|
108
|
+
# candle reads over the fixed fade basket.
|
|
109
|
+
- id: candles
|
|
110
|
+
node: candles_fetch
|
|
111
|
+
with:
|
|
112
|
+
whitelist: ["BTC", "ETH", "SOL", "HYPE"]
|
|
113
|
+
intervals: ["4h"]
|
|
114
|
+
# overextension read: RSI extreme + a recent price run.
|
|
115
|
+
- id: rsi
|
|
116
|
+
node: rsi
|
|
117
|
+
with:
|
|
118
|
+
period: 14
|
|
119
|
+
interval: "4h"
|
|
120
|
+
- id: move
|
|
121
|
+
node: pct_change
|
|
122
|
+
with:
|
|
123
|
+
lookback: 6
|
|
124
|
+
interval: "4h"
|
|
125
|
+
# crowd read: smart-money lean (fade only WITH the crowd) + the funding regime gate.
|
|
126
|
+
- id: lean
|
|
127
|
+
node: sm_lean
|
|
128
|
+
with:
|
|
129
|
+
agree_threshold: 0.6 # TODO(edit): smart-money agreement band (fade corpus 0.55-0.60).
|
|
130
|
+
- id: regime
|
|
131
|
+
node: funding_regime_fetch
|
|
132
|
+
# ---------------------------------------------------------------
|
|
133
|
+
# EDGE (verbatim — the machine did NOT interpret this; encode it
|
|
134
|
+
# by editing the TODO slots on this scoring node):
|
|
135
|
+
# fade the crowd: when a liquid major rips 3%+ in ~4h and smart money is piled in on that side, the late entrants are maximally exposed — short the overbought rip / buy the oversold flush and ride the mean reversion; step aside when RSI reverts through the midline.
|
|
136
|
+
# ---------------------------------------------------------------
|
|
137
|
+
# pure_fn signed-fade scorer (crowd) — THE EDGE SLOT (edit the body; see the EDGE
|
|
138
|
+
# block above). TWO outputs: entry_scores (entry tail) + exit_scores (exit tail).
|
|
139
|
+
- id: score
|
|
140
|
+
kind: pure_fn
|
|
141
|
+
title: "crowd fade: RSI/price overextension the smart-money crowd is piling into"
|
|
142
|
+
inputs:
|
|
143
|
+
- { name: rsi, type: feature_map, doc: "Per-asset RSI in [0,100]." }
|
|
144
|
+
- { name: move, type: feature_map, doc: "Per-asset recent percent return (extension)." }
|
|
145
|
+
- { name: sm, type: feature_map, doc: "Per-asset smart-money tilt in [0,1] (0.5 neutral)." }
|
|
146
|
+
- { name: regime, type: label, doc: "Market-wide funding regime (LONG_CROWDED|SHORT_CROWDED|NEUTRAL): the fade master gate." }
|
|
147
|
+
- { name: positions, type: record_list, doc: "Open positions (from open_positions) — held-exclusion + exit direction." }
|
|
148
|
+
outputs:
|
|
149
|
+
- { name: entry_scores, type: score_map, doc: "ENTER the FADE (opposite the exhausted move) when overextended and FLAT." }
|
|
150
|
+
- { name: exit_scores, type: score_map, doc: "CLOSE a held fade when RSI crosses back through the 50 midline OR the move invalidates it." }
|
|
151
|
+
params:
|
|
152
|
+
- { name: overbought_rsi, type: float, default: 70.0, doc: "RSI at/above which an up-move is exhausted (fade SHORT)." }
|
|
153
|
+
- { name: oversold_rsi, type: float, default: 30.0, doc: "RSI at/below which a down-move is exhausted (fade LONG)." }
|
|
154
|
+
- { name: min_extension_pct, type: float, default: 3.0, doc: "|recent return %| that qualifies a move as overextended (dog 3%+/~4h)." }
|
|
155
|
+
- { name: crowd_threshold, type: float, default: 0.15, doc: "Smart-money lean deviation from 0.5 that counts as crowded (dog ~15%+)." }
|
|
156
|
+
- { name: invalidation_factor, type: float, default: 2.0, doc: "A held fade is INVALIDATED when the move extends past this multiple of min_extension_pct while the RSI extreme is unresolved (the crowd kept pushing)." }
|
|
157
|
+
- { name: signal_strength, type: float, default: 1.0, doc: "Per-signal score unit multiplier." }
|
|
158
|
+
fn: |
|
|
159
|
+
def score_fade_crowd(rsi, move, sm, regime, positions, overbought_rsi, oversold_rsi, min_extension_pct, crowd_threshold, invalidation_factor, signal_strength):
|
|
160
|
+
# TODO(edit): the CROWD-FADE edge (a WORKING DEFAULT; dog/lemon/egret/owl + hyena regime gate).
|
|
161
|
+
# For each asset, an OVEREXTENSION is an RSI extreme confirmed by a recent price run: overbought =
|
|
162
|
+
# rsi >= overbought_rsi AND move (recent % return) >= min_extension_pct; oversold is the mirror.
|
|
163
|
+
# Fade it ONLY when the CROWD is aligned WITH the exhausted move (smart-money tilt in [0,1], 0.5
|
|
164
|
+
# neutral: crowded-long = tilt >= 0.5+crowd_threshold) AND the market-wide funding regime PERMITS
|
|
165
|
+
# the fade (LONG_CROWDED -> only SHORT fades; SHORT_CROWDED -> only LONG fades; NEUTRAL/"" -> both).
|
|
166
|
+
# ENTER the OPPOSITE direction when FLAT. EXIT a held fade when MEAN REVERSION IS COMPLETE (RSI
|
|
167
|
+
# crossed back through the 50 midline) OR the THESIS IS INVALIDATED (the move extended further
|
|
168
|
+
# against the fade). Held-exclusion + the real held direction live here (via positions) so entry
|
|
169
|
+
# and exit can never both fire. A missing leg simply abstains (no vote), never raises. Encode YOUR
|
|
170
|
+
# edge by editing this body — it is purity-checked (no ctx/MCP/I/O, math only) and its inline tests
|
|
171
|
+
# run at validate time.
|
|
172
|
+
reg = (regime or "").upper()
|
|
173
|
+
allow_short = reg != "SHORT_CROWDED"
|
|
174
|
+
allow_long = reg != "LONG_CROWDED"
|
|
175
|
+
held = {}
|
|
176
|
+
for record in positions or []:
|
|
177
|
+
coin = str(record.get("coin", "")).upper()
|
|
178
|
+
if coin:
|
|
179
|
+
held[coin] = record.get("direction") or "LONG"
|
|
180
|
+
entry_scores = {}
|
|
181
|
+
exit_scores = {}
|
|
182
|
+
for asset in sorted(set(rsi) | set(move)):
|
|
183
|
+
r = rsi.get(asset)
|
|
184
|
+
mv = move.get(asset)
|
|
185
|
+
if r is None or mv is None:
|
|
186
|
+
continue
|
|
187
|
+
raw = sm.get(asset, 0.5)
|
|
188
|
+
tilt = raw.get("tilt", raw.get("lean", 0.5)) if isinstance(raw, dict) else raw
|
|
189
|
+
crowd_long = tilt >= 0.5 + crowd_threshold
|
|
190
|
+
crowd_short = tilt <= 0.5 - crowd_threshold
|
|
191
|
+
overbought = r >= overbought_rsi and mv >= min_extension_pct
|
|
192
|
+
oversold = r <= oversold_rsi and mv <= -min_extension_pct
|
|
193
|
+
held_direction = held.get(str(asset).upper())
|
|
194
|
+
if held_direction is None:
|
|
195
|
+
if overbought and crowd_long and allow_short:
|
|
196
|
+
score = signal_strength * (abs(r - overbought_rsi) + abs(mv))
|
|
197
|
+
entry_scores[asset] = {"score": round(score, 4), "direction": "SHORT",
|
|
198
|
+
"reasons": ["overbought_rip", "crowd_long"]}
|
|
199
|
+
elif oversold and crowd_short and allow_long:
|
|
200
|
+
score = signal_strength * (abs(oversold_rsi - r) + abs(mv))
|
|
201
|
+
entry_scores[asset] = {"score": round(score, 4), "direction": "LONG",
|
|
202
|
+
"reasons": ["oversold_flush", "crowd_short"]}
|
|
203
|
+
else:
|
|
204
|
+
if held_direction == "SHORT":
|
|
205
|
+
reverted = r <= 50.0
|
|
206
|
+
invalidated = r >= overbought_rsi and mv >= min_extension_pct * invalidation_factor
|
|
207
|
+
else:
|
|
208
|
+
reverted = r >= 50.0
|
|
209
|
+
invalidated = r <= oversold_rsi and mv <= -min_extension_pct * invalidation_factor
|
|
210
|
+
if reverted or invalidated:
|
|
211
|
+
exit_scores[asset] = {"score": round(abs(mv), 4), "direction": held_direction,
|
|
212
|
+
"reasons": ["reversion_complete" if reverted else "thesis_invalidated"]}
|
|
213
|
+
return entry_scores, exit_scores
|
|
214
|
+
tests:
|
|
215
|
+
- name: crowd_long_overbought_fade_short
|
|
216
|
+
inputs:
|
|
217
|
+
rsi:
|
|
218
|
+
BTC: 75.0
|
|
219
|
+
move:
|
|
220
|
+
BTC: 5.0
|
|
221
|
+
sm:
|
|
222
|
+
BTC: 0.75
|
|
223
|
+
regime: NEUTRAL
|
|
224
|
+
positions: []
|
|
225
|
+
expect:
|
|
226
|
+
entry_scores:
|
|
227
|
+
BTC:
|
|
228
|
+
score: 10.0
|
|
229
|
+
direction: SHORT
|
|
230
|
+
reasons: ["overbought_rip", "crowd_long"]
|
|
231
|
+
exit_scores: {}
|
|
232
|
+
- name: crowd_short_oversold_fade_long
|
|
233
|
+
inputs:
|
|
234
|
+
rsi:
|
|
235
|
+
ETH: 25.0
|
|
236
|
+
move:
|
|
237
|
+
ETH: -5.0
|
|
238
|
+
sm:
|
|
239
|
+
ETH: 0.24999999999999997
|
|
240
|
+
regime: NEUTRAL
|
|
241
|
+
positions: []
|
|
242
|
+
expect:
|
|
243
|
+
entry_scores:
|
|
244
|
+
ETH:
|
|
245
|
+
score: 10.0
|
|
246
|
+
direction: LONG
|
|
247
|
+
reasons: ["oversold_flush", "crowd_short"]
|
|
248
|
+
exit_scores: {}
|
|
249
|
+
- name: regime_vetoes_long_fade
|
|
250
|
+
inputs:
|
|
251
|
+
rsi:
|
|
252
|
+
ETH: 25.0
|
|
253
|
+
move:
|
|
254
|
+
ETH: -5.0
|
|
255
|
+
sm:
|
|
256
|
+
ETH: 0.24999999999999997
|
|
257
|
+
regime: LONG_CROWDED
|
|
258
|
+
positions: []
|
|
259
|
+
expect:
|
|
260
|
+
entry_scores: {}
|
|
261
|
+
exit_scores: {}
|
|
262
|
+
- name: crowd_not_aligned_no_entry
|
|
263
|
+
inputs:
|
|
264
|
+
rsi:
|
|
265
|
+
BTC: 75.0
|
|
266
|
+
move:
|
|
267
|
+
BTC: 5.0
|
|
268
|
+
sm:
|
|
269
|
+
BTC: 0.5
|
|
270
|
+
regime: NEUTRAL
|
|
271
|
+
positions: []
|
|
272
|
+
expect:
|
|
273
|
+
entry_scores: {}
|
|
274
|
+
exit_scores: {}
|
|
275
|
+
- name: reversion_complete_exit_when_held
|
|
276
|
+
inputs:
|
|
277
|
+
rsi:
|
|
278
|
+
BTC: 45.0
|
|
279
|
+
move:
|
|
280
|
+
BTC: 0.0
|
|
281
|
+
sm:
|
|
282
|
+
BTC: 0.5
|
|
283
|
+
regime: NEUTRAL
|
|
284
|
+
positions:
|
|
285
|
+
- coin: BTC
|
|
286
|
+
direction: SHORT
|
|
287
|
+
szi: -1.0
|
|
288
|
+
entry_px: 100.0
|
|
289
|
+
unrealized_pnl_pct: 1.0
|
|
290
|
+
margin_used: 10.0
|
|
291
|
+
expect:
|
|
292
|
+
entry_scores: {}
|
|
293
|
+
exit_scores:
|
|
294
|
+
BTC:
|
|
295
|
+
score: 0.0
|
|
296
|
+
direction: SHORT
|
|
297
|
+
reasons: ["reversion_complete"]
|
|
298
|
+
- name: thesis_invalidated_exit_when_held
|
|
299
|
+
inputs:
|
|
300
|
+
rsi:
|
|
301
|
+
BTC: 75.0
|
|
302
|
+
move:
|
|
303
|
+
BTC: 7.0
|
|
304
|
+
sm:
|
|
305
|
+
BTC: 0.75
|
|
306
|
+
regime: NEUTRAL
|
|
307
|
+
positions:
|
|
308
|
+
- coin: BTC
|
|
309
|
+
direction: SHORT
|
|
310
|
+
szi: -1.0
|
|
311
|
+
entry_px: 100.0
|
|
312
|
+
unrealized_pnl_pct: 1.0
|
|
313
|
+
margin_used: 10.0
|
|
314
|
+
expect:
|
|
315
|
+
entry_scores: {}
|
|
316
|
+
exit_scores:
|
|
317
|
+
BTC:
|
|
318
|
+
score: 7.0
|
|
319
|
+
direction: SHORT
|
|
320
|
+
reasons: ["thesis_invalidated"]
|
|
321
|
+
# ranker: single best candidate (cardinality = 1).
|
|
322
|
+
- id: best
|
|
323
|
+
node: emit_single_best
|
|
324
|
+
with:
|
|
325
|
+
min_score: 0
|
|
326
|
+
# entry emitter: build the scan() OPEN signal list.
|
|
327
|
+
- id: emit
|
|
328
|
+
node: signal_envelope
|
|
329
|
+
with:
|
|
330
|
+
margin_pct: 5.0
|
|
331
|
+
leverage: 3.0
|
|
332
|
+
# exit emitter: reduce-only CLOSE list (no sizing/equity). The compiler splits
|
|
333
|
+
# this into a second, exit-only scanner; the DSL preset stays as the safety net.
|
|
334
|
+
- id: close_emit
|
|
335
|
+
node: close_signal_envelope
|
|
336
|
+
with:
|
|
337
|
+
signal_type: "thesis_exit"
|
|
338
|
+
|
|
339
|
+
edges:
|
|
340
|
+
# score -> rank -> emit
|
|
341
|
+
- { from: score.entry_scores, to: best.scores }
|
|
342
|
+
# candles fan out to the overextension read
|
|
343
|
+
- { from: candles.candles, to: rsi.candles }
|
|
344
|
+
- { from: candles.candles, to: move.candles }
|
|
345
|
+
# overextension + positions -> the fade pure_fn
|
|
346
|
+
- { from: rsi.rsi, to: score.rsi }
|
|
347
|
+
- { from: move.pct, to: score.move }
|
|
348
|
+
- { from: lean.lean, to: score.sm }
|
|
349
|
+
- { from: regime.regime, to: score.regime }
|
|
350
|
+
- { from: positions.positions, to: score.positions }
|
|
351
|
+
# entry tail: entry_scores -> rank -> emit
|
|
352
|
+
- { from: best.selected, to: emit.selected }
|
|
353
|
+
- { from: account.equity, to: emit.equity }
|
|
354
|
+
# exit tail: exit_scores -> close emitter (+ positions for direction)
|
|
355
|
+
- { from: score.exit_scores, to: close_emit.selected }
|
|
356
|
+
- { from: positions.positions, to: close_emit.positions }
|