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