@finatic/client 0.0.135 → 0.0.137

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -198,6 +198,12 @@ class OrderValidationError extends ApiError {
198
198
  this.name = 'OrderValidationError';
199
199
  }
200
200
  }
201
+ class TradingNotEnabledError extends ApiError {
202
+ constructor(message, details) {
203
+ super(403, message, details);
204
+ this.name = 'TradingNotEnabledError';
205
+ }
206
+ }
201
207
 
202
208
  class ApiClient {
203
209
  constructor(baseUrl, deviceInfo) {
@@ -513,6 +519,9 @@ class ApiClient {
513
519
  if (error.detail?.code === 'NO_COMPANY_ACCESS') {
514
520
  return new CompanyAccessError(error.detail.message || 'No broker connections found for this company', error.detail);
515
521
  }
522
+ if (error.detail?.code === 'TRADING_NOT_ENABLED') {
523
+ return new TradingNotEnabledError(error.detail.message || 'Trading is not enabled for your company', error.detail);
524
+ }
516
525
  return new AuthorizationError(message || 'Forbidden: No access to the requested data', error);
517
526
  case 404:
518
527
  return new ApiError(status, message || 'Not found: The requested data does not exist', error);
@@ -643,23 +652,6 @@ class ApiClient {
643
652
  },
644
653
  });
645
654
  }
