@reefclaw/openclaw-plugin 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/bridge/gateway/event-parser.d.ts +6 -1
  2. package/bridge/gateway/event-parser.js +19 -2
  3. package/bridge/gateway/poller.d.ts +1 -0
  4. package/bridge/gateway/poller.js +14 -2
  5. package/bridge/providers/gateway.d.ts +22 -2
  6. package/bridge/providers/gateway.js +67 -9
  7. package/ccxt/binance-private.d.ts +21 -0
  8. package/ccxt/binance-private.js +132 -22
  9. package/ccxt/public-market-data-api.d.ts +14 -0
  10. package/ccxt/public-market-data-api.js +15 -1
  11. package/config/plugin-config-io.d.ts +13 -0
  12. package/config/plugin-config-io.js +15 -0
  13. package/config/tool-gate.js +3 -0
  14. package/exchange-adapter.d.ts +16 -0
  15. package/index.js +658 -83
  16. package/ingest/position-auto-capture.d.ts +68 -0
  17. package/ingest/position-auto-capture.js +321 -23
  18. package/ingest/position-decisions-client.d.ts +7 -2
  19. package/ingest/position-decisions-client.js +13 -3
  20. package/ingest/reconcile-db-vs-exchange.d.ts +39 -1
  21. package/ingest/reconcile-db-vs-exchange.js +66 -10
  22. package/lifecycle/trading-operation-lock.d.ts +17 -0
  23. package/lifecycle/trading-operation-lock.js +14 -0
  24. package/live/bracket-id.d.ts +2 -3
  25. package/live/bracket-id.js +22 -9
  26. package/live/fill-price.d.ts +13 -0
  27. package/live/fill-price.js +37 -0
  28. package/live/live-adapter.d.ts +57 -2
  29. package/live/live-adapter.js +290 -49
  30. package/live/local-signal-service.js +11 -6
  31. package/live/local-strategy-evaluator.js +4 -0
  32. package/live/position-state-store.d.ts +4 -0
  33. package/live/proposal-decision-listener.d.ts +6 -0
  34. package/live/proposal-decision-listener.js +4 -0
  35. package/live/stop-watcher.d.ts +35 -2
  36. package/live/stop-watcher.js +63 -3
  37. package/onboarding/runtime.d.ts +19 -0
  38. package/onboarding/runtime.js +34 -3
  39. package/openclaw.plugin.json +1 -0
  40. package/package.json +2 -2
  41. package/portfolio/reentry-tracker.d.ts +36 -0
  42. package/portfolio/reentry-tracker.js +127 -0
  43. package/portfolio/wave9-admission.d.ts +67 -0
  44. package/portfolio/wave9-admission.js +262 -0
  45. package/portfolio/wave9-policy.d.ts +36 -0
  46. package/portfolio/wave9-policy.js +183 -0
  47. package/signals/conditions/registry.js +61 -2
  48. package/signals/strategy-adapter.js +17 -7
  49. package/simulator/exchange-simulator.d.ts +12 -0
  50. package/simulator/exchange-simulator.js +78 -3
  51. package/simulator/fill-engine.js +5 -1
  52. package/simulator/types.d.ts +10 -1
  53. package/skills/reefclaw/SKILL.md +2 -0
  54. package/strategy/evaluator.d.ts +3 -0
  55. package/strategy/evaluator.js +5 -0
  56. package/tools/assessment-validation.d.ts +23 -0
  57. package/tools/assessment-validation.js +58 -0
  58. package/tools/attach-brackets.d.ts +7 -2
  59. package/tools/attach-brackets.js +201 -0
  60. package/tools/audit-bracket-protection.js +157 -1
  61. package/tools/bracket-control.d.ts +12 -0
  62. package/tools/bracket-control.js +35 -0
  63. package/tools/cancel-all-orders.d.ts +2 -0
  64. package/tools/cancel-all-orders.js +4 -1
  65. package/tools/cancel-order.d.ts +4 -0
  66. package/tools/cancel-order.js +49 -3
  67. package/tools/close-position.d.ts +23 -0
  68. package/tools/close-position.js +286 -13
  69. package/tools/create-order.d.ts +28 -0
  70. package/tools/create-order.js +1390 -190
  71. package/tools/get-analytics.js +2 -2
  72. package/tools/get-basis.js +2 -2
  73. package/tools/get-cascade-risk.js +2 -2
  74. package/tools/get-crypto-metrics.js +14 -4
  75. package/tools/get-cvd.js +2 -2
  76. package/tools/get-divergences.js +2 -2
  77. package/tools/get-funding-context.js +2 -2
  78. package/tools/get-liquidation-levels.js +2 -2
  79. package/tools/get-liquidation-pulse.js +2 -2
  80. package/tools/get-pattern-scan.js +2 -2
  81. package/tools/get-regime.js +2 -2
  82. package/tools/get-resting-liquidity.js +2 -2
  83. package/tools/get-risk-scenario.js +2 -2
  84. package/tools/get-session-review.js +2 -2
  85. package/tools/get-setup-detail.js +21 -2
  86. package/tools/get-signals.js +2 -2
  87. package/tools/get-sizing.js +2 -2
  88. package/tools/get-trade-feedback.js +2 -2
  89. package/tools/get-trade-flow.js +2 -2
  90. package/tools/get-volume-profile.js +2 -2
  91. package/tools/get-wave9-status.d.ts +127 -0
  92. package/tools/get-wave9-status.js +796 -0
  93. package/tools/intel-api.d.ts +20 -0
  94. package/tools/intel-api.js +67 -0
  95. package/tools/intel-cache.d.ts +1 -1
  96. package/tools/intel-cache.js +20 -5
  97. package/tools/list-strategies.d.ts +11 -1
  98. package/tools/list-strategies.js +17 -0
  99. package/tools/modify-stop.d.ts +4 -0
  100. package/tools/modify-stop.js +63 -24
  101. package/tools/modify-target.d.ts +4 -0
  102. package/tools/modify-target.js +62 -23
  103. package/tools/scan-pairs.d.ts +4 -0
  104. package/tools/scan-pairs.js +22 -8
  105. package/tools/toggle-strategy.js +7 -0
  106. package/types.d.ts +5 -0
  107. package/venues/hyperliquid/hl-balance.d.ts +116 -0
  108. package/venues/hyperliquid/hl-balance.js +145 -0
  109. package/venues/hyperliquid/hl-bracket-coordinator.d.ts +123 -0
  110. package/venues/hyperliquid/hl-bracket-coordinator.js +533 -0
  111. package/venues/hyperliquid/hl-brackets.d.ts +102 -0
  112. package/venues/hyperliquid/hl-brackets.js +172 -0
  113. package/venues/hyperliquid/hl-cloid.d.ts +22 -0
  114. package/venues/hyperliquid/hl-cloid.js +82 -0
  115. package/venues/hyperliquid/hl-info-cache.d.ts +46 -0
  116. package/venues/hyperliquid/hl-info-cache.js +125 -0
  117. package/venues/hyperliquid/hl-live-adapter.d.ts +146 -0
  118. package/venues/hyperliquid/hl-live-adapter.js +728 -0
  119. package/venues/hyperliquid/hl-precision.d.ts +61 -0
  120. package/venues/hyperliquid/hl-precision.js +176 -0
  121. package/venues/hyperliquid/hl-private.d.ts +88 -0
  122. package/venues/hyperliquid/hl-private.js +357 -0
  123. package/venues/hyperliquid/hl-public.d.ts +31 -4
  124. package/venues/hyperliquid/hl-public.js +163 -12
  125. package/venues/hyperliquid/hl-rate-gate.d.ts +57 -0
  126. package/venues/hyperliquid/hl-rate-gate.js +220 -0
  127. package/venues/hyperliquid/hl-user-stream.d.ts +90 -0
  128. package/venues/hyperliquid/hl-user-stream.js +220 -0
  129. package/venues/registry.d.ts +23 -9
  130. package/venues/registry.js +12 -13
  131. package/venues/symbols.d.ts +43 -0
  132. package/venues/symbols.js +107 -0
  133. package/wave9/live-account-capture.d.ts +67 -0
  134. package/wave9/live-account-capture.js +435 -0
  135. package/wave9/live-autonomous-protection.d.ts +39 -0
  136. package/wave9/live-autonomous-protection.js +112 -0
  137. package/wave9/live-durable-reconciliation-scheduler.d.ts +33 -0
  138. package/wave9/live-durable-reconciliation-scheduler.js +115 -0
  139. package/wave9/live-execution-ledger.d.ts +107 -0
  140. package/wave9/live-execution-ledger.js +498 -0
  141. package/wave9/live-position-confirmation.d.ts +18 -0
  142. package/wave9/live-position-confirmation.js +111 -0
  143. package/wave9/live-residual-protection.d.ts +18 -0
  144. package/wave9/live-residual-protection.js +250 -0
  145. package/wave9/live-startup-reconciliation.d.ts +38 -0
  146. package/wave9/live-startup-reconciliation.js +454 -0
  147. package/wave9/live-symbol-ownership.d.ts +20 -0
  148. package/wave9/live-symbol-ownership.js +132 -0
  149. package/wave9/paper-admission-guard.d.ts +199 -0
  150. package/wave9/paper-admission-guard.js +650 -0
  151. package/wave9/usdm-evidence-provider.d.ts +42 -0
  152. package/wave9/usdm-evidence-provider.js +133 -0
