@123456btc/123456btc-cli 1.0.5 → 1.0.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/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +27 -9
- package/dist/api/client.js.map +1 -1
- package/dist/api/signals.d.ts +3 -3
- package/dist/api/signals.d.ts.map +1 -1
- package/dist/api/signals.js +3 -3
- package/dist/api/signals.js.map +1 -1
- package/dist/api/strategy-stream.d.ts +4 -9
- package/dist/api/strategy-stream.d.ts.map +1 -1
- package/dist/api/strategy-stream.js +14 -13
- package/dist/api/strategy-stream.js.map +1 -1
- package/dist/api/strategy.d.ts +3 -1
- package/dist/api/strategy.d.ts.map +1 -1
- package/dist/api/strategy.js +3 -1
- package/dist/api/strategy.js.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/auth.js +1 -1
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/exchange.d.ts +8 -0
- package/dist/commands/exchange.d.ts.map +1 -0
- package/dist/commands/exchange.js +146 -0
- package/dist/commands/exchange.js.map +1 -0
- package/dist/commands/gas.js +1 -1
- package/dist/commands/gas.js.map +1 -1
- package/dist/commands/install.js +1 -1
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/provider.js +1 -1
- package/dist/commands/provider.js.map +1 -1
- package/dist/commands/signals.js +8 -8
- package/dist/commands/signals.js.map +1 -1
- package/dist/commands/strategy.d.ts.map +1 -1
- package/dist/commands/strategy.js +182 -9
- package/dist/commands/strategy.js.map +1 -1
- package/dist/commands/tier.js +1 -1
- package/dist/commands/tier.js.map +1 -1
- package/dist/commands/vault.d.ts.map +1 -1
- package/dist/commands/vault.js +37 -4
- package/dist/commands/vault.js.map +1 -1
- package/dist/commands/wallet.js +1 -1
- package/dist/commands/wallet.js.map +1 -1
- package/dist/config/strategy-exchange.d.ts +30 -0
- package/dist/config/strategy-exchange.d.ts.map +1 -0
- package/dist/config/strategy-exchange.js +130 -0
- package/dist/config/strategy-exchange.js.map +1 -0
- package/dist/exchange/index.d.ts +106 -0
- package/dist/exchange/index.d.ts.map +1 -0
- package/dist/exchange/index.js +43 -0
- package/dist/exchange/index.js.map +1 -0
- package/dist/exchange/v2/adapters/base.d.ts +130 -0
- package/dist/exchange/v2/adapters/base.d.ts.map +1 -0
- package/dist/exchange/v2/adapters/base.js +296 -0
- package/dist/exchange/v2/adapters/base.js.map +1 -0
- package/dist/exchange/v2/adapters/binance.d.ts +51 -0
- package/dist/exchange/v2/adapters/binance.d.ts.map +1 -0
- package/dist/exchange/v2/adapters/binance.js +549 -0
- package/dist/exchange/v2/adapters/binance.js.map +1 -0
- package/dist/exchange/v2/adapters/bitget.d.ts +43 -0
- package/dist/exchange/v2/adapters/bitget.d.ts.map +1 -0
- package/dist/exchange/v2/adapters/bitget.js +524 -0
- package/dist/exchange/v2/adapters/bitget.js.map +1 -0
- package/dist/exchange/v2/adapters/hyperliquid.d.ts +102 -0
- package/dist/exchange/v2/adapters/hyperliquid.d.ts.map +1 -0
- package/dist/exchange/v2/adapters/hyperliquid.js +536 -0
- package/dist/exchange/v2/adapters/hyperliquid.js.map +1 -0
- package/dist/exchange/v2/adapters/okx.d.ts +38 -0
- package/dist/exchange/v2/adapters/okx.d.ts.map +1 -0
- package/dist/exchange/v2/adapters/okx.js +437 -0
- package/dist/exchange/v2/adapters/okx.js.map +1 -0
- package/dist/exchange/v2/algorithms/index.d.ts +78 -0
- package/dist/exchange/v2/algorithms/index.d.ts.map +1 -0
- package/dist/exchange/v2/algorithms/index.js +441 -0
- package/dist/exchange/v2/algorithms/index.js.map +1 -0
- package/dist/exchange/v2/executor/circuit-breaker.d.ts +44 -0
- package/dist/exchange/v2/executor/circuit-breaker.d.ts.map +1 -0
- package/dist/exchange/v2/executor/circuit-breaker.js +90 -0
- package/dist/exchange/v2/executor/circuit-breaker.js.map +1 -0
- package/dist/exchange/v2/executor/core.d.ts +64 -0
- package/dist/exchange/v2/executor/core.d.ts.map +1 -0
- package/dist/exchange/v2/executor/core.js +458 -0
- package/dist/exchange/v2/executor/core.js.map +1 -0
- package/dist/exchange/v2/executor/risk.d.ts +29 -0
- package/dist/exchange/v2/executor/risk.d.ts.map +1 -0
- package/dist/exchange/v2/executor/risk.js +144 -0
- package/dist/exchange/v2/executor/risk.js.map +1 -0
- package/dist/exchange/v2/executor/state.d.ts +29 -0
- package/dist/exchange/v2/executor/state.d.ts.map +1 -0
- package/dist/exchange/v2/executor/state.js +171 -0
- package/dist/exchange/v2/executor/state.js.map +1 -0
- package/dist/exchange/v2/index.d.ts +50 -0
- package/dist/exchange/v2/index.d.ts.map +1 -0
- package/dist/exchange/v2/index.js +82 -0
- package/dist/exchange/v2/index.js.map +1 -0
- package/dist/exchange/v2/registry.d.ts +50 -0
- package/dist/exchange/v2/registry.d.ts.map +1 -0
- package/dist/exchange/v2/registry.js +101 -0
- package/dist/exchange/v2/registry.js.map +1 -0
- package/dist/exchange/v2/types.d.ts +413 -0
- package/dist/exchange/v2/types.d.ts.map +1 -0
- package/dist/exchange/v2/types.js +3 -0
- package/dist/exchange/v2/types.js.map +1 -0
- package/dist/index.d.ts +105 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -37
- package/dist/index.js.map +1 -1
- package/dist/types/financial.d.ts +14 -0
- package/dist/types/financial.d.ts.map +1 -0
- package/dist/types/financial.js +6 -0
- package/dist/types/financial.js.map +1 -0
- package/dist/vault/crypto.js +3 -3
- package/dist/vault/crypto.js.map +1 -1
- package/dist/vault/index.d.ts +28 -0
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +70 -0
- package/dist/vault/index.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hyperliquid Exchange Adapter — Institutional v2
|
|
3
|
+
* Supports: USDC perpetuals (all markets are cross-margin)
|
|
4
|
+
* Auth: EIP-712 secp256k1 signing (NOT HMAC)
|
|
5
|
+
* Note: Hyperliquid uses a custom signing scheme compatible with Ethereum wallets.
|
|
6
|
+
* Because Cloudflare Workers does not have Node.js `crypto` or `ethers.js` natively,
|
|
7
|
+
* we expose a `signerFn` hook — callers provide their own signing function.
|
|
8
|
+
* A default `rawPrivateKeySignerFactory` is provided for environments that
|
|
9
|
+
* support SubtleCrypto P-256 as a proxy (see caveat below).
|
|
10
|
+
*
|
|
11
|
+
* IMPORTANT: For production use with real private keys, inject a signer via
|
|
12
|
+
* `HyperliquidAdapter.signerFn` that calls an ethers.js / viem signer,
|
|
13
|
+
* or use the Hyperliquid Agent wallet pattern.
|
|
14
|
+
*/
|
|
15
|
+
import { BaseExchangeAdapter } from './base';
|
|
16
|
+
import type { ExchangeId, MarketType, TickerInfo, SymbolInfo, OHLCVBar, OrderBook, FundingInfo, AccountSnapshot, PositionInfo, OrderResponse, RawOrderParams, ExchangeCredentials } from '../types';
|
|
17
|
+
interface HlOrderWire {
|
|
18
|
+
a: number;
|
|
19
|
+
b: boolean;
|
|
20
|
+
p: string;
|
|
21
|
+
s: string;
|
|
22
|
+
r: boolean;
|
|
23
|
+
t: {
|
|
24
|
+
limit?: {
|
|
25
|
+
tif: string;
|
|
26
|
+
};
|
|
27
|
+
} | {
|
|
28
|
+
trigger?: {
|
|
29
|
+
isMarket: boolean;
|
|
30
|
+
tpsl: string;
|
|
31
|
+
triggerPx: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
c: string;
|
|
35
|
+
}
|
|
36
|
+
interface HlAction {
|
|
37
|
+
type: string;
|
|
38
|
+
orders?: HlOrderWire[];
|
|
39
|
+
cancels?: Array<{
|
|
40
|
+
a: number;
|
|
41
|
+
o: number;
|
|
42
|
+
}>;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Signer function type — accepts the EIP-712 action hash payload and returns
|
|
47
|
+
* { r, s, v } as a hex signature string (65 bytes = 130 hex chars).
|
|
48
|
+
* Callers must inject this in environments where secp256k1 is available.
|
|
49
|
+
*/
|
|
50
|
+
export type HyperliquidSignerFn = (action: HlAction, nonce: number, vaultAddress?: string) => Promise<string>;
|
|
51
|
+
export declare class HyperliquidAdapter extends BaseExchangeAdapter {
|
|
52
|
+
readonly exchangeId: ExchangeId;
|
|
53
|
+
private readonly walletAddress;
|
|
54
|
+
private readonly signerFn;
|
|
55
|
+
/** Asset name → index cache (populated on first use) */
|
|
56
|
+
private assetCache;
|
|
57
|
+
private assetInfoCache;
|
|
58
|
+
constructor(creds: ExchangeCredentials,
|
|
59
|
+
/** Inject your signer here. See HyperliquidSignerFn. */
|
|
60
|
+
signerFn?: HyperliquidSignerFn);
|
|
61
|
+
/**
|
|
62
|
+
* Fallback signer: uses HMAC-SHA256 as a PLACEHOLDER.
|
|
63
|
+
* This will NOT be accepted by Hyperliquid for real orders.
|
|
64
|
+
* Replace with a real EIP-712 secp256k1 signer in production.
|
|
65
|
+
*/
|
|
66
|
+
private fallbackSigner;
|
|
67
|
+
private hlInfo;
|
|
68
|
+
private hlExchange;
|
|
69
|
+
private ensureAssetCache;
|
|
70
|
+
private getAssetIndex;
|
|
71
|
+
private normalizeCoin;
|
|
72
|
+
/** Format Hyperliquid coin to unified symbol. */
|
|
73
|
+
private formatHlSymbol;
|
|
74
|
+
/** Map Hyperliquid raw status to unified OrderStatus. */
|
|
75
|
+
private mapHlStatus;
|
|
76
|
+
/** Map Hyperliquid open-order / historical-order item to OrderResponse. */
|
|
77
|
+
private mapHlOrderResponse;
|
|
78
|
+
/** Map Hyperliquid fill to unified Fill. */
|
|
79
|
+
private mapHlFillResponse;
|
|
80
|
+
/** Format price for Hyperliquid wire. HL uses 6 decimals for most assets. */
|
|
81
|
+
private formatHlPrice;
|
|
82
|
+
/** Format size for Hyperliquid wire based on asset szDecimals. */
|
|
83
|
+
private formatHlSize;
|
|
84
|
+
getTicker(symbol: string): Promise<TickerInfo>;
|
|
85
|
+
getSymbolInfo(symbol: string): Promise<SymbolInfo>;
|
|
86
|
+
getOHLCV(symbol: string, timeframe: string, limit?: number): Promise<OHLCVBar[]>;
|
|
87
|
+
getOrderBook(symbol: string, _depth?: number): Promise<OrderBook>;
|
|
88
|
+
getFundingInfo(symbol: string): Promise<FundingInfo | null>;
|
|
89
|
+
listSymbols(_marketType: MarketType): Promise<string[]>;
|
|
90
|
+
getAccount(): Promise<AccountSnapshot>;
|
|
91
|
+
getPositions(symbol?: string): Promise<PositionInfo[]>;
|
|
92
|
+
setLeverage(symbol: string, leverage: number, marginMode?: 'cross' | 'isolated'): Promise<void>;
|
|
93
|
+
placeOrder(params: RawOrderParams): Promise<OrderResponse>;
|
|
94
|
+
cancelOrder(orderId: string, symbol: string): Promise<boolean>;
|
|
95
|
+
cancelAllOrders(_request?: import('../types').CancelAllOrdersRequest): Promise<boolean>;
|
|
96
|
+
getOrderStatus(orderId: string, symbol: string): Promise<OrderResponse>;
|
|
97
|
+
getOpenOrders(symbol?: string): Promise<OrderResponse[]>;
|
|
98
|
+
getOrderHistory(_query: import('../types').OrderHistoryQuery): Promise<import('../types').OrderHistoryResponse>;
|
|
99
|
+
getFills(query: import('../types').TradeHistoryQuery): Promise<import('../types').TradeHistoryResponse>;
|
|
100
|
+
}
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=hyperliquid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hyperliquid.d.ts","sourceRoot":"","sources":["../../../../src/exchange/v2/adapters/hyperliquid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hyperliquid Exchange Adapter — Institutional v2
|
|
4
|
+
* Supports: USDC perpetuals (all markets are cross-margin)
|
|
5
|
+
* Auth: EIP-712 secp256k1 signing (NOT HMAC)
|
|
6
|
+
* Note: Hyperliquid uses a custom signing scheme compatible with Ethereum wallets.
|
|
7
|
+
* Because Cloudflare Workers does not have Node.js `crypto` or `ethers.js` natively,
|
|
8
|
+
* we expose a `signerFn` hook — callers provide their own signing function.
|
|
9
|
+
* A default `rawPrivateKeySignerFactory` is provided for environments that
|
|
10
|
+
* support SubtleCrypto P-256 as a proxy (see caveat below).
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: For production use with real private keys, inject a signer via
|
|
13
|
+
* `HyperliquidAdapter.signerFn` that calls an ethers.js / viem signer,
|
|
14
|
+
* or use the Hyperliquid Agent wallet pattern.
|
|
15
|
+
*/
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.HyperliquidAdapter = void 0;
|
|
21
|
+
const base_1 = require("./base");
|
|
22
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
23
|
+
const HL_BASE = 'https://api.hyperliquid.xyz';
|
|
24
|
+
const HL_TESTNET = 'https://api.hyperliquid-testnet.xyz';
|
|
25
|
+
// ─── Adapter ──────────────────────────────────────────────────────────────
|
|
26
|
+
class HyperliquidAdapter extends base_1.BaseExchangeAdapter {
|
|
27
|
+
exchangeId = 'hyperliquid';
|
|
28
|
+
walletAddress;
|
|
29
|
+
signerFn;
|
|
30
|
+
/** Asset name → index cache (populated on first use) */
|
|
31
|
+
assetCache = new Map();
|
|
32
|
+
assetInfoCache = new Map();
|
|
33
|
+
constructor(creds,
|
|
34
|
+
/** Inject your signer here. See HyperliquidSignerFn. */
|
|
35
|
+
signerFn) {
|
|
36
|
+
super({
|
|
37
|
+
credentials: creds,
|
|
38
|
+
baseUrl: creds.testnet ? HL_TESTNET : HL_BASE,
|
|
39
|
+
});
|
|
40
|
+
// walletAddress = apiKey field (0x... Ethereum address)
|
|
41
|
+
this.walletAddress = (creds.apiKey ?? '').trim();
|
|
42
|
+
this.signerFn = signerFn ?? this.fallbackSigner.bind(this);
|
|
43
|
+
}
|
|
44
|
+
// ─── Signer ───────────────────────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Fallback signer: uses HMAC-SHA256 as a PLACEHOLDER.
|
|
47
|
+
* This will NOT be accepted by Hyperliquid for real orders.
|
|
48
|
+
* Replace with a real EIP-712 secp256k1 signer in production.
|
|
49
|
+
*/
|
|
50
|
+
async fallbackSigner(action, nonce) {
|
|
51
|
+
// Build the EIP-712 phantom agent hash (Hyperliquid-specific)
|
|
52
|
+
const enc = new TextEncoder();
|
|
53
|
+
const payload = JSON.stringify({ action, nonce, vaultAddress: null });
|
|
54
|
+
const secret = this.creds.privateKey?.startsWith('0x')
|
|
55
|
+
? this.creds.privateKey.slice(2)
|
|
56
|
+
: this.creds.privateKey ?? this.creds.apiSecret ?? '';
|
|
57
|
+
// Decode hex key
|
|
58
|
+
const keyBytes = secret.match(/.{1,2}/g)?.map(b => parseInt(b, 16));
|
|
59
|
+
if (!keyBytes || keyBytes.length < 32) {
|
|
60
|
+
throw new base_1.ExchangeError('Hyperliquid requires a valid 32-byte secp256k1 private key. ' +
|
|
61
|
+
'Please inject a real EIP-712 signer via the signerFn parameter.', 'hyperliquid');
|
|
62
|
+
}
|
|
63
|
+
const key = await crypto.subtle.importKey('raw', new Uint8Array(keyBytes), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
64
|
+
const sig = await crypto.subtle.sign('HMAC', key, enc.encode(payload));
|
|
65
|
+
const hex = Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
66
|
+
// Return as 65-byte placeholder (will fail on HL — this is intentional)
|
|
67
|
+
return `0x${hex.padEnd(130, '0')}`;
|
|
68
|
+
}
|
|
69
|
+
// ─── HTTP ─────────────────────────────────────────────────────────────────
|
|
70
|
+
async hlInfo(body) {
|
|
71
|
+
const res = await fetch(`${this.baseUrl}/info`, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: { 'Content-Type': 'application/json' },
|
|
74
|
+
body: JSON.stringify(body),
|
|
75
|
+
});
|
|
76
|
+
const text = await res.text();
|
|
77
|
+
let json;
|
|
78
|
+
try {
|
|
79
|
+
json = JSON.parse(text);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
json = text;
|
|
83
|
+
}
|
|
84
|
+
if (!res.ok || json?.status === 'err') {
|
|
85
|
+
throw new base_1.ExchangeError(JSON.stringify(json), 'hyperliquid');
|
|
86
|
+
}
|
|
87
|
+
return json;
|
|
88
|
+
}
|
|
89
|
+
async hlExchange(action) {
|
|
90
|
+
const nonce = Date.now();
|
|
91
|
+
const signature = await this.signerFn(action, nonce);
|
|
92
|
+
const res = await fetch(`${this.baseUrl}/exchange`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: { 'Content-Type': 'application/json' },
|
|
95
|
+
body: JSON.stringify({ action, nonce, signature }),
|
|
96
|
+
});
|
|
97
|
+
const text = await res.text();
|
|
98
|
+
let json;
|
|
99
|
+
try {
|
|
100
|
+
json = JSON.parse(text);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
json = text;
|
|
104
|
+
}
|
|
105
|
+
if (!res.ok)
|
|
106
|
+
throw new base_1.ExchangeError(JSON.stringify(json), 'hyperliquid');
|
|
107
|
+
const r = json;
|
|
108
|
+
if (r.status === 'err')
|
|
109
|
+
throw new base_1.ExchangeError(JSON.stringify(json), 'hyperliquid');
|
|
110
|
+
return json;
|
|
111
|
+
}
|
|
112
|
+
// ─── Asset Registry ───────────────────────────────────────────────────────
|
|
113
|
+
async ensureAssetCache() {
|
|
114
|
+
if (this.assetCache.size > 0)
|
|
115
|
+
return;
|
|
116
|
+
const meta = await this.hlInfo({ type: 'meta' });
|
|
117
|
+
const universe = meta[0]?.universe ?? [];
|
|
118
|
+
universe.forEach((asset, idx) => {
|
|
119
|
+
this.assetCache.set(asset.name, idx);
|
|
120
|
+
this.assetCache.set(`${asset.name}-USDC`, idx);
|
|
121
|
+
this.assetCache.set(`${asset.name}USDT`, idx);
|
|
122
|
+
this.assetInfoCache.set(asset.name, { szDecimals: asset.szDecimals, maxLeverage: asset.maxLeverage });
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async getAssetIndex(symbol) {
|
|
126
|
+
await this.ensureAssetCache();
|
|
127
|
+
const coin = symbol.replace(/[-/](USDC|USDT|USD|PERP)$/i, '');
|
|
128
|
+
const idx = this.assetCache.get(symbol) ?? this.assetCache.get(coin);
|
|
129
|
+
if (idx === undefined)
|
|
130
|
+
throw new base_1.ExchangeError(`Unknown symbol: ${symbol}`, 'hyperliquid');
|
|
131
|
+
return idx;
|
|
132
|
+
}
|
|
133
|
+
normalizeCoin(symbol) {
|
|
134
|
+
return symbol.replace(/[-/](USDC|USDT|USD|PERP)$/i, '');
|
|
135
|
+
}
|
|
136
|
+
/** Format Hyperliquid coin to unified symbol. */
|
|
137
|
+
formatHlSymbol(coin) {
|
|
138
|
+
if (coin.includes('/'))
|
|
139
|
+
return coin;
|
|
140
|
+
if (coin.startsWith('@'))
|
|
141
|
+
return coin;
|
|
142
|
+
return `${coin}-USDC`;
|
|
143
|
+
}
|
|
144
|
+
/** Map Hyperliquid raw status to unified OrderStatus. */
|
|
145
|
+
mapHlStatus(raw) {
|
|
146
|
+
const m = {
|
|
147
|
+
filled: 'filled',
|
|
148
|
+
canceled: 'cancelled',
|
|
149
|
+
open: 'open',
|
|
150
|
+
iocCancelRejected: 'rejected',
|
|
151
|
+
positionIncreaseAtOpenInterestCapRejected: 'rejected',
|
|
152
|
+
selfTradeCanceled: 'cancelled',
|
|
153
|
+
};
|
|
154
|
+
return m[raw] ?? this.normaliseStatus(raw);
|
|
155
|
+
}
|
|
156
|
+
/** Map Hyperliquid open-order / historical-order item to OrderResponse. */
|
|
157
|
+
mapHlOrderResponse(order, status, statusTimestamp) {
|
|
158
|
+
const origQty = parseFloat(order.origSz ?? order.sz ?? '0');
|
|
159
|
+
const remainingQty = parseFloat(order.sz ?? '0');
|
|
160
|
+
const filledQty = Math.max(0, origQty - remainingQty);
|
|
161
|
+
const price = parseFloat(order.limitPx ?? '0');
|
|
162
|
+
return {
|
|
163
|
+
orderId: String(order.oid),
|
|
164
|
+
clientOrderId: order.cloid ?? undefined,
|
|
165
|
+
symbol: this.formatHlSymbol(order.coin),
|
|
166
|
+
side: (order.side === 'B' ? 'buy' : 'sell'),
|
|
167
|
+
type: (order.orderType?.toLowerCase() ?? 'limit'),
|
|
168
|
+
quantity: origQty,
|
|
169
|
+
price: price > 0 ? price : undefined,
|
|
170
|
+
status: status ? this.mapHlStatus(status) : 'open',
|
|
171
|
+
filledQuantity: filledQty,
|
|
172
|
+
createdAt: order.timestamp,
|
|
173
|
+
updatedAt: statusTimestamp,
|
|
174
|
+
raw: { order, status, statusTimestamp },
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Map Hyperliquid fill to unified Fill. */
|
|
178
|
+
mapHlFillResponse(f) {
|
|
179
|
+
return {
|
|
180
|
+
fillId: String(f.tid ?? ''),
|
|
181
|
+
orderId: String(f.oid ?? ''),
|
|
182
|
+
symbol: this.formatHlSymbol(f.coin),
|
|
183
|
+
side: (f.side === 'B' ? 'buy' : 'sell'),
|
|
184
|
+
quantity: parseFloat(f.sz ?? '0'),
|
|
185
|
+
price: parseFloat(f.px ?? '0'),
|
|
186
|
+
fee: parseFloat(f.fee ?? '0'),
|
|
187
|
+
feeCurrency: f.feeToken ?? 'USDC',
|
|
188
|
+
liquidity: f.crossed ? 'taker' : 'maker',
|
|
189
|
+
timestamp: typeof f.time === 'number' ? f.time : parseInt(`${f.time ?? Date.now()}`),
|
|
190
|
+
raw: f,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/** Format price for Hyperliquid wire. HL uses 6 decimals for most assets. */
|
|
194
|
+
formatHlPrice(price, symbol) {
|
|
195
|
+
// Try to infer price precision from szDecimals (price szDecimals relationship is not direct,
|
|
196
|
+
// but szDecimals gives us a lower bound on precision). Fallback to 6 decimals.
|
|
197
|
+
const coin = this.normalizeCoin(symbol);
|
|
198
|
+
const info = this.assetInfoCache.get(coin);
|
|
199
|
+
// szDecimals typically ranges 0-8; price precision is often max(6, 8 - szDecimals) on HL
|
|
200
|
+
const priceDecimals = info ? Math.max(6, 8 - info.szDecimals) : 6;
|
|
201
|
+
return price.toFixed(priceDecimals);
|
|
202
|
+
}
|
|
203
|
+
/** Format size for Hyperliquid wire based on asset szDecimals. */
|
|
204
|
+
formatHlSize(size, symbol) {
|
|
205
|
+
const coin = this.normalizeCoin(symbol);
|
|
206
|
+
const info = this.assetInfoCache.get(coin);
|
|
207
|
+
const decimals = info?.szDecimals ?? 6;
|
|
208
|
+
return size.toFixed(decimals);
|
|
209
|
+
}
|
|
210
|
+
// ─── Market Data ─────────────────────────────────────────────────────────
|
|
211
|
+
async getTicker(symbol) {
|
|
212
|
+
const coin = this.normalizeCoin(symbol);
|
|
213
|
+
const [metaAndCtxs, l2] = await Promise.all([
|
|
214
|
+
this.hlInfo({ type: 'metaAndAssetCtxs' }),
|
|
215
|
+
this.hlInfo({ type: 'l2Book', coin }),
|
|
216
|
+
]);
|
|
217
|
+
const ctxs = metaAndCtxs[1] ?? [];
|
|
218
|
+
await this.ensureAssetCache();
|
|
219
|
+
const idx = this.assetCache.get(coin) ?? 0;
|
|
220
|
+
const ctx = ctxs[idx] ?? {};
|
|
221
|
+
const markPrice = +new decimal_js_1.default(String((ctx.markPx ?? ctx.oraclePx ?? '0') || '0'));
|
|
222
|
+
const levels = l2.levels ?? [[], []];
|
|
223
|
+
const bid = levels[0]?.[0] ? +new decimal_js_1.default(String(levels[0][0][0] || '0')) : markPrice * 0.999;
|
|
224
|
+
const ask = levels[1]?.[0] ? +new decimal_js_1.default(String(levels[1][0][0] || '0')) : markPrice * 1.001;
|
|
225
|
+
return {
|
|
226
|
+
symbol, bid, ask, last: markPrice,
|
|
227
|
+
volume24h: parseFloat(ctx.dayNtlVlm ?? '0'),
|
|
228
|
+
openInterest: parseFloat(ctx.openInterest ?? '0'),
|
|
229
|
+
markPrice, fundingRate: parseFloat(ctx.funding ?? '0'),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async getSymbolInfo(symbol) {
|
|
233
|
+
await this.ensureAssetCache();
|
|
234
|
+
const coin = this.normalizeCoin(symbol);
|
|
235
|
+
const info = this.assetInfoCache.get(coin);
|
|
236
|
+
const stepSize = info ? Math.pow(10, -info.szDecimals) : 0.001;
|
|
237
|
+
return {
|
|
238
|
+
symbol, base: coin, quote: 'USDC', marketType: 'swap',
|
|
239
|
+
minQty: stepSize, maxQty: 999999999, stepSize, tickSize: 0.01,
|
|
240
|
+
minNotional: 10, maxLeverage: info?.maxLeverage ?? 50,
|
|
241
|
+
settleCurrency: 'USDC',
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
async getOHLCV(symbol, timeframe, limit = 100) {
|
|
245
|
+
const coin = this.normalizeCoin(symbol);
|
|
246
|
+
const intervalMap = {
|
|
247
|
+
'1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m',
|
|
248
|
+
'1h': '1h', '2h': '2h', '4h': '4h', '8h': '8h', '1d': '1d',
|
|
249
|
+
};
|
|
250
|
+
const interval = intervalMap[timeframe] ?? '1h';
|
|
251
|
+
const endTime = Date.now();
|
|
252
|
+
const intervalMs = {
|
|
253
|
+
'1m': 60_000, '3m': 180_000, '5m': 300_000, '15m': 900_000, '30m': 1_800_000,
|
|
254
|
+
'1h': 3_600_000, '2h': 7_200_000, '4h': 14_400_000, '8h': 28_800_000, '1d': 86_400_000,
|
|
255
|
+
};
|
|
256
|
+
const startTime = endTime - (intervalMs[interval] ?? 3_600_000) * limit;
|
|
257
|
+
const data = await this.hlInfo({ type: 'candleSnapshot', req: { coin, interval, startTime, endTime } });
|
|
258
|
+
return (data ?? []).map(c => ({
|
|
259
|
+
timestamp: c.t, open: parseFloat(c.o), high: parseFloat(c.h),
|
|
260
|
+
low: parseFloat(c.l), close: parseFloat(c.c), volume: parseFloat(c.v),
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
async getOrderBook(symbol, _depth = 20) {
|
|
264
|
+
const coin = this.normalizeCoin(symbol);
|
|
265
|
+
const data = await this.hlInfo({ type: 'l2Book', coin });
|
|
266
|
+
const levels = data.levels ?? [[], []];
|
|
267
|
+
return {
|
|
268
|
+
symbol,
|
|
269
|
+
bids: (levels[0] ?? []).map(([p, q]) => ({ price: parseFloat(p), quantity: parseFloat(q) })),
|
|
270
|
+
asks: (levels[1] ?? []).map(([p, q]) => ({ price: parseFloat(p), quantity: parseFloat(q) })),
|
|
271
|
+
timestamp: Date.now(),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
async getFundingInfo(symbol) {
|
|
275
|
+
const coin = this.normalizeCoin(symbol);
|
|
276
|
+
try {
|
|
277
|
+
const [metaCtxs, hist] = await Promise.all([
|
|
278
|
+
this.hlInfo({ type: 'metaAndAssetCtxs' }),
|
|
279
|
+
this.hlInfo({
|
|
280
|
+
type: 'fundingHistory', coin,
|
|
281
|
+
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, endTime: Date.now(),
|
|
282
|
+
}),
|
|
283
|
+
]);
|
|
284
|
+
await this.ensureAssetCache();
|
|
285
|
+
const idx = this.assetCache.get(coin) ?? 0;
|
|
286
|
+
const ctx = metaCtxs[1]?.[idx] ?? {};
|
|
287
|
+
return {
|
|
288
|
+
symbol, fundingRate: parseFloat(ctx.funding ?? '0'),
|
|
289
|
+
nextFundingTime: 0, openInterest: parseFloat(ctx.openInterest ?? '0'),
|
|
290
|
+
historyRates: (hist ?? []).slice(-7).map(h => ({
|
|
291
|
+
rate: parseFloat(h.fundingRate ?? '0'), timestamp: parseInt(h.time ?? '0'),
|
|
292
|
+
})),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async listSymbols(_marketType) {
|
|
300
|
+
const meta = await this.hlInfo({ type: 'meta' });
|
|
301
|
+
return (meta[0]?.universe ?? []).map(u => `${u.name}-USDC`);
|
|
302
|
+
}
|
|
303
|
+
// ─── Account ─────────────────────────────────────────────────────────────
|
|
304
|
+
async getAccount() {
|
|
305
|
+
if (!this.walletAddress)
|
|
306
|
+
throw new base_1.ExchangeError('Wallet address required', 'hyperliquid');
|
|
307
|
+
const data = await this.hlInfo({ type: 'clearinghouseState', user: this.walletAddress });
|
|
308
|
+
const ms = data.marginSummary ?? {};
|
|
309
|
+
const accountValue = +new decimal_js_1.default(String(ms.accountValue ?? '0'));
|
|
310
|
+
const withdrawable = +new decimal_js_1.default(String(data.withdrawable ?? '0'));
|
|
311
|
+
return {
|
|
312
|
+
balances: [{
|
|
313
|
+
asset: 'USDC', free: withdrawable,
|
|
314
|
+
locked: Math.max(0, accountValue - withdrawable), total: accountValue,
|
|
315
|
+
}],
|
|
316
|
+
totalEquity: accountValue,
|
|
317
|
+
availableMargin: withdrawable,
|
|
318
|
+
unrealizedPnl: parseFloat(ms.totalUnrealizedPnl ?? ms.totalRawUpl ?? '0'),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
async getPositions(symbol) {
|
|
322
|
+
if (!this.walletAddress)
|
|
323
|
+
return [];
|
|
324
|
+
const data = await this.hlInfo({ type: 'clearinghouseState', user: this.walletAddress });
|
|
325
|
+
return (data.assetPositions ?? [])
|
|
326
|
+
.map(item => item.position)
|
|
327
|
+
.filter(p => {
|
|
328
|
+
const size = +new decimal_js_1.default(String(p.szi ?? '0'));
|
|
329
|
+
if (size === 0)
|
|
330
|
+
return false;
|
|
331
|
+
if (symbol) {
|
|
332
|
+
const coin = this.normalizeCoin(symbol);
|
|
333
|
+
return p.coin === coin || p.coin === symbol;
|
|
334
|
+
}
|
|
335
|
+
return true;
|
|
336
|
+
})
|
|
337
|
+
.map(p => {
|
|
338
|
+
const size = +new decimal_js_1.default(String(p.szi ?? '0'));
|
|
339
|
+
const entry = +new decimal_js_1.default(String(p.entryPx ?? '0'));
|
|
340
|
+
const mark = +new decimal_js_1.default(String(p.markPx ?? entry));
|
|
341
|
+
return {
|
|
342
|
+
symbol: this.formatHlSymbol(p.coin), side: size > 0 ? 'long' : 'short',
|
|
343
|
+
size: Math.abs(size), entryPrice: entry, markPrice: mark,
|
|
344
|
+
notional: Math.abs(size) * mark,
|
|
345
|
+
leverage: parseFloat((p.leverage && typeof p.leverage === 'object' ? p.leverage.value : p.leverage) ?? '1'),
|
|
346
|
+
marginMode: 'cross',
|
|
347
|
+
unrealizedPnl: parseFloat(p.unrealizedPnl ?? '0'),
|
|
348
|
+
liquidationPrice: p.liquidationPx ? parseFloat(p.liquidationPx) : undefined,
|
|
349
|
+
margin: parseFloat(p.marginUsed ?? '0'),
|
|
350
|
+
openedAt: Date.now(),
|
|
351
|
+
};
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
// ─── Leverage ────────────────────────────────────────────────────────────
|
|
355
|
+
async setLeverage(symbol, leverage, marginMode = 'cross') {
|
|
356
|
+
const assetIdx = await this.getAssetIndex(symbol);
|
|
357
|
+
await this.hlExchange({
|
|
358
|
+
type: 'updateLeverage',
|
|
359
|
+
asset: assetIdx,
|
|
360
|
+
isCross: marginMode === 'cross',
|
|
361
|
+
leverage,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
// ─── Order Placement ──────────────────────────────────────────────────────
|
|
365
|
+
async placeOrder(params) {
|
|
366
|
+
const assetIdx = await this.getAssetIndex(params.symbol);
|
|
367
|
+
const isBuy = params.side === 'buy';
|
|
368
|
+
const clientOid = params.clientOrderId ?? (0, base_1.genClientOrderId)('hl');
|
|
369
|
+
// Convert clientOrderId to 16-byte hex cloid
|
|
370
|
+
const cloid = '0x' + Array.from(new TextEncoder().encode(clientOid.padEnd(16, '0').slice(0, 16)))
|
|
371
|
+
.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
372
|
+
let orderType;
|
|
373
|
+
switch (params.type) {
|
|
374
|
+
case 'market':
|
|
375
|
+
// Market on Hyperliquid = IOC limit far from market
|
|
376
|
+
orderType = { limit: { tif: 'Ioc' } };
|
|
377
|
+
break;
|
|
378
|
+
case 'limit':
|
|
379
|
+
orderType = { limit: { tif: params.timeInForce === 'IOC' ? 'Ioc' : params.postOnly ? 'Alo' : 'Gtc' } };
|
|
380
|
+
break;
|
|
381
|
+
case 'stop_market':
|
|
382
|
+
orderType = { trigger: { isMarket: true, tpsl: 'sl', triggerPx: String(params.stopPrice ?? params.price ?? '0') } };
|
|
383
|
+
break;
|
|
384
|
+
case 'stop_limit':
|
|
385
|
+
orderType = { trigger: { isMarket: false, tpsl: 'sl', triggerPx: String(params.stopPrice ?? '0') } };
|
|
386
|
+
break;
|
|
387
|
+
case 'take_profit_market':
|
|
388
|
+
orderType = { trigger: { isMarket: true, tpsl: 'tp', triggerPx: String(params.stopPrice ?? params.price ?? '0') } };
|
|
389
|
+
break;
|
|
390
|
+
case 'take_profit_limit':
|
|
391
|
+
orderType = { trigger: { isMarket: false, tpsl: 'tp', triggerPx: String(params.stopPrice ?? '0') } };
|
|
392
|
+
break;
|
|
393
|
+
default:
|
|
394
|
+
orderType = { limit: { tif: 'Gtc' } };
|
|
395
|
+
}
|
|
396
|
+
// For market orders, use a wide price to ensure fill
|
|
397
|
+
let price = params.price ?? 0;
|
|
398
|
+
if (params.type === 'market' && price === 0) {
|
|
399
|
+
const ticker = await this.getTicker(params.symbol);
|
|
400
|
+
price = isBuy ? ticker.ask * 1.05 : ticker.bid * 0.95;
|
|
401
|
+
}
|
|
402
|
+
const orderWire = {
|
|
403
|
+
a: assetIdx, b: isBuy,
|
|
404
|
+
p: this.formatHlPrice(price, params.symbol),
|
|
405
|
+
s: this.formatHlSize(params.quantity ?? 0, params.symbol),
|
|
406
|
+
r: params.reduceOnly ?? false,
|
|
407
|
+
t: orderType, c: cloid,
|
|
408
|
+
};
|
|
409
|
+
const result = await this.hlExchange({ type: 'order', orders: [orderWire], grouping: 'na' });
|
|
410
|
+
const status = result.response?.data?.statuses?.[0];
|
|
411
|
+
if (status?.error)
|
|
412
|
+
throw new base_1.ExchangeError(status.error, 'hyperliquid');
|
|
413
|
+
const oid = String(status?.resting?.oid ?? status?.filled?.oid ?? Date.now());
|
|
414
|
+
const filledQty = status?.filled ? parseFloat(status.filled.totalSz) : 0;
|
|
415
|
+
const avgPrice = status?.filled ? parseFloat(status.filled.avgPx) : undefined;
|
|
416
|
+
return {
|
|
417
|
+
orderId: oid, clientOrderId: clientOid, symbol: params.symbol,
|
|
418
|
+
side: params.side, type: params.type,
|
|
419
|
+
quantity: params.quantity ?? 0, price: params.price,
|
|
420
|
+
status: filledQty >= (params.quantity ?? 0) ? 'filled' : filledQty > 0 ? 'partially_filled' : 'open',
|
|
421
|
+
filledQuantity: filledQty, avgFillPrice: avgPrice,
|
|
422
|
+
createdAt: Date.now(), raw: result,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
async cancelOrder(orderId, symbol) {
|
|
426
|
+
const assetIdx = await this.getAssetIndex(symbol);
|
|
427
|
+
try {
|
|
428
|
+
await this.hlExchange({
|
|
429
|
+
type: 'cancel',
|
|
430
|
+
cancels: [{ a: assetIdx, o: parseInt(orderId) }],
|
|
431
|
+
});
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async cancelAllOrders(_request) {
|
|
439
|
+
try {
|
|
440
|
+
await this.hlExchange({ type: 'cancelAll' });
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async getOrderStatus(orderId, symbol) {
|
|
448
|
+
if (!this.walletAddress)
|
|
449
|
+
throw new base_1.ExchangeError('Wallet address required', 'hyperliquid');
|
|
450
|
+
const orders = await this.hlInfo({ type: 'openOrders', user: this.walletAddress });
|
|
451
|
+
const order = orders.find(o => String(o.oid) === orderId);
|
|
452
|
+
if (!order) {
|
|
453
|
+
// Order not in open orders — could be filled, cancelled, or unknown
|
|
454
|
+
return {
|
|
455
|
+
orderId, symbol, side: 'buy', type: 'limit', quantity: 0,
|
|
456
|
+
status: 'unknown',
|
|
457
|
+
filledQuantity: 0, createdAt: Date.now(),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
orderId, symbol: `${order.coin}-USDC`,
|
|
462
|
+
side: order.side?.toLowerCase() === 'b' || order.side === 'buy' ? 'buy' : 'sell',
|
|
463
|
+
type: 'limit', quantity: +new decimal_js_1.default(String(order.sz ?? '0')),
|
|
464
|
+
price: +new decimal_js_1.default(String(order.limitPx ?? '0')),
|
|
465
|
+
status: +new decimal_js_1.default(String(order.filledSz ?? '0')) >= +new decimal_js_1.default(String(order.sz ?? '0')) ? 'filled'
|
|
466
|
+
: +new decimal_js_1.default(String(order.filledSz ?? '0')) > 0 ? 'partially_filled' : 'open',
|
|
467
|
+
filledQuantity: +new decimal_js_1.default(String(order.filledSz ?? '0')),
|
|
468
|
+
avgFillPrice: order.avgFillPx ? +new decimal_js_1.default(String(order.avgFillPx)) : undefined,
|
|
469
|
+
createdAt: parseInt(order.timestamp ?? `${Date.now()}`),
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
async getOpenOrders(symbol) {
|
|
473
|
+
if (!this.walletAddress)
|
|
474
|
+
return [];
|
|
475
|
+
const orders = await this.hlInfo({ type: 'openOrders', user: this.walletAddress });
|
|
476
|
+
return orders
|
|
477
|
+
.filter(o => !symbol || o.coin === this.normalizeCoin(symbol))
|
|
478
|
+
.map(o => this.mapHlOrderResponse(o));
|
|
479
|
+
}
|
|
480
|
+
async getOrderHistory(_query) {
|
|
481
|
+
if (!this.walletAddress)
|
|
482
|
+
return { orders: [], hasMore: false };
|
|
483
|
+
const endTime = _query.endTime ?? Date.now();
|
|
484
|
+
const startTime = _query.startTime ?? endTime - 30 * 24 * 60 * 60 * 1000;
|
|
485
|
+
const data = await this.hlInfo({
|
|
486
|
+
type: 'historicalOrders',
|
|
487
|
+
user: this.walletAddress,
|
|
488
|
+
startTime: Math.floor(startTime),
|
|
489
|
+
endTime: Math.floor(endTime),
|
|
490
|
+
});
|
|
491
|
+
// Group by oid and keep the latest status per order
|
|
492
|
+
const statusPriority = {
|
|
493
|
+
filled: 3, canceled: 3, selfTradeCanceled: 3,
|
|
494
|
+
iocCancelRejected: 2, positionIncreaseAtOpenInterestCapRejected: 2,
|
|
495
|
+
open: 1,
|
|
496
|
+
};
|
|
497
|
+
const latestByOid = new Map();
|
|
498
|
+
for (const item of (data ?? [])) {
|
|
499
|
+
const existing = latestByOid.get(item.order.oid);
|
|
500
|
+
if (!existing) {
|
|
501
|
+
latestByOid.set(item.order.oid, item);
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const existingP = statusPriority[existing.status] ?? 0;
|
|
505
|
+
const newP = statusPriority[item.status] ?? 0;
|
|
506
|
+
if (item.statusTimestamp > existing.statusTimestamp ||
|
|
507
|
+
(item.statusTimestamp === existing.statusTimestamp && newP > existingP)) {
|
|
508
|
+
latestByOid.set(item.order.oid, item);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const orders = Array.from(latestByOid.values())
|
|
512
|
+
.filter(item => !_query.symbol || item.order.coin === this.normalizeCoin(_query.symbol))
|
|
513
|
+
.slice(0, _query.limit ?? 50)
|
|
514
|
+
.map(item => this.mapHlOrderResponse(item.order, item.status, item.statusTimestamp));
|
|
515
|
+
return { orders, hasMore: false };
|
|
516
|
+
}
|
|
517
|
+
async getFills(query) {
|
|
518
|
+
if (!this.walletAddress)
|
|
519
|
+
return { fills: [], hasMore: false };
|
|
520
|
+
const endTime = query.endTime ?? Date.now();
|
|
521
|
+
const startTime = query.startTime ?? endTime - 30 * 24 * 60 * 60 * 1000;
|
|
522
|
+
const data = await this.hlInfo({
|
|
523
|
+
type: 'userFillsByTime',
|
|
524
|
+
user: this.walletAddress,
|
|
525
|
+
startTime: Math.floor(startTime),
|
|
526
|
+
endTime: Math.floor(endTime),
|
|
527
|
+
});
|
|
528
|
+
const fills = (data ?? [])
|
|
529
|
+
.filter(f => !query.symbol || f.coin === this.normalizeCoin(query.symbol))
|
|
530
|
+
.slice(0, query.limit ?? 50)
|
|
531
|
+
.map(f => this.mapHlFillResponse(f));
|
|
532
|
+
return { fills, hasMore: false };
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
exports.HyperliquidAdapter = HyperliquidAdapter;
|
|
536
|
+
//# sourceMappingURL=hyperliquid.js.map
|