klingex 1.0.0

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.js ADDED
@@ -0,0 +1,1098 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthenticationError: () => AuthenticationError,
24
+ InsufficientFundsError: () => InsufficientFundsError,
25
+ InvoicesEndpoint: () => InvoicesEndpoint,
26
+ KlingEx: () => KlingEx,
27
+ KlingExError: () => KlingExError,
28
+ KlingExWebSocket: () => KlingExWebSocket,
29
+ MarketsEndpoint: () => MarketsEndpoint,
30
+ OrdersEndpoint: () => OrdersEndpoint,
31
+ RateLimitError: () => RateLimitError,
32
+ ValidationError: () => ValidationError,
33
+ WalletEndpoint: () => WalletEndpoint,
34
+ default: () => KlingEx
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/types/index.ts
39
+ var KlingExError = class extends Error {
40
+ constructor(message, code, statusCode, details) {
41
+ super(message);
42
+ this.code = code;
43
+ this.statusCode = statusCode;
44
+ this.details = details;
45
+ this.name = "KlingExError";
46
+ }
47
+ };
48
+ var AuthenticationError = class extends KlingExError {
49
+ constructor(message = "Authentication failed") {
50
+ super(message, "AUTH_ERROR", 401);
51
+ this.name = "AuthenticationError";
52
+ }
53
+ };
54
+ var RateLimitError = class extends KlingExError {
55
+ constructor(message = "Rate limit exceeded", retryAfter) {
56
+ super(message, "RATE_LIMIT", 429);
57
+ this.retryAfter = retryAfter;
58
+ this.name = "RateLimitError";
59
+ }
60
+ };
61
+ var ValidationError = class extends KlingExError {
62
+ constructor(message, details) {
63
+ super(message, "VALIDATION_ERROR", 400, details);
64
+ this.name = "ValidationError";
65
+ }
66
+ };
67
+ var InsufficientFundsError = class extends KlingExError {
68
+ constructor(message = "Insufficient funds") {
69
+ super(message, "INSUFFICIENT_FUNDS", 400);
70
+ this.name = "InsufficientFundsError";
71
+ }
72
+ };
73
+
74
+ // src/http.ts
75
+ var HttpClient = class {
76
+ constructor(config) {
77
+ this.config = config;
78
+ }
79
+ /**
80
+ * Update authentication credentials
81
+ */
82
+ setAuth(auth) {
83
+ if (auth.apiKey) this.config.apiKey = auth.apiKey;
84
+ if (auth.jwt) this.config.jwt = auth.jwt;
85
+ }
86
+ /**
87
+ * Make an HTTP request to the API
88
+ */
89
+ async request(endpoint, options = {}) {
90
+ const { method = "GET", body, params, headers = {} } = options;
91
+ let url = `${this.config.baseUrl}${endpoint}`;
92
+ if (params) {
93
+ const searchParams = new URLSearchParams();
94
+ for (const [key, value] of Object.entries(params)) {
95
+ if (value !== void 0 && value !== null) {
96
+ searchParams.append(key, String(value));
97
+ }
98
+ }
99
+ const queryString = searchParams.toString();
100
+ if (queryString) {
101
+ url += `?${queryString}`;
102
+ }
103
+ }
104
+ const requestHeaders = {
105
+ "Content-Type": "application/json",
106
+ ...headers
107
+ };
108
+ if (this.config.apiKey) {
109
+ requestHeaders["X-API-Key"] = this.config.apiKey;
110
+ } else if (this.config.jwt) {
111
+ requestHeaders["Authorization"] = `Bearer ${this.config.jwt}`;
112
+ }
113
+ const controller = new AbortController();
114
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
115
+ try {
116
+ const response = await fetch(url, {
117
+ method,
118
+ headers: requestHeaders,
119
+ body: body ? JSON.stringify(body) : void 0,
120
+ signal: controller.signal
121
+ });
122
+ clearTimeout(timeoutId);
123
+ const contentType = response.headers.get("content-type");
124
+ let data;
125
+ if (contentType?.includes("application/json")) {
126
+ data = await response.json();
127
+ } else if (contentType?.includes("application/pdf")) {
128
+ data = await response.blob();
129
+ } else {
130
+ data = await response.text();
131
+ }
132
+ if (!response.ok) {
133
+ this.handleError(response.status, data);
134
+ }
135
+ return data;
136
+ } catch (error) {
137
+ clearTimeout(timeoutId);
138
+ if (error instanceof KlingExError) {
139
+ throw error;
140
+ }
141
+ if (error instanceof Error) {
142
+ if (error.name === "AbortError") {
143
+ throw new KlingExError("Request timeout", "TIMEOUT", 408);
144
+ }
145
+ throw new KlingExError(error.message, "NETWORK_ERROR");
146
+ }
147
+ throw new KlingExError("Unknown error occurred", "UNKNOWN");
148
+ }
149
+ }
150
+ /**
151
+ * Handle HTTP error responses
152
+ */
153
+ handleError(status, data) {
154
+ const errorMessage = this.extractErrorMessage(data);
155
+ switch (status) {
156
+ case 401:
157
+ throw new AuthenticationError(errorMessage);
158
+ case 429:
159
+ const retryAfter = this.extractRetryAfter(data);
160
+ throw new RateLimitError(errorMessage, retryAfter);
161
+ case 400:
162
+ if (errorMessage.toLowerCase().includes("insufficient")) {
163
+ throw new InsufficientFundsError(errorMessage);
164
+ }
165
+ throw new ValidationError(errorMessage, data);
166
+ default:
167
+ throw new KlingExError(errorMessage, "API_ERROR", status, data);
168
+ }
169
+ }
170
+ /**
171
+ * Extract error message from response
172
+ */
173
+ extractErrorMessage(data) {
174
+ if (typeof data === "string") return data;
175
+ if (typeof data === "object" && data !== null) {
176
+ const obj = data;
177
+ return String(obj.error || obj.message || obj.detail || "Unknown error");
178
+ }
179
+ return "Unknown error";
180
+ }
181
+ /**
182
+ * Extract retry-after value from rate limit response
183
+ */
184
+ extractRetryAfter(data) {
185
+ if (typeof data === "object" && data !== null) {
186
+ const obj = data;
187
+ if (typeof obj.retry_after === "number") {
188
+ return obj.retry_after;
189
+ }
190
+ }
191
+ return void 0;
192
+ }
193
+ // Convenience methods
194
+ async get(endpoint, params) {
195
+ return this.request(endpoint, { method: "GET", params });
196
+ }
197
+ async post(endpoint, body) {
198
+ return this.request(endpoint, { method: "POST", body });
199
+ }
200
+ async put(endpoint, body) {
201
+ return this.request(endpoint, { method: "PUT", body });
202
+ }
203
+ async delete(endpoint, body) {
204
+ return this.request(endpoint, { method: "DELETE", body });
205
+ }
206
+ };
207
+
208
+ // src/websocket.ts
209
+ var KlingExWebSocket = class {
210
+ constructor(url, auth, options = {}) {
211
+ this.ws = null;
212
+ this.subscriptions = /* @__PURE__ */ new Map();
213
+ this.reconnectAttempts = 0;
214
+ this.reconnectTimeout = null;
215
+ this.pingInterval = null;
216
+ this.isConnecting = false;
217
+ this.url = url;
218
+ this.apiKey = auth.apiKey;
219
+ this.jwt = auth.jwt;
220
+ this.options = {
221
+ reconnect: options.reconnect ?? true,
222
+ reconnectInterval: options.reconnectInterval ?? 5e3,
223
+ maxReconnectAttempts: options.maxReconnectAttempts ?? 10
224
+ };
225
+ }
226
+ /**
227
+ * Connect to WebSocket server
228
+ */
229
+ async connect() {
230
+ if (this.ws?.readyState === WebSocket.OPEN || this.isConnecting) {
231
+ return;
232
+ }
233
+ this.isConnecting = true;
234
+ return new Promise((resolve, reject) => {
235
+ try {
236
+ const wsUrl = new URL(this.url);
237
+ if (this.apiKey) {
238
+ wsUrl.searchParams.set("apiKey", this.apiKey);
239
+ } else if (this.jwt) {
240
+ wsUrl.searchParams.set("token", this.jwt);
241
+ }
242
+ this.ws = new WebSocket(wsUrl.toString());
243
+ this.ws.onopen = () => {
244
+ this.isConnecting = false;
245
+ this.reconnectAttempts = 0;
246
+ this.startPingInterval();
247
+ this.resubscribeAll();
248
+ resolve();
249
+ };
250
+ this.ws.onclose = (event) => {
251
+ this.isConnecting = false;
252
+ this.stopPingInterval();
253
+ if (this.options.reconnect && !event.wasClean) {
254
+ this.scheduleReconnect();
255
+ }
256
+ };
257
+ this.ws.onerror = (_event) => {
258
+ this.isConnecting = false;
259
+ const err = new Error("WebSocket error");
260
+ this.errorHandler?.(err);
261
+ reject(err);
262
+ };
263
+ this.ws.onmessage = (event) => {
264
+ this.handleMessage(event.data);
265
+ };
266
+ } catch (error) {
267
+ this.isConnecting = false;
268
+ reject(error);
269
+ }
270
+ });
271
+ }
272
+ /**
273
+ * Disconnect from WebSocket server
274
+ */
275
+ disconnect() {
276
+ this.options.reconnect = false;
277
+ this.stopPingInterval();
278
+ if (this.reconnectTimeout) {
279
+ clearTimeout(this.reconnectTimeout);
280
+ this.reconnectTimeout = null;
281
+ }
282
+ if (this.ws) {
283
+ this.ws.close(1e3, "Client disconnect");
284
+ this.ws = null;
285
+ }
286
+ this.subscriptions.clear();
287
+ }
288
+ /**
289
+ * Set error handler
290
+ */
291
+ onError(handler) {
292
+ this.errorHandler = handler;
293
+ }
294
+ /**
295
+ * Subscribe to orderbook updates
296
+ * @param symbol - Trading pair symbol
297
+ * @param handler - Callback for orderbook updates
298
+ */
299
+ orderbook(symbol, handler) {
300
+ return this.subscribe("orderbook", symbol, handler);
301
+ }
302
+ /**
303
+ * Subscribe to trade updates
304
+ * @param symbol - Trading pair symbol
305
+ * @param handler - Callback for trade updates
306
+ */
307
+ trades(symbol, handler) {
308
+ return this.subscribe("trades", symbol, handler);
309
+ }
310
+ /**
311
+ * Subscribe to ticker updates
312
+ * @param symbol - Trading pair symbol
313
+ * @param handler - Callback for ticker updates
314
+ */
315
+ ticker(symbol, handler) {
316
+ return this.subscribe("ticker", symbol, handler);
317
+ }
318
+ /**
319
+ * Subscribe to user order updates (requires auth)
320
+ * @param handler - Callback for order updates
321
+ */
322
+ userOrders(handler) {
323
+ return this.subscribe("user.orders", void 0, handler);
324
+ }
325
+ /**
326
+ * Subscribe to user balance updates (requires auth)
327
+ * @param handler - Callback for balance updates
328
+ */
329
+ userBalances(handler) {
330
+ return this.subscribe("user.balances", void 0, handler);
331
+ }
332
+ // =========================================================================
333
+ // Private methods
334
+ // =========================================================================
335
+ subscribe(channel, symbol, handler) {
336
+ const key = symbol ? `${channel}:${symbol}` : channel;
337
+ this.subscriptions.set(key, {
338
+ channel,
339
+ symbol,
340
+ handler
341
+ });
342
+ if (this.ws?.readyState === WebSocket.OPEN) {
343
+ this.sendSubscribe(channel, symbol);
344
+ }
345
+ return () => {
346
+ this.subscriptions.delete(key);
347
+ if (this.ws?.readyState === WebSocket.OPEN) {
348
+ this.sendUnsubscribe(channel, symbol);
349
+ }
350
+ };
351
+ }
352
+ sendSubscribe(channel, symbol) {
353
+ this.send({
354
+ action: "subscribe",
355
+ channel,
356
+ symbol
357
+ });
358
+ }
359
+ sendUnsubscribe(channel, symbol) {
360
+ this.send({
361
+ action: "unsubscribe",
362
+ channel,
363
+ symbol
364
+ });
365
+ }
366
+ send(data) {
367
+ if (this.ws?.readyState === WebSocket.OPEN) {
368
+ this.ws.send(JSON.stringify(data));
369
+ }
370
+ }
371
+ handleMessage(data) {
372
+ try {
373
+ const message = JSON.parse(data);
374
+ if (message.type === "pong") {
375
+ return;
376
+ }
377
+ const key = message.data && typeof message.data === "object" && "symbol" in message.data ? `${message.channel}:${message.data.symbol}` : message.channel;
378
+ const subscription = this.subscriptions.get(key) || this.subscriptions.get(message.channel);
379
+ if (subscription) {
380
+ subscription.handler(message.data);
381
+ }
382
+ } catch (error) {
383
+ this.errorHandler?.(error instanceof Error ? error : new Error("Failed to parse message"));
384
+ }
385
+ }
386
+ resubscribeAll() {
387
+ for (const [, subscription] of this.subscriptions) {
388
+ this.sendSubscribe(subscription.channel, subscription.symbol);
389
+ }
390
+ }
391
+ scheduleReconnect() {
392
+ if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
393
+ this.errorHandler?.(new Error("Max reconnection attempts reached"));
394
+ return;
395
+ }
396
+ this.reconnectAttempts++;
397
+ const delay = this.options.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1);
398
+ this.reconnectTimeout = setTimeout(() => {
399
+ this.connect().catch((error) => {
400
+ this.errorHandler?.(error instanceof Error ? error : new Error("Reconnection failed"));
401
+ });
402
+ }, delay);
403
+ }
404
+ startPingInterval() {
405
+ this.pingInterval = setInterval(() => {
406
+ this.send({ type: "ping" });
407
+ }, 3e4);
408
+ }
409
+ stopPingInterval() {
410
+ if (this.pingInterval) {
411
+ clearInterval(this.pingInterval);
412
+ this.pingInterval = null;
413
+ }
414
+ }
415
+ };
416
+
417
+ // src/endpoints/markets.ts
418
+ var MarketsEndpoint = class {
419
+ constructor(http) {
420
+ this.http = http;
421
+ }
422
+ /**
423
+ * Get all available markets/trading pairs
424
+ * @example
425
+ * const markets = await client.markets.list();
426
+ * // Returns full market info including:
427
+ * // - base_asset_symbol, quote_asset_symbol
428
+ * // - maker_fee_rate, taker_fee_rate
429
+ * // - last_price, volume_24h, priceChange24h
430
+ */
431
+ async list() {
432
+ return this.http.get("/api/markets");
433
+ }
434
+ /**
435
+ * Get a specific market by ID
436
+ * @param marketId - The market/trading pair ID
437
+ */
438
+ async get(marketId) {
439
+ const markets = await this.list();
440
+ return markets.find((m) => m.id === marketId);
441
+ }
442
+ /**
443
+ * Find a market by symbol pair
444
+ * @param baseSymbol - Base asset symbol (e.g., "BTC")
445
+ * @param quoteSymbol - Quote asset symbol (e.g., "USDT")
446
+ */
447
+ async findBySymbols(baseSymbol, quoteSymbol) {
448
+ const markets = await this.list();
449
+ return markets.find(
450
+ (m) => m.base_asset_symbol.toUpperCase() === baseSymbol.toUpperCase() && m.quote_asset_symbol.toUpperCase() === quoteSymbol.toUpperCase()
451
+ );
452
+ }
453
+ /**
454
+ * Get all tickers with 24h price data (CMC format)
455
+ * @example
456
+ * const tickers = await client.markets.tickers();
457
+ * // Returns ticker_id as "BTC_USDT" format
458
+ */
459
+ async tickers() {
460
+ return this.http.get("/api/tickers");
461
+ }
462
+ /**
463
+ * Get ticker for a specific symbol pair
464
+ * @param tickerId - Ticker ID in CMC format (e.g., "BTC_USDT")
465
+ */
466
+ async ticker(tickerId) {
467
+ const tickers = await this.tickers();
468
+ return tickers.find((t) => t.ticker_id === tickerId);
469
+ }
470
+ /**
471
+ * Get ticker by base and quote currency
472
+ * @param baseCurrency - Base currency symbol (e.g., "BTC")
473
+ * @param targetCurrency - Target/quote currency symbol (e.g., "USDT")
474
+ */
475
+ async tickerByPair(baseCurrency, targetCurrency) {
476
+ const tickers = await this.tickers();
477
+ return tickers.find(
478
+ (t) => t.base_currency.toUpperCase() === baseCurrency.toUpperCase() && t.target_currency.toUpperCase() === targetCurrency.toUpperCase()
479
+ );
480
+ }
481
+ /**
482
+ * Get orderbook for a trading pair
483
+ * @param marketId - The market/trading pair ID
484
+ * @param options - Options for the orderbook request
485
+ * @example
486
+ * const orderbook = await client.markets.orderbook(1);
487
+ * console.log('Best bid:', orderbook.bids[0]);
488
+ * console.log('Best ask:', orderbook.asks[0]);
489
+ */
490
+ async orderbook(marketId, options = {}) {
491
+ const response = await this.http.get("/api/orderbook", {
492
+ marketId,
493
+ isCmc: options.isCmc ?? false
494
+ });
495
+ return {
496
+ trading_pair_id: response.trading_pair_id,
497
+ base_symbol: response.base_symbol,
498
+ quote_symbol: response.quote_symbol,
499
+ bids: (response.bids || []).map(([price, quantity]) => ({ price, quantity })),
500
+ asks: (response.asks || []).map(([price, quantity]) => ({ price, quantity }))
501
+ };
502
+ }
503
+ /**
504
+ * Get raw orderbook (original API format with arrays)
505
+ * @param marketId - The market/trading pair ID
506
+ */
507
+ async orderbookRaw(marketId, options = {}) {
508
+ return this.http.get("/api/orderbook", {
509
+ marketId,
510
+ isCmc: options.isCmc ?? false
511
+ });
512
+ }
513
+ /**
514
+ * Get OHLCV (candlestick) data
515
+ * @param marketId - Trading pair ID
516
+ * @param timeframe - Candle timeframe (e.g., "1h", "1d")
517
+ * @param options - Additional options
518
+ * @example
519
+ * const candles = await client.markets.ohlcv(1, '1h');
520
+ */
521
+ async ohlcv(marketId, timeframe, options = {}) {
522
+ return this.http.get("/api/ohlcv", {
523
+ marketId,
524
+ timeframe,
525
+ limit: options.limit,
526
+ startDate: options.startDate,
527
+ endDate: options.endDate
528
+ });
529
+ }
530
+ /**
531
+ * Get all available assets
532
+ * @example
533
+ * const assets = await client.markets.assets();
534
+ */
535
+ async assets() {
536
+ const response = await this.http.get("/api/assets");
537
+ return response.assets || [];
538
+ }
539
+ /**
540
+ * Get a specific asset by symbol
541
+ * @param symbol - Asset symbol (e.g., "BTC")
542
+ */
543
+ async asset(symbol) {
544
+ const assets = await this.assets();
545
+ return assets.find((a) => a.symbol.toUpperCase() === symbol.toUpperCase());
546
+ }
547
+ /**
548
+ * Get recent trades for a trading pair
549
+ * @param marketId - Trading pair ID
550
+ * @param limit - Number of trades to return
551
+ */
552
+ async trades(marketId, limit = 50) {
553
+ return this.http.get("/api/trades", { marketId, limit });
554
+ }
555
+ };
556
+
557
+ // src/endpoints/orders.ts
558
+ var OrdersEndpoint = class {
559
+ constructor(http, humanReadableDefault = true) {
560
+ this.http = http;
561
+ this.humanReadableDefault = humanReadableDefault;
562
+ }
563
+ /**
564
+ * Submit a new order
565
+ * @param params - Order parameters
566
+ * @example
567
+ * // Buy 1.5 BTC at $50,000 (human-readable values)
568
+ * const order = await client.orders.submit({
569
+ * symbol: 'BTC-USDT',
570
+ * tradingPairId: 1,
571
+ * side: 'BUY',
572
+ * quantity: '1.5',
573
+ * price: '50000.00'
574
+ * });
575
+ *
576
+ * @example
577
+ * // Market order (price = 0)
578
+ * const order = await client.orders.submit({
579
+ * symbol: 'BTC-USDT',
580
+ * tradingPairId: 1,
581
+ * side: 'BUY',
582
+ * quantity: '1.0',
583
+ * price: '0',
584
+ * slippage: 0.01 // 1% slippage tolerance
585
+ * });
586
+ */
587
+ async submit(params) {
588
+ const rawValues = params.rawValues ?? !this.humanReadableDefault;
589
+ return this.http.post("/api/submit-order", {
590
+ symbol: params.symbol,
591
+ tradingPairId: params.tradingPairId,
592
+ side: params.side.toUpperCase(),
593
+ quantity: params.quantity,
594
+ price: params.price,
595
+ rawValues,
596
+ slippage: params.slippage
597
+ });
598
+ }
599
+ /**
600
+ * Cancel an existing order
601
+ * @param params - Order ID and trading pair ID
602
+ * @example
603
+ * const result = await client.orders.cancel({
604
+ * orderId: '7c9e6679-7425-40de-944b-e07fc1f90ae7',
605
+ * tradingPairId: 1
606
+ * });
607
+ * console.log(`Released balance: ${result.released_balance}`);
608
+ */
609
+ async cancel(params) {
610
+ return this.http.post("/api/cancel-order", {
611
+ orderId: params.orderId,
612
+ tradingPairId: params.tradingPairId
613
+ });
614
+ }
615
+ /**
616
+ * Cancel all open orders for a trading pair
617
+ * @param tradingPairId - Trading pair ID
618
+ * @example
619
+ * const result = await client.orders.cancelAll(1);
620
+ * console.log(`Cancelled ${result.cancelledCount} orders`);
621
+ */
622
+ async cancelAll(tradingPairId) {
623
+ return this.http.post("/api/cancel-all-orders", { tradingPairId });
624
+ }
625
+ /**
626
+ * Get your open orders
627
+ * @param params - Filter parameters
628
+ * @example
629
+ * // Get all open orders
630
+ * const orders = await client.orders.list();
631
+ *
632
+ * // Get orders for specific trading pair
633
+ * const orders = await client.orders.list({ tradingPairId: 1 });
634
+ */
635
+ async list(params = {}) {
636
+ const response = await this.http.get("/api/user-orders", {
637
+ tradingPairId: params.tradingPairId,
638
+ status: params.status,
639
+ limit: params.limit || 50
640
+ });
641
+ return response.orders || [];
642
+ }
643
+ /**
644
+ * Get order history (including filled/cancelled orders)
645
+ * @param params - Filter and pagination parameters
646
+ */
647
+ async history(params = {}) {
648
+ return this.http.get("/api/orders-history", {
649
+ tradingPairId: params.tradingPairId,
650
+ status: params.status,
651
+ limit: params.limit || 50,
652
+ offset: params.offset || 0
653
+ });
654
+ }
655
+ /**
656
+ * Get a specific order by ID
657
+ * @param orderId - Order UUID
658
+ */
659
+ async get(orderId) {
660
+ const orders = await this.list();
661
+ return orders.find((o) => o.id === orderId);
662
+ }
663
+ // =========================================================================
664
+ // Convenience methods for common order types
665
+ // =========================================================================
666
+ /**
667
+ * Place a limit buy order (human-readable values)
668
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
669
+ * @param tradingPairId - Trading pair ID
670
+ * @param quantity - Amount to buy
671
+ * @param price - Price per unit
672
+ */
673
+ async limitBuy(symbol, tradingPairId, quantity, price) {
674
+ return this.submit({
675
+ symbol,
676
+ tradingPairId,
677
+ side: "BUY",
678
+ quantity,
679
+ price
680
+ });
681
+ }
682
+ /**
683
+ * Place a limit sell order (human-readable values)
684
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
685
+ * @param tradingPairId - Trading pair ID
686
+ * @param quantity - Amount to sell
687
+ * @param price - Price per unit
688
+ */
689
+ async limitSell(symbol, tradingPairId, quantity, price) {
690
+ return this.submit({
691
+ symbol,
692
+ tradingPairId,
693
+ side: "SELL",
694
+ quantity,
695
+ price
696
+ });
697
+ }
698
+ /**
699
+ * Place a market buy order
700
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
701
+ * @param tradingPairId - Trading pair ID
702
+ * @param quantity - Amount to buy
703
+ * @param slippage - Slippage tolerance (0-1, e.g., 0.01 for 1%)
704
+ */
705
+ async marketBuy(symbol, tradingPairId, quantity, slippage = 0.01) {
706
+ return this.submit({
707
+ symbol,
708
+ tradingPairId,
709
+ side: "BUY",
710
+ quantity,
711
+ price: "0",
712
+ slippage
713
+ });
714
+ }
715
+ /**
716
+ * Place a market sell order
717
+ * @param symbol - Trading pair symbol (e.g., "BTC-USDT")
718
+ * @param tradingPairId - Trading pair ID
719
+ * @param quantity - Amount to sell
720
+ * @param slippage - Slippage tolerance (0-1, e.g., 0.01 for 1%)
721
+ */
722
+ async marketSell(symbol, tradingPairId, quantity, slippage = 0.01) {
723
+ return this.submit({
724
+ symbol,
725
+ tradingPairId,
726
+ side: "SELL",
727
+ quantity,
728
+ price: "0",
729
+ slippage
730
+ });
731
+ }
732
+ };
733
+
734
+ // src/endpoints/wallet.ts
735
+ var WalletEndpoint = class {
736
+ constructor(http) {
737
+ this.http = http;
738
+ }
739
+ /**
740
+ * Get all wallet balances
741
+ * Each balance includes:
742
+ * - balance: Total balance (raw value)
743
+ * - locked_balance: Locked in orders (raw value)
744
+ * - wallet_id: UUID of the wallet
745
+ * - deposit_address: Deposit address for this asset
746
+ * - id, symbol, name, decimals: Asset info
747
+ * - min_deposit, min_withdrawal, withdrawal_fee: Limits
748
+ *
749
+ * @example
750
+ * const balances = await client.wallet.balances();
751
+ * const ethBalance = balances.find(b => b.symbol === 'ETH');
752
+ * console.log(`ETH Balance: ${ethBalance?.balance}`);
753
+ * console.log(`Deposit Address: ${ethBalance?.deposit_address}`);
754
+ */
755
+ async balances() {
756
+ const balances = await this.http.get("/api/user-balances");
757
+ return balances.map((b) => {
758
+ const balance = BigInt(b.balance);
759
+ const locked = BigInt(b.locked_balance);
760
+ const available = balance - locked;
761
+ const decimals = b.decimals;
762
+ return {
763
+ ...b,
764
+ available_balance: available.toString(),
765
+ human_balance: this.formatUnits(b.balance, decimals),
766
+ human_locked: this.formatUnits(b.locked_balance, decimals),
767
+ human_available: this.formatUnits(available.toString(), decimals)
768
+ };
769
+ });
770
+ }
771
+ /**
772
+ * Get balance for a specific asset
773
+ * @param symbol - Asset symbol (e.g., "BTC", "ETH", "USDT")
774
+ */
775
+ async balance(symbol) {
776
+ const balances = await this.balances();
777
+ return balances.find((b) => b.symbol.toUpperCase() === symbol.toUpperCase());
778
+ }
779
+ /**
780
+ * Get deposit address for an asset (from balance data)
781
+ * The deposit_address is included in the balance response
782
+ * @param symbol - Asset symbol (e.g., "BTC")
783
+ * @example
784
+ * const address = await client.wallet.depositAddress('ETH');
785
+ * console.log(`Send ETH to: ${address.address}`);
786
+ */
787
+ async depositAddress(symbol) {
788
+ const balances = await this.http.get("/api/user-balances");
789
+ const balance = balances.find((b) => b.symbol.toUpperCase() === symbol.toUpperCase());
790
+ if (!balance) {
791
+ return void 0;
792
+ }
793
+ return {
794
+ address: balance.deposit_address
795
+ };
796
+ }
797
+ /**
798
+ * Generate a new deposit address (if supported)
799
+ * @param assetId - Asset ID
800
+ */
801
+ async generateDepositAddress(assetId) {
802
+ const response = await this.http.post("/api/generate-deposit-address", {
803
+ assetId
804
+ });
805
+ return {
806
+ address: response.address,
807
+ memo: response.memo
808
+ };
809
+ }
810
+ /**
811
+ * Submit a withdrawal request
812
+ * @param params - Withdrawal parameters
813
+ * @example
814
+ * const result = await client.wallet.withdraw({
815
+ * assetId: 2,
816
+ * symbol: 'ETH',
817
+ * address: '0x...',
818
+ * amount: '0.1'
819
+ * });
820
+ *
821
+ * if (result.requires_2fa) {
822
+ * // Need to complete 2FA verification
823
+ * await client.wallet.confirm2FA(result.session_token, '123456');
824
+ * }
825
+ */
826
+ async withdraw(params) {
827
+ return this.http.post("/api/submit-withdraw", {
828
+ assetId: params.assetId,
829
+ symbol: params.symbol,
830
+ address: params.address,
831
+ amount: params.amount,
832
+ destinationTag: params.memo
833
+ });
834
+ }
835
+ /**
836
+ * Complete withdrawal 2FA verification
837
+ * @param sessionToken - Session token from withdraw response
838
+ * @param code - 2FA code from authenticator app
839
+ */
840
+ async confirm2FA(sessionToken, code) {
841
+ return this.http.post("/api/withdrawal/complete-2fa", {
842
+ sessionToken,
843
+ code
844
+ });
845
+ }
846
+ /**
847
+ * Get deposit history
848
+ * @param options - Pagination options
849
+ */
850
+ async deposits(options = {}) {
851
+ return this.http.get("/api/deposits", {
852
+ limit: options.limit || 50,
853
+ offset: options.offset || 0
854
+ });
855
+ }
856
+ /**
857
+ * Get withdrawal history
858
+ * @param options - Pagination options
859
+ */
860
+ async withdrawals(options = {}) {
861
+ return this.http.get("/api/withdrawals", {
862
+ limit: options.limit || 50,
863
+ offset: options.offset || 0
864
+ });
865
+ }
866
+ /**
867
+ * Get transaction history (deposits + withdrawals + trades)
868
+ */
869
+ async history() {
870
+ return this.http.get("/api/history");
871
+ }
872
+ /**
873
+ * Format raw units to human-readable decimal string
874
+ * @param value - Raw value as string
875
+ * @param decimals - Number of decimals
876
+ */
877
+ formatUnits(value, decimals) {
878
+ if (decimals === 0) return value;
879
+ const str = value.padStart(decimals + 1, "0");
880
+ const intPart = str.slice(0, -decimals) || "0";
881
+ const decPart = str.slice(-decimals);
882
+ const trimmedDec = decPart.replace(/0+$/, "");
883
+ return trimmedDec ? `${intPart}.${trimmedDec}` : intPart;
884
+ }
885
+ };
886
+
887
+ // src/endpoints/invoices.ts
888
+ var InvoicesEndpoint = class {
889
+ constructor(http) {
890
+ this.http = http;
891
+ }
892
+ /**
893
+ * Create a new payment invoice
894
+ * @param params - Invoice parameters
895
+ * @example
896
+ * const invoice = await client.invoices.create({
897
+ * amount: '100.00',
898
+ * asset: 'USDT',
899
+ * description: 'Order #12345',
900
+ * external_id: 'order-12345',
901
+ * webhook_url: 'https://yoursite.com/webhook'
902
+ * });
903
+ *
904
+ * console.log(`Payment address: ${invoice.payment_address}`);
905
+ * console.log(`Expires: ${invoice.expires_at}`);
906
+ */
907
+ async create(params) {
908
+ const response = await this.http.post("/api/invoices", {
909
+ amount: params.amount,
910
+ asset: params.asset,
911
+ description: params.description,
912
+ external_id: params.external_id,
913
+ webhook_url: params.webhook_url,
914
+ redirect_url: params.redirect_url,
915
+ expires_in: params.expires_in
916
+ });
917
+ if (!response.data) {
918
+ throw new Error("Failed to create invoice");
919
+ }
920
+ return response.data;
921
+ }
922
+ /**
923
+ * Get all invoices
924
+ * @param options - Pagination and filter options
925
+ */
926
+ async list(options = {}) {
927
+ return this.http.get("/api/invoices", {
928
+ limit: options.limit || 50,
929
+ offset: options.offset || 0,
930
+ status: options.status
931
+ });
932
+ }
933
+ /**
934
+ * Get a specific invoice by ID
935
+ * @param invoiceId - Invoice UUID
936
+ */
937
+ async get(invoiceId) {
938
+ const response = await this.http.get(`/api/invoices/${invoiceId}`);
939
+ if (!response.data) {
940
+ throw new Error("Invoice not found");
941
+ }
942
+ return response.data;
943
+ }
944
+ /**
945
+ * Get invoice status (for polling)
946
+ * @param invoiceId - Invoice UUID
947
+ */
948
+ async status(invoiceId) {
949
+ return this.http.get(`/api/invoices/${invoiceId}/status`);
950
+ }
951
+ /**
952
+ * Cancel a pending invoice
953
+ * @param invoiceId - Invoice UUID
954
+ */
955
+ async cancel(invoiceId) {
956
+ return this.http.post(`/api/invoices/${invoiceId}/cancel`);
957
+ }
958
+ /**
959
+ * Get invoice PDF
960
+ * @param invoiceId - Invoice UUID
961
+ * @returns PDF blob
962
+ */
963
+ async pdf(invoiceId) {
964
+ return this.http.get(`/api/invoices/${invoiceId}/pdf`);
965
+ }
966
+ /**
967
+ * Get estimated fees for an invoice
968
+ * @param asset - Asset symbol
969
+ * @param amount - Invoice amount
970
+ */
971
+ async fees(asset, amount) {
972
+ const response = await this.http.get("/api/invoices/fees", {
973
+ asset,
974
+ amount
975
+ });
976
+ if (!response.data) {
977
+ return {
978
+ network_fee: "0",
979
+ service_fee: "0",
980
+ total_fee: "0"
981
+ };
982
+ }
983
+ return response.data;
984
+ }
985
+ /**
986
+ * Get public invoice payment page data (no auth required)
987
+ * @param invoiceId - Invoice UUID
988
+ */
989
+ async paymentPage(invoiceId) {
990
+ return this.http.get(`/api/invoices/${invoiceId}/pay`);
991
+ }
992
+ };
993
+
994
+ // src/client.ts
995
+ var DEFAULT_BASE_URL = "https://api.klingex.io";
996
+ var DEFAULT_WS_URL = "wss://api.klingex.io/ws";
997
+ var DEFAULT_TIMEOUT = 3e4;
998
+ var KlingEx = class {
999
+ constructor(config = {}) {
1000
+ this._ws = null;
1001
+ this.config = {
1002
+ apiKey: config.apiKey,
1003
+ jwt: config.jwt,
1004
+ baseUrl: config.baseUrl || DEFAULT_BASE_URL,
1005
+ wsUrl: config.wsUrl || DEFAULT_WS_URL,
1006
+ timeout: config.timeout || DEFAULT_TIMEOUT,
1007
+ humanReadable: config.humanReadable ?? true
1008
+ };
1009
+ this.http = new HttpClient({
1010
+ baseUrl: this.config.baseUrl,
1011
+ apiKey: this.config.apiKey,
1012
+ jwt: this.config.jwt,
1013
+ timeout: this.config.timeout
1014
+ });
1015
+ this.markets = new MarketsEndpoint(this.http);
1016
+ this.orders = new OrdersEndpoint(this.http, this.config.humanReadable);
1017
+ this.wallet = new WalletEndpoint(this.http);
1018
+ this.invoices = new InvoicesEndpoint(this.http);
1019
+ }
1020
+ /**
1021
+ * WebSocket client for real-time data
1022
+ * Note: Call ws.connect() before subscribing to channels
1023
+ */
1024
+ get ws() {
1025
+ if (!this._ws) {
1026
+ this._ws = new KlingExWebSocket(
1027
+ this.config.wsUrl,
1028
+ { apiKey: this.config.apiKey, jwt: this.config.jwt }
1029
+ );
1030
+ }
1031
+ return this._ws;
1032
+ }
1033
+ /**
1034
+ * Create WebSocket connection with custom options
1035
+ */
1036
+ createWebSocket(options) {
1037
+ return new KlingExWebSocket(
1038
+ this.config.wsUrl,
1039
+ { apiKey: this.config.apiKey, jwt: this.config.jwt },
1040
+ options
1041
+ );
1042
+ }
1043
+ /**
1044
+ * Update authentication credentials
1045
+ */
1046
+ setAuth(auth) {
1047
+ if (auth.apiKey) this.config.apiKey = auth.apiKey;
1048
+ if (auth.jwt) this.config.jwt = auth.jwt;
1049
+ this.http.setAuth(auth);
1050
+ }
1051
+ /**
1052
+ * Check if client has authentication configured
1053
+ */
1054
+ get isAuthenticated() {
1055
+ return !!(this.config.apiKey || this.config.jwt);
1056
+ }
1057
+ /**
1058
+ * Get the configured base URL
1059
+ */
1060
+ get baseUrl() {
1061
+ return this.config.baseUrl;
1062
+ }
1063
+ // =========================================================================
1064
+ // Account endpoints (directly on client for convenience)
1065
+ // =========================================================================
1066
+ /**
1067
+ * Get current user profile
1068
+ */
1069
+ async getProfile() {
1070
+ return this.http.get("/api/profile");
1071
+ }
1072
+ /**
1073
+ * Get current user info
1074
+ */
1075
+ async getUser() {
1076
+ return this.http.get("/api/user");
1077
+ }
1078
+ /**
1079
+ * Get API key statistics
1080
+ */
1081
+ async getApiKeyStats() {
1082
+ return this.http.get("/api/api-keys/stats");
1083
+ }
1084
+ };
1085
+ // Annotate the CommonJS export names for ESM import in node:
1086
+ 0 && (module.exports = {
1087
+ AuthenticationError,
1088
+ InsufficientFundsError,
1089
+ InvoicesEndpoint,
1090
+ KlingEx,
1091
+ KlingExError,
1092
+ KlingExWebSocket,
1093
+ MarketsEndpoint,
1094
+ OrdersEndpoint,
1095
+ RateLimitError,
1096
+ ValidationError,
1097
+ WalletEndpoint
1098
+ });