hedgequantx 2.4.7 → 2.4.8

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.4.7",
3
+ "version": "2.4.8",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -9,10 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "start": "node bin/cli.js",
12
- "dev": "node bin/cli.js",
13
- "build": "node scripts/build.js",
14
- "publish:safe": "node scripts/publish.js",
15
- "prepublishOnly": "npm run build"
12
+ "dev": "node bin/cli.js"
16
13
  },
17
14
  "keywords": [
18
15
  "trading",
@@ -40,11 +37,11 @@
40
37
  },
41
38
  "files": [
42
39
  "bin/",
43
- "dist/lib/",
44
40
  "protos/",
45
41
  "src/app.js",
46
42
  "src/api/",
47
43
  "src/config/",
44
+ "src/lib/",
48
45
  "src/menus/",
49
46
  "src/pages/",
50
47
  "src/security/",
@@ -0,0 +1,286 @@
1
+ /**
2
+ * =============================================================================
3
+ * MARKET DATA FEED - SignalR Real-Time Data
4
+ * =============================================================================
5
+ * Connects to ProjectX Gateway RTC for real-time market data
6
+ *
7
+ * Events emitted:
8
+ * - tick: Quote/trade updates (price, bid, ask, volume)
9
+ * - quote: Quote updates only
10
+ * - trade: Trade executions only
11
+ * - depth: DOM/Level 2 updates
12
+ * - connected: Connection established
13
+ * - disconnected: Connection lost
14
+ * - error: Connection error
15
+ *
16
+ * SOURCE: Based on ProjectX Gateway RTC API
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ const EventEmitter = require('events');
22
+ const { HubConnectionBuilder, HttpTransportType, LogLevel } = require('@microsoft/signalr');
23
+
24
+ // Inline PROPFIRMS config for RTC URLs (standalone module)
25
+ const PROPFIRMS = {
26
+ topstep: { gatewayApi: 'api.topstepx.com' },
27
+ alpha_futures: { gatewayApi: 'api.alphafutures.projectx.com' },
28
+ tickticktrader: { gatewayApi: 'api.tickticktrader.projectx.com' },
29
+ bulenox: { gatewayApi: 'api.bulenox.projectx.com' },
30
+ tradeday: { gatewayApi: 'api.tradeday.projectx.com' },
31
+ blusky: { gatewayApi: 'api.blusky.projectx.com' },
32
+ goat_futures: { gatewayApi: 'api.goatfutures.projectx.com' },
33
+ futures_desk: { gatewayApi: 'api.thefuturesdesk.projectx.com' },
34
+ daytraders: { gatewayApi: 'api.daytraders.projectx.com' },
35
+ e8_futures: { gatewayApi: 'api.e8futures.projectx.com' },
36
+ blue_guardian: { gatewayApi: 'api.blueguardianfutures.projectx.com' },
37
+ futures_elite: { gatewayApi: 'api.futureselite.projectx.com' },
38
+ fxify: { gatewayApi: 'api.fxify.projectx.com' },
39
+ hola_prime: { gatewayApi: 'api.holaprime.projectx.com' },
40
+ top_one_futures: { gatewayApi: 'api.toponefutures.projectx.com' },
41
+ funding_futures: { gatewayApi: 'api.fundingfutures.projectx.com' },
42
+ tx3_funding: { gatewayApi: 'api.tx3funding.projectx.com' },
43
+ lucid_trading: { gatewayApi: 'api.lucidtrading.projectx.com' },
44
+ tradeify: { gatewayApi: 'api.tradeify.projectx.com' }
45
+ };
46
+
47
+ // =============================================================================
48
+ // MARKET DATA FEED CLASS
49
+ // =============================================================================
50
+
51
+ class MarketDataFeed extends EventEmitter {
52
+ constructor(options = {}) {
53
+ super();
54
+
55
+ this.propfirmKey = (options.propfirm || 'topstep').toLowerCase().replace(/\s+/g, '_');
56
+ this.connection = null;
57
+ this.connected = false;
58
+ this.subscriptions = new Set();
59
+ this.reconnectAttempts = 0;
60
+ this.maxReconnectAttempts = 5;
61
+ }
62
+
63
+ /**
64
+ * Get market hub URL for propfirm
65
+ */
66
+ _getMarketHubUrl(propfirmKey) {
67
+ const propfirm = PROPFIRMS[propfirmKey] || PROPFIRMS.topstep;
68
+
69
+ if (propfirm.rtcApi) {
70
+ return `https://${propfirm.rtcApi}/hubs/market`;
71
+ }
72
+
73
+ if (propfirm.gatewayApi) {
74
+ const rtcHost = propfirm.gatewayApi.replace('gateway-api', 'gateway-rtc');
75
+ return `https://${rtcHost}/hubs/market`;
76
+ }
77
+
78
+ return 'https://gateway-rtc-demo.s2f.projectx.com/hubs/market';
79
+ }
80
+
81
+ /**
82
+ * Connect to market data hub
83
+ */
84
+ async connect(token, propfirmKey, contractId = null) {
85
+ if (this.connected) return;
86
+
87
+ this.propfirmKey = propfirmKey || this.propfirmKey;
88
+ const hubUrl = this._getMarketHubUrl(this.propfirmKey);
89
+
90
+ try {
91
+ this.connection = new HubConnectionBuilder()
92
+ .withUrl(hubUrl, {
93
+ skipNegotiation: true,
94
+ transport: HttpTransportType.WebSockets,
95
+ accessTokenFactory: () => token,
96
+ timeout: 30000,
97
+ })
98
+ .withAutomaticReconnect({
99
+ nextRetryDelayInMilliseconds: (ctx) => {
100
+ if (ctx.previousRetryCount >= this.maxReconnectAttempts) return null;
101
+ return Math.min(1000 * Math.pow(2, ctx.previousRetryCount), 30000);
102
+ }
103
+ })
104
+ .configureLogging(LogLevel.Warning)
105
+ .build();
106
+
107
+ this._setupEventHandlers();
108
+ await this.connection.start();
109
+
110
+ this.connected = true;
111
+ this.reconnectAttempts = 0;
112
+ this.emit('connected');
113
+
114
+ if (contractId) {
115
+ await this.subscribe(null, contractId);
116
+ }
117
+ } catch (error) {
118
+ this.emit('error', error);
119
+ throw error;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Setup SignalR event handlers
125
+ */
126
+ _setupEventHandlers() {
127
+ if (!this.connection) return;
128
+
129
+ // Quote updates
130
+ this.connection.on('GatewayQuote', (quote) => {
131
+ const tick = {
132
+ type: 'quote',
133
+ contractId: quote.symbol || quote.symbolId,
134
+ symbol: quote.symbolName || quote.symbol,
135
+ price: quote.lastPrice,
136
+ bid: quote.bestBid,
137
+ ask: quote.bestAsk,
138
+ change: quote.change,
139
+ changePercent: quote.changePercent,
140
+ open: quote.open,
141
+ high: quote.high,
142
+ low: quote.low,
143
+ volume: quote.volume,
144
+ timestamp: quote.timestamp ? new Date(quote.timestamp).getTime() : Date.now()
145
+ };
146
+ this.emit('tick', tick);
147
+ this.emit('quote', tick);
148
+ });
149
+
150
+ // Trade executions
151
+ this.connection.on('GatewayTrade', (trade) => {
152
+ const tick = {
153
+ type: 'trade',
154
+ contractId: trade.symbolId,
155
+ price: trade.price,
156
+ volume: trade.volume,
157
+ side: trade.type === 0 ? 'buy' : 'sell',
158
+ lastTradeSide: trade.type === 0 ? 'buy' : 'sell',
159
+ timestamp: trade.timestamp ? new Date(trade.timestamp).getTime() : Date.now()
160
+ };
161
+ this.emit('tick', tick);
162
+ this.emit('trade', tick);
163
+ });
164
+
165
+ // DOM updates
166
+ this.connection.on('GatewayDepth', (depth) => {
167
+ const domUpdate = {
168
+ type: 'depth',
169
+ price: depth.price,
170
+ volume: depth.volume,
171
+ currentVolume: depth.currentVolume,
172
+ side: depth.type === 0 ? 'bid' : 'ask',
173
+ timestamp: depth.timestamp ? new Date(depth.timestamp).getTime() : Date.now()
174
+ };
175
+ this.emit('depth', domUpdate);
176
+ this.emit('dom', domUpdate);
177
+ });
178
+
179
+ // Connection state
180
+ this.connection.onreconnecting((error) => {
181
+ this.connected = false;
182
+ this.emit('reconnecting', error);
183
+ });
184
+
185
+ this.connection.onreconnected((connectionId) => {
186
+ this.connected = true;
187
+ this.reconnectAttempts = 0;
188
+ this.emit('reconnected', connectionId);
189
+ this._resubscribeAll();
190
+ });
191
+
192
+ this.connection.onclose((error) => {
193
+ this.connected = false;
194
+ this.emit('disconnected', error);
195
+ });
196
+ }
197
+
198
+ /**
199
+ * Subscribe to contract market data
200
+ */
201
+ async subscribe(symbol, contractId) {
202
+ if (!this.connection || !this.connected) {
203
+ throw new Error('Not connected');
204
+ }
205
+
206
+ const id = contractId || symbol;
207
+
208
+ try {
209
+ await this.connection.invoke('SubscribeContractQuotes', id);
210
+ await this.connection.invoke('SubscribeContractTrades', id);
211
+ await this.connection.invoke('SubscribeContractMarketDepth', id);
212
+ this.subscriptions.add(id);
213
+ this.emit('subscribed', { symbol, contractId: id });
214
+ } catch (error) {
215
+ this.emit('error', new Error(`Subscribe failed: ${error.message}`));
216
+ throw error;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Unsubscribe from contract
222
+ */
223
+ async unsubscribe(contractId) {
224
+ if (!this.connection || !this.connected) return;
225
+
226
+ try {
227
+ await this.connection.invoke('UnsubscribeContractQuotes', contractId);
228
+ await this.connection.invoke('UnsubscribeContractTrades', contractId);
229
+ await this.connection.invoke('UnsubscribeContractMarketDepth', contractId);
230
+ this.subscriptions.delete(contractId);
231
+ this.emit('unsubscribed', { contractId });
232
+ } catch (error) {
233
+ // Silently handle
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Resubscribe after reconnect
239
+ */
240
+ async _resubscribeAll() {
241
+ for (const contractId of this.subscriptions) {
242
+ try {
243
+ await this.connection.invoke('SubscribeContractQuotes', contractId);
244
+ await this.connection.invoke('SubscribeContractTrades', contractId);
245
+ await this.connection.invoke('SubscribeContractMarketDepth', contractId);
246
+ } catch (error) {
247
+ // Continue
248
+ }
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Disconnect
254
+ */
255
+ async disconnect() {
256
+ if (this.connection) {
257
+ try {
258
+ for (const contractId of this.subscriptions) {
259
+ await this.unsubscribe(contractId);
260
+ }
261
+ await this.connection.stop();
262
+ } catch (error) {
263
+ // Ignore
264
+ }
265
+ this.connection = null;
266
+ this.connected = false;
267
+ this.subscriptions.clear();
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Check connection status
273
+ */
274
+ isConnected() {
275
+ return this.connected && this.connection?.state === 'Connected';
276
+ }
277
+
278
+ /**
279
+ * Get active subscriptions
280
+ */
281
+ getSubscriptions() {
282
+ return Array.from(this.subscriptions);
283
+ }
284
+ }
285
+
286
+ module.exports = { MarketDataFeed };
@@ -0,0 +1,521 @@
1
+ /**
2
+ * =============================================================================
3
+ * HQX ULTRA SCALPING STRATEGY
4
+ * =============================================================================
5
+ * 6 Mathematical Models with 4-Layer Trailing Stop System
6
+ *
7
+ * BACKTEST RESULTS (162 tests, V4):
8
+ * - Net P&L: $195,272.52
9
+ * - Win Rate: 86.3%
10
+ * - Profit Factor: 34.44
11
+ * - Sharpe: 1.29
12
+ * - Tests Passed: 150/162 (92.6%)
13
+ *
14
+ * MATHEMATICAL MODELS:
15
+ * 1. Z-Score Mean Reversion (Entry: |Z| > threshold, Exit: |Z| < 0.5)
16
+ * 2. VPIN (Volume-Synchronized Probability of Informed Trading)
17
+ * 3. Kyle's Lambda (Price Impact / Liquidity Measurement)
18
+ * 4. Kalman Filter (Signal Extraction from Noise)
19
+ * 5. Volatility Regime Detection (Low/Normal/High adaptive)
20
+ * 6. Order Flow Imbalance (OFI) - Directional Bias Confirmation
21
+ *
22
+ * KEY PARAMETERS:
23
+ * - Stop: 8 ticks = $40
24
+ * - Target: 16 ticks = $80
25
+ * - R:R = 1:2
26
+ * - Trailing: 50% profit lock
27
+ *
28
+ * SOURCE: /root/HQX-Dev/hqx_tg/src/algo/strategy/hqx-ultra-scalping.strategy.ts
29
+ */
30
+
31
+ 'use strict';
32
+
33
+ const EventEmitter = require('events');
34
+ const { v4: uuidv4 } = require('uuid');
35
+
36
+ // =============================================================================
37
+ // CONSTANTS
38
+ // =============================================================================
39
+
40
+ const OrderSide = { BID: 'BID', ASK: 'ASK' };
41
+ const SignalStrength = { WEAK: 'WEAK', MODERATE: 'MODERATE', STRONG: 'STRONG', VERY_STRONG: 'VERY_STRONG' };
42
+
43
+ // =============================================================================
44
+ // HELPER: Extract base symbol from contractId
45
+ // =============================================================================
46
+ function extractBaseSymbol(contractId) {
47
+ // CON.F.US.ENQ.H25 -> NQ, CON.F.US.EP.H25 -> ES
48
+ const mapping = {
49
+ 'ENQ': 'NQ', 'EP': 'ES', 'EMD': 'EMD', 'RTY': 'RTY',
50
+ 'MNQ': 'MNQ', 'MES': 'MES', 'M2K': 'M2K', 'MYM': 'MYM',
51
+ 'NKD': 'NKD', 'GC': 'GC', 'SI': 'SI', 'CL': 'CL', 'YM': 'YM'
52
+ };
53
+
54
+ if (!contractId) return 'UNKNOWN';
55
+ const parts = contractId.split('.');
56
+ if (parts.length >= 4) {
57
+ const symbol = parts[3];
58
+ return mapping[symbol] || symbol;
59
+ }
60
+ return contractId;
61
+ }
62
+
63
+ // =============================================================================
64
+ // HQX ULTRA SCALPING STRATEGY CLASS
65
+ // =============================================================================
66
+
67
+ class HQXUltraScalpingStrategy extends EventEmitter {
68
+ constructor() {
69
+ super();
70
+
71
+ this.tickSize = 0.25;
72
+ this.tickValue = 5.0;
73
+
74
+ // === Model Parameters (from V4 backtest) ===
75
+ this.zscoreEntryThreshold = 1.5; // Adaptive per regime
76
+ this.zscoreExitThreshold = 0.5;
77
+ this.vpinWindow = 50;
78
+ this.vpinToxicThreshold = 0.7;
79
+ this.kalmanProcessNoise = 0.01;
80
+ this.kalmanMeasurementNoise = 0.1;
81
+ this.volatilityLookback = 100;
82
+ this.ofiLookback = 20;
83
+
84
+ // === Trade Parameters (from V4 backtest) ===
85
+ this.baseStopTicks = 8; // $40
86
+ this.baseTargetTicks = 16; // $80
87
+ this.breakevenTicks = 4; // Move to BE at +4 ticks
88
+ this.profitLockPct = 0.5; // Lock 50% of profit
89
+
90
+ // === State Storage ===
91
+ this.barHistory = new Map();
92
+ this.kalmanStates = new Map();
93
+ this.priceBuffer = new Map();
94
+ this.volumeBuffer = new Map();
95
+ this.tradesBuffer = new Map();
96
+ this.atrHistory = new Map();
97
+
98
+ // === Tick aggregation ===
99
+ this.tickBuffer = new Map();
100
+ this.lastBarTime = new Map();
101
+ this.barIntervalMs = 5000; // 5-second bars
102
+
103
+ // === Performance Tracking ===
104
+ this.recentTrades = [];
105
+ this.winStreak = 0;
106
+ this.lossStreak = 0;
107
+ }
108
+
109
+ /**
110
+ * Initialize strategy for a contract
111
+ */
112
+ initialize(contractId, tickSize = 0.25, tickValue = 5.0) {
113
+ this.tickSize = tickSize;
114
+ this.tickValue = tickValue;
115
+ this.barHistory.set(contractId, []);
116
+ this.priceBuffer.set(contractId, []);
117
+ this.volumeBuffer.set(contractId, []);
118
+ this.tradesBuffer.set(contractId, []);
119
+ this.atrHistory.set(contractId, []);
120
+ this.tickBuffer.set(contractId, []);
121
+ this.lastBarTime.set(contractId, 0);
122
+ this.kalmanStates.set(contractId, { estimate: 0, errorCovariance: 1.0 });
123
+ }
124
+
125
+ /**
126
+ * Process a tick - aggregates into bars then runs strategy
127
+ */
128
+ processTick(tick) {
129
+ const contractId = tick.contractId;
130
+
131
+ if (!this.barHistory.has(contractId)) {
132
+ this.initialize(contractId);
133
+ }
134
+
135
+ // Add tick to buffer
136
+ let ticks = this.tickBuffer.get(contractId);
137
+ ticks.push(tick);
138
+
139
+ // Check if we should form a new bar
140
+ const now = Date.now();
141
+ const lastBar = this.lastBarTime.get(contractId);
142
+
143
+ if (now - lastBar >= this.barIntervalMs && ticks.length > 0) {
144
+ const bar = this._aggregateTicksToBar(ticks, now);
145
+ this.tickBuffer.set(contractId, []);
146
+ this.lastBarTime.set(contractId, now);
147
+
148
+ if (bar) {
149
+ const signal = this.processBar(contractId, bar);
150
+ if (signal) {
151
+ this.emit('signal', signal);
152
+ return signal;
153
+ }
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+
159
+ /**
160
+ * Aggregate ticks into a bar
161
+ */
162
+ _aggregateTicksToBar(ticks, timestamp) {
163
+ if (ticks.length === 0) return null;
164
+
165
+ const prices = ticks.map(t => t.price).filter(p => p != null);
166
+ if (prices.length === 0) return null;
167
+
168
+ let buyVol = 0, sellVol = 0;
169
+ for (let i = 1; i < ticks.length; i++) {
170
+ const vol = ticks[i].volume || 1;
171
+ if (ticks[i].price > ticks[i-1].price) buyVol += vol;
172
+ else if (ticks[i].price < ticks[i-1].price) sellVol += vol;
173
+ else { buyVol += vol / 2; sellVol += vol / 2; }
174
+ }
175
+
176
+ return {
177
+ timestamp,
178
+ open: prices[0],
179
+ high: Math.max(...prices),
180
+ low: Math.min(...prices),
181
+ close: prices[prices.length - 1],
182
+ volume: ticks.reduce((sum, t) => sum + (t.volume || 1), 0),
183
+ delta: buyVol - sellVol,
184
+ tickCount: ticks.length
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Process a new bar and potentially generate signal
190
+ */
191
+ processBar(contractId, bar) {
192
+ let bars = this.barHistory.get(contractId);
193
+ if (!bars) {
194
+ this.initialize(contractId);
195
+ bars = this.barHistory.get(contractId);
196
+ }
197
+
198
+ bars.push(bar);
199
+ if (bars.length > 500) bars.shift();
200
+
201
+ // Update price buffer
202
+ const prices = this.priceBuffer.get(contractId);
203
+ prices.push(bar.close);
204
+ if (prices.length > 200) prices.shift();
205
+
206
+ // Update volume buffer
207
+ const volumes = this.volumeBuffer.get(contractId);
208
+ const barRange = bar.high - bar.low;
209
+ let buyVol = bar.volume * 0.5;
210
+ let sellVol = bar.volume * 0.5;
211
+ if (barRange > 0) {
212
+ const closePosition = (bar.close - bar.low) / barRange;
213
+ buyVol = bar.volume * closePosition;
214
+ sellVol = bar.volume * (1 - closePosition);
215
+ }
216
+ volumes.push({ buy: buyVol, sell: sellVol });
217
+ if (volumes.length > 100) volumes.shift();
218
+
219
+ // Need minimum data
220
+ if (bars.length < 50) return null;
221
+
222
+ // === 6 MODELS ===
223
+ const zscore = this._computeZScore(prices);
224
+ const vpin = this._computeVPIN(volumes);
225
+ const kyleLambda = this._computeKyleLambda(bars);
226
+ const kalmanEstimate = this._applyKalmanFilter(contractId, bar.close);
227
+ const { regime, params } = this._detectVolatilityRegime(contractId, bars);
228
+ const ofi = this._computeOrderFlowImbalance(bars);
229
+
230
+ // === SIGNAL GENERATION ===
231
+ return this._generateSignal(contractId, bar.close, zscore, vpin, kyleLambda, kalmanEstimate, regime, params, ofi, bars);
232
+ }
233
+
234
+ // ===========================================================================
235
+ // MODEL 1: Z-SCORE MEAN REVERSION
236
+ // ===========================================================================
237
+ _computeZScore(prices, window = 50) {
238
+ if (prices.length < window) return 0;
239
+ const recentPrices = prices.slice(-window);
240
+ const mean = recentPrices.reduce((a, b) => a + b, 0) / window;
241
+ const variance = recentPrices.reduce((sum, p) => sum + Math.pow(p - mean, 2), 0) / window;
242
+ const std = Math.sqrt(variance);
243
+ if (std < 0.0001) return 0;
244
+ return (prices[prices.length - 1] - mean) / std;
245
+ }
246
+
247
+ // ===========================================================================
248
+ // MODEL 2: VPIN
249
+ // ===========================================================================
250
+ _computeVPIN(volumes) {
251
+ if (volumes.length < this.vpinWindow) return 0.5;
252
+ const recent = volumes.slice(-this.vpinWindow);
253
+ let totalBuy = 0, totalSell = 0;
254
+ for (const v of recent) { totalBuy += v.buy; totalSell += v.sell; }
255
+ const total = totalBuy + totalSell;
256
+ if (total < 1) return 0.5;
257
+ return Math.abs(totalBuy - totalSell) / total;
258
+ }
259
+
260
+ // ===========================================================================
261
+ // MODEL 3: KYLE'S LAMBDA
262
+ // ===========================================================================
263
+ _computeKyleLambda(bars) {
264
+ if (bars.length < 20) return 0;
265
+ const recent = bars.slice(-20);
266
+ const priceChanges = [], vols = [];
267
+ for (let i = 1; i < recent.length; i++) {
268
+ priceChanges.push(recent[i].close - recent[i - 1].close);
269
+ vols.push(recent[i].volume);
270
+ }
271
+ const meanP = priceChanges.reduce((a, b) => a + b, 0) / priceChanges.length;
272
+ const meanV = vols.reduce((a, b) => a + b, 0) / vols.length;
273
+ let cov = 0, varV = 0;
274
+ for (let i = 0; i < priceChanges.length; i++) {
275
+ cov += (priceChanges[i] - meanP) * (vols[i] - meanV);
276
+ varV += Math.pow(vols[i] - meanV, 2);
277
+ }
278
+ cov /= priceChanges.length;
279
+ varV /= priceChanges.length;
280
+ if (varV < 0.0001) return 0;
281
+ return Math.abs(cov / varV);
282
+ }
283
+
284
+ // ===========================================================================
285
+ // MODEL 4: KALMAN FILTER
286
+ // ===========================================================================
287
+ _applyKalmanFilter(contractId, measurement) {
288
+ let state = this.kalmanStates.get(contractId);
289
+ if (!state) {
290
+ state = { estimate: measurement, errorCovariance: 1.0 };
291
+ this.kalmanStates.set(contractId, state);
292
+ return measurement;
293
+ }
294
+ const predictedEstimate = state.estimate;
295
+ const predictedCovariance = state.errorCovariance + this.kalmanProcessNoise;
296
+ const kalmanGain = predictedCovariance / (predictedCovariance + this.kalmanMeasurementNoise);
297
+ state.estimate = predictedEstimate + kalmanGain * (measurement - predictedEstimate);
298
+ state.errorCovariance = (1 - kalmanGain) * predictedCovariance;
299
+ return state.estimate;
300
+ }
301
+
302
+ // ===========================================================================
303
+ // MODEL 5: VOLATILITY REGIME
304
+ // ===========================================================================
305
+ _detectVolatilityRegime(contractId, bars) {
306
+ const atr = this._calculateATR(bars);
307
+ let atrHist = this.atrHistory.get(contractId);
308
+ if (!atrHist) { atrHist = []; this.atrHistory.set(contractId, atrHist); }
309
+ atrHist.push(atr);
310
+ if (atrHist.length > 500) atrHist.shift();
311
+
312
+ let atrPercentile = 0.5;
313
+ if (atrHist.length >= 20) {
314
+ atrPercentile = atrHist.filter(a => a <= atr).length / atrHist.length;
315
+ }
316
+
317
+ let regime, params;
318
+ if (atrPercentile < 0.25) {
319
+ regime = 'low';
320
+ params = { stopMultiplier: 0.8, targetMultiplier: 0.9, zscoreThreshold: 1.2, confidenceBonus: 0.05 };
321
+ } else if (atrPercentile < 0.75) {
322
+ regime = 'normal';
323
+ params = { stopMultiplier: 1.0, targetMultiplier: 1.0, zscoreThreshold: 1.5, confidenceBonus: 0.0 };
324
+ } else {
325
+ regime = 'high';
326
+ params = { stopMultiplier: 1.3, targetMultiplier: 1.2, zscoreThreshold: 2.0, confidenceBonus: -0.05 };
327
+ }
328
+ return { regime, params };
329
+ }
330
+
331
+ _calculateATR(bars, period = 14) {
332
+ if (bars.length < period + 1) return 2.5;
333
+ const trValues = [];
334
+ for (let i = bars.length - period; i < bars.length; i++) {
335
+ const bar = bars[i];
336
+ const prevClose = bars[i - 1].close;
337
+ const tr = Math.max(bar.high - bar.low, Math.abs(bar.high - prevClose), Math.abs(bar.low - prevClose));
338
+ trValues.push(tr);
339
+ }
340
+ return trValues.reduce((a, b) => a + b, 0) / trValues.length;
341
+ }
342
+
343
+ // ===========================================================================
344
+ // MODEL 6: ORDER FLOW IMBALANCE
345
+ // ===========================================================================
346
+ _computeOrderFlowImbalance(bars) {
347
+ if (bars.length < this.ofiLookback) return 0;
348
+ const recent = bars.slice(-this.ofiLookback);
349
+ let buyPressure = 0, sellPressure = 0;
350
+ for (const bar of recent) {
351
+ const range = bar.high - bar.low;
352
+ if (range > 0) {
353
+ const closePos = (bar.close - bar.low) / range;
354
+ buyPressure += closePos * bar.volume;
355
+ sellPressure += (1 - closePos) * bar.volume;
356
+ }
357
+ }
358
+ const total = buyPressure + sellPressure;
359
+ if (total < 1) return 0;
360
+ return (buyPressure - sellPressure) / total;
361
+ }
362
+
363
+ // ===========================================================================
364
+ // SIGNAL GENERATION
365
+ // ===========================================================================
366
+ _generateSignal(contractId, currentPrice, zscore, vpin, kyleLambda, kalmanEstimate, regime, volParams, ofi, bars) {
367
+ const absZscore = Math.abs(zscore);
368
+ if (absZscore < volParams.zscoreThreshold) return null;
369
+ if (vpin > this.vpinToxicThreshold) return null;
370
+
371
+ let direction;
372
+ if (zscore < -volParams.zscoreThreshold) direction = 'long';
373
+ else if (zscore > volParams.zscoreThreshold) direction = 'short';
374
+ else return null;
375
+
376
+ const ofiConfirms = (direction === 'long' && ofi > 0.1) || (direction === 'short' && ofi < -0.1);
377
+ const kalmanDiff = currentPrice - kalmanEstimate;
378
+ const kalmanConfirms = (direction === 'long' && kalmanDiff < 0) || (direction === 'short' && kalmanDiff > 0);
379
+
380
+ const scores = {
381
+ zscore: Math.min(1.0, absZscore / 4.0),
382
+ vpin: 1.0 - vpin,
383
+ kyleLambda: kyleLambda > 0.001 ? 0.5 : 0.8,
384
+ kalman: kalmanConfirms ? 0.8 : 0.4,
385
+ volatility: regime === 'normal' ? 0.8 : regime === 'low' ? 0.7 : 0.6,
386
+ ofi: ofiConfirms ? 0.9 : 0.5,
387
+ composite: 0
388
+ };
389
+
390
+ scores.composite = scores.zscore * 0.30 + scores.vpin * 0.15 + scores.kyleLambda * 0.10 +
391
+ scores.kalman * 0.15 + scores.volatility * 0.10 + scores.ofi * 0.20;
392
+
393
+ const confidence = Math.min(1.0, scores.composite + volParams.confidenceBonus);
394
+ if (confidence < 0.55) return null;
395
+
396
+ const stopTicks = Math.round(this.baseStopTicks * volParams.stopMultiplier);
397
+ const targetTicks = Math.round(this.baseTargetTicks * volParams.targetMultiplier);
398
+ const actualStopTicks = Math.max(6, Math.min(12, stopTicks));
399
+ const actualTargetTicks = Math.max(actualStopTicks * 1.5, Math.min(24, targetTicks));
400
+
401
+ let stopLoss, takeProfit, beBreakeven, profitLockLevel;
402
+ if (direction === 'long') {
403
+ stopLoss = currentPrice - actualStopTicks * this.tickSize;
404
+ takeProfit = currentPrice + actualTargetTicks * this.tickSize;
405
+ beBreakeven = currentPrice + this.breakevenTicks * this.tickSize;
406
+ profitLockLevel = currentPrice + (actualTargetTicks * this.profitLockPct) * this.tickSize;
407
+ } else {
408
+ stopLoss = currentPrice + actualStopTicks * this.tickSize;
409
+ takeProfit = currentPrice - actualTargetTicks * this.tickSize;
410
+ beBreakeven = currentPrice - this.breakevenTicks * this.tickSize;
411
+ profitLockLevel = currentPrice - (actualTargetTicks * this.profitLockPct) * this.tickSize;
412
+ }
413
+
414
+ const riskReward = actualTargetTicks / actualStopTicks;
415
+ const trailTriggerTicks = Math.round(actualTargetTicks * 0.5);
416
+ const trailDistanceTicks = Math.round(actualStopTicks * 0.4);
417
+
418
+ let strength = SignalStrength.MODERATE;
419
+ if (confidence >= 0.85) strength = SignalStrength.VERY_STRONG;
420
+ else if (confidence >= 0.75) strength = SignalStrength.STRONG;
421
+ else if (confidence < 0.60) strength = SignalStrength.WEAK;
422
+
423
+ const winProb = 0.5 + (confidence - 0.5) * 0.4;
424
+ const edge = winProb * Math.abs(takeProfit - currentPrice) - (1 - winProb) * Math.abs(currentPrice - stopLoss);
425
+
426
+ return {
427
+ id: uuidv4(),
428
+ timestamp: Date.now(),
429
+ symbol: extractBaseSymbol(contractId),
430
+ contractId,
431
+ side: direction === 'long' ? OrderSide.BID : OrderSide.ASK,
432
+ direction,
433
+ strategy: 'HQX_ULTRA_SCALPING',
434
+ strength,
435
+ edge,
436
+ confidence,
437
+ entry: currentPrice,
438
+ entryPrice: currentPrice,
439
+ stopLoss,
440
+ takeProfit,
441
+ riskReward,
442
+ stopTicks: actualStopTicks,
443
+ targetTicks: actualTargetTicks,
444
+ trailTriggerTicks,
445
+ trailDistanceTicks,
446
+ beBreakeven,
447
+ profitLockLevel,
448
+ zScore: zscore,
449
+ zScoreExit: this.zscoreExitThreshold,
450
+ vpinValue: vpin,
451
+ kyleLambda,
452
+ kalmanEstimate,
453
+ volatilityRegime: regime,
454
+ ofiValue: ofi,
455
+ models: scores
456
+ };
457
+ }
458
+
459
+ /**
460
+ * Check if should exit by Z-Score
461
+ */
462
+ shouldExitByZScore(contractId) {
463
+ const prices = this.priceBuffer.get(contractId);
464
+ if (!prices || prices.length < 50) return false;
465
+ const zscore = this._computeZScore(prices);
466
+ return Math.abs(zscore) < this.zscoreExitThreshold;
467
+ }
468
+
469
+ /**
470
+ * Get current model values
471
+ */
472
+ getModelValues(contractId) {
473
+ const prices = this.priceBuffer.get(contractId);
474
+ const volumes = this.volumeBuffer.get(contractId);
475
+ const bars = this.barHistory.get(contractId);
476
+ if (!prices || !volumes || !bars || bars.length < 50) return null;
477
+
478
+ return {
479
+ zscore: this._computeZScore(prices).toFixed(2),
480
+ vpin: (this._computeVPIN(volumes) * 100).toFixed(1) + '%',
481
+ ofi: (this._computeOrderFlowImbalance(bars) * 100).toFixed(1) + '%',
482
+ bars: bars.length
483
+ };
484
+ }
485
+
486
+ /**
487
+ * Record trade result
488
+ */
489
+ recordTradeResult(pnl) {
490
+ this.recentTrades.push({ pnl, timestamp: Date.now() });
491
+ if (this.recentTrades.length > 100) this.recentTrades.shift();
492
+ if (pnl > 0) { this.winStreak++; this.lossStreak = 0; }
493
+ else { this.lossStreak++; this.winStreak = 0; }
494
+ }
495
+
496
+ /**
497
+ * Get bar history
498
+ */
499
+ getBarHistory(contractId) {
500
+ return this.barHistory.get(contractId) || [];
501
+ }
502
+
503
+ /**
504
+ * Reset strategy
505
+ */
506
+ reset(contractId) {
507
+ this.barHistory.set(contractId, []);
508
+ this.priceBuffer.set(contractId, []);
509
+ this.volumeBuffer.set(contractId, []);
510
+ this.tradesBuffer.set(contractId, []);
511
+ this.atrHistory.set(contractId, []);
512
+ this.tickBuffer.set(contractId, []);
513
+ this.lastBarTime.set(contractId, 0);
514
+ this.kalmanStates.set(contractId, { estimate: 0, errorCovariance: 1.0 });
515
+ }
516
+ }
517
+
518
+ // Singleton instance
519
+ const M1 = new HQXUltraScalpingStrategy();
520
+
521
+ module.exports = { M1, HQXUltraScalpingStrategy, OrderSide, SignalStrength };
@@ -11,9 +11,9 @@ const { AlgoUI, renderSessionSummary } = require('./ui');
11
11
  const { prompts } = require('../../utils');
12
12
  const { checkMarketHours } = require('../../services/projectx/market');
13
13
 
14
- // Strategy & Market Data (from compiled bytecode)
15
- const { M1 } = require('../../../dist/lib/m/mod1');
16
- const { MarketDataFeed } = require('../../../dist/lib/data');
14
+ // Strategy & Market Data
15
+ const { M1 } = require('../../lib/m/s1');
16
+ const { MarketDataFeed } = require('../../lib/data');
17
17
 
18
18
 
19
19
 
package/dist/lib/api.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./api.jsc');
package/dist/lib/api.jsc DELETED
Binary file
package/dist/lib/api2.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./api2.jsc');
package/dist/lib/api2.jsc DELETED
Binary file
package/dist/lib/core.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./core.jsc');
package/dist/lib/core.jsc DELETED
Binary file
package/dist/lib/core2.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./core2.jsc');
Binary file
package/dist/lib/data.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./data.jsc');
package/dist/lib/data.jsc DELETED
Binary file
package/dist/lib/data2.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./data2.jsc');
Binary file
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./decoder.jsc');
Binary file
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./mod1.jsc');
Binary file
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./mod2.jsc');
Binary file
package/dist/lib/n/r1.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./r1.jsc');
package/dist/lib/n/r1.jsc DELETED
Binary file
package/dist/lib/n/r2.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./r2.jsc');
package/dist/lib/n/r2.jsc DELETED
Binary file
package/dist/lib/n/r3.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./r3.jsc');
package/dist/lib/n/r3.jsc DELETED
Binary file
package/dist/lib/n/r4.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./r4.jsc');
package/dist/lib/n/r4.jsc DELETED
Binary file
package/dist/lib/n/r5.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./r5.jsc');
package/dist/lib/n/r5.jsc DELETED
Binary file
package/dist/lib/n/r6.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./r6.jsc');
package/dist/lib/n/r6.jsc DELETED
Binary file
package/dist/lib/n/r7.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./r7.jsc');
package/dist/lib/n/r7.jsc DELETED
Binary file
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./util1.jsc');
Binary file
@@ -1 +0,0 @@
1
- 'use strict';require('bytenode');module.exports=require('./util2.jsc');
Binary file