@finatic/client 0.0.139 → 0.0.141
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/README.md +335 -446
- package/dist/index.d.ts +272 -515
- package/dist/index.js +531 -449
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +532 -449
- package/dist/index.mjs.map +1 -1
- package/dist/types/core/client/ApiClient.d.ts +81 -27
- package/dist/types/core/client/FinaticConnect.d.ts +53 -103
- package/dist/types/index.d.ts +1 -2
- package/dist/types/mocks/MockApiClient.d.ts +2 -4
- package/dist/types/mocks/utils.d.ts +0 -5
- package/dist/types/types/api/auth.d.ts +12 -30
- package/dist/types/types/api/broker.d.ts +117 -1
- package/package.json +7 -3
- package/src/core/client/ApiClient.ts +1978 -0
- package/src/core/client/FinaticConnect.ts +1557 -0
- package/src/core/portal/PortalUI.ts +300 -0
- package/src/index.d.ts +23 -0
- package/src/index.ts +99 -0
- package/src/mocks/MockApiClient.ts +1032 -0
- package/src/mocks/MockDataProvider.ts +986 -0
- package/src/mocks/MockFactory.ts +97 -0
- package/src/mocks/utils.ts +133 -0
- package/src/themes/portalPresets.ts +1307 -0
- package/src/types/api/auth.ts +112 -0
- package/src/types/api/broker.ts +461 -0
- package/src/types/api/core.ts +53 -0
- package/src/types/api/errors.ts +35 -0
- package/src/types/api/orders.ts +45 -0
- package/src/types/api/portfolio.ts +59 -0
- package/src/types/common/pagination.ts +138 -0
- package/src/types/connect.ts +56 -0
- package/src/types/index.ts +25 -0
- package/src/types/portal.ts +214 -0
- package/src/types/ui/theme.ts +105 -0
- package/src/utils/brokerUtils.ts +85 -0
- package/src/utils/errors.ts +104 -0
- package/src/utils/events.ts +54 -0
- package/src/utils/themeUtils.ts +146 -0
package/dist/index.mjs
CHANGED
|
@@ -203,15 +203,13 @@ class TradingNotEnabledError extends ApiError {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
// Supabase import removed - SDK no longer depends on Supabase
|
|
206
207
|
class ApiClient {
|
|
207
208
|
constructor(baseUrl, deviceInfo) {
|
|
208
209
|
this.currentSessionState = null;
|
|
209
210
|
this.currentSessionId = null;
|
|
210
211
|
this.tradingContext = {};
|
|
211
|
-
//
|
|
212
|
-
this.tokenInfo = null;
|
|
213
|
-
this.refreshPromise = null;
|
|
214
|
-
this.REFRESH_BUFFER_MINUTES = 5; // Refresh token 5 minutes before expiry
|
|
212
|
+
// Session management (no Supabase needed)
|
|
215
213
|
// Session and company context
|
|
216
214
|
this.companyId = null;
|
|
217
215
|
this.csrfToken = null;
|
|
@@ -223,7 +221,9 @@ class ApiClient {
|
|
|
223
221
|
if (!this.baseUrl.includes('/api/v1')) {
|
|
224
222
|
this.baseUrl = `${this.baseUrl}/api/v1`;
|
|
225
223
|
}
|
|
224
|
+
// No Supabase initialization needed - SDK is clean
|
|
226
225
|
}
|
|
226
|
+
// Supabase initialization removed - SDK no longer depends on Supabase
|
|
227
227
|
/**
|
|
228
228
|
* Set session context (session ID, company ID, CSRF token)
|
|
229
229
|
*/
|
|
@@ -251,105 +251,30 @@ class ApiClient {
|
|
|
251
251
|
return this.csrfToken;
|
|
252
252
|
}
|
|
253
253
|
/**
|
|
254
|
-
*
|
|
255
|
-
*/
|
|
256
|
-
setTokens(accessToken, refreshToken, expiresAt, userId) {
|
|
257
|
-
this.tokenInfo = {
|
|
258
|
-
accessToken,
|
|
259
|
-
refreshToken,
|
|
260
|
-
expiresAt,
|
|
261
|
-
userId,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Get the current access token, refreshing if necessary
|
|
254
|
+
* Get a valid access token (session-based auth - no tokens needed)
|
|
266
255
|
*/
|
|
267
256
|
async getValidAccessToken() {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
// Check if token is expired or about to expire
|
|
272
|
-
if (this.isTokenExpired()) {
|
|
273
|
-
await this.refreshTokens();
|
|
274
|
-
}
|
|
275
|
-
return this.tokenInfo.accessToken;
|
|
257
|
+
// Session-based auth - return empty token as we use session headers
|
|
258
|
+
return '';
|
|
276
259
|
}
|
|
260
|
+
// Token expiration check removed - session-based auth doesn't use expiring tokens
|
|
261
|
+
// Supabase refresh method removed - SDK no longer uses Supabase tokens
|
|
277
262
|
/**
|
|
278
|
-
*
|
|
263
|
+
* Perform the actual Supabase session refresh
|
|
279
264
|
*/
|
|
280
|
-
|
|
281
|
-
if (!this.tokenInfo)
|
|
282
|
-
return true;
|
|
283
|
-
const expiryTime = new Date(this.tokenInfo.expiresAt).getTime();
|
|
284
|
-
const currentTime = Date.now();
|
|
285
|
-
const bufferTime = this.REFRESH_BUFFER_MINUTES * 60 * 1000; // 5 minutes in milliseconds
|
|
286
|
-
return currentTime >= expiryTime - bufferTime;
|
|
287
|
-
}
|
|
265
|
+
// Supabase refresh method removed - SDK no longer uses Supabase tokens
|
|
288
266
|
/**
|
|
289
|
-
*
|
|
290
|
-
*/
|
|
291
|
-
async refreshTokens() {
|
|
292
|
-
if (!this.tokenInfo) {
|
|
293
|
-
throw new AuthenticationError('No refresh token available.');
|
|
294
|
-
}
|
|
295
|
-
// If a refresh is already in progress, wait for it
|
|
296
|
-
if (this.refreshPromise) {
|
|
297
|
-
await this.refreshPromise;
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
// Start a new refresh
|
|
301
|
-
this.refreshPromise = this.performTokenRefresh();
|
|
302
|
-
try {
|
|
303
|
-
await this.refreshPromise;
|
|
304
|
-
}
|
|
305
|
-
finally {
|
|
306
|
-
this.refreshPromise = null;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Perform the actual token refresh request
|
|
311
|
-
*/
|
|
312
|
-
async performTokenRefresh() {
|
|
313
|
-
if (!this.tokenInfo) {
|
|
314
|
-
throw new AuthenticationError('No refresh token available.');
|
|
315
|
-
}
|
|
316
|
-
try {
|
|
317
|
-
const response = await this.request('/company/auth/refresh', {
|
|
318
|
-
method: 'POST',
|
|
319
|
-
headers: {
|
|
320
|
-
'Content-Type': 'application/json',
|
|
321
|
-
},
|
|
322
|
-
body: {
|
|
323
|
-
refresh_token: this.tokenInfo.refreshToken,
|
|
324
|
-
},
|
|
325
|
-
});
|
|
326
|
-
// Update stored tokens
|
|
327
|
-
this.tokenInfo = {
|
|
328
|
-
accessToken: response.response_data.access_token,
|
|
329
|
-
refreshToken: response.response_data.refresh_token,
|
|
330
|
-
expiresAt: response.response_data.expires_at,
|
|
331
|
-
userId: this.tokenInfo.userId,
|
|
332
|
-
};
|
|
333
|
-
return this.tokenInfo;
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
// Clear tokens on refresh failure
|
|
337
|
-
this.tokenInfo = null;
|
|
338
|
-
throw new AuthenticationError('Token refresh failed. Please re-authenticate.', error);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Clear stored tokens (useful for logout)
|
|
267
|
+
* Clear session tokens (useful for logout)
|
|
343
268
|
*/
|
|
344
269
|
clearTokens() {
|
|
345
|
-
|
|
346
|
-
this.refreshPromise = null;
|
|
270
|
+
// Session-based auth - no tokens to clear
|
|
347
271
|
}
|
|
348
272
|
/**
|
|
349
|
-
* Get current
|
|
273
|
+
* Get current session info (for debugging/testing) - session-based auth
|
|
350
274
|
*/
|
|
351
275
|
getTokenInfo() {
|
|
352
|
-
|
|
276
|
+
// Session-based auth - no tokens to return
|
|
277
|
+
return null;
|
|
353
278
|
}
|
|
354
279
|
/**
|
|
355
280
|
* Make a request to the API.
|
|
@@ -363,9 +288,12 @@ class ApiClient {
|
|
|
363
288
|
url.searchParams.append(key, value);
|
|
364
289
|
});
|
|
365
290
|
}
|
|
291
|
+
// Get Supabase JWT token
|
|
292
|
+
const accessToken = await this.getValidAccessToken();
|
|
366
293
|
// Build comprehensive headers object with all available session data
|
|
367
294
|
const comprehensiveHeaders = {
|
|
368
295
|
'Content-Type': 'application/json',
|
|
296
|
+
Authorization: `Bearer ${accessToken}`,
|
|
369
297
|
};
|
|
370
298
|
// Add device info if available
|
|
371
299
|
if (this.deviceInfo) {
|
|
@@ -533,7 +461,7 @@ class ApiClient {
|
|
|
533
461
|
}
|
|
534
462
|
// Session Management
|
|
535
463
|
async startSession(token, userId) {
|
|
536
|
-
const response = await this.request('/
|
|
464
|
+
const response = await this.request('/session/start', {
|
|
537
465
|
method: 'POST',
|
|
538
466
|
headers: {
|
|
539
467
|
'Content-Type': 'application/json',
|
|
@@ -587,11 +515,8 @@ class ApiClient {
|
|
|
587
515
|
otp,
|
|
588
516
|
},
|
|
589
517
|
});
|
|
590
|
-
//
|
|
591
|
-
if (response.success && response.data)
|
|
592
|
-
const expiresAt = new Date(Date.now() + response.data.expires_in * 1000).toISOString();
|
|
593
|
-
this.setTokens(response.data.access_token, response.data.refresh_token, expiresAt, response.data.user_id);
|
|
594
|
-
}
|
|
518
|
+
// OTP verification successful - tokens are handled by Supabase client
|
|
519
|
+
if (response.success && response.data) ;
|
|
595
520
|
return response;
|
|
596
521
|
}
|
|
597
522
|
// Direct Authentication
|
|
@@ -600,7 +525,7 @@ class ApiClient {
|
|
|
600
525
|
if (this.currentSessionState !== SessionState.ACTIVE) {
|
|
601
526
|
throw new SessionError('Session must be in ACTIVE state to authenticate');
|
|
602
527
|
}
|
|
603
|
-
const response = await this.request('/
|
|
528
|
+
const response = await this.request('/session/authenticate', {
|
|
604
529
|
method: 'POST',
|
|
605
530
|
headers: {
|
|
606
531
|
'Content-Type': 'application/json',
|
|
@@ -618,11 +543,7 @@ class ApiClient {
|
|
|
618
543
|
},
|
|
619
544
|
});
|
|
620
545
|
// Store tokens after successful direct authentication
|
|
621
|
-
if (response.success && response.data)
|
|
622
|
-
// For direct auth, we don't get expires_in, so we'll set a default 1-hour expiry
|
|
623
|
-
const expiresAt = new Date(Date.now() + 60 * 60 * 1000).toISOString(); // 1 hour
|
|
624
|
-
this.setTokens(response.data.access_token, response.data.refresh_token, expiresAt, userId);
|
|
625
|
-
}
|
|
546
|
+
if (response.success && response.data) ;
|
|
626
547
|
return response;
|
|
627
548
|
}
|
|
628
549
|
// Portal Management
|
|
@@ -636,7 +557,7 @@ class ApiClient {
|
|
|
636
557
|
if (this.currentSessionState !== SessionState.ACTIVE) {
|
|
637
558
|
throw new SessionError('Session must be in ACTIVE state to get portal URL');
|
|
638
559
|
}
|
|
639
|
-
return this.request('/
|
|
560
|
+
return this.request('/session/portal', {
|
|
640
561
|
method: 'GET',
|
|
641
562
|
headers: {
|
|
642
563
|
'Content-Type': 'application/json',
|
|
@@ -797,12 +718,6 @@ class ApiClient {
|
|
|
797
718
|
this.tradingContext.accountNumber = accountNumber;
|
|
798
719
|
this.tradingContext.accountId = accountId;
|
|
799
720
|
}
|
|
800
|
-
getTradingContext() {
|
|
801
|
-
return { ...this.tradingContext };
|
|
802
|
-
}
|
|
803
|
-
clearTradingContext() {
|
|
804
|
-
this.tradingContext = {};
|
|
805
|
-
}
|
|
806
721
|
// Stock convenience methods
|
|
807
722
|
async placeStockMarketOrder(symbol, orderQty, action, broker, accountNumber, extras = {}, connection_id) {
|
|
808
723
|
return this.placeBrokerOrder({
|
|
@@ -810,7 +725,7 @@ class ApiClient {
|
|
|
810
725
|
orderQty,
|
|
811
726
|
action,
|
|
812
727
|
orderType: 'Market',
|
|
813
|
-
assetType: '
|
|
728
|
+
assetType: 'equity',
|
|
814
729
|
timeInForce: 'day',
|
|
815
730
|
broker,
|
|
816
731
|
accountNumber,
|
|
@@ -822,7 +737,7 @@ class ApiClient {
|
|
|
822
737
|
orderQty,
|
|
823
738
|
action,
|
|
824
739
|
orderType: 'Limit',
|
|
825
|
-
assetType: '
|
|
740
|
+
assetType: 'equity',
|
|
826
741
|
price,
|
|
827
742
|
timeInForce,
|
|
828
743
|
broker,
|
|
@@ -835,7 +750,7 @@ class ApiClient {
|
|
|
835
750
|
orderQty,
|
|
836
751
|
action,
|
|
837
752
|
orderType: 'Stop',
|
|
838
|
-
assetType: '
|
|
753
|
+
assetType: 'equity',
|
|
839
754
|
stopPrice,
|
|
840
755
|
timeInForce,
|
|
841
756
|
broker,
|
|
@@ -849,7 +764,7 @@ class ApiClient {
|
|
|
849
764
|
orderQty,
|
|
850
765
|
action,
|
|
851
766
|
orderType: 'Market',
|
|
852
|
-
assetType: '
|
|
767
|
+
assetType: 'crypto',
|
|
853
768
|
timeInForce: 'day',
|
|
854
769
|
broker,
|
|
855
770
|
accountNumber,
|
|
@@ -862,7 +777,7 @@ class ApiClient {
|
|
|
862
777
|
orderQty,
|
|
863
778
|
action,
|
|
864
779
|
orderType: 'Limit',
|
|
865
|
-
assetType: '
|
|
780
|
+
assetType: 'crypto',
|
|
866
781
|
price,
|
|
867
782
|
timeInForce,
|
|
868
783
|
broker,
|
|
@@ -877,7 +792,7 @@ class ApiClient {
|
|
|
877
792
|
orderQty,
|
|
878
793
|
action,
|
|
879
794
|
orderType: 'Market',
|
|
880
|
-
assetType: '
|
|
795
|
+
assetType: 'equity_option',
|
|
881
796
|
timeInForce: 'day',
|
|
882
797
|
broker,
|
|
883
798
|
accountNumber,
|
|
@@ -890,7 +805,7 @@ class ApiClient {
|
|
|
890
805
|
orderQty,
|
|
891
806
|
action,
|
|
892
807
|
orderType: 'Limit',
|
|
893
|
-
assetType: '
|
|
808
|
+
assetType: 'equity_option',
|
|
894
809
|
price,
|
|
895
810
|
timeInForce,
|
|
896
811
|
broker,
|
|
@@ -905,7 +820,7 @@ class ApiClient {
|
|
|
905
820
|
orderQty,
|
|
906
821
|
action,
|
|
907
822
|
orderType: 'Market',
|
|
908
|
-
assetType: '
|
|
823
|
+
assetType: 'future',
|
|
909
824
|
timeInForce: 'day',
|
|
910
825
|
broker,
|
|
911
826
|
accountNumber,
|
|
@@ -917,7 +832,7 @@ class ApiClient {
|
|
|
917
832
|
orderQty,
|
|
918
833
|
action,
|
|
919
834
|
orderType: 'Limit',
|
|
920
|
-
assetType: '
|
|
835
|
+
assetType: 'future',
|
|
921
836
|
price,
|
|
922
837
|
timeInForce,
|
|
923
838
|
broker,
|
|
@@ -1021,7 +936,7 @@ class ApiClient {
|
|
|
1021
936
|
}
|
|
1022
937
|
async getUserToken(sessionId) {
|
|
1023
938
|
const accessToken = await this.getValidAccessToken();
|
|
1024
|
-
return this.request(`/
|
|
939
|
+
return this.request(`/session/${sessionId}/user`, {
|
|
1025
940
|
method: 'GET',
|
|
1026
941
|
headers: {
|
|
1027
942
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -1036,18 +951,27 @@ class ApiClient {
|
|
|
1036
951
|
}
|
|
1037
952
|
/**
|
|
1038
953
|
* Refresh the current session to extend its lifetime
|
|
954
|
+
* Note: This now uses Supabase session refresh instead of custom endpoint
|
|
1039
955
|
*/
|
|
1040
956
|
async refreshSession() {
|
|
1041
957
|
if (!this.currentSessionId || !this.companyId) {
|
|
1042
958
|
throw new SessionError('No active session to refresh');
|
|
1043
959
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
960
|
+
// Session-based auth - no token refresh needed
|
|
961
|
+
// Return session info in expected format
|
|
962
|
+
return {
|
|
963
|
+
success: true,
|
|
964
|
+
response_data: {
|
|
965
|
+
session_id: this.currentSessionId,
|
|
966
|
+
company_id: this.companyId,
|
|
967
|
+
status: 'active',
|
|
968
|
+
expires_at: new Date(Date.now() + 60 * 60 * 1000).toISOString(), // 1 hour from now
|
|
969
|
+
user_id: '', // Session-based auth - user_id comes from session
|
|
970
|
+
auto_login: false,
|
|
1049
971
|
},
|
|
1050
|
-
|
|
972
|
+
message: 'Session refreshed successfully',
|
|
973
|
+
status_code: 200,
|
|
974
|
+
};
|
|
1051
975
|
}
|
|
1052
976
|
// Broker Data Management
|
|
1053
977
|
async getBrokerList() {
|
|
@@ -1485,6 +1409,155 @@ class ApiClient {
|
|
|
1485
1409
|
},
|
|
1486
1410
|
});
|
|
1487
1411
|
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Get order fills for a specific order
|
|
1414
|
+
* @param orderId - The order ID
|
|
1415
|
+
* @param filter - Optional filter parameters
|
|
1416
|
+
* @returns Promise with order fills response
|
|
1417
|
+
*/
|
|
1418
|
+
async getOrderFills(orderId, filter) {
|
|
1419
|
+
const accessToken = await this.getValidAccessToken();
|
|
1420
|
+
const params = {};
|
|
1421
|
+
if (filter?.connection_id) {
|
|
1422
|
+
params.connection_id = filter.connection_id;
|
|
1423
|
+
}
|
|
1424
|
+
if (filter?.limit) {
|
|
1425
|
+
params.limit = filter.limit.toString();
|
|
1426
|
+
}
|
|
1427
|
+
if (filter?.offset) {
|
|
1428
|
+
params.offset = filter.offset.toString();
|
|
1429
|
+
}
|
|
1430
|
+
return this.request(`/brokers/data/orders/${orderId}/fills`, {
|
|
1431
|
+
method: 'GET',
|
|
1432
|
+
headers: {
|
|
1433
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1434
|
+
},
|
|
1435
|
+
params,
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Get order events for a specific order
|
|
1440
|
+
* @param orderId - The order ID
|
|
1441
|
+
* @param filter - Optional filter parameters
|
|
1442
|
+
* @returns Promise with order events response
|
|
1443
|
+
*/
|
|
1444
|
+
async getOrderEvents(orderId, filter) {
|
|
1445
|
+
const accessToken = await this.getValidAccessToken();
|
|
1446
|
+
const params = {};
|
|
1447
|
+
if (filter?.connection_id) {
|
|
1448
|
+
params.connection_id = filter.connection_id;
|
|
1449
|
+
}
|
|
1450
|
+
if (filter?.limit) {
|
|
1451
|
+
params.limit = filter.limit.toString();
|
|
1452
|
+
}
|
|
1453
|
+
if (filter?.offset) {
|
|
1454
|
+
params.offset = filter.offset.toString();
|
|
1455
|
+
}
|
|
1456
|
+
return this.request(`/brokers/data/orders/${orderId}/events`, {
|
|
1457
|
+
method: 'GET',
|
|
1458
|
+
headers: {
|
|
1459
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1460
|
+
},
|
|
1461
|
+
params,
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Get order groups
|
|
1466
|
+
* @param filter - Optional filter parameters
|
|
1467
|
+
* @returns Promise with order groups response
|
|
1468
|
+
*/
|
|
1469
|
+
async getOrderGroups(filter) {
|
|
1470
|
+
const accessToken = await this.getValidAccessToken();
|
|
1471
|
+
const params = {};
|
|
1472
|
+
if (filter?.broker_id) {
|
|
1473
|
+
params.broker_id = filter.broker_id;
|
|
1474
|
+
}
|
|
1475
|
+
if (filter?.connection_id) {
|
|
1476
|
+
params.connection_id = filter.connection_id;
|
|
1477
|
+
}
|
|
1478
|
+
if (filter?.limit) {
|
|
1479
|
+
params.limit = filter.limit.toString();
|
|
1480
|
+
}
|
|
1481
|
+
if (filter?.offset) {
|
|
1482
|
+
params.offset = filter.offset.toString();
|
|
1483
|
+
}
|
|
1484
|
+
if (filter?.created_after) {
|
|
1485
|
+
params.created_after = filter.created_after;
|
|
1486
|
+
}
|
|
1487
|
+
if (filter?.created_before) {
|
|
1488
|
+
params.created_before = filter.created_before;
|
|
1489
|
+
}
|
|
1490
|
+
return this.request('/brokers/data/orders/groups', {
|
|
1491
|
+
method: 'GET',
|
|
1492
|
+
headers: {
|
|
1493
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1494
|
+
},
|
|
1495
|
+
params,
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Get position lots (tax lots for positions)
|
|
1500
|
+
* @param filter - Optional filter parameters
|
|
1501
|
+
* @returns Promise with position lots response
|
|
1502
|
+
*/
|
|
1503
|
+
async getPositionLots(filter) {
|
|
1504
|
+
const accessToken = await this.getValidAccessToken();
|
|
1505
|
+
const params = {};
|
|
1506
|
+
if (filter?.broker_id) {
|
|
1507
|
+
params.broker_id = filter.broker_id;
|
|
1508
|
+
}
|
|
1509
|
+
if (filter?.connection_id) {
|
|
1510
|
+
params.connection_id = filter.connection_id;
|
|
1511
|
+
}
|
|
1512
|
+
if (filter?.account_id) {
|
|
1513
|
+
params.account_id = filter.account_id;
|
|
1514
|
+
}
|
|
1515
|
+
if (filter?.symbol) {
|
|
1516
|
+
params.symbol = filter.symbol;
|
|
1517
|
+
}
|
|
1518
|
+
if (filter?.position_id) {
|
|
1519
|
+
params.position_id = filter.position_id;
|
|
1520
|
+
}
|
|
1521
|
+
if (filter?.limit) {
|
|
1522
|
+
params.limit = filter.limit.toString();
|
|
1523
|
+
}
|
|
1524
|
+
if (filter?.offset) {
|
|
1525
|
+
params.offset = filter.offset.toString();
|
|
1526
|
+
}
|
|
1527
|
+
return this.request('/brokers/data/positions/lots', {
|
|
1528
|
+
method: 'GET',
|
|
1529
|
+
headers: {
|
|
1530
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1531
|
+
},
|
|
1532
|
+
params,
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Get position lot fills for a specific lot
|
|
1537
|
+
* @param lotId - The position lot ID
|
|
1538
|
+
* @param filter - Optional filter parameters
|
|
1539
|
+
* @returns Promise with position lot fills response
|
|
1540
|
+
*/
|
|
1541
|
+
async getPositionLotFills(lotId, filter) {
|
|
1542
|
+
const accessToken = await this.getValidAccessToken();
|
|
1543
|
+
const params = {};
|
|
1544
|
+
if (filter?.connection_id) {
|
|
1545
|
+
params.connection_id = filter.connection_id;
|
|
1546
|
+
}
|
|
1547
|
+
if (filter?.limit) {
|
|
1548
|
+
params.limit = filter.limit.toString();
|
|
1549
|
+
}
|
|
1550
|
+
if (filter?.offset) {
|
|
1551
|
+
params.offset = filter.offset.toString();
|
|
1552
|
+
}
|
|
1553
|
+
return this.request(`/brokers/data/positions/lots/${lotId}/fills`, {
|
|
1554
|
+
method: 'GET',
|
|
1555
|
+
headers: {
|
|
1556
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1557
|
+
},
|
|
1558
|
+
params,
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1488
1561
|
}
|
|
1489
1562
|
|
|
1490
1563
|
class EventEmitter {
|
|
@@ -1818,15 +1891,11 @@ class MockDataProvider {
|
|
|
1818
1891
|
* Generate mock tokens
|
|
1819
1892
|
*/
|
|
1820
1893
|
generateTokens(userId) {
|
|
1821
|
-
|
|
1822
|
-
|
|
1894
|
+
`mock_access_${v4().replace(/-/g, '')}`;
|
|
1895
|
+
`mock_refresh_${v4().replace(/-/g, '')}`;
|
|
1823
1896
|
return {
|
|
1824
|
-
accessToken,
|
|
1825
|
-
refreshToken,
|
|
1826
|
-
expiresIn: 3600, // 1 hour
|
|
1827
1897
|
user_id: userId,
|
|
1828
|
-
|
|
1829
|
-
scope: 'read write',
|
|
1898
|
+
// Removed token fields - we no longer use Supabase tokens in the SDK
|
|
1830
1899
|
};
|
|
1831
1900
|
}
|
|
1832
1901
|
// Authentication & Session Management Mocks
|
|
@@ -1846,6 +1915,7 @@ class MockDataProvider {
|
|
|
1846
1915
|
};
|
|
1847
1916
|
this.sessionData.set(sessionId, sessionData);
|
|
1848
1917
|
return {
|
|
1918
|
+
success: true,
|
|
1849
1919
|
data: sessionData,
|
|
1850
1920
|
message: 'Session started successfully',
|
|
1851
1921
|
};
|
|
@@ -1867,12 +1937,12 @@ class MockDataProvider {
|
|
|
1867
1937
|
success: true,
|
|
1868
1938
|
message: 'OTP verified successfully',
|
|
1869
1939
|
data: {
|
|
1870
|
-
access_token: tokens
|
|
1871
|
-
refresh_token: tokens
|
|
1940
|
+
access_token: '', // No longer using Supabase tokens
|
|
1941
|
+
refresh_token: '', // No longer using Supabase tokens
|
|
1872
1942
|
user_id: userId,
|
|
1873
|
-
expires_in:
|
|
1874
|
-
scope:
|
|
1875
|
-
token_type:
|
|
1943
|
+
expires_in: 0, // No token expiration for session-based auth
|
|
1944
|
+
scope: 'api:access',
|
|
1945
|
+
token_type: 'Bearer',
|
|
1876
1946
|
},
|
|
1877
1947
|
};
|
|
1878
1948
|
}
|
|
@@ -1884,8 +1954,8 @@ class MockDataProvider {
|
|
|
1884
1954
|
success: true,
|
|
1885
1955
|
message: 'Authentication successful',
|
|
1886
1956
|
data: {
|
|
1887
|
-
access_token: tokens
|
|
1888
|
-
refresh_token: tokens
|
|
1957
|
+
access_token: '', // No longer using Supabase tokens
|
|
1958
|
+
refresh_token: '', // No longer using Supabase tokens
|
|
1889
1959
|
},
|
|
1890
1960
|
};
|
|
1891
1961
|
}
|
|
@@ -1931,6 +2001,8 @@ class MockDataProvider {
|
|
|
1931
2001
|
valid: true,
|
|
1932
2002
|
company_id: this.generateCompanyId(),
|
|
1933
2003
|
status: 'active',
|
|
2004
|
+
is_sandbox: false,
|
|
2005
|
+
environment: 'production',
|
|
1934
2006
|
};
|
|
1935
2007
|
}
|
|
1936
2008
|
async mockCompletePortalSession(sessionId) {
|
|
@@ -2034,6 +2106,12 @@ class MockDataProvider {
|
|
|
2034
2106
|
created_at: new Date().toISOString(),
|
|
2035
2107
|
updated_at: new Date().toISOString(),
|
|
2036
2108
|
last_synced_at: new Date().toISOString(),
|
|
2109
|
+
positions_synced_at: new Date().toISOString(),
|
|
2110
|
+
orders_synced_at: new Date().toISOString(),
|
|
2111
|
+
balances_synced_at: new Date().toISOString(),
|
|
2112
|
+
account_created_at: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString(), // 1 year ago
|
|
2113
|
+
account_updated_at: new Date().toISOString(),
|
|
2114
|
+
account_first_trade_at: new Date(Date.now() - 300 * 24 * 60 * 60 * 1000).toISOString(), // 300 days ago
|
|
2037
2115
|
},
|
|
2038
2116
|
{
|
|
2039
2117
|
id: v4(),
|
|
@@ -2048,6 +2126,12 @@ class MockDataProvider {
|
|
|
2048
2126
|
created_at: new Date().toISOString(),
|
|
2049
2127
|
updated_at: new Date().toISOString(),
|
|
2050
2128
|
last_synced_at: new Date().toISOString(),
|
|
2129
|
+
positions_synced_at: new Date().toISOString(),
|
|
2130
|
+
orders_synced_at: new Date().toISOString(),
|
|
2131
|
+
balances_synced_at: new Date().toISOString(),
|
|
2132
|
+
account_created_at: new Date(Date.now() - 730 * 24 * 60 * 60 * 1000).toISOString(), // 2 years ago
|
|
2133
|
+
account_updated_at: new Date().toISOString(),
|
|
2134
|
+
account_first_trade_at: new Date(Date.now() - 700 * 24 * 60 * 60 * 1000).toISOString(), // 700 days ago
|
|
2051
2135
|
},
|
|
2052
2136
|
];
|
|
2053
2137
|
return {
|
|
@@ -2533,6 +2617,12 @@ class MockDataProvider {
|
|
|
2533
2617
|
created_at: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last year
|
|
2534
2618
|
updated_at: new Date().toISOString(),
|
|
2535
2619
|
last_synced_at: new Date().toISOString(),
|
|
2620
|
+
positions_synced_at: new Date().toISOString(),
|
|
2621
|
+
orders_synced_at: new Date().toISOString(),
|
|
2622
|
+
balances_synced_at: new Date().toISOString(),
|
|
2623
|
+
account_created_at: new Date(Date.now() - Math.random() * 730 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last 2 years
|
|
2624
|
+
account_updated_at: new Date().toISOString(),
|
|
2625
|
+
account_first_trade_at: new Date(Date.now() - Math.random() * 500 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last 500 days
|
|
2536
2626
|
});
|
|
2537
2627
|
}
|
|
2538
2628
|
return accounts;
|
|
@@ -2869,12 +2959,6 @@ class MockApiClient {
|
|
|
2869
2959
|
this.tradingContext.accountId = accountId;
|
|
2870
2960
|
console.log('MockApiClient.setAccount Debug - Updated context:', this.tradingContext);
|
|
2871
2961
|
}
|
|
2872
|
-
getTradingContext() {
|
|
2873
|
-
return { ...this.tradingContext };
|
|
2874
|
-
}
|
|
2875
|
-
clearTradingContext() {
|
|
2876
|
-
this.tradingContext = {};
|
|
2877
|
-
}
|
|
2878
2962
|
// Stock convenience methods
|
|
2879
2963
|
async placeStockMarketOrder(symbol, orderQty, action, broker, accountNumber, extras = {}) {
|
|
2880
2964
|
return this.placeBrokerOrder({
|
|
@@ -2884,7 +2968,7 @@ class MockApiClient {
|
|
|
2884
2968
|
orderQty,
|
|
2885
2969
|
action,
|
|
2886
2970
|
orderType: 'Market',
|
|
2887
|
-
assetType: '
|
|
2971
|
+
assetType: 'equity',
|
|
2888
2972
|
timeInForce: 'day',
|
|
2889
2973
|
}, extras);
|
|
2890
2974
|
}
|
|
@@ -2896,7 +2980,7 @@ class MockApiClient {
|
|
|
2896
2980
|
orderQty,
|
|
2897
2981
|
action,
|
|
2898
2982
|
orderType: 'Limit',
|
|
2899
|
-
assetType: '
|
|
2983
|
+
assetType: 'equity',
|
|
2900
2984
|
timeInForce,
|
|
2901
2985
|
price,
|
|
2902
2986
|
}, extras);
|
|
@@ -2909,7 +2993,7 @@ class MockApiClient {
|
|
|
2909
2993
|
orderQty,
|
|
2910
2994
|
action,
|
|
2911
2995
|
orderType: 'Stop',
|
|
2912
|
-
assetType: '
|
|
2996
|
+
assetType: 'equity',
|
|
2913
2997
|
timeInForce,
|
|
2914
2998
|
stopPrice,
|
|
2915
2999
|
}, extras);
|
|
@@ -2923,7 +3007,7 @@ class MockApiClient {
|
|
|
2923
3007
|
orderQty: options.quantity || orderQty,
|
|
2924
3008
|
action,
|
|
2925
3009
|
orderType: 'Market',
|
|
2926
|
-
assetType: '
|
|
3010
|
+
assetType: 'crypto',
|
|
2927
3011
|
timeInForce: 'gtc', // Crypto typically uses GTC
|
|
2928
3012
|
};
|
|
2929
3013
|
return this.placeBrokerOrder(orderParams, extras);
|
|
@@ -2936,7 +3020,7 @@ class MockApiClient {
|
|
|
2936
3020
|
orderQty: options.quantity || orderQty,
|
|
2937
3021
|
action,
|
|
2938
3022
|
orderType: 'Limit',
|
|
2939
|
-
assetType: '
|
|
3023
|
+
assetType: 'crypto',
|
|
2940
3024
|
timeInForce,
|
|
2941
3025
|
price,
|
|
2942
3026
|
};
|
|
@@ -2951,7 +3035,7 @@ class MockApiClient {
|
|
|
2951
3035
|
orderQty,
|
|
2952
3036
|
action,
|
|
2953
3037
|
orderType: 'Market',
|
|
2954
|
-
assetType: '
|
|
3038
|
+
assetType: 'equity_option',
|
|
2955
3039
|
timeInForce: 'day',
|
|
2956
3040
|
};
|
|
2957
3041
|
return this.placeBrokerOrder(orderParams, extras);
|
|
@@ -2964,7 +3048,7 @@ class MockApiClient {
|
|
|
2964
3048
|
orderQty,
|
|
2965
3049
|
action,
|
|
2966
3050
|
orderType: 'Limit',
|
|
2967
|
-
assetType: '
|
|
3051
|
+
assetType: 'equity_option',
|
|
2968
3052
|
timeInForce,
|
|
2969
3053
|
price,
|
|
2970
3054
|
};
|
|
@@ -2979,7 +3063,7 @@ class MockApiClient {
|
|
|
2979
3063
|
orderQty,
|
|
2980
3064
|
action,
|
|
2981
3065
|
orderType: 'Market',
|
|
2982
|
-
assetType: '
|
|
3066
|
+
assetType: 'future',
|
|
2983
3067
|
timeInForce: 'day',
|
|
2984
3068
|
}, extras);
|
|
2985
3069
|
}
|
|
@@ -2991,7 +3075,7 @@ class MockApiClient {
|
|
|
2991
3075
|
orderQty,
|
|
2992
3076
|
action,
|
|
2993
3077
|
orderType: 'Limit',
|
|
2994
|
-
assetType: '
|
|
3078
|
+
assetType: 'future',
|
|
2995
3079
|
timeInForce,
|
|
2996
3080
|
price,
|
|
2997
3081
|
}, extras);
|
|
@@ -3205,6 +3289,8 @@ class MockApiClient {
|
|
|
3205
3289
|
/**
|
|
3206
3290
|
* Utility functions for mock system environment detection
|
|
3207
3291
|
*/
|
|
3292
|
+
// Type declarations for Node.js environment
|
|
3293
|
+
// Note: process is already declared globally in Node.js types
|
|
3208
3294
|
/**
|
|
3209
3295
|
* Check if mocks should be used based on environment variables
|
|
3210
3296
|
* Supports both browser and Node.js environments
|
|
@@ -4907,95 +4993,161 @@ class FinaticConnect extends EventEmitter {
|
|
|
4907
4993
|
// Register automatic session cleanup
|
|
4908
4994
|
this.registerSessionCleanup();
|
|
4909
4995
|
}
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4996
|
+
async linkUserToSession(userId) {
|
|
4997
|
+
try {
|
|
4998
|
+
if (!this.sessionId) {
|
|
4999
|
+
console.error('No session ID available for user linking');
|
|
5000
|
+
return false;
|
|
5001
|
+
}
|
|
5002
|
+
// Call API endpoint to authenticate user with session
|
|
5003
|
+
const response = await this.apiClient.request('/session/authenticate', {
|
|
5004
|
+
method: 'POST',
|
|
5005
|
+
body: {
|
|
5006
|
+
session_id: this.sessionId,
|
|
5007
|
+
user_id: userId,
|
|
5008
|
+
},
|
|
5009
|
+
});
|
|
5010
|
+
if (response.error) {
|
|
5011
|
+
console.error('Failed to link user to session:', response.error);
|
|
5012
|
+
return false;
|
|
5013
|
+
}
|
|
5014
|
+
console.log('User linked to session successfully');
|
|
5015
|
+
return true;
|
|
5016
|
+
}
|
|
5017
|
+
catch (error) {
|
|
5018
|
+
console.error('Error linking user to session:', error);
|
|
5019
|
+
return false;
|
|
4913
5020
|
}
|
|
4914
|
-
// Keep existing user_id or use empty string as fallback
|
|
4915
|
-
const userId = this.userToken?.user_id || '';
|
|
4916
|
-
this.userToken = {
|
|
4917
|
-
accessToken: tokens.access_token,
|
|
4918
|
-
refreshToken: tokens.refresh_token,
|
|
4919
|
-
expiresIn: 3600, // Default to 1 hour if not provided
|
|
4920
|
-
user_id: userId,
|
|
4921
|
-
tokenType: 'Bearer',
|
|
4922
|
-
scope: 'api:access',
|
|
4923
|
-
};
|
|
4924
|
-
// Store tokens in ApiClient for automatic refresh
|
|
4925
|
-
const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
|
|
4926
|
-
this.apiClient.setTokens(tokens.access_token, tokens.refresh_token, expiresAt, userId);
|
|
4927
5021
|
}
|
|
4928
5022
|
/**
|
|
4929
|
-
*
|
|
4930
|
-
* @
|
|
5023
|
+
* Store user ID for authentication state persistence
|
|
5024
|
+
* @param userId - The user ID to store
|
|
4931
5025
|
*/
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
this.userToken
|
|
5026
|
+
storeUserId(userId) {
|
|
5027
|
+
// Initialize userToken if it doesn't exist
|
|
5028
|
+
if (!this.userToken) {
|
|
5029
|
+
this.userToken = {
|
|
5030
|
+
user_id: userId,
|
|
5031
|
+
};
|
|
5032
|
+
}
|
|
5033
|
+
else {
|
|
5034
|
+
// Update existing userToken with new userId
|
|
5035
|
+
this.userToken.user_id = userId;
|
|
5036
|
+
}
|
|
5037
|
+
// Set user ID in ApiClient for session context
|
|
5038
|
+
this.apiClient.setSessionContext(this.sessionId || '', this.companyId, undefined);
|
|
4936
5039
|
}
|
|
4937
5040
|
/**
|
|
4938
|
-
* Check if the
|
|
4939
|
-
* @returns True if authenticated
|
|
5041
|
+
* Check if the user is fully authenticated (has userId in session context)
|
|
5042
|
+
* @returns True if the user is fully authenticated and ready for API calls
|
|
4940
5043
|
*/
|
|
4941
|
-
|
|
4942
|
-
|
|
5044
|
+
async isAuthenticated() {
|
|
5045
|
+
// Check internal session context only - no localStorage dependency
|
|
5046
|
+
return this.userToken?.user_id !== undefined && this.userToken?.user_id !== null;
|
|
4943
5047
|
}
|
|
4944
5048
|
/**
|
|
4945
5049
|
* Get user's orders with pagination and optional filtering
|
|
4946
5050
|
* @param params - Query parameters including page, perPage, and filters
|
|
4947
5051
|
* @returns Promise with paginated result that supports navigation
|
|
4948
5052
|
*/
|
|
4949
|
-
async getOrders(
|
|
4950
|
-
if (!this.
|
|
5053
|
+
async getOrders(page = 1, perPage = 100, options, filters) {
|
|
5054
|
+
if (!(await this.isAuthenticated())) {
|
|
4951
5055
|
throw new AuthenticationError('User is not authenticated');
|
|
4952
5056
|
}
|
|
4953
|
-
const
|
|
4954
|
-
|
|
4955
|
-
const
|
|
4956
|
-
|
|
5057
|
+
const result = await this.apiClient.getBrokerOrdersPage(page, perPage, filters);
|
|
5058
|
+
// Add navigation methods to the result
|
|
5059
|
+
const paginatedResult = result;
|
|
5060
|
+
paginatedResult.next_page = async () => {
|
|
5061
|
+
if (paginatedResult.hasNext) {
|
|
5062
|
+
return this.apiClient.getBrokerOrdersPage(page + 1, perPage, filters);
|
|
5063
|
+
}
|
|
5064
|
+
throw new Error('No next page available');
|
|
5065
|
+
};
|
|
5066
|
+
paginatedResult.previous_page = async () => {
|
|
5067
|
+
if (paginatedResult.has_previous) {
|
|
5068
|
+
return this.apiClient.getBrokerOrdersPage(page - 1, perPage, filters);
|
|
5069
|
+
}
|
|
5070
|
+
throw new Error('No previous page available');
|
|
5071
|
+
};
|
|
5072
|
+
return paginatedResult;
|
|
4957
5073
|
}
|
|
4958
5074
|
/**
|
|
4959
5075
|
* Get user's positions with pagination and optional filtering
|
|
4960
5076
|
* @param params - Query parameters including page, perPage, and filters
|
|
4961
5077
|
* @returns Promise with paginated result that supports navigation
|
|
4962
5078
|
*/
|
|
4963
|
-
async getPositions(
|
|
4964
|
-
if (!this.
|
|
5079
|
+
async getPositions(page = 1, perPage = 100, options, filters) {
|
|
5080
|
+
if (!(await this.isAuthenticated())) {
|
|
4965
5081
|
throw new AuthenticationError('User is not authenticated');
|
|
4966
5082
|
}
|
|
4967
|
-
const
|
|
4968
|
-
|
|
4969
|
-
const
|
|
4970
|
-
|
|
5083
|
+
const result = await this.apiClient.getBrokerPositionsPage(page, perPage, filters);
|
|
5084
|
+
// Add navigation methods to the result
|
|
5085
|
+
const paginatedResult = result;
|
|
5086
|
+
paginatedResult.next_page = async () => {
|
|
5087
|
+
if (paginatedResult.hasNext) {
|
|
5088
|
+
return this.apiClient.getBrokerPositionsPage(page + 1, perPage, filters);
|
|
5089
|
+
}
|
|
5090
|
+
throw new Error('No next page available');
|
|
5091
|
+
};
|
|
5092
|
+
paginatedResult.previous_page = async () => {
|
|
5093
|
+
if (paginatedResult.has_previous) {
|
|
5094
|
+
return this.apiClient.getBrokerPositionsPage(page - 1, perPage, filters);
|
|
5095
|
+
}
|
|
5096
|
+
throw new Error('No previous page available');
|
|
5097
|
+
};
|
|
5098
|
+
return paginatedResult;
|
|
4971
5099
|
}
|
|
4972
5100
|
/**
|
|
4973
5101
|
* Get user's accounts with pagination and optional filtering
|
|
4974
5102
|
* @param params - Query parameters including page, perPage, and filters
|
|
4975
5103
|
* @returns Promise with paginated result that supports navigation
|
|
4976
5104
|
*/
|
|
4977
|
-
async getAccounts(
|
|
4978
|
-
if (!this.
|
|
5105
|
+
async getAccounts(page = 1, perPage = 100, options, filters) {
|
|
5106
|
+
if (!(await this.isAuthenticated())) {
|
|
4979
5107
|
throw new AuthenticationError('User is not authenticated');
|
|
4980
5108
|
}
|
|
4981
|
-
const
|
|
4982
|
-
|
|
4983
|
-
const
|
|
4984
|
-
|
|
5109
|
+
const result = await this.apiClient.getBrokerAccountsPage(page, perPage, filters);
|
|
5110
|
+
// Add navigation methods to the result
|
|
5111
|
+
const paginatedResult = result;
|
|
5112
|
+
paginatedResult.next_page = async () => {
|
|
5113
|
+
if (paginatedResult.hasNext) {
|
|
5114
|
+
return this.apiClient.getBrokerAccountsPage(page + 1, perPage, filters);
|
|
5115
|
+
}
|
|
5116
|
+
throw new Error('No next page available');
|
|
5117
|
+
};
|
|
5118
|
+
paginatedResult.previous_page = async () => {
|
|
5119
|
+
if (paginatedResult.has_previous) {
|
|
5120
|
+
return this.apiClient.getBrokerAccountsPage(page - 1, perPage, filters);
|
|
5121
|
+
}
|
|
5122
|
+
throw new Error('No previous page available');
|
|
5123
|
+
};
|
|
5124
|
+
return paginatedResult;
|
|
4985
5125
|
}
|
|
4986
5126
|
/**
|
|
4987
5127
|
* Get user's balances with pagination and optional filtering
|
|
4988
5128
|
* @param params - Query parameters including page, perPage, and filters
|
|
4989
5129
|
* @returns Promise with paginated result that supports navigation
|
|
4990
5130
|
*/
|
|
4991
|
-
async getBalances(
|
|
4992
|
-
if (!this.
|
|
5131
|
+
async getBalances(page = 1, perPage = 100, options, filters) {
|
|
5132
|
+
if (!(await this.isAuthenticated())) {
|
|
4993
5133
|
throw new AuthenticationError('User is not authenticated');
|
|
4994
5134
|
}
|
|
4995
|
-
const
|
|
4996
|
-
|
|
4997
|
-
const
|
|
4998
|
-
|
|
5135
|
+
const result = await this.apiClient.getBrokerBalancesPage(page, perPage, filters);
|
|
5136
|
+
// Add navigation methods to the result
|
|
5137
|
+
const paginatedResult = result;
|
|
5138
|
+
paginatedResult.next_page = async () => {
|
|
5139
|
+
if (paginatedResult.hasNext) {
|
|
5140
|
+
return this.apiClient.getBrokerBalancesPage(page + 1, perPage, filters);
|
|
5141
|
+
}
|
|
5142
|
+
throw new Error('No next page available');
|
|
5143
|
+
};
|
|
5144
|
+
paginatedResult.previous_page = async () => {
|
|
5145
|
+
if (paginatedResult.has_previous) {
|
|
5146
|
+
return this.apiClient.getBrokerBalancesPage(page - 1, perPage, filters);
|
|
5147
|
+
}
|
|
5148
|
+
throw new Error('No previous page available');
|
|
5149
|
+
};
|
|
5150
|
+
return paginatedResult;
|
|
4999
5151
|
}
|
|
5000
5152
|
/**
|
|
5001
5153
|
* Initialize the Finatic Connect SDK
|
|
@@ -5048,26 +5200,20 @@ class FinaticConnect extends EventEmitter {
|
|
|
5048
5200
|
FinaticConnect.instance.apiClient.setSessionContext(FinaticConnect.instance.sessionId, FinaticConnect.instance.companyId, startResponse.data.csrf_token // If available in response
|
|
5049
5201
|
);
|
|
5050
5202
|
}
|
|
5051
|
-
// If userId is provided,
|
|
5203
|
+
// If userId is provided, try to link user to session
|
|
5052
5204
|
if (normalizedUserId) {
|
|
5053
5205
|
try {
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
FinaticConnect.instance.userToken = userToken;
|
|
5066
|
-
// Set tokens in ApiClient for automatic token management
|
|
5067
|
-
const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
|
|
5068
|
-
FinaticConnect.instance.apiClient.setTokens(authResponse.data.access_token, authResponse.data.refresh_token, expiresAt, normalizedUserId);
|
|
5069
|
-
// Emit success event
|
|
5070
|
-
FinaticConnect.instance.emit('success', normalizedUserId);
|
|
5206
|
+
// Try to link user to session via API
|
|
5207
|
+
const linked = await FinaticConnect.instance.linkUserToSession(normalizedUserId);
|
|
5208
|
+
if (linked) {
|
|
5209
|
+
// Store user ID for authentication state
|
|
5210
|
+
FinaticConnect.instance.storeUserId(normalizedUserId);
|
|
5211
|
+
// Emit success event
|
|
5212
|
+
FinaticConnect.instance.emit('success', normalizedUserId);
|
|
5213
|
+
}
|
|
5214
|
+
else {
|
|
5215
|
+
console.warn('Failed to link user to session during initialization');
|
|
5216
|
+
}
|
|
5071
5217
|
}
|
|
5072
5218
|
catch (error) {
|
|
5073
5219
|
FinaticConnect.instance.emit('error', error);
|
|
@@ -5088,38 +5234,25 @@ class FinaticConnect extends EventEmitter {
|
|
|
5088
5234
|
* Get the user and tokens for a completed session
|
|
5089
5235
|
* @returns Promise with user information and tokens
|
|
5090
5236
|
*/
|
|
5091
|
-
async getSessionUser() {
|
|
5092
|
-
if (!this.isAuthed()) {
|
|
5093
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5094
|
-
}
|
|
5095
|
-
if (!this.userToken) {
|
|
5096
|
-
throw new AuthenticationError('No user token available');
|
|
5097
|
-
}
|
|
5098
|
-
return {
|
|
5099
|
-
user_id: this.userToken.userId,
|
|
5100
|
-
access_token: this.userToken.accessToken,
|
|
5101
|
-
refresh_token: this.userToken.refreshToken,
|
|
5102
|
-
expires_in: this.userToken.expiresIn,
|
|
5103
|
-
token_type: this.userToken.tokenType,
|
|
5104
|
-
scope: this.userToken.scope,
|
|
5105
|
-
company_id: this.companyId,
|
|
5106
|
-
};
|
|
5107
|
-
}
|
|
5108
5237
|
async initializeWithUser(userId) {
|
|
5109
5238
|
try {
|
|
5110
5239
|
if (!this.sessionId) {
|
|
5111
5240
|
throw new SessionError('Session not initialized');
|
|
5112
5241
|
}
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
if (
|
|
5116
|
-
|
|
5117
|
-
|
|
5242
|
+
// Try to link user to session
|
|
5243
|
+
const linked = await this.linkUserToSession(userId);
|
|
5244
|
+
if (!linked) {
|
|
5245
|
+
console.warn('Failed to link user to session during initialization');
|
|
5246
|
+
// Don't throw error, just continue without authentication
|
|
5247
|
+
return;
|
|
5118
5248
|
}
|
|
5249
|
+
// Store user ID for authentication state
|
|
5250
|
+
this.storeUserId(userId);
|
|
5119
5251
|
this.emit('success', userId);
|
|
5120
5252
|
}
|
|
5121
5253
|
catch (error) {
|
|
5122
5254
|
this.emit('error', error);
|
|
5255
|
+
throw error;
|
|
5123
5256
|
}
|
|
5124
5257
|
}
|
|
5125
5258
|
/**
|
|
@@ -5172,20 +5305,32 @@ class FinaticConnect extends EventEmitter {
|
|
|
5172
5305
|
url.searchParams.set('email', options.email);
|
|
5173
5306
|
themedPortalUrl = url.toString();
|
|
5174
5307
|
}
|
|
5308
|
+
// Add session ID to portal URL so the portal can use it
|
|
5309
|
+
const url = new URL(themedPortalUrl);
|
|
5310
|
+
if (this.sessionId) {
|
|
5311
|
+
url.searchParams.set('session_id', this.sessionId);
|
|
5312
|
+
}
|
|
5313
|
+
if (this.companyId) {
|
|
5314
|
+
url.searchParams.set('company_id', this.companyId);
|
|
5315
|
+
}
|
|
5316
|
+
themedPortalUrl = url.toString();
|
|
5175
5317
|
// Create portal UI if not exists
|
|
5176
5318
|
if (!this.portalUI) {
|
|
5177
5319
|
this.portalUI = new PortalUI(this.baseUrl);
|
|
5178
5320
|
}
|
|
5179
5321
|
// Show portal
|
|
5180
5322
|
this.portalUI.show(themedPortalUrl, this.sessionId || '', {
|
|
5181
|
-
onSuccess: async (userId
|
|
5323
|
+
onSuccess: async (userId) => {
|
|
5182
5324
|
try {
|
|
5183
5325
|
if (!this.sessionId) {
|
|
5184
5326
|
throw new SessionError('Session not initialized');
|
|
5185
5327
|
}
|
|
5186
|
-
//
|
|
5187
|
-
|
|
5188
|
-
|
|
5328
|
+
// Store the userId for authentication state
|
|
5329
|
+
this.storeUserId(userId);
|
|
5330
|
+
// Try to link user to session via API
|
|
5331
|
+
const linked = await this.linkUserToSession(userId);
|
|
5332
|
+
if (!linked) {
|
|
5333
|
+
console.warn('Failed to link user to session, but continuing with authentication');
|
|
5189
5334
|
}
|
|
5190
5335
|
// Emit portal success event
|
|
5191
5336
|
this.emit('portal:success', userId);
|
|
@@ -5276,32 +5421,16 @@ class FinaticConnect extends EventEmitter {
|
|
|
5276
5421
|
* Place a new order using the broker order API
|
|
5277
5422
|
* @param order - Order details with broker context
|
|
5278
5423
|
*/
|
|
5279
|
-
async placeOrder(order) {
|
|
5280
|
-
if (!this.
|
|
5281
|
-
throw new
|
|
5424
|
+
async placeOrder(order, extras) {
|
|
5425
|
+
if (!(await this.isAuthenticated())) {
|
|
5426
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5427
|
+
}
|
|
5428
|
+
if (!this.userToken?.user_id) {
|
|
5429
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5282
5430
|
}
|
|
5283
5431
|
try {
|
|
5284
|
-
//
|
|
5285
|
-
|
|
5286
|
-
symbol: order.symbol,
|
|
5287
|
-
orderQty: order.quantity,
|
|
5288
|
-
action: order.side === 'buy' ? 'Buy' : 'Sell',
|
|
5289
|
-
orderType: order.orderType === 'market'
|
|
5290
|
-
? 'Market'
|
|
5291
|
-
: order.orderType === 'limit'
|
|
5292
|
-
? 'Limit'
|
|
5293
|
-
: order.orderType === 'stop'
|
|
5294
|
-
? 'Stop'
|
|
5295
|
-
: 'StopLimit',
|
|
5296
|
-
assetType: order.assetType || 'Stock',
|
|
5297
|
-
timeInForce: order.timeInForce,
|
|
5298
|
-
price: order.price,
|
|
5299
|
-
stopPrice: order.stopPrice,
|
|
5300
|
-
broker: order.broker,
|
|
5301
|
-
accountNumber: order.accountNumber,
|
|
5302
|
-
order_id: order.order_id,
|
|
5303
|
-
};
|
|
5304
|
-
return await this.apiClient.placeBrokerOrder(this.userToken.accessToken, brokerOrder, {}, order.connection_id);
|
|
5432
|
+
// Use the order parameter directly since it's already BrokerOrderParams
|
|
5433
|
+
return await this.apiClient.placeBrokerOrder(order, extras || {}, order.connection_id);
|
|
5305
5434
|
}
|
|
5306
5435
|
catch (error) {
|
|
5307
5436
|
this.emit('error', error);
|
|
@@ -5315,8 +5444,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5315
5444
|
* @param connection_id - Optional connection ID for testing bypass
|
|
5316
5445
|
*/
|
|
5317
5446
|
async cancelOrder(orderId, broker, connection_id) {
|
|
5318
|
-
if (!this.
|
|
5319
|
-
throw new
|
|
5447
|
+
if (!(await this.isAuthenticated())) {
|
|
5448
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5449
|
+
}
|
|
5450
|
+
if (!this.userToken?.user_id) {
|
|
5451
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5320
5452
|
}
|
|
5321
5453
|
try {
|
|
5322
5454
|
return await this.apiClient.cancelBrokerOrder(orderId, broker, {}, connection_id);
|
|
@@ -5334,8 +5466,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5334
5466
|
* @param connection_id - Optional connection ID for testing bypass
|
|
5335
5467
|
*/
|
|
5336
5468
|
async modifyOrder(orderId, modifications, broker, connection_id) {
|
|
5337
|
-
if (!this.
|
|
5338
|
-
throw new
|
|
5469
|
+
if (!(await this.isAuthenticated())) {
|
|
5470
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5471
|
+
}
|
|
5472
|
+
if (!this.userToken?.user_id) {
|
|
5473
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5339
5474
|
}
|
|
5340
5475
|
try {
|
|
5341
5476
|
// Convert modifications to broker format
|
|
@@ -5363,39 +5498,15 @@ class FinaticConnect extends EventEmitter {
|
|
|
5363
5498
|
throw error;
|
|
5364
5499
|
}
|
|
5365
5500
|
}
|
|
5366
|
-
/**
|
|
5367
|
-
* Set the broker context for trading
|
|
5368
|
-
* @param broker - The broker to use for trading
|
|
5369
|
-
*/
|
|
5370
|
-
setTradingContextBroker(broker) {
|
|
5371
|
-
this.apiClient.setBroker(broker);
|
|
5372
|
-
}
|
|
5373
|
-
/**
|
|
5374
|
-
* Set the account context for trading
|
|
5375
|
-
* @param accountNumber - The account number to use for trading
|
|
5376
|
-
* @param accountId - Optional account ID
|
|
5377
|
-
*/
|
|
5378
|
-
setTradingContextAccount(accountNumber, accountId) {
|
|
5379
|
-
this.apiClient.setAccount(accountNumber, accountId);
|
|
5380
|
-
}
|
|
5381
|
-
/**
|
|
5382
|
-
* Get the current trading context
|
|
5383
|
-
*/
|
|
5384
|
-
getTradingContext() {
|
|
5385
|
-
return this.apiClient.getTradingContext();
|
|
5386
|
-
}
|
|
5387
|
-
/**
|
|
5388
|
-
* Clear the trading context
|
|
5389
|
-
*/
|
|
5390
|
-
clearTradingContext() {
|
|
5391
|
-
this.apiClient.clearTradingContext();
|
|
5392
|
-
}
|
|
5393
5501
|
/**
|
|
5394
5502
|
* Place a stock market order (convenience method)
|
|
5395
5503
|
*/
|
|
5396
5504
|
async placeStockMarketOrder(symbol, quantity, side, broker, accountNumber) {
|
|
5397
|
-
if (!this.
|
|
5398
|
-
throw new
|
|
5505
|
+
if (!(await this.isAuthenticated())) {
|
|
5506
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5507
|
+
}
|
|
5508
|
+
if (!this.userToken?.user_id) {
|
|
5509
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5399
5510
|
}
|
|
5400
5511
|
try {
|
|
5401
5512
|
return await this.apiClient.placeStockMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
|
|
@@ -5409,8 +5520,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5409
5520
|
* Place a stock limit order (convenience method)
|
|
5410
5521
|
*/
|
|
5411
5522
|
async placeStockLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
|
|
5412
|
-
if (!this.
|
|
5413
|
-
throw new
|
|
5523
|
+
if (!(await this.isAuthenticated())) {
|
|
5524
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5525
|
+
}
|
|
5526
|
+
if (!this.userToken?.user_id) {
|
|
5527
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5414
5528
|
}
|
|
5415
5529
|
try {
|
|
5416
5530
|
return await this.apiClient.placeStockLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
|
|
@@ -5424,8 +5538,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5424
5538
|
* Place a stock stop order (convenience method)
|
|
5425
5539
|
*/
|
|
5426
5540
|
async placeStockStopOrder(symbol, quantity, side, stopPrice, timeInForce = 'gtc', broker, accountNumber) {
|
|
5427
|
-
if (!this.
|
|
5428
|
-
throw new
|
|
5541
|
+
if (!(await this.isAuthenticated())) {
|
|
5542
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5543
|
+
}
|
|
5544
|
+
if (!this.userToken?.user_id) {
|
|
5545
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5429
5546
|
}
|
|
5430
5547
|
try {
|
|
5431
5548
|
return await this.apiClient.placeStockStopOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', stopPrice, timeInForce, broker, accountNumber);
|
|
@@ -5439,8 +5556,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5439
5556
|
* Place a crypto market order (convenience method)
|
|
5440
5557
|
*/
|
|
5441
5558
|
async placeCryptoMarketOrder(symbol, quantity, side, broker, accountNumber) {
|
|
5442
|
-
if (!this.
|
|
5443
|
-
throw new
|
|
5559
|
+
if (!(await this.isAuthenticated())) {
|
|
5560
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5561
|
+
}
|
|
5562
|
+
if (!this.userToken?.user_id) {
|
|
5563
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5444
5564
|
}
|
|
5445
5565
|
try {
|
|
5446
5566
|
return await this.apiClient.placeCryptoMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
|
|
@@ -5454,8 +5574,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5454
5574
|
* Place a crypto limit order (convenience method)
|
|
5455
5575
|
*/
|
|
5456
5576
|
async placeCryptoLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
|
|
5457
|
-
if (!this.
|
|
5458
|
-
throw new
|
|
5577
|
+
if (!(await this.isAuthenticated())) {
|
|
5578
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5579
|
+
}
|
|
5580
|
+
if (!this.userToken?.user_id) {
|
|
5581
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5459
5582
|
}
|
|
5460
5583
|
try {
|
|
5461
5584
|
return await this.apiClient.placeCryptoLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
|
|
@@ -5469,8 +5592,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5469
5592
|
* Place an options market order (convenience method)
|
|
5470
5593
|
*/
|
|
5471
5594
|
async placeOptionsMarketOrder(symbol, quantity, side, broker, accountNumber) {
|
|
5472
|
-
if (!this.
|
|
5473
|
-
throw new
|
|
5595
|
+
if (!(await this.isAuthenticated())) {
|
|
5596
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5597
|
+
}
|
|
5598
|
+
if (!this.userToken?.user_id) {
|
|
5599
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5474
5600
|
}
|
|
5475
5601
|
try {
|
|
5476
5602
|
return await this.apiClient.placeOptionsMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
|
|
@@ -5484,8 +5610,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5484
5610
|
* Place an options limit order (convenience method)
|
|
5485
5611
|
*/
|
|
5486
5612
|
async placeOptionsLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
|
|
5487
|
-
if (!this.
|
|
5488
|
-
throw new
|
|
5613
|
+
if (!(await this.isAuthenticated())) {
|
|
5614
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5615
|
+
}
|
|
5616
|
+
if (!this.userToken?.user_id) {
|
|
5617
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5489
5618
|
}
|
|
5490
5619
|
try {
|
|
5491
5620
|
return await this.apiClient.placeOptionsLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
|
|
@@ -5499,8 +5628,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5499
5628
|
* Place a futures market order (convenience method)
|
|
5500
5629
|
*/
|
|
5501
5630
|
async placeFuturesMarketOrder(symbol, quantity, side, broker, accountNumber) {
|
|
5502
|
-
if (!this.
|
|
5503
|
-
throw new
|
|
5631
|
+
if (!(await this.isAuthenticated())) {
|
|
5632
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5633
|
+
}
|
|
5634
|
+
if (!this.userToken?.user_id) {
|
|
5635
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5504
5636
|
}
|
|
5505
5637
|
try {
|
|
5506
5638
|
return await this.apiClient.placeFuturesMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
|
|
@@ -5514,8 +5646,11 @@ class FinaticConnect extends EventEmitter {
|
|
|
5514
5646
|
* Place a futures limit order (convenience method)
|
|
5515
5647
|
*/
|
|
5516
5648
|
async placeFuturesLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
|
|
5517
|
-
if (!this.
|
|
5518
|
-
throw new
|
|
5649
|
+
if (!(await this.isAuthenticated())) {
|
|
5650
|
+
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5651
|
+
}
|
|
5652
|
+
if (!this.userToken?.user_id) {
|
|
5653
|
+
throw new AuthenticationError('No user ID available. Please connect a broker first.');
|
|
5519
5654
|
}
|
|
5520
5655
|
try {
|
|
5521
5656
|
return await this.apiClient.placeFuturesLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
|
|
@@ -5530,8 +5665,8 @@ class FinaticConnect extends EventEmitter {
|
|
|
5530
5665
|
* @returns The current user ID or undefined if not authenticated
|
|
5531
5666
|
* @throws AuthenticationError if user is not authenticated
|
|
5532
5667
|
*/
|
|
5533
|
-
getUserId() {
|
|
5534
|
-
if (!this.
|
|
5668
|
+
async getUserId() {
|
|
5669
|
+
if (!(await this.isAuthenticated())) {
|
|
5535
5670
|
return null;
|
|
5536
5671
|
}
|
|
5537
5672
|
if (!this.userToken?.user_id) {
|
|
@@ -5544,7 +5679,7 @@ class FinaticConnect extends EventEmitter {
|
|
|
5544
5679
|
* @returns Promise with array of broker information
|
|
5545
5680
|
*/
|
|
5546
5681
|
async getBrokerList() {
|
|
5547
|
-
// if (!this.
|
|
5682
|
+
// if (!this.isAuthenticated()) {
|
|
5548
5683
|
// throw new AuthenticationError('Not authenticated');
|
|
5549
5684
|
// }
|
|
5550
5685
|
const response = await this.apiClient.getBrokerList();
|
|
@@ -5561,7 +5696,7 @@ class FinaticConnect extends EventEmitter {
|
|
|
5561
5696
|
* @throws AuthenticationError if user is not authenticated
|
|
5562
5697
|
*/
|
|
5563
5698
|
async getBrokerConnections() {
|
|
5564
|
-
if (!this.
|
|
5699
|
+
if (!(await this.isAuthenticated())) {
|
|
5565
5700
|
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5566
5701
|
}
|
|
5567
5702
|
if (!this.userToken?.user_id) {
|
|
@@ -5635,118 +5770,22 @@ class FinaticConnect extends EventEmitter {
|
|
|
5635
5770
|
return this.getAllPositions({ broker_id: brokerId });
|
|
5636
5771
|
}
|
|
5637
5772
|
// Pagination methods
|
|
5638
|
-
/**
|
|
5639
|
-
* Get a specific page of orders with pagination metadata
|
|
5640
|
-
* @param page - Page number (default: 1)
|
|
5641
|
-
* @param perPage - Items per page (default: 100)
|
|
5642
|
-
* @param filter - Optional filter parameters
|
|
5643
|
-
* @returns Promise with paginated orders result
|
|
5644
|
-
*/
|
|
5645
|
-
async getOrdersPage(page = 1, perPage = 100, filter) {
|
|
5646
|
-
if (!this.isAuthed()) {
|
|
5647
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5648
|
-
}
|
|
5649
|
-
return this.apiClient.getBrokerOrdersPage(page, perPage, filter);
|
|
5650
|
-
}
|
|
5651
|
-
/**
|
|
5652
|
-
* Get a specific page of positions with pagination metadata
|
|
5653
|
-
* @param page - Page number (default: 1)
|
|
5654
|
-
* @param perPage - Items per page (default: 100)
|
|
5655
|
-
* @param filter - Optional filter parameters
|
|
5656
|
-
* @returns Promise with paginated positions result
|
|
5657
|
-
*/
|
|
5658
|
-
async getPositionsPage(page = 1, perPage = 100, filter) {
|
|
5659
|
-
if (!this.isAuthed()) {
|
|
5660
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5661
|
-
}
|
|
5662
|
-
return this.apiClient.getBrokerPositionsPage(page, perPage, filter);
|
|
5663
|
-
}
|
|
5664
|
-
/**
|
|
5665
|
-
* Get a specific page of accounts with pagination metadata
|
|
5666
|
-
* @param page - Page number (default: 1)
|
|
5667
|
-
* @param perPage - Items per page (default: 100)
|
|
5668
|
-
* @param filter - Optional filter parameters
|
|
5669
|
-
* @returns Promise with paginated accounts result
|
|
5670
|
-
*/
|
|
5671
|
-
async getAccountsPage(page = 1, perPage = 100, filter) {
|
|
5672
|
-
if (!this.isAuthed()) {
|
|
5673
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5674
|
-
}
|
|
5675
|
-
return this.apiClient.getBrokerAccountsPage(page, perPage, filter);
|
|
5676
|
-
}
|
|
5677
|
-
async getBalancesPage(page = 1, perPage = 100, filter) {
|
|
5678
|
-
if (!this.isAuthed()) {
|
|
5679
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5680
|
-
}
|
|
5681
|
-
return this.apiClient.getBrokerBalancesPage(page, perPage, filter);
|
|
5682
|
-
}
|
|
5683
|
-
/**
|
|
5684
|
-
* Get the next page of orders
|
|
5685
|
-
* @param previousResult - The previous paginated result
|
|
5686
|
-
* @returns Promise with next page of orders or null if no more pages
|
|
5687
|
-
*/
|
|
5688
|
-
async getNextOrdersPage(previousResult) {
|
|
5689
|
-
if (!this.isAuthed()) {
|
|
5690
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5691
|
-
}
|
|
5692
|
-
return this.apiClient.getNextPage(previousResult, (offset, limit) => {
|
|
5693
|
-
const page = Math.floor(offset / limit) + 1;
|
|
5694
|
-
return this.apiClient.getBrokerOrdersPage(page, limit);
|
|
5695
|
-
});
|
|
5696
|
-
}
|
|
5697
|
-
/**
|
|
5698
|
-
* Get the next page of positions
|
|
5699
|
-
* @param previousResult - The previous paginated result
|
|
5700
|
-
* @returns Promise with next page of positions or null if no more pages
|
|
5701
|
-
*/
|
|
5702
|
-
async getNextPositionsPage(previousResult) {
|
|
5703
|
-
if (!this.isAuthed()) {
|
|
5704
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5705
|
-
}
|
|
5706
|
-
return this.apiClient.getNextPage(previousResult, (offset, limit) => {
|
|
5707
|
-
const page = Math.floor(offset / limit) + 1;
|
|
5708
|
-
return this.apiClient.getBrokerPositionsPage(page, limit);
|
|
5709
|
-
});
|
|
5710
|
-
}
|
|
5711
|
-
/**
|
|
5712
|
-
* Get the next page of accounts
|
|
5713
|
-
* @param previousResult - The previous paginated result
|
|
5714
|
-
* @returns Promise with next page of accounts or null if no more pages
|
|
5715
|
-
*/
|
|
5716
|
-
async getNextAccountsPage(previousResult) {
|
|
5717
|
-
if (!this.isAuthed()) {
|
|
5718
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5719
|
-
}
|
|
5720
|
-
return this.apiClient.getNextPage(previousResult, (offset, limit) => {
|
|
5721
|
-
const page = Math.floor(offset / limit) + 1;
|
|
5722
|
-
return this.apiClient.getBrokerAccountsPage(page, limit);
|
|
5723
|
-
});
|
|
5724
|
-
}
|
|
5725
|
-
async getNextBalancesPage(previousResult) {
|
|
5726
|
-
if (!this.isAuthed()) {
|
|
5727
|
-
throw new AuthenticationError('User is not authenticated');
|
|
5728
|
-
}
|
|
5729
|
-
return this.apiClient.getNextPage(previousResult, (offset, limit) => {
|
|
5730
|
-
const page = Math.floor(offset / limit) + 1;
|
|
5731
|
-
return this.apiClient.getBrokerBalancesPage(page, limit);
|
|
5732
|
-
});
|
|
5733
|
-
}
|
|
5734
5773
|
/**
|
|
5735
5774
|
* Get all orders across all pages (convenience method)
|
|
5736
5775
|
* @param filter - Optional filter parameters
|
|
5737
5776
|
* @returns Promise with all orders
|
|
5738
5777
|
*/
|
|
5739
5778
|
async getAllOrders(filter) {
|
|
5740
|
-
if (!this.
|
|
5779
|
+
if (!(await this.isAuthenticated())) {
|
|
5741
5780
|
throw new AuthenticationError('User is not authenticated');
|
|
5742
5781
|
}
|
|
5743
5782
|
const allData = [];
|
|
5744
|
-
let currentResult = await this.
|
|
5783
|
+
let currentResult = await this.apiClient.getBrokerOrdersPage(1, 100, filter);
|
|
5745
5784
|
while (currentResult) {
|
|
5746
5785
|
allData.push(...currentResult.data);
|
|
5747
5786
|
if (!currentResult.hasNext)
|
|
5748
5787
|
break;
|
|
5749
|
-
const nextResult = await
|
|
5788
|
+
const nextResult = await currentResult.nextPage();
|
|
5750
5789
|
if (!nextResult)
|
|
5751
5790
|
break;
|
|
5752
5791
|
currentResult = nextResult;
|
|
@@ -5759,16 +5798,16 @@ class FinaticConnect extends EventEmitter {
|
|
|
5759
5798
|
* @returns Promise with all positions
|
|
5760
5799
|
*/
|
|
5761
5800
|
async getAllPositions(filter) {
|
|
5762
|
-
if (!this.
|
|
5801
|
+
if (!(await this.isAuthenticated())) {
|
|
5763
5802
|
throw new AuthenticationError('User is not authenticated');
|
|
5764
5803
|
}
|
|
5765
5804
|
const allData = [];
|
|
5766
|
-
let currentResult = await this.
|
|
5805
|
+
let currentResult = await this.apiClient.getBrokerPositionsPage(1, 100, filter);
|
|
5767
5806
|
while (currentResult) {
|
|
5768
5807
|
allData.push(...currentResult.data);
|
|
5769
5808
|
if (!currentResult.hasNext)
|
|
5770
5809
|
break;
|
|
5771
|
-
const nextResult = await
|
|
5810
|
+
const nextResult = await currentResult.nextPage();
|
|
5772
5811
|
if (!nextResult)
|
|
5773
5812
|
break;
|
|
5774
5813
|
currentResult = nextResult;
|
|
@@ -5781,16 +5820,16 @@ class FinaticConnect extends EventEmitter {
|
|
|
5781
5820
|
* @returns Promise with all accounts
|
|
5782
5821
|
*/
|
|
5783
5822
|
async getAllAccounts(filter) {
|
|
5784
|
-
if (!this.
|
|
5823
|
+
if (!(await this.isAuthenticated())) {
|
|
5785
5824
|
throw new AuthenticationError('User is not authenticated');
|
|
5786
5825
|
}
|
|
5787
5826
|
const allData = [];
|
|
5788
|
-
let currentResult = await this.
|
|
5827
|
+
let currentResult = await this.apiClient.getBrokerAccountsPage(1, 100, filter);
|
|
5789
5828
|
while (currentResult) {
|
|
5790
5829
|
allData.push(...currentResult.data);
|
|
5791
5830
|
if (!currentResult.hasNext)
|
|
5792
5831
|
break;
|
|
5793
|
-
const nextResult = await
|
|
5832
|
+
const nextResult = await currentResult.nextPage();
|
|
5794
5833
|
if (!nextResult)
|
|
5795
5834
|
break;
|
|
5796
5835
|
currentResult = nextResult;
|
|
@@ -5798,16 +5837,16 @@ class FinaticConnect extends EventEmitter {
|
|
|
5798
5837
|
return allData;
|
|
5799
5838
|
}
|
|
5800
5839
|
async getAllBalances(filter) {
|
|
5801
|
-
if (!this.
|
|
5840
|
+
if (!(await this.isAuthenticated())) {
|
|
5802
5841
|
throw new AuthenticationError('User is not authenticated');
|
|
5803
5842
|
}
|
|
5804
5843
|
const allData = [];
|
|
5805
|
-
let currentResult = await this.
|
|
5844
|
+
let currentResult = await this.apiClient.getBrokerBalancesPage(1, 100, filter);
|
|
5806
5845
|
while (currentResult) {
|
|
5807
5846
|
allData.push(...currentResult.data);
|
|
5808
5847
|
if (!currentResult.hasNext)
|
|
5809
5848
|
break;
|
|
5810
|
-
const nextResult = await
|
|
5849
|
+
const nextResult = await currentResult.nextPage();
|
|
5811
5850
|
if (!nextResult)
|
|
5812
5851
|
break;
|
|
5813
5852
|
currentResult = nextResult;
|
|
@@ -5852,7 +5891,7 @@ class FinaticConnect extends EventEmitter {
|
|
|
5852
5891
|
* Validate session for keep-alive purposes and handle automatic refresh
|
|
5853
5892
|
*/
|
|
5854
5893
|
async validateSessionKeepAlive() {
|
|
5855
|
-
if (!this.sessionId || !this.
|
|
5894
|
+
if (!this.sessionId || !(await this.isAuthenticated())) {
|
|
5856
5895
|
console.log('[FinaticConnect] Session keep-alive skipped - no active session');
|
|
5857
5896
|
return;
|
|
5858
5897
|
}
|
|
@@ -5963,7 +6002,6 @@ class FinaticConnect extends EventEmitter {
|
|
|
5963
6002
|
const response = await fetch(`${this.baseUrl}/portal/${sessionId}/complete`, {
|
|
5964
6003
|
method: 'POST',
|
|
5965
6004
|
headers: {
|
|
5966
|
-
Authorization: `Bearer ${this.userToken?.accessToken || ''}`,
|
|
5967
6005
|
'Content-Type': 'application/json',
|
|
5968
6006
|
},
|
|
5969
6007
|
});
|
|
@@ -5986,7 +6024,7 @@ class FinaticConnect extends EventEmitter {
|
|
|
5986
6024
|
* @throws AuthenticationError if user is not authenticated
|
|
5987
6025
|
*/
|
|
5988
6026
|
async disconnectCompany(connectionId) {
|
|
5989
|
-
if (!this.
|
|
6027
|
+
if (!(await this.isAuthenticated())) {
|
|
5990
6028
|
throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
|
|
5991
6029
|
}
|
|
5992
6030
|
if (!this.userToken?.user_id) {
|
|
@@ -5995,25 +6033,70 @@ class FinaticConnect extends EventEmitter {
|
|
|
5995
6033
|
return this.apiClient.disconnectCompany(connectionId);
|
|
5996
6034
|
}
|
|
5997
6035
|
/**
|
|
5998
|
-
* Get
|
|
5999
|
-
* @param
|
|
6000
|
-
* @
|
|
6036
|
+
* Get order fills for a specific order
|
|
6037
|
+
* @param orderId - The order ID
|
|
6038
|
+
* @param filter - Optional filter parameters
|
|
6039
|
+
* @returns Promise with order fills response
|
|
6001
6040
|
*/
|
|
6002
|
-
async
|
|
6003
|
-
if (!this.
|
|
6041
|
+
async getOrderFills(orderId, filter) {
|
|
6042
|
+
if (!(await this.isAuthenticated())) {
|
|
6004
6043
|
throw new AuthenticationError('User is not authenticated');
|
|
6005
6044
|
}
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6045
|
+
const response = await this.apiClient.getOrderFills(orderId, filter);
|
|
6046
|
+
return response.response_data;
|
|
6047
|
+
}
|
|
6048
|
+
/**
|
|
6049
|
+
* Get order events for a specific order
|
|
6050
|
+
* @param orderId - The order ID
|
|
6051
|
+
* @param filter - Optional filter parameters
|
|
6052
|
+
* @returns Promise with order events response
|
|
6053
|
+
*/
|
|
6054
|
+
async getOrderEvents(orderId, filter) {
|
|
6055
|
+
if (!(await this.isAuthenticated())) {
|
|
6056
|
+
throw new AuthenticationError('User is not authenticated');
|
|
6009
6057
|
}
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6058
|
+
const response = await this.apiClient.getOrderEvents(orderId, filter);
|
|
6059
|
+
return response.response_data;
|
|
6060
|
+
}
|
|
6061
|
+
/**
|
|
6062
|
+
* Get order groups
|
|
6063
|
+
* @param filter - Optional filter parameters
|
|
6064
|
+
* @returns Promise with order groups response
|
|
6065
|
+
*/
|
|
6066
|
+
async getOrderGroups(filter) {
|
|
6067
|
+
if (!(await this.isAuthenticated())) {
|
|
6068
|
+
throw new AuthenticationError('User is not authenticated');
|
|
6013
6069
|
}
|
|
6070
|
+
const response = await this.apiClient.getOrderGroups(filter);
|
|
6071
|
+
return response.response_data;
|
|
6072
|
+
}
|
|
6073
|
+
/**
|
|
6074
|
+
* Get position lots (tax lots for positions)
|
|
6075
|
+
* @param filter - Optional filter parameters
|
|
6076
|
+
* @returns Promise with position lots response
|
|
6077
|
+
*/
|
|
6078
|
+
async getPositionLots(filter) {
|
|
6079
|
+
if (!(await this.isAuthenticated())) {
|
|
6080
|
+
throw new AuthenticationError('User is not authenticated');
|
|
6081
|
+
}
|
|
6082
|
+
const response = await this.apiClient.getPositionLots(filter);
|
|
6083
|
+
return response.response_data;
|
|
6084
|
+
}
|
|
6085
|
+
/**
|
|
6086
|
+
* Get position lot fills for a specific lot
|
|
6087
|
+
* @param lotId - The position lot ID
|
|
6088
|
+
* @param filter - Optional filter parameters
|
|
6089
|
+
* @returns Promise with position lot fills response
|
|
6090
|
+
*/
|
|
6091
|
+
async getPositionLotFills(lotId, filter) {
|
|
6092
|
+
if (!(await this.isAuthenticated())) {
|
|
6093
|
+
throw new AuthenticationError('User is not authenticated');
|
|
6094
|
+
}
|
|
6095
|
+
const response = await this.apiClient.getPositionLotFills(lotId, filter);
|
|
6096
|
+
return response.response_data;
|
|
6014
6097
|
}
|
|
6015
6098
|
}
|
|
6016
6099
|
FinaticConnect.instance = null;
|
|
6017
6100
|
|
|
6018
|
-
export { ApiClient, ApiError, AuthenticationError, AuthorizationError, BaseError, CompanyAccessError, EventEmitter, FinaticConnect,
|
|
6101
|
+
export { ApiClient, ApiError, AuthenticationError, AuthorizationError, BaseError, CompanyAccessError, EventEmitter, FinaticConnect, NetworkError, OrderError, OrderValidationError, PaginatedResult, RateLimitError, SecurityError, SessionError, TokenError, TradingNotEnabledError, ValidationError, appendThemeToURL, createCustomThemeFromPreset, generatePortalThemeURL, getThemePreset, portalThemePresets, validateCustomTheme };
|
|
6019
6102
|
//# sourceMappingURL=index.mjs.map
|