@reefclaw/openclaw-plugin 0.1.6 → 0.1.8
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/public-market-data-api.d.ts +14 -0
- package/ccxt/public-market-data-api.js +15 -1
- package/config/plugin-config-io.d.ts +7 -0
- package/config/plugin-config-io.js +15 -0
- package/index.js +116 -31
- 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/live/fill-price.d.ts +13 -0
- package/live/fill-price.js +37 -0
- package/live/live-adapter.d.ts +33 -1
- package/live/live-adapter.js +176 -47
- package/live/position-state-store.d.ts +4 -0
- package/live/stop-watcher.d.ts +8 -1
- package/live/stop-watcher.js +5 -2
- package/onboarding/runtime.d.ts +18 -0
- package/onboarding/runtime.js +45 -3
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/portfolio/reentry-tracker.d.ts +36 -0
- package/portfolio/reentry-tracker.js +127 -0
- package/scripts/assemble.mjs +18 -2
- package/signals/conditions/registry.js +11 -2
- package/signals/strategy-adapter.js +17 -7
- package/simulator/exchange-simulator.d.ts +12 -0
- package/simulator/exchange-simulator.js +73 -3
- package/simulator/types.d.ts +4 -0
- package/skills/reefclaw/SKILL.md +2 -0
- package/tools/assessment-validation.d.ts +21 -0
- package/tools/assessment-validation.js +58 -0
- package/tools/attach-brackets.js +165 -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/create-order.d.ts +7 -0
- package/tools/create-order.js +42 -3
- package/tools/get-setup-detail.js +12 -1
- package/tools/modify-stop.js +5 -5
- package/tools/modify-target.js +5 -5
- package/tools/scan-pairs.d.ts +4 -0
- package/tools/scan-pairs.js +4 -1
- package/tools/set-trading-mode.js +23 -6
- package/venues/hyperliquid/hl-bracket-coordinator.d.ts +123 -0
- package/venues/hyperliquid/hl-bracket-coordinator.js +533 -0
- package/venues/hyperliquid/hl-live-adapter.d.ts +61 -3
- package/venues/hyperliquid/hl-live-adapter.js +380 -5
- package/venues/hyperliquid/hl-public.js +8 -1
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// because the caller chain is currently controlled end-to-end by the skill.
|
|
10
10
|
import { readPluginConfig } from '../config/plugin-config-io.js';
|
|
11
11
|
import { validateModeTransition, modeRequiresCredentials } from '../onboarding/mode-ladder.js';
|
|
12
|
+
import { parseVenue } from '../venues/registry.js';
|
|
12
13
|
import { logger } from '../logger.js';
|
|
13
14
|
import { recordModeTransition } from '../audit/mode-transition-audit.js';
|
|
14
15
|
const TAG = 'set-trading-mode';
|
|
@@ -49,12 +50,26 @@ export async function setTradingModeTool(args, deps) {
|
|
|
49
50
|
readiness: deps.runtime.adapter.readiness,
|
|
50
51
|
};
|
|
51
52
|
}
|
|
52
|
-
// 3. Credential requirement check
|
|
53
|
+
// 3. Credential requirement check — PER VENUE (issue #217: this gate was
|
|
54
|
+
// Binance-only, so the go-live flip on the hyperliquid venue was rejected
|
|
55
|
+
// despite valid walletAddress+agentPrivateKey in plugin-config).
|
|
53
56
|
let exchange = null;
|
|
57
|
+
let hlCredentials = null;
|
|
58
|
+
let venue = 'binance';
|
|
54
59
|
if (modeRequiresCredentials(target)) {
|
|
55
60
|
try {
|
|
56
61
|
const cfg = readPluginConfig(deps.configPath);
|
|
57
|
-
|
|
62
|
+
venue = parseVenue(cfg.exchange?.venue).venue;
|
|
63
|
+
if (venue === 'hyperliquid') {
|
|
64
|
+
if (cfg.exchange?.walletAddress && cfg.exchange?.agentPrivateKey) {
|
|
65
|
+
hlCredentials = {
|
|
66
|
+
walletAddress: String(cfg.exchange.walletAddress),
|
|
67
|
+
agentPrivateKey: String(cfg.exchange.agentPrivateKey),
|
|
68
|
+
testnet: cfg.exchange.testnet === true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (cfg.exchange?.apiKey && cfg.exchange?.secret) {
|
|
58
73
|
exchange = {
|
|
59
74
|
apiKey: cfg.exchange.apiKey,
|
|
60
75
|
secret: cfg.exchange.secret,
|
|
@@ -74,11 +89,13 @@ export async function setTradingModeTool(args, deps) {
|
|
|
74
89
|
reason: 'config_read_error',
|
|
75
90
|
};
|
|
76
91
|
}
|
|
77
|
-
if (!exchange) {
|
|
92
|
+
if (!exchange && !hlCredentials) {
|
|
78
93
|
recordModeTransition({ previousMode, targetMode: target, acknowledged, ok: false, reason: 'missing_credentials' });
|
|
79
94
|
return {
|
|
80
95
|
ok: false,
|
|
81
|
-
message:
|
|
96
|
+
message: venue === 'hyperliquid'
|
|
97
|
+
? `${target} mode on Hyperliquid requires exchange.walletAddress (master) + exchange.agentPrivateKey (agent wallet) in plugin-config.`
|
|
98
|
+
: `${target} mode requires exchange credentials. Run set_exchange_credentials first.`,
|
|
82
99
|
previousMode,
|
|
83
100
|
mode: previousMode,
|
|
84
101
|
readiness: deps.runtime.adapter.readiness,
|
|
@@ -116,14 +133,14 @@ export async function setTradingModeTool(args, deps) {
|
|
|
116
133
|
};
|
|
117
134
|
}
|
|
118
135
|
// 6. Swap the adapter.
|
|
119
|
-
await deps.runtime.reconnect({ mode: target, exchange }, { adapterDeps: deps.adapterDeps });
|
|
136
|
+
await deps.runtime.reconnect({ mode: target, exchange, venue, hlCredentials }, { adapterDeps: deps.adapterDeps });
|
|
120
137
|
logger.info(TAG, `Transitioned ${previousMode} → ${target}`);
|
|
121
138
|
recordModeTransition({
|
|
122
139
|
previousMode,
|
|
123
140
|
targetMode: target,
|
|
124
141
|
acknowledged,
|
|
125
142
|
ok: true,
|
|
126
|
-
testnet: exchange?.testnet,
|
|
143
|
+
testnet: exchange?.testnet ?? hlCredentials?.testnet,
|
|
127
144
|
});
|
|
128
145
|
return {
|
|
129
146
|
ok: true,
|
|
@@ -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
|
+
}
|