@senpi/runtime 3.0.0-dev.runtime-phase-2.20260623210537 → 3.0.1-dev.openclaw-upgrade-mai.20260625140818
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/README.md +10 -9
- package/dist/actions/action-runtime-module.d.ts.map +1 -1
- package/dist/actions/action-runtime-module.js +1 -1
- package/dist/actions/action-runtime-module.js.map +1 -1
- package/dist/actions/decision-engine.d.ts.map +1 -1
- package/dist/actions/decision-engine.js +3 -0
- package/dist/actions/decision-engine.js.map +1 -1
- package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
- package/dist/actions/open-position/open-position.action.js +54 -14
- package/dist/actions/open-position/open-position.action.js.map +1 -1
- package/dist/actions/types.d.ts +10 -2
- package/dist/actions/types.d.ts.map +1 -1
- package/dist/actions/types.js +8 -0
- package/dist/actions/types.js.map +1 -1
- package/dist/cli/guide-commands.d.ts.map +1 -1
- package/dist/cli/guide-commands.js +80 -74
- package/dist/cli/guide-commands.js.map +1 -1
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +0 -108
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/constants/dsl/index.d.ts +2 -0
- package/dist/constants/dsl/index.d.ts.map +1 -1
- package/dist/constants/dsl/index.js +2 -0
- package/dist/constants/dsl/index.js.map +1 -1
- package/dist/dsl/config/parse-tiers.d.ts.map +1 -1
- package/dist/dsl/config/parse-tiers.js +6 -2
- package/dist/dsl/config/parse-tiers.js.map +1 -1
- package/dist/dsl/engine-client/config-mapping.d.ts +7 -0
- package/dist/dsl/engine-client/config-mapping.d.ts.map +1 -1
- package/dist/dsl/engine-client/config-mapping.js +16 -2
- package/dist/dsl/engine-client/config-mapping.js.map +1 -1
- package/dist/dsl/events/handlers.d.ts +18 -2
- package/dist/dsl/events/handlers.d.ts.map +1 -1
- package/dist/dsl/events/handlers.js +302 -213
- package/dist/dsl/events/handlers.js.map +1 -1
- package/dist/dsl/monitor/create-monitor.d.ts +15 -0
- package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
- package/dist/dsl/monitor/create-monitor.js +46 -9
- package/dist/dsl/monitor/create-monitor.js.map +1 -1
- package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
- package/dist/dsl/monitor/process-one-position.js +138 -77
- package/dist/dsl/monitor/process-one-position.js.map +1 -1
- package/dist/dsl/plugin/index.d.ts +16 -1
- package/dist/dsl/plugin/index.d.ts.map +1 -1
- package/dist/dsl/plugin/index.js +35 -4
- package/dist/dsl/plugin/index.js.map +1 -1
- package/dist/dsl/plugin/options.d.ts +18 -0
- package/dist/dsl/plugin/options.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/create-scanner-module.d.ts +4 -3
- package/dist/runtime/create-scanner-module.d.ts.map +1 -1
- package/dist/runtime/create-scanner-module.js +14 -5
- package/dist/runtime/create-scanner-module.js.map +1 -1
- package/dist/runtime/create-scanner-providers-for-run.d.ts.map +1 -1
- package/dist/runtime/create-scanner-providers-for-run.js +12 -1
- package/dist/runtime/create-scanner-providers-for-run.js.map +1 -1
- package/dist/runtime/load-runtime-config.js +1 -1
- package/dist/runtime/load-runtime-config.js.map +1 -1
- package/dist/runtime/map-strategies-registration.js +7 -8
- package/dist/runtime/map-strategies-registration.js.map +1 -1
- package/dist/runtime/map-strategies.d.ts.map +1 -1
- package/dist/runtime/map-strategies.js +12 -3
- package/dist/runtime/map-strategies.js.map +1 -1
- package/dist/runtime/reconcile-decide.d.ts +73 -0
- package/dist/runtime/reconcile-decide.d.ts.map +1 -0
- package/dist/runtime/reconcile-decide.js +186 -0
- package/dist/runtime/reconcile-decide.js.map +1 -0
- package/dist/runtime/reconcile-run.d.ts +62 -0
- package/dist/runtime/reconcile-run.d.ts.map +1 -0
- package/dist/runtime/reconcile-run.js +173 -0
- package/dist/runtime/reconcile-run.js.map +1 -0
- package/dist/runtime/reconcile-state.d.ts +44 -0
- package/dist/runtime/reconcile-state.d.ts.map +1 -1
- package/dist/runtime/reconcile-state.js +80 -44
- package/dist/runtime/reconcile-state.js.map +1 -1
- package/dist/runtime/risk-guard.d.ts.map +1 -1
- package/dist/runtime/risk-guard.js +17 -12
- package/dist/runtime/risk-guard.js.map +1 -1
- package/dist/runtime/runtime-schema.d.ts +2912 -1052
- package/dist/runtime/runtime-schema.d.ts.map +1 -1
- package/dist/runtime/runtime-schema.js +136 -42
- package/dist/runtime/runtime-schema.js.map +1 -1
- package/dist/runtime/runtime.d.ts +26 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +254 -8
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/scanner-registry.d.ts +1 -1
- package/dist/runtime/scanner-registry.d.ts.map +1 -1
- package/dist/runtime/scanner-registry.js +3 -12
- package/dist/runtime/scanner-registry.js.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.js +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
- package/dist/runtime/supervisor/orphan-sweep.js +1 -1
- package/dist/runtime/supervisor/orphan-sweep.js.map +1 -1
- package/dist/runtime/supervisor/supervisor-utils.d.ts +1 -1
- package/dist/runtime/supervisor/supervisor-utils.d.ts.map +1 -1
- package/dist/runtime/supervisor/supervisor-utils.js +1 -1
- package/dist/runtime/supervisor/supervisor-utils.js.map +1 -1
- package/dist/runtime-api/intake/acceptance-record.d.ts +10 -0
- package/dist/runtime-api/intake/acceptance-record.d.ts.map +1 -1
- package/dist/runtime-api/intake/acceptance-record.js +28 -0
- package/dist/runtime-api/intake/acceptance-record.js.map +1 -1
- package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-mount.js +28 -36
- package/dist/runtime-api/intake/intake-mount.js.map +1 -1
- package/dist/runtime-api/intake/process-signal-post.d.ts +20 -1
- package/dist/runtime-api/intake/process-signal-post.d.ts.map +1 -1
- package/dist/runtime-api/intake/process-signal-post.js +57 -6
- package/dist/runtime-api/intake/process-signal-post.js.map +1 -1
- package/dist/runtime-api/intake/scanner-launcher.d.ts +18 -4
- package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
- package/dist/runtime-api/intake/scanner-launcher.js +24 -3
- package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
- package/dist/runtime-api/intake/types.d.ts +16 -0
- package/dist/runtime-api/intake/types.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.schema.d.ts +4 -0
- package/dist/runtime-api/routes/signals.schema.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.schema.js +12 -0
- package/dist/runtime-api/routes/signals.schema.js.map +1 -1
- package/dist/scanners/external/scaffold/__main__.py +65 -24
- package/dist/scanners/external/scaffold/ctx.py +78 -0
- package/dist/scanners/external/scaffold/delivery.py +56 -22
- package/dist/scanners/external/scaffold/entrypoint_loader.py +42 -48
- package/dist/scanners/external/scaffold/journal.py +13 -2
- package/dist/scanners/external/scaffold/requirements.txt +11 -0
- package/dist/scanners/external/scaffold/scaffold.py +159 -128
- package/dist/scanners/external/scaffold/senpi_mcp.py +250 -0
- package/dist/scanners/external/scaffold/signal_envelope.py +177 -34
- package/dist/scanners/external/scaffold/state_manager.py +175 -0
- package/dist/scanners/external-scanner-receiver.d.ts +6 -0
- package/dist/scanners/external-scanner-receiver.d.ts.map +1 -1
- package/dist/scanners/external-scanner-receiver.js +14 -1
- package/dist/scanners/external-scanner-receiver.js.map +1 -1
- package/dist/scripts/run-dsl-standalone-live.d.ts +1 -1
- package/dist/scripts/run-dsl-standalone-live.js +5 -3
- package/dist/scripts/run-dsl-standalone-live.js.map +1 -1
- package/dist/senpi/client.d.ts +37 -4
- package/dist/senpi/client.d.ts.map +1 -1
- package/dist/senpi/client.js +101 -24
- package/dist/senpi/client.js.map +1 -1
- package/dist/state/state-manager.d.ts +17 -0
- package/dist/state/state-manager.d.ts.map +1 -1
- package/dist/state/state-manager.js +50 -4
- package/dist/state/state-manager.js.map +1 -1
- package/dist/strategy/strategy-state.d.ts +9 -1
- package/dist/strategy/strategy-state.d.ts.map +1 -1
- package/dist/strategy/strategy-state.js +62 -9
- package/dist/strategy/strategy-state.js.map +1 -1
- package/dist/tracking/constants.d.ts.map +1 -1
- package/dist/tracking/constants.js +2 -0
- package/dist/tracking/constants.js.map +1 -1
- package/dist/types/dsl/index.d.ts +1 -1
- package/dist/types/dsl/index.d.ts.map +1 -1
- package/dist/types/scanner.d.ts +7 -0
- package/dist/types/scanner.d.ts.map +1 -1
- package/dist/types/strategy.d.ts +3 -1
- package/dist/types/strategy.d.ts.map +1 -1
- package/dist/utils/keyed-mutex.d.ts +37 -0
- package/dist/utils/keyed-mutex.d.ts.map +1 -0
- package/dist/utils/keyed-mutex.js +77 -0
- package/dist/utils/keyed-mutex.js.map +1 -0
- package/examples/strategies/README.md +17 -12
- package/examples/strategies/iguana/README.md +135 -0
- package/examples/strategies/iguana/recipe.yaml +137 -0
- package/examples/strategies/iguana/scanners/scan.py +243 -0
- package/examples/strategies/iguana/scanners/scoring.py +152 -0
- package/examples/strategies/raptor/README.md +156 -0
- package/examples/strategies/raptor/recipe.yaml +154 -0
- package/examples/strategies/raptor/scanners/scan.py +349 -0
- package/examples/strategies/raptor/scanners/scoring.py +452 -0
- package/examples/strategies/spider/README.md +89 -0
- package/examples/strategies/spider/scalp/recipe.yaml +129 -0
- package/examples/strategies/spider/scalp/scanners/scan.py +219 -0
- package/examples/strategies/spider/scalp/scanners/scoring.py +179 -0
- package/examples/strategies/spider/swing/recipe.yaml +137 -0
- package/examples/strategies/spider/swing/scanners/scan.py +334 -0
- package/examples/strategies/spider/swing/scanners/scoring.py +184 -0
- package/examples/strategies/turbine/README.md +166 -0
- package/examples/strategies/turbine/recipe-runners.yaml +123 -0
- package/examples/strategies/turbine/recipe-volume.yaml +129 -0
- package/examples/strategies/turbine/scanners/scan.py +283 -0
- package/examples/strategies/turbine/scanners/scoring.py +190 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/scanners/external/liquidation-watchdog/README.md +0 -107
- package/dist/scanners/external/liquidation-watchdog/liquidation-watchdog.yaml +0 -48
- package/dist/scanners/external/liquidation-watchdog/producer.mjs +0 -336
- package/dist/scanners/external/momentum/README.md +0 -148
- package/dist/scanners/external/momentum/momentum.yaml +0 -116
- package/dist/scanners/external/momentum/producer.mjs +0 -251
- package/dist/scanners/external/shared/ingest.mjs +0 -112
- package/examples/README.md +0 -89
- package/examples/scanners-consumer-quickstart.mjs +0 -134
- package/examples/scanners-emerging-movers-v4.mjs +0 -287
- package/examples/scanners-liquidation-watchdog.mjs +0 -341
- package/examples/scanners-live-mcp.mjs +0 -208
- package/examples/scanners-multi-strategy.mjs +0 -165
- package/examples/scanners-position-tracker-e2e.mjs +0 -225
- package/examples/scanners-scheduled-events.mjs +0 -96
- package/examples/scanners-senpi-provider-client.mjs +0 -182
- package/examples/scanners-single-strategy.mjs +0 -198
- package/examples/strategies/dsl-showcase.yaml +0 -68
- package/examples/strategies/dsl.yaml +0 -51
- package/examples/strategies/fox-minimal-loosened.yaml +0 -240
- package/examples/strategies/fox-minimal.yaml +0 -242
- package/examples/strategies/minimal.yaml +0 -46
- package/examples/strategies/momentum/how-it-works.md +0 -48
- package/examples/strategies/momentum/momentum-guarded.yaml +0 -161
- package/examples/strategies/risk-showcase.yaml +0 -93
- package/examples/strategies/viper.yaml +0 -67
- package/examples/strategies/wolf.yaml +0 -72
- package/examples/yamls/sample-with-dsl-and-emerging-movers.yaml +0 -123
|
@@ -51,7 +51,7 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
51
51
|
normalizeDslCoinDex(event.coin, event.dex ?? "").coin &&
|
|
52
52
|
s.direction === newDirection);
|
|
53
53
|
}
|
|
54
|
-
async function handlePositionOpened(payload, triggerReason) {
|
|
54
|
+
async function handlePositionOpened(payload, triggerReason, opts) {
|
|
55
55
|
let event = null;
|
|
56
56
|
if (isHookEvent(payload)) {
|
|
57
57
|
event = hookToPositionOpenedEvent(payload);
|
|
@@ -72,105 +72,125 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
72
72
|
return;
|
|
73
73
|
event = normalizePositionOpenedEvent(event);
|
|
74
74
|
const dexKey = event.dex ?? "";
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
const posKey = stateKey(event.address, event.coin, dexKey);
|
|
76
|
+
// Serialize concurrent opens on the same (address,coin,dex) key.
|
|
77
|
+
// NON-REENTRANT: callers must NOT hold posKey when calling this function.
|
|
78
|
+
await stateManager.withKeyLock(posKey, async () => {
|
|
79
|
+
// Re-read inside the lock so a racing adopt that already wrote is visible.
|
|
80
|
+
const existing = await stateManager.getDslState(event.address, event.coin, dexKey);
|
|
81
|
+
if (existing?.active) {
|
|
82
|
+
logger.warn("[dsl] position.opened ignored: DSL state already active for this asset/dex", {
|
|
83
|
+
address: event.address,
|
|
84
|
+
asset: event.coin,
|
|
85
|
+
dex: dexKey,
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (await isDuplicatePositionEvent(event)) {
|
|
90
|
+
logger.warn("[dsl] duplicate position event ignored (same entry, size, coin, direction)", {
|
|
91
|
+
address: event.address,
|
|
92
|
+
asset: event.coin,
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const presetName = event.preset ?? config.preset;
|
|
97
|
+
const preset = config.presets[presetName];
|
|
98
|
+
if (!preset) {
|
|
99
|
+
logger.warn("preset not found, skipping DSL", { presetName, asset: event.coin });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const entryPrice = parseFloat(String(event.entryPx));
|
|
103
|
+
const direction = parseFloat(String(event.szi)) > 0 ? "LONG" : "SHORT";
|
|
104
|
+
const leverage = event.leverage ?? 1;
|
|
105
|
+
const phase1Floor = phase1FloorFromPreset(entryPrice, direction, preset.maxLossPct, leverage);
|
|
106
|
+
const resolvedTrigger = triggerReason ?? "position_opened";
|
|
107
|
+
const state = buildInitialState(event, preset, phase1Floor, resolvedTrigger);
|
|
108
|
+
const floor = state.floorPrice;
|
|
109
|
+
logger.info("[dsl] position.opened handling", {
|
|
86
110
|
address: event.address,
|
|
87
111
|
asset: event.coin,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
const presetName = event.preset ?? config.preset;
|
|
92
|
-
const preset = config.presets[presetName];
|
|
93
|
-
if (!preset) {
|
|
94
|
-
logger.warn("preset not found, skipping DSL", { presetName, asset: event.coin });
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const entryPrice = parseFloat(String(event.entryPx));
|
|
98
|
-
const direction = parseFloat(String(event.szi)) > 0 ? "LONG" : "SHORT";
|
|
99
|
-
const leverage = event.leverage ?? 1;
|
|
100
|
-
const phase1Floor = phase1FloorFromPreset(entryPrice, direction, preset.maxLossPct, leverage);
|
|
101
|
-
const resolvedTrigger = triggerReason ?? "position_opened";
|
|
102
|
-
const state = buildInitialState(event, preset, phase1Floor, resolvedTrigger);
|
|
103
|
-
const floor = state.floorPrice;
|
|
104
|
-
logger.info("[dsl] position.opened handling", {
|
|
105
|
-
address: event.address,
|
|
106
|
-
asset: event.coin,
|
|
107
|
-
dex: event.dex ?? "",
|
|
108
|
-
preset: presetName,
|
|
109
|
-
floor,
|
|
110
|
-
wallet: event.wallet,
|
|
111
|
-
});
|
|
112
|
-
const strategy = strategyStates.get(event.address);
|
|
113
|
-
if (!strategy) {
|
|
114
|
-
logger.warn("[dsl] no strategy found for address, skipping initial SL", { address: event.address });
|
|
115
|
-
state.slOrderId = null;
|
|
116
|
-
state.lastSlSyncError = "no strategy state for address";
|
|
117
|
-
state.lastError = state.lastSlSyncError;
|
|
118
|
-
state.lastErrorAt = now();
|
|
119
|
-
await stateManager.setDslState(event.address, event.coin, event.dex ?? "", state);
|
|
120
|
-
bus.emit(DSL_EVENTS.CREATED, buildDslCreatedPayload(event, presetName, preset, state, now()));
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const posKey = stateKey(event.address, event.coin, event.dex ?? "");
|
|
124
|
-
slPlacementInProgress.add(posKey);
|
|
125
|
-
try {
|
|
126
|
-
await stateManager.setDslState(event.address, event.coin, event.dex ?? "", state);
|
|
127
|
-
logger.info("[dsl] initial state written, calling editPosition for initial SL", {
|
|
128
|
-
asset: event.coin,
|
|
129
|
-
wallet: event.wallet,
|
|
130
|
-
dex: event.dex || undefined,
|
|
112
|
+
dex: event.dex ?? "",
|
|
113
|
+
preset: presetName,
|
|
131
114
|
floor,
|
|
115
|
+
wallet: event.wallet,
|
|
132
116
|
});
|
|
117
|
+
const strategy = strategyStates.get(event.address);
|
|
118
|
+
if (!strategy) {
|
|
119
|
+
logger.warn("[dsl] no strategy found for address, skipping initial SL", { address: event.address });
|
|
120
|
+
state.slOrderId = null;
|
|
121
|
+
state.lastSlSyncError = "no strategy state for address";
|
|
122
|
+
state.lastError = state.lastSlSyncError;
|
|
123
|
+
state.lastErrorAt = now();
|
|
124
|
+
await stateManager.setDslState(event.address, event.coin, event.dex ?? "", state);
|
|
125
|
+
bus.emit(DSL_EVENTS.CREATED, buildDslCreatedPayload(event, presetName, preset, state, now()));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const shouldPlaceSl = opts?.placeSl !== false;
|
|
129
|
+
if (!shouldPlaceSl) {
|
|
130
|
+
// Adoption with deferred SL: monitor places it on the next tick via shouldSyncExchangeSl.
|
|
131
|
+
state.slOrderId = null;
|
|
132
|
+
state.lastSlSyncError = "adopt: SL deferred to monitor";
|
|
133
|
+
logger.info("[dsl] initial state written, SL deferred to monitor (adopt)", {
|
|
134
|
+
asset: event.coin,
|
|
135
|
+
wallet: event.wallet,
|
|
136
|
+
dex: event.dex || undefined,
|
|
137
|
+
floor,
|
|
138
|
+
});
|
|
139
|
+
await stateManager.setDslState(event.address, event.coin, event.dex ?? "", state);
|
|
140
|
+
bus.emit(DSL_EVENTS.CREATED, buildDslCreatedPayload(event, presetName, preset, state, now()));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
slPlacementInProgress.add(posKey);
|
|
133
144
|
try {
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
await stateManager.setDslState(event.address, event.coin, event.dex ?? "", state);
|
|
146
|
+
logger.info("[dsl] initial state written, calling editPosition for initial SL", {
|
|
147
|
+
asset: event.coin,
|
|
148
|
+
wallet: event.wallet,
|
|
136
149
|
dex: event.dex || undefined,
|
|
137
|
-
|
|
138
|
-
}), {
|
|
139
|
-
retries: DSL_EXTERNAL_RETRIES,
|
|
140
|
-
delayMs: DSL_EXTERNAL_RETRY_DELAY_MS,
|
|
141
|
-
label: "editPosition (initial SL)",
|
|
142
|
-
onRetry: (attempt, remaining, err) => logger.warn("[dsl] editPosition retry", { attempt, remaining, asset: event.coin, err }),
|
|
150
|
+
floor,
|
|
143
151
|
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
try {
|
|
153
|
+
const res = await withRetry(() => strategy.editPosition({
|
|
154
|
+
coin: event.coin,
|
|
155
|
+
dex: event.dex || undefined,
|
|
156
|
+
stopLoss: { triggerPx: floor },
|
|
157
|
+
}), {
|
|
158
|
+
retries: DSL_EXTERNAL_RETRIES,
|
|
159
|
+
delayMs: DSL_EXTERNAL_RETRY_DELAY_MS,
|
|
160
|
+
label: "editPosition (initial SL)",
|
|
161
|
+
onRetry: (attempt, remaining, err) => logger.warn("[dsl] editPosition retry", { attempt, remaining, asset: event.coin, err }),
|
|
162
|
+
});
|
|
163
|
+
state.slOrderId = res.slOrderId ?? null;
|
|
164
|
+
state.lastSyncedFloorPrice = floor;
|
|
165
|
+
state.slOrderIdUpdatedAt = now();
|
|
166
|
+
state.lastSlSyncError = null;
|
|
167
|
+
logger.info("[dsl] exchange SL created (position.opened)", {
|
|
168
|
+
asset: event.coin,
|
|
169
|
+
address: event.address,
|
|
170
|
+
slOrderId: state.slOrderId,
|
|
171
|
+
floorPrice: floor,
|
|
172
|
+
entryPrice: state.entryPrice,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
state.slOrderId = null;
|
|
177
|
+
state.lastSlSyncError = String(err);
|
|
178
|
+
state.lastError = String(err);
|
|
179
|
+
state.lastErrorAt = now();
|
|
180
|
+
logger.warn("initial SL place failed", { asset: event.coin, err });
|
|
181
|
+
}
|
|
182
|
+
logger.info("[dsl] writing state after initial SL", {
|
|
149
183
|
asset: event.coin,
|
|
150
|
-
address: event.address,
|
|
151
184
|
slOrderId: state.slOrderId,
|
|
152
|
-
|
|
153
|
-
entryPrice: state.entryPrice,
|
|
185
|
+
key: posKey,
|
|
154
186
|
});
|
|
187
|
+
await stateManager.setDslState(event.address, event.coin, event.dex ?? "", state);
|
|
188
|
+
bus.emit(DSL_EVENTS.CREATED, buildDslCreatedPayload(event, presetName, preset, state, now()));
|
|
155
189
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
state.lastSlSyncError = String(err);
|
|
159
|
-
state.lastError = String(err);
|
|
160
|
-
state.lastErrorAt = now();
|
|
161
|
-
logger.warn("initial SL place failed", { asset: event.coin, err });
|
|
190
|
+
finally {
|
|
191
|
+
slPlacementInProgress.delete(posKey);
|
|
162
192
|
}
|
|
163
|
-
|
|
164
|
-
asset: event.coin,
|
|
165
|
-
slOrderId: state.slOrderId,
|
|
166
|
-
key: posKey,
|
|
167
|
-
});
|
|
168
|
-
await stateManager.setDslState(event.address, event.coin, event.dex ?? "", state);
|
|
169
|
-
bus.emit(DSL_EVENTS.CREATED, buildDslCreatedPayload(event, presetName, preset, state, now()));
|
|
170
|
-
}
|
|
171
|
-
finally {
|
|
172
|
-
slPlacementInProgress.delete(posKey);
|
|
173
|
-
}
|
|
193
|
+
});
|
|
174
194
|
}
|
|
175
195
|
async function handlePositionClosed(payload) {
|
|
176
196
|
let event = null;
|
|
@@ -188,62 +208,65 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
188
208
|
asset: event.asset,
|
|
189
209
|
dex: event.dex ?? "",
|
|
190
210
|
});
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
const closedKey = stateKey(event.address, event.asset, event.dex ?? "");
|
|
212
|
+
await stateManager.withKeyLock(closedKey, async () => {
|
|
213
|
+
const state = await stateManager.getDslStateByAsset(event.address, event.asset, event.dex);
|
|
214
|
+
if (!state?.active)
|
|
215
|
+
return;
|
|
216
|
+
logger.info("[dsl] closing active state", { asset: state.asset, slOrderId: state.slOrderId });
|
|
217
|
+
let reason = CLOSE_REASON.MANUAL_CLOSE;
|
|
218
|
+
if (state.slOrderId != null) {
|
|
219
|
+
try {
|
|
220
|
+
const strategy = strategyStates.get(state.address);
|
|
221
|
+
const status = strategy
|
|
222
|
+
? await withRetry(() => strategy.getOrderStatus(state.slOrderId), {
|
|
223
|
+
retries: DSL_EXTERNAL_RETRIES,
|
|
224
|
+
delayMs: DSL_EXTERNAL_RETRY_DELAY_MS,
|
|
225
|
+
label: "getOrderStatus (position.closed)",
|
|
226
|
+
onRetry: (attempt, remaining, err) => logger.warn("[dsl] getOrderStatus retry", { attempt, remaining, asset: event.asset, err }),
|
|
227
|
+
})
|
|
228
|
+
: null;
|
|
229
|
+
if (status?.filled)
|
|
230
|
+
reason = CLOSE_REASON.EXCHANGE_SL_HIT;
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
logger.warn("getOrderStatus failed, assuming manual_close", { asset: event.asset, err: e });
|
|
234
|
+
}
|
|
209
235
|
}
|
|
210
|
-
|
|
211
|
-
|
|
236
|
+
const rawCloseReason = fromHook ? payload.data?.closeReason : undefined;
|
|
237
|
+
const dataCloseReason = typeof rawCloseReason === "string" ? rawCloseReason : null;
|
|
238
|
+
const archiveCloseReason = reason === CLOSE_REASON.MANUAL_CLOSE && dataCloseReason ? dataCloseReason : reason;
|
|
239
|
+
logger.info("[dsl] archiving position (position.closed)", {
|
|
240
|
+
address: event.address,
|
|
241
|
+
asset: state.asset,
|
|
242
|
+
closeReason: archiveCloseReason,
|
|
243
|
+
slOrderId: state.slOrderId,
|
|
244
|
+
fromHook: fromHook,
|
|
245
|
+
});
|
|
246
|
+
const closedState = { ...state, active: false, closedAt: now(), closeReason: archiveCloseReason };
|
|
247
|
+
await stateManager.archiveAndDeleteDslState(state.address, state.asset, state.dex, closedState);
|
|
248
|
+
if (state.handoffStatus === "ok" && state.backendDslPositionId != null) {
|
|
249
|
+
fireAndForgetBackendDelete(state, "position.closed");
|
|
212
250
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
address: state.address,
|
|
232
|
-
asset: state.asset,
|
|
233
|
-
wallet: state.wallet,
|
|
234
|
-
reason,
|
|
235
|
-
closeReason: archiveCloseReason,
|
|
236
|
-
timestamp: closedTs,
|
|
237
|
-
// Provenance marker (data only, no consumer branches on it): this emit re-broadcasts a close
|
|
238
|
-
// after archiving DSL state; the recording subscriber skips it to avoid double-counting a
|
|
239
|
-
// close already recorded from the original (enveloped or flat) emit.
|
|
240
|
-
source: "dsl_archive",
|
|
251
|
+
const closedTs = now();
|
|
252
|
+
bus.emit(EVENTS.POSITION_CLOSED, {
|
|
253
|
+
address: state.address,
|
|
254
|
+
asset: state.asset,
|
|
255
|
+
wallet: state.wallet,
|
|
256
|
+
reason,
|
|
257
|
+
closeReason: archiveCloseReason,
|
|
258
|
+
timestamp: closedTs,
|
|
259
|
+
// Provenance marker (data only, no consumer branches on it): this emit re-broadcasts a close
|
|
260
|
+
// after archiving DSL state; the recording subscriber skips it to avoid double-counting a
|
|
261
|
+
// close already recorded from the original (enveloped or flat) emit.
|
|
262
|
+
source: "dsl_archive",
|
|
263
|
+
});
|
|
264
|
+
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(closedState, {
|
|
265
|
+
reason: archiveCloseReason,
|
|
266
|
+
closeReason: archiveCloseReason,
|
|
267
|
+
timestamp: closedTs,
|
|
268
|
+
}));
|
|
241
269
|
});
|
|
242
|
-
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(closedState, {
|
|
243
|
-
reason: archiveCloseReason,
|
|
244
|
-
closeReason: archiveCloseReason,
|
|
245
|
-
timestamp: closedTs,
|
|
246
|
-
}));
|
|
247
270
|
}
|
|
248
271
|
async function handlePositionFlipped(payload) {
|
|
249
272
|
let event = null;
|
|
@@ -260,26 +283,36 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
260
283
|
archiveAsset: event.asset,
|
|
261
284
|
newCoin: event.coin,
|
|
262
285
|
});
|
|
286
|
+
// Archive the OLD state under its own key — lock released before calling handlePositionOpened
|
|
287
|
+
// (which locks the NEW key itself; keys differ after a flip).
|
|
263
288
|
const oldState = await stateManager.getDslStateByAsset(event.address, event.asset, event.dex);
|
|
264
289
|
if (oldState?.active) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
290
|
+
const oldKey = stateKey(event.address, oldState.asset, oldState.dex ?? "");
|
|
291
|
+
await stateManager.withKeyLock(oldKey, async () => {
|
|
292
|
+
// Re-read to confirm still active (concurrent close could have already archived it).
|
|
293
|
+
const currentOld = await stateManager.getDslStateByAsset(event.address, event.asset, event.dex);
|
|
294
|
+
if (!currentOld?.active)
|
|
295
|
+
return;
|
|
296
|
+
logger.info("[dsl] archiving position (position.flipped)", {
|
|
297
|
+
address: event.address,
|
|
298
|
+
asset: currentOld.asset,
|
|
299
|
+
dex: currentOld.dex ?? "",
|
|
300
|
+
closeReason: CLOSE_REASON.FLIPPED,
|
|
301
|
+
slOrderId: currentOld.slOrderId,
|
|
302
|
+
});
|
|
303
|
+
const closedState = { ...currentOld, active: false, closedAt: now(), closeReason: CLOSE_REASON.FLIPPED };
|
|
304
|
+
// Must use canonical keys from loaded state — not event.asset (e.g. xyz:BASE vs BASE + dex xyz
|
|
305
|
+
// produce different stateKey paths; wrong asset would archive nothing and leave the live file active).
|
|
306
|
+
await stateManager.archiveAndDeleteDslState(event.address, currentOld.asset, currentOld.dex ?? "", closedState);
|
|
307
|
+
if (currentOld.handoffStatus === "ok" && currentOld.backendDslPositionId != null) {
|
|
308
|
+
fireAndForgetBackendDelete(currentOld, "position.flipped");
|
|
309
|
+
}
|
|
271
310
|
});
|
|
272
|
-
const closedState = { ...oldState, active: false, closedAt: now(), closeReason: CLOSE_REASON.FLIPPED };
|
|
273
|
-
// Must use canonical keys from loaded state — not event.asset (e.g. xyz:BASE vs BASE + dex xyz
|
|
274
|
-
// produce different stateKey paths; wrong asset would archive nothing and leave the live file active).
|
|
275
|
-
await stateManager.archiveAndDeleteDslState(event.address, oldState.asset, oldState.dex ?? "", closedState);
|
|
276
|
-
if (oldState.handoffStatus === "ok" && oldState.backendDslPositionId != null) {
|
|
277
|
-
fireAndForgetBackendDelete(oldState, "position.flipped");
|
|
278
|
-
}
|
|
279
311
|
}
|
|
280
312
|
else {
|
|
281
313
|
logger.warn("[dsl] position.flipped: no active DSL state matched for archive (check asset/dex vs state file). If a live file still exists for this market, the next position.opened may be ignored as duplicate.", { address: event.address, asset: event.asset, dex: event.dex ?? "" });
|
|
282
314
|
}
|
|
315
|
+
// Old-key lock is released above. handlePositionOpened locks the NEW key itself.
|
|
283
316
|
// New row uses `coin` (and dex) from the flip payload — not `asset`, which identifies the pre-flip state for archival.
|
|
284
317
|
const openedPayload = {
|
|
285
318
|
address: event.address,
|
|
@@ -305,8 +338,13 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
305
338
|
if (!raw)
|
|
306
339
|
return;
|
|
307
340
|
const event = normalizePositionOpenedEvent(raw);
|
|
308
|
-
|
|
309
|
-
|
|
341
|
+
// Archive the OLD state under its key — lock released BEFORE calling handlePositionOpened
|
|
342
|
+
// (which re-locks the same key; holding it across the nested call would deadlock).
|
|
343
|
+
const archiveKey = stateKey(event.address, event.coin, event.dex ?? "");
|
|
344
|
+
await stateManager.withKeyLock(archiveKey, async () => {
|
|
345
|
+
const oldState = await stateManager.getDslStateByAsset(event.address, event.coin, event.dex);
|
|
346
|
+
if (!oldState?.active)
|
|
347
|
+
return;
|
|
310
348
|
const closedState = {
|
|
311
349
|
...oldState,
|
|
312
350
|
active: false,
|
|
@@ -319,7 +357,8 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
319
357
|
if (oldState.handoffStatus === "ok" && oldState.backendDslPositionId != null) {
|
|
320
358
|
fireAndForgetBackendDelete(oldState, "position.increased");
|
|
321
359
|
}
|
|
322
|
-
}
|
|
360
|
+
});
|
|
361
|
+
// archiveKey lock is released; handlePositionOpened re-acquires it safely.
|
|
323
362
|
await handlePositionOpened(event, "position_increased");
|
|
324
363
|
}
|
|
325
364
|
async function handlePositionDecreased(payload) {
|
|
@@ -333,8 +372,13 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
333
372
|
if (!raw)
|
|
334
373
|
return;
|
|
335
374
|
const event = normalizePositionOpenedEvent(raw);
|
|
336
|
-
|
|
337
|
-
|
|
375
|
+
// Archive the OLD state under its key — lock released BEFORE calling handlePositionOpened
|
|
376
|
+
// (which re-locks the same key; holding it across the nested call would deadlock).
|
|
377
|
+
const archiveKey = stateKey(event.address, event.coin, event.dex ?? "");
|
|
378
|
+
await stateManager.withKeyLock(archiveKey, async () => {
|
|
379
|
+
const oldState = await stateManager.getDslStateByAsset(event.address, event.coin, event.dex);
|
|
380
|
+
if (!oldState?.active)
|
|
381
|
+
return;
|
|
338
382
|
const closedState = {
|
|
339
383
|
...oldState,
|
|
340
384
|
active: false,
|
|
@@ -347,7 +391,8 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
347
391
|
if (oldState.handoffStatus === "ok" && oldState.backendDslPositionId != null) {
|
|
348
392
|
fireAndForgetBackendDelete(oldState, "position.decreased");
|
|
349
393
|
}
|
|
350
|
-
}
|
|
394
|
+
});
|
|
395
|
+
// archiveKey lock is released; handlePositionOpened re-acquires it safely.
|
|
351
396
|
await handlePositionOpened(event, "position_decreased");
|
|
352
397
|
}
|
|
353
398
|
async function deleteDslsForStrategy(address) {
|
|
@@ -355,62 +400,70 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
355
400
|
const ts = now();
|
|
356
401
|
let abandoned = 0;
|
|
357
402
|
for (const key of keys) {
|
|
358
|
-
|
|
359
|
-
|
|
403
|
+
// Peek outside the lock to skip inactive entries cheaply.
|
|
404
|
+
const peeked = await stateManager.getOrLoad(key);
|
|
405
|
+
if (!peeked?.active)
|
|
360
406
|
continue;
|
|
361
|
-
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
407
|
+
// Lock the per-position key for the archive RMW.
|
|
408
|
+
const perPosKey = stateKey(peeked.address, peeked.asset, peeked.dex ?? "");
|
|
409
|
+
await stateManager.withKeyLock(perPosKey, async () => {
|
|
410
|
+
const state = await stateManager.getOrLoad(key);
|
|
411
|
+
if (!state?.active)
|
|
412
|
+
return;
|
|
413
|
+
abandoned += 1;
|
|
414
|
+
const strategy = strategyStates.get(state.address);
|
|
415
|
+
const cleanupPromises = [];
|
|
416
|
+
if (strategy) {
|
|
417
|
+
cleanupPromises.push(withRetry(() => strategy.editPosition({ coin: state.asset, dex: state.dex || undefined }), {
|
|
418
|
+
retries: DSL_EXTERNAL_RETRIES,
|
|
419
|
+
delayMs: DSL_EXTERNAL_RETRY_DELAY_MS,
|
|
420
|
+
label: "editPosition (cancel SL, dsl delete)",
|
|
421
|
+
onRetry: (attempt, remaining, err) => logger.warn("[dsl] editPosition cancel SL retry", { attempt, remaining, err }),
|
|
422
|
+
}).then(() => undefined).catch((err) => {
|
|
423
|
+
logger.warn("[dsl] cancel SL failed during deleteDslsForStrategy; continuing archive", {
|
|
424
|
+
address,
|
|
425
|
+
asset: state.asset,
|
|
426
|
+
err,
|
|
427
|
+
});
|
|
428
|
+
}));
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
logger.warn("[dsl] no strategy found for address, skipping SL cancel", { address });
|
|
432
|
+
}
|
|
433
|
+
if (state.handoffStatus === "ok" && state.backendDslPositionId != null && strategy) {
|
|
434
|
+
cleanupPromises.push(strategy.deleteRatchetStop({
|
|
373
435
|
asset: state.asset,
|
|
374
|
-
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
436
|
+
dex: state.dex || undefined,
|
|
437
|
+
}).then(() => {
|
|
438
|
+
logger.info("[dsl] deleted backend dsl position (deleteDslsForStrategy)", {
|
|
439
|
+
address,
|
|
440
|
+
asset: state.asset,
|
|
441
|
+
});
|
|
442
|
+
}).catch((err) => {
|
|
443
|
+
logger.warn("[dsl] failed to delete backend dsl position (deleteDslsForStrategy)", {
|
|
444
|
+
address,
|
|
445
|
+
asset: state.asset,
|
|
446
|
+
err,
|
|
447
|
+
});
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
await Promise.all(cleanupPromises);
|
|
451
|
+
const closedState = {
|
|
452
|
+
...state,
|
|
453
|
+
active: false,
|
|
454
|
+
closedAt: ts,
|
|
455
|
+
closeReason: CLOSE_REASON.DSL_DELETED,
|
|
456
|
+
};
|
|
457
|
+
await stateManager.archiveAndDeleteDslState(state.address, state.asset, state.dex, closedState);
|
|
458
|
+
bus.emit(DSL_EVENTS.DELETED, {
|
|
459
|
+
address: state.address,
|
|
383
460
|
asset: state.asset,
|
|
384
|
-
dex: state.dex
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}).catch((err) => {
|
|
391
|
-
logger.warn("[dsl] failed to delete backend dsl position (deleteDslsForStrategy)", {
|
|
392
|
-
address,
|
|
393
|
-
asset: state.asset,
|
|
394
|
-
err,
|
|
395
|
-
});
|
|
396
|
-
}));
|
|
397
|
-
}
|
|
398
|
-
await Promise.all(cleanupPromises);
|
|
399
|
-
const closedState = {
|
|
400
|
-
...state,
|
|
401
|
-
active: false,
|
|
402
|
-
closedAt: ts,
|
|
403
|
-
closeReason: CLOSE_REASON.DSL_DELETED,
|
|
404
|
-
};
|
|
405
|
-
await stateManager.archiveAndDeleteDslState(state.address, state.asset, state.dex, closedState);
|
|
406
|
-
bus.emit(DSL_EVENTS.DELETED, {
|
|
407
|
-
address: state.address,
|
|
408
|
-
asset: state.asset,
|
|
409
|
-
dex: state.dex ?? "",
|
|
410
|
-
triggerReason: state.triggerReason,
|
|
411
|
-
__positionId: state.positionId ?? state.correlationId,
|
|
412
|
-
__correlationId: state.correlationId,
|
|
413
|
-
timestamp: ts,
|
|
461
|
+
dex: state.dex ?? "",
|
|
462
|
+
triggerReason: state.triggerReason,
|
|
463
|
+
__positionId: state.positionId ?? state.correlationId,
|
|
464
|
+
__correlationId: state.correlationId,
|
|
465
|
+
timestamp: ts,
|
|
466
|
+
});
|
|
414
467
|
});
|
|
415
468
|
}
|
|
416
469
|
return abandoned;
|
|
@@ -418,6 +471,40 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
418
471
|
function isSlPlacementInProgress(key) {
|
|
419
472
|
return slPlacementInProgress.has(key);
|
|
420
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Adopt an orphaned exchange position into DSL.
|
|
476
|
+
* Calls handlePositionOpened directly — NO bus emit of on_position_opened / position.opened.
|
|
477
|
+
* triggerReason is always "adopted" so formatDslCreated returns null (no user "Opened" message).
|
|
478
|
+
*/
|
|
479
|
+
async function adoptPosition(payload, opts) {
|
|
480
|
+
await handlePositionOpened(payload, "adopted", opts);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Resize: archive any existing ACTIVE DSL state for the (coin, dex), then call adoptPosition.
|
|
484
|
+
* Archive uses CLOSE_REASON.RECONCILE_RESIZE. Lock is released before the nested adopt
|
|
485
|
+
* (same pattern as handlePositionIncreased) to avoid deadlock.
|
|
486
|
+
*/
|
|
487
|
+
async function resizePosition(payload, opts) {
|
|
488
|
+
const dexKey = payload.dex ?? "";
|
|
489
|
+
const archiveKey = stateKey(payload.address, payload.coin, dexKey);
|
|
490
|
+
await stateManager.withKeyLock(archiveKey, async () => {
|
|
491
|
+
const oldState = await stateManager.getDslStateByAsset(payload.address, payload.coin, dexKey);
|
|
492
|
+
if (!oldState?.active)
|
|
493
|
+
return;
|
|
494
|
+
const closedState = {
|
|
495
|
+
...oldState,
|
|
496
|
+
active: false,
|
|
497
|
+
closedAt: now(),
|
|
498
|
+
closeReason: CLOSE_REASON.RECONCILE_RESIZE,
|
|
499
|
+
};
|
|
500
|
+
await stateManager.archiveAndDeleteDslState(payload.address, oldState.asset, oldState.dex, closedState);
|
|
501
|
+
if (oldState.handoffStatus === "ok" && oldState.backendDslPositionId != null) {
|
|
502
|
+
fireAndForgetBackendDelete(oldState, "reconcile_resize");
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
// archiveKey lock released above; adoptPosition (via handlePositionOpened) re-acquires it safely.
|
|
506
|
+
await adoptPosition(payload, opts);
|
|
507
|
+
}
|
|
421
508
|
function fireAndForgetBackendDelete(state, trigger) {
|
|
422
509
|
const resolveAndDelete = async () => {
|
|
423
510
|
const strategy = strategyStates.get(state.address);
|
|
@@ -465,6 +552,8 @@ export function createEventHandlers(config, stateManager, strategyStates, bus, _
|
|
|
465
552
|
handlePositionDecreased,
|
|
466
553
|
deleteDslsForStrategy,
|
|
467
554
|
isSlPlacementInProgress,
|
|
555
|
+
adoptPosition,
|
|
556
|
+
resizePosition,
|
|
468
557
|
};
|
|
469
558
|
}
|
|
470
559
|
//# sourceMappingURL=handlers.js.map
|