hedgequantx 1.8.48 → 2.3.0

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.
Files changed (105) hide show
  1. package/README.md +7 -6
  2. package/bin/cli.js +13 -7
  3. package/dist/algo/copy-engine.js +3 -0
  4. package/dist/algo/copy-engine.jsc +0 -0
  5. package/dist/algo/engine.js +3 -0
  6. package/dist/algo/engine.jsc +0 -0
  7. package/dist/algo/market-data-rithmic.js +3 -0
  8. package/dist/algo/market-data-rithmic.jsc +0 -0
  9. package/dist/algo/market-data.js +3 -0
  10. package/dist/algo/market-data.jsc +0 -0
  11. package/dist/algo/rithmic/connection.js +3 -0
  12. package/dist/algo/rithmic/connection.jsc +0 -0
  13. package/dist/algo/rithmic/constants.js +3 -0
  14. package/dist/algo/rithmic/constants.jsc +0 -0
  15. package/dist/algo/rithmic/index.js +3 -0
  16. package/dist/algo/rithmic/index.jsc +0 -0
  17. package/dist/algo/rithmic/market-data.js +3 -0
  18. package/dist/algo/rithmic/market-data.jsc +0 -0
  19. package/dist/algo/rithmic/pnl.js +3 -0
  20. package/dist/algo/rithmic/pnl.jsc +0 -0
  21. package/dist/algo/rithmic/pool.js +3 -0
  22. package/dist/algo/rithmic/pool.jsc +0 -0
  23. package/dist/algo/rithmic/trading.js +3 -0
  24. package/dist/algo/rithmic/trading.jsc +0 -0
  25. package/dist/algo/rithmic-decoder.js +3 -0
  26. package/dist/algo/rithmic-decoder.jsc +0 -0
  27. package/dist/algo/strategies/ultra-scalping-v2.js +3 -0
  28. package/dist/algo/strategies/ultra-scalping-v2.jsc +0 -0
  29. package/dist/algo/strategies/ultra-scalping.js +3 -0
  30. package/dist/algo/strategies/ultra-scalping.jsc +0 -0
  31. package/dist/algo/trading-api-rithmic.js +3 -0
  32. package/dist/algo/trading-api-rithmic.jsc +0 -0
  33. package/dist/algo/trading-api.js +3 -0
  34. package/dist/algo/trading-api.jsc +0 -0
  35. package/dist/algo/utils/smart-logger.js +3 -0
  36. package/dist/algo/utils/smart-logger.jsc +0 -0
  37. package/dist/algo/utils/smart-logs.js +3 -0
  38. package/dist/algo/utils/smart-logs.jsc +0 -0
  39. package/package.json +33 -10
  40. package/protos/rithmic/account_pnl_position_update.proto +59 -0
  41. package/protos/rithmic/base.proto +7 -0
  42. package/protos/rithmic/best_bid_offer.proto +39 -0
  43. package/protos/rithmic/exchange_order_notification.proto +140 -0
  44. package/protos/rithmic/instrument_pnl_position_update.proto +50 -0
  45. package/protos/rithmic/last_trade.proto +53 -0
  46. package/protos/rithmic/request_account_list.proto +20 -0
  47. package/protos/rithmic/request_cancel_all_orders.proto +15 -0
  48. package/protos/rithmic/request_front_month_contract.proto +10 -0
  49. package/protos/rithmic/request_heartbeat.proto +13 -0
  50. package/protos/rithmic/request_login.proto +28 -0
  51. package/protos/rithmic/request_login_info.proto +10 -0
  52. package/protos/rithmic/request_logout.proto +10 -0
  53. package/protos/rithmic/request_market_data_update.proto +42 -0
  54. package/protos/rithmic/request_new_order.proto +84 -0
  55. package/protos/rithmic/request_pnl_position_snapshot.proto +14 -0
  56. package/protos/rithmic/request_pnl_position_updates.proto +20 -0
  57. package/protos/rithmic/request_product_codes.proto +9 -0
  58. package/protos/rithmic/request_rithmic_system_info.proto +8 -0
  59. package/protos/rithmic/request_show_order_history.proto +16 -0
  60. package/protos/rithmic/request_show_order_history_dates.proto +10 -0
  61. package/protos/rithmic/request_show_order_history_summary.proto +14 -0
  62. package/protos/rithmic/request_show_orders.proto +14 -0
  63. package/protos/rithmic/request_subscribe_for_order_updates.proto +14 -0
  64. package/protos/rithmic/request_tick_bar_replay.proto +48 -0
  65. package/protos/rithmic/request_trade_routes.proto +11 -0
  66. package/protos/rithmic/response_account_list.proto +18 -0
  67. package/protos/rithmic/response_front_month_contract.proto +13 -0
  68. package/protos/rithmic/response_heartbeat.proto +14 -0
  69. package/protos/rithmic/response_login.proto +18 -0
  70. package/protos/rithmic/response_login_info.proto +24 -0
  71. package/protos/rithmic/response_logout.proto +11 -0
  72. package/protos/rithmic/response_market_data_update.proto +9 -0
  73. package/protos/rithmic/response_new_order.proto +18 -0
  74. package/protos/rithmic/response_pnl_position_snapshot.proto +11 -0
  75. package/protos/rithmic/response_pnl_position_updates.proto +11 -0
  76. package/protos/rithmic/response_product_codes.proto +12 -0
  77. package/protos/rithmic/response_rithmic_system_info.proto +12 -0
  78. package/protos/rithmic/response_show_order_history.proto +11 -0
  79. package/protos/rithmic/response_show_order_history_dates.proto +13 -0
  80. package/protos/rithmic/response_show_order_history_summary.proto +11 -0
  81. package/protos/rithmic/response_show_orders.proto +11 -0
  82. package/protos/rithmic/response_subscribe_for_order_updates.proto +11 -0
  83. package/protos/rithmic/response_tick_bar_replay.proto +40 -0
  84. package/protos/rithmic/response_trade_routes.proto +19 -0
  85. package/protos/rithmic/rithmic_order_notification.proto +124 -0
  86. package/src/app.js +136 -89
  87. package/src/config/index.js +27 -8
  88. package/src/config/settings.js +155 -0
  89. package/src/pages/accounts.js +2 -3
  90. package/src/pages/algo/copy-trading.js +293 -200
  91. package/src/pages/algo/one-account.js +1 -1
  92. package/src/security/encryption.js +81 -46
  93. package/src/security/index.js +12 -8
  94. package/src/security/rateLimit.js +68 -65
  95. package/src/security/validation.js +93 -79
  96. package/src/services/hqx-server.js +538 -206
  97. package/src/services/projectx/index.js +327 -204
  98. package/src/services/rithmic/index.js +288 -285
  99. package/src/services/session.js +184 -114
  100. package/src/services/tradovate/index.js +286 -297
  101. package/src/ui/index.js +53 -1
  102. package/src/utils/http.js +236 -0
  103. package/src/utils/index.js +11 -2
  104. package/src/utils/logger.js +64 -33
  105. package/src/utils/prompts.js +79 -71
