@luxfi/dex 1.2.1
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/index.d.ts +116 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +225 -0
- package/dist/marketData.d.ts +152 -0
- package/dist/marketData.d.ts.map +1 -0
- package/dist/marketData.js +253 -0
- package/package.json +41 -0
- package/src/index.ts +309 -0
- package/src/marketData.ts +351 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Market data, liquidation, and settlement features for LX TypeScript SDK
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.MarketDataProviders = exports.LiquidationMonitor = exports.MarketDataClient = void 0;
|
|
10
|
+
const ws_1 = __importDefault(require("ws"));
|
|
11
|
+
class MarketDataClient {
|
|
12
|
+
constructor(jsonRpcClient) {
|
|
13
|
+
this.jsonRpc = jsonRpcClient;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get market data from a specific source
|
|
17
|
+
*/
|
|
18
|
+
async getMarketData(symbol, source) {
|
|
19
|
+
const result = await this.jsonRpc.call('market_data.get', {
|
|
20
|
+
symbol,
|
|
21
|
+
source
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
name: result.name,
|
|
25
|
+
symbol: result.symbol,
|
|
26
|
+
price: result.price,
|
|
27
|
+
bid: result.bid,
|
|
28
|
+
ask: result.ask,
|
|
29
|
+
volume: result.volume,
|
|
30
|
+
latencyNs: result.latency_ns,
|
|
31
|
+
provider: result.provider
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get aggregated market data from all sources
|
|
36
|
+
*/
|
|
37
|
+
async getAggregatedMarketData(symbol) {
|
|
38
|
+
const result = await this.jsonRpc.call('market_data.aggregate', {
|
|
39
|
+
symbol
|
|
40
|
+
});
|
|
41
|
+
return result.map((data) => ({
|
|
42
|
+
name: data.name,
|
|
43
|
+
symbol: data.symbol,
|
|
44
|
+
price: data.price,
|
|
45
|
+
bid: data.bid,
|
|
46
|
+
ask: data.ask,
|
|
47
|
+
volume: data.volume,
|
|
48
|
+
latencyNs: data.latency_ns,
|
|
49
|
+
provider: data.provider
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get recent liquidations
|
|
54
|
+
*/
|
|
55
|
+
async getLiquidations(symbol, limit = 100) {
|
|
56
|
+
const result = await this.jsonRpc.call('liquidations.get', {
|
|
57
|
+
symbol,
|
|
58
|
+
limit
|
|
59
|
+
});
|
|
60
|
+
return result.map((liq) => ({
|
|
61
|
+
userId: liq.user_id,
|
|
62
|
+
positionId: liq.position_id,
|
|
63
|
+
symbol: liq.symbol,
|
|
64
|
+
size: liq.size,
|
|
65
|
+
liquidationPrice: liq.liquidation_price,
|
|
66
|
+
markPrice: liq.mark_price,
|
|
67
|
+
status: liq.status,
|
|
68
|
+
timestamp: new Date(liq.timestamp)
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get settlement batch information
|
|
73
|
+
*/
|
|
74
|
+
async getSettlementBatch(batchId) {
|
|
75
|
+
const result = await this.jsonRpc.call('settlement.batch', {
|
|
76
|
+
batch_id: batchId
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
batchId: result.batch_id,
|
|
80
|
+
orderIds: result.order_ids,
|
|
81
|
+
status: result.status,
|
|
82
|
+
txHash: result.tx_hash,
|
|
83
|
+
gasUsed: result.gas_used,
|
|
84
|
+
timestamp: new Date(result.timestamp)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get margin information for a user
|
|
89
|
+
*/
|
|
90
|
+
async getMarginInfo(userId) {
|
|
91
|
+
const result = await this.jsonRpc.call('margin.info', {
|
|
92
|
+
user_id: userId
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
userId: result.user_id,
|
|
96
|
+
initialMargin: result.initial_margin,
|
|
97
|
+
maintenanceMargin: result.maintenance_margin,
|
|
98
|
+
marginRatio: result.margin_ratio,
|
|
99
|
+
freeMargin: result.free_margin,
|
|
100
|
+
marginLevel: result.margin_level
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check liquidation risk for a user
|
|
105
|
+
*/
|
|
106
|
+
async checkLiquidationRisk(userId) {
|
|
107
|
+
const result = await this.jsonRpc.call('margin.liquidation_risk', {
|
|
108
|
+
user_id: userId
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
userId: result.user_id,
|
|
112
|
+
riskLevel: result.risk_level,
|
|
113
|
+
marginLevel: result.margin_level,
|
|
114
|
+
liquidationPrice: result.liquidation_price,
|
|
115
|
+
timeToLiquidation: result.time_to_liquidation,
|
|
116
|
+
recommendations: result.recommendations || []
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get insurance fund status
|
|
121
|
+
*/
|
|
122
|
+
async getInsuranceFundStatus() {
|
|
123
|
+
const result = await this.jsonRpc.call('insurance_fund.status');
|
|
124
|
+
return {
|
|
125
|
+
totalFund: result.total_fund,
|
|
126
|
+
availableFund: result.available_fund,
|
|
127
|
+
usedFund: result.used_fund,
|
|
128
|
+
pendingClaims: result.pending_claims,
|
|
129
|
+
lastUpdate: new Date(result.last_update)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get list of available market data sources
|
|
134
|
+
*/
|
|
135
|
+
async getMarketDataSources() {
|
|
136
|
+
return await this.jsonRpc.call('market_data.sources');
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get comprehensive market statistics
|
|
140
|
+
*/
|
|
141
|
+
async getMarketStats(symbol) {
|
|
142
|
+
const result = await this.jsonRpc.call('market.stats', {
|
|
143
|
+
symbol
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
symbol: result.symbol,
|
|
147
|
+
volume24h: result.volume_24h,
|
|
148
|
+
high24h: result.high_24h,
|
|
149
|
+
low24h: result.low_24h,
|
|
150
|
+
priceChange24h: result.price_change_24h,
|
|
151
|
+
priceChangePercent24h: result.price_change_percent_24h,
|
|
152
|
+
openInterest: result.open_interest,
|
|
153
|
+
fundingRate: result.funding_rate,
|
|
154
|
+
nextFundingTime: new Date(result.next_funding_time)
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.MarketDataClient = MarketDataClient;
|
|
159
|
+
class LiquidationMonitor {
|
|
160
|
+
constructor(wsConnection) {
|
|
161
|
+
this.callbacks = new Map();
|
|
162
|
+
this.ws = wsConnection;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Set WebSocket connection
|
|
166
|
+
*/
|
|
167
|
+
setWebSocket(ws) {
|
|
168
|
+
this.ws = ws;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Subscribe to liquidation events
|
|
172
|
+
*/
|
|
173
|
+
subscribeLiquidations(callback) {
|
|
174
|
+
if (!this.callbacks.has('liquidations')) {
|
|
175
|
+
this.callbacks.set('liquidations', []);
|
|
176
|
+
}
|
|
177
|
+
this.callbacks.get('liquidations').push(callback);
|
|
178
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
179
|
+
this.ws.send(JSON.stringify({
|
|
180
|
+
type: 'subscribe',
|
|
181
|
+
channel: 'liquidations'
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Subscribe to settlement events
|
|
187
|
+
*/
|
|
188
|
+
subscribeSettlements(callback) {
|
|
189
|
+
if (!this.callbacks.has('settlements')) {
|
|
190
|
+
this.callbacks.set('settlements', []);
|
|
191
|
+
}
|
|
192
|
+
this.callbacks.get('settlements').push(callback);
|
|
193
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
194
|
+
this.ws.send(JSON.stringify({
|
|
195
|
+
type: 'subscribe',
|
|
196
|
+
channel: 'settlements'
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Subscribe to margin call events for a user
|
|
202
|
+
*/
|
|
203
|
+
subscribeMarginCalls(userId, callback) {
|
|
204
|
+
const channel = `margin_calls:${userId}`;
|
|
205
|
+
if (!this.callbacks.has(channel)) {
|
|
206
|
+
this.callbacks.set(channel, []);
|
|
207
|
+
}
|
|
208
|
+
this.callbacks.get(channel).push(callback);
|
|
209
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
210
|
+
this.ws.send(JSON.stringify({
|
|
211
|
+
type: 'subscribe',
|
|
212
|
+
channel
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Unsubscribe from a channel
|
|
218
|
+
*/
|
|
219
|
+
unsubscribe(channel) {
|
|
220
|
+
this.callbacks.delete(channel);
|
|
221
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
222
|
+
this.ws.send(JSON.stringify({
|
|
223
|
+
type: 'unsubscribe',
|
|
224
|
+
channel
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Handle incoming message
|
|
230
|
+
*/
|
|
231
|
+
handleMessage(channel, data) {
|
|
232
|
+
const callbacks = this.callbacks.get(channel);
|
|
233
|
+
if (callbacks) {
|
|
234
|
+
callbacks.forEach(cb => cb(data));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.LiquidationMonitor = LiquidationMonitor;
|
|
239
|
+
/**
|
|
240
|
+
* Market data sources supported by LX
|
|
241
|
+
*/
|
|
242
|
+
exports.MarketDataProviders = {
|
|
243
|
+
ALPACA: 'alpaca',
|
|
244
|
+
NYSE_ARCA: 'nyse_arca',
|
|
245
|
+
IEX_CLOUD: 'iex',
|
|
246
|
+
POLYGON: 'polygon',
|
|
247
|
+
CME_GROUP: 'cme',
|
|
248
|
+
REFINITIV: 'refinitiv',
|
|
249
|
+
ICE_DATA: 'ice',
|
|
250
|
+
BLOOMBERG: 'bloomberg',
|
|
251
|
+
NASDAQ_TOTALVIEW: 'nasdaq',
|
|
252
|
+
COINBASE_PRO: 'coinbase'
|
|
253
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@luxfi/dex",
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "TypeScript SDK for LX",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"lint": "eslint src/**/*.ts",
|
|
11
|
+
"prepublish": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"luxfi",
|
|
15
|
+
"dex",
|
|
16
|
+
"trading",
|
|
17
|
+
"blockchain",
|
|
18
|
+
"sdk"
|
|
19
|
+
],
|
|
20
|
+
"author": "Lux Network",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"axios": "^1.6.0",
|
|
24
|
+
"ws": "^8.14.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/jest": "^29.5.0",
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"@types/ws": "^8.5.0",
|
|
30
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
31
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
32
|
+
"eslint": "^8.0.0",
|
|
33
|
+
"jest": "^29.5.0",
|
|
34
|
+
"ts-jest": "^29.1.0",
|
|
35
|
+
"typescript": "^5.0.0"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/luxfi/dex.git"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import {
|
|
4
|
+
MarketDataClient,
|
|
5
|
+
LiquidationMonitor,
|
|
6
|
+
MarketDataSource,
|
|
7
|
+
LiquidationInfo,
|
|
8
|
+
SettlementBatch,
|
|
9
|
+
MarginInfo,
|
|
10
|
+
MarketDataProviders
|
|
11
|
+
} from './marketData';
|
|
12
|
+
|
|
13
|
+
// Types
|
|
14
|
+
export enum OrderType {
|
|
15
|
+
LIMIT = 0,
|
|
16
|
+
MARKET = 1,
|
|
17
|
+
STOP = 2,
|
|
18
|
+
STOP_LIMIT = 3,
|
|
19
|
+
ICEBERG = 4,
|
|
20
|
+
PEG = 5
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export enum OrderSide {
|
|
24
|
+
BUY = 0,
|
|
25
|
+
SELL = 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export enum OrderStatus {
|
|
29
|
+
OPEN = 'open',
|
|
30
|
+
PARTIAL = 'partial',
|
|
31
|
+
FILLED = 'filled',
|
|
32
|
+
CANCELLED = 'cancelled',
|
|
33
|
+
REJECTED = 'rejected'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export enum TimeInForce {
|
|
37
|
+
GTC = 'GTC',
|
|
38
|
+
IOC = 'IOC',
|
|
39
|
+
FOK = 'FOK',
|
|
40
|
+
DAY = 'DAY'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Order {
|
|
44
|
+
orderId?: number;
|
|
45
|
+
symbol: string;
|
|
46
|
+
type: OrderType;
|
|
47
|
+
side: OrderSide;
|
|
48
|
+
price: number;
|
|
49
|
+
size: number;
|
|
50
|
+
userID?: string;
|
|
51
|
+
clientID?: string;
|
|
52
|
+
timeInForce?: TimeInForce;
|
|
53
|
+
postOnly?: boolean;
|
|
54
|
+
reduceOnly?: boolean;
|
|
55
|
+
status?: OrderStatus;
|
|
56
|
+
filled?: number;
|
|
57
|
+
remaining?: number;
|
|
58
|
+
timestamp?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface Trade {
|
|
62
|
+
tradeId: number;
|
|
63
|
+
symbol: string;
|
|
64
|
+
price: number;
|
|
65
|
+
size: number;
|
|
66
|
+
side: OrderSide;
|
|
67
|
+
buyOrderId: number;
|
|
68
|
+
sellOrderId: number;
|
|
69
|
+
buyerId: string;
|
|
70
|
+
sellerId: string;
|
|
71
|
+
timestamp: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface OrderBookLevel {
|
|
75
|
+
price: number;
|
|
76
|
+
size: number;
|
|
77
|
+
count?: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface OrderBook {
|
|
81
|
+
symbol: string;
|
|
82
|
+
bids: OrderBookLevel[];
|
|
83
|
+
asks: OrderBookLevel[];
|
|
84
|
+
timestamp: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface NodeInfo {
|
|
88
|
+
version: string;
|
|
89
|
+
network: string;
|
|
90
|
+
orderCount: number;
|
|
91
|
+
tradeCount: number;
|
|
92
|
+
timestamp: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface LXDexConfig {
|
|
96
|
+
jsonRpcUrl?: string;
|
|
97
|
+
wsUrl?: string;
|
|
98
|
+
grpcUrl?: string;
|
|
99
|
+
apiKey?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// JSON-RPC Client
|
|
103
|
+
class JSONRPCClient {
|
|
104
|
+
private axios: AxiosInstance;
|
|
105
|
+
private idCounter = 1;
|
|
106
|
+
|
|
107
|
+
constructor(baseURL: string) {
|
|
108
|
+
this.axios = axios.create({
|
|
109
|
+
baseURL,
|
|
110
|
+
headers: {
|
|
111
|
+
'Content-Type': 'application/json'
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async call(method: string, params: any = {}): Promise<any> {
|
|
117
|
+
const response = await this.axios.post('/rpc', {
|
|
118
|
+
jsonrpc: '2.0',
|
|
119
|
+
method,
|
|
120
|
+
params,
|
|
121
|
+
id: this.idCounter++
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (response.data.error) {
|
|
125
|
+
throw new Error(response.data.error.message);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return response.data.result;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Main SDK Client
|
|
133
|
+
export class LXDexClient {
|
|
134
|
+
private jsonRpc: JSONRPCClient;
|
|
135
|
+
private ws: WebSocket | null = null;
|
|
136
|
+
private wsCallbacks: Map<string, Function[]> = new Map();
|
|
137
|
+
private config: LXDexConfig;
|
|
138
|
+
public marketData: MarketDataClient;
|
|
139
|
+
public liquidationMonitor: LiquidationMonitor;
|
|
140
|
+
|
|
141
|
+
constructor(config: LXDexConfig = {}) {
|
|
142
|
+
this.config = {
|
|
143
|
+
jsonRpcUrl: config.jsonRpcUrl || 'http://localhost:8080',
|
|
144
|
+
wsUrl: config.wsUrl || 'ws://localhost:8081',
|
|
145
|
+
...config
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
this.jsonRpc = new JSONRPCClient(this.config.jsonRpcUrl!);
|
|
149
|
+
|
|
150
|
+
// Initialize sub-clients
|
|
151
|
+
this.marketData = new MarketDataClient(this.jsonRpc);
|
|
152
|
+
this.liquidationMonitor = new LiquidationMonitor(null);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Connection Management
|
|
156
|
+
async connect(): Promise<void> {
|
|
157
|
+
if (this.ws) return;
|
|
158
|
+
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
this.ws = new WebSocket(this.config.wsUrl!);
|
|
161
|
+
|
|
162
|
+
this.ws.on('open', () => {
|
|
163
|
+
console.log('WebSocket connected');
|
|
164
|
+
// Set WebSocket for liquidation monitor
|
|
165
|
+
this.liquidationMonitor.setWebSocket(this.ws!);
|
|
166
|
+
resolve();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
this.ws.on('message', (data: string) => {
|
|
170
|
+
try {
|
|
171
|
+
const msg = JSON.parse(data);
|
|
172
|
+
this.handleWebSocketMessage(msg);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error('Failed to parse WebSocket message:', err);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
this.ws.on('error', (err) => {
|
|
179
|
+
console.error('WebSocket error:', err);
|
|
180
|
+
reject(err);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
this.ws.on('close', () => {
|
|
184
|
+
console.log('WebSocket disconnected');
|
|
185
|
+
this.ws = null;
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
disconnect(): void {
|
|
191
|
+
if (this.ws) {
|
|
192
|
+
this.ws.close();
|
|
193
|
+
this.ws = null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Order Management
|
|
198
|
+
async placeOrder(order: Partial<Order>): Promise<{ orderId: number; status: string }> {
|
|
199
|
+
return this.jsonRpc.call('lx_placeOrder', order);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async cancelOrder(orderId: number): Promise<{ success: boolean; message: string }> {
|
|
203
|
+
return this.jsonRpc.call('lx_cancelOrder', { orderId });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async getOrder(orderId: number): Promise<Order> {
|
|
207
|
+
return this.jsonRpc.call('lx_getOrder', { orderId });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Market Data
|
|
211
|
+
async getOrderBook(symbol: string = 'BTC-USD', depth: number = 10): Promise<OrderBook> {
|
|
212
|
+
return this.jsonRpc.call('lx_getOrderBook', { symbol, depth });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async getBestBid(symbol: string = 'BTC-USD'): Promise<number> {
|
|
216
|
+
const result = await this.jsonRpc.call('lx_getBestBid', { symbol });
|
|
217
|
+
return result.price;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async getBestAsk(symbol: string = 'BTC-USD'): Promise<number> {
|
|
221
|
+
const result = await this.jsonRpc.call('lx_getBestAsk', { symbol });
|
|
222
|
+
return result.price;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async getTrades(symbol: string = 'BTC-USD', limit: number = 100): Promise<Trade[]> {
|
|
226
|
+
return this.jsonRpc.call('lx_getTrades', { symbol, limit });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Node Information
|
|
230
|
+
async getInfo(): Promise<NodeInfo> {
|
|
231
|
+
return this.jsonRpc.call('lx_getInfo');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async ping(): Promise<string> {
|
|
235
|
+
return this.jsonRpc.call('lx_ping');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// WebSocket Subscriptions
|
|
239
|
+
subscribe(channel: string, callback: Function): void {
|
|
240
|
+
if (!this.wsCallbacks.has(channel)) {
|
|
241
|
+
this.wsCallbacks.set(channel, []);
|
|
242
|
+
}
|
|
243
|
+
this.wsCallbacks.get(channel)!.push(callback);
|
|
244
|
+
|
|
245
|
+
// Send subscription message
|
|
246
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
247
|
+
this.ws.send(JSON.stringify({
|
|
248
|
+
type: 'subscribe',
|
|
249
|
+
channel
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
unsubscribe(channel: string, callback?: Function): void {
|
|
255
|
+
if (callback) {
|
|
256
|
+
const callbacks = this.wsCallbacks.get(channel);
|
|
257
|
+
if (callbacks) {
|
|
258
|
+
const index = callbacks.indexOf(callback);
|
|
259
|
+
if (index > -1) {
|
|
260
|
+
callbacks.splice(index, 1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
this.wsCallbacks.delete(channel);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Send unsubscribe message
|
|
268
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
269
|
+
this.ws.send(JSON.stringify({
|
|
270
|
+
type: 'unsubscribe',
|
|
271
|
+
channel
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
subscribeOrderBook(symbol: string, callback: (book: OrderBook) => void): void {
|
|
277
|
+
this.subscribe(`orderbook:${symbol}`, callback);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
subscribeTrades(symbol: string, callback: (trade: Trade) => void): void {
|
|
281
|
+
this.subscribe(`trades:${symbol}`, callback);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Private methods
|
|
285
|
+
private handleWebSocketMessage(msg: any): void {
|
|
286
|
+
const { channel, data } = msg;
|
|
287
|
+
const callbacks = this.wsCallbacks.get(channel);
|
|
288
|
+
if (callbacks) {
|
|
289
|
+
callbacks.forEach(cb => cb(data));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Utility methods
|
|
294
|
+
static formatPrice(price: number, decimals: number = 2): string {
|
|
295
|
+
return price.toFixed(decimals);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static formatSize(size: number, decimals: number = 8): string {
|
|
299
|
+
return size.toFixed(decimals);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
static calculateTotal(price: number, size: number): number {
|
|
303
|
+
return price * size;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Export everything
|
|
308
|
+
export default LXDexClient;
|
|
309
|
+
export * from './marketData';
|