@reefclaw/openclaw-plugin 0.1.3 → 0.1.5
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/ccxt/binance-public.d.ts +2 -1
- package/ccxt/intel-public.d.ts +25 -0
- package/ccxt/intel-public.js +80 -0
- package/ccxt/public-market-data-api.d.ts +12 -0
- package/ccxt/public-market-data-api.js +9 -0
- package/config/plugin-config-io.d.ts +15 -0
- package/index.js +125 -16
- package/ingest/position-auto-capture.d.ts +5 -0
- package/ingest/position-auto-capture.js +8 -2
- package/ingest/position-decisions-client.d.ts +4 -0
- package/ingest/readiness-reporter.d.ts +28 -5
- package/ingest/readiness-reporter.js +40 -19
- package/live/bracket-id.d.ts +9 -0
- package/live/bracket-id.js +18 -0
- package/package.json +2 -2
- package/persistence/state-manager.d.ts +24 -0
- package/persistence/state-manager.js +62 -4
- package/tools/close-position.d.ts +2 -2
- package/tools/create-order.d.ts +2 -2
- package/tools/fetch-ohlcv.d.ts +2 -2
- package/tools/fetch-ticker.d.ts +2 -2
- package/tools/get-crypto-metrics.d.ts +2 -2
- package/tools/get-crypto-metrics.js +10 -3
- package/tools/get-market-structure.d.ts +2 -2
- package/tools/get-orderbook.d.ts +2 -2
- package/tools/get-volume-analysis.d.ts +2 -2
- package/tools/helpers.d.ts +5 -4
- package/tools/helpers.js +2 -1
- package/types.d.ts +20 -1
- package/venues/hyperliquid/hl-public.d.ts +52 -0
- package/venues/hyperliquid/hl-public.js +285 -0
- package/venues/registry.d.ts +24 -0
- package/venues/registry.js +47 -0
package/live/bracket-id.js
CHANGED
|
@@ -79,3 +79,21 @@ export function parseBracketCid(cid) {
|
|
|
79
79
|
export function isBracketCid(cid) {
|
|
80
80
|
return CID_REGEX.test(cid) || LEGACY_CID_REGEX.test(cid);
|
|
81
81
|
}
|
|
82
|
+
// ---- Venue-dispatched recognition (multi-venue Phase 0 seam) ----
|
|
83
|
+
//
|
|
84
|
+
// ALL bracket client-order-id recognition stays centralized in THIS module —
|
|
85
|
+
// the brackets.md rule ("never hardcode an rc-/bkt regex elsewhere") extends
|
|
86
|
+
// per-venue. Binance = the bkt/rc- schemes above. Hyperliquid client order
|
|
87
|
+
// ids are 128-bit hex cloids with a different scheme (hl-cloid.ts, Phase 3 of
|
|
88
|
+
// docs/HYPERLIQUID_INTEGRATION_PLAN.md §5.7) and will be dispatched from
|
|
89
|
+
// here; until then the hyperliquid arm recognises nothing, which is correct —
|
|
90
|
+
// this build never emits an HL bracket order.
|
|
91
|
+
/** Venue-aware parseBracketCid. Binance delegates to the existing dual-scheme
|
|
92
|
+
* parser; other venues return null until their scheme ships. */
|
|
93
|
+
export function parseBracketClientId(venue, cid) {
|
|
94
|
+
return venue === 'binance' ? parseBracketCid(cid) : null;
|
|
95
|
+
}
|
|
96
|
+
/** Venue-aware isBracketCid. */
|
|
97
|
+
export function isBracketClientId(venue, cid) {
|
|
98
|
+
return venue === 'binance' ? isBracketCid(cid) : false;
|
|
99
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reefclaw/openclaw-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "ReefClaw trading plugin for OpenClaw \u2014 paper trading with real Binance market data, plus the ReefClaw dashboard connector (supervised by OpenClaw, no service manager needed). Install: /plugins install clawhub:@reefclaw/openclaw-plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@reefclaw/shared": "0.1.
|
|
25
|
+
"@reefclaw/shared": "0.1.1",
|
|
26
26
|
"ccxt": "4.5.37",
|
|
27
27
|
"json5": "2.2.3",
|
|
28
28
|
"ws": "8.19.0"
|
|
@@ -39,4 +39,28 @@ export declare class StateManager {
|
|
|
39
39
|
loadSync(): SimulatorState | null;
|
|
40
40
|
/** Synchronous load-or-create-default — for use in synchronous plugin register(). */
|
|
41
41
|
loadOrDefaultSync(startingBalance: number, quoteCurrency: string): SimulatorState;
|
|
42
|
+
private saveSyncSafe;
|
|
42
43
|
}
|
|
44
|
+
export interface PaperQuoteMigrationResult {
|
|
45
|
+
state: SimulatorState;
|
|
46
|
+
migrated: boolean;
|
|
47
|
+
fromQuote: string;
|
|
48
|
+
releasedPositions: number;
|
|
49
|
+
droppedOrders: number;
|
|
50
|
+
}
|
|
51
|
+
/** Migrate a persisted paper state whose quote currency no longer matches the
|
|
52
|
+
* configured venue's (issue #174 — e.g. a USDT wallet after switching to the
|
|
53
|
+
* USDC-quoted hyperliquid venue). Pure; returns the input untouched when the
|
|
54
|
+
* quotes already match.
|
|
55
|
+
*
|
|
56
|
+
* Semantics (equity-preserving):
|
|
57
|
+
* - Open positions are released at their entry price: opening deducted the
|
|
58
|
+
* full entry notional from the wallet (spot-collateral model), so that
|
|
59
|
+
* notional is credited back before the position rows — which reference the
|
|
60
|
+
* OLD venue's symbols and are unpriceable on the new one — are dropped.
|
|
61
|
+
* - Unfilled open orders never debited the wallet (fills do) — just dropped.
|
|
62
|
+
* - Balances carry 1:1 (USDT↔USDC are both dollar stables; paper precision),
|
|
63
|
+
* merging into any existing balance under the new quote.
|
|
64
|
+
* - Trade history is kept (display-only). savedAt is bumped so the other
|
|
65
|
+
* process's replaceState() staleness guard accepts the migrated snapshot. */
|
|
66
|
+
export declare function migratePaperQuoteCurrency(state: SimulatorState, expectedQuote: string): PaperQuoteMigrationResult;
|
|
@@ -144,11 +144,26 @@ export class StateManager {
|
|
|
144
144
|
/** Synchronous load-or-create-default — for use in synchronous plugin register(). */
|
|
145
145
|
loadOrDefaultSync(startingBalance, quoteCurrency) {
|
|
146
146
|
const loaded = this.loadSync();
|
|
147
|
-
if (loaded)
|
|
148
|
-
|
|
147
|
+
if (loaded) {
|
|
148
|
+
// Venue switch heal (issue #174): a persisted wallet quoted in the OLD
|
|
149
|
+
// venue's currency reads as $0 equity on the new venue. Migrate 1:1
|
|
150
|
+
// (USDT↔USDC), preserving total equity exactly.
|
|
151
|
+
const mig = migratePaperQuoteCurrency(loaded, quoteCurrency);
|
|
152
|
+
if (mig.migrated) {
|
|
153
|
+
logger.info(TAG, `Paper wallet quote migrated ${mig.fromQuote} → ${quoteCurrency} (venue switch): ` +
|
|
154
|
+
`released ${mig.releasedPositions} open position(s) at entry price, ` +
|
|
155
|
+
`dropped ${mig.droppedOrders} unfilled order(s), balances carried 1:1`);
|
|
156
|
+
this.saveSyncSafe(mig.state, 'migrated');
|
|
157
|
+
}
|
|
158
|
+
return mig.state;
|
|
159
|
+
}
|
|
149
160
|
const state = createDefaultState(startingBalance, quoteCurrency);
|
|
150
161
|
logger.info(TAG, `Initialized default state: ${startingBalance} ${quoteCurrency}`);
|
|
151
162
|
// Save synchronously so state persists immediately
|
|
163
|
+
this.saveSyncSafe(state, 'initial');
|
|
164
|
+
return state;
|
|
165
|
+
}
|
|
166
|
+
saveSyncSafe(state, label) {
|
|
152
167
|
try {
|
|
153
168
|
const dir = dirname(this.statePath);
|
|
154
169
|
if (!existsSync(dir)) {
|
|
@@ -157,8 +172,51 @@ export class StateManager {
|
|
|
157
172
|
writeFileSync(this.statePath, JSON.stringify(state, null, 2), 'utf-8');
|
|
158
173
|
}
|
|
159
174
|
catch (err) {
|
|
160
|
-
logger.error(TAG, `Failed to save
|
|
175
|
+
logger.error(TAG, `Failed to save ${label} state: ${err instanceof Error ? err.message : String(err)}`);
|
|
161
176
|
}
|
|
162
|
-
return state;
|
|
163
177
|
}
|
|
164
178
|
}
|
|
179
|
+
/** Migrate a persisted paper state whose quote currency no longer matches the
|
|
180
|
+
* configured venue's (issue #174 — e.g. a USDT wallet after switching to the
|
|
181
|
+
* USDC-quoted hyperliquid venue). Pure; returns the input untouched when the
|
|
182
|
+
* quotes already match.
|
|
183
|
+
*
|
|
184
|
+
* Semantics (equity-preserving):
|
|
185
|
+
* - Open positions are released at their entry price: opening deducted the
|
|
186
|
+
* full entry notional from the wallet (spot-collateral model), so that
|
|
187
|
+
* notional is credited back before the position rows — which reference the
|
|
188
|
+
* OLD venue's symbols and are unpriceable on the new one — are dropped.
|
|
189
|
+
* - Unfilled open orders never debited the wallet (fills do) — just dropped.
|
|
190
|
+
* - Balances carry 1:1 (USDT↔USDC are both dollar stables; paper precision),
|
|
191
|
+
* merging into any existing balance under the new quote.
|
|
192
|
+
* - Trade history is kept (display-only). savedAt is bumped so the other
|
|
193
|
+
* process's replaceState() staleness guard accepts the migrated snapshot. */
|
|
194
|
+
export function migratePaperQuoteCurrency(state, expectedQuote) {
|
|
195
|
+
const fromQuote = state.config?.quoteCurrency ?? 'USDT';
|
|
196
|
+
if (fromQuote === expectedQuote) {
|
|
197
|
+
return { state, migrated: false, fromQuote, releasedPositions: 0, droppedOrders: 0 };
|
|
198
|
+
}
|
|
199
|
+
const next = JSON.parse(JSON.stringify(state));
|
|
200
|
+
next.config = { ...next.config, quoteCurrency: expectedQuote };
|
|
201
|
+
const old = next.wallet[fromQuote] ?? { total: 0, available: 0, locked: 0 };
|
|
202
|
+
let releasedPositions = 0;
|
|
203
|
+
for (const pos of next.positions ?? []) {
|
|
204
|
+
const notional = pos.entryPrice * pos.quantity;
|
|
205
|
+
if (Number.isFinite(notional) && notional > 0) {
|
|
206
|
+
old.total += notional;
|
|
207
|
+
old.available += notional;
|
|
208
|
+
}
|
|
209
|
+
releasedPositions++;
|
|
210
|
+
}
|
|
211
|
+
const droppedOrders = (next.openOrders ?? []).length;
|
|
212
|
+
next.positions = [];
|
|
213
|
+
next.openOrders = [];
|
|
214
|
+
const target = next.wallet[expectedQuote] ?? { total: 0, available: 0, locked: 0 };
|
|
215
|
+
target.total += old.total;
|
|
216
|
+
target.available += old.available;
|
|
217
|
+
target.locked += old.locked;
|
|
218
|
+
next.wallet[expectedQuote] = target;
|
|
219
|
+
delete next.wallet[fromQuote];
|
|
220
|
+
next.savedAt = Date.now();
|
|
221
|
+
return { state: next, migrated: true, fromQuote, releasedPositions, droppedOrders };
|
|
222
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
import type { IExchangeAdapter } from '../exchange-adapter.js';
|
|
3
3
|
import type { CcxtOrder } from '../types.js';
|
|
4
4
|
import { type ClosePositionArgs } from './assessment-validation.js';
|
|
5
5
|
import { type AutoCaptureContext } from '../ingest/position-auto-capture.js';
|
|
6
6
|
import { type ExitGateMode } from '../config/position-review-config.js';
|
|
7
7
|
export declare function closePositionTool(args: ClosePositionArgs, deps: {
|
|
8
|
-
binanceApi:
|
|
8
|
+
binanceApi: PublicMarketDataApi;
|
|
9
9
|
adapter: IExchangeAdapter;
|
|
10
10
|
autoCapture?: AutoCaptureContext;
|
|
11
11
|
/** Override for tests — production reads from plugin-config.json on each
|
package/tools/create-order.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
import type { IExchangeAdapter } from '../exchange-adapter.js';
|
|
3
3
|
import type { CcxtOrder } from '../types.js';
|
|
4
4
|
import { type AutoCaptureContext } from '../ingest/position-auto-capture.js';
|
|
@@ -21,7 +21,7 @@ export declare function createOrderTool(args: {
|
|
|
21
21
|
realization_rule?: unknown;
|
|
22
22
|
supersedes_id?: string;
|
|
23
23
|
}, deps: {
|
|
24
|
-
binanceApi:
|
|
24
|
+
binanceApi: PublicMarketDataApi;
|
|
25
25
|
adapter: IExchangeAdapter;
|
|
26
26
|
autoCapture?: AutoCaptureContext;
|
|
27
27
|
/** Approval-mode wiring. The same ProposalManager handles both modes;
|
package/tools/fetch-ohlcv.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
import type { CcxtOHLCV } from '../types.js';
|
|
3
3
|
export declare function fetchOhlcvTool(args: {
|
|
4
4
|
symbol: string;
|
|
5
5
|
timeframe?: string;
|
|
6
6
|
limit?: number;
|
|
7
7
|
}, deps: {
|
|
8
|
-
binanceApi:
|
|
8
|
+
binanceApi: PublicMarketDataApi;
|
|
9
9
|
}): Promise<CcxtOHLCV[] | {
|
|
10
10
|
error: string;
|
|
11
11
|
}>;
|
package/tools/fetch-ticker.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
import type { ExchangeSimulator } from '../simulator/exchange-simulator.js';
|
|
3
3
|
import type { CcxtTicker } from '../types.js';
|
|
4
4
|
export declare function fetchTickerTool(args: {
|
|
5
5
|
symbol: string;
|
|
6
6
|
}, deps: {
|
|
7
|
-
binanceApi:
|
|
7
|
+
binanceApi: PublicMarketDataApi;
|
|
8
8
|
simulator: ExchangeSimulator;
|
|
9
9
|
}): Promise<CcxtTicker | {
|
|
10
10
|
error: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
export interface CryptoMetricsResult {
|
|
3
3
|
symbol: string;
|
|
4
4
|
fundingRate: number | null;
|
|
@@ -12,7 +12,7 @@ export interface CryptoMetricsResult {
|
|
|
12
12
|
export declare function getCryptoMetricsTool(args: {
|
|
13
13
|
symbol: string;
|
|
14
14
|
}, deps: {
|
|
15
|
-
binanceApi:
|
|
15
|
+
binanceApi: PublicMarketDataApi;
|
|
16
16
|
}): Promise<CryptoMetricsResult | {
|
|
17
17
|
error: string;
|
|
18
18
|
}>;
|
|
@@ -3,9 +3,16 @@
|
|
|
3
3
|
// because Binance's 24hr ticker endpoint does NOT include funding/OI data.
|
|
4
4
|
export async function getCryptoMetricsTool(args, deps) {
|
|
5
5
|
try {
|
|
6
|
-
// Funding rate & OI only exist on perpetual futures.
|
|
7
|
-
//
|
|
8
|
-
|
|
6
|
+
// Funding rate & OI only exist on perpetual futures. Convert a canonical
|
|
7
|
+
// symbol to its linear-perp ccxt form by settling in the QUOTE currency
|
|
8
|
+
// (BTC/USDT → BTC/USDT:USDT on Binance, BTC/USDC → BTC/USDC:USDC on
|
|
9
|
+
// Hyperliquid). A hardcoded :USDT suffix produced the nonsense form
|
|
10
|
+
// BTC/USDC:USDT on the HL venue (seen live, wisekid soak 2026-07-12) —
|
|
11
|
+
// never silently translate quote assets across venues.
|
|
12
|
+
const quote = args.symbol.split('/')[1];
|
|
13
|
+
const perpSymbol = args.symbol.includes(':') || !quote
|
|
14
|
+
? args.symbol
|
|
15
|
+
: `${args.symbol}:${quote}`;
|
|
9
16
|
// Fetch funding rate and open interest in parallel (separate API endpoints)
|
|
10
17
|
const [fundingData, oiData, tickerData] = await Promise.all([
|
|
11
18
|
deps.binanceApi.fetchFundingRate(perpSymbol),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
/**
|
|
3
3
|
* Average True Range using Wilder's smoothing.
|
|
4
4
|
* Input: OHLCV candles array, period (default 14).
|
|
@@ -41,7 +41,7 @@ export declare function getMarketStructureTool(args: {
|
|
|
41
41
|
symbol: string;
|
|
42
42
|
timeframes?: string[];
|
|
43
43
|
}, deps: {
|
|
44
|
-
binanceApi:
|
|
44
|
+
binanceApi: PublicMarketDataApi;
|
|
45
45
|
}): Promise<MarketStructureResult | {
|
|
46
46
|
error: string;
|
|
47
47
|
}>;
|
package/tools/get-orderbook.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
export interface OrderbookResult {
|
|
3
3
|
symbol: string;
|
|
4
4
|
bids: [number, number][];
|
|
@@ -15,7 +15,7 @@ export declare function getOrderbookTool(args: {
|
|
|
15
15
|
symbol: string;
|
|
16
16
|
depth?: number;
|
|
17
17
|
}, deps: {
|
|
18
|
-
binanceApi:
|
|
18
|
+
binanceApi: PublicMarketDataApi;
|
|
19
19
|
}): Promise<OrderbookResult | {
|
|
20
20
|
error: string;
|
|
21
21
|
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
export interface VolumeAnalysisResult {
|
|
3
3
|
symbol: string;
|
|
4
4
|
timeframe: string;
|
|
@@ -15,7 +15,7 @@ export declare function getVolumeAnalysisTool(args: {
|
|
|
15
15
|
symbol: string;
|
|
16
16
|
timeframe?: string;
|
|
17
17
|
}, deps: {
|
|
18
|
-
binanceApi:
|
|
18
|
+
binanceApi: PublicMarketDataApi;
|
|
19
19
|
}): Promise<VolumeAnalysisResult | {
|
|
20
20
|
error: string;
|
|
21
21
|
}>;
|
package/tools/helpers.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PublicMarketDataApi } from '../ccxt/public-market-data-api.js';
|
|
2
2
|
import type { ExchangeSimulator } from '../simulator/exchange-simulator.js';
|
|
3
3
|
import type { CcxtTicker } from '../types.js';
|
|
4
4
|
/**
|
|
5
|
-
* Fetch the latest ticker from
|
|
5
|
+
* Fetch the latest ticker from the configured market-data source (Binance, or
|
|
6
|
+
* intel in paper mode on a geo-blocked host) and update the simulator.
|
|
6
7
|
* Returns the ticker on success, or an error object on failure.
|
|
7
8
|
*/
|
|
8
9
|
export declare function fetchCurrentPrice(symbol: string, deps: {
|
|
9
|
-
binanceApi:
|
|
10
|
+
binanceApi: PublicMarketDataApi;
|
|
10
11
|
simulator: ExchangeSimulator;
|
|
11
12
|
}): Promise<CcxtTicker | {
|
|
12
13
|
error: string;
|
|
@@ -16,7 +17,7 @@ export declare function fetchCurrentPrice(symbol: string, deps: {
|
|
|
16
17
|
* Best-effort: never throws. Skips fetch if cached book is fresh enough.
|
|
17
18
|
*/
|
|
18
19
|
export declare function fetchOrderBook(symbol: string, deps: {
|
|
19
|
-
binanceApi:
|
|
20
|
+
binanceApi: PublicMarketDataApi;
|
|
20
21
|
simulator: ExchangeSimulator;
|
|
21
22
|
}): Promise<void>;
|
|
22
23
|
/** Type guard: check if the result is an error object */
|
package/tools/helpers.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Shared helpers for tool implementations.
|
|
2
2
|
/**
|
|
3
|
-
* Fetch the latest ticker from
|
|
3
|
+
* Fetch the latest ticker from the configured market-data source (Binance, or
|
|
4
|
+
* intel in paper mode on a geo-blocked host) and update the simulator.
|
|
4
5
|
* Returns the ticker on success, or an error object on failure.
|
|
5
6
|
*/
|
|
6
7
|
export async function fetchCurrentPrice(symbol, deps) {
|
package/types.d.ts
CHANGED
|
@@ -102,9 +102,28 @@ export interface PluginConfig {
|
|
|
102
102
|
quoteCurrency: string;
|
|
103
103
|
}
|
|
104
104
|
export declare const DEFAULT_CONFIG: PluginConfig;
|
|
105
|
-
/** Exchange credentials for shadow/live modes. Read from
|
|
105
|
+
/** Exchange credentials for shadow/live modes. Read from plugin-config.json's
|
|
106
|
+
* `exchange` block (openclaw.json legacy fallback). Which fields matter
|
|
107
|
+
* depends on `venue` (docs/HYPERLIQUID_INTEGRATION_PLAN.md §5.2):
|
|
108
|
+
*
|
|
109
|
+
* binance (default, and the only live-supported venue until Phase 3) —
|
|
110
|
+
* apiKey + secret (HMAC pair) are required; walletAddress/agentPrivateKey
|
|
111
|
+
* are ignored.
|
|
112
|
+
* hyperliquid — walletAddress (MASTER wallet address, 0x…, used for
|
|
113
|
+
* queries; never its private key) + agentPrivateKey (an approved
|
|
114
|
+
* agent/API wallet's key — signs orders, cannot withdraw). apiKey/secret
|
|
115
|
+
* are meaningless. Boot falls back to PAPER for this venue until the
|
|
116
|
+
* Phase 3 adapter ships.
|
|
117
|
+
*
|
|
118
|
+
* apiKey/secret stay required at the type level because every constructed
|
|
119
|
+
* instance today feeds BinancePrivateApi; the on-disk JSON is parsed, not
|
|
120
|
+
* type-constructed, so an HL block without them is readable. Phase 3
|
|
121
|
+
* restructures this into a per-venue discriminated union. */
|
|
106
122
|
export interface ExchangeConfig {
|
|
123
|
+
venue?: 'binance' | 'hyperliquid';
|
|
107
124
|
apiKey: string;
|
|
108
125
|
secret: string;
|
|
109
126
|
testnet?: boolean;
|
|
127
|
+
walletAddress?: string;
|
|
128
|
+
agentPrivateKey?: string;
|
|
110
129
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { CcxtTicker, CcxtOHLCV } from '../../types.js';
|
|
2
|
+
import type { OrderBookDepth } from '../../simulator/types.js';
|
|
3
|
+
import type { PublicMarketDataApi } from '../../ccxt/public-market-data-api.js';
|
|
4
|
+
export interface HyperliquidPublicApiOptions {
|
|
5
|
+
testnet?: boolean;
|
|
6
|
+
/** Test seam — injected ccxt exchange instance. */
|
|
7
|
+
exchange?: any;
|
|
8
|
+
fetchImpl?: typeof fetch;
|
|
9
|
+
}
|
|
10
|
+
export declare class HyperliquidPublicApi implements PublicMarketDataApi {
|
|
11
|
+
private exchange;
|
|
12
|
+
private readonly testnet;
|
|
13
|
+
private readonly fetchImpl;
|
|
14
|
+
private readonly tickerTtlMs;
|
|
15
|
+
/** One upstream fetchTickers call serves every symbol within the TTL. */
|
|
16
|
+
private tickersCache;
|
|
17
|
+
private tickersInflight;
|
|
18
|
+
constructor(opts?: HyperliquidPublicApiOptions);
|
|
19
|
+
private baseUrl;
|
|
20
|
+
/** Canonical/ccxt symbol → this venue's ccxt symbol, or null (logged) when
|
|
21
|
+
* the symbol isn't a Hyperliquid USDC perp — a caller bug we surface
|
|
22
|
+
* loudly rather than silently translating quote assets. */
|
|
23
|
+
private venueSymbol;
|
|
24
|
+
/** Fetch-all-tickers with a short TTL + inflight dedup. Returns a map keyed
|
|
25
|
+
* by ccxt symbol, or null on failure. */
|
|
26
|
+
private getTickers;
|
|
27
|
+
fetchTickerRaw(symbol: string): Promise<Record<string, any> | null>;
|
|
28
|
+
/** Ticker from the cached all-assets snapshot. Hyperliquid's asset contexts
|
|
29
|
+
* carry mark/mid rather than a trade-tape bid/ask; absent fields fall back
|
|
30
|
+
* to `last` with zero modeled spread — the paper fill engine models
|
|
31
|
+
* slippage itself (same convention as IntelPublicApi). */
|
|
32
|
+
fetchTicker(symbol: string): Promise<CcxtTicker | null>;
|
|
33
|
+
fetchFundingRate(symbol: string): Promise<Record<string, any> | null>;
|
|
34
|
+
fetchOpenInterest(symbol: string): Promise<Record<string, any> | null>;
|
|
35
|
+
/** l2Book — Hyperliquid serves at most 20 levels/side (plan §3.6). */
|
|
36
|
+
fetchOrderBook(symbol: string, limit?: number): Promise<OrderBookDepth | null>;
|
|
37
|
+
/** candleSnapshot — only the most recent 5000 candles exist per (coin,
|
|
38
|
+
* interval) (plan §3.6); requests inside that window behave like Binance. */
|
|
39
|
+
fetchOHLCV(symbol: string, timeframe?: string, limit?: number): Promise<CcxtOHLCV[] | null>;
|
|
40
|
+
/** Probe Hyperliquid reachability from this host — the readiness gate's
|
|
41
|
+
* venue signal, mirroring BinancePublicApi.probeReachability's outcome
|
|
42
|
+
* shape. Hand-rolled POST /info `exchangeStatus` (weight 2) so the probe
|
|
43
|
+
* has zero ccxt-method-shape dependence; the response's `time` field
|
|
44
|
+
* (verified live 2026-07-11: `{"specialStatuses":null,"time":…}`) doubles
|
|
45
|
+
* as the clock-drift source. Geo classification is best-effort — HL's
|
|
46
|
+
* API-level geo behavior is UNVERIFIED (plan §3.9); a 403/451 maps to
|
|
47
|
+
* geo_blocked, anything else non-2xx/network maps to unreachable. */
|
|
48
|
+
probeReachability(): Promise<{
|
|
49
|
+
outcome: 'reachable' | 'geo_blocked' | 'unreachable' | 'unknown';
|
|
50
|
+
driftMs: number | null;
|
|
51
|
+
}>;
|
|
52
|
+
}
|