@@ -0,0 +1,39 @@
1
+ import type { IExchangeAdapter } from '../exchange-adapter.js';
2
+ import type { Wave9LiveExecutionLedger } from './live-execution-ledger.js';
3
+ import { type Wave9LiveFlatConfirmation, type Wave9LiveFlatConfirmationOptions } from './live-position-confirmation.js';
4
+ import { reprotectWave9LiveResidual, type Wave9LiveResidualProtectionResult } from './live-residual-protection.js';
5
+ export interface Wave9LiveFlatRecoveryResult {
6
+ status: 'flat';
7
+ detail: string;
8
+ candidateId: string;
9
+ symbol: string;
10
+ }
11
+ export type Wave9LiveAutonomousProtectionResult = Wave9LiveFlatRecoveryResult | Wave9LiveResidualProtectionResult;
12
+ /** Test seams are optional; production callers always use the exact defaults. */
13
+ export interface Wave9LiveAutonomousProtectionOptions {
14
+ flatConfirmation?: Wave9LiveFlatConfirmationOptions;
15
+ confirmFlat?: (adapter: IExchangeAdapter, symbol: string, options?: Wave9LiveFlatConfirmationOptions) => Promise<Wave9LiveFlatConfirmation>;
16
+ reprotect?: typeof reprotectWave9LiveResidual;
17
+ /**
18
+ * Optional crash-recovery identity fence. Startup reconciliation supplies
19
+ * this after binding the durable candidate to its exact exchange entry CID.
20
+ * It is re-evaluated immediately before every operation that may cancel,
21
+ * replace, or close same-symbol exposure. A failed/unknown fence leaves the
22
+ * durable row unresolved and performs no exchange mutation.
23
+ */
24
+ authorizeExposureMutation?: () => Promise<{
25
+ authorized: boolean;
26
+ detail: string;
27
+ }>;
28
+ }
29
+ /**
30
+ * Resolve a Wave 9 LIVE exposure after a close acknowledgement or a native
31
+ * stop-loss event. The only successful terminal state is stable exchange
32
+ * flatness persisted to the durable execution ledger. Otherwise the exact
33
+ * frozen candidate stop is proven/re-armed. If that cannot be done, issue one
34
+ * emergency reduce-only close, prove flatness again, and make one final
35
+ * re-protection attempt for any residual. Every unresolved state stays OPEN.
36
+ *
37
+ * The caller must hold the shared trading-operation lock for the whole call.
38
+ */
39
+ export declare function recoverWave9LiveExposure(adapter: IExchangeAdapter, executionLedger: Wave9LiveExecutionLedger, candidateId: string, symbol: string, options?: Wave9LiveAutonomousProtectionOptions): Promise<Wave9LiveAutonomousProtectionResult>;
@@ -0,0 +1,112 @@
1
+ import { confirmWave9LivePositionFlat, } from './live-position-confirmation.js';
2
+ import { reprotectWave9LiveResidual, } from './live-residual-protection.js';
3
+ function manualResult(ledger, candidateId, symbol, detail) {
4
+ return {
5
+ status: 'manual_intervention',
6
+ detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: ${detail}`,
7
+ candidateId,
8
+ symbol,
9
+ stopPrice: ledger.getByCandidateId(candidateId)?.initialStop ?? Number.NaN,
10
+ };
11
+ }
12
+ function combineManualDetail(result, prefix) {
13
+ return {
14
+ ...result,
15
+ detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: ${prefix}; ${result.detail}`,
16
+ };
17
+ }
18
+ async function authorizeMutation(ledger, candidateId, symbol, action, authorize) {
19
+ if (!authorize)
20
+ return undefined;
21
+ try {
22
+ const result = await authorize();
23
+ if (result.authorized)
24
+ return undefined;
25
+ return manualResult(ledger, candidateId, symbol, `${action} was refused by the exact entry/position identity fence: ${result.detail}`);
26
+ }
27
+ catch (error) {
28
+ return manualResult(ledger, candidateId, symbol, `${action} identity verification threw: ${String(error)}`);
29
+ }
30
+ }
31
+ async function markConfirmedFlat(ledger, candidateId, symbol, confirmation) {
32
+ try {
33
+ ledger.markClosed(candidateId);
34
+ return {
35
+ status: 'flat',
36
+ detail: confirmation.detail,
37
+ candidateId,
38
+ symbol,
39
+ };
40
+ }
41
+ catch (error) {
42
+ return manualResult(ledger, candidateId, symbol, `exchange flatness was proven but durable closure could not be persisted: ${String(error)}`);
43
+ }
44
+ }
45
+ /**
46
+ * Resolve a Wave 9 LIVE exposure after a close acknowledgement or a native
47
+ * stop-loss event. The only successful terminal state is stable exchange
48
+ * flatness persisted to the durable execution ledger. Otherwise the exact
49
+ * frozen candidate stop is proven/re-armed. If that cannot be done, issue one
50
+ * emergency reduce-only close, prove flatness again, and make one final
51
+ * re-protection attempt for any residual. Every unresolved state stays OPEN.
52
+ *
53
+ * The caller must hold the shared trading-operation lock for the whole call.
54
+ */
55
+ export async function recoverWave9LiveExposure(adapter, executionLedger, candidateId, symbol, options = {}) {
56
+ const confirmFlat = options.confirmFlat ?? confirmWave9LivePositionFlat;
57
+ const reprotect = options.reprotect ?? reprotectWave9LiveResidual;
58
+ const firstFlat = await confirmFlat(adapter, symbol, options.flatConfirmation);
59
+ if (firstFlat.confirmed) {
60
+ return markConfirmedFlat(executionLedger, candidateId, symbol, firstFlat);
61
+ }
62
+ const initialMutationRefusal = await authorizeMutation(executionLedger, candidateId, symbol, 'initial residual re-protection', options.authorizeExposureMutation);
63
+ if (initialMutationRefusal)
64
+ return initialMutationRefusal;
65
+ let firstProtection;
66
+ try {
67
+ firstProtection = await reprotect(adapter, executionLedger, candidateId, symbol);
68
+ }
69
+ catch (error) {
70
+ firstProtection = manualResult(executionLedger, candidateId, symbol, `initial residual re-protection threw: ${String(error)}`);
71
+ }
72
+ if (firstProtection.status === 'protected')
73
+ return firstProtection;
74
+ const closeRefusal = await authorizeMutation(executionLedger, candidateId, symbol, 'emergency close', options.authorizeExposureMutation);
75
+ if (closeRefusal) {
76
+ return combineManualDetail(closeRefusal, firstProtection.detail);
77
+ }
78
+ let emergencyCloseError;
79
+ try {
80
+ await adapter.closePosition(symbol, 'emergency');
81
+ }
82
+ catch (error) {
83
+ // A post-submit exception is not proof the exchange rejected the close.
84
+ // Always inspect stable exchange position state before deciding what is
85
+ // left to protect.
86
+ emergencyCloseError = String(error);
87
+ }
88
+ const afterEmergencyFlat = await confirmFlat(adapter, symbol, options.flatConfirmation);
89
+ if (afterEmergencyFlat.confirmed) {
90
+ return markConfirmedFlat(executionLedger, candidateId, symbol, afterEmergencyFlat);
91
+ }
92
+ const finalMutationRefusal = await authorizeMutation(executionLedger, candidateId, symbol, 'final residual re-protection', options.authorizeExposureMutation);
93
+ if (finalMutationRefusal) {
94
+ const closeDetail = emergencyCloseError
95
+ ? `emergency close threw (${emergencyCloseError}) and stable flatness was not proven`
96
+ : 'emergency close was acknowledged but stable flatness was not proven';
97
+ return combineManualDetail(finalMutationRefusal, `${firstProtection.detail}; ${closeDetail}`);
98
+ }
99
+ let finalProtection;
100
+ try {
101
+ finalProtection = await reprotect(adapter, executionLedger, candidateId, symbol);
102
+ }
103
+ catch (error) {
104
+ finalProtection = manualResult(executionLedger, candidateId, symbol, `final residual re-protection threw: ${String(error)}`);
105
+ }
106
+ if (finalProtection.status === 'protected')
107
+ return finalProtection;
108
+ const closeDetail = emergencyCloseError
109
+ ? `emergency close threw (${emergencyCloseError}) and stable flatness was not proven`
110
+ : 'emergency close was acknowledged but stable flatness was not proven';
111
+ return combineManualDetail(finalProtection, `${firstProtection.detail}; ${closeDetail}; final residual remains unverified`);
112
+ }
@@ -0,0 +1,33 @@
1
+ import type { AdapterReadiness, IExchangeAdapter } from '../exchange-adapter.js';
2
+ import type { TradingOperationLock } from '../lifecycle/trading-operation-lock.js';
3
+ import type { Wave9LiveStartupReconciliationResult } from './live-startup-reconciliation.js';
4
+ export interface Wave9LiveReadySource extends IExchangeAdapter {
5
+ on(event: 'readiness_changed', listener: (readiness: AdapterReadiness, detail?: string) => void): unknown;
6
+ }
7
+ export interface Wave9LiveDurableReconciliationSchedulerOptions {
8
+ operationLock: TradingOperationLock;
9
+ isCurrentSource(source: Wave9LiveReadySource): boolean;
10
+ reconcile(source: Wave9LiveReadySource): Promise<Wave9LiveStartupReconciliationResult>;
11
+ onResult?(trigger: string, result: Wave9LiveStartupReconciliationResult): void;
12
+ onError?(trigger: string, error: unknown): void;
13
+ retryDelaysMs?: readonly number[];
14
+ periodicMs?: number;
15
+ }
16
+ /**
17
+ * Source-bound READY/periodic driver for durable Wave 9 recovery. At most one
18
+ * timer and one lock-serialized pass exist for the active adapter. A replaced
19
+ * adapter is stopped synchronously and an in-flight old pass cannot reschedule
20
+ * itself. Timers are unref'ed so this safety loop never keeps Node alive.
21
+ */
22
+ export declare class Wave9LiveDurableReconciliationScheduler {
23
+ private readonly options;
24
+ private readonly retryDelaysMs;
25
+ private readonly periodicMs;
26
+ private active?;
27
+ constructor(options: Wave9LiveDurableReconciliationSchedulerOptions);
28
+ attach(source: Wave9LiveReadySource): void;
29
+ stopIfSourceChanged(current: IExchangeAdapter): void;
30
+ stop(): void;
31
+ private isCurrentReady;
32
+ private schedule;
33
+ }
@@ -0,0 +1,115 @@
1
+ const DEFAULT_RETRY_DELAYS_MS = [1_000, 3_000, 10_000];
2
+ const DEFAULT_PERIODIC_MS = 60_000;
3
+ /**
4
+ * Source-bound READY/periodic driver for durable Wave 9 recovery. At most one
5
+ * timer and one lock-serialized pass exist for the active adapter. A replaced
6
+ * adapter is stopped synchronously and an in-flight old pass cannot reschedule
7
+ * itself. Timers are unref'ed so this safety loop never keeps Node alive.
8
+ */
9
+ export class Wave9LiveDurableReconciliationScheduler {
10
+ options;
11
+ retryDelaysMs;
12
+ periodicMs;
13
+ active;
14
+ constructor(options) {
15
+ this.options = options;
16
+ this.retryDelaysMs = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
17
+ this.periodicMs = options.periodicMs ?? DEFAULT_PERIODIC_MS;
18
+ if (this.retryDelaysMs.some((delay) => !Number.isFinite(delay) || delay < 0)) {
19
+ throw new Error('Wave 9 reconciliation retry delays must be finite and non-negative');
20
+ }
21
+ if (!Number.isFinite(this.periodicMs) || this.periodicMs <= 0) {
22
+ throw new Error('Wave 9 reconciliation periodic interval must be positive and finite');
23
+ }
24
+ }
25
+ attach(source) {
26
+ if (this.active?.source === source && !this.active.stopped)
27
+ return;
28
+ this.stop();
29
+ const state = { source, stopped: false };
30
+ this.active = state;
31
+ source.on('readiness_changed', (readiness) => {
32
+ // Runtime installs this listener immediately before it publishes a
33
+ // reconnect adapter. Queue first, then let the timer callback verify
34
+ // current source identity, so a synchronously-fast READY cannot be lost.
35
+ if (readiness === 'READY' && !state.stopped) {
36
+ this.schedule(state, 0, 0, 'ready');
37
+ }
38
+ });
39
+ if (source.readiness === 'READY') {
40
+ this.schedule(state, 0, 0, 'ready_replay');
41
+ }
42
+ }
43
+ stopIfSourceChanged(current) {
44
+ if (this.active && this.active.source !== current)
45
+ this.stop();
46
+ }
47
+ stop() {
48
+ const state = this.active;
49
+ if (!state)
50
+ return;
51
+ state.stopped = true;
52
+ if (state.timer !== undefined)
53
+ clearTimeout(state.timer);
54
+ state.timer = undefined;
55
+ this.active = undefined;
56
+ }
57
+ isCurrentReady(state) {
58
+ const source = state.source;
59
+ return !state.stopped
60
+ && this.active === state
61
+ && this.options.isCurrentSource(source)
62
+ && source.isLive
63
+ && source.mode === 'LIVE'
64
+ && source.readiness === 'READY';
65
+ }
66
+ schedule(state, delayMs, retryIndex, trigger) {
67
+ if (state.stopped || this.active !== state || state.timer !== undefined || state.inFlight) {
68
+ return;
69
+ }
70
+ const timer = setTimeout(() => {
71
+ state.timer = undefined;
72
+ if (!this.isCurrentReady(state)) {
73
+ if (!this.options.isCurrentSource(state.source)) {
74
+ state.stopped = true;
75
+ if (this.active === state)
76
+ this.active = undefined;
77
+ }
78
+ return;
79
+ }
80
+ let passResult;
81
+ let passFailed = false;
82
+ const pass = this.options.operationLock.withAccountLock(`wave9_live_durable_reconciliation:${trigger}`, async () => {
83
+ if (!this.isCurrentReady(state))
84
+ return;
85
+ passResult = await this.options.reconcile(state.source);
86
+ this.options.onResult?.(trigger, passResult);
87
+ }).catch((error) => {
88
+ passFailed = true;
89
+ this.options.onError?.(trigger, error);
90
+ }).finally(() => {
91
+ state.inFlight = undefined;
92
+ if (!this.isCurrentReady(state)) {
93
+ if (!this.options.isCurrentSource(state.source)) {
94
+ state.stopped = true;
95
+ if (this.active === state)
96
+ this.active = undefined;
97
+ }
98
+ return;
99
+ }
100
+ const retryable = passFailed || passResult?.retryable === true;
101
+ if (retryable && retryIndex < this.retryDelaysMs.length) {
102
+ this.schedule(state, this.retryDelaysMs[retryIndex], retryIndex + 1, `ready_retry_${retryIndex + 1}`);
103
+ return;
104
+ }
105
+ // Always retain a periodic pass. It catches ATTEMPTED/OPEN rows born
106
+ // after READY when a live submit's exact CID read was transiently
107
+ // unknown, while healthy rows are a cheap exact-stop no-op.
108
+ this.schedule(state, this.periodicMs, 0, 'periodic');
109
+ });
110
+ state.inFlight = pass;
111
+ }, delayMs);
112
+ timer.unref();
113
+ state.timer = timer;
114
+ }
115
+ }
@@ -0,0 +1,107 @@
1
+ import { type Wave9StrategyName } from '../portfolio/wave9-policy.js';
2
+ export type Wave9LiveExecutionState = 'attempted' | 'open' | 'closed';
3
+ export type Wave9LiveDirection = 'LONG' | 'SHORT';
4
+ export type Wave9LiveOrderSide = 'buy' | 'sell';
5
+ export type Wave9LivePositionSide = 'long' | 'short';
6
+ export interface Wave9LiveExecutionRecord {
7
+ candidateId: string;
8
+ missionId: string;
9
+ eventTime: string;
10
+ /** Canonical frozen-universe form, e.g. BTCUSDT. */
11
+ symbol: string;
12
+ strategy: Wave9StrategyName;
13
+ direction: Wave9LiveDirection;
14
+ side: Wave9LiveOrderSide;
15
+ /** Exact quantity authorized for the market-order attempt. */
16
+ quantity: number;
17
+ initialStop: number;
18
+ /** Deterministic Binance-safe idempotency key. */
19
+ clientOrderId: string;
20
+ state: Wave9LiveExecutionState;
21
+ attemptedAt: number;
22
+ updatedAt: number;
23
+ openedAt?: number;
24
+ closedAt?: number;
25
+ filledQuantity?: number;
26
+ entryPrice?: number;
27
+ exchangeOrderId?: string;
28
+ }
29
+ export interface Wave9LiveAttemptInput {
30
+ candidateId: string;
31
+ missionId: string;
32
+ eventTime: string;
33
+ symbol: string;
34
+ strategy: Wave9StrategyName;
35
+ direction: Wave9LiveDirection;
36
+ side: Wave9LiveOrderSide;
37
+ quantity: number;
38
+ initialStop: number;
39
+ clientOrderId: string;
40
+ }
41
+ export interface Wave9LiveOpenInput {
42
+ filledQuantity?: number;
43
+ entryPrice?: number;
44
+ exchangeOrderId?: string;
45
+ }
46
+ /** Final fill learned by resolving the exact deterministic entry CID. */
47
+ export interface Wave9LiveExactOrderFillInput {
48
+ filledQuantity: number;
49
+ exchangeOrderId: string;
50
+ entryPrice?: number;
51
+ }
52
+ export interface Wave9LiveExecutionLedgerOptions {
53
+ pluginId?: string;
54
+ basePath?: string;
55
+ now?: () => number;
56
+ }
57
+ /** Stable Binance-compatible client order id (35 chars; Binance max is 36). */
58
+ export declare function wave9ClientOrderId(candidateId: string): string;
59
+ /** Backwards-compatible descriptive alias. */
60
+ export declare const wave9LiveClientOrderId: typeof wave9ClientOrderId;
61
+ export declare class Wave9LiveExecutionLedger {
62
+ private readonly filePath;
63
+ private readonly dir;
64
+ private readonly now;
65
+ private readonly entries;
66
+ constructor(options?: Wave9LiveExecutionLedgerOptions);
67
+ /**
68
+ * Persist an execution attempt before exchange submission. Repeating the
69
+ * exact immutable authorization is idempotent; any mismatch fails closed.
70
+ */
71
+ recordAttempt(input: Wave9LiveAttemptInput): Wave9LiveExecutionRecord;
72
+ /** Mark a confirmed fill. Repeating the exact confirmation is idempotent. */
73
+ markOpen(candidateId: string, input?: Wave9LiveOpenInput): Wave9LiveExecutionRecord;
74
+ /**
75
+ * Enrich an already-OPEN row after an exact deterministic entry lookup has
76
+ * proven the order terminal. This is deliberately narrower than markOpen:
77
+ * the final fill may populate a previously missing value or increase a
78
+ * crash-time partial fill, but it can never decrease, exceed authorization,
79
+ * or replace an already-bound exchange order identity.
80
+ */
81
+ reconcileOpenFillFromExactOrder(candidateId: string, input: Wave9LiveExactOrderFillInput): Wave9LiveExecutionRecord;
82
+ /** Mark an attempted/open record terminal. Terminal rows are never deleted. */
83
+ markClosed(candidateId: string): Wave9LiveExecutionRecord;
84
+ getByCandidateId(candidateId: string): Wave9LiveExecutionRecord | undefined;
85
+ /**
86
+ * Latest confirmed-open row matching an exchange position.
87
+ *
88
+ * An `attempted` row is a durable replay tombstone: the exchange outcome
89
+ * may be ambiguous, so the candidate must never execute again, but the row
90
+ * is not proof that a later same-symbol/same-side position belongs to Wave 9.
91
+ * Only `markOpen()` establishes durable position ownership.
92
+ */
93
+ findLatestForPosition(symbol: string, side: Wave9LivePositionSide | Wave9LiveOrderSide): Wave9LiveExecutionRecord | undefined;
94
+ /** Attempts count as executed because exchange outcome can be crash-ambiguous. */
95
+ getExecutedCandidateIds(): string[];
96
+ getAll(): Wave9LiveExecutionRecord[];
97
+ getFilePath(): string;
98
+ private currentTimestamp;
99
+ private requireCandidate;
100
+ private validateAttemptInput;
101
+ private validateOpenInput;
102
+ private sameAttempt;
103
+ private loadOrInit;
104
+ private persist;
105
+ private validateFile;
106
+ private validateStoredRecord;
107
+ }