hedgequantx 1.8.34 → 1.8.35
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/package.json
CHANGED
|
@@ -180,7 +180,7 @@ const getContractsFromAPI = async () => {
|
|
|
180
180
|
};
|
|
181
181
|
|
|
182
182
|
/**
|
|
183
|
-
* Symbol selection helper -
|
|
183
|
+
* Symbol selection helper - grouped by category with indices first
|
|
184
184
|
*/
|
|
185
185
|
const selectSymbol = async (service, label) => {
|
|
186
186
|
const spinner = ora({ text: 'Loading symbols...', color: 'yellow' }).start();
|
|
@@ -193,6 +193,7 @@ const selectSymbol = async (service, label) => {
|
|
|
193
193
|
if (!contracts && typeof service.getContracts === 'function') {
|
|
194
194
|
const result = await service.getContracts();
|
|
195
195
|
if (result.success && result.contracts?.length > 0) {
|
|
196
|
+
// Contracts from Rithmic are already categorized and sorted
|
|
196
197
|
contracts = result.contracts;
|
|
197
198
|
}
|
|
198
199
|
}
|
|
@@ -203,23 +204,36 @@ const selectSymbol = async (service, label) => {
|
|
|
203
204
|
return null;
|
|
204
205
|
}
|
|
205
206
|
|
|
206
|
-
// Sort: Popular indices first (ES, NQ, YM, RTY, MES, MNQ)
|
|
207
|
-
const prioritySymbols = ['ES', 'NQ', 'YM', 'RTY', 'MES', 'MNQ', 'MYM', 'M2K', 'NKD', 'CL', 'GC'];
|
|
208
|
-
contracts.sort((a, b) => {
|
|
209
|
-
const aSymbol = (a.symbol || a.name || '').toUpperCase();
|
|
210
|
-
const bSymbol = (b.symbol || b.name || '').toUpperCase();
|
|
211
|
-
const aIdx = prioritySymbols.findIndex(p => aSymbol.startsWith(p));
|
|
212
|
-
const bIdx = prioritySymbols.findIndex(p => bSymbol.startsWith(p));
|
|
213
|
-
if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
|
|
214
|
-
if (aIdx !== -1) return -1;
|
|
215
|
-
if (bIdx !== -1) return 1;
|
|
216
|
-
return (a.name || '').localeCompare(b.name || '');
|
|
217
|
-
});
|
|
218
|
-
|
|
219
207
|
spinner.succeed(`Found ${contracts.length} contracts`);
|
|
220
208
|
|
|
221
|
-
|
|
222
|
-
options
|
|
209
|
+
// Build options with category headers (if contracts have category info)
|
|
210
|
+
const options = [];
|
|
211
|
+
let currentCategory = null;
|
|
212
|
+
|
|
213
|
+
for (const c of contracts) {
|
|
214
|
+
// Add category header when category changes (if available)
|
|
215
|
+
if (c.categoryName && c.categoryName !== currentCategory) {
|
|
216
|
+
currentCategory = c.categoryName;
|
|
217
|
+
options.push({
|
|
218
|
+
label: chalk.cyan.bold(`── ${currentCategory} ──`),
|
|
219
|
+
value: null,
|
|
220
|
+
disabled: true
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Format label based on available data
|
|
225
|
+
const symbolDisplay = c.symbol || c.name;
|
|
226
|
+
const nameDisplay = c.name || c.symbol;
|
|
227
|
+
const exchangeDisplay = c.exchange ? ` (${c.exchange})` : '';
|
|
228
|
+
const label = c.categoryName
|
|
229
|
+
? ` ${symbolDisplay} - ${nameDisplay}${exchangeDisplay}`
|
|
230
|
+
: `${nameDisplay}${exchangeDisplay}`;
|
|
231
|
+
|
|
232
|
+
options.push({ label, value: c });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
options.push({ label: '', value: null, disabled: true }); // Spacer
|
|
236
|
+
options.push({ label: chalk.gray('< Cancel'), value: null });
|
|
223
237
|
|
|
224
238
|
return await prompts.selectOption(`${label} Symbol:`, options);
|
|
225
239
|
} catch (e) {
|
|
@@ -72,7 +72,7 @@ const oneAccountMenu = async (service) => {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Symbol selection -
|
|
75
|
+
* Symbol selection - grouped by category with indices first
|
|
76
76
|
*/
|
|
77
77
|
const selectSymbol = async (service, account) => {
|
|
78
78
|
const spinner = ora({ text: 'Loading symbols...', color: 'yellow' }).start();
|
|
@@ -83,33 +83,36 @@ const selectSymbol = async (service, account) => {
|
|
|
83
83
|
return null;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
//
|
|
87
|
-
const contracts = contractsResult.contracts
|
|
88
|
-
...c,
|
|
89
|
-
symbol: c.name || c.symbol,
|
|
90
|
-
name: c.description || c.name || c.symbol
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
// Sort: Popular indices first (ES, NQ, YM, RTY, MES, MNQ)
|
|
94
|
-
const prioritySymbols = ['ES', 'NQ', 'YM', 'RTY', 'MES', 'MNQ', 'MYM', 'M2K', 'NKD', 'CL', 'GC'];
|
|
95
|
-
contracts.sort((a, b) => {
|
|
96
|
-
const aSymbol = (a.symbol || '').toUpperCase();
|
|
97
|
-
const bSymbol = (b.symbol || '').toUpperCase();
|
|
98
|
-
const aIdx = prioritySymbols.findIndex(p => aSymbol.startsWith(p));
|
|
99
|
-
const bIdx = prioritySymbols.findIndex(p => bSymbol.startsWith(p));
|
|
100
|
-
if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
|
|
101
|
-
if (aIdx !== -1) return -1;
|
|
102
|
-
if (bIdx !== -1) return 1;
|
|
103
|
-
return (a.name || '').localeCompare(b.name || '');
|
|
104
|
-
});
|
|
86
|
+
// Contracts are already sorted by category from getContracts()
|
|
87
|
+
const contracts = contractsResult.contracts;
|
|
105
88
|
|
|
106
89
|
spinner.succeed(`Found ${contracts.length} contracts`);
|
|
107
90
|
|
|
108
|
-
|
|
109
|
-
options
|
|
91
|
+
// Build options with category headers
|
|
92
|
+
const options = [];
|
|
93
|
+
let currentCategory = null;
|
|
94
|
+
|
|
95
|
+
for (const c of contracts) {
|
|
96
|
+
// Add category header when category changes
|
|
97
|
+
if (c.categoryName !== currentCategory) {
|
|
98
|
+
currentCategory = c.categoryName;
|
|
99
|
+
options.push({
|
|
100
|
+
label: chalk.cyan.bold(`── ${currentCategory} ──`),
|
|
101
|
+
value: null,
|
|
102
|
+
disabled: true
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Format: "ESH6 - E-mini S&P 500 (CME)"
|
|
107
|
+
const label = ` ${c.symbol} - ${c.name} (${c.exchange})`;
|
|
108
|
+
options.push({ label, value: c });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
options.push({ label: '', value: null, disabled: true }); // Spacer
|
|
112
|
+
options.push({ label: chalk.gray('< Back'), value: 'back' });
|
|
110
113
|
|
|
111
114
|
const contract = await prompts.selectOption(chalk.yellow('Select Symbol:'), options);
|
|
112
|
-
return contract === 'back' ? null : contract;
|
|
115
|
+
return contract === 'back' || contract === null ? null : contract;
|
|
113
116
|
};
|
|
114
117
|
|
|
115
118
|
/**
|
|
@@ -165,6 +165,155 @@ const PROTO_FILES = [
|
|
|
165
165
|
'response_front_month_contract.proto',
|
|
166
166
|
];
|
|
167
167
|
|
|
168
|
+
// Symbol Categories - Order matters for display (indices first)
|
|
169
|
+
const SYMBOL_CATEGORIES = {
|
|
170
|
+
INDICES: {
|
|
171
|
+
name: 'Indices',
|
|
172
|
+
order: 1,
|
|
173
|
+
symbols: {
|
|
174
|
+
// E-mini Indices
|
|
175
|
+
'ES': { name: 'E-mini S&P 500', tickSize: 0.25, tickValue: 12.50 },
|
|
176
|
+
'NQ': { name: 'E-mini Nasdaq-100', tickSize: 0.25, tickValue: 5.00 },
|
|
177
|
+
'YM': { name: 'E-mini Dow Jones', tickSize: 1.00, tickValue: 5.00 },
|
|
178
|
+
'RTY': { name: 'E-mini Russell 2000', tickSize: 0.10, tickValue: 5.00 },
|
|
179
|
+
'EMD': { name: 'E-mini S&P MidCap 400', tickSize: 0.10, tickValue: 10.00 },
|
|
180
|
+
// Micro Indices
|
|
181
|
+
'MES': { name: 'Micro E-mini S&P 500', tickSize: 0.25, tickValue: 1.25 },
|
|
182
|
+
'MNQ': { name: 'Micro E-mini Nasdaq-100', tickSize: 0.25, tickValue: 0.50 },
|
|
183
|
+
'MYM': { name: 'Micro E-mini Dow Jones', tickSize: 1.00, tickValue: 0.50 },
|
|
184
|
+
'M2K': { name: 'Micro E-mini Russell 2000', tickSize: 0.10, tickValue: 0.50 },
|
|
185
|
+
// International Indices
|
|
186
|
+
'NKD': { name: 'Nikkei 225 (USD)', tickSize: 5.00, tickValue: 25.00 },
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
ENERGY: {
|
|
190
|
+
name: 'Energy',
|
|
191
|
+
order: 2,
|
|
192
|
+
symbols: {
|
|
193
|
+
'CL': { name: 'Crude Oil WTI', tickSize: 0.01, tickValue: 10.00 },
|
|
194
|
+
'MCL': { name: 'Micro Crude Oil WTI', tickSize: 0.01, tickValue: 1.00 },
|
|
195
|
+
'BZ': { name: 'Brent Crude Oil', tickSize: 0.01, tickValue: 10.00 },
|
|
196
|
+
'NG': { name: 'Natural Gas', tickSize: 0.001, tickValue: 10.00 },
|
|
197
|
+
'HO': { name: 'Heating Oil', tickSize: 0.0001, tickValue: 4.20 },
|
|
198
|
+
'RB': { name: 'RBOB Gasoline', tickSize: 0.0001, tickValue: 4.20 },
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
METALS: {
|
|
202
|
+
name: 'Metals',
|
|
203
|
+
order: 3,
|
|
204
|
+
symbols: {
|
|
205
|
+
'GC': { name: 'Gold', tickSize: 0.10, tickValue: 10.00 },
|
|
206
|
+
'MGC': { name: 'Micro Gold', tickSize: 0.10, tickValue: 1.00 },
|
|
207
|
+
'1OZ': { name: '1 Ounce Gold', tickSize: 0.25, tickValue: 0.25 },
|
|
208
|
+
'SI': { name: 'Silver', tickSize: 0.005, tickValue: 25.00 },
|
|
209
|
+
'SIL': { name: 'Silver 1000oz', tickSize: 0.005, tickValue: 5.00 },
|
|
210
|
+
'HG': { name: 'Copper', tickSize: 0.0005, tickValue: 12.50 },
|
|
211
|
+
'MHG': { name: 'Micro Copper', tickSize: 0.0005, tickValue: 1.25 },
|
|
212
|
+
'PL': { name: 'Platinum', tickSize: 0.10, tickValue: 5.00 },
|
|
213
|
+
'PA': { name: 'Palladium', tickSize: 0.10, tickValue: 10.00 },
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
CURRENCIES: {
|
|
217
|
+
name: 'Currencies (FX)',
|
|
218
|
+
order: 4,
|
|
219
|
+
symbols: {
|
|
220
|
+
'6E': { name: 'Euro FX', tickSize: 0.00005, tickValue: 6.25 },
|
|
221
|
+
'M6E': { name: 'Micro Euro FX', tickSize: 0.0001, tickValue: 1.25 },
|
|
222
|
+
'6B': { name: 'British Pound', tickSize: 0.0001, tickValue: 6.25 },
|
|
223
|
+
'M6B': { name: 'Micro British Pound', tickSize: 0.0001, tickValue: 0.625 },
|
|
224
|
+
'6J': { name: 'Japanese Yen', tickSize: 0.0000005, tickValue: 6.25 },
|
|
225
|
+
'6A': { name: 'Australian Dollar', tickSize: 0.0001, tickValue: 10.00 },
|
|
226
|
+
'M6A': { name: 'Micro Australian Dollar', tickSize: 0.0001, tickValue: 1.00 },
|
|
227
|
+
'6C': { name: 'Canadian Dollar', tickSize: 0.00005, tickValue: 5.00 },
|
|
228
|
+
'6S': { name: 'Swiss Franc', tickSize: 0.0001, tickValue: 12.50 },
|
|
229
|
+
'6N': { name: 'New Zealand Dollar', tickSize: 0.0001, tickValue: 10.00 },
|
|
230
|
+
'6M': { name: 'Mexican Peso', tickSize: 0.00001, tickValue: 5.00 },
|
|
231
|
+
'E7': { name: 'E-mini Euro FX', tickSize: 0.0001, tickValue: 6.25 },
|
|
232
|
+
'RF': { name: 'Euro FX/Swiss Franc', tickSize: 0.0001, tickValue: 12.50 },
|
|
233
|
+
'RP': { name: 'Euro FX/British Pound', tickSize: 0.00005, tickValue: 6.25 },
|
|
234
|
+
'RY': { name: 'Euro FX/Japanese Yen', tickSize: 0.01, tickValue: 6.25 },
|
|
235
|
+
'SEK': { name: 'Swedish Krona', tickSize: 0.00001, tickValue: 12.50 },
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
CRYPTO: {
|
|
239
|
+
name: 'Crypto',
|
|
240
|
+
order: 5,
|
|
241
|
+
symbols: {
|
|
242
|
+
'BTC': { name: 'Bitcoin', tickSize: 5.00, tickValue: 25.00 },
|
|
243
|
+
'MBT': { name: 'Micro Bitcoin', tickSize: 5.00, tickValue: 0.50 },
|
|
244
|
+
'ETH': { name: 'Ether', tickSize: 0.25, tickValue: 12.50 },
|
|
245
|
+
'MET': { name: 'Micro Ether', tickSize: 0.05, tickValue: 0.25 },
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
RATES: {
|
|
249
|
+
name: 'Interest Rates',
|
|
250
|
+
order: 6,
|
|
251
|
+
symbols: {
|
|
252
|
+
'ZB': { name: '30-Year T-Bond', tickSize: 0.03125, tickValue: 31.25 },
|
|
253
|
+
'ZN': { name: '10-Year T-Note', tickSize: 0.015625, tickValue: 15.625 },
|
|
254
|
+
'ZF': { name: '5-Year T-Note', tickSize: 0.0078125, tickValue: 7.8125 },
|
|
255
|
+
'ZT': { name: '2-Year T-Note', tickSize: 0.0078125, tickValue: 15.625 },
|
|
256
|
+
'TN': { name: 'Ultra 10-Year T-Note', tickSize: 0.015625, tickValue: 15.625 },
|
|
257
|
+
'ZQ': { name: '30-Day Fed Funds', tickSize: 0.0025, tickValue: 10.4167 },
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
AGRICULTURE: {
|
|
261
|
+
name: 'Agriculture',
|
|
262
|
+
order: 7,
|
|
263
|
+
symbols: {
|
|
264
|
+
'ZC': { name: 'Corn', tickSize: 0.25, tickValue: 12.50 },
|
|
265
|
+
'ZS': { name: 'Soybeans', tickSize: 0.25, tickValue: 12.50 },
|
|
266
|
+
'ZW': { name: 'Wheat', tickSize: 0.25, tickValue: 12.50 },
|
|
267
|
+
'ZL': { name: 'Soybean Oil', tickSize: 0.01, tickValue: 6.00 },
|
|
268
|
+
'ZM': { name: 'Soybean Meal', tickSize: 0.10, tickValue: 10.00 },
|
|
269
|
+
'ZO': { name: 'Oats', tickSize: 0.25, tickValue: 12.50 },
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
MEATS: {
|
|
273
|
+
name: 'Meats',
|
|
274
|
+
order: 8,
|
|
275
|
+
symbols: {
|
|
276
|
+
'LE': { name: 'Live Cattle', tickSize: 0.025, tickValue: 10.00 },
|
|
277
|
+
'HE': { name: 'Lean Hogs', tickSize: 0.025, tickValue: 10.00 },
|
|
278
|
+
'GF': { name: 'Feeder Cattle', tickSize: 0.025, tickValue: 12.50 },
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get symbol info (category, name, tick info)
|
|
285
|
+
*/
|
|
286
|
+
function getSymbolInfo(baseSymbol) {
|
|
287
|
+
for (const [catKey, category] of Object.entries(SYMBOL_CATEGORIES)) {
|
|
288
|
+
if (category.symbols[baseSymbol]) {
|
|
289
|
+
return {
|
|
290
|
+
category: catKey,
|
|
291
|
+
categoryName: category.name,
|
|
292
|
+
categoryOrder: category.order,
|
|
293
|
+
...category.symbols[baseSymbol]
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Unknown symbol
|
|
298
|
+
return {
|
|
299
|
+
category: 'OTHER',
|
|
300
|
+
categoryName: 'Other',
|
|
301
|
+
categoryOrder: 99,
|
|
302
|
+
name: baseSymbol,
|
|
303
|
+
tickSize: null,
|
|
304
|
+
tickValue: null
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get all categories in display order
|
|
310
|
+
*/
|
|
311
|
+
function getCategoryOrder() {
|
|
312
|
+
return Object.entries(SYMBOL_CATEGORIES)
|
|
313
|
+
.sort((a, b) => a[1].order - b[1].order)
|
|
314
|
+
.map(([key, val]) => ({ key, name: val.name, order: val.order }));
|
|
315
|
+
}
|
|
316
|
+
|
|
168
317
|
module.exports = {
|
|
169
318
|
RITHMIC_ENDPOINTS,
|
|
170
319
|
RITHMIC_SYSTEMS,
|
|
@@ -173,4 +322,7 @@ module.exports = {
|
|
|
173
322
|
RES,
|
|
174
323
|
STREAM,
|
|
175
324
|
PROTO_FILES,
|
|
325
|
+
SYMBOL_CATEGORIES,
|
|
326
|
+
getSymbolInfo,
|
|
327
|
+
getCategoryOrder,
|
|
176
328
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const EventEmitter = require('events');
|
|
7
7
|
const { RithmicConnection } = require('./connection');
|
|
8
|
-
const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS } = require('./constants');
|
|
8
|
+
const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS, getSymbolInfo, getCategoryOrder } = require('./constants');
|
|
9
9
|
const { createOrderHandler, createPnLHandler } = require('./handlers');
|
|
10
10
|
const { fetchAccounts, getTradingAccounts, requestPnLSnapshot, subscribePnLUpdates, getPositions, hashAccountId } = require('./accounts');
|
|
11
11
|
const { placeOrder, cancelOrder, getOrders, getOrderHistory, closePosition } = require('./orders');
|
|
@@ -425,19 +425,34 @@ class RithmicService extends EventEmitter {
|
|
|
425
425
|
setTimeout(() => {
|
|
426
426
|
this.tickerConn.removeListener('message', frontMonthHandler);
|
|
427
427
|
|
|
428
|
-
// Merge with product names
|
|
428
|
+
// Merge with product names and add category info
|
|
429
429
|
const results = [];
|
|
430
430
|
for (const [baseSymbol, contract] of contracts) {
|
|
431
431
|
const productKey = `${baseSymbol}:${contract.exchange}`;
|
|
432
432
|
const product = productsToCheck.get(productKey);
|
|
433
|
+
const symbolInfo = getSymbolInfo(baseSymbol);
|
|
434
|
+
|
|
433
435
|
results.push({
|
|
434
436
|
symbol: contract.symbol,
|
|
435
437
|
baseSymbol: baseSymbol,
|
|
436
|
-
name:
|
|
438
|
+
name: symbolInfo.name || product?.productName || baseSymbol,
|
|
437
439
|
exchange: contract.exchange,
|
|
440
|
+
category: symbolInfo.category,
|
|
441
|
+
categoryName: symbolInfo.categoryName,
|
|
442
|
+
categoryOrder: symbolInfo.categoryOrder,
|
|
443
|
+
tickSize: symbolInfo.tickSize,
|
|
444
|
+
tickValue: symbolInfo.tickValue,
|
|
438
445
|
});
|
|
439
446
|
}
|
|
440
447
|
|
|
448
|
+
// Sort by category order, then by symbol name within category
|
|
449
|
+
results.sort((a, b) => {
|
|
450
|
+
if (a.categoryOrder !== b.categoryOrder) {
|
|
451
|
+
return a.categoryOrder - b.categoryOrder;
|
|
452
|
+
}
|
|
453
|
+
return a.baseSymbol.localeCompare(b.baseSymbol);
|
|
454
|
+
});
|
|
455
|
+
|
|
441
456
|
debug(`Got ${results.length} tradeable contracts from API`);
|
|
442
457
|
resolve(results);
|
|
443
458
|
}, 8000);
|
package/src/utils/prompts.js
CHANGED
|
@@ -126,6 +126,7 @@ const numberInput = async (message, defaultVal = 1, min = 1, max = 1000) => {
|
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
128
|
* Select - arrow keys navigation
|
|
129
|
+
* Supports disabled options (separators) via opt.disabled
|
|
129
130
|
*/
|
|
130
131
|
const selectOption = async (message, options) => {
|
|
131
132
|
// Close shared readline before inquirer to avoid conflicts
|
|
@@ -135,10 +136,16 @@ const selectOption = async (message, options) => {
|
|
|
135
136
|
}
|
|
136
137
|
prepareStdin();
|
|
137
138
|
|
|
138
|
-
const choices = options.map(opt =>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
const choices = options.map(opt => {
|
|
140
|
+
if (opt.disabled) {
|
|
141
|
+
// Use inquirer Separator for disabled items (category headers)
|
|
142
|
+
return new inquirer.Separator(opt.label);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
name: opt.label,
|
|
146
|
+
value: opt.value
|
|
147
|
+
};
|
|
148
|
+
});
|
|
142
149
|
|
|
143
150
|
const { value } = await inquirer.prompt([{
|
|
144
151
|
type: 'list',
|
|
@@ -147,7 +154,7 @@ const selectOption = async (message, options) => {
|
|
|
147
154
|
choices,
|
|
148
155
|
prefix: '',
|
|
149
156
|
loop: false,
|
|
150
|
-
pageSize:
|
|
157
|
+
pageSize: 20 // Increased to show more symbols
|
|
151
158
|
}]);
|
|
152
159
|
|
|
153
160
|
return value;
|