@@ -3,10 +3,16 @@
3
3
  * @module security/validation
4
4
  */
5
5
 
6
+ const { VALIDATION, SECURITY } = require('../config/settings');
7
+
6
8
  /**
7
- * Validation error class
9
+ * Validation error class with field information
8
10
  */
9
11
  class ValidationError extends Error {
12
+ /**
13
+ * @param {string} message - Error message
14
+ * @param {string} [field] - Field that failed validation
15
+ */
10
16
  constructor(message, field = null) {
11
17
  super(message);
12
18
  this.name = 'ValidationError';
@@ -15,45 +21,63 @@ class ValidationError extends Error {
15
21
  }
16
22
 
17
23
  /**
18
- * Validates username format
19
- * @param {string} username - Username to validate
20
- * @returns {boolean} True if valid
21
- * @throws {ValidationError} If invalid
24
+ * Validates a string against constraints
25
+ * @param {*} value - Value to validate
26
+ * @param {Object} opts - Validation options
27
+ * @returns {string} Validated and trimmed string
28
+ * @throws {ValidationError}
29
+ * @private
22
30
  */
23
- const validateUsername = (username) => {
24
- if (!username || typeof username !== 'string') {
25
- throw new ValidationError('Username is required', 'username');
31
+ const validateString = (value, { field, min, max, pattern, patternMsg }) => {
32
+ if (!value || typeof value !== 'string') {
33
+ throw new ValidationError(`${field} is required`, field);
26
34
  }
27
35
 
28
- const trimmed = username.trim();
36
+ const trimmed = value.trim();
29
37
 
30
- if (trimmed.length < 3) {
31
- throw new ValidationError('Username must be at least 3 characters', 'username');
38
+ if (trimmed.length < min) {
39
+ throw new ValidationError(`${field} must be at least ${min} characters`, field);
32
40
  }
33
41
 
34
- if (trimmed.length > 50) {
35
- throw new ValidationError('Username must be less than 50 characters', 'username');
42
+ if (trimmed.length > max) {
43
+ throw new ValidationError(`${field} must be less than ${max} characters`, field);
36
44
  }
37
45
 
38
- // Allow alphanumeric, dots, underscores, hyphens, and @ for emails
39
- if (!/^[a-zA-Z0-9._@-]+$/.test(trimmed)) {
40
- throw new ValidationError('Username contains invalid characters', 'username');
46
+ if (pattern && !pattern.test(trimmed)) {
47
+ throw new ValidationError(patternMsg || `${field} contains invalid characters`, field);
41
48
  }
42
49
 
43
- return true;
50
+ return trimmed;
44
51
  };
45
52
 
53
+ /**
54
+ * Validates username format
55
+ * @param {string} username - Username to validate
56
+ * @returns {string} Validated username
57
+ * @throws {ValidationError}
58
+ */
59
+ const validateUsername = (username) => validateString(username, {
60
+ field: 'Username',
61
+ min: VALIDATION.USERNAME_MIN,
62
+ max: VALIDATION.USERNAME_MAX,
63
+ pattern: VALIDATION.USERNAME_PATTERN,
64
+ patternMsg: 'Username contains invalid characters (allowed: letters, numbers, . _ @ -)',
65
+ });
66
+
46
67
  /**
47
68
  * Validates password strength
48
69
  * @param {string} password - Password to validate
49
- * @param {Object} [options] - Validation options
50
- * @param {number} [options.minLength=6] - Minimum length
51
- * @param {boolean} [options.requireSpecial=false] - Require special character
70
+ * @param {Object} [options] - Override default requirements
52
71
  * @returns {boolean} True if valid
53
- * @throws {ValidationError} If invalid
72
+ * @throws {ValidationError}
54
73
  */
55
74
  const validatePassword = (password, options = {}) => {
56
- const { minLength = 6, requireSpecial = false } = options;
75
+ const {
76
+ minLength = SECURITY.PASSWORD_MIN_LENGTH,
77
+ requireUppercase = SECURITY.PASSWORD_REQUIRE_UPPERCASE,
78
+ requireNumber = SECURITY.PASSWORD_REQUIRE_NUMBER,
79
+ requireSpecial = SECURITY.PASSWORD_REQUIRE_SPECIAL,
80
+ } = options;
57
81
 
58
82
  if (!password || typeof password !== 'string') {
59
83
  throw new ValidationError('Password is required', 'password');
@@ -63,8 +87,16 @@ const validatePassword = (password, options = {}) => {
63
87
  throw new ValidationError(`Password must be at least ${minLength} characters`, 'password');
64
88
  }
65
89
 
66
- if (password.length > 128) {
67
- throw new ValidationError('Password must be less than 128 characters', 'password');
90
+ if (password.length > SECURITY.PASSWORD_MAX_LENGTH) {
91
+ throw new ValidationError(`Password must be less than ${SECURITY.PASSWORD_MAX_LENGTH} characters`, 'password');
92
+ }
93
+
94
+ if (requireUppercase && !/[A-Z]/.test(password)) {
95
+ throw new ValidationError('Password must contain at least one uppercase letter', 'password');
96
+ }
97
+
98
+ if (requireNumber && !/\d/.test(password)) {
99
+ throw new ValidationError('Password must contain at least one number', 'password');
68
100
  }
69
101
 
70
102
  if (requireSpecial && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
@@ -77,37 +109,21 @@ const validatePassword = (password, options = {}) => {
77
109
  /**
78
110
  * Validates API key format
79
111
  * @param {string} apiKey - API key to validate
80
- * @returns {boolean} True if valid
81
- * @throws {ValidationError} If invalid
112
+ * @returns {string} Validated API key
113
+ * @throws {ValidationError}
82
114
  */
83
- const validateApiKey = (apiKey) => {
84
- if (!apiKey || typeof apiKey !== 'string') {
85
- throw new ValidationError('API key is required', 'apiKey');
86
- }
87
-
88
- const trimmed = apiKey.trim();
89
-
90
- if (trimmed.length < 10) {
91
- throw new ValidationError('API key is too short', 'apiKey');
92
- }
93
-
94
- if (trimmed.length > 256) {
95
- throw new ValidationError('API key is too long', 'apiKey');
96
- }
97
-
98
- // Allow alphanumeric and common API key characters
99
- if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
100
- throw new ValidationError('API key contains invalid characters', 'apiKey');
101
- }
102
-
103
- return true;
104
- };
115
+ const validateApiKey = (apiKey) => validateString(apiKey, {
116
+ field: 'API key',
117
+ min: VALIDATION.API_KEY_MIN,
118
+ max: VALIDATION.API_KEY_MAX,
119
+ pattern: VALIDATION.API_KEY_PATTERN,
120
+ });
105
121
 
106
122
  /**
107
123
  * Validates account ID
108
124
  * @param {number|string} accountId - Account ID to validate
109
125
  * @returns {number} Validated account ID as integer
110
- * @throws {ValidationError} If invalid
126
+ * @throws {ValidationError}
111
127
  */
112
128
  const validateAccountId = (accountId) => {
113
129
  const id = parseInt(accountId, 10);
@@ -116,7 +132,7 @@ const validateAccountId = (accountId) => {
116
132
  throw new ValidationError('Invalid account ID', 'accountId');
117
133
  }
118
134
 
119
- if (id > Number.MAX_SAFE_INTEGER) {
135
+ if (id > VALIDATION.ACCOUNT_ID_MAX) {
120
136
  throw new ValidationError('Account ID is too large', 'accountId');
121
137
  }
122
138
 
@@ -127,13 +143,11 @@ const validateAccountId = (accountId) => {
127
143
  * Validates order quantity
128
144
  * @param {number|string} quantity - Quantity to validate
129
145
  * @param {Object} [options] - Validation options
130
- * @param {number} [options.min=1] - Minimum quantity
131
- * @param {number} [options.max=1000] - Maximum quantity
132
146
  * @returns {number} Validated quantity as integer
133
- * @throws {ValidationError} If invalid
147
+ * @throws {ValidationError}
134
148
  */
135
149
  const validateQuantity = (quantity, options = {}) => {
136
- const { min = 1, max = 1000 } = options;
150
+ const { min = VALIDATION.QUANTITY_MIN, max = VALIDATION.QUANTITY_MAX } = options;
137
151
  const qty = parseInt(quantity, 10);
138
152
 
139
153
  if (isNaN(qty)) {
@@ -155,7 +169,7 @@ const validateQuantity = (quantity, options = {}) => {
155
169
  * Validates price
156
170
  * @param {number|string} price - Price to validate
157
171
  * @returns {number} Validated price as float
158
- * @throws {ValidationError} If invalid
172
+ * @throws {ValidationError}
159
173
  */
160
174
  const validatePrice = (price) => {
161
175
  const p = parseFloat(price);
@@ -164,11 +178,11 @@ const validatePrice = (price) => {
164
178
  throw new ValidationError('Price must be a number', 'price');
165
179
  }
166
180
 
167
- if (p < 0) {
181
+ if (p < VALIDATION.PRICE_MIN) {
168
182
  throw new ValidationError('Price cannot be negative', 'price');
169
183
  }
170
184
 
171
- if (p > 1000000) {
185
+ if (p > VALIDATION.PRICE_MAX) {
172
186
  throw new ValidationError('Price is too large', 'price');
173
187
  }
174
188
 
@@ -179,28 +193,26 @@ const validatePrice = (price) => {
179
193
  * Validates symbol format
180
194
  * @param {string} symbol - Symbol to validate
181
195
  * @returns {string} Validated symbol (uppercase, trimmed)
182
- * @throws {ValidationError} If invalid
196
+ * @throws {ValidationError}
183
197
  */
184
198
  const validateSymbol = (symbol) => {
185
- if (!symbol || typeof symbol !== 'string') {
186
- throw new ValidationError('Symbol is required', 'symbol');
187
- }
188
-
189
- const trimmed = symbol.trim().toUpperCase();
199
+ const validated = validateString(symbol, {
200
+ field: 'Symbol',
201
+ min: VALIDATION.SYMBOL_MIN,
202
+ max: VALIDATION.SYMBOL_MAX,
203
+ });
190
204
 
191
- if (trimmed.length < 1 || trimmed.length > 20) {
192
- throw new ValidationError('Invalid symbol length', 'symbol');
193
- }
205
+ const upper = validated.toUpperCase();
194
206
 
195
- if (!/^[A-Z0-9]+$/.test(trimmed)) {
207
+ if (!VALIDATION.SYMBOL_PATTERN.test(upper)) {
196
208
  throw new ValidationError('Symbol contains invalid characters', 'symbol');
197
209
  }
198
210
 
199
- return trimmed;
211
+ return upper;
200
212
  };
201
213
 
202
214
  /**
203
- * Sanitizes a string by removing potentially dangerous characters
215
+ * Sanitizes a string by removing dangerous characters
204
216
  * @param {string} input - Input to sanitize
205
217
  * @returns {string} Sanitized string
206
218
  */
@@ -209,9 +221,9 @@ const sanitizeString = (input) => {
209
221
 
210
222
  return input
211
223
  .trim()
212
- .replace(/[<>]/g, '') // Remove HTML brackets
213
- .replace(/[\x00-\x1F\x7F]/g, '') // Remove control characters
214
- .substring(0, 1000); // Limit length
224
+ .replace(/[<>]/g, '') // Remove HTML brackets
225
+ .replace(/[\x00-\x1F\x7F]/g, '') // Remove control characters
226
+ .slice(0, VALIDATION.STRING_MAX_LENGTH); // Limit length
215
227
  };
216
228
 
217
229
  /**
@@ -219,20 +231,22 @@ const sanitizeString = (input) => {
219
231
  * @param {Object} data - Data object to validate
220
232
  * @param {Object} schema - Validation schema
221
233
  * @returns {Object} Validated data
222
- * @throws {ValidationError} If any field is invalid
234
+ * @throws {ValidationError}
223
235
  */
224
236
  const validateObject = (data, schema) => {
225
237
  const result = {};
226
238
 
227
- for (const [field, validator] of Object.entries(schema)) {
239
+ for (const [field, config] of Object.entries(schema)) {
228
240
  const value = data[field];
229
241
 
230
- if (typeof validator === 'function') {
231
- result[field] = validator(value);
232
- } else if (validator.required && (value === undefined || value === null)) {
242
+ if (typeof config === 'function') {
243
+ result[field] = config(value);
244
+ } else if (config.required && (value === undefined || value === null)) {
233
245
  throw new ValidationError(`${field} is required`, field);
246
+ } else if (value !== undefined && value !== null && config.validate) {
247
+ result[field] = config.validate(value);
234
248
  } else if (value !== undefined && value !== null) {
235
- result[field] = validator.validate ? validator.validate(value) : value;
249
+ result[field] = value;
236
250
  }
237
251
  }
238
252
 
@@ -249,5 +263,5 @@ module.exports = {
249
263
  validatePrice,
250
264
  validateSymbol,
251
265
  sanitizeString,
252
- validateObject
266
+ validateObject,
253
267
  };