@pipsend/sdk 0.1.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.mjs ADDED
@@ -0,0 +1,1005 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/types/index.ts
9
+ var PipsendError = class extends Error {
10
+ constructor(message, code, statusCode) {
11
+ super(message);
12
+ this.code = code;
13
+ this.statusCode = statusCode;
14
+ this.name = "PipsendError";
15
+ }
16
+ };
17
+ var AuthenticationError = class extends PipsendError {
18
+ constructor(message) {
19
+ super(message, "AUTH_ERROR", 401);
20
+ this.name = "AuthenticationError";
21
+ }
22
+ };
23
+ var ConfigurationError = class extends PipsendError {
24
+ constructor(message) {
25
+ super(message, "CONFIG_ERROR");
26
+ this.name = "ConfigurationError";
27
+ }
28
+ };
29
+ var WebSocketError = class extends PipsendError {
30
+ constructor(message) {
31
+ super(message, "WEBSOCKET_ERROR");
32
+ this.name = "WebSocketError";
33
+ }
34
+ };
35
+
36
+ // src/core/auth.ts
37
+ var AuthManager = class {
38
+ constructor(config) {
39
+ this.config = config;
40
+ const marginMinutes = config.refreshMarginMinutes ?? 5;
41
+ this.refreshMarginMs = marginMinutes * 60 * 1e3;
42
+ }
43
+ /**
44
+ * Checks if a valid token is available
45
+ */
46
+ isAuthenticated() {
47
+ return !!this.tokenInfo && !this.isTokenExpired();
48
+ }
49
+ /**
50
+ * Gets the current token (if it exists and is valid)
51
+ */
52
+ getToken() {
53
+ if (this.isAuthenticated()) {
54
+ return this.tokenInfo?.token;
55
+ }
56
+ return void 0;
57
+ }
58
+ /**
59
+ * Ensures a valid token exists, authenticating if necessary
60
+ */
61
+ async ensureAuthenticated() {
62
+ if (!this.isAuthenticated()) {
63
+ await this.authenticate();
64
+ }
65
+ return this.tokenInfo.token;
66
+ }
67
+ /**
68
+ * Performs authentication with the server
69
+ * TODO: Implement real API call when available
70
+ */
71
+ async authenticate() {
72
+ try {
73
+ const mockResponse = {
74
+ token: "mock_token_" + Date.now(),
75
+ expiresIn: 180 * 60
76
+ // 180 minutes in seconds
77
+ };
78
+ this.setTokenInfo(mockResponse);
79
+ console.log("\u2705 Authentication successful (mock)");
80
+ } catch (error) {
81
+ if (error instanceof AuthenticationError) {
82
+ throw error;
83
+ }
84
+ throw new AuthenticationError(
85
+ `Authentication error: ${error instanceof Error ? error.message : "Unknown error"}`
86
+ );
87
+ }
88
+ }
89
+ /**
90
+ * Forces a re-authentication
91
+ */
92
+ async refreshToken() {
93
+ this.tokenInfo = void 0;
94
+ await this.authenticate();
95
+ }
96
+ /**
97
+ * Clears the current token
98
+ */
99
+ clearToken() {
100
+ this.tokenInfo = void 0;
101
+ }
102
+ /**
103
+ * Checks if the token is expired or about to expire
104
+ */
105
+ isTokenExpired() {
106
+ if (!this.tokenInfo) return true;
107
+ const now = Date.now();
108
+ const expiryWithMargin = this.tokenInfo.expiresAt.getTime() - this.refreshMarginMs;
109
+ return now >= expiryWithMargin;
110
+ }
111
+ /**
112
+ * Stores the received token information
113
+ */
114
+ setTokenInfo(response) {
115
+ const now = /* @__PURE__ */ new Date();
116
+ const expiresAt = new Date(now.getTime() + response.expiresIn * 1e3);
117
+ this.tokenInfo = {
118
+ token: response.token,
119
+ issuedAt: now,
120
+ expiresAt
121
+ };
122
+ }
123
+ };
124
+
125
+ // src/core/websocket.ts
126
+ var WebSocketManager = class {
127
+ constructor(config, authManager) {
128
+ this.config = config;
129
+ this.authManager = authManager;
130
+ // WebSocket instance (browser or ws package)
131
+ this.reconnectAttempts = 0;
132
+ this.listeners = /* @__PURE__ */ new Map();
133
+ this.isConnected = false;
134
+ this.subscribedChannels = [];
135
+ this.isReconnecting = false;
136
+ }
137
+ /**
138
+ * Connects to the WebSocket server
139
+ */
140
+ async connect() {
141
+ if (this.isConnected) {
142
+ console.log("\u26A0\uFE0F WebSocket already connected");
143
+ return;
144
+ }
145
+ if (this.isReconnecting) {
146
+ console.log("\u26A0\uFE0F WebSocket reconnection in progress");
147
+ return;
148
+ }
149
+ const wsUrl = this.buildWebSocketUrl();
150
+ const token = await this.authManager.ensureAuthenticated();
151
+ return new Promise((resolve, reject) => {
152
+ try {
153
+ const WS = this.getWebSocketConstructor();
154
+ this.ws = new WS(wsUrl);
155
+ this.ws.onopen = () => {
156
+ console.log("\u2705 WebSocket connected");
157
+ this.isConnected = true;
158
+ this.isReconnecting = false;
159
+ this.reconnectAttempts = 0;
160
+ this.send({
161
+ type: "auth",
162
+ payload: { token },
163
+ timestamp: Date.now()
164
+ });
165
+ this.emit("connected", { timestamp: Date.now() });
166
+ if (this.subscribedChannels.length > 0) {
167
+ console.log("\u{1F504} Resubscribing to channels:", this.subscribedChannels);
168
+ this.subscribe(this.subscribedChannels);
169
+ }
170
+ this.startHeartbeat();
171
+ resolve();
172
+ };
173
+ this.ws.onmessage = (event) => {
174
+ try {
175
+ const data = JSON.parse(event.data);
176
+ this.handleMessage(data);
177
+ } catch (error) {
178
+ console.error("\u274C Failed to parse WebSocket message:", error);
179
+ }
180
+ };
181
+ this.ws.onerror = (error) => {
182
+ console.error("\u274C WebSocket error:", error);
183
+ this.emit("error", error);
184
+ if (!this.isConnected) {
185
+ reject(new WebSocketError("Failed to connect to WebSocket server"));
186
+ }
187
+ };
188
+ this.ws.onclose = (event) => {
189
+ console.log("WebSocket disconnected", event.code, event.reason);
190
+ this.isConnected = false;
191
+ this.stopHeartbeat();
192
+ this.emit("disconnected", {
193
+ code: event.code,
194
+ reason: event.reason,
195
+ timestamp: Date.now()
196
+ });
197
+ this.handleReconnect();
198
+ };
199
+ } catch (error) {
200
+ reject(new WebSocketError(
201
+ `Failed to create WebSocket: ${error instanceof Error ? error.message : "Unknown error"}`
202
+ ));
203
+ }
204
+ });
205
+ }
206
+ /**
207
+ * Subscribes to channels (symbols, events, etc.)
208
+ * TODO: Adjust message format according to real WebSocket API
209
+ */
210
+ subscribe(channels) {
211
+ if (!this.isConnected) {
212
+ throw new WebSocketError("WebSocket not connected. Call connect() first.");
213
+ }
214
+ this.subscribedChannels = [.../* @__PURE__ */ new Set([...this.subscribedChannels, ...channels])];
215
+ this.send({
216
+ type: "subscribe",
217
+ payload: { channels },
218
+ timestamp: Date.now()
219
+ });
220
+ console.log("\u{1F4E1} Subscribed to channels:", channels);
221
+ }
222
+ /**
223
+ * Unsubscribes from channels
224
+ * TODO: Adjust message format according to real WebSocket API
225
+ */
226
+ unsubscribe(channels) {
227
+ if (!this.isConnected) {
228
+ console.warn("WebSocket not connected, cannot unsubscribe");
229
+ return;
230
+ }
231
+ this.subscribedChannels = this.subscribedChannels.filter(
232
+ (ch) => !channels.includes(ch)
233
+ );
234
+ this.send({
235
+ type: "unsubscribe",
236
+ payload: { channels },
237
+ timestamp: Date.now()
238
+ });
239
+ console.log("\u{1F4E1} Unsubscribed from channels:", channels);
240
+ }
241
+ /**
242
+ * Registers an event listener
243
+ */
244
+ on(event, callback) {
245
+ if (!this.listeners.has(event)) {
246
+ this.listeners.set(event, /* @__PURE__ */ new Set());
247
+ }
248
+ this.listeners.get(event).add(callback);
249
+ }
250
+ /**
251
+ * Removes an event listener
252
+ */
253
+ off(event, callback) {
254
+ if (!callback) {
255
+ this.listeners.delete(event);
256
+ } else {
257
+ this.listeners.get(event)?.delete(callback);
258
+ }
259
+ }
260
+ /**
261
+ * Disconnects from WebSocket
262
+ */
263
+ disconnect() {
264
+ console.log("\u{1F44B} Disconnecting WebSocket...");
265
+ this.stopHeartbeat();
266
+ if (this.ws) {
267
+ this.ws.close();
268
+ this.ws = void 0;
269
+ }
270
+ this.isConnected = false;
271
+ this.isReconnecting = false;
272
+ this.subscribedChannels = [];
273
+ this.listeners.clear();
274
+ }
275
+ /**
276
+ * Checks if WebSocket is connected
277
+ */
278
+ isWebSocketConnected() {
279
+ return this.isConnected;
280
+ }
281
+ /**
282
+ * Gets list of subscribed channels
283
+ */
284
+ getSubscribedChannels() {
285
+ return [...this.subscribedChannels];
286
+ }
287
+ // Private methods
288
+ /**
289
+ * Builds WebSocket URL from HTTP(S) server URL
290
+ */
291
+ buildWebSocketUrl() {
292
+ try {
293
+ const url = new URL(this.config.server);
294
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
295
+ const wsPath = this.config.websocket?.path || "/ws";
296
+ url.pathname = wsPath;
297
+ return url.toString();
298
+ } catch (error) {
299
+ throw new WebSocketError("Invalid server URL for WebSocket connection");
300
+ }
301
+ }
302
+ /**
303
+ * Gets WebSocket constructor (browser or Node.js)
304
+ */
305
+ getWebSocketConstructor() {
306
+ if (typeof WebSocket !== "undefined") {
307
+ return WebSocket;
308
+ }
309
+ try {
310
+ return __require("ws");
311
+ } catch {
312
+ throw new WebSocketError(
313
+ 'WebSocket not available. Install "ws" package for Node.js: npm install ws'
314
+ );
315
+ }
316
+ }
317
+ /**
318
+ * Sends a message through WebSocket
319
+ */
320
+ send(message) {
321
+ if (!this.ws || !this.isConnected) {
322
+ console.warn("\u26A0\uFE0F WebSocket not ready, message not sent:", message.type);
323
+ return;
324
+ }
325
+ try {
326
+ const payload = JSON.stringify(message);
327
+ this.ws.send(payload);
328
+ } catch (error) {
329
+ console.error("\u274C Failed to send WebSocket message:", error);
330
+ }
331
+ }
332
+ /**
333
+ * Handles incoming WebSocket messages
334
+ * TODO: Adjust message handling according to real WebSocket API
335
+ */
336
+ handleMessage(data) {
337
+ if (data.type === "authenticated") {
338
+ console.log("\u2705 WebSocket authenticated");
339
+ this.emit("authenticated", data.payload || {});
340
+ return;
341
+ }
342
+ if (data.type === "pong") {
343
+ return;
344
+ }
345
+ const eventType = data.type;
346
+ this.emit(eventType, data.payload || data);
347
+ this.emit("*", data);
348
+ }
349
+ /**
350
+ * Emits an event to all registered listeners
351
+ */
352
+ emit(event, data) {
353
+ const listeners = this.listeners.get(event);
354
+ if (listeners && listeners.size > 0) {
355
+ listeners.forEach((callback) => {
356
+ try {
357
+ callback(data);
358
+ } catch (error) {
359
+ console.error(`\u274C Error in WebSocket listener for "${event}":`, error);
360
+ }
361
+ });
362
+ }
363
+ }
364
+ /**
365
+ * Handles reconnection logic with exponential backoff
366
+ */
367
+ handleReconnect() {
368
+ const autoReconnect = this.config.websocket?.autoReconnect ?? true;
369
+ const maxAttempts = this.config.websocket?.maxReconnectAttempts ?? 5;
370
+ if (!autoReconnect) {
371
+ console.log("Auto-reconnect disabled");
372
+ return;
373
+ }
374
+ if (this.reconnectAttempts >= maxAttempts) {
375
+ console.log(`\u274C Max reconnection attempts (${maxAttempts}) reached. Giving up.`);
376
+ this.emit("error", new WebSocketError("Max reconnection attempts reached"));
377
+ return;
378
+ }
379
+ const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
380
+ console.log(
381
+ `\u{1F504} Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts + 1}/${maxAttempts})...`
382
+ );
383
+ this.isReconnecting = true;
384
+ setTimeout(() => {
385
+ this.reconnectAttempts++;
386
+ this.connect().catch((error) => {
387
+ console.error("\u274C Reconnection failed:", error);
388
+ });
389
+ }, delay);
390
+ }
391
+ /**
392
+ * Starts heartbeat to keep connection alive
393
+ */
394
+ startHeartbeat() {
395
+ const interval = this.config.websocket?.heartbeatInterval || 3e4;
396
+ this.heartbeatTimer = setInterval(() => {
397
+ if (this.isConnected) {
398
+ this.send({
399
+ type: "ping",
400
+ timestamp: Date.now()
401
+ });
402
+ }
403
+ }, interval);
404
+ }
405
+ /**
406
+ * Stops heartbeat timer
407
+ */
408
+ stopHeartbeat() {
409
+ if (this.heartbeatTimer) {
410
+ clearInterval(this.heartbeatTimer);
411
+ this.heartbeatTimer = void 0;
412
+ }
413
+ }
414
+ };
415
+
416
+ // src/api/http-client.ts
417
+ var HttpClient = class {
418
+ constructor(baseUrl) {
419
+ this.baseUrl = baseUrl;
420
+ }
421
+ /**
422
+ * Makes an HTTP request
423
+ */
424
+ async request(endpoint, options = {}) {
425
+ const url = `${this.baseUrl}${endpoint}`;
426
+ try {
427
+ const response = await fetch(url, {
428
+ ...options,
429
+ headers: {
430
+ "Content-Type": "application/json",
431
+ ...options.headers
432
+ }
433
+ });
434
+ if (!response.ok) {
435
+ const errorText = await response.text();
436
+ throw new PipsendError(
437
+ `HTTP ${response.status}: ${errorText || response.statusText}`,
438
+ "HTTP_ERROR",
439
+ response.status
440
+ );
441
+ }
442
+ const text = await response.text();
443
+ if (!text) {
444
+ return {};
445
+ }
446
+ return JSON.parse(text);
447
+ } catch (error) {
448
+ if (error instanceof PipsendError) {
449
+ throw error;
450
+ }
451
+ throw new PipsendError(
452
+ `Request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
453
+ "NETWORK_ERROR"
454
+ );
455
+ }
456
+ }
457
+ /**
458
+ * GET request
459
+ */
460
+ async get(endpoint, params) {
461
+ const queryString = params ? this.buildQueryString(params) : "";
462
+ const url = queryString ? `${endpoint}?${queryString}` : endpoint;
463
+ return this.request(url, { method: "GET" });
464
+ }
465
+ /**
466
+ * POST request
467
+ */
468
+ async post(endpoint, body) {
469
+ return this.request(endpoint, {
470
+ method: "POST",
471
+ body: body ? JSON.stringify(body) : void 0
472
+ });
473
+ }
474
+ /**
475
+ * PUT request
476
+ */
477
+ async put(endpoint, body) {
478
+ return this.request(endpoint, {
479
+ method: "PUT",
480
+ body: body ? JSON.stringify(body) : void 0
481
+ });
482
+ }
483
+ /**
484
+ * DELETE request
485
+ */
486
+ async delete(endpoint) {
487
+ return this.request(endpoint, { method: "DELETE" });
488
+ }
489
+ /**
490
+ * Builds query string from params object
491
+ */
492
+ buildQueryString(params) {
493
+ const filtered = Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
494
+ return filtered.join("&");
495
+ }
496
+ };
497
+
498
+ // src/api/accounts.ts
499
+ var AccountsAPI = class {
500
+ constructor(http) {
501
+ this.http = http;
502
+ }
503
+ /**
504
+ * List all accounts with optional filters and pagination
505
+ */
506
+ async list(params) {
507
+ return this.http.get("/api/v1/accounts", params);
508
+ }
509
+ /**
510
+ * Get all account logins
511
+ */
512
+ async getLogins() {
513
+ return this.http.get("/api/v1/accounts/logins");
514
+ }
515
+ /**
516
+ * Get account statistics with optional filters
517
+ */
518
+ async getStatistics(params) {
519
+ return this.http.get("/api/v1/accounts/statistics", params);
520
+ }
521
+ /**
522
+ * Change master password for an account
523
+ */
524
+ async changeMasterPassword(login, request) {
525
+ return this.http.put(`/api/v1/accounts/${login}/password/master`, request);
526
+ }
527
+ /**
528
+ * Change investor password for an account
529
+ */
530
+ async changeInvestorPassword(login, request) {
531
+ return this.http.put(`/api/v1/accounts/${login}/password/investor`, request);
532
+ }
533
+ /**
534
+ * Archive an account
535
+ */
536
+ async archive(login) {
537
+ return this.http.put(`/api/v1/accounts/${login}/archive`);
538
+ }
539
+ /**
540
+ * Unarchive an account
541
+ */
542
+ async unarchive(login) {
543
+ return this.http.put(`/api/v1/accounts/${login}/unarchive`);
544
+ }
545
+ };
546
+
547
+ // src/api/trading-groups.ts
548
+ var TradingGroupsAPI = class {
549
+ constructor(http) {
550
+ this.http = http;
551
+ }
552
+ /**
553
+ * List all trading groups with optional filters
554
+ */
555
+ async list(params) {
556
+ return this.http.get("/api/v1/trading-groups", params);
557
+ }
558
+ /**
559
+ * Get trading group by name
560
+ */
561
+ async getByName(name) {
562
+ return this.http.get(`/api/v1/trading-groups/${encodeURIComponent(name)}`);
563
+ }
564
+ /**
565
+ * Get all trading group names
566
+ */
567
+ async getNames() {
568
+ return this.http.get("/api/v1/trading-groups/names");
569
+ }
570
+ /**
571
+ * Get trading group statistics
572
+ */
573
+ async getStatistics(type) {
574
+ return this.http.get("/api/v1/trading-groups/statistics", type ? { type } : void 0);
575
+ }
576
+ };
577
+
578
+ // src/api/market-data.ts
579
+ var MarketDataAPI = class {
580
+ constructor(http) {
581
+ this.http = http;
582
+ }
583
+ /**
584
+ * Get historical OHLCV candles
585
+ *
586
+ * @param params - Candles parameters (symbol and timeframe are required)
587
+ * @returns Paginated candles or array of candles
588
+ *
589
+ * @example
590
+ * ```ts
591
+ * // Get last 100 candles
592
+ * const candles = await client.marketData.getCandles({
593
+ * symbol: 'BTCUSD',
594
+ * timeframe: '1m',
595
+ * limit: 100,
596
+ * order: 'desc'
597
+ * });
598
+ *
599
+ * // Get candles with time range
600
+ * const candles = await client.marketData.getCandles({
601
+ * symbol: 'BTCUSD,ETHUSD',
602
+ * timeframe: '1h',
603
+ * from: '2025-11-01T00:00:00Z',
604
+ * to: '2025-11-02T00:00:00Z'
605
+ * });
606
+ * ```
607
+ */
608
+ async getCandles(params) {
609
+ return this.http.get("/api/v1/candles", params);
610
+ }
611
+ /**
612
+ * Get available symbols with metadata
613
+ *
614
+ * @param params - Optional filters for symbols
615
+ * @returns Paginated symbols or array of symbols
616
+ *
617
+ * @example
618
+ * ```ts
619
+ * // Get all symbols
620
+ * const symbols = await client.marketData.getSymbols();
621
+ *
622
+ * // Filter by group
623
+ * const cryptoSymbols = await client.marketData.getSymbols({
624
+ * group: 'CRYPTO_MAJOR',
625
+ * has_data: true
626
+ * });
627
+ * ```
628
+ */
629
+ async getSymbols(params) {
630
+ return this.http.get("/api/v1/symbols", params);
631
+ }
632
+ /**
633
+ * Get symbol groups hierarchy
634
+ *
635
+ * Note: This endpoint never uses pagination
636
+ *
637
+ * @param params - Optional filters for groups
638
+ * @returns Array of groups
639
+ *
640
+ * @example
641
+ * ```ts
642
+ * // Get all groups
643
+ * const groups = await client.marketData.getGroups();
644
+ *
645
+ * // Get only root groups
646
+ * const rootGroups = await client.marketData.getGroups({
647
+ * root_only: true
648
+ * });
649
+ * ```
650
+ */
651
+ async getGroups(params) {
652
+ return this.http.get("/api/v1/groups", params);
653
+ }
654
+ };
655
+
656
+ // src/api/positions.ts
657
+ var PositionsAPI = class {
658
+ constructor(http) {
659
+ this.http = http;
660
+ }
661
+ /**
662
+ * List positions with optional filters and pagination
663
+ */
664
+ async list(params) {
665
+ return this.http.get("/api/v1/positions", params);
666
+ }
667
+ /**
668
+ * Get position statistics with optional filters and grouping
669
+ */
670
+ async getStats(params) {
671
+ return this.http.get("/api/v1/positions/stats", params);
672
+ }
673
+ /**
674
+ * Get position totals with dynamic grouping
675
+ */
676
+ async getTotals(params) {
677
+ return this.http.get("/api/v1/positions/totals", params);
678
+ }
679
+ /**
680
+ * Update position (modify SL/TP)
681
+ */
682
+ async update(id, request) {
683
+ return this.http.put(`/api/v1/positions/${id}`, request);
684
+ }
685
+ /**
686
+ * Delete (close) position
687
+ */
688
+ async delete(id, params) {
689
+ return this.http.delete(`/api/v1/positions/${id}?login=${params.login}`);
690
+ }
691
+ /**
692
+ * Check and recalculate positions
693
+ */
694
+ async check(request) {
695
+ return this.http.post("/api/v1/positions/check", request);
696
+ }
697
+ };
698
+
699
+ // src/api/orders.ts
700
+ var OrdersAPI = class {
701
+ constructor(http) {
702
+ this.http = http;
703
+ }
704
+ /**
705
+ * List orders with optional filters and pagination
706
+ */
707
+ async list(params) {
708
+ return this.http.get("/api/v1/orders", params);
709
+ }
710
+ /**
711
+ * Get order statistics with optional filters and grouping
712
+ */
713
+ async getStats(params) {
714
+ return this.http.get("/api/v1/orders/stats", params);
715
+ }
716
+ /**
717
+ * Get order totals with dynamic grouping
718
+ */
719
+ async getTotals(params) {
720
+ return this.http.get("/api/v1/orders/totals", params);
721
+ }
722
+ /**
723
+ * Update order (modify SL/TP)
724
+ */
725
+ async update(id, request) {
726
+ return this.http.put(`/api/v1/orders/${id}`, request);
727
+ }
728
+ /**
729
+ * Delete (cancel) order
730
+ */
731
+ async delete(id, params) {
732
+ return this.http.delete(`/api/v1/orders/${id}?login=${params.login}`);
733
+ }
734
+ /**
735
+ * Check and validate pending orders
736
+ */
737
+ async check(request) {
738
+ return this.http.post("/api/v1/orders/check", request);
739
+ }
740
+ };
741
+
742
+ // src/api/trades.ts
743
+ var TradesAPI = class {
744
+ constructor(http) {
745
+ this.http = http;
746
+ }
747
+ /**
748
+ * Open a new trade (market or pending order)
749
+ */
750
+ async open(request) {
751
+ return this.http.post("/api/v1/trades/open", request);
752
+ }
753
+ /**
754
+ * Close a trade (full or partial)
755
+ */
756
+ async close(request) {
757
+ return this.http.post("/api/v1/trades/close", request);
758
+ }
759
+ /**
760
+ * Modify trade Stop Loss and/or Take Profit
761
+ */
762
+ async modify(request) {
763
+ return this.http.put("/api/v1/trades/modify", request);
764
+ }
765
+ /**
766
+ * Check if there's sufficient margin for a trade
767
+ */
768
+ async checkMargin(request) {
769
+ return this.http.post("/api/v1/trades/check-margin", request);
770
+ }
771
+ /**
772
+ * Calculate potential profit for a trade
773
+ */
774
+ async calculateProfit(request) {
775
+ return this.http.post("/api/v1/trades/calc-profit", request);
776
+ }
777
+ };
778
+
779
+ // src/core/client.ts
780
+ var PipsendClient = class {
781
+ constructor(config) {
782
+ /**
783
+ * Authentication API
784
+ * TODO: Implement when authentication is available
785
+ */
786
+ this.auth = {
787
+ /**
788
+ * Forces a token refresh
789
+ */
790
+ refresh: async () => {
791
+ await this.authManager.refreshToken();
792
+ },
793
+ /**
794
+ * Checks if the client is authenticated
795
+ */
796
+ isAuthenticated: () => {
797
+ return this.authManager.isAuthenticated();
798
+ },
799
+ /**
800
+ * Gets the current token (if it exists)
801
+ */
802
+ getToken: () => {
803
+ return this.authManager.getToken();
804
+ },
805
+ /**
806
+ * Logs out by clearing the token
807
+ */
808
+ logout: () => {
809
+ this.authManager.clearToken();
810
+ }
811
+ };
812
+ /**
813
+ * WebSocket streaming API
814
+ * Provides real-time updates for prices, orders, positions, etc.
815
+ */
816
+ this.stream = {
817
+ /**
818
+ * Connects to WebSocket server
819
+ */
820
+ connect: async () => {
821
+ if (!this.wsManager) {
822
+ throw new WebSocketError(
823
+ "WebSocket not enabled. Set websocket.enabled: true in config"
824
+ );
825
+ }
826
+ await this.wsManager.connect();
827
+ },
828
+ /**
829
+ * Disconnects from WebSocket server
830
+ */
831
+ disconnect: () => {
832
+ this.wsManager?.disconnect();
833
+ },
834
+ /**
835
+ * Checks if WebSocket is connected
836
+ */
837
+ isConnected: () => {
838
+ return this.wsManager?.isWebSocketConnected() ?? false;
839
+ },
840
+ /**
841
+ * Subscribes to symbols or channels
842
+ * @example
843
+ * client.stream.subscribe(['EURUSD', 'GBPUSD']);
844
+ */
845
+ subscribe: (channels) => {
846
+ if (!this.wsManager) {
847
+ throw new WebSocketError("WebSocket not enabled");
848
+ }
849
+ this.wsManager.subscribe(channels);
850
+ },
851
+ /**
852
+ * Unsubscribes from symbols or channels
853
+ */
854
+ unsubscribe: (channels) => {
855
+ if (!this.wsManager) {
856
+ throw new WebSocketError("WebSocket not enabled");
857
+ }
858
+ this.wsManager.unsubscribe(channels);
859
+ },
860
+ /**
861
+ * Gets list of subscribed channels
862
+ */
863
+ getSubscriptions: () => {
864
+ return this.wsManager?.getSubscribedChannels() ?? [];
865
+ },
866
+ /**
867
+ * Listens to real-time price updates
868
+ */
869
+ onPriceUpdate: (callback) => {
870
+ if (!this.wsManager) {
871
+ throw new WebSocketError("WebSocket not enabled");
872
+ }
873
+ this.wsManager.on("price", callback);
874
+ },
875
+ /**
876
+ * Listens to real-time order updates
877
+ */
878
+ onOrderUpdate: (callback) => {
879
+ if (!this.wsManager) {
880
+ throw new WebSocketError("WebSocket not enabled");
881
+ }
882
+ this.wsManager.on("order", callback);
883
+ },
884
+ /**
885
+ * Listens to real-time position updates
886
+ */
887
+ onPositionUpdate: (callback) => {
888
+ if (!this.wsManager) {
889
+ throw new WebSocketError("WebSocket not enabled");
890
+ }
891
+ this.wsManager.on("position", callback);
892
+ },
893
+ /**
894
+ * Listens to real-time balance updates
895
+ */
896
+ onBalanceUpdate: (callback) => {
897
+ if (!this.wsManager) {
898
+ throw new WebSocketError("WebSocket not enabled");
899
+ }
900
+ this.wsManager.on("balance", callback);
901
+ },
902
+ /**
903
+ * Listens to connection events
904
+ */
905
+ onConnected: (callback) => {
906
+ if (!this.wsManager) {
907
+ throw new WebSocketError("WebSocket not enabled");
908
+ }
909
+ this.wsManager.on("connected", callback);
910
+ },
911
+ /**
912
+ * Listens to disconnection events
913
+ */
914
+ onDisconnected: (callback) => {
915
+ if (!this.wsManager) {
916
+ throw new WebSocketError("WebSocket not enabled");
917
+ }
918
+ this.wsManager.on("disconnected", callback);
919
+ },
920
+ /**
921
+ * Listens to error events
922
+ */
923
+ onError: (callback) => {
924
+ if (!this.wsManager) {
925
+ throw new WebSocketError("WebSocket not enabled");
926
+ }
927
+ this.wsManager.on("error", callback);
928
+ },
929
+ /**
930
+ * Removes event listener
931
+ */
932
+ off: (event, callback) => {
933
+ this.wsManager?.off(event, callback);
934
+ }
935
+ };
936
+ this.validateConfig(config);
937
+ this.config = {
938
+ ...config,
939
+ autoRefresh: config.autoRefresh ?? true,
940
+ refreshMarginMinutes: config.refreshMarginMinutes ?? 5,
941
+ timezone: config.timezone ?? "UTC"
942
+ };
943
+ this.authManager = new AuthManager(this.config);
944
+ this.http = new HttpClient(this.config.server);
945
+ this.accounts = new AccountsAPI(this.http);
946
+ this.tradingGroups = new TradingGroupsAPI(this.http);
947
+ this.marketData = new MarketDataAPI(this.http);
948
+ this.positions = new PositionsAPI(this.http);
949
+ this.orders = new OrdersAPI(this.http);
950
+ this.trades = new TradesAPI(this.http);
951
+ if (config.websocket?.enabled) {
952
+ this.wsManager = new WebSocketManager(this.config, this.authManager);
953
+ if (config.websocket.autoConnect) {
954
+ this.wsManager.connect().catch((error) => {
955
+ console.error("Failed to auto-connect WebSocket:", error);
956
+ });
957
+ }
958
+ }
959
+ }
960
+ /**
961
+ * Validates the initial configuration
962
+ */
963
+ validateConfig(config) {
964
+ if (!config.server) {
965
+ throw new ConfigurationError('The "server" parameter is required');
966
+ }
967
+ try {
968
+ new URL(config.server);
969
+ } catch {
970
+ throw new ConfigurationError('The "server" parameter must be a valid URL');
971
+ }
972
+ }
973
+ /**
974
+ * Health check
975
+ */
976
+ async ping() {
977
+ return this.http.get("/ping");
978
+ }
979
+ /**
980
+ * Client information
981
+ */
982
+ getConfig() {
983
+ return { ...this.config };
984
+ }
985
+ };
986
+
987
+ // src/index.ts
988
+ function createClient(config) {
989
+ return new PipsendClient(config);
990
+ }
991
+ var sdk = {
992
+ createClient,
993
+ PipsendClient
994
+ };
995
+ var index_default = sdk;
996
+ export {
997
+ AuthenticationError,
998
+ ConfigurationError,
999
+ PipsendClient,
1000
+ PipsendError,
1001
+ WebSocketError,
1002
+ createClient,
1003
+ index_default as default
1004
+ };
1005
+ //# sourceMappingURL=index.mjs.map