646
- async validatePortalSession(sessionId, signature) {
647
- return this.request('/portal/validate', {
648
- method: 'GET',
649
- headers: {
650
- 'Content-Type': 'application/json',
651
- 'X-Session-ID': sessionId,
652
- 'X-Device-Info': JSON.stringify({
653
- ip_address: this.deviceInfo?.ip_address || '',
654
- user_agent: this.deviceInfo?.user_agent || '',
655
- fingerprint: this.deviceInfo?.fingerprint || '',
656
- }),
657
- },
658
- params: {
659
- signature,
660
- },
661
- });
662
- }
663
655
  async completePortalSession(sessionId) {
664
656
  return this.request(`/portal/${sessionId}/complete`, {
665
657
  method: 'POST',
@@ -669,34 +661,14 @@ class ApiClient {
669
661
  });
670
662
  }
671
663
  // Portfolio Management
672
- async getHoldings() {
673
- const accessToken = await this.getValidAccessToken();
674
- return this.request('/portfolio/holdings', {
675
- method: 'GET',
676
- headers: {
677
- 'Authorization': `Bearer ${accessToken}`,
678
- },
679
- });
680
- }
681
664
  async getOrders() {
682
665
  const accessToken = await this.getValidAccessToken();
683
- return this.request('/data/orders', {
684
- method: 'GET',
685
- headers: {
686
- 'Authorization': `Bearer ${accessToken}`,
687
- },
688
- });
689
- }
690
- async getPortfolio() {
691
- const accessToken = await this.getValidAccessToken();
692
- const response = await this.request('/portfolio/', {
666
+ return this.request('/brokers/data/orders', {
693
667
  method: 'GET',
694
668
  headers: {
695
669
  'Authorization': `Bearer ${accessToken}`,
696
- 'Content-Type': 'application/json',
697
670
  },
698
671
  });
699
- return response;
700
672
  }
701
673
  // Enhanced Trading Methods with Session Management
702
674
  async placeBrokerOrder(params, extras = {}, connection_id) {
@@ -759,10 +731,7 @@ class ApiClient {
759
731
  }
760
732
  const accountNumber = this.tradingContext.accountNumber;
761
733
  // Build query parameters as required by API documentation
762
- const queryParams = {
763
- broker: selectedBroker,
764
- order_id: orderId,
765
- };
734
+ const queryParams = {};
766
735
  // Add optional parameters if available
767
736
  if (accountNumber) {
768
737
  queryParams.account_number = accountNumber.toString();
@@ -782,7 +751,7 @@ class ApiClient {
782
751
  }
783
752
  };
784
753
  }
785
- return this.request('/brokers/orders', {
754
+ return this.request(`/brokers/orders/${orderId}`, {
786
755
  method: 'DELETE',
787
756
  headers: {
788
757
  'Content-Type': 'application/json',
@@ -1052,19 +1021,9 @@ class ApiClient {
1052
1021
  return extras;
1053
1022
  }
1054
1023
  }
1055
- async revokeToken() {
1024
+ async getUserToken(sessionId) {
1056
1025
  const accessToken = await this.getValidAccessToken();
1057
- await this.request('/auth/token/revoke', {
1058
- method: 'POST',
1059
- headers: {
1060
- 'Authorization': `Bearer ${accessToken}`,
1061
- },
1062
- });
1063
- this.clearTokens();
1064
- }
1065
- async getUserToken(userId) {
1066
- const accessToken = await this.getValidAccessToken();
1067
- return this.request(`/auth/token/user/${userId}`, {
1026
+ return this.request(`/auth/session/${sessionId}/user`, {
1068
1027
  method: 'GET',
1069
1028
  headers: {
1070
1029
  'Authorization': `Bearer ${accessToken}`,
@@ -1095,7 +1054,7 @@ class ApiClient {
1095
1054
  // Broker Data Management
1096
1055
  async getBrokerList() {
1097
1056
  const accessToken = await this.getValidAccessToken();
1098
- return this.request('/brokers/list', {
1057
+ return this.request('/brokers/', {
1099
1058
  method: 'GET',
1100
1059
  headers: {
1101
1060
  'Authorization': `Bearer ${accessToken}`,
@@ -1162,6 +1121,26 @@ class ApiClient {
1162
1121
  params,
1163
1122
  });
1164
1123
  }
1124
+ async getBrokerBalances(options) {
1125
+ const accessToken = await this.getValidAccessToken();
1126
+ const params = {};
1127
+ if (options?.broker_name) {
1128
+ params.broker_id = options.broker_name;
1129
+ }
1130
+ if (options?.account_id) {
1131
+ params.account_id = options.account_id;
1132
+ }
1133
+ if (options?.symbol) {
1134
+ params.symbol = options.symbol;
1135
+ }
1136
+ return this.request('/brokers/data/balances', {
1137
+ method: 'GET',
1138
+ headers: {
1139
+ 'Authorization': `Bearer ${accessToken}`,
1140
+ },
1141
+ params,
1142
+ });
1143
+ }
1165
1144
  async getBrokerConnections() {
1166
1145
  const accessToken = await this.getValidAccessToken();
1167
1146
  return this.request('/brokers/connections', {
@@ -1171,6 +1150,25 @@ class ApiClient {
1171
1150
  },
1172
1151
  });
1173
1152
  }
1153
+ async getBalances(filters) {
1154
+ const accessToken = await this.getValidAccessToken();
1155
+ const params = new URLSearchParams();
1156
+ if (filters) {
1157
+ Object.entries(filters).forEach(([key, value]) => {
1158
+ if (value !== undefined && value !== null) {
1159
+ params.append(key, String(value));
1160
+ }
1161
+ });
1162
+ }
1163
+ const queryString = params.toString();
1164
+ const url = queryString ? `/brokers/data/balances?${queryString}` : '/brokers/data/balances';
1165
+ return this.request(url, {
1166
+ method: 'GET',
1167
+ headers: {
1168
+ 'Authorization': `Bearer ${accessToken}`,
1169
+ },
1170
+ });
1171
+ }
1174
1172
  // Page-based pagination methods
1175
1173
  async getBrokerOrdersPage(page = 1, perPage = 100, filters) {
1176
1174
  const accessToken = await this.getValidAccessToken();
@@ -1389,6 +1387,81 @@ class ApiClient {
1389
1387
  limit: perPage,
1390
1388
  }, navigationCallback);
1391
1389
  }
1390
+ async getBrokerBalancesPage(page = 1, perPage = 100, filters) {
1391
+ const accessToken = await this.getValidAccessToken();
1392
+ const offset = (page - 1) * perPage;
1393
+ const params = {
1394
+ limit: perPage.toString(),
1395
+ offset: offset.toString(),
1396
+ };
1397
+ // Add filter parameters
1398
+ if (filters) {
1399
+ if (filters.broker_id)
1400
+ params.broker_id = filters.broker_id;
1401
+ if (filters.connection_id)
1402
+ params.connection_id = filters.connection_id;
1403
+ if (filters.account_id)
1404
+ params.account_id = filters.account_id;
1405
+ if (filters.is_end_of_day_snapshot !== undefined)
1406
+ params.is_end_of_day_snapshot = filters.is_end_of_day_snapshot.toString();
1407
+ if (filters.balance_created_after)
1408
+ params.balance_created_after = filters.balance_created_after;
1409
+ if (filters.balance_created_before)
1410
+ params.balance_created_before = filters.balance_created_before;
1411
+ if (filters.with_metadata !== undefined)
1412
+ params.with_metadata = filters.with_metadata.toString();
1413
+ }
1414
+ const response = await this.request('/brokers/data/balances', {
1415
+ method: 'GET',
1416
+ headers: {
1417
+ Authorization: `Bearer ${accessToken}`,
1418
+ },
1419
+ params,
1420
+ });
1421
+ // Create navigation callback for pagination
1422
+ const navigationCallback = async (newOffset, newLimit) => {
1423
+ const newParams = {
1424
+ limit: newLimit.toString(),
1425
+ offset: newOffset.toString(),
1426
+ };
1427
+ // Add filter parameters
1428
+ if (filters) {
1429
+ if (filters.broker_id)
1430
+ newParams.broker_id = filters.broker_id;
1431
+ if (filters.connection_id)
1432
+ newParams.connection_id = filters.connection_id;
1433
+ if (filters.account_id)
1434
+ newParams.account_id = filters.account_id;
1435
+ if (filters.is_end_of_day_snapshot !== undefined)
1436
+ newParams.is_end_of_day_snapshot = filters.is_end_of_day_snapshot.toString();
1437
+ if (filters.balance_created_after)
1438
+ newParams.balance_created_after = filters.balance_created_after;
1439
+ if (filters.balance_created_before)
1440
+ newParams.balance_created_before = filters.balance_created_before;
1441
+ if (filters.with_metadata !== undefined)
1442
+ newParams.with_metadata = filters.with_metadata.toString();
1443
+ }
1444
+ const newResponse = await this.request('/brokers/data/balances', {
1445
+ method: 'GET',
1446
+ headers: {
1447
+ Authorization: `Bearer ${accessToken}`,
1448
+ },
1449
+ params: newParams,
1450
+ });
1451
+ return new PaginatedResult(newResponse.response_data, newResponse.pagination || {
1452
+ has_more: false,
1453
+ next_offset: newOffset,
1454
+ current_offset: newOffset,
1455
+ limit: newLimit,
1456
+ }, navigationCallback);
1457
+ };
1458
+ return new PaginatedResult(response.response_data, response.pagination || {
1459
+ has_more: false,
1460
+ next_offset: offset,
1461
+ current_offset: offset,
1462
+ limit: perPage,
1463
+ }, navigationCallback);
1464
+ }
1392
1465
  // Navigation methods
1393
1466
  async getNextPage(previousResult, fetchFunction) {
1394
1467
  if (!previousResult.hasNext) {
@@ -1410,7 +1483,7 @@ class ApiClient {
1410
1483
  */
1411
1484
  async disconnectCompany(connectionId) {
1412
1485
  const accessToken = await this.getValidAccessToken();
1413
- return this.request(`/brokers/disconnect-company/${connectionId}`, {
1486
+ return this.request(`/brokers/disconnect/${connectionId}`, {
1414
1487
  method: 'DELETE',
1415
1488
  headers: {
1416
1489
  'Authorization': `Bearer ${accessToken}`,
@@ -2065,109 +2138,6 @@ class MockDataProvider {
2065
2138
  };
2066
2139
  }
2067
2140
  // Portfolio & Trading Mocks
2068
- async mockGetHoldings() {
2069
- await this.simulateDelay();
2070
- const holdings = [
2071
- {
2072
- symbol: 'AAPL',
2073
- quantity: 100,
2074
- averagePrice: 150.25,
2075
- currentPrice: 175.5,
2076
- marketValue: 17550.0,
2077
- unrealizedPnL: 2525.0,
2078
- realizedPnL: 0,
2079
- costBasis: 15025.0,
2080
- currency: 'USD',
2081
- },
2082
- {
2083
- symbol: 'TSLA',
2084
- quantity: 50,
2085
- averagePrice: 200.0,
2086
- currentPrice: 220.75,
2087
- marketValue: 11037.5,
2088
- unrealizedPnL: 1037.5,
2089
- realizedPnL: 0,
2090
- costBasis: 10000.0,
2091
- currency: 'USD',
2092
- },
2093
- {
2094
- symbol: 'MSFT',
2095
- quantity: 75,
2096
- averagePrice: 300.0,
2097
- currentPrice: 325.25,
2098
- marketValue: 24393.75,
2099
- unrealizedPnL: 1893.75,
2100
- realizedPnL: 0,
2101
- costBasis: 22500.0,
2102
- currency: 'USD',
2103
- },
2104
- ];
2105
- return { data: holdings };
2106
- }
2107
- async mockGetPortfolio() {
2108
- await this.simulateDelay();
2109
- const portfolio = {
2110
- id: uuid.v4(),
2111
- name: 'Main Portfolio',
2112
- type: 'individual',
2113
- status: 'active',
2114
- cash: 15000.5,
2115
- buyingPower: 45000.0,
2116
- equity: 52981.25,
2117
- longMarketValue: 37981.25,
2118
- shortMarketValue: 0,
2119
- initialMargin: 0,
2120
- maintenanceMargin: 0,
2121
- lastEquity: 52000.0,
2122
- positions: [
2123
- {
2124
- symbol: 'AAPL',
2125
- quantity: 100,
2126
- averagePrice: 150.25,
2127
- currentPrice: 175.5,
2128
- marketValue: 17550.0,
2129
- unrealizedPnL: 2525.0,
2130
- realizedPnL: 0,
2131
- costBasis: 15025.0,
2132
- currency: 'USD',
2133
- },
2134
- {
2135
- symbol: 'TSLA',
2136
- quantity: 50,
2137
- averagePrice: 200.0,
2138
- currentPrice: 220.75,
2139
- marketValue: 11037.5,
2140
- unrealizedPnL: 1037.5,
2141
- realizedPnL: 0,
2142
- costBasis: 10000.0,
2143
- currency: 'USD',
2144
- },
2145
- {
2146
- symbol: 'MSFT',
2147
- quantity: 75,
2148
- averagePrice: 300.0,
2149
- currentPrice: 325.25,
2150
- marketValue: 24393.75,
2151
- unrealizedPnL: 1893.75,
2152
- realizedPnL: 0,
2153
- costBasis: 22500.0,
2154
- currency: 'USD',
2155
- },
2156
- ],
2157
- performance: {
2158
- totalReturn: 0.089,
2159
- dailyReturn: 0.002,
2160
- weeklyReturn: 0.015,
2161
- monthlyReturn: 0.045,
2162
- yearlyReturn: 0.089,
2163
- maxDrawdown: -0.05,
2164
- sharpeRatio: 1.2,
2165
- beta: 0.95,
2166
- alpha: 0.02,
2167
- },
2168
- };
2169
- return { data: portfolio };
2170
- }
2171
2141
  async mockGetOrders(filter) {
2172
2142
  await this.simulateDelay();
2173
2143
  const mockOrders = [
@@ -2228,6 +2198,52 @@ class MockDataProvider {
2228
2198
  data: filteredPositions,
2229
2199
  };
2230
2200
  }
2201
+ async mockGetBrokerBalances(filter) {
2202
+ await this.simulateDelay();
2203
+ // Determine how many balances to generate based on limit parameter
2204
+ const limit = filter?.limit || 100;
2205
+ const maxLimit = Math.min(limit, 1000); // Cap at 1000
2206
+ const mockBalances = [];
2207
+ for (let i = 0; i < maxLimit; i++) {
2208
+ const totalCashValue = Math.random() * 100000 + 10000; // $10k - $110k
2209
+ const netLiquidationValue = totalCashValue * (0.8 + Math.random() * 0.4); // ±20% variation
2210
+ const initialMargin = netLiquidationValue * 0.1; // 10% of net liquidation
2211
+ const maintenanceMargin = initialMargin * 0.8; // 80% of initial margin
2212
+ const availableToWithdraw = totalCashValue * 0.9; // 90% of cash available
2213
+ const totalRealizedPnl = (Math.random() - 0.5) * 10000; // -$5k to +$5k
2214
+ const balance = {
2215
+ id: `balance_${i + 1}`,
2216
+ account_id: `account_${Math.floor(Math.random() * 3) + 1}`,
2217
+ total_cash_value: totalCashValue,
2218
+ net_liquidation_value: netLiquidationValue,
2219
+ initial_margin: initialMargin,
2220
+ maintenance_margin: maintenanceMargin,
2221
+ available_to_withdraw: availableToWithdraw,
2222
+ total_realized_pnl: totalRealizedPnl,
2223
+ balance_created_at: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString(),
2224
+ balance_updated_at: new Date().toISOString(),
2225
+ is_end_of_day_snapshot: Math.random() > 0.7, // 30% chance of being EOD snapshot
2226
+ raw_payload: {
2227
+ broker_specific_data: {
2228
+ margin_ratio: netLiquidationValue / initialMargin,
2229
+ day_trading_buying_power: availableToWithdraw * 4,
2230
+ overnight_buying_power: availableToWithdraw * 2,
2231
+ },
2232
+ },
2233
+ created_at: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString(),
2234
+ updated_at: new Date().toISOString(),
2235
+ };
2236
+ mockBalances.push(balance);
2237
+ }
2238
+ // Apply filters if provided
2239
+ let filteredBalances = mockBalances;
2240
+ if (filter) {
2241
+ filteredBalances = this.applyBrokerBalanceFilters(mockBalances, filter);
2242
+ }
2243
+ return {
2244
+ data: filteredBalances,
2245
+ };
2246
+ }
2231
2247
  async mockGetBrokerDataAccounts(filter) {
2232
2248
  await this.simulateDelay();
2233
2249
  // Determine how many accounts to generate based on limit parameter
@@ -2280,8 +2296,8 @@ class MockDataProvider {
2280
2296
  /**
2281
2297
  * Get stored user token
2282
2298
  */
2283
- getUserToken(userId) {
2284
- return this.userTokens.get(userId);
2299
+ getUserToken(sessionId) {
2300
+ return this.userTokens.get(sessionId);
2285
2301
  }
2286
2302
  /**
2287
2303
  * Clear all stored data
@@ -2373,6 +2389,19 @@ class MockDataProvider {
2373
2389
  return true;
2374
2390
  });
2375
2391
  }
2392
+ applyBrokerBalanceFilters(balances, filter) {
2393
+ return balances.filter(balance => {
2394
+ if (filter.account_id && balance.account_id !== filter.account_id)
2395
+ return false;
2396
+ if (filter.is_end_of_day_snapshot !== undefined && balance.is_end_of_day_snapshot !== filter.is_end_of_day_snapshot)
2397
+ return false;
2398
+ if (filter.balance_created_after && balance.balance_created_at && new Date(balance.balance_created_at) < new Date(filter.balance_created_after))
2399
+ return false;
2400
+ if (filter.balance_created_before && balance.balance_created_at && new Date(balance.balance_created_at) > new Date(filter.balance_created_before))
2401
+ return false;
2402
+ return true;
2403
+ });
2404
+ }
2376
2405
  /**
2377
2406
  * Generate mock orders with diverse data
2378
2407
  */
@@ -2784,18 +2813,10 @@ class MockApiClient {
2784
2813
  return this.mockDataProvider.mockCompletePortalSession(sessionId);
2785
2814
  }
2786
2815
  // Portfolio Management
2787
- async getHoldings(filter) {
2788
- await this.getValidAccessToken();
2789
- return this.mockDataProvider.mockGetHoldings();
2790
- }
2791
2816
  async getOrders(filter) {
2792
2817
  await this.getValidAccessToken();
2793
2818
  return this.mockDataProvider.mockGetOrders(filter);
2794
2819
  }
2795
- async getPortfolio() {
2796
- await this.getValidAccessToken();
2797
- return this.mockDataProvider.mockGetPortfolio();
2798
- }
2799
2820
  async placeOrder(order) {
2800
2821
  await this.getValidAccessToken();
2801
2822
  await this.mockDataProvider.mockPlaceOrder(order);
@@ -3006,12 +3027,8 @@ class MockApiClient {
3006
3027
  price,
3007
3028
  }, extras);
3008
3029
  }
3009
- async revokeToken(accessToken) {
3010
- // Clear tokens on revoke
3011
- this.clearTokens();
3012
- }
3013
- async getUserToken(userId) {
3014
- const token = this.mockDataProvider.getUserToken(userId);
3030
+ async getUserToken(sessionId) {
3031
+ const token = this.mockDataProvider.getUserToken(sessionId);
3015
3032
  if (!token) {
3016
3033
  throw new AuthenticationError('User token not found');
3017
3034
  }
@@ -3060,6 +3077,9 @@ class MockApiClient {
3060
3077
  async getBrokerPositionsWithFilter(filter) {
3061
3078
  return this.mockDataProvider.mockGetBrokerPositions(filter);
3062
3079
  }
3080
+ async getBrokerBalancesWithFilter(filter) {
3081
+ return this.mockDataProvider.mockGetBrokerBalances(filter);
3082
+ }
3063
3083
  async getBrokerDataAccountsWithFilter(filter) {
3064
3084
  return this.mockDataProvider.mockGetBrokerDataAccounts(filter);
3065
3085
  }
@@ -3154,6 +3174,36 @@ class MockApiClient {
3154
3174
  limit: perPage,
3155
3175
  }, navigationCallback);
3156
3176
  }
3177
+ async getBrokerBalancesPage(page = 1, perPage = 100, filters) {
3178
+ const mockBalances = await this.mockDataProvider.mockGetBrokerBalances(filters);
3179
+ const balances = mockBalances.data;
3180
+ // Simulate pagination
3181
+ const startIndex = (page - 1) * perPage;
3182
+ const endIndex = startIndex + perPage;
3183
+ const paginatedBalances = balances.slice(startIndex, endIndex);
3184
+ const hasMore = endIndex < balances.length;
3185
+ const nextOffset = hasMore ? endIndex : startIndex;
3186
+ // Create navigation callback for mock pagination
3187
+ const navigationCallback = async (newOffset, newLimit) => {
3188
+ const newStartIndex = newOffset;
3189
+ const newEndIndex = newStartIndex + newLimit;
3190
+ const newPaginatedBalances = balances.slice(newStartIndex, newEndIndex);
3191
+ const newHasMore = newEndIndex < balances.length;
3192
+ const newNextOffset = newHasMore ? newEndIndex : newStartIndex;
3193
+ return new PaginatedResult(newPaginatedBalances, {
3194
+ has_more: newHasMore,
3195
+ next_offset: newNextOffset,
3196
+ current_offset: newStartIndex,
3197
+ limit: newLimit,
3198
+ }, navigationCallback);
3199
+ };
3200
+ return new PaginatedResult(paginatedBalances, {
3201
+ has_more: hasMore,
3202
+ next_offset: nextOffset,
3203
+ current_offset: startIndex,
3204
+ limit: perPage,
3205
+ }, navigationCallback);
3206
+ }
3157
3207
  async getBrokerConnections() {
3158
3208
  await this.getValidAccessToken();
3159
3209
  return this.mockDataProvider.mockGetBrokerConnections();
@@ -3392,9 +3442,135 @@ const darkTheme = {
3392
3442
  },
3393
3443
  },
3394
3444
  },
3445
+ typography: {
3446
+ fontFamily: {
3447
+ primary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3448
+ secondary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3449
+ },
3450
+ fontSize: {
3451
+ xs: '0.75rem',
3452
+ sm: '0.875rem',
3453
+ base: '1rem',
3454
+ lg: '1.125rem',
3455
+ xl: '1.25rem',
3456
+ '2xl': '1.5rem',
3457
+ '3xl': '1.875rem',
3458
+ '4xl': '2.25rem',
3459
+ },
3460
+ fontWeight: {
3461
+ normal: 400,
3462
+ medium: 500,
3463
+ semibold: 600,
3464
+ bold: 700,
3465
+ extrabold: 800,
3466
+ },
3467
+ lineHeight: {
3468
+ tight: '1.25',
3469
+ normal: '1.5',
3470
+ relaxed: '1.75',
3471
+ },
3472
+ },
3473
+ spacing: {
3474
+ xs: '0.25rem',
3475
+ sm: '0.5rem',
3476
+ md: '1rem',
3477
+ lg: '1.5rem',
3478
+ xl: '2rem',
3479
+ '2xl': '3rem',
3480
+ '3xl': '4rem',
3481
+ },
3482
+ layout: {
3483
+ containerMaxWidth: '1440px',
3484
+ gridGap: '1rem',
3485
+ cardPadding: '1.5rem',
3486
+ borderRadius: {
3487
+ sm: '0.25rem',
3488
+ md: '0.5rem',
3489
+ lg: '0.75rem',
3490
+ xl: '1rem',
3491
+ '2xl': '1.5rem',
3492
+ full: '9999px',
3493
+ },
3494
+ },
3495
+ components: {
3496
+ brokerCard: {
3497
+ width: '100%',
3498
+ height: '180px',
3499
+ logoSize: '64px',
3500
+ padding: '1.5rem',
3501
+ },
3502
+ statusIndicator: {
3503
+ size: '22px',
3504
+ glowIntensity: 0.9,
3505
+ },
3506
+ modal: {
3507
+ background: 'rgba(0, 0, 0, 0.95)',
3508
+ backdrop: 'rgba(0, 0, 0, 0.8)',
3509
+ },
3510
+ brokerCardModern: {
3511
+ width: '150px',
3512
+ height: '150px',
3513
+ padding: '16px',
3514
+ logoSize: '48px',
3515
+ statusSize: '22px',
3516
+ },
3517
+ connectButton: {
3518
+ width: '120px',
3519
+ height: '120px',
3520
+ },
3521
+ themeSwitcher: {
3522
+ indicatorSize: '24px',
3523
+ },
3524
+ },
3525
+ effects: {
3526
+ glassmorphism: {
3527
+ enabled: true,
3528
+ blur: '12px',
3529
+ opacity: 0.15,
3530
+ border: 'rgba(0, 255, 255, 0.3)',
3531
+ },
3532
+ animations: {
3533
+ enabled: true,
3534
+ duration: {
3535
+ fast: '150ms',
3536
+ normal: '300ms',
3537
+ slow: '500ms',
3538
+ },
3539
+ easing: {
3540
+ default: 'cubic-bezier(0.4, 0, 0.2, 1)',
3541
+ smooth: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
3542
+ bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
3543
+ },
3544
+ },
3545
+ shadows: {
3546
+ sm: '0 1px 2px rgba(0, 0, 0, 0.5)',
3547
+ md: '0 4px 6px rgba(0, 0, 0, 0.6)',
3548
+ lg: '0 10px 15px rgba(0, 0, 0, 0.7)',
3549
+ xl: '0 20px 25px rgba(0, 0, 0, 0.8)',
3550
+ card: '0px 4px 12px rgba(0, 0, 0, 0.6), 0 0 20px rgba(0, 255, 255, 0.2)',
3551
+ cardHover: '0px 4px 24px rgba(0, 255, 255, 0.3), 0 0 30px rgba(0, 255, 255, 0.25)',
3552
+ glow: '0 0 20px rgba(0, 255, 255, 0.8)',
3553
+ focus: '0 0 0 2px #00FFFF, 0 0 8px 2px rgba(0, 255, 255, 0.8)',
3554
+ },
3555
+ },
3395
3556
  branding: {
3396
3557
  primaryColor: '#00FFFF',
3397
3558
  },
3559
+ glow: {
3560
+ primary: 'rgba(0, 255, 255, 0.4)',
3561
+ secondary: 'rgba(0, 255, 255, 0.6)',
3562
+ card: 'rgba(0, 255, 255, 0.2)',
3563
+ cardHover: 'rgba(0, 255, 255, 0.3)',
3564
+ button: 'rgba(0, 255, 255, 0.6)',
3565
+ focus: 'rgba(0, 255, 255, 0.8)',
3566
+ scrollbar: 'rgba(0, 255, 255, 0.4)',
3567
+ },
3568
+ gradients: {
3569
+ start: 'rgba(0, 255, 255, 0.08)',
3570
+ end: 'rgba(0, 255, 255, 0.03)',
3571
+ hoverStart: 'rgba(0, 255, 255, 0.15)',
3572
+ hoverEnd: 'rgba(0, 255, 255, 0.08)',
3573
+ },
3398
3574
  };
3399
3575
  // Light theme with cyan accents
3400
3576
  const lightTheme = {
@@ -3451,9 +3627,121 @@ const lightTheme = {
3451
3627
  },
3452
3628
  },
3453
3629
  },
3630
+ typography: {
3631
+ fontFamily: {
3632
+ primary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3633
+ secondary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3634
+ },
3635
+ fontSize: {
3636
+ xs: '0.75rem',
3637
+ sm: '0.875rem',
3638
+ base: '1rem',
3639
+ lg: '1.125rem',
3640
+ xl: '1.25rem',
3641
+ '2xl': '1.5rem',
3642
+ '3xl': '1.875rem',
3643
+ '4xl': '2.25rem',
3644
+ },
3645
+ fontWeight: {
3646
+ normal: 400,
3647
+ medium: 500,
3648
+ semibold: 600,
3649
+ bold: 700,
3650
+ extrabold: 800,
3651
+ },
3652
+ lineHeight: {
3653
+ tight: '1.25',
3654
+ normal: '1.5',
3655
+ relaxed: '1.75',
3656
+ },
3657
+ },
3658
+ spacing: {
3659
+ xs: '0.25rem',
3660
+ sm: '0.5rem',
3661
+ md: '1rem',
3662
+ lg: '1.5rem',
3663
+ xl: '2rem',
3664
+ '2xl': '3rem',
3665
+ '3xl': '4rem',
3666
+ },
3667
+ layout: {
3668
+ containerMaxWidth: '1440px',
3669
+ gridGap: '1rem',
3670
+ cardPadding: '1.5rem',
3671
+ borderRadius: {
3672
+ sm: '0.25rem',
3673
+ md: '0.5rem',
3674
+ lg: '0.75rem',
3675
+ xl: '1rem',
3676
+ '2xl': '1.5rem',
3677
+ full: '9999px',
3678
+ },
3679
+ },
3680
+ components: {
3681
+ brokerCard: {
3682
+ width: '100%',
3683
+ height: '180px',
3684
+ logoSize: '64px',
3685
+ padding: '1.5rem',
3686
+ },
3687
+ statusIndicator: {
3688
+ size: '22px',
3689
+ glowIntensity: 0.9,
3690
+ },
3691
+ modal: {
3692
+ background: 'rgba(255, 255, 255, 0.95)',
3693
+ backdrop: 'rgba(0, 0, 0, 0.5)',
3694
+ },
3695
+ },
3696
+ effects: {
3697
+ glassmorphism: {
3698
+ enabled: true,
3699
+ blur: '12px',
3700
+ opacity: 0.15,
3701
+ border: 'rgba(0, 255, 255, 0.3)',
3702
+ },
3703
+ animations: {
3704
+ enabled: true,
3705
+ duration: {
3706
+ fast: '150ms',
3707
+ normal: '300ms',
3708
+ slow: '500ms',
3709
+ },
3710
+ easing: {
3711
+ default: 'cubic-bezier(0.4, 0, 0.2, 1)',
3712
+ smooth: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
3713
+ bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
3714
+ },
3715
+ },
3716
+ shadows: {
3717
+ sm: '0 1px 2px rgba(0, 0, 0, 0.1)',
3718
+ md: '0 4px 6px rgba(0, 0, 0, 0.1)',
3719
+ lg: '0 10px 15px rgba(0, 0, 0, 0.1)',
3720
+ xl: '0 20px 25px rgba(0, 0, 0, 0.1)',
3721
+ card: '0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 20px rgba(0, 255, 255, 0.2)',
3722
+ cardHover: '0px 4px 24px rgba(0, 255, 255, 0.3), 0 0 30px rgba(0, 255, 255, 0.25)',
3723
+ glow: '0 0 20px rgba(0, 255, 255, 0.8)',
3724
+ focus: '0 0 0 2px #00FFFF, 0 0 8px 2px rgba(0, 255, 255, 0.8)',
3725
+ },
3726
+ },
3454
3727
  branding: {
3455
3728
  primaryColor: '#00FFFF',
3456
3729
  },
3730
+ glow: {
3731
+ primary: 'rgba(0, 255, 255, 0.4)',
3732
+ secondary: 'rgba(0, 255, 255, 0.6)',
3733
+ card: 'rgba(0, 255, 255, 0.2)',
3734
+ cardHover: 'rgba(0, 255, 255, 0.3)',
3735
+ button: 'rgba(0, 255, 255, 0.6)',
3736
+ focus: 'rgba(0, 255, 255, 0.8)',
3737
+ scrollbar: 'rgba(0, 255, 255, 0.4)',
3738
+ },
3739
+ gradients: {
3740
+ start: 'rgba(0, 255, 255, 0.08)',
3741
+ end: 'rgba(0, 255, 255, 0.03)',
3742
+ hoverStart: 'rgba(0, 255, 255, 0.15)',
3743
+ hoverEnd: 'rgba(0, 255, 255, 0.08)',
3744
+ },
3457
3745
  };
3458
3746
  // Corporate blue theme
3459
3747
  const corporateBlueTheme = {
@@ -3510,14 +3798,140 @@ const corporateBlueTheme = {
3510
3798
  },
3511
3799
  },
3512
3800
  },
3513
- branding: {
3514
- primaryColor: '#3B82F6',
3801
+ typography: {
3802
+ fontFamily: {
3803
+ primary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3804
+ secondary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3805
+ },
3806
+ fontSize: {
3807
+ xs: '0.75rem',
3808
+ sm: '0.875rem',
3809
+ base: '1rem',
3810
+ lg: '1.125rem',
3811
+ xl: '1.25rem',
3812
+ '2xl': '1.5rem',
3813
+ '3xl': '1.875rem',
3814
+ '4xl': '2.25rem',
3815
+ },
3816
+ fontWeight: {
3817
+ normal: 400,
3818
+ medium: 500,
3819
+ semibold: 600,
3820
+ bold: 700,
3821
+ extrabold: 800,
3822
+ },
3823
+ lineHeight: {
3824
+ tight: '1.25',
3825
+ normal: '1.5',
3826
+ relaxed: '1.75',
3827
+ },
3515
3828
  },
3516
- };
3517
- // Purple theme
3518
- const purpleTheme = {
3519
- mode: 'dark',
3520
- colors: {
3829
+ spacing: {
3830
+ xs: '0.25rem',
3831
+ sm: '0.5rem',
3832
+ md: '1rem',
3833
+ lg: '1.5rem',
3834
+ xl: '2rem',
3835
+ '2xl': '3rem',
3836
+ '3xl': '4rem',
3837
+ },
3838
+ layout: {
3839
+ containerMaxWidth: '1440px',
3840
+ gridGap: '1rem',
3841
+ cardPadding: '1.5rem',
3842
+ borderRadius: {
3843
+ sm: '0.25rem',
3844
+ md: '0.5rem',
3845
+ lg: '0.75rem',
3846
+ xl: '1rem',
3847
+ '2xl': '1.5rem',
3848
+ full: '9999px',
3849
+ },
3850
+ },
3851
+ components: {
3852
+ brokerCard: {
3853
+ width: '100%',
3854
+ height: '180px',
3855
+ logoSize: '64px',
3856
+ padding: '1.5rem',
3857
+ },
3858
+ statusIndicator: {
3859
+ size: '22px',
3860
+ glowIntensity: 0.9,
3861
+ },
3862
+ modal: {
3863
+ background: 'rgba(0, 0, 0, 0.95)',
3864
+ backdrop: 'rgba(0, 0, 0, 0.8)',
3865
+ },
3866
+ brokerCardModern: {
3867
+ width: '150px',
3868
+ height: '150px',
3869
+ padding: '16px',
3870
+ logoSize: '48px',
3871
+ statusSize: '22px',
3872
+ },
3873
+ connectButton: {
3874
+ width: '120px',
3875
+ height: '120px',
3876
+ },
3877
+ themeSwitcher: {
3878
+ indicatorSize: '24px',
3879
+ },
3880
+ },
3881
+ effects: {
3882
+ glassmorphism: {
3883
+ enabled: true,
3884
+ blur: '12px',
3885
+ opacity: 0.15,
3886
+ border: 'rgba(59, 130, 246, 0.3)',
3887
+ },
3888
+ animations: {
3889
+ enabled: true,
3890
+ duration: {
3891
+ fast: '150ms',
3892
+ normal: '300ms',
3893
+ slow: '500ms',
3894
+ },
3895
+ easing: {
3896
+ default: 'cubic-bezier(0.4, 0, 0.2, 1)',
3897
+ smooth: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
3898
+ bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
3899
+ },
3900
+ },
3901
+ shadows: {
3902
+ sm: '0 1px 2px rgba(0, 0, 0, 0.5)',
3903
+ md: '0 4px 6px rgba(0, 0, 0, 0.6)',
3904
+ lg: '0 10px 15px rgba(0, 0, 0, 0.7)',
3905
+ xl: '0 20px 25px rgba(0, 0, 0, 0.8)',
3906
+ card: '0px 4px 12px rgba(0, 0, 0, 0.6), 0 0 20px rgba(59, 130, 246, 0.2)',
3907
+ cardHover: '0px 4px 24px rgba(59, 130, 246, 0.3), 0 0 30px rgba(59, 130, 246, 0.25)',
3908
+ glow: '0 0 20px rgba(59, 130, 246, 0.8)',
3909
+ focus: '0 0 0 2px #3B82F6, 0 0 8px 2px rgba(59, 130, 246, 0.8)',
3910
+ },
3911
+ },
3912
+ branding: {
3913
+ primaryColor: '#3B82F6',
3914
+ },
3915
+ glow: {
3916
+ primary: 'rgba(59, 130, 246, 0.4)',
3917
+ secondary: 'rgba(59, 130, 246, 0.6)',
3918
+ card: 'rgba(59, 130, 246, 0.2)',
3919
+ cardHover: 'rgba(59, 130, 246, 0.3)',
3920
+ button: 'rgba(59, 130, 246, 0.6)',
3921
+ focus: 'rgba(59, 130, 246, 0.8)',
3922
+ scrollbar: 'rgba(59, 130, 246, 0.4)',
3923
+ },
3924
+ gradients: {
3925
+ start: 'rgba(59, 130, 246, 0.08)',
3926
+ end: 'rgba(59, 130, 246, 0.03)',
3927
+ hoverStart: 'rgba(59, 130, 246, 0.15)',
3928
+ hoverEnd: 'rgba(59, 130, 246, 0.08)',
3929
+ },
3930
+ };
3931
+ // Purple theme
3932
+ const purpleTheme = {
3933
+ mode: 'dark',
3934
+ colors: {
3521
3935
  background: {
3522
3936
  primary: '#1a1a1a',
3523
3937
  secondary: '#2a2a2a',
@@ -3569,9 +3983,135 @@ const purpleTheme = {
3569
3983
  },
3570
3984
  },
3571
3985
  },
3986
+ typography: {
3987
+ fontFamily: {
3988
+ primary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3989
+ secondary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
3990
+ },
3991
+ fontSize: {
3992
+ xs: '0.75rem',
3993
+ sm: '0.875rem',
3994
+ base: '1rem',
3995
+ lg: '1.125rem',
3996
+ xl: '1.25rem',
3997
+ '2xl': '1.5rem',
3998
+ '3xl': '1.875rem',
3999
+ '4xl': '2.25rem',
4000
+ },
4001
+ fontWeight: {
4002
+ normal: 400,
4003
+ medium: 500,
4004
+ semibold: 600,
4005
+ bold: 700,
4006
+ extrabold: 800,
4007
+ },
4008
+ lineHeight: {
4009
+ tight: '1.25',
4010
+ normal: '1.5',
4011
+ relaxed: '1.75',
4012
+ },
4013
+ },
4014
+ spacing: {
4015
+ xs: '0.25rem',
4016
+ sm: '0.5rem',
4017
+ md: '1rem',
4018
+ lg: '1.5rem',
4019
+ xl: '2rem',
4020
+ '2xl': '3rem',
4021
+ '3xl': '4rem',
4022
+ },
4023
+ layout: {
4024
+ containerMaxWidth: '1440px',
4025
+ gridGap: '1rem',
4026
+ cardPadding: '1.5rem',
4027
+ borderRadius: {
4028
+ sm: '0.25rem',
4029
+ md: '0.5rem',
4030
+ lg: '0.75rem',
4031
+ xl: '1rem',
4032
+ '2xl': '1.5rem',
4033
+ full: '9999px',
4034
+ },
4035
+ },
4036
+ components: {
4037
+ brokerCard: {
4038
+ width: '100%',
4039
+ height: '180px',
4040
+ logoSize: '64px',
4041
+ padding: '1.5rem',
4042
+ },
4043
+ statusIndicator: {
4044
+ size: '22px',
4045
+ glowIntensity: 0.9,
4046
+ },
4047
+ modal: {
4048
+ background: 'rgba(0, 0, 0, 0.95)',
4049
+ backdrop: 'rgba(0, 0, 0, 0.8)',
4050
+ },
4051
+ brokerCardModern: {
4052
+ width: '150px',
4053
+ height: '150px',
4054
+ padding: '16px',
4055
+ logoSize: '48px',
4056
+ statusSize: '22px',
4057
+ },
4058
+ connectButton: {
4059
+ width: '120px',
4060
+ height: '120px',
4061
+ },
4062
+ themeSwitcher: {
4063
+ indicatorSize: '24px',
4064
+ },
4065
+ },
4066
+ effects: {
4067
+ glassmorphism: {
4068
+ enabled: true,
4069
+ blur: '12px',
4070
+ opacity: 0.15,
4071
+ border: 'rgba(168, 85, 247, 0.3)',
4072
+ },
4073
+ animations: {
4074
+ enabled: true,
4075
+ duration: {
4076
+ fast: '150ms',
4077
+ normal: '300ms',
4078
+ slow: '500ms',
4079
+ },
4080
+ easing: {
4081
+ default: 'cubic-bezier(0.4, 0, 0.2, 1)',
4082
+ smooth: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
4083
+ bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
4084
+ },
4085
+ },
4086
+ shadows: {
4087
+ sm: '0 1px 2px rgba(0, 0, 0, 0.5)',
4088
+ md: '0 4px 6px rgba(0, 0, 0, 0.6)',
4089
+ lg: '0 10px 15px rgba(0, 0, 0, 0.7)',
4090
+ xl: '0 20px 25px rgba(0, 0, 0, 0.8)',
4091
+ card: '0px 4px 12px rgba(0, 0, 0, 0.6), 0 0 20px rgba(168, 85, 247, 0.2)',
4092
+ cardHover: '0px 4px 24px rgba(168, 85, 247, 0.3), 0 0 30px rgba(168, 85, 247, 0.25)',
4093
+ glow: '0 0 20px rgba(168, 85, 247, 0.8)',
4094
+ focus: '0 0 0 2px #A855F7, 0 0 8px 2px rgba(168, 85, 247, 0.8)',
4095
+ },
4096
+ },
3572
4097
  branding: {
3573
4098
  primaryColor: '#A855F7',
3574
4099
  },
4100
+ glow: {
4101
+ primary: 'rgba(168, 85, 247, 0.4)',
4102
+ secondary: 'rgba(168, 85, 247, 0.6)',
4103
+ card: 'rgba(168, 85, 247, 0.2)',
4104
+ cardHover: 'rgba(168, 85, 247, 0.3)',
4105
+ button: 'rgba(168, 85, 247, 0.6)',
4106
+ focus: 'rgba(168, 85, 247, 0.8)',
4107
+ scrollbar: 'rgba(168, 85, 247, 0.4)',
4108
+ },
4109
+ gradients: {
4110
+ start: 'rgba(168, 85, 247, 0.08)',
4111
+ end: 'rgba(168, 85, 247, 0.03)',
4112
+ hoverStart: 'rgba(168, 85, 247, 0.15)',
4113
+ hoverEnd: 'rgba(168, 85, 247, 0.08)',
4114
+ },
3575
4115
  };
3576
4116
  // Green theme
3577
4117
  const greenTheme = {
@@ -3628,9 +4168,135 @@ const greenTheme = {
3628
4168
  },
3629
4169
  },
3630
4170
  },
4171
+ typography: {
4172
+ fontFamily: {
4173
+ primary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
4174
+ secondary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
4175
+ },
4176
+ fontSize: {
4177
+ xs: '0.75rem',
4178
+ sm: '0.875rem',
4179
+ base: '1rem',
4180
+ lg: '1.125rem',
4181
+ xl: '1.25rem',
4182
+ '2xl': '1.5rem',
4183
+ '3xl': '1.875rem',
4184
+ '4xl': '2.25rem',
4185
+ },
4186
+ fontWeight: {
4187
+ normal: 400,
4188
+ medium: 500,
4189
+ semibold: 600,
4190
+ bold: 700,
4191
+ extrabold: 800,
4192
+ },
4193
+ lineHeight: {
4194
+ tight: '1.25',
4195
+ normal: '1.5',
4196
+ relaxed: '1.75',
4197
+ },
4198
+ },
4199
+ spacing: {
4200
+ xs: '0.25rem',
4201
+ sm: '0.5rem',
4202
+ md: '1rem',
4203
+ lg: '1.5rem',
4204
+ xl: '2rem',
4205
+ '2xl': '3rem',
4206
+ '3xl': '4rem',
4207
+ },
4208
+ layout: {
4209
+ containerMaxWidth: '1440px',
4210
+ gridGap: '1rem',
4211
+ cardPadding: '1.5rem',
4212
+ borderRadius: {
4213
+ sm: '0.25rem',
4214
+ md: '0.5rem',
4215
+ lg: '0.75rem',
4216
+ xl: '1rem',
4217
+ '2xl': '1.5rem',
4218
+ full: '9999px',
4219
+ },
4220
+ },
4221
+ components: {
4222
+ brokerCard: {
4223
+ width: '100%',
4224
+ height: '180px',
4225
+ logoSize: '64px',
4226
+ padding: '1.5rem',
4227
+ },
4228
+ statusIndicator: {
4229
+ size: '22px',
4230
+ glowIntensity: 0.9,
4231
+ },
4232
+ modal: {
4233
+ background: 'rgba(0, 0, 0, 0.95)',
4234
+ backdrop: 'rgba(0, 0, 0, 0.8)',
4235
+ },
4236
+ brokerCardModern: {
4237
+ width: '150px',
4238
+ height: '150px',
4239
+ padding: '16px',
4240
+ logoSize: '48px',
4241
+ statusSize: '22px',
4242
+ },
4243
+ connectButton: {
4244
+ width: '120px',
4245
+ height: '120px',
4246
+ },
4247
+ themeSwitcher: {
4248
+ indicatorSize: '24px',
4249
+ },
4250
+ },
4251
+ effects: {
4252
+ glassmorphism: {
4253
+ enabled: true,
4254
+ blur: '12px',
4255
+ opacity: 0.15,
4256
+ border: 'rgba(34, 197, 94, 0.3)',
4257
+ },
4258
+ animations: {
4259
+ enabled: true,
4260
+ duration: {
4261
+ fast: '150ms',
4262
+ normal: '300ms',
4263
+ slow: '500ms',
4264
+ },
4265
+ easing: {
4266
+ default: 'cubic-bezier(0.4, 0, 0.2, 1)',
4267
+ smooth: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
4268
+ bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
4269
+ },
4270
+ },
4271
+ shadows: {
4272
+ sm: '0 1px 2px rgba(0, 0, 0, 0.5)',
4273
+ md: '0 4px 6px rgba(0, 0, 0, 0.6)',
4274
+ lg: '0 10px 15px rgba(0, 0, 0, 0.7)',
4275
+ xl: '0 20px 25px rgba(0, 0, 0, 0.8)',
4276
+ card: '0px 4px 12px rgba(0, 0, 0, 0.6), 0 0 20px rgba(34, 197, 94, 0.2)',
4277
+ cardHover: '0px 4px 24px rgba(34, 197, 94, 0.3), 0 0 30px rgba(34, 197, 94, 0.25)',
4278
+ glow: '0 0 20px rgba(34, 197, 94, 0.8)',
4279
+ focus: '0 0 0 2px #22C55E, 0 0 8px 2px rgba(34, 197, 94, 0.8)',
4280
+ },
4281
+ },
3631
4282
  branding: {
3632
4283
  primaryColor: '#22C55E',
3633
4284
  },
4285
+ glow: {
4286
+ primary: 'rgba(34, 197, 94, 0.4)',
4287
+ secondary: 'rgba(34, 197, 94, 0.6)',
4288
+ card: 'rgba(34, 197, 94, 0.2)',
4289
+ cardHover: 'rgba(34, 197, 94, 0.3)',
4290
+ button: 'rgba(34, 197, 94, 0.6)',
4291
+ focus: 'rgba(34, 197, 94, 0.8)',
4292
+ scrollbar: 'rgba(34, 197, 94, 0.4)',
4293
+ },
4294
+ gradients: {
4295
+ start: 'rgba(34, 197, 94, 0.08)',
4296
+ end: 'rgba(34, 197, 94, 0.03)',
4297
+ hoverStart: 'rgba(34, 197, 94, 0.15)',
4298
+ hoverEnd: 'rgba(34, 197, 94, 0.08)',
4299
+ },
3634
4300
  };
3635
4301
  // Orange theme
3636
4302
  const orangeTheme = {
@@ -3687,9 +4353,135 @@ const orangeTheme = {
3687
4353
  },
3688
4354
  },
3689
4355
  },
4356
+ typography: {
4357
+ fontFamily: {
4358
+ primary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
4359
+ secondary: 'Orbitron, Futura, Inter, system-ui, sans-serif',
4360
+ },
4361
+ fontSize: {
4362
+ xs: '0.75rem',
4363
+ sm: '0.875rem',
4364
+ base: '1rem',
4365
+ lg: '1.125rem',
4366
+ xl: '1.25rem',
4367
+ '2xl': '1.5rem',
4368
+ '3xl': '1.875rem',
4369
+ '4xl': '2.25rem',
4370
+ },
4371
+ fontWeight: {
4372
+ normal: 400,
4373
+ medium: 500,
4374
+ semibold: 600,
4375
+ bold: 700,
4376
+ extrabold: 800,
4377
+ },
4378
+ lineHeight: {
4379
+ tight: '1.25',
4380
+ normal: '1.5',
4381
+ relaxed: '1.75',
4382
+ },
4383
+ },
4384
+ spacing: {
4385
+ xs: '0.25rem',
4386
+ sm: '0.5rem',
4387
+ md: '1rem',
4388
+ lg: '1.5rem',
4389
+ xl: '2rem',
4390
+ '2xl': '3rem',
4391
+ '3xl': '4rem',
4392
+ },
4393
+ layout: {
4394
+ containerMaxWidth: '1440px',
4395
+ gridGap: '1rem',
4396
+ cardPadding: '1.5rem',
4397
+ borderRadius: {
4398
+ sm: '0.25rem',
4399
+ md: '0.5rem',
4400
+ lg: '0.75rem',
4401
+ xl: '1rem',
4402
+ '2xl': '1.5rem',
4403
+ full: '9999px',
4404
+ },
4405
+ },
4406
+ components: {
4407
+ brokerCard: {
4408
+ width: '100%',
4409
+ height: '180px',
4410
+ logoSize: '64px',
4411
+ padding: '1.5rem',
4412
+ },
4413
+ statusIndicator: {
4414
+ size: '22px',
4415
+ glowIntensity: 0.9,
4416
+ },
4417
+ modal: {
4418
+ background: 'rgba(0, 0, 0, 0.95)',
4419
+ backdrop: 'rgba(0, 0, 0, 0.8)',
4420
+ },
4421
+ brokerCardModern: {
4422
+ width: '150px',
4423
+ height: '150px',
4424
+ padding: '16px',
4425
+ logoSize: '48px',
4426
+ statusSize: '22px',
4427
+ },
4428
+ connectButton: {
4429
+ width: '120px',
4430
+ height: '120px',
4431
+ },
4432
+ themeSwitcher: {
4433
+ indicatorSize: '24px',
4434
+ },
4435
+ },
4436
+ effects: {
4437
+ glassmorphism: {
4438
+ enabled: true,
4439
+ blur: '12px',
4440
+ opacity: 0.15,
4441
+ border: 'rgba(249, 115, 22, 0.3)',
4442
+ },
4443
+ animations: {
4444
+ enabled: true,
4445
+ duration: {
4446
+ fast: '150ms',
4447
+ normal: '300ms',
4448
+ slow: '500ms',
4449
+ },
4450
+ easing: {
4451
+ default: 'cubic-bezier(0.4, 0, 0.2, 1)',
4452
+ smooth: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
4453
+ bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
4454
+ },
4455
+ },
4456
+ shadows: {
4457
+ sm: '0 1px 2px rgba(0, 0, 0, 0.5)',
4458
+ md: '0 4px 6px rgba(0, 0, 0, 0.6)',
4459
+ lg: '0 10px 15px rgba(0, 0, 0, 0.7)',
4460
+ xl: '0 20px 25px rgba(0, 0, 0, 0.8)',
4461
+ card: '0px 4px 12px rgba(0, 0, 0, 0.6), 0 0 20px rgba(249, 115, 22, 0.2)',
4462
+ cardHover: '0px 4px 24px rgba(249, 115, 22, 0.3), 0 0 30px rgba(249, 115, 22, 0.25)',
4463
+ glow: '0 0 20px rgba(249, 115, 22, 0.8)',
4464
+ focus: '0 0 0 2px #F97316, 0 0 8px 2px rgba(249, 115, 22, 0.8)',
4465
+ },
4466
+ },
3690
4467
  branding: {
3691
4468
  primaryColor: '#F97316',
3692
4469
  },
4470
+ glow: {
4471
+ primary: 'rgba(249, 115, 22, 0.4)',
4472
+ secondary: 'rgba(249, 115, 22, 0.6)',
4473
+ card: 'rgba(249, 115, 22, 0.2)',
4474
+ cardHover: 'rgba(249, 115, 22, 0.3)',
4475
+ button: 'rgba(249, 115, 22, 0.6)',
4476
+ focus: 'rgba(249, 115, 22, 0.8)',
4477
+ scrollbar: 'rgba(249, 115, 22, 0.4)',
4478
+ },
4479
+ gradients: {
4480
+ start: 'rgba(249, 115, 22, 0.08)',
4481
+ end: 'rgba(249, 115, 22, 0.03)',
4482
+ hoverStart: 'rgba(249, 115, 22, 0.15)',
4483
+ hoverEnd: 'rgba(249, 115, 22, 0.08)',
4484
+ },
3693
4485
  };
3694
4486
  // Theme preset mapping
3695
4487
  const portalThemePresets = {
@@ -3774,17 +4566,36 @@ function getThemePreset(preset) {
3774
4566
  */
3775
4567
  function validateCustomTheme(theme) {
3776
4568
  try {
3777
- // Check required properties
3778
- if (!theme.mode || !['dark', 'light', 'auto'].includes(theme.mode)) {
4569
+ // Only validate what's provided - everything else gets defaults
4570
+ if (theme.mode && !['dark', 'light', 'auto'].includes(theme.mode)) {
3779
4571
  return false;
3780
4572
  }
3781
- if (!theme.colors) {
3782
- return false;
4573
+ // If colors are provided, validate the structure
4574
+ if (theme.colors) {
4575
+ // Check that any provided color sections have valid structure
4576
+ const colorSections = ['background', 'status', 'text', 'border', 'input', 'button'];
4577
+ for (const section of colorSections) {
4578
+ const colorSection = theme.colors[section];
4579
+ if (colorSection && typeof colorSection !== 'object') {
4580
+ return false;
4581
+ }
4582
+ }
4583
+ }
4584
+ // If typography is provided, validate structure
4585
+ if (theme.typography) {
4586
+ if (theme.typography.fontSize && typeof theme.typography.fontSize !== 'object') {
4587
+ return false;
4588
+ }
4589
+ if (theme.typography.fontWeight && typeof theme.typography.fontWeight !== 'object') {
4590
+ return false;
4591
+ }
3783
4592
  }
3784
- // Check required color sections
3785
- const requiredSections = ['background', 'status', 'text', 'border', 'input', 'button'];
3786
- for (const section of requiredSections) {
3787
- if (!theme.colors[section]) {
4593
+ // If effects are provided, validate structure
4594
+ if (theme.effects) {
4595
+ if (theme.effects.animations && typeof theme.effects.animations !== 'object') {
4596
+ return false;
4597
+ }
4598
+ if (theme.effects.shadows && typeof theme.effects.shadows !== 'object') {
3788
4599
  return false;
3789
4600
  }
3790
4601
  }
@@ -3816,12 +4627,14 @@ function createCustomThemeFromPreset(preset, modifications) {
3816
4627
  /**
3817
4628
  * Broker filtering utility functions
3818
4629
  */
3819
- // Supported broker names and their corresponding IDs
4630
+ // Supported broker names and their corresponding IDs (including aliases)
3820
4631
  const SUPPORTED_BROKERS = {
3821
4632
  'alpaca': 'alpaca',
3822
4633
  'robinhood': 'robinhood',
3823
4634
  'tasty_trade': 'tasty_trade',
3824
- 'ninja_trader': 'ninja_trader'
4635
+ 'ninja_trader': 'ninja_trader',
4636
+ 'tradovate': 'tradovate', // Alias for ninja_trader
4637
+ 'interactive_brokers': 'interactive_brokers',
3825
4638
  };
3826
4639
  /**
3827
4640
  * Convert broker names to broker IDs, filtering out unsupported ones
@@ -3961,6 +4774,13 @@ class FinaticConnect extends EventEmitter {
3961
4774
  this.userToken?.refreshToken &&
3962
4775
  this.userToken?.user_id);
3963
4776
  }
4777
+ /**
4778
+ * Check if the client is authenticated (alias for isAuthed for consistency)
4779
+ * @returns True if authenticated, false otherwise
4780
+ */
4781
+ is_authenticated() {
4782
+ return this.isAuthed();
4783
+ }
3964
4784
  /**
3965
4785
  * Get user's orders with pagination and optional filtering
3966
4786
  * @param params - Query parameters including page, perPage, and filters
@@ -4004,20 +4824,18 @@ class FinaticConnect extends EventEmitter {
4004
4824
  return this.getAccountsPage(page, perPage, filter);
4005
4825
  }
4006
4826
  /**
4007
- * Revoke the current user's access
4827
+ * Get user's balances with pagination and optional filtering
4828
+ * @param params - Query parameters including page, perPage, and filters
4829
+ * @returns Promise with paginated result that supports navigation
4008
4830
  */
4009
- async revokeToken() {
4010
- if (!this.userToken) {
4011
- return;
4012
- }
4013
- try {
4014
- await this.apiClient.revokeToken(this.userToken.accessToken);
4015
- this.userToken = null;
4016
- }
4017
- catch (error) {
4018
- this.emit('error', error);
4019
- throw error;
4831
+ async getBalances(params) {
4832
+ if (!this.isAuthed()) {
4833
+ throw new AuthenticationError('User is not authenticated');
4020
4834
  }
4835
+ const page = params?.page || 1;
4836
+ const perPage = params?.perPage || 100;
4837
+ const filter = params?.filter;
4838
+ return this.getBalancesPage(page, perPage, filter);
4021
4839
  }
4022
4840
  /**
4023
4841
  * Initialize the Finatic Connect SDK
@@ -4106,9 +4924,33 @@ class FinaticConnect extends EventEmitter {
4106
4924
  async setUserId(userId) {
4107
4925
  await this.initializeWithUser(userId);
4108
4926
  }
4927
+ /**
4928
+ * Get the user and tokens for a completed session
4929
+ * @returns Promise with user information and tokens
4930
+ */
4931
+ async getSessionUser() {
4932
+ if (!this.isAuthed()) {
4933
+ throw new AuthenticationError('User is not authenticated');
4934
+ }
4935
+ if (!this.userToken) {
4936
+ throw new AuthenticationError('No user token available');
4937
+ }
4938
+ return {
4939
+ user_id: this.userToken.userId,
4940
+ access_token: this.userToken.accessToken,
4941
+ refresh_token: this.userToken.refreshToken,
4942
+ expires_in: this.userToken.expiresIn,
4943
+ token_type: this.userToken.tokenType,
4944
+ scope: this.userToken.scope,
4945
+ company_id: this.companyId
4946
+ };
4947
+ }
4109
4948
  async initializeWithUser(userId) {
4110
4949
  try {
4111
- this.userToken = await this.apiClient.getUserToken(userId);
4950
+ if (!this.sessionId) {
4951
+ throw new SessionError('Session not initialized');
4952
+ }
4953
+ this.userToken = await this.apiClient.getUserToken(this.sessionId);
4112
4954
  // Set tokens in ApiClient for automatic token management
4113
4955
  if (this.userToken) {
4114
4956
  const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
@@ -4152,20 +4994,8 @@ class FinaticConnect extends EventEmitter {
4152
4994
  this.apiClient.setSessionContext(this.sessionId, this.companyId, startResponse.data.csrf_token // If available in response
4153
4995
  );
4154
4996
  }
4155
- // Wait for session to become active
4156
- const maxAttempts = 5;
4157
- let attempts = 0;
4158
- while (attempts < maxAttempts) {
4159
- const sessionResponse = await this.apiClient.validatePortalSession(this.sessionId, '');
4160
- if (sessionResponse.status === 'active') {
4161
- break;
4162
- }
4163
- await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second between attempts
4164
- attempts++;
4165
- }
4166
- if (attempts === maxAttempts) {
4167
- throw new SessionError('Session failed to become active');
4168
- }
4997
+ // Session is now active
4998
+ this.currentSessionState = SessionState.ACTIVE;
4169
4999
  }
4170
5000
  // Get portal URL
4171
5001
  const portalResponse = await this.apiClient.getPortalUrl(this.sessionId);
@@ -4176,6 +5006,12 @@ class FinaticConnect extends EventEmitter {
4176
5006
  let themedPortalUrl = appendThemeToURL(portalResponse.data.portal_url, options?.theme);
4177
5007
  // Apply broker filter to portal URL if provided
4178
5008
  themedPortalUrl = appendBrokerFilterToURL(themedPortalUrl, options?.brokers);
5009
+ // Apply email parameter to portal URL if provided
5010
+ if (options?.email) {
5011
+ const url = new URL(themedPortalUrl);
5012
+ url.searchParams.set('email', options.email);
5013
+ themedPortalUrl = url.toString();
5014
+ }
4179
5015
  // Create portal UI if not exists
4180
5016
  if (!this.portalUI) {
4181
5017
  this.portalUI = new PortalUI(this.baseUrl);
@@ -4271,21 +5107,8 @@ class FinaticConnect extends EventEmitter {
4271
5107
  }
4272
5108
  // For non-direct auth, we need to wait for the session to be ACTIVE
4273
5109
  if (response.data.state === SessionState.PENDING) {
4274
- // Wait for session to become active
4275
- const maxAttempts = 5;
4276
- let attempts = 0;
4277
- while (attempts < maxAttempts) {
4278
- const sessionResponse = await this.apiClient.validatePortalSession(this.sessionId, '');
4279
- if (sessionResponse.status === 'active') {
4280
- this.currentSessionState = SessionState.ACTIVE;
4281
- break;
4282
- }
4283
- await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second between attempts
4284
- attempts++;
4285
- }
4286
- if (attempts === maxAttempts) {
4287
- throw new SessionError('Session failed to become active');
4288
- }
5110
+ // Session is now active
5111
+ this.currentSessionState = SessionState.ACTIVE;
4289
5112
  }
4290
5113
  }
4291
5114
  catch (error) {
@@ -4443,6 +5266,138 @@ class FinaticConnect extends EventEmitter {
4443
5266
  throw error;
4444
5267
  }
4445
5268
  }
5269
+ /**
5270
+ * Place a stock stop order (convenience method)
5271
+ */
5272
+ async placeStockStopOrder(symbol, quantity, side, stopPrice, timeInForce = 'gtc', broker, accountNumber) {
5273
+ if (!this.userToken) {
5274
+ throw new Error('Not initialized with user');
5275
+ }
5276
+ try {
5277
+ return await this.apiClient.placeStockStopOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', stopPrice, timeInForce, broker, accountNumber);
5278
+ }
5279
+ catch (error) {
5280
+ this.emit('error', error);
5281
+ throw error;
5282
+ }
5283
+ }
5284
+ /**
5285
+ * Place a crypto market order (convenience method)
5286
+ */
5287
+ async placeCryptoMarketOrder(symbol, quantity, side, broker, accountNumber) {
5288
+ if (!this.userToken) {
5289
+ throw new Error('Not initialized with user');
5290
+ }
5291
+ try {
5292
+ return await this.apiClient.placeCryptoMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
5293
+ }
5294
+ catch (error) {
5295
+ this.emit('error', error);
5296
+ throw error;
5297
+ }
5298
+ }
5299
+ /**
5300
+ * Place a crypto limit order (convenience method)
5301
+ */
5302
+ async placeCryptoLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5303
+ if (!this.userToken) {
5304
+ throw new Error('Not initialized with user');
5305
+ }
5306
+ try {
5307
+ return await this.apiClient.placeCryptoLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
5308
+ }
5309
+ catch (error) {
5310
+ this.emit('error', error);
5311
+ throw error;
5312
+ }
5313
+ }
5314
+ /**
5315
+ * Place an options market order (convenience method)
5316
+ */
5317
+ async placeOptionsMarketOrder(symbol, quantity, side, broker, accountNumber) {
5318
+ if (!this.userToken) {
5319
+ throw new Error('Not initialized with user');
5320
+ }
5321
+ try {
5322
+ return await this.apiClient.placeOptionsMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
5323
+ }
5324
+ catch (error) {
5325
+ this.emit('error', error);
5326
+ throw error;
5327
+ }
5328
+ }
5329
+ /**
5330
+ * Place an options limit order (convenience method)
5331
+ */
5332
+ async placeOptionsLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5333
+ if (!this.userToken) {
5334
+ throw new Error('Not initialized with user');
5335
+ }
5336
+ try {
5337
+ return await this.apiClient.placeOptionsLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
5338
+ }
5339
+ catch (error) {
5340
+ this.emit('error', error);
5341
+ throw error;
5342
+ }
5343
+ }
5344
+ /**
5345
+ * Place a futures market order (convenience method)
5346
+ */
5347
+ async placeFuturesMarketOrder(symbol, quantity, side, broker, accountNumber) {
5348
+ if (!this.userToken) {
5349
+ throw new Error('Not initialized with user');
5350
+ }
5351
+ try {
5352
+ return await this.apiClient.placeFuturesMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
5353
+ }
5354
+ catch (error) {
5355
+ this.emit('error', error);
5356
+ throw error;
5357
+ }
5358
+ }
5359
+ /**
5360
+ * Place a futures limit order (convenience method)
5361
+ */
5362
+ async placeFuturesLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5363
+ if (!this.userToken) {
5364
+ throw new Error('Not initialized with user');
5365
+ }
5366
+ try {
5367
+ return await this.apiClient.placeFuturesLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
5368
+ }
5369
+ catch (error) {
5370
+ this.emit('error', error);
5371
+ throw error;
5372
+ }
5373
+ }
5374
+ /**
5375
+ * Set the broker context for trading operations
5376
+ * @param broker - The broker to set as context
5377
+ */
5378
+ setBroker(broker) {
5379
+ this.apiClient.setBroker(broker);
5380
+ }
5381
+ /**
5382
+ * Set the account context for trading operations
5383
+ * @param accountNumber - The account number to set as context
5384
+ */
5385
+ setAccount(accountNumber) {
5386
+ this.apiClient.setAccount(accountNumber);
5387
+ }
5388
+ /**
5389
+ * Get the current trading context
5390
+ * @returns Object with current broker and account context
5391
+ */
5392
+ getTradingContext() {
5393
+ return this.apiClient.getTradingContext();
5394
+ }
5395
+ /**
5396
+ * Clear the trading context
5397
+ */
5398
+ clearTradingContext() {
5399
+ this.apiClient.clearTradingContext();
5400
+ }
4446
5401
  /**
4447
5402
  * Get the current user ID
4448
5403
  * @returns The current user ID or undefined if not authenticated
@@ -4592,6 +5547,12 @@ class FinaticConnect extends EventEmitter {
4592
5547
  }
4593
5548
  return this.apiClient.getBrokerAccountsPage(page, perPage, filter);
4594
5549
  }
5550
+ async getBalancesPage(page = 1, perPage = 100, filter) {
5551
+ if (!this.isAuthed()) {
5552
+ throw new AuthenticationError('User is not authenticated');
5553
+ }
5554
+ return this.apiClient.getBrokerBalancesPage(page, perPage, filter);
5555
+ }
4595
5556
  /**
4596
5557
  * Get the next page of orders
4597
5558
  * @param previousResult - The previous paginated result
@@ -4634,6 +5595,15 @@ class FinaticConnect extends EventEmitter {
4634
5595
  return this.apiClient.getBrokerAccountsPage(page, limit);
4635
5596
  });
4636
5597
  }
5598
+ async getNextBalancesPage(previousResult) {
5599
+ if (!this.isAuthed()) {
5600
+ throw new AuthenticationError('User is not authenticated');
5601
+ }
5602
+ return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5603
+ const page = Math.floor(offset / limit) + 1;
5604
+ return this.apiClient.getBrokerBalancesPage(page, limit);
5605
+ });
5606
+ }
4637
5607
  /**
4638
5608
  * Get all orders across all pages (convenience method)
4639
5609
  * @param filter - Optional filter parameters
@@ -4700,6 +5670,23 @@ class FinaticConnect extends EventEmitter {
4700
5670
  }
4701
5671
  return allData;
4702
5672
  }
5673
+ async getAllBalances(filter) {
5674
+ if (!this.isAuthed()) {
5675
+ throw new AuthenticationError('User is not authenticated');
5676
+ }
5677
+ const allData = [];
5678
+ let currentResult = await this.getBalancesPage(1, 100, filter);
5679
+ while (currentResult) {
5680
+ allData.push(...currentResult.data);
5681
+ if (!currentResult.hasNext)
5682
+ break;
5683
+ const nextResult = await this.getNextBalancesPage(currentResult);
5684
+ if (!nextResult)
5685
+ break;
5686
+ currentResult = nextResult;
5687
+ }
5688
+ return allData;
5689
+ }
4703
5690
  /**
4704
5691
  * Register session management (but don't auto-cleanup for 24-hour sessions)
4705
5692
  */
@@ -4749,20 +5736,9 @@ class FinaticConnect extends EventEmitter {
4749
5736
  await this.refreshSessionAutomatically();
4750
5737
  return;
4751
5738
  }
4752
- // Use a timeout to prevent hanging requests
4753
- const timeoutPromise = new Promise((_, reject) => {
4754
- setTimeout(() => reject(new Error('Session validation timeout')), this.SESSION_VALIDATION_TIMEOUT);
4755
- });
4756
- const validationPromise = this.apiClient.validatePortalSession(this.sessionId, '');
4757
- const response = await Promise.race([validationPromise, timeoutPromise]);
4758
- if (response && response.status === 'active') {
4759
- console.log('[FinaticConnect] Session keep-alive successful');
4760
- this.currentSessionState = 'active';
4761
- }
4762
- else {
4763
- console.warn('[FinaticConnect] Session keep-alive failed - session not active');
4764
- this.currentSessionState = response?.status || 'unknown';
4765
- }
5739
+ // Session keep-alive - assume session is active if we have a session ID
5740
+ console.log('[FinaticConnect] Session keep-alive successful');
5741
+ this.currentSessionState = 'active';
4766
5742
  }
4767
5743
  catch (error) {
4768
5744
  console.warn('[FinaticConnect] Session keep-alive error:', error);
@@ -4891,6 +5867,24 @@ class FinaticConnect extends EventEmitter {
4891
5867
  }
4892
5868
  return this.apiClient.disconnectCompany(connectionId);
4893
5869
  }
5870
+ /**
5871
+ * Get account balances for the authenticated user
5872
+ * @param filters - Optional filters for balances
5873
+ * @returns Promise with balance data
5874
+ */
5875
+ async getBalances(filters) {
5876
+ if (!this.isAuthed()) {
5877
+ throw new AuthenticationError('User is not authenticated');
5878
+ }
5879
+ try {
5880
+ const response = await this.apiClient.getBalances(filters);
5881
+ return response.response_data || [];
5882
+ }
5883
+ catch (error) {
5884
+ this.emit('error', error);
5885
+ throw error;
5886
+ }
5887
+ }
4894
5888
  }
4895
5889
  FinaticConnect.instance = null;
4896
5890
 
@@ -4911,6 +5905,7 @@ exports.RateLimitError = RateLimitError;
4911
5905
  exports.SecurityError = SecurityError;
4912
5906
  exports.SessionError = SessionError;
4913
5907
  exports.TokenError = TokenError;
5908
+ exports.TradingNotEnabledError = TradingNotEnabledError;
4914
5909
  exports.ValidationError = ValidationError;
4915
5910
  exports.appendThemeToURL = appendThemeToURL;
4916
5911
  exports.createCustomThemeFromPreset = createCustomThemeFromPreset;