@senpi/runtime 3.0.135 → 3.0.139
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/actions/action-events.d.ts +7 -21
- package/dist/actions/action-events.d.ts.map +1 -1
- package/dist/actions/action-events.js +26 -54
- package/dist/actions/action-events.js.map +1 -1
- package/dist/actions/action-runtime-module.d.ts.map +1 -1
- package/dist/actions/action-runtime-module.js +7 -38
- package/dist/actions/action-runtime-module.js.map +1 -1
- package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
- package/dist/actions/close-position/close-position.action.js +15 -122
- package/dist/actions/close-position/close-position.action.js.map +1 -1
- package/dist/actions/decision-engine.d.ts.map +1 -1
- package/dist/actions/decision-engine.js +2 -15
- package/dist/actions/decision-engine.js.map +1 -1
- package/dist/actions/event-derivation.d.ts +2 -71
- package/dist/actions/event-derivation.d.ts.map +1 -1
- package/dist/actions/event-derivation.js +8 -251
- package/dist/actions/event-derivation.js.map +1 -1
- package/dist/actions/open-position/open-position.action.d.ts +2 -12
- package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
- package/dist/actions/open-position/open-position.action.js +41 -141
- package/dist/actions/open-position/open-position.action.js.map +1 -1
- package/dist/actions/position-tracker/position-tracker.action.d.ts +1 -2
- package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
- package/dist/actions/position-tracker/position-tracker.action.js +17 -62
- package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
- package/dist/actions/store/types.d.ts +2 -9
- package/dist/actions/store/types.d.ts.map +1 -1
- package/dist/actions/store/types.js.map +1 -1
- package/dist/actions/types.d.ts +0 -13
- package/dist/actions/types.d.ts.map +1 -1
- package/dist/actions/types.js +0 -13
- package/dist/actions/types.js.map +1 -1
- package/dist/cli/guide-commands.d.ts.map +1 -1
- package/dist/cli/guide-commands.js +7 -4
- package/dist/cli/guide-commands.js.map +1 -1
- package/dist/constants/dsl/index.d.ts +0 -6
- package/dist/constants/dsl/index.d.ts.map +1 -1
- package/dist/constants/dsl/index.js +0 -6
- package/dist/constants/dsl/index.js.map +1 -1
- package/dist/constants/telemetry.d.ts +15 -0
- package/dist/constants/telemetry.d.ts.map +1 -0
- package/dist/constants/telemetry.js +28 -0
- package/dist/constants/telemetry.js.map +1 -0
- package/dist/dsl/engine-client/event-mapping.d.ts.map +1 -1
- package/dist/dsl/engine-client/event-mapping.js +0 -14
- package/dist/dsl/engine-client/event-mapping.js.map +1 -1
- package/dist/dsl/events/build-closed-event.d.ts +1 -16
- package/dist/dsl/events/build-closed-event.d.ts.map +1 -1
- package/dist/dsl/events/build-closed-event.js +0 -6
- package/dist/dsl/events/build-closed-event.js.map +1 -1
- package/dist/dsl/events/handlers.d.ts +0 -7
- package/dist/dsl/events/handlers.d.ts.map +1 -1
- package/dist/dsl/events/handlers.js +7 -37
- package/dist/dsl/events/handlers.js.map +1 -1
- package/dist/dsl/monitor/create-monitor.d.ts +2 -6
- package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
- package/dist/dsl/monitor/create-monitor.js +9 -82
- package/dist/dsl/monitor/create-monitor.js.map +1 -1
- package/dist/dsl/monitor/dsl-failure-events.d.ts +2 -45
- package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -1
- package/dist/dsl/monitor/dsl-failure-events.js +3 -52
- package/dist/dsl/monitor/dsl-failure-events.js.map +1 -1
- package/dist/dsl/monitor/process-one-position.d.ts +0 -1
- package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
- package/dist/dsl/monitor/process-one-position.js +36 -159
- package/dist/dsl/monitor/process-one-position.js.map +1 -1
- package/dist/dsl/plugin/index.d.ts +2 -8
- package/dist/dsl/plugin/index.d.ts.map +1 -1
- package/dist/dsl/plugin/index.js +8 -20
- package/dist/dsl/plugin/index.js.map +1 -1
- package/dist/health/types.d.ts +0 -13
- package/dist/health/types.d.ts.map +1 -1
- package/dist/health/types.js +0 -12
- package/dist/health/types.js.map +1 -1
- package/dist/index.d.ts +8 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -272
- package/dist/index.js.map +1 -1
- package/dist/runtime/action-registry.d.ts.map +1 -1
- package/dist/runtime/action-registry.js +3 -4
- package/dist/runtime/action-registry.js.map +1 -1
- package/dist/runtime/domain-events.d.ts.map +1 -1
- package/dist/runtime/domain-events.js +0 -7
- package/dist/runtime/domain-events.js.map +1 -1
- package/dist/runtime/events-telemetry.d.ts +96 -0
- package/dist/runtime/events-telemetry.d.ts.map +1 -0
- package/dist/runtime/events-telemetry.js +232 -0
- package/dist/runtime/events-telemetry.js.map +1 -0
- package/dist/runtime/lifecycle-events.d.ts +3 -15
- package/dist/runtime/lifecycle-events.d.ts.map +1 -1
- package/dist/runtime/lifecycle-events.js +3 -17
- package/dist/runtime/lifecycle-events.js.map +1 -1
- package/dist/runtime/lifecycle-reaper.d.ts +6 -4
- package/dist/runtime/lifecycle-reaper.d.ts.map +1 -1
- package/dist/runtime/lifecycle-reaper.js +6 -4
- package/dist/runtime/lifecycle-reaper.js.map +1 -1
- package/dist/runtime/reconcile-run.d.ts +3 -13
- package/dist/runtime/reconcile-run.d.ts.map +1 -1
- package/dist/runtime/reconcile-run.js +3 -4
- package/dist/runtime/reconcile-run.js.map +1 -1
- package/dist/runtime/reconcile-state.d.ts +0 -8
- package/dist/runtime/reconcile-state.d.ts.map +1 -1
- package/dist/runtime/reconcile-state.js +1 -4
- package/dist/runtime/reconcile-state.js.map +1 -1
- package/dist/runtime/risk-guard.d.ts +20 -1
- package/dist/runtime/risk-guard.d.ts.map +1 -1
- package/dist/runtime/risk-guard.js +47 -6
- package/dist/runtime/risk-guard.js.map +1 -1
- package/dist/runtime/run.d.ts +7 -1
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +7 -0
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/runtime-schema.js +3 -3
- package/dist/runtime/runtime-schema.js.map +1 -1
- package/dist/runtime/runtime.d.ts +22 -2
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +74 -39
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/strategies-store.d.ts +26 -0
- package/dist/runtime/strategies-store.d.ts.map +1 -0
- package/dist/runtime/strategies-store.js +47 -0
- package/dist/runtime/strategies-store.js.map +1 -0
- package/dist/runtime/strategies-telemetry.d.ts +91 -0
- package/dist/runtime/strategies-telemetry.d.ts.map +1 -0
- package/dist/runtime/strategies-telemetry.js +128 -0
- package/dist/runtime/strategies-telemetry.js.map +1 -0
- package/dist/runtime/strategy-lifecycle.d.ts +108 -2
- package/dist/runtime/strategy-lifecycle.d.ts.map +1 -1
- package/dist/runtime/strategy-lifecycle.js +325 -4
- package/dist/runtime/strategy-lifecycle.js.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.d.ts +0 -13
- package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.js +0 -1
- package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
- package/dist/runtime/validate-dsl-position-tracking.d.ts.map +1 -1
- package/dist/runtime/validate-dsl-position-tracking.js +1 -2
- package/dist/runtime/validate-dsl-position-tracking.js.map +1 -1
- package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
- package/dist/runtime-api/handlers/ingest-signals.js +0 -1
- package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
- package/dist/runtime-api/handlers/types.d.ts +0 -5
- package/dist/runtime-api/handlers/types.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-events.d.ts +1 -1
- package/dist/runtime-api/intake/intake-events.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-events.js +3 -9
- package/dist/runtime-api/intake/intake-events.js.map +1 -1
- package/dist/runtime-api/intake/intake-mount.d.ts +2 -0
- package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-mount.js +25 -7
- package/dist/runtime-api/intake/intake-mount.js.map +1 -1
- package/dist/runtime-api/intake/process-error-post.d.ts +1 -6
- package/dist/runtime-api/intake/process-error-post.d.ts.map +1 -1
- package/dist/runtime-api/intake/process-error-post.js +1 -5
- package/dist/runtime-api/intake/process-error-post.js.map +1 -1
- package/dist/runtime-api/intake/process-signal-post.d.ts +1 -7
- package/dist/runtime-api/intake/process-signal-post.d.ts.map +1 -1
- package/dist/runtime-api/intake/process-signal-post.js +0 -5
- package/dist/runtime-api/intake/process-signal-post.js.map +1 -1
- package/dist/runtime-api/intake/scaffold-error-router.js.map +1 -1
- package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
- package/dist/runtime-api/intake/scanner-launcher.js +0 -15
- package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
- package/dist/runtime-api/intake/types.d.ts +0 -55
- package/dist/runtime-api/intake/types.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.d.ts +0 -2
- package/dist/runtime-api/routes/signals.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.js +11 -5
- package/dist/runtime-api/routes/signals.js.map +1 -1
- package/dist/runtime-api/routes/signals.schema.d.ts +0 -43
- package/dist/runtime-api/routes/signals.schema.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.schema.js +0 -43
- package/dist/runtime-api/routes/signals.schema.js.map +1 -1
- package/dist/runtime-api/server.d.ts +1 -7
- package/dist/runtime-api/server.d.ts.map +1 -1
- package/dist/runtime-api/server.js +0 -1
- package/dist/runtime-api/server.js.map +1 -1
- package/dist/scanners/engine/engine.d.ts +1 -22
- package/dist/scanners/engine/engine.d.ts.map +1 -1
- package/dist/scanners/engine/engine.js +11 -78
- package/dist/scanners/engine/engine.js.map +1 -1
- package/dist/scanners/engine/lifecycle.d.ts +1 -7
- package/dist/scanners/engine/lifecycle.d.ts.map +1 -1
- package/dist/scanners/events.d.ts +0 -4
- package/dist/scanners/events.d.ts.map +1 -1
- package/dist/scanners/events.js +0 -2
- package/dist/scanners/events.js.map +1 -1
- package/dist/scanners/external/scaffold/delivery.py +25 -194
- package/dist/scanners/external/scaffold/scaffold.py +31 -172
- package/dist/scanners/external/scaffold/senpi_mcp.py +8 -150
- package/dist/scanners/external-scanner-receiver.d.ts +0 -17
- package/dist/scanners/external-scanner-receiver.d.ts.map +1 -1
- package/dist/scanners/external-scanner-receiver.js +0 -8
- package/dist/scanners/external-scanner-receiver.js.map +1 -1
- package/dist/scanners/runtime-module.d.ts +0 -1
- package/dist/scanners/runtime-module.d.ts.map +1 -1
- package/dist/scanners/runtime-module.js +9 -66
- package/dist/scanners/runtime-module.js.map +1 -1
- package/dist/state/state-manager.d.ts +2 -0
- package/dist/state/state-manager.d.ts.map +1 -1
- package/dist/state/state-manager.js +8 -0
- package/dist/state/state-manager.js.map +1 -1
- package/dist/telemetry/attributes.d.ts +0 -20
- package/dist/telemetry/attributes.d.ts.map +1 -1
- package/dist/telemetry/attributes.js +0 -24
- package/dist/telemetry/attributes.js.map +1 -1
- package/dist/telemetry/event-builders.d.ts +6 -81
- package/dist/telemetry/event-builders.d.ts.map +1 -1
- package/dist/telemetry/event-builders.js +17 -141
- package/dist/telemetry/event-builders.js.map +1 -1
- package/dist/telemetry/event-store.d.ts.map +1 -1
- package/dist/telemetry/event-store.js +2 -6
- package/dist/telemetry/event-store.js.map +1 -1
- package/dist/telemetry/metrics.d.ts +2 -14
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +4 -19
- package/dist/telemetry/metrics.js.map +1 -1
- package/dist/telemetry/spans/redact-pii.d.ts.map +1 -1
- package/dist/telemetry/spans/redact-pii.js +11 -16
- package/dist/telemetry/spans/redact-pii.js.map +1 -1
- package/dist/telemetry/tracing.d.ts +0 -9
- package/dist/telemetry/tracing.d.ts.map +1 -1
- package/dist/telemetry/tracing.js +0 -36
- package/dist/telemetry/tracing.js.map +1 -1
- package/dist/types/dsl/index.d.ts +0 -48
- package/dist/types/dsl/index.d.ts.map +1 -1
- package/dist/types/scanner.d.ts +0 -15
- package/dist/types/scanner.d.ts.map +1 -1
- package/dist/utils/event-catalog.d.ts +0 -202
- package/dist/utils/event-catalog.d.ts.map +1 -1
- package/dist/utils/event-catalog.js +0 -26
- package/dist/utils/event-catalog.js.map +1 -1
- package/dist/utils/logger.d.ts +1 -4
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/validate/recipe-checks.d.ts.map +1 -1
- package/dist/validate/recipe-checks.js +2 -2
- package/dist/validate/recipe-checks.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/actions/skipped-signal-results.d.ts +0 -25
- package/dist/actions/skipped-signal-results.d.ts.map +0 -1
- package/dist/actions/skipped-signal-results.js +0 -71
- package/dist/actions/skipped-signal-results.js.map +0 -1
- package/dist/runtime-api/intake/ingest-request.d.ts +0 -17
- package/dist/runtime-api/intake/ingest-request.d.ts.map +0 -1
- package/dist/runtime-api/intake/ingest-request.js +0 -34
- package/dist/runtime-api/intake/ingest-request.js.map +0 -1
- package/dist/runtime-api/intake/scaffold-tick-router.d.ts +0 -39
- package/dist/runtime-api/intake/scaffold-tick-router.d.ts.map +0 -1
- package/dist/runtime-api/intake/scaffold-tick-router.js +0 -122
- package/dist/runtime-api/intake/scaffold-tick-router.js.map +0 -1
- package/dist/scanners/external/scaffold/tick_facts.py +0 -130
- package/dist/scanners/external/scaffold/ulid.py +0 -58
- package/dist/telemetry/dsl-positions.d.ts +0 -111
- package/dist/telemetry/dsl-positions.d.ts.map +0 -1
- package/dist/telemetry/dsl-positions.js +0 -118
- package/dist/telemetry/dsl-positions.js.map +0 -1
- package/dist/telemetry/live-telemetry.d.ts +0 -257
- package/dist/telemetry/live-telemetry.d.ts.map +0 -1
- package/dist/telemetry/live-telemetry.js +0 -959
- package/dist/telemetry/live-telemetry.js.map +0 -1
- package/dist/telemetry/scanner-stats.d.ts +0 -143
- package/dist/telemetry/scanner-stats.d.ts.map +0 -1
- package/dist/telemetry/scanner-stats.js +0 -426
- package/dist/telemetry/scanner-stats.js.map +0 -1
- package/dist/telemetry/scanner-tick-feed.d.ts +0 -87
- package/dist/telemetry/scanner-tick-feed.d.ts.map +0 -1
- package/dist/telemetry/scanner-tick-feed.js +0 -48
- package/dist/telemetry/scanner-tick-feed.js.map +0 -1
- package/dist/telemetry/strategy-topology.d.ts +0 -99
- package/dist/telemetry/strategy-topology.d.ts.map +0 -1
- package/dist/telemetry/strategy-topology.js +0 -93
- package/dist/telemetry/strategy-topology.js.map +0 -1
- package/dist/telemetry/telemetry-publisher.d.ts +0 -83
- package/dist/telemetry/telemetry-publisher.d.ts.map +0 -1
- package/dist/telemetry/telemetry-publisher.js +0 -256
- package/dist/telemetry/telemetry-publisher.js.map +0 -1
- package/dist/telemetry/tick-frame.d.ts +0 -212
- package/dist/telemetry/tick-frame.d.ts.map +0 -1
- package/dist/telemetry/tick-frame.js +0 -421
- package/dist/telemetry/tick-frame.js.map +0 -1
- package/dist/telemetry/timeline-buffer.d.ts +0 -95
- package/dist/telemetry/timeline-buffer.d.ts.map +0 -1
- package/dist/telemetry/timeline-buffer.js +0 -237
- package/dist/telemetry/timeline-buffer.js.map +0 -1
- package/dist/utils/telemetry-required-log.d.ts +0 -60
- package/dist/utils/telemetry-required-log.d.ts.map +0 -1
- package/dist/utils/telemetry-required-log.js +0 -105
- package/dist/utils/telemetry-required-log.js.map +0 -1
- package/dist/utils/ulid.d.ts +0 -9
- package/dist/utils/ulid.d.ts.map +0 -1
- package/dist/utils/ulid.js +0 -43
- package/dist/utils/ulid.js.map +0 -1
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
* Pure builders for the DSL exit-engine's failure-state events — degradation the monitor tracks but
|
|
3
3
|
* that previously surfaced only as a tick-span error or a `warn` log: an exchange SL push that threw
|
|
4
4
|
* ({@link buildSlSyncFailedEvent}), a position with no price past the stale threshold
|
|
5
|
-
* ({@link buildPriceFetchStaleEvent}), a Phase-2 backend handoff that degraded
|
|
6
|
-
* ({@link buildHandoffFailedEvent})
|
|
7
|
-
* reached a single position ({@link buildTickAbortedEvent}) and the tick that ends the outage
|
|
8
|
-
* ({@link buildTickRecoveredEvent}).
|
|
5
|
+
* ({@link buildPriceFetchStaleEvent}), and a Phase-2 backend handoff that degraded
|
|
6
|
+
* ({@link buildHandoffFailedEvent}).
|
|
9
7
|
*
|
|
10
8
|
* Pure `(input) => EmittableEvent`; the monitor emits the result through `emitEvent`'s guard, so a
|
|
11
9
|
* throwing build or a telemetry failure can never disturb a tick. A thrown venue/backend error rides
|
|
@@ -15,13 +13,12 @@
|
|
|
15
13
|
* events, so it is not passed here.
|
|
16
14
|
*/
|
|
17
15
|
import { EVENT_NAME, eventAttrs } from "../../utils/event-catalog.js";
|
|
18
|
-
/** The id/asset/dex
|
|
16
|
+
/** The id/asset/dex attributes every DSL failure event carries. */
|
|
19
17
|
function baseAttrs(ctx) {
|
|
20
18
|
return {
|
|
21
19
|
"senpi.asset": ctx.asset,
|
|
22
20
|
"senpi.dex": ctx.dex || undefined,
|
|
23
21
|
"senpi.position.id": ctx.positionId,
|
|
24
|
-
"senpi.tick.id": ctx.tickId,
|
|
25
22
|
};
|
|
26
23
|
}
|
|
27
24
|
/** An exchange SL create/update (`editPosition`) threw — the stop the venue never received. */
|
|
@@ -73,50 +70,4 @@ export function buildHandoffFailedEvent(ctx, opts) {
|
|
|
73
70
|
...(opts.error !== undefined ? { error: opts.error } : {}),
|
|
74
71
|
};
|
|
75
72
|
}
|
|
76
|
-
/** How the body phrases the age, including the case where there is no age to phrase. */
|
|
77
|
-
function ageText(ageMs) {
|
|
78
|
-
return ageMs == null ? "no tick has completed yet" : `last completed tick ${ageMs}ms ago`;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* A monitor tick that aborted at the clearinghouse gate — no position under this monitor was
|
|
82
|
-
* evaluated, so no breach, no time cut and no stop sync ran for any of them. The per-position
|
|
83
|
-
* staleness counters live past this gate and never advance, which is why this event has to exist:
|
|
84
|
-
* `dsl.price_fetch_stale` cannot fire for this failure mode.
|
|
85
|
-
*
|
|
86
|
-
* The caller latches it, so a sustained outage costs one record rather than one per tick.
|
|
87
|
-
*/
|
|
88
|
-
export function buildTickAbortedEvent(input) {
|
|
89
|
-
return {
|
|
90
|
-
name: EVENT_NAME.dslTickAborted,
|
|
91
|
-
level: "warn",
|
|
92
|
-
body: `DSL monitor tick aborted (${input.reason}); ` +
|
|
93
|
-
`${input.unevaluatedPositionCount} position(s) unevaluated, ${ageText(input.lastCompletedTickAgeMs)}`,
|
|
94
|
-
attributes: eventAttrs({
|
|
95
|
-
"senpi.tick.id": input.tickId,
|
|
96
|
-
"senpi.dsl.tick_abort_reason": input.reason,
|
|
97
|
-
"senpi.dsl.last_completed_tick_age_ms": input.lastCompletedTickAgeMs,
|
|
98
|
-
"senpi.dsl.unavailable_address_count": input.unavailableAddressCount,
|
|
99
|
-
"senpi.dsl.unevaluated_position_count": input.unevaluatedPositionCount,
|
|
100
|
-
}),
|
|
101
|
-
...(input.error !== undefined ? { error: input.error } : {}),
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* The tick that ends an abort outage. Emitted once, on the first tick to complete after one or more
|
|
106
|
-
* aborted ones, so a reader told "not protected" is told when that stops being true. Its age field
|
|
107
|
-
* is measured before the recovering tick stamps its own completion, so it is the length of the gap.
|
|
108
|
-
*/
|
|
109
|
-
export function buildTickRecoveredEvent(input) {
|
|
110
|
-
return {
|
|
111
|
-
name: EVENT_NAME.dslTickRecovered,
|
|
112
|
-
level: "info",
|
|
113
|
-
body: `DSL monitor tick completed after ${input.abortedTickCount} aborted tick(s); ` +
|
|
114
|
-
ageText(input.lastCompletedTickAgeMs),
|
|
115
|
-
attributes: eventAttrs({
|
|
116
|
-
"senpi.tick.id": input.tickId,
|
|
117
|
-
"senpi.dsl.last_completed_tick_age_ms": input.lastCompletedTickAgeMs,
|
|
118
|
-
"senpi.dsl.aborted_tick_count": input.abortedTickCount,
|
|
119
|
-
}),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
73
|
//# sourceMappingURL=dsl-failure-events.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dsl-failure-events.js","sourceRoot":"","sources":["../../../src/dsl/monitor/dsl-failure-events.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dsl-failure-events.js","sourceRoot":"","sources":["../../../src/dsl/monitor/dsl-failure-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAiB,MAAM,8BAA8B,CAAC;AAerF,mEAAmE;AACnE,SAAS,SAAS,CAAC,GAAsB;IAKvC,OAAO;QACL,aAAa,EAAE,GAAG,CAAC,KAAK;QACxB,WAAW,EAAE,GAAG,CAAC,GAAG,IAAI,SAAS;QACjC,mBAAmB,EAAE,GAAG,CAAC,UAAU;KACpC,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,sBAAsB,CACpC,GAAsB,EACtB,YAAoB,EACpB,KAAc;IAEd,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,eAAe;QAChC,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,+BAA+B,GAAG,CAAC,KAAK,KAAK,YAAY,eAAe;QAC9E,UAAU,EAAE,UAAU,CAA8C;YAClE,GAAG,SAAS,CAAC,GAAG,CAAC;YACjB,iCAAiC,EAAE,YAAY;SAChD,CAAC;QACF,KAAK;KACN,CAAC;AACJ,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,yBAAyB,CACvC,GAAsB,EACtB,YAAoB;IAEpB,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,kBAAkB;QACnC,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,sCAAsC,GAAG,CAAC,KAAK,KAAK,YAAY,eAAe;QACrF,UAAU,EAAE,UAAU,CAAiD;YACrE,GAAG,SAAS,CAAC,GAAG,CAAC;YACjB,+BAA+B,EAAE,YAAY;SAC9C,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAsB,EACtB,IAKC;IAED,MAAM,IAAI,GACR,IAAI,CAAC,OAAO,KAAK,UAAU;QACzB,CAAC,CAAC,4BAA4B,GAAG,CAAC,KAAK,8BAA8B;QACrE,CAAC,CAAC,IAAI,CAAC,OAAO,KAAK,MAAM;YACvB,CAAC,CAAC,mCAAmC,GAAG,CAAC,KAAK,EAAE;YAChD,CAAC,CAAC,2CAA2C,GAAG,CAAC,KAAK,oBAAoB,CAAC;IACjF,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,gBAAgB;QACjC,KAAK,EAAE,MAAM;QACb,IAAI;QACJ,UAAU,EAAE,UAAU,CAA+C;YACnE,GAAG,SAAS,CAAC,GAAG,CAAC;YACjB,2BAA2B,EAAE,IAAI,CAAC,OAAO;YACzC,+BAA+B,EAAE,IAAI,CAAC,iBAAiB;SACxD,CAAC;QACF,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3D,CAAC;AACJ,CAAC"}
|
|
@@ -17,7 +17,6 @@ export declare function closeReasonContext(closeReason: string, s: DslState): {
|
|
|
17
17
|
closePeakRoe?: number;
|
|
18
18
|
};
|
|
19
19
|
export interface MonitorProcessContext {
|
|
20
|
-
/** The id for this tick — a ULID, one per `runTick`, on its log lines, events and spans. */
|
|
21
20
|
tickId: string;
|
|
22
21
|
ts: string;
|
|
23
22
|
now: () => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-one-position.d.ts","sourceRoot":"","sources":["../../../src/dsl/monitor/process-one-position.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"process-one-position.d.ts","sourceRoot":"","sources":["../../../src/dsl/monitor/process-one-position.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAqBzD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAgB/C;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,CAAC,EAAE,QAAQ,GACV;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAYvD;AAkFD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,GAAG,EAAE,aAAa,CAAC;IACnB,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,sBAAsB,EAAE,cAAc,CAAC;IAC/D,oDAAoD;IACpD,wBAAwB,CAAC,EAAE,OAAO,sBAAsB,EAAE,wBAAwB,CAAC;CACpF;AAiCD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,qBAAqB,EAC1B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAozBf"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Per-position work for one monitor tick: SL fill check, pending close, dslTick, exchange sync.
|
|
3
3
|
*/
|
|
4
4
|
import { dslTick, exchangeStopLossTriggerPx } from "../engine/index.js";
|
|
5
|
-
import { CLOSE_REASON, MAX_CONSECUTIVE_CLOSE_FAILURES, DSL_EXTERNAL_RETRIES, DSL_EXTERNAL_RETRY_DELAY_MS, DSL_HANDOFF_SL_STATUS_TIMEOUT_MS, DSL_EVENTS, EVENTS, DSL_PRICE_FETCH_FAILURE_WARN_THRESHOLD, DSL_HEARTBEAT_FIRST_HOUR_MS, DSL_HEARTBEAT_INTERVAL_MS, DSL_HEARTBEAT_TIER_SUPPRESS_MS,
|
|
5
|
+
import { CLOSE_REASON, MAX_CONSECUTIVE_CLOSE_FAILURES, DSL_EXTERNAL_RETRIES, DSL_EXTERNAL_RETRY_DELAY_MS, DSL_HANDOFF_SL_STATUS_TIMEOUT_MS, DSL_EVENTS, EVENTS, DSL_PRICE_FETCH_FAILURE_WARN_THRESHOLD, DSL_HEARTBEAT_FIRST_HOUR_MS, DSL_HEARTBEAT_INTERVAL_MS, DSL_HEARTBEAT_TIER_SUPPRESS_MS, } from "../../constants/dsl/index.js";
|
|
6
6
|
import { logger } from "../../utils/logger/dsl.js";
|
|
7
7
|
import { withRetry } from "../../utils/retry.js";
|
|
8
8
|
import { isRetriableMcpError } from "../../senpi/client.js";
|
|
@@ -16,7 +16,6 @@ import { mapBackendEventToBusEvent } from "../engine-client/event-mapping.js";
|
|
|
16
16
|
import { buildDslClosedEventFromState } from "../events/build-closed-event.js";
|
|
17
17
|
import { parseCloseFillDetail } from "../../senpi/client.js";
|
|
18
18
|
import { emitEvent } from "../../actions/event-derivation.js";
|
|
19
|
-
import { PAYLOAD_KEY } from "../../utils/event-catalog.js";
|
|
20
19
|
import { buildSlSyncFailedEvent, buildPriceFetchStaleEvent, buildHandoffFailedEvent, } from "./dsl-failure-events.js";
|
|
21
20
|
/**
|
|
22
21
|
* Structured close-reason context forwarded with a DSL-cut close (notifications 2.3):
|
|
@@ -78,35 +77,23 @@ async function setIfStillActive(stateManager, key, address, asset, buildNext, ct
|
|
|
78
77
|
});
|
|
79
78
|
}
|
|
80
79
|
/**
|
|
81
|
-
* Telemetry stamps
|
|
82
|
-
*
|
|
83
|
-
*
|
|
80
|
+
* Telemetry stamps for a flat POSITION_CLOSED emit: the cross-flow position.id plus the
|
|
81
|
+
* closing-order fill detail when a close response (or SL order id) is available. Data-only —
|
|
82
|
+
* no consumer branches on these; the venue `orderId` is the fills-ledger join key for
|
|
83
|
+
* post-hoc lifecycle correlation.
|
|
84
84
|
*/
|
|
85
|
-
function
|
|
85
|
+
function closedEmitStamps(state, fill = {}) {
|
|
86
86
|
return {
|
|
87
87
|
__positionId: state.positionId ?? state.correlationId,
|
|
88
|
-
[PAYLOAD_KEY.tickId]: tickId,
|
|
89
|
-
...(source !== undefined ? { __dslSource: source } : {}),
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Stamps for a flat POSITION_CLOSED emit: {@link emitStamps} plus the closing-order fill detail when
|
|
94
|
-
* a close response (or SL order id) is available. The venue `orderId` is the fills-ledger join key
|
|
95
|
-
* for post-hoc lifecycle correlation. No source: the lifecycle record has no field for it.
|
|
96
|
-
*/
|
|
97
|
-
function closedEmitStamps(state, tickId, fill = {}) {
|
|
98
|
-
return {
|
|
99
|
-
...emitStamps(state, tickId),
|
|
100
88
|
...fill,
|
|
101
89
|
};
|
|
102
90
|
}
|
|
103
91
|
/** The position identity a DSL failure event pins onto its record. */
|
|
104
|
-
function failureContext(state
|
|
92
|
+
function failureContext(state) {
|
|
105
93
|
return {
|
|
106
94
|
asset: state.asset,
|
|
107
95
|
dex: state.dex || undefined,
|
|
108
96
|
positionId: state.positionId ?? state.correlationId,
|
|
109
|
-
tickId,
|
|
110
97
|
};
|
|
111
98
|
}
|
|
112
99
|
/**
|
|
@@ -139,40 +126,6 @@ function isGoneVenueSlStatus(status) {
|
|
|
139
126
|
const s = status.trim().toLowerCase();
|
|
140
127
|
return GONE_VENUE_SL_STATUSES.has(s) || s.endsWith("rejected");
|
|
141
128
|
}
|
|
142
|
-
/**
|
|
143
|
-
* The venue's answer about the stop order, in the shape the state persists it.
|
|
144
|
-
*
|
|
145
|
-
* `undefined` whenever the venue named no status — a failed lookup, an unreadable response, or a
|
|
146
|
-
* response with no status field. Those are not answers, so they write nothing: an absent
|
|
147
|
-
* `lastVenueStatus` has to keep meaning "the venue has not told us", which is precisely what a
|
|
148
|
-
* reader needs to tell a confirmed stop from a believed one. Nothing is defaulted or synthesised
|
|
149
|
-
* for them.
|
|
150
|
-
*
|
|
151
|
-
* The status is stored verbatim (only length-capped), because the branching above already ignores
|
|
152
|
-
* everything outside the known-dead set — the unrecognised answer is the one worth keeping.
|
|
153
|
-
*/
|
|
154
|
-
function venueStatusStamp(status) {
|
|
155
|
-
const raw = status?.status;
|
|
156
|
-
if (raw == null)
|
|
157
|
-
return undefined;
|
|
158
|
-
return { lastVenueStatus: raw.slice(0, DSL_VENUE_STATUS_MAX_LENGTH), lastVenueStatusAt: Date.now() };
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* The venue's answer about the BACKEND ratchet's `activeSLOrderId`, in the shape the state persists.
|
|
162
|
-
*
|
|
163
|
-
* A different order from the position's own `slOrderId`, so a different pair of fields: the handoff
|
|
164
|
-
* asks about the stop the backend placed, and recording that answer as the position's own would
|
|
165
|
-
* claim a confirmation the venue never gave for it.
|
|
166
|
-
*/
|
|
167
|
-
function backendSlStatusStamp(status) {
|
|
168
|
-
const raw = status?.status;
|
|
169
|
-
if (raw == null)
|
|
170
|
-
return undefined;
|
|
171
|
-
return {
|
|
172
|
-
backendSlVenueStatus: raw.slice(0, DSL_VENUE_STATUS_MAX_LENGTH),
|
|
173
|
-
backendSlVenueStatusAt: Date.now(),
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
129
|
export async function processOnePosition(ctx, key, state, price, span) {
|
|
177
130
|
const { tickId, ts, now: isoNow, stateManager, strategyStates, bus, isSlPlacementInProgress = () => false } = ctx;
|
|
178
131
|
const strategy = strategyStates.get(state.address);
|
|
@@ -208,9 +161,6 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
208
161
|
label: "getOrderStatus (SL-hit check)",
|
|
209
162
|
onRetry: (attempt, remaining, err) => logger.warn("[dsl] getOrderStatus retry", { asset: state.asset, attempt, remaining, err }),
|
|
210
163
|
}));
|
|
211
|
-
// The venue has answered. Every branch below keeps that answer; only a throw or a
|
|
212
|
-
// status-less response leaves the fields absent.
|
|
213
|
-
const venueStamp = venueStatusStamp(status);
|
|
214
164
|
if (status?.filled) {
|
|
215
165
|
span.addEvent("external_close_detected", { source: "sl_order" });
|
|
216
166
|
span.setAttribute("senpi.dsl.close_reason", CLOSE_REASON.EXCHANGE_SL_HIT);
|
|
@@ -220,13 +170,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
220
170
|
slOrderId: state.slOrderId,
|
|
221
171
|
address: state.address,
|
|
222
172
|
});
|
|
223
|
-
const closedState = {
|
|
224
|
-
...state,
|
|
225
|
-
...venueStamp,
|
|
226
|
-
active: false,
|
|
227
|
-
closedAt: ts,
|
|
228
|
-
closeReason: CLOSE_REASON.EXCHANGE_SL_HIT,
|
|
229
|
-
};
|
|
173
|
+
const closedState = { ...state, active: false, closedAt: ts, closeReason: CLOSE_REASON.EXCHANGE_SL_HIT };
|
|
230
174
|
const archived = await archiveIfStillActive(stateManager, key, state.address, state.asset, effectiveDex(state), () => closedState, { tickId, skipMessage: "[dsl] skip SL-hit archive: state already cleared (concurrent close)" });
|
|
231
175
|
if (!archived)
|
|
232
176
|
return;
|
|
@@ -237,15 +181,13 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
237
181
|
reason: CLOSE_REASON.EXCHANGE_SL_HIT,
|
|
238
182
|
closeReason: CLOSE_REASON.EXCHANGE_SL_HIT,
|
|
239
183
|
// The closing order IS the exchange SL order — its id joins the closing fill.
|
|
240
|
-
...closedEmitStamps(state,
|
|
184
|
+
...closedEmitStamps(state, state.slOrderId != null ? { orderId: String(state.slOrderId) } : {}),
|
|
241
185
|
timestamp: ts,
|
|
242
186
|
});
|
|
243
187
|
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(closedState, {
|
|
244
188
|
reason: CLOSE_REASON.EXCHANGE_SL_HIT,
|
|
245
189
|
closeReason: CLOSE_REASON.EXCHANGE_SL_HIT,
|
|
246
190
|
timestamp: ts,
|
|
247
|
-
tickId,
|
|
248
|
-
source: "runtime",
|
|
249
191
|
}));
|
|
250
192
|
return;
|
|
251
193
|
}
|
|
@@ -266,17 +208,8 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
266
208
|
phase: state.phase,
|
|
267
209
|
});
|
|
268
210
|
recordDslDegradation({ failure: "sl_gone" });
|
|
269
|
-
await setIfStillActive(stateManager, key, state.address, state.asset, (current) => ({ ...current,
|
|
270
|
-
state = { ...state,
|
|
271
|
-
}
|
|
272
|
-
else if (venueStamp) {
|
|
273
|
-
// The stop is neither filled nor in the known-dead set, so the branching above changes
|
|
274
|
-
// nothing — but the answer itself is the evidence, and it is the only thing that separates
|
|
275
|
-
// "a stop IS resting" from "we believe one is". Persisted here rather than left to the
|
|
276
|
-
// tick's own write, because the paths that return before it (no price, pendingClose error)
|
|
277
|
-
// merge onto the re-read state and would drop a stamp held only in this local copy.
|
|
278
|
-
await setIfStillActive(stateManager, key, state.address, state.asset, (current) => ({ ...current, ...venueStamp }), { tickId, skipMessage: "[dsl] skip venue-status stamp: state already cleared (concurrent close)" });
|
|
279
|
-
state = { ...state, ...venueStamp };
|
|
211
|
+
await setIfStillActive(stateManager, key, state.address, state.asset, (current) => ({ ...current, slOrderId: null, lastSyncedFloorPrice: null }), { tickId, skipMessage: "[dsl] skip gone-SL clear: state already cleared (concurrent close)" });
|
|
212
|
+
state = { ...state, slOrderId: null, lastSyncedFloorPrice: null };
|
|
280
213
|
}
|
|
281
214
|
}
|
|
282
215
|
catch (e) {
|
|
@@ -329,15 +262,13 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
329
262
|
wallet: state.wallet,
|
|
330
263
|
reason: closeReasonToUse,
|
|
331
264
|
closeReason: closeReasonToUse,
|
|
332
|
-
...closedEmitStamps(state,
|
|
265
|
+
...closedEmitStamps(state, closeFill),
|
|
333
266
|
timestamp: ts,
|
|
334
267
|
});
|
|
335
268
|
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(closedState, {
|
|
336
269
|
reason: closeReasonToUse,
|
|
337
270
|
closeReason: closeReasonToUse,
|
|
338
271
|
timestamp: ts,
|
|
339
|
-
tickId,
|
|
340
|
-
source: "runtime",
|
|
341
272
|
}));
|
|
342
273
|
}
|
|
343
274
|
catch (e) {
|
|
@@ -356,15 +287,13 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
356
287
|
wallet: state.wallet,
|
|
357
288
|
reason: "error",
|
|
358
289
|
closeReason: CLOSE_REASON.CLOSE_POSITION_FAILED,
|
|
359
|
-
...closedEmitStamps(state
|
|
290
|
+
...closedEmitStamps(state),
|
|
360
291
|
timestamp: isoNow(),
|
|
361
292
|
});
|
|
362
293
|
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(failedCloseState, {
|
|
363
294
|
reason: "error",
|
|
364
295
|
closeReason: CLOSE_REASON.CLOSE_POSITION_FAILED,
|
|
365
296
|
timestamp: isoNow(),
|
|
366
|
-
tickId,
|
|
367
|
-
source: "runtime",
|
|
368
297
|
}));
|
|
369
298
|
}
|
|
370
299
|
else {
|
|
@@ -374,7 +303,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
374
303
|
asset: state.asset,
|
|
375
304
|
attempt: next.consecutiveCloseFailures,
|
|
376
305
|
triggerReason: state.triggerReason,
|
|
377
|
-
|
|
306
|
+
__positionId: state.positionId ?? state.correlationId,
|
|
378
307
|
timestamp: isoNow(),
|
|
379
308
|
});
|
|
380
309
|
}
|
|
@@ -399,7 +328,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
399
328
|
...(shouldEmitStale ? { priceFetchStaleEmitted: true } : {}),
|
|
400
329
|
}), { tickId, skipMessage: "[dsl] skip no-price counter write: state already cleared (concurrent close)" });
|
|
401
330
|
if (shouldEmitStale) {
|
|
402
|
-
emitEvent(logger, () => buildPriceFetchStaleEvent(failureContext(state
|
|
331
|
+
emitEvent(logger, () => buildPriceFetchStaleEvent(failureContext(state), next.consecutiveFetchFailures));
|
|
403
332
|
recordDslDegradation({ failure: "price_stale" });
|
|
404
333
|
}
|
|
405
334
|
logger.info("[dsl] monitor no price for position, skipping dslTick", {
|
|
@@ -569,15 +498,13 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
569
498
|
wallet: state.wallet,
|
|
570
499
|
reason: "error",
|
|
571
500
|
closeReason: CLOSE_REASON.CLOSE_POSITION_FAILED,
|
|
572
|
-
...closedEmitStamps(state
|
|
501
|
+
...closedEmitStamps(state),
|
|
573
502
|
timestamp: isoNow(),
|
|
574
503
|
});
|
|
575
504
|
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(failedCloseState, {
|
|
576
505
|
reason: "error",
|
|
577
506
|
closeReason: CLOSE_REASON.CLOSE_POSITION_FAILED,
|
|
578
507
|
timestamp: isoNow(),
|
|
579
|
-
tickId,
|
|
580
|
-
source: "runtime",
|
|
581
508
|
}));
|
|
582
509
|
}
|
|
583
510
|
else {
|
|
@@ -587,7 +514,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
587
514
|
asset: state.asset,
|
|
588
515
|
attempt: pending.consecutiveCloseFailures,
|
|
589
516
|
triggerReason: state.triggerReason,
|
|
590
|
-
|
|
517
|
+
__positionId: state.positionId ?? state.correlationId,
|
|
591
518
|
timestamp: isoNow(),
|
|
592
519
|
});
|
|
593
520
|
}
|
|
@@ -609,15 +536,13 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
609
536
|
wallet: state.wallet,
|
|
610
537
|
reason: closeReasonToUse,
|
|
611
538
|
closeReason: closeReasonToUse,
|
|
612
|
-
...closedEmitStamps(state,
|
|
539
|
+
...closedEmitStamps(state, closeFill),
|
|
613
540
|
timestamp: ts,
|
|
614
541
|
});
|
|
615
542
|
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(closedState, {
|
|
616
543
|
reason: closeReasonToUse,
|
|
617
544
|
closeReason: closeReasonToUse,
|
|
618
545
|
timestamp: ts,
|
|
619
|
-
tickId,
|
|
620
|
-
source: "runtime",
|
|
621
546
|
}));
|
|
622
547
|
return;
|
|
623
548
|
}
|
|
@@ -637,7 +562,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
637
562
|
triggerPct: tier.triggerPct,
|
|
638
563
|
newFloorPrice: dslEffectiveFloor,
|
|
639
564
|
triggerReason: next.triggerReason,
|
|
640
|
-
|
|
565
|
+
__positionId: next.positionId ?? next.correlationId,
|
|
641
566
|
timestamp: ts,
|
|
642
567
|
direction: next.direction,
|
|
643
568
|
entryPrice: next.entryPrice,
|
|
@@ -667,7 +592,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
667
592
|
phase: result.newPhase ?? 2,
|
|
668
593
|
tierIndex: result.newTierIndex ?? 0,
|
|
669
594
|
triggerReason: next.triggerReason,
|
|
670
|
-
|
|
595
|
+
__positionId: next.positionId ?? next.correlationId,
|
|
671
596
|
timestamp: ts,
|
|
672
597
|
});
|
|
673
598
|
}
|
|
@@ -781,7 +706,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
781
706
|
newSLPrice: exchangeTriggerPx,
|
|
782
707
|
slOrderId: synced.slOrderId ?? 0,
|
|
783
708
|
triggerReason: synced.triggerReason,
|
|
784
|
-
|
|
709
|
+
__positionId: synced.positionId ?? synced.correlationId,
|
|
785
710
|
direction: synced.direction,
|
|
786
711
|
// Stop-raise copy inputs: position leverage, the high-water price behind
|
|
787
712
|
// the raise ("new high …"), and entry price (leverage + entry → the
|
|
@@ -807,7 +732,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
807
732
|
}, { tickId, skipMessage: "[dsl] skip SL sync-failure write: state no longer active (concurrent close)" });
|
|
808
733
|
if (syncFailureCount == null)
|
|
809
734
|
return;
|
|
810
|
-
emitEvent(logger, () => buildSlSyncFailedEvent(failureContext(state
|
|
735
|
+
emitEvent(logger, () => buildSlSyncFailedEvent(failureContext(state), syncFailureCount ?? 0, e));
|
|
811
736
|
recordDslDegradation({ failure: "sl_sync", errorType: errorTypeOf(e) });
|
|
812
737
|
}
|
|
813
738
|
}
|
|
@@ -840,7 +765,7 @@ export async function processOnePosition(ctx, key, state, price, span) {
|
|
|
840
765
|
currentTierIndex: hbState.currentTierIndex,
|
|
841
766
|
elapsedMinutes: hbState.elapsedMinutes,
|
|
842
767
|
triggerReason: hbState.triggerReason,
|
|
843
|
-
|
|
768
|
+
__positionId: hbState.positionId ?? hbState.correlationId,
|
|
844
769
|
margin: hbState.leverage > 0
|
|
845
770
|
? (hbState.size * hbState.entryPrice) / hbState.leverage
|
|
846
771
|
: 0,
|
|
@@ -883,17 +808,15 @@ const LIVE_VENUE_SL_STATUSES = new Set(["open", "triggered"]);
|
|
|
883
808
|
async function backendHasLiveSl(pos, strategy, tickId, asset) {
|
|
884
809
|
const slOrderId = parseSlOrderId(pos.activeSLOrderId);
|
|
885
810
|
if (slOrderId == null)
|
|
886
|
-
return
|
|
811
|
+
return false;
|
|
887
812
|
try {
|
|
888
813
|
const status = await withRetry(() => strategy.getOrderStatus(slOrderId, { timeoutMs: DSL_HANDOFF_SL_STATUS_TIMEOUT_MS }), {
|
|
889
814
|
retries: DSL_EXTERNAL_RETRIES,
|
|
890
815
|
delayMs: DSL_EXTERNAL_RETRY_DELAY_MS,
|
|
891
816
|
label: "getOrderStatus (handoff live-SL check)",
|
|
892
817
|
});
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
return { live: true, ...(stamp === undefined ? {} : { stamp }) };
|
|
896
|
-
}
|
|
818
|
+
if (status?.status != null && LIVE_VENUE_SL_STATUSES.has(status.status))
|
|
819
|
+
return true;
|
|
897
820
|
logger.warn("[dsl] backend ratchet names an exchange SL that is not resting on the venue", {
|
|
898
821
|
tickId,
|
|
899
822
|
asset,
|
|
@@ -901,7 +824,7 @@ async function backendHasLiveSl(pos, strategy, tickId, asset) {
|
|
|
901
824
|
slOrderId,
|
|
902
825
|
venueStatus: status?.status ?? null,
|
|
903
826
|
});
|
|
904
|
-
return
|
|
827
|
+
return false;
|
|
905
828
|
}
|
|
906
829
|
catch (e) {
|
|
907
830
|
logger.warn("[dsl] could not read the backend ratchet's exchange SL status; treating it as not live", {
|
|
@@ -911,36 +834,7 @@ async function backendHasLiveSl(pos, strategy, tickId, asset) {
|
|
|
911
834
|
slOrderId,
|
|
912
835
|
err: e,
|
|
913
836
|
});
|
|
914
|
-
return
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Persist the venue's answer about the backend's stop on a path that writes nothing else.
|
|
919
|
-
*
|
|
920
|
-
* That path is the one worth recording: the checks above leave the handoff at `"pending"` and
|
|
921
|
-
* return, so without this the reason it is stuck exists only in a warn line. A stamp held in a local
|
|
922
|
-
* copy would be dropped by the next write, which merges onto the re-read state, so it is written
|
|
923
|
-
* under the key lock like every other field.
|
|
924
|
-
*
|
|
925
|
-
* No stamp means the venue named no status — a failed lookup, or a ratchet with no order id to ask
|
|
926
|
-
* about. Nothing is written for those: an absent field has to keep meaning "the venue has not told
|
|
927
|
-
* us".
|
|
928
|
-
*/
|
|
929
|
-
async function recordBackendSlStatus(ctx, key, state, stamp) {
|
|
930
|
-
if (stamp === undefined)
|
|
931
|
-
return;
|
|
932
|
-
try {
|
|
933
|
-
await setIfStillActive(ctx.stateManager, key, state.address, state.asset, (current) => ({ ...current, ...stamp }), { tickId: ctx.tickId, skipMessage: "[dsl] skip backend SL status stamp: state already cleared (concurrent close)" });
|
|
934
|
-
Object.assign(state, stamp);
|
|
935
|
-
}
|
|
936
|
-
catch (e) {
|
|
937
|
-
// One caller sits inside the pre-check's try, whose catch falls through to sending an add. A
|
|
938
|
-
// failed write of an observability field must not be the reason a second ratchet is registered.
|
|
939
|
-
logger.warn("[dsl] could not persist the backend SL venue status", {
|
|
940
|
-
tickId: ctx.tickId,
|
|
941
|
-
asset: state.asset,
|
|
942
|
-
err: e,
|
|
943
|
-
});
|
|
837
|
+
return false;
|
|
944
838
|
}
|
|
945
839
|
}
|
|
946
840
|
async function attemptHandoffRegistration(ctx, key, next, _original, strategy) {
|
|
@@ -957,10 +851,8 @@ async function attemptHandoffRegistration(ctx, key, next, _original, strategy) {
|
|
|
957
851
|
const active = existing.filter((p) => !BACKEND_TERMINAL_STATUSES.has(p.status));
|
|
958
852
|
if (active.length > 0) {
|
|
959
853
|
const adopted = active[0];
|
|
960
|
-
|
|
961
|
-
if (!adoptedSlCheck.live) {
|
|
854
|
+
if (!(await backendHasLiveSl(adopted, strategy, tickId, next.asset))) {
|
|
962
855
|
logger.warn("[dsl] pre-check found backend ratchet without an exchange SL; staying pending so the runtime keeps managing the SL", { tickId, asset: next.asset, adoptedId: adopted.id, status: adopted.status });
|
|
963
|
-
await recordBackendSlStatus(ctx, key, next, adoptedSlCheck.stamp);
|
|
964
856
|
return;
|
|
965
857
|
}
|
|
966
858
|
logger.info("[dsl] pre-check found existing backend ratchet, adopting instead of re-adding", {
|
|
@@ -979,7 +871,6 @@ async function attemptHandoffRegistration(ctx, key, next, _original, strategy) {
|
|
|
979
871
|
const adoptedSl = mapEnginePositionToStateFields(adopted);
|
|
980
872
|
const updated = {
|
|
981
873
|
...next,
|
|
982
|
-
...adoptedSlCheck.stamp,
|
|
983
874
|
slOrderId: adoptedSl.slOrderId,
|
|
984
875
|
lastSyncedFloorPrice: adoptedSl.lastSyncedFloorPrice,
|
|
985
876
|
handoffStatus: "ok",
|
|
@@ -1031,8 +922,7 @@ async function attemptHandoffRegistration(ctx, key, next, _original, strategy) {
|
|
|
1031
922
|
Object.assign(next, aborted);
|
|
1032
923
|
return;
|
|
1033
924
|
}
|
|
1034
|
-
|
|
1035
|
-
if (!registeredSlCheck.live) {
|
|
925
|
+
if (!(await backendHasLiveSl(result.position, strategy, tickId, next.asset))) {
|
|
1036
926
|
logger.warn("[dsl] backend created the position but has not placed an exchange SL; staying pending so the runtime keeps managing the SL", {
|
|
1037
927
|
tickId,
|
|
1038
928
|
asset: next.asset,
|
|
@@ -1040,14 +930,12 @@ async function attemptHandoffRegistration(ctx, key, next, _original, strategy) {
|
|
|
1040
930
|
status: result.position.status,
|
|
1041
931
|
rawResult: JSON.stringify(result).slice(0, 1000),
|
|
1042
932
|
});
|
|
1043
|
-
await recordBackendSlStatus(ctx, key, next, registeredSlCheck.stamp);
|
|
1044
933
|
return;
|
|
1045
934
|
}
|
|
1046
935
|
// Mirror the backend's stop on the registration tick itself — see the adopt site above.
|
|
1047
936
|
const registeredSl = mapEnginePositionToStateFields(result.position);
|
|
1048
937
|
const updated = {
|
|
1049
938
|
...next,
|
|
1050
|
-
...registeredSlCheck.stamp,
|
|
1051
939
|
slOrderId: registeredSl.slOrderId,
|
|
1052
940
|
lastSyncedFloorPrice: registeredSl.lastSyncedFloorPrice,
|
|
1053
941
|
handoffStatus: "ok",
|
|
@@ -1064,7 +952,7 @@ async function attemptHandoffRegistration(ctx, key, next, _original, strategy) {
|
|
|
1064
952
|
});
|
|
1065
953
|
}
|
|
1066
954
|
else {
|
|
1067
|
-
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(next
|
|
955
|
+
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(next), {
|
|
1068
956
|
failure: "register",
|
|
1069
957
|
redact: { result: JSON.stringify(result).slice(0, 800) },
|
|
1070
958
|
}));
|
|
@@ -1072,7 +960,7 @@ async function attemptHandoffRegistration(ctx, key, next, _original, strategy) {
|
|
|
1072
960
|
}
|
|
1073
961
|
}
|
|
1074
962
|
catch (e) {
|
|
1075
|
-
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(next
|
|
963
|
+
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(next), { failure: "register", error: e }));
|
|
1076
964
|
recordDslDegradation({ failure: "handoff", handoffFailure: "register", errorType: errorTypeOf(e) });
|
|
1077
965
|
}
|
|
1078
966
|
}
|
|
@@ -1095,7 +983,7 @@ async function handleHandoffOk(ctx, key, next, original, strategy, span) {
|
|
|
1095
983
|
? positions.filter((p) => !BACKEND_TERMINAL_STATUSES.has(p.status))[0]
|
|
1096
984
|
: null;
|
|
1097
985
|
if (!backendPos) {
|
|
1098
|
-
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(original
|
|
986
|
+
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(original), {
|
|
1099
987
|
failure: "rollback",
|
|
1100
988
|
backendPositionId: next.backendDslPositionId ?? undefined,
|
|
1101
989
|
}));
|
|
@@ -1135,15 +1023,13 @@ async function handleHandoffOk(ctx, key, next, original, strategy, span) {
|
|
|
1135
1023
|
reason: closeReason,
|
|
1136
1024
|
closeReason,
|
|
1137
1025
|
// Backend-driven close: the closing order id is not locally verifiable — stamp ids only.
|
|
1138
|
-
...closedEmitStamps(updated
|
|
1026
|
+
...closedEmitStamps(updated),
|
|
1139
1027
|
timestamp: ts,
|
|
1140
1028
|
});
|
|
1141
1029
|
bus.emit(DSL_EVENTS.CLOSED, buildDslClosedEventFromState(closedState, {
|
|
1142
1030
|
reason: closeReason,
|
|
1143
1031
|
closeReason,
|
|
1144
1032
|
timestamp: ts,
|
|
1145
|
-
tickId,
|
|
1146
|
-
source: "backend",
|
|
1147
1033
|
}));
|
|
1148
1034
|
return;
|
|
1149
1035
|
}
|
|
@@ -1152,7 +1038,7 @@ async function handleHandoffOk(ctx, key, next, original, strategy, span) {
|
|
|
1152
1038
|
}
|
|
1153
1039
|
}
|
|
1154
1040
|
catch (e) {
|
|
1155
|
-
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(original
|
|
1041
|
+
emitEvent(logger, () => buildHandoffFailedEvent(failureContext(original), {
|
|
1156
1042
|
failure: "poll",
|
|
1157
1043
|
backendPositionId: next.backendDslPositionId ?? undefined,
|
|
1158
1044
|
error: e,
|
|
@@ -1222,20 +1108,11 @@ async function handleHandoffOk(ctx, key, next, original, strategy, span) {
|
|
|
1222
1108
|
const minClosed = mapped.eventName === DSL_EVENTS.CLOSED
|
|
1223
1109
|
? mapped.payload
|
|
1224
1110
|
: null;
|
|
1225
|
-
if (minClosed == null) {
|
|
1226
|
-
// Single-owner payload: mapBackendEventToBusEvent built this object for this emit and
|
|
1227
|
-
// nothing else holds a reference to it, so the tick stamp goes on in place — cloning
|
|
1228
|
-
// it once per backend event buys nothing.
|
|
1229
|
-
mapped.payload[PAYLOAD_KEY.tickId] = tickId;
|
|
1230
|
-
}
|
|
1231
1111
|
const emitPayload = minClosed != null
|
|
1232
1112
|
? buildDslClosedEventFromState(updated, {
|
|
1233
1113
|
reason: minClosed.reason,
|
|
1234
1114
|
closeReason: minClosed.closeReason,
|
|
1235
1115
|
timestamp: minClosed.timestamp,
|
|
1236
|
-
tickId,
|
|
1237
|
-
source: "backend",
|
|
1238
|
-
backendEventAt: minClosed.__backendEventAt,
|
|
1239
1116
|
})
|
|
1240
1117
|
: mapped.payload;
|
|
1241
1118
|
bus.emit(mapped.eventName, emitPayload);
|
|
@@ -1282,7 +1159,7 @@ async function handleHandoffOk(ctx, key, next, original, strategy, span) {
|
|
|
1282
1159
|
reason: closeReason,
|
|
1283
1160
|
closeReason,
|
|
1284
1161
|
// Backend-driven close: the closing order id is not locally verifiable — stamp ids only.
|
|
1285
|
-
...closedEmitStamps(updated
|
|
1162
|
+
...closedEmitStamps(updated),
|
|
1286
1163
|
timestamp: ts,
|
|
1287
1164
|
});
|
|
1288
1165
|
return;
|