ccxt
Version:
1,036 lines (1,033 loc) • 45.5 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import hyperliquidRest from '../hyperliquid.js';
import { ExchangeError } from '../base/errors.js';
import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById } from '../base/ws/Cache.js';
// ---------------------------------------------------------------------------
export default class hyperliquid extends hyperliquidRest {
describe() {
return this.deepExtend(super.describe(), {
'has': {
'ws': true,
'createOrderWs': true,
'createOrdersWs': true,
'editOrderWs': true,
'watchBalance': false,
'watchMyTrades': true,
'watchOHLCV': true,
'watchOrderBook': true,
'watchOrders': true,
'watchTicker': true,
'watchTickers': true,
'watchTrades': true,
'watchTradesForSymbols': false,
'watchPosition': false,
},
'urls': {
'api': {
'ws': {
'public': 'wss://api.hyperliquid.xyz/ws',
},
},
'test': {
'ws': {
'public': 'wss://api.hyperliquid-testnet.xyz/ws',
},
},
},
'options': {},
'streaming': {
'ping': this.ping,
'keepAlive': 20000,
},
'exceptions': {
'ws': {
'exact': {},
},
},
});
}
/**
* @method
* @name hyperliquid#createOrdersWs
* @description create a list of trade orders using WebSocket post request
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
* @param {Array} orders list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createOrdersWs(orders, params = {}) {
await this.loadMarkets();
const url = this.urls['api']['ws']['public'];
const ordersRequest = this.createOrdersRequest(orders, params);
const wrapped = this.wrapAsPostAction(ordersRequest);
const request = this.safeDict(wrapped, 'request', {});
const requestId = this.safeString(wrapped, 'requestId');
const response = await this.watch(url, requestId, request, requestId);
const responseOjb = this.safeDict(response, 'response', {});
const data = this.safeDict(responseOjb, 'data', {});
const statuses = this.safeList(data, 'statuses', []);
return this.parseOrders(statuses, undefined);
}
/**
* @method
* @name hyperliquid#createOrderWs
* @description create a trade order using WebSocket post request
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.timeInForce] 'Gtc', 'Ioc', 'Alo'
* @param {bool} [params.postOnly] true or false whether the order is post-only
* @param {bool} [params.reduceOnly] true or false whether the order is reduce-only
* @param {float} [params.triggerPrice] The price at which a trigger order is triggered at
* @param {string} [params.clientOrderId] client order id, (optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
* @param {string} [params.slippage] the slippage for market order
* @param {string} [params.vaultAddress] the vault address for order
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createOrderWs(symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets();
const [order, globalParams] = this.parseCreateEditOrderArgs(undefined, symbol, type, side, amount, price, params);
const orders = await this.createOrdersWs([order], globalParams);
const parsedOrder = orders[0];
const orderInfo = this.safeDict(parsedOrder, 'info');
// handle potential error here
this.handleErrors(undefined, undefined, undefined, undefined, undefined, this.json(orderInfo), orderInfo, undefined, undefined);
return parsedOrder;
}
/**
* @method
* @name hyperliquid#editOrderWs
* @description edit a trade order
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
* @param {string} id cancel order id
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.timeInForce] 'Gtc', 'Ioc', 'Alo'
* @param {bool} [params.postOnly] true or false whether the order is post-only
* @param {bool} [params.reduceOnly] true or false whether the order is reduce-only
* @param {float} [params.triggerPrice] The price at which a trigger order is triggered at
* @param {string} [params.clientOrderId] client order id, (optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
* @param {string} [params.vaultAddress] the vault address for order
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async editOrderWs(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const url = this.urls['api']['ws']['public'];
const [order, globalParams] = this.parseCreateEditOrderArgs(id, symbol, type, side, amount, price, params);
const postRequest = this.editOrdersRequest([order], globalParams);
const wrapped = this.wrapAsPostAction(postRequest);
const request = this.safeDict(wrapped, 'request', {});
const requestId = this.safeString(wrapped, 'requestId');
const response = await this.watch(url, requestId, request, requestId);
// response is the same as in this.editOrder
const responseObject = this.safeDict(response, 'response', {});
const dataObject = this.safeDict(responseObject, 'data', {});
const statuses = this.safeList(dataObject, 'statuses', []);
const first = this.safeDict(statuses, 0, {});
const parsedOrder = this.parseOrder(first, market);
const orderInfo = this.safeDict(parsedOrder, 'info');
// handle potential error here
this.handleErrors(undefined, undefined, undefined, undefined, undefined, this.json(orderInfo), orderInfo, undefined, undefined);
return parsedOrder;
}
/**
* @method
* @name hyperliquid#watchOrderBook
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async watchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const messageHash = 'orderbook:' + symbol;
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'subscribe',
'subscription': {
'type': 'l2Book',
'coin': market['swap'] ? market['base'] : market['id'],
},
};
const message = this.extend(request, params);
const orderbook = await this.watch(url, messageHash, message, messageHash);
return orderbook.limit();
}
/**
* @method
* @name hyperliquid#unWatchOrderBook
* @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async unWatchOrderBook(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const subMessageHash = 'orderbook:' + symbol;
const messageHash = 'unsubscribe:' + subMessageHash;
const url = this.urls['api']['ws']['public'];
const id = this.nonce().toString();
const request = {
'id': id,
'method': 'unsubscribe',
'subscription': {
'type': 'l2Book',
'coin': market['swap'] ? market['base'] : market['id'],
},
};
const message = this.extend(request, params);
return await this.watch(url, messageHash, message, messageHash);
}
handleOrderBook(client, message) {
//
// {
// "channel": "l2Book",
// "data": {
// "coin": "BTC",
// "time": 1710131872708,
// "levels": [
// [
// {
// "px": "68674.0",
// "sz": "0.97139",
// "n": 4
// }
// ],
// [
// {
// "px": "68675.0",
// "sz": "0.04396",
// "n": 1
// }
// ]
// ]
// }
// }
//
const entry = this.safeDict(message, 'data', {});
const coin = this.safeString(entry, 'coin');
const marketId = this.coinToMarketId(coin);
const market = this.market(marketId);
const symbol = market['symbol'];
const rawData = this.safeList(entry, 'levels', []);
const data = {
'bids': this.safeList(rawData, 0, []),
'asks': this.safeList(rawData, 1, []),
};
const timestamp = this.safeInteger(entry, 'time');
const snapshot = this.parseOrderBook(data, symbol, timestamp, 'bids', 'asks', 'px', 'sz');
if (!(symbol in this.orderbooks)) {
const ob = this.orderBook(snapshot);
this.orderbooks[symbol] = ob;
}
const orderbook = this.orderbooks[symbol];
orderbook.reset(snapshot);
const messageHash = 'orderbook:' + symbol;
client.resolve(orderbook, messageHash);
}
/**
* @method
* @name hyperliquid#watchTicker
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async watchTicker(symbol, params = {}) {
const market = this.market(symbol);
symbol = market['symbol'];
const tickers = await this.watchTickers([symbol], params);
return tickers[symbol];
}
/**
* @method
* @name hyperliquid#watchTickers
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async watchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols, undefined, true);
const messageHash = 'tickers';
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'subscribe',
'subscription': {
'type': 'webData2',
'user': '0x0000000000000000000000000000000000000000',
},
};
const tickers = await this.watch(url, messageHash, this.extend(request, params), messageHash);
if (this.newUpdates) {
return this.filterByArrayTickers(tickers, 'symbol', symbols);
}
return this.tickers;
}
/**
* @method
* @name hyperliquid#unWatchTickers
* @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async unWatchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols, undefined, true);
const subMessageHash = 'tickers';
const messageHash = 'unsubscribe:' + subMessageHash;
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'unsubscribe',
'subscription': {
'type': 'webData2',
'user': '0x0000000000000000000000000000000000000000',
},
};
return await this.watch(url, messageHash, this.extend(request, params), messageHash);
}
/**
* @method
* @name hyperliquid#watchMyTrades
* @description watches information on multiple trades made by the user
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.user] user address, will default to this.walletAddress if not provided
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
let userAddress = undefined;
[userAddress, params] = this.handlePublicAddress('watchMyTrades', params);
await this.loadMarkets();
let messageHash = 'myTrades';
if (symbol !== undefined) {
symbol = this.symbol(symbol);
messageHash += ':' + symbol;
}
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'subscribe',
'subscription': {
'type': 'userFills',
'user': userAddress,
},
};
const message = this.extend(request, params);
const trades = await this.watch(url, messageHash, message, messageHash);
if (this.newUpdates) {
limit = trades.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
}
handleWsTickers(client, message) {
//
// {
// "channel": "webData2",
// "data": {
// "meta": {
// "universe": [
// {
// "szDecimals": 5,
// "name": "BTC",
// "maxLeverage": 50,
// "onlyIsolated": false
// },
// ...
// ],
// },
// "assetCtxs": [
// {
// "funding": "0.00003005",
// "openInterest": "2311.50778",
// "prevDayPx": "63475.0",
// "dayNtlVlm": "468043329.64289033",
// "premium": "0.00094264",
// "oraclePx": "64712.0",
// "markPx": "64774.0",
// "midPx": "64773.5",
// "impactPxs": [
// "64773.0",
// "64774.0"
// ]
// },
// ...
// ],
// "spotAssetCtxs": [
// {
// "prevDayPx": "0.20937",
// "dayNtlVlm": "11188888.61984999",
// "markPx": "0.19722",
// "midPx": "0.197145",
// "circulatingSupply": "598760557.12072003",
// "coin": "PURR/USDC"
// },
// ...
// ],
// }
// }
//
// spot
const rawData = this.safeDict(message, 'data', {});
const spotAssets = this.safeList(rawData, 'spotAssetCtxs', []);
const parsedTickers = [];
for (let i = 0; i < spotAssets.length; i++) {
const assetObject = spotAssets[i];
const marketId = this.safeString(assetObject, 'coin');
const market = this.safeMarket(marketId, undefined, undefined, 'spot');
const ticker = this.parseWsTicker(assetObject, market);
parsedTickers.push(ticker);
}
// perpetuals
const meta = this.safeDict(rawData, 'meta', {});
const universe = this.safeList(meta, 'universe', []);
const assetCtxs = this.safeList(rawData, 'assetCtxs', []);
for (let i = 0; i < universe.length; i++) {
const data = this.extend(this.safeDict(universe, i, {}), this.safeDict(assetCtxs, i, {}));
const id = data['name'] + '/USDC:USDC';
const market = this.safeMarket(id, undefined, undefined, 'swap');
const ticker = this.parseWsTicker(data, market);
parsedTickers.push(ticker);
}
const tickers = this.indexBy(parsedTickers, 'symbol');
client.resolve(tickers, 'tickers');
}
parseWsTicker(rawTicker, market = undefined) {
return this.parseTicker(rawTicker, market);
}
handleMyTrades(client, message) {
//
// {
// "channel": "userFills",
// "data": {
// "isSnapshot": true,
// "user": "0x15f43d1f2dee81424afd891943262aa90f22cc2a",
// "fills": [
// {
// "coin": "BTC",
// "px": "72528.0",
// "sz": "0.11693",
// "side": "A",
// "time": 1710208712815,
// "startPosition": "0.11693",
// "dir": "Close Long",
// "closedPnl": "-0.81851",
// "hash": "0xc5adaf35f8402750c218040b0a7bc301130051521273b6f398b3caad3e1f3f5f",
// "oid": 7484888874,
// "crossed": true,
// "fee": "2.968244",
// "liquidationMarkPx": null,
// "tid": 567547935839686,
// "cloid": null
// }
// ]
// }
// }
//
const entry = this.safeDict(message, 'data', {});
if (this.myTrades === undefined) {
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
this.myTrades = new ArrayCacheBySymbolById(limit);
}
const trades = this.myTrades;
const symbols = {};
const data = this.safeList(entry, 'fills', []);
const dataLength = data.length;
if (dataLength === 0) {
return;
}
for (let i = 0; i < data.length; i++) {
const rawTrade = data[i];
const parsed = this.parseWsTrade(rawTrade);
const symbol = parsed['symbol'];
symbols[symbol] = true;
trades.append(parsed);
}
const keys = Object.keys(symbols);
for (let i = 0; i < keys.length; i++) {
const currentMessageHash = 'myTrades:' + keys[i];
client.resolve(trades, currentMessageHash);
}
// non-symbol specific
const messageHash = 'myTrades';
client.resolve(trades, messageHash);
}
async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
// s
// @method
// @name hyperliquid#watchTrades
// @description watches information on multiple trades made in a market
// @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
// @param {string} symbol unified market symbol of the market trades were made in
// @param {int} [since] the earliest time in ms to fetch trades for
// @param {int} [limit] the maximum number of trade structures to retrieve
// @param {object} [params] extra parameters specific to the exchange API endpoint
// @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
//
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const messageHash = 'trade:' + symbol;
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'subscribe',
'subscription': {
'type': 'trades',
'coin': market['swap'] ? market['base'] : market['id'],
},
};
const message = this.extend(request, params);
const trades = await this.watch(url, messageHash, message, messageHash);
if (this.newUpdates) {
limit = trades.getLimit(symbol, limit);
}
return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
}
/**
* @method
* @name hyperliquid#unWatchTrades
* @description unWatches information on multiple trades made in a market
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string} symbol unified market symbol of the market trades were made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async unWatchTrades(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const subMessageHash = 'trade:' + symbol;
const messageHash = 'unsubscribe:' + subMessageHash;
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'unsubscribe',
'subscription': {
'type': 'trades',
'coin': market['swap'] ? market['base'] : market['id'],
},
};
const message = this.extend(request, params);
return await this.watch(url, messageHash, message, messageHash);
}
handleTrades(client, message) {
//
// {
// "channel": "trades",
// "data": [
// {
// "coin": "BTC",
// "side": "A",
// "px": "68517.0",
// "sz": "0.005",
// "time": 1710125266669,
// "hash": "0xc872699f116e012186620407fc08a802015e0097c5cce74710697f7272e6e959",
// "tid": 981894269203506
// }
// ]
// }
//
const entry = this.safeList(message, 'data', []);
const first = this.safeDict(entry, 0, {});
const coin = this.safeString(first, 'coin');
const marketId = this.coinToMarketId(coin);
const market = this.market(marketId);
const symbol = market['symbol'];
if (!(symbol in this.trades)) {
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
const stored = new ArrayCache(limit);
this.trades[symbol] = stored;
}
const trades = this.trades[symbol];
for (let i = 0; i < entry.length; i++) {
const data = this.safeDict(entry, i);
const trade = this.parseWsTrade(data);
trades.append(trade);
}
const messageHash = 'trade:' + symbol;
client.resolve(trades, messageHash);
}
parseWsTrade(trade, market = undefined) {
//
// fetchMyTrades
//
// {
// "coin": "BTC",
// "px": "72528.0",
// "sz": "0.11693",
// "side": "A",
// "time": 1710208712815,
// "startPosition": "0.11693",
// "dir": "Close Long",
// "closedPnl": "-0.81851",
// "hash": "0xc5adaf35f8402750c218040b0a7bc301130051521273b6f398b3caad3e1f3f5f",
// "oid": 7484888874,
// "crossed": true,
// "fee": "2.968244",
// "liquidationMarkPx": null,
// "tid": 567547935839686,
// "cloid": null
// }
//
// fetchTrades
//
// {
// "coin": "BTC",
// "side": "A",
// "px": "68517.0",
// "sz": "0.005",
// "time": 1710125266669,
// "hash": "0xc872699f116e012186620407fc08a802015e0097c5cce74710697f7272e6e959",
// "tid": 981894269203506
// }
//
const timestamp = this.safeInteger(trade, 'time');
const price = this.safeString(trade, 'px');
const amount = this.safeString(trade, 'sz');
const coin = this.safeString(trade, 'coin');
const marketId = this.coinToMarketId(coin);
market = this.safeMarket(marketId, undefined);
const symbol = market['symbol'];
const id = this.safeString(trade, 'tid');
let side = this.safeString(trade, 'side');
if (side !== undefined) {
side = (side === 'A') ? 'sell' : 'buy';
}
const fee = this.safeString(trade, 'fee');
return this.safeTrade({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'id': id,
'order': this.safeString(trade, 'oid'),
'type': undefined,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': undefined,
'fee': { 'cost': fee, 'currency': 'USDC' },
}, market);
}
/**
* @method
* @name hyperliquid#watchOHLCV
* @description watches historical candlestick data containing the open, high, low, close price, and the volume of a market
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'subscribe',
'subscription': {
'type': 'candle',
'coin': market['swap'] ? market['base'] : market['id'],
'interval': timeframe,
},
};
const messageHash = 'candles:' + timeframe + ':' + symbol;
const message = this.extend(request, params);
const ohlcv = await this.watch(url, messageHash, message, messageHash);
if (this.newUpdates) {
limit = ohlcv.getLimit(symbol, limit);
}
return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
}
/**
* @method
* @name hyperliquid#unWatchOHLCV
* @description watches historical candlestick data containing the open, high, low, close price, and the volume of a market
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async unWatchOHLCV(symbol, timeframe = '1m', params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'unsubscribe',
'subscription': {
'type': 'candle',
'coin': market['swap'] ? market['base'] : market['id'],
'interval': timeframe,
},
};
const subMessageHash = 'candles:' + timeframe + ':' + symbol;
const messagehash = 'unsubscribe:' + subMessageHash;
const message = this.extend(request, params);
return await this.watch(url, messagehash, message, messagehash);
}
handleOHLCV(client, message) {
//
// {
// channel: 'candle',
// data: {
// t: 1710146280000,
// T: 1710146339999,
// s: 'BTC',
// i: '1m',
// o: '71400.0',
// c: '71411.0',
// h: '71422.0',
// l: '71389.0',
// v: '1.20407',
// n: 20
// }
// }
//
const data = this.safeDict(message, 'data', {});
const base = this.safeString(data, 's');
const marketId = this.coinToMarketId(base);
const symbol = this.safeSymbol(marketId);
const timeframe = this.safeString(data, 'i');
if (!(symbol in this.ohlcvs)) {
this.ohlcvs[symbol] = {};
}
if (!(timeframe in this.ohlcvs[symbol])) {
const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
const stored = new ArrayCacheByTimestamp(limit);
this.ohlcvs[symbol][timeframe] = stored;
}
const ohlcv = this.ohlcvs[symbol][timeframe];
const parsed = this.parseOHLCV(data);
ohlcv.append(parsed);
const messageHash = 'candles:' + timeframe + ':' + symbol;
client.resolve(ohlcv, messageHash);
}
handleWsPost(client, message) {
// {
// channel: "post",
// data: {
// id: <number>,
// response: {
// type: "info" | "action" | "error",
// payload: { ... }
// }
// }
const data = this.safeDict(message, 'data');
const id = this.safeString(data, 'id');
const response = this.safeDict(data, 'response');
const payload = this.safeDict(response, 'payload');
client.resolve(payload, id);
}
/**
* @method
* @name hyperliquid#watchOrders
* @description watches information on multiple orders made by the user
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.user] user address, will default to this.walletAddress if not provided
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let userAddress = undefined;
[userAddress, params] = this.handlePublicAddress('watchOrders', params);
let market = undefined;
let messageHash = 'order';
if (symbol !== undefined) {
market = this.market(symbol);
symbol = market['symbol'];
messageHash = messageHash + ':' + symbol;
}
const url = this.urls['api']['ws']['public'];
const request = {
'method': 'subscribe',
'subscription': {
'type': 'orderUpdates',
'user': userAddress,
},
};
const message = this.extend(request, params);
const orders = await this.watch(url, messageHash, message, messageHash);
if (this.newUpdates) {
limit = orders.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
}
handleOrder(client, message) {
//
// {
// channel: 'orderUpdates',
// data: [
// {
// order: {
// coin: 'BTC',
// side: 'B',
// limitPx: '30000.0',
// sz: '0.001',
// oid: 7456484275,
// timestamp: 1710163596492,
// origSz: '0.001'
// },
// status: 'open',
// statusTimestamp: 1710163596492
// }
// ]
// }
//
const data = this.safeList(message, 'data', []);
if (this.orders === undefined) {
const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
this.orders = new ArrayCacheBySymbolById(limit);
}
const dataLength = data.length;
if (dataLength === 0) {
return;
}
const stored = this.orders;
const messageHash = 'order';
const marketSymbols = {};
for (let i = 0; i < data.length; i++) {
const rawOrder = data[i];
const order = this.parseOrder(rawOrder);
stored.append(order);
const symbol = this.safeString(order, 'symbol');
marketSymbols[symbol] = true;
}
const keys = Object.keys(marketSymbols);
for (let i = 0; i < keys.length; i++) {
const symbol = keys[i];
const innerMessageHash = messageHash + ':' + symbol;
client.resolve(stored, innerMessageHash);
}
client.resolve(stored, messageHash);
}
handleErrorMessage(client, message) {
//
// {
// "channel": "error",
// "data": "Error parsing JSON into valid websocket request: { \"type\": \"allMids\" }"
// }
//
const channel = this.safeString(message, 'channel', '');
const ret_msg = this.safeString(message, 'data', '');
if (channel === 'error') {
throw new ExchangeError(this.id + ' ' + ret_msg);
}
else {
return false;
}
}
handleOrderBookUnsubscription(client, subscription) {
//
// "subscription":{
// "type":"l2Book",
// "coin":"BTC",
// "nSigFigs":5,
// "mantissa":null
// }
//
const coin = this.safeString(subscription, 'coin');
const marketId = this.coinToMarketId(coin);
const symbol = this.safeSymbol(marketId);
const subMessageHash = 'orderbook:' + symbol;
const messageHash = 'unsubscribe:' + subMessageHash;
this.cleanUnsubscription(client, subMessageHash, messageHash);
if (symbol in this.orderbooks) {
delete this.orderbooks[symbol];
}
}
handleTradesUnsubscription(client, subscription) {
//
const coin = this.safeString(subscription, 'coin');
const marketId = this.coinToMarketId(coin);
const symbol = this.safeSymbol(marketId);
const subMessageHash = 'trade:' + symbol;
const messageHash = 'unsubscribe:' + subMessageHash;
this.cleanUnsubscription(client, subMessageHash, messageHash);
if (symbol in this.trades) {
delete this.trades[symbol];
}
}
handleTickersUnsubscription(client, subscription) {
//
const subMessageHash = 'tickers';
const messageHash = 'unsubscribe:' + subMessageHash;
this.cleanUnsubscription(client, subMessageHash, messageHash);
const symbols = Object.keys(this.tickers);
for (let i = 0; i < symbols.length; i++) {
delete this.tickers[symbols[i]];
}
}
handleOHLCVUnsubscription(client, subscription) {
const coin = this.safeString(subscription, 'coin');
const marketId = this.coinToMarketId(coin);
const symbol = this.safeSymbol(marketId);
const interval = this.safeString(subscription, 'interval');
const timeframe = this.findTimeframe(interval);
const subMessageHash = 'candles:' + timeframe + ':' + symbol;
const messageHash = 'unsubscribe:' + subMessageHash;
this.cleanUnsubscription(client, subMessageHash, messageHash);
if (symbol in this.ohlcvs) {
if (timeframe in this.ohlcvs[symbol]) {
delete this.ohlcvs[symbol][timeframe];
}
}
}
handleSubscriptionResponse(client, message) {
// {
// "channel":"subscriptionResponse",
// "data":{
// "method":"unsubscribe",
// "subscription":{
// "type":"l2Book",
// "coin":"BTC",
// "nSigFigs":5,
// "mantissa":null
// }
// }
// }
//
// {
// "channel":"subscriptionResponse",
// "data":{
// "method":"unsubscribe",
// "subscription":{
// "type":"trades",
// "coin":"PURR/USDC"
// }
// }
// }
//
const data = this.safeDict(message, 'data', {});
const method = this.safeString(data, 'method');
if (method === 'unsubscribe') {
const subscription = this.safeDict(data, 'subscription', {});
const type = this.safeString(subscription, 'type');
if (type === 'l2Book') {
this.handleOrderBookUnsubscription(client, subscription);
}
else if (type === 'trades') {
this.handleTradesUnsubscription(client, subscription);
}
else if (type === 'webData2') {
this.handleTickersUnsubscription(client, subscription);
}
else if (type === 'candle') {
this.handleOHLCVUnsubscription(client, subscription);
}
}
}
handleMessage(client, message) {
//
// {
// "channel":"subscriptionResponse",
// "data":{
// "method":"unsubscribe",
// "subscription":{
// "type":"l2Book",
// "coin":"BTC",
// "nSigFigs":5,
// "mantissa":null
// }
// }
// }
//
if (this.handleErrorMessage(client, message)) {
return;
}
const topic = this.safeString(message, 'channel', '');
const methods = {
'pong': this.handlePong,
'trades': this.handleTrades,
'l2Book': this.handleOrderBook,
'candle': this.handleOHLCV,
'orderUpdates': this.handleOrder,
'userFills': this.handleMyTrades,
'webData2': this.handleWsTickers,
'post': this.handleWsPost,
'subscriptionResponse': this.handleSubscriptionResponse,
};
const exacMethod = this.safeValue(methods, topic);
if (exacMethod !== undefined) {
exacMethod.call(this, client, message);
return;
}
const keys = Object.keys(methods);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (topic.indexOf(keys[i]) >= 0) {
const method = methods[key];
method.call(this, client, message);
return;
}
}
}
ping(client) {
return {
'method': 'ping',
};
}
handlePong(client, message) {
//
// {
// "channel": "pong"
// }
//
client.lastPong = this.safeInteger(message, 'pong');
return message;
}
requestId() {
const requestId = this.sum(this.safeInteger(this.options, 'requestId', 0), 1);
this.options['requestId'] = requestId;
return requestId;
}
wrapAsPostAction(request) {
const requestId = this.requestId();
return {
'requestId': requestId,
'request': {
'method': 'post',
'id': requestId,
'request': {
'type': 'action',
'payload': request,
},
},
};
}
}