@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.
- package/bridge/gateway/event-parser.d.ts +6 -1
- package/bridge/gateway/event-parser.js +19 -2
- package/bridge/gateway/poller.d.ts +1 -0
- package/bridge/gateway/poller.js +14 -2
- package/bridge/providers/gateway.d.ts +22 -2
- package/bridge/providers/gateway.js +67 -9
- package/ccxt/binance-private.d.ts +21 -0
- package/ccxt/binance-private.js +132 -22
- package/ccxt/public-market-data-api.d.ts +14 -0
- package/ccxt/public-market-data-api.js +15 -1
- package/config/plugin-config-io.d.ts +13 -0
- package/config/plugin-config-io.js +15 -0
- package/config/tool-gate.js +3 -0
- package/exchange-adapter.d.ts +16 -0
- package/index.js +658 -83
- package/ingest/position-auto-capture.d.ts +68 -0
- package/ingest/position-auto-capture.js +321 -23
- package/ingest/position-decisions-client.d.ts +7 -2
- package/ingest/position-decisions-client.js +13 -3
- package/ingest/reconcile-db-vs-exchange.d.ts +39 -1
- package/ingest/reconcile-db-vs-exchange.js +66 -10
- package/lifecycle/trading-operation-lock.d.ts +17 -0
- package/lifecycle/trading-operation-lock.js +14 -0
- package/live/bracket-id.d.ts +2 -3
- package/live/bracket-id.js +22 -9
- package/live/fill-price.d.ts +13 -0
- package/live/fill-price.js +37 -0
- package/live/live-adapter.d.ts +57 -2
- package/live/live-adapter.js +290 -49
- package/live/local-signal-service.js +11 -6
- package/live/local-strategy-evaluator.js +4 -0
- package/live/position-state-store.d.ts +4 -0
- package/live/proposal-decision-listener.d.ts +6 -0
- package/live/proposal-decision-listener.js +4 -0
- package/live/stop-watcher.d.ts +35 -2
- package/live/stop-watcher.js +63 -3
- package/onboarding/runtime.d.ts +19 -0
- package/onboarding/runtime.js +34 -3
- package/openclaw.plugin.json +1 -0
- package/package.json +2 -2
- package/portfolio/reentry-tracker.d.ts +36 -0
- package/portfolio/reentry-tracker.js +127 -0
- package/portfolio/wave9-admission.d.ts +67 -0
- package/portfolio/wave9-admission.js +262 -0
- package/portfolio/wave9-policy.d.ts +36 -0
- package/portfolio/wave9-policy.js +183 -0
- package/signals/conditions/registry.js +61 -2
- package/signals/strategy-adapter.js +17 -7
- package/simulator/exchange-simulator.d.ts +12 -0
- package/simulator/exchange-simulator.js +78 -3
- package/simulator/fill-engine.js +5 -1
- package/simulator/types.d.ts +10 -1
- package/skills/reefclaw/SKILL.md +2 -0
- package/strategy/evaluator.d.ts +3 -0
- package/strategy/evaluator.js +5 -0
- package/tools/assessment-validation.d.ts +23 -0
- package/tools/assessment-validation.js +58 -0
- package/tools/attach-brackets.d.ts +7 -2
- package/tools/attach-brackets.js +201 -0
- package/tools/audit-bracket-protection.js +157 -1
- package/tools/bracket-control.d.ts +12 -0
- package/tools/bracket-control.js +35 -0
- package/tools/cancel-all-orders.d.ts +2 -0
- package/tools/cancel-all-orders.js +4 -1
- package/tools/cancel-order.d.ts +4 -0
- package/tools/cancel-order.js +49 -3
- package/tools/close-position.d.ts +23 -0
- package/tools/close-position.js +286 -13
- package/tools/create-order.d.ts +28 -0
- package/tools/create-order.js +1390 -190
- package/tools/get-analytics.js +2 -2
- package/tools/get-basis.js +2 -2
- package/tools/get-cascade-risk.js +2 -2
- package/tools/get-crypto-metrics.js +14 -4
- package/tools/get-cvd.js +2 -2
- package/tools/get-divergences.js +2 -2
- package/tools/get-funding-context.js +2 -2
- package/tools/get-liquidation-levels.js +2 -2
- package/tools/get-liquidation-pulse.js +2 -2
- package/tools/get-pattern-scan.js +2 -2
- package/tools/get-regime.js +2 -2
- package/tools/get-resting-liquidity.js +2 -2
- package/tools/get-risk-scenario.js +2 -2
- package/tools/get-session-review.js +2 -2
- package/tools/get-setup-detail.js +21 -2
- package/tools/get-signals.js +2 -2
- package/tools/get-sizing.js +2 -2
- package/tools/get-trade-feedback.js +2 -2
- package/tools/get-trade-flow.js +2 -2
- package/tools/get-volume-profile.js +2 -2
- package/tools/get-wave9-status.d.ts +127 -0
- package/tools/get-wave9-status.js +796 -0
- package/tools/intel-api.d.ts +20 -0
- package/tools/intel-api.js +67 -0
- package/tools/intel-cache.d.ts +1 -1
- package/tools/intel-cache.js +20 -5
- package/tools/list-strategies.d.ts +11 -1
- package/tools/list-strategies.js +17 -0
- package/tools/modify-stop.d.ts +4 -0
- package/tools/modify-stop.js +63 -24
- package/tools/modify-target.d.ts +4 -0
- package/tools/modify-target.js +62 -23
- package/tools/scan-pairs.d.ts +4 -0
- package/tools/scan-pairs.js +22 -8
- package/tools/toggle-strategy.js +7 -0
- package/types.d.ts +5 -0
- package/venues/hyperliquid/hl-balance.d.ts +116 -0
- package/venues/hyperliquid/hl-balance.js +145 -0
- package/venues/hyperliquid/hl-bracket-coordinator.d.ts +123 -0
- package/venues/hyperliquid/hl-bracket-coordinator.js +533 -0
- package/venues/hyperliquid/hl-brackets.d.ts +102 -0
- package/venues/hyperliquid/hl-brackets.js +172 -0
- package/venues/hyperliquid/hl-cloid.d.ts +22 -0
- package/venues/hyperliquid/hl-cloid.js +82 -0
- package/venues/hyperliquid/hl-info-cache.d.ts +46 -0
- package/venues/hyperliquid/hl-info-cache.js +125 -0
- package/venues/hyperliquid/hl-live-adapter.d.ts +146 -0
- package/venues/hyperliquid/hl-live-adapter.js +728 -0
- package/venues/hyperliquid/hl-precision.d.ts +61 -0
- package/venues/hyperliquid/hl-precision.js +176 -0
- package/venues/hyperliquid/hl-private.d.ts +88 -0
- package/venues/hyperliquid/hl-private.js +357 -0
- package/venues/hyperliquid/hl-public.d.ts +31 -4
- package/venues/hyperliquid/hl-public.js +163 -12
- package/venues/hyperliquid/hl-rate-gate.d.ts +57 -0
- package/venues/hyperliquid/hl-rate-gate.js +220 -0
- package/venues/hyperliquid/hl-user-stream.d.ts +90 -0
- package/venues/hyperliquid/hl-user-stream.js +220 -0
- package/venues/registry.d.ts +23 -9
- package/venues/registry.js +12 -13
- package/venues/symbols.d.ts +43 -0
- package/venues/symbols.js +107 -0
- package/wave9/live-account-capture.d.ts +67 -0
- package/wave9/live-account-capture.js +435 -0
- package/wave9/live-autonomous-protection.d.ts +39 -0
- package/wave9/live-autonomous-protection.js +112 -0
- package/wave9/live-durable-reconciliation-scheduler.d.ts +33 -0
- package/wave9/live-durable-reconciliation-scheduler.js +115 -0
- package/wave9/live-execution-ledger.d.ts +107 -0
- package/wave9/live-execution-ledger.js +498 -0
- package/wave9/live-position-confirmation.d.ts +18 -0
- package/wave9/live-position-confirmation.js +111 -0
- package/wave9/live-residual-protection.d.ts +18 -0
- package/wave9/live-residual-protection.js +250 -0
- package/wave9/live-startup-reconciliation.d.ts +38 -0
- package/wave9/live-startup-reconciliation.js +454 -0
- package/wave9/live-symbol-ownership.d.ts +20 -0
- package/wave9/live-symbol-ownership.js +132 -0
- package/wave9/paper-admission-guard.d.ts +199 -0
- package/wave9/paper-admission-guard.js +650 -0
- package/wave9/usdm-evidence-provider.d.ts +42 -0
- package/wave9/usdm-evidence-provider.js +133 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { CcxtBalance } from '../../types.js';
|
|
2
|
+
import type { CloseReason } from '../../simulator/types.js';
|
|
3
|
+
export interface HlClearinghouseState {
|
|
4
|
+
marginSummary?: {
|
|
5
|
+
accountValue?: string | number;
|
|
6
|
+
totalMarginUsed?: string | number;
|
|
7
|
+
totalNtlPos?: string | number;
|
|
8
|
+
};
|
|
9
|
+
withdrawable?: string | number;
|
|
10
|
+
assetPositions?: Array<{
|
|
11
|
+
position?: {
|
|
12
|
+
coin?: string;
|
|
13
|
+
szi?: string | number;
|
|
14
|
+
entryPx?: string | number;
|
|
15
|
+
unrealizedPnl?: string | number;
|
|
16
|
+
positionValue?: string | number;
|
|
17
|
+
liquidationPx?: string | number;
|
|
18
|
+
marginUsed?: string | number;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export interface HlNav {
|
|
23
|
+
/** accountValue — what the HL app's portfolio panel shows. */
|
|
24
|
+
equity: number;
|
|
25
|
+
/** equity − Σ uPnl. The Binance "wallet"/margin-balance analog. */
|
|
26
|
+
wallet: number;
|
|
27
|
+
totalUnrealizedPnl: number;
|
|
28
|
+
/** Free collateral (HL `withdrawable`). */
|
|
29
|
+
available: number;
|
|
30
|
+
}
|
|
31
|
+
/** Derive NAV/wallet from a clearinghouseState payload.
|
|
32
|
+
*
|
|
33
|
+
* ★ Returns null when the payload is UNREADABLE — never a zeroed NAV. A phantom
|
|
34
|
+
* "$0 equity" would trip risk limits and look like a catastrophic loss; null
|
|
35
|
+
* means "unknown", which the caller must refuse to act on (null ≠ empty). */
|
|
36
|
+
export declare function deriveHlNav(state: HlClearinghouseState | null | undefined): HlNav | null;
|
|
37
|
+
/** CcxtBalance shape the rest of the plugin (KPI strip, sizing, LiveBalanceEnricher)
|
|
38
|
+
* already consumes. USDC is HL's settlement asset. */
|
|
39
|
+
export declare function toCcxtBalance(nav: HlNav): CcxtBalance;
|
|
40
|
+
export interface HlFillForAnchor {
|
|
41
|
+
time: number;
|
|
42
|
+
closedPnl?: string | number;
|
|
43
|
+
fee?: string | number;
|
|
44
|
+
builderFee?: string | number;
|
|
45
|
+
}
|
|
46
|
+
export interface HlFundingForAnchor {
|
|
47
|
+
time: number;
|
|
48
|
+
/** HL funding deltas are signed USDC (negative = paid). */
|
|
49
|
+
usdc?: string | number;
|
|
50
|
+
delta?: {
|
|
51
|
+
usdc?: string | number;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Σ(closedPnl − fee − builderFee) + Σ funding, over rows at/after `sinceMs`.
|
|
55
|
+
*
|
|
56
|
+
* This is the exact analog of Binance's `netNonTransfer` (REALIZED_PNL +
|
|
57
|
+
* COMMISSION + FUNDING_FEE), and — critically — it is NET of fees. The Binance
|
|
58
|
+
* lesson (the ledger was systematically gross, causing a visible KPI gap) applies
|
|
59
|
+
* verbatim: a gross anchor WILL disagree with the HL app. */
|
|
60
|
+
export declare function computeNetNonTransfer(args: {
|
|
61
|
+
fills: HlFillForAnchor[];
|
|
62
|
+
fundings: HlFundingForAnchor[];
|
|
63
|
+
sinceMs: number;
|
|
64
|
+
}): {
|
|
65
|
+
netNonTransfer: number;
|
|
66
|
+
realizedPnlGross: number;
|
|
67
|
+
fees: number;
|
|
68
|
+
funding: number;
|
|
69
|
+
};
|
|
70
|
+
/** UTC-midnight epoch ms for a given instant (the Day-P&L boundary). */
|
|
71
|
+
export declare function utcMidnightMs(now?: number): number;
|
|
72
|
+
/** sessionStartNav = wallet_now − netNonTransfer_since_midnight.
|
|
73
|
+
* dayPnl = NAV − sessionStartNav (computed by the caller each tick). */
|
|
74
|
+
export declare function deriveSessionStartNav(args: {
|
|
75
|
+
walletNow: number;
|
|
76
|
+
netNonTransfer: number;
|
|
77
|
+
}): number;
|
|
78
|
+
/** A capital flow (deposit / withdraw / internal transfer) changes the wallet
|
|
79
|
+
* WITHOUT being trading P&L — bump the anchor by the same signed amount so it
|
|
80
|
+
* never shows up as Day-P&L (the DepositTracker rule).
|
|
81
|
+
*
|
|
82
|
+
* T-3 note: the `delta.type` enum is OPEN (docs incomplete; only
|
|
83
|
+
* `internalTransfer` observed so far). We therefore treat any ledger row that
|
|
84
|
+
* carries a usdc delta as a capital flow rather than switch on a closed set — an
|
|
85
|
+
* UNKNOWN type must never be silently dropped, or the anchor drifts. */
|
|
86
|
+
export declare function applyLedgerDelta(args: {
|
|
87
|
+
sessionStartNav: number;
|
|
88
|
+
ledgerRows: Array<{
|
|
89
|
+
time: number;
|
|
90
|
+
delta?: {
|
|
91
|
+
usdc?: string | number;
|
|
92
|
+
type?: string;
|
|
93
|
+
};
|
|
94
|
+
}>;
|
|
95
|
+
sinceMs: number;
|
|
96
|
+
seenHashes?: Set<string>;
|
|
97
|
+
}): {
|
|
98
|
+
sessionStartNav: number;
|
|
99
|
+
applied: number;
|
|
100
|
+
};
|
|
101
|
+
/** ★ Close-bypass mapping (§5.9) — HL signals → journal close reasons.
|
|
102
|
+
*
|
|
103
|
+
* A position can leave our control without our close path running: a bracket
|
|
104
|
+
* triggering, a LIQUIDATION, an ADL, or a delisting. Each must land in the
|
|
105
|
+
* journal honestly (the KPI and the learning loop both read these), never as a
|
|
106
|
+
* silent disappearance. */
|
|
107
|
+
export declare function mapHlCloseReason(signal: {
|
|
108
|
+
/** Fill-level liquidation object (authoritative). */
|
|
109
|
+
liquidation?: {
|
|
110
|
+
method?: string;
|
|
111
|
+
} | null;
|
|
112
|
+
/** Order status from `orderUpdates`. */
|
|
113
|
+
orderStatus?: string;
|
|
114
|
+
/** Our own cloid role, when the closing order was one of our legs. */
|
|
115
|
+
bracketRole?: 'stop' | 'target';
|
|
116
|
+
}): CloseReason | 'liquidated' | 'adl' | 'venue_delisted' | null;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Hyperliquid balance / NAV / Day-P&L anchor (plan §5.8) + close-bypass mapping
|
|
2
|
+
// (§5.9). Pure functions — the adapter feeds them raw HL payloads.
|
|
3
|
+
//
|
|
4
|
+
// ★ THE INVARIANT THIS FILE EXISTS TO KEEP: "KPI must equal the exchange app to
|
|
5
|
+
// the cent." On Binance that meant anchoring Day-P&L to the UTC-midnight income
|
|
6
|
+
// endpoint (a multi-dollar gap there was ALWAYS a bug, never a formula quirk).
|
|
7
|
+
// HL has no income endpoint, so the anchor is RECONSTRUCTED:
|
|
8
|
+
//
|
|
9
|
+
// wallet = accountValue − Σ unrealizedPnl (accountValue INCLUDES uPnl)
|
|
10
|
+
// NAV/equity = accountValue (so equity = wallet + Σ uPnl
|
|
11
|
+
// stays literally true)
|
|
12
|
+
// netNonTransfer(since UTC midnight)
|
|
13
|
+
// = Σ closedPnl − Σ fee − Σ builderFee (fills)
|
|
14
|
+
// + Σ funding (userFunding)
|
|
15
|
+
// sessionStartNav = wallet_now − netNonTransfer
|
|
16
|
+
// dayPnl = NAV − sessionStartNav
|
|
17
|
+
//
|
|
18
|
+
// Deposits/withdrawals/transfers are NOT trading P&L: they bump the anchor
|
|
19
|
+
// instead (the DepositTracker rule), via `applyLedgerDelta`.
|
|
20
|
+
//
|
|
21
|
+
// ★ FILL-HISTORY SHALLOWNESS: only the 10,000 most recent fills exist server-side
|
|
22
|
+
// on HL (deep history is NOT queryable). The anchor must therefore be able to
|
|
23
|
+
// run off WS-captured fills persisted in our own `trades` table — which is why
|
|
24
|
+
// the WS-first audit-trail rule is a hard prerequisite here, not hygiene.
|
|
25
|
+
const num = (v) => {
|
|
26
|
+
const n = typeof v === 'number' ? v : Number(v);
|
|
27
|
+
return Number.isFinite(n) ? n : 0;
|
|
28
|
+
};
|
|
29
|
+
/** Derive NAV/wallet from a clearinghouseState payload.
|
|
30
|
+
*
|
|
31
|
+
* ★ Returns null when the payload is UNREADABLE — never a zeroed NAV. A phantom
|
|
32
|
+
* "$0 equity" would trip risk limits and look like a catastrophic loss; null
|
|
33
|
+
* means "unknown", which the caller must refuse to act on (null ≠ empty). */
|
|
34
|
+
export function deriveHlNav(state) {
|
|
35
|
+
if (!state || typeof state !== 'object')
|
|
36
|
+
return null;
|
|
37
|
+
const accountValueRaw = state.marginSummary?.accountValue;
|
|
38
|
+
if (accountValueRaw === undefined || accountValueRaw === null)
|
|
39
|
+
return null;
|
|
40
|
+
const equity = num(accountValueRaw);
|
|
41
|
+
const totalUnrealizedPnl = (state.assetPositions ?? []).reduce((sum, ap) => sum + num(ap?.position?.unrealizedPnl), 0);
|
|
42
|
+
return {
|
|
43
|
+
equity,
|
|
44
|
+
wallet: equity - totalUnrealizedPnl,
|
|
45
|
+
totalUnrealizedPnl,
|
|
46
|
+
available: num(state.withdrawable),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** CcxtBalance shape the rest of the plugin (KPI strip, sizing, LiveBalanceEnricher)
|
|
50
|
+
* already consumes. USDC is HL's settlement asset. */
|
|
51
|
+
export function toCcxtBalance(nav) {
|
|
52
|
+
const free = { USDC: nav.available };
|
|
53
|
+
const used = { USDC: Math.max(0, nav.wallet - nav.available) };
|
|
54
|
+
const total = { USDC: nav.wallet };
|
|
55
|
+
return {
|
|
56
|
+
free,
|
|
57
|
+
used,
|
|
58
|
+
total,
|
|
59
|
+
USDC: { free: nav.available, used: used.USDC, total: nav.wallet },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** Σ(closedPnl − fee − builderFee) + Σ funding, over rows at/after `sinceMs`.
|
|
63
|
+
*
|
|
64
|
+
* This is the exact analog of Binance's `netNonTransfer` (REALIZED_PNL +
|
|
65
|
+
* COMMISSION + FUNDING_FEE), and — critically — it is NET of fees. The Binance
|
|
66
|
+
* lesson (the ledger was systematically gross, causing a visible KPI gap) applies
|
|
67
|
+
* verbatim: a gross anchor WILL disagree with the HL app. */
|
|
68
|
+
export function computeNetNonTransfer(args) {
|
|
69
|
+
let realizedPnlGross = 0;
|
|
70
|
+
let fees = 0;
|
|
71
|
+
for (const f of args.fills) {
|
|
72
|
+
if (!Number.isFinite(f.time) || f.time < args.sinceMs)
|
|
73
|
+
continue;
|
|
74
|
+
realizedPnlGross += num(f.closedPnl);
|
|
75
|
+
fees += num(f.fee) + num(f.builderFee);
|
|
76
|
+
}
|
|
77
|
+
let funding = 0;
|
|
78
|
+
for (const f of args.fundings) {
|
|
79
|
+
if (!Number.isFinite(f.time) || f.time < args.sinceMs)
|
|
80
|
+
continue;
|
|
81
|
+
funding += num(f.usdc ?? f.delta?.usdc);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
netNonTransfer: realizedPnlGross - fees + funding,
|
|
85
|
+
realizedPnlGross,
|
|
86
|
+
fees,
|
|
87
|
+
funding,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** UTC-midnight epoch ms for a given instant (the Day-P&L boundary). */
|
|
91
|
+
export function utcMidnightMs(now = Date.now()) {
|
|
92
|
+
const d = new Date(now);
|
|
93
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
94
|
+
}
|
|
95
|
+
/** sessionStartNav = wallet_now − netNonTransfer_since_midnight.
|
|
96
|
+
* dayPnl = NAV − sessionStartNav (computed by the caller each tick). */
|
|
97
|
+
export function deriveSessionStartNav(args) {
|
|
98
|
+
return args.walletNow - args.netNonTransfer;
|
|
99
|
+
}
|
|
100
|
+
/** A capital flow (deposit / withdraw / internal transfer) changes the wallet
|
|
101
|
+
* WITHOUT being trading P&L — bump the anchor by the same signed amount so it
|
|
102
|
+
* never shows up as Day-P&L (the DepositTracker rule).
|
|
103
|
+
*
|
|
104
|
+
* T-3 note: the `delta.type` enum is OPEN (docs incomplete; only
|
|
105
|
+
* `internalTransfer` observed so far). We therefore treat any ledger row that
|
|
106
|
+
* carries a usdc delta as a capital flow rather than switch on a closed set — an
|
|
107
|
+
* UNKNOWN type must never be silently dropped, or the anchor drifts. */
|
|
108
|
+
export function applyLedgerDelta(args) {
|
|
109
|
+
let bump = 0;
|
|
110
|
+
let applied = 0;
|
|
111
|
+
for (const row of args.ledgerRows) {
|
|
112
|
+
if (!Number.isFinite(row.time) || row.time < args.sinceMs)
|
|
113
|
+
continue;
|
|
114
|
+
const usdc = num(row.delta?.usdc);
|
|
115
|
+
if (usdc === 0)
|
|
116
|
+
continue;
|
|
117
|
+
bump += usdc;
|
|
118
|
+
applied += 1;
|
|
119
|
+
}
|
|
120
|
+
return { sessionStartNav: args.sessionStartNav + bump, applied };
|
|
121
|
+
}
|
|
122
|
+
/** ★ Close-bypass mapping (§5.9) — HL signals → journal close reasons.
|
|
123
|
+
*
|
|
124
|
+
* A position can leave our control without our close path running: a bracket
|
|
125
|
+
* triggering, a LIQUIDATION, an ADL, or a delisting. Each must land in the
|
|
126
|
+
* journal honestly (the KPI and the learning loop both read these), never as a
|
|
127
|
+
* silent disappearance. */
|
|
128
|
+
export function mapHlCloseReason(signal) {
|
|
129
|
+
if (signal.liquidation) {
|
|
130
|
+
// HL marks backstop (HLP vault) liquidations distinctly from market ones; ADL
|
|
131
|
+
// arrives as a liquidation of the OPPOSITE side hitting us.
|
|
132
|
+
const method = (signal.liquidation.method ?? '').toLowerCase();
|
|
133
|
+
return method.includes('adl') ? 'adl' : 'liquidated';
|
|
134
|
+
}
|
|
135
|
+
const status = (signal.orderStatus ?? '').toLowerCase();
|
|
136
|
+
if (status === 'liquidatedcanceled')
|
|
137
|
+
return 'liquidated';
|
|
138
|
+
if (status === 'delistedcanceled')
|
|
139
|
+
return 'venue_delisted';
|
|
140
|
+
if (signal.bracketRole === 'stop')
|
|
141
|
+
return 'exchange_stop';
|
|
142
|
+
if (signal.bracketRole === 'target')
|
|
143
|
+
return 'exchange_target';
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { CcxtOrder, CcxtPosition } from '../../types.js';
|
|
3
|
+
import type { CloseReason } from '../../simulator/types.js';
|
|
4
|
+
import type { BracketId, BracketRequest, BracketState } from '../../live/bracket-types.js';
|
|
5
|
+
import type { BracketLedger } from '../../live/bracket-ledger.js';
|
|
6
|
+
import type { HlOrderUpdateEvent } from './hl-user-stream.js';
|
|
7
|
+
/** Narrow execution surface the coordinator needs — the HyperliquidLiveAdapter
|
|
8
|
+
* implements it; tests mock it without a network. */
|
|
9
|
+
export interface HlBracketExecutor {
|
|
10
|
+
attachBrackets(args: {
|
|
11
|
+
bracketId: BracketId;
|
|
12
|
+
symbol: string;
|
|
13
|
+
positionSide: 'long' | 'short';
|
|
14
|
+
positionSize: number;
|
|
15
|
+
stopPrice?: number;
|
|
16
|
+
targetPrice?: number;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
orders: CcxtOrder[];
|
|
19
|
+
slCid?: string;
|
|
20
|
+
tpCid?: string;
|
|
21
|
+
}>;
|
|
22
|
+
resizeBrackets(args: {
|
|
23
|
+
bracketId: BracketId;
|
|
24
|
+
symbol: string;
|
|
25
|
+
positionSide: 'long' | 'short';
|
|
26
|
+
positionSize: number;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
resized: boolean;
|
|
29
|
+
slCid?: string;
|
|
30
|
+
tpCid?: string;
|
|
31
|
+
}>;
|
|
32
|
+
/** Cancel one leg by cloid. Idempotent — "already gone" must not throw. */
|
|
33
|
+
cancelBracketLeg(cloid: string, symbol: string): Promise<void>;
|
|
34
|
+
/** Coverage audit — THROWS when order state is unknown (null ≠ empty). */
|
|
35
|
+
auditBracketCoverage(symbol: string, positionSize: number): Promise<{
|
|
36
|
+
covered: boolean;
|
|
37
|
+
shortfall: {
|
|
38
|
+
stop: number;
|
|
39
|
+
target: number;
|
|
40
|
+
};
|
|
41
|
+
missing: ('stop' | 'target')[];
|
|
42
|
+
}>;
|
|
43
|
+
getPositionsOrNull(symbol?: string): Promise<CcxtPosition[] | null>;
|
|
44
|
+
closePosition(symbol: string, closeReason?: CloseReason): Promise<CcxtOrder>;
|
|
45
|
+
}
|
|
46
|
+
export interface HlAttachResult {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
latencyMs: number;
|
|
49
|
+
slCid?: string;
|
|
50
|
+
tpCid?: string;
|
|
51
|
+
error?: string;
|
|
52
|
+
attempts: number;
|
|
53
|
+
}
|
|
54
|
+
export interface HlCoordinatorOpts {
|
|
55
|
+
maxAttempts?: number;
|
|
56
|
+
retryBackoffMs?: (attempt: number) => number;
|
|
57
|
+
now?: () => number;
|
|
58
|
+
sleep?: (ms: number) => Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export declare function isTerminalBracketState(state: BracketState): boolean;
|
|
61
|
+
export declare class HlBracketCoordinator extends EventEmitter {
|
|
62
|
+
private readonly executor;
|
|
63
|
+
private readonly ledger;
|
|
64
|
+
private readonly maxAttempts;
|
|
65
|
+
private readonly backoff;
|
|
66
|
+
private readonly now;
|
|
67
|
+
private readonly sleep;
|
|
68
|
+
/** Per-symbol in-flight guard so a WS fill + the createOrder return path
|
|
69
|
+
* can't both run attachOnFill concurrently (double-submit). */
|
|
70
|
+
private readonly inFlight;
|
|
71
|
+
/** Cloids WE deliberately cancelled (resize/modify supersede, retry cleanup,
|
|
72
|
+
* cancelBrackets). Their WS 'canceled' events are OUR OWN and must never
|
|
73
|
+
* trip the stripped-protection canary — the T-8 testnet run proved the WS
|
|
74
|
+
* event can arrive BEFORE the ledger's fresh-cid update lands, so a
|
|
75
|
+
* generation check alone loses the race. Entries pruned after 5 min. */
|
|
76
|
+
private readonly ownCancels;
|
|
77
|
+
constructor(executor: HlBracketExecutor, ledger: BracketLedger, opts?: HlCoordinatorOpts);
|
|
78
|
+
getLedger(): BracketLedger;
|
|
79
|
+
/** Record a leg cancel WE initiated (see `ownCancels`). The adapter calls
|
|
80
|
+
* this for every deliberate leg cancel, including the resize path's direct
|
|
81
|
+
* api cancels, BEFORE the cancel request goes out. */
|
|
82
|
+
noteOwnLegCancel(cloid: string): void;
|
|
83
|
+
/** Same anti-clobber contract as BracketManager.registerEntry: a duplicate
|
|
84
|
+
* fill signal against a non-terminal row is a warned NO-OP, never a fresh
|
|
85
|
+
* bracketId that orphans the live legs' ledger identity. */
|
|
86
|
+
registerEntry(req: BracketRequest, bracketId: BracketId, entryCid: string): void;
|
|
87
|
+
/** Attach both legs (ONE batched signed action) with retries. Idempotent on
|
|
88
|
+
* a non-pending row. On exhaustion: ledger 'failed' + attach_failed event —
|
|
89
|
+
* the ADAPTER escalates to auto-flatten (it owns closePosition). */
|
|
90
|
+
attachOnFill(symbol: string, filledSize: number): Promise<HlAttachResult>;
|
|
91
|
+
/** ★ T-2: bring the legs to the CURRENT position size (scale-in / partial
|
|
92
|
+
* fill growth). Submit-first-then-cancel inside the adapter. Updates the
|
|
93
|
+
* ledger's qty + fresh cids on success. */
|
|
94
|
+
resizeToPosition(symbol: string, newPositionSize: number): Promise<{
|
|
95
|
+
resized: boolean;
|
|
96
|
+
}>;
|
|
97
|
+
/** Move the stop. HL ordering: submit the NEW leg first, then cancel the old
|
|
98
|
+
* one — over-protection for a moment beats a naked window (see header). */
|
|
99
|
+
modifyStop(symbol: string, newStopPrice: number): Promise<void>;
|
|
100
|
+
modifyTarget(symbol: string, newTargetPrice: number): Promise<void>;
|
|
101
|
+
private modifyLeg;
|
|
102
|
+
/** Cancel both legs, mark cancelled. Risk-reducing; best-effort per leg. */
|
|
103
|
+
cancelBrackets(symbol: string, reason: string): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* The HL analog of the Binance ALGO_UPDATE handler — `orderUpdates` is
|
|
106
|
+
* authoritative for bracket-leg lifecycle. Returns the transition applied
|
|
107
|
+
* (for the adapter to emit drift/close-bypass signals on triggers).
|
|
108
|
+
*/
|
|
109
|
+
handleOrderUpdate(update: HlOrderUpdateEvent): 'triggered_sl' | 'triggered_tp' | 'forced_close' | null;
|
|
110
|
+
/**
|
|
111
|
+
* ★ T-5 mandate: REST truth-check. Runs after every WS (re)connect AND on the
|
|
112
|
+
* periodic sweep. TRUSTED reads only — a null positions fetch skips the pass
|
|
113
|
+
* (never act on unknown). Self-heals: pending rows whose entry filled while
|
|
114
|
+
* we were blind get their legs attached; undersized legs get resized; flat
|
|
115
|
+
* positions get their rows closed out (T-1 already cancelled the legs).
|
|
116
|
+
* Returns symbols whose rows were closed out (close-bypass signals for the
|
|
117
|
+
* adapter's drift pipeline).
|
|
118
|
+
*/
|
|
119
|
+
resyncAgainstExchange(reasonTag: string): Promise<{
|
|
120
|
+
closedSymbols: string[];
|
|
121
|
+
}>;
|
|
122
|
+
private emitEvent;
|
|
123
|
+
}
|