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.
- package/README.md +7 -6
- package/bin/cli.js +13 -7
- package/dist/algo/copy-engine.js +3 -0
- package/dist/algo/copy-engine.jsc +0 -0
- package/dist/algo/engine.js +3 -0
- package/dist/algo/engine.jsc +0 -0
- package/dist/algo/market-data-rithmic.js +3 -0
- package/dist/algo/market-data-rithmic.jsc +0 -0
- package/dist/algo/market-data.js +3 -0
- package/dist/algo/market-data.jsc +0 -0
- package/dist/algo/rithmic/connection.js +3 -0
- package/dist/algo/rithmic/connection.jsc +0 -0
- package/dist/algo/rithmic/constants.js +3 -0
- package/dist/algo/rithmic/constants.jsc +0 -0
- package/dist/algo/rithmic/index.js +3 -0
- package/dist/algo/rithmic/index.jsc +0 -0
- package/dist/algo/rithmic/market-data.js +3 -0
- package/dist/algo/rithmic/market-data.jsc +0 -0
- package/dist/algo/rithmic/pnl.js +3 -0
- package/dist/algo/rithmic/pnl.jsc +0 -0
- package/dist/algo/rithmic/pool.js +3 -0
- package/dist/algo/rithmic/pool.jsc +0 -0
- package/dist/algo/rithmic/trading.js +3 -0
- package/dist/algo/rithmic/trading.jsc +0 -0
- package/dist/algo/rithmic-decoder.js +3 -0
- package/dist/algo/rithmic-decoder.jsc +0 -0
- package/dist/algo/strategies/ultra-scalping-v2.js +3 -0
- package/dist/algo/strategies/ultra-scalping-v2.jsc +0 -0
- package/dist/algo/strategies/ultra-scalping.js +3 -0
- package/dist/algo/strategies/ultra-scalping.jsc +0 -0
- package/dist/algo/trading-api-rithmic.js +3 -0
- package/dist/algo/trading-api-rithmic.jsc +0 -0
- package/dist/algo/trading-api.js +3 -0
- package/dist/algo/trading-api.jsc +0 -0
- package/dist/algo/utils/smart-logger.js +3 -0
- package/dist/algo/utils/smart-logger.jsc +0 -0
- package/dist/algo/utils/smart-logs.js +3 -0
- package/dist/algo/utils/smart-logs.jsc +0 -0
- package/package.json +33 -10
- package/protos/rithmic/account_pnl_position_update.proto +59 -0
- package/protos/rithmic/base.proto +7 -0
- package/protos/rithmic/best_bid_offer.proto +39 -0
- package/protos/rithmic/exchange_order_notification.proto +140 -0
- package/protos/rithmic/instrument_pnl_position_update.proto +50 -0
- package/protos/rithmic/last_trade.proto +53 -0
- package/protos/rithmic/request_account_list.proto +20 -0
- package/protos/rithmic/request_cancel_all_orders.proto +15 -0
- package/protos/rithmic/request_front_month_contract.proto +10 -0
- package/protos/rithmic/request_heartbeat.proto +13 -0
- package/protos/rithmic/request_login.proto +28 -0
- package/protos/rithmic/request_login_info.proto +10 -0
- package/protos/rithmic/request_logout.proto +10 -0
- package/protos/rithmic/request_market_data_update.proto +42 -0
- package/protos/rithmic/request_new_order.proto +84 -0
- package/protos/rithmic/request_pnl_position_snapshot.proto +14 -0
- package/protos/rithmic/request_pnl_position_updates.proto +20 -0
- package/protos/rithmic/request_product_codes.proto +9 -0
- package/protos/rithmic/request_rithmic_system_info.proto +8 -0
- package/protos/rithmic/request_show_order_history.proto +16 -0
- package/protos/rithmic/request_show_order_history_dates.proto +10 -0
- package/protos/rithmic/request_show_order_history_summary.proto +14 -0
- package/protos/rithmic/request_show_orders.proto +14 -0
- package/protos/rithmic/request_subscribe_for_order_updates.proto +14 -0
- package/protos/rithmic/request_tick_bar_replay.proto +48 -0
- package/protos/rithmic/request_trade_routes.proto +11 -0
- package/protos/rithmic/response_account_list.proto +18 -0
- package/protos/rithmic/response_front_month_contract.proto +13 -0
- package/protos/rithmic/response_heartbeat.proto +14 -0
- package/protos/rithmic/response_login.proto +18 -0
- package/protos/rithmic/response_login_info.proto +24 -0
- package/protos/rithmic/response_logout.proto +11 -0
- package/protos/rithmic/response_market_data_update.proto +9 -0
- package/protos/rithmic/response_new_order.proto +18 -0
- package/protos/rithmic/response_pnl_position_snapshot.proto +11 -0
- package/protos/rithmic/response_pnl_position_updates.proto +11 -0
- package/protos/rithmic/response_product_codes.proto +12 -0
- package/protos/rithmic/response_rithmic_system_info.proto +12 -0
- package/protos/rithmic/response_show_order_history.proto +11 -0
- package/protos/rithmic/response_show_order_history_dates.proto +13 -0
- package/protos/rithmic/response_show_order_history_summary.proto +11 -0
- package/protos/rithmic/response_show_orders.proto +11 -0
- package/protos/rithmic/response_subscribe_for_order_updates.proto +11 -0
- package/protos/rithmic/response_tick_bar_replay.proto +40 -0
- package/protos/rithmic/response_trade_routes.proto +19 -0
- package/protos/rithmic/rithmic_order_notification.proto +124 -0
- package/src/app.js +136 -89
- package/src/config/index.js +27 -8
- package/src/config/settings.js +155 -0
- package/src/pages/accounts.js +2 -3
- package/src/pages/algo/copy-trading.js +293 -200
- package/src/pages/algo/one-account.js +1 -1
- package/src/security/encryption.js +81 -46
- package/src/security/index.js +12 -8
- package/src/security/rateLimit.js +68 -65
- package/src/security/validation.js +93 -79
- package/src/services/hqx-server.js +538 -206
- package/src/services/projectx/index.js +327 -204
- package/src/services/rithmic/index.js +288 -285
- package/src/services/session.js +184 -114
- package/src/services/tradovate/index.js +286 -297
- package/src/ui/index.js +53 -1
- package/src/utils/http.js +236 -0
- package/src/utils/index.js +11 -2
- package/src/utils/logger.js +64 -33
- 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
|
|
19
|
-
* @param {
|
|
20
|
-
* @
|
|
21
|
-
* @
|
|
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
|
|
24
|
-
if (!
|
|
25
|
-
throw new ValidationError(
|
|
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 =
|
|
36
|
+
const trimmed = value.trim();
|
|
29
37
|
|
|
30
|
-
if (trimmed.length <
|
|
31
|
-
throw new ValidationError(
|
|
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 >
|
|
35
|
-
throw new ValidationError(
|
|
42
|
+
if (trimmed.length > max) {
|
|
43
|
+
throw new ValidationError(`${field} must be less than ${max} characters`, field);
|
|
36
44
|
}
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
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] -
|
|
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}
|
|
72
|
+
* @throws {ValidationError}
|
|
54
73
|
*/
|
|
55
74
|
const validatePassword = (password, options = {}) => {
|
|
56
|
-
const {
|
|
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 >
|
|
67
|
-
throw new ValidationError(
|
|
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 {
|
|
81
|
-
* @throws {ValidationError}
|
|
112
|
+
* @returns {string} Validated API key
|
|
113
|
+
* @throws {ValidationError}
|
|
82
114
|
*/
|
|
83
|
-
const validateApiKey = (apiKey) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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}
|
|
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 >
|
|
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}
|
|
147
|
+
* @throws {ValidationError}
|
|
134
148
|
*/
|
|
135
149
|
const validateQuantity = (quantity, options = {}) => {
|
|
136
|
-
const { min =
|
|
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}
|
|
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 <
|
|
181
|
+
if (p < VALIDATION.PRICE_MIN) {
|
|
168
182
|
throw new ValidationError('Price cannot be negative', 'price');
|
|
169
183
|
}
|
|
170
184
|
|
|
171
|
-
if (p >
|
|
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}
|
|
196
|
+
* @throws {ValidationError}
|
|
183
197
|
*/
|
|
184
198
|
const validateSymbol = (symbol) => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
199
|
+
const validated = validateString(symbol, {
|
|
200
|
+
field: 'Symbol',
|
|
201
|
+
min: VALIDATION.SYMBOL_MIN,
|
|
202
|
+
max: VALIDATION.SYMBOL_MAX,
|
|
203
|
+
});
|
|
190
204
|
|
|
191
|
-
|
|
192
|
-
throw new ValidationError('Invalid symbol length', 'symbol');
|
|
193
|
-
}
|
|
205
|
+
const upper = validated.toUpperCase();
|
|
194
206
|
|
|
195
|
-
if (
|
|
207
|
+
if (!VALIDATION.SYMBOL_PATTERN.test(upper)) {
|
|
196
208
|
throw new ValidationError('Symbol contains invalid characters', 'symbol');
|
|
197
209
|
}
|
|
198
210
|
|
|
199
|
-
return
|
|
211
|
+
return upper;
|
|
200
212
|
};
|
|
201
213
|
|
|
202
214
|
/**
|
|
203
|
-
* Sanitizes a string by removing
|
|
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, '')
|
|
213
|
-
.replace(/[\x00-\x1F\x7F]/g, '')
|
|
214
|
-
.
|
|
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}
|
|
234
|
+
* @throws {ValidationError}
|
|
223
235
|
*/
|
|
224
236
|
const validateObject = (data, schema) => {
|
|
225
237
|
const result = {};
|
|
226
238
|
|
|
227
|
-
for (const [field,
|
|
239
|
+
for (const [field, config] of Object.entries(schema)) {
|
|
228
240
|
const value = data[field];
|
|
229
241
|
|
|
230
|
-
if (typeof
|
|
231
|
-
result[field] =
|
|
232
|
-
} else if (
|
|
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] =
|
|
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
|
};
|