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