hedgequantx 2.6.145 → 2.6.146
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 +1 -1
- package/src/pages/algo/one-account.js +46 -15
- package/src/pages/algo/smart-logs.js +480 -289
package/package.json
CHANGED
|
@@ -32,7 +32,21 @@ const USE_HFT_STRATEGY = true;
|
|
|
32
32
|
const aiService = require('../../services/ai');
|
|
33
33
|
const StrategySupervisor = require('../../services/ai/strategy-supervisor');
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Format price to avoid floating point errors
|
|
37
|
+
* Uses the tick size to round properly
|
|
38
|
+
* @param {number} price - Raw price
|
|
39
|
+
* @param {number} tickSize - Tick size (default 0.25)
|
|
40
|
+
* @returns {string} - Formatted price string
|
|
41
|
+
*/
|
|
42
|
+
const formatPrice = (price, tickSize = 0.25) => {
|
|
43
|
+
if (price === null || price === undefined || isNaN(price)) return '--';
|
|
44
|
+
// Round to nearest tick, then format
|
|
45
|
+
const rounded = Math.round(price / tickSize) * tickSize;
|
|
46
|
+
// Determine decimal places from tick size
|
|
47
|
+
const decimals = tickSize < 1 ? Math.max(0, -Math.floor(Math.log10(tickSize))) : 0;
|
|
48
|
+
return rounded.toFixed(decimals);
|
|
49
|
+
};
|
|
36
50
|
|
|
37
51
|
/**
|
|
38
52
|
* Check if service supports fast path (Rithmic direct)
|
|
@@ -456,8 +470,9 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
456
470
|
stats.entryLatencies.push(fillLatencyMs);
|
|
457
471
|
stats.avgFillLatency = stats.entryLatencies.reduce((a, b) => a + b, 0) / stats.entryLatencies.length;
|
|
458
472
|
const side = position.side === 0 ? 'LONG' : 'SHORT';
|
|
473
|
+
const priceStr = formatPrice(position.entryPrice, tickSize || 0.25);
|
|
459
474
|
// Use 'filled' type for colored FILL icon
|
|
460
|
-
ui.addLog('filled', `${side} ${position.size}x ${symbolName} @ ${
|
|
475
|
+
ui.addLog('filled', `${side} ${position.size}x ${symbolName} @ ${priceStr} | ${fillLatencyMs}ms`);
|
|
461
476
|
});
|
|
462
477
|
|
|
463
478
|
positionManager.on('exitFilled', ({ orderTag, exitPrice, pnlTicks, holdDurationMs }) => {
|
|
@@ -493,10 +508,12 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
493
508
|
|
|
494
509
|
if (pnlDollars >= 0) {
|
|
495
510
|
stats.wins++;
|
|
496
|
-
|
|
511
|
+
const priceStr = formatPrice(exitPrice, tickSize || 0.25);
|
|
512
|
+
ui.addLog('win', `+$${pnlDollars.toFixed(2)} @ ${priceStr} | ${holdSec}s`);
|
|
497
513
|
} else {
|
|
498
514
|
stats.losses++;
|
|
499
|
-
|
|
515
|
+
const priceStr = formatPrice(exitPrice, tickSize || 0.25);
|
|
516
|
+
ui.addLog('loss', `-$${Math.abs(pnlDollars).toFixed(2)} @ ${priceStr} | ${holdSec}s`);
|
|
500
517
|
}
|
|
501
518
|
} else {
|
|
502
519
|
// Log with ticks only if tickValue unavailable
|
|
@@ -521,7 +538,8 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
521
538
|
|
|
522
539
|
positionManager.on('breakevenActivated', ({ orderTag, position, breakevenPrice, pnlTicks }) => {
|
|
523
540
|
// Use 'be' type for yellow BE icon
|
|
524
|
-
|
|
541
|
+
const priceStr = formatPrice(breakevenPrice, tickSize || 0.25);
|
|
542
|
+
ui.addLog('be', `Breakeven @ ${priceStr} | +${pnlTicks} ticks`)
|
|
525
543
|
});
|
|
526
544
|
|
|
527
545
|
positionManager.on('exitOrderFired', ({ orderTag, exitReason, latencyMs }) => {
|
|
@@ -1557,7 +1575,8 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1557
1575
|
stats.entryLatencies.push(fillLatencyMs);
|
|
1558
1576
|
stats.avgFillLatency = stats.entryLatencies.reduce((a, b) => a + b, 0) / stats.entryLatencies.length;
|
|
1559
1577
|
const side = position.side === 0 ? 'LONG' : 'SHORT';
|
|
1560
|
-
|
|
1578
|
+
const priceStr = formatPrice(position.entryPrice, tickSize || 0.25);
|
|
1579
|
+
ui.addLog('filled', `[${symbolName}] ${side} ${position.size}x @ ${priceStr} | ${fillLatencyMs}ms`);
|
|
1561
1580
|
stats.symbolStats[symbolName].position = position.side === 0 ? position.size : -position.size;
|
|
1562
1581
|
ui.render(stats);
|
|
1563
1582
|
});
|
|
@@ -1597,11 +1616,13 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1597
1616
|
if (pnlDollars >= 0) {
|
|
1598
1617
|
stats.wins++;
|
|
1599
1618
|
stats.symbolStats[symbolName].wins++;
|
|
1600
|
-
|
|
1619
|
+
const priceStr = formatPrice(exitPrice, tickSize || 0.25);
|
|
1620
|
+
ui.addLog('win', `[${symbolName}] +$${pnlDollars.toFixed(2)} @ ${priceStr} | ${holdSec}s`);
|
|
1601
1621
|
} else {
|
|
1602
1622
|
stats.losses++;
|
|
1603
1623
|
stats.symbolStats[symbolName].losses++;
|
|
1604
|
-
|
|
1624
|
+
const priceStr = formatPrice(exitPrice, tickSize || 0.25);
|
|
1625
|
+
ui.addLog('loss', `[${symbolName}] -$${Math.abs(pnlDollars).toFixed(2)} @ ${priceStr} | ${holdSec}s`);
|
|
1605
1626
|
// Symbol can trade again - no disable, continue until Target/Risk reached
|
|
1606
1627
|
}
|
|
1607
1628
|
} else if (pnlTicks !== null) {
|
|
@@ -1632,7 +1653,8 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1632
1653
|
});
|
|
1633
1654
|
|
|
1634
1655
|
pm.on('breakevenActivated', ({ breakevenPrice, pnlTicks }) => {
|
|
1635
|
-
|
|
1656
|
+
const priceStr = formatPrice(breakevenPrice, tickSize || 0.25);
|
|
1657
|
+
ui.addLog('be', `[${symbolName}] BE @ ${priceStr} | +${pnlTicks} ticks`);
|
|
1636
1658
|
});
|
|
1637
1659
|
|
|
1638
1660
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -2080,9 +2102,22 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
2080
2102
|
await Promise.race([disconnectPromise, timeoutPromise]);
|
|
2081
2103
|
} catch {}
|
|
2082
2104
|
|
|
2083
|
-
// Cleanup keyboard
|
|
2105
|
+
// Cleanup keyboard FIRST - restore stdin to normal mode
|
|
2084
2106
|
try { if (cleanupKeys) cleanupKeys(); } catch {}
|
|
2085
2107
|
|
|
2108
|
+
// Force stdin back to normal mode (critical for waitForEnter to work)
|
|
2109
|
+
try {
|
|
2110
|
+
if (process.stdin.isTTY) {
|
|
2111
|
+
process.stdin.setRawMode(false);
|
|
2112
|
+
}
|
|
2113
|
+
// Remove all listeners to prevent interference
|
|
2114
|
+
process.stdin.removeAllListeners('keypress');
|
|
2115
|
+
process.stdin.resume();
|
|
2116
|
+
} catch {}
|
|
2117
|
+
|
|
2118
|
+
// Small delay to let stdin settle
|
|
2119
|
+
await new Promise(r => setTimeout(r, 100));
|
|
2120
|
+
|
|
2086
2121
|
// Calculate duration before closeLog
|
|
2087
2122
|
const durationMs = Date.now() - stats.startTime;
|
|
2088
2123
|
const hours = Math.floor(durationMs / 3600000);
|
|
@@ -2099,17 +2134,13 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
2099
2134
|
|
|
2100
2135
|
try { ui.cleanup(); } catch {}
|
|
2101
2136
|
|
|
2102
|
-
try {
|
|
2103
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
2104
|
-
process.stdin.resume();
|
|
2105
|
-
} catch {}
|
|
2106
|
-
|
|
2107
2137
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2108
2138
|
// SESSION SUMMARY (duration already calculated above)
|
|
2109
2139
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2110
2140
|
// Render multi-symbol summary with same style as single-symbol
|
|
2111
2141
|
renderMultiSymbolSummary(stats, stopReason, stats.symbolStats);
|
|
2112
2142
|
|
|
2143
|
+
// Wait for user to press Enter before returning to menu
|
|
2113
2144
|
await prompts.waitForEnter();
|
|
2114
2145
|
};
|
|
2115
2146
|
|
|
@@ -1,401 +1,592 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* =============================================================================
|
|
3
|
-
*
|
|
3
|
+
* HQX INSTITUTIONAL SMART LOGGING SYSTEM
|
|
4
4
|
* =============================================================================
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* -
|
|
5
|
+
* HedgeFund-grade execution logs with full market microstructure context
|
|
6
|
+
*
|
|
7
|
+
* SHOWS:
|
|
8
|
+
* - Order Flow Imbalance (OFI) with directional bias
|
|
9
|
+
* - Cumulative Delta & Volume Profile
|
|
10
|
+
* - Z-Score deviation from fair value
|
|
11
|
+
* - Momentum vectors & acceleration
|
|
12
|
+
* - Liquidity depth & spread dynamics
|
|
13
|
+
* - Position Greeks: Ticks to SL/TP, R-multiple, hold time
|
|
14
|
+
* - Flow alignment score (position vs market flow)
|
|
15
|
+
* - Tick-by-tick execution latency
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
'use strict';
|
|
12
19
|
|
|
13
|
-
//
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// STATE TRACKING
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
14
24
|
const recentMessages = new Map();
|
|
15
25
|
const MAX_RECENT = 5;
|
|
16
26
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
// Market microstructure state
|
|
28
|
+
const microstructure = {
|
|
29
|
+
ofi: 0, // Order Flow Imbalance (-1 to +1)
|
|
30
|
+
cumDelta: 0, // Cumulative Delta
|
|
31
|
+
delta: 0, // Current bar delta
|
|
32
|
+
zscore: 0, // Z-Score from VWAP/mean
|
|
33
|
+
momentum: 0, // Price momentum
|
|
34
|
+
acceleration: 0, // Momentum change rate
|
|
35
|
+
spread: 0, // Bid-ask spread in ticks
|
|
36
|
+
bidDepth: 0, // Bid side liquidity
|
|
37
|
+
askDepth: 0, // Ask side liquidity
|
|
38
|
+
imbalance: 0, // Book imbalance ratio
|
|
39
|
+
vwap: 0, // Volume Weighted Avg Price
|
|
40
|
+
poc: 0, // Point of Control
|
|
41
|
+
atr: 0, // Average True Range
|
|
42
|
+
tps: 0, // Ticks per second
|
|
43
|
+
buyVol: 0, // Buy volume
|
|
44
|
+
sellVol: 0, // Sell volume
|
|
45
|
+
lastUpdate: Date.now(),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Position state for Greeks calculation
|
|
49
|
+
const position = {
|
|
50
|
+
side: null, // 'LONG' | 'SHORT'
|
|
51
|
+
symbol: '',
|
|
52
|
+
size: 0,
|
|
53
|
+
entryPrice: 0,
|
|
54
|
+
currentPrice: 0,
|
|
55
|
+
stopLoss: 0,
|
|
56
|
+
takeProfit: 0,
|
|
57
|
+
entryTime: 0,
|
|
58
|
+
unrealizedPnl: 0,
|
|
59
|
+
tickSize: 0.25,
|
|
60
|
+
tickValue: 12.50,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// =============================================================================
|
|
64
|
+
// HELPER FUNCTIONS
|
|
65
|
+
// =============================================================================
|
|
66
|
+
|
|
20
67
|
function getVariedMessage(category, pool, defaultMsg) {
|
|
21
68
|
const recent = recentMessages.get(category) || [];
|
|
22
|
-
|
|
23
|
-
// Filter out recently used messages
|
|
24
69
|
const available = pool.filter(msg => !recent.includes(msg));
|
|
25
|
-
|
|
26
|
-
// If all messages were recently used, reset
|
|
27
70
|
if (available.length === 0) {
|
|
28
71
|
recentMessages.set(category, []);
|
|
29
72
|
return pool[Math.floor(Math.random() * pool.length)] || defaultMsg;
|
|
30
73
|
}
|
|
31
|
-
|
|
32
|
-
// Pick a random available message
|
|
33
74
|
const chosen = available[Math.floor(Math.random() * available.length)] || defaultMsg;
|
|
34
|
-
|
|
35
|
-
// Track it
|
|
36
75
|
recent.push(chosen);
|
|
37
76
|
if (recent.length > MAX_RECENT) recent.shift();
|
|
38
77
|
recentMessages.set(category, recent);
|
|
39
|
-
|
|
40
78
|
return chosen;
|
|
41
79
|
}
|
|
42
80
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'Buyers in control',
|
|
50
|
-
'Long-side pressure',
|
|
51
|
-
'Bid accumulation',
|
|
52
|
-
'Buy programs active',
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
const SHORT_BIAS_MESSAGES = [
|
|
56
|
-
'Bearish bias detected',
|
|
57
|
-
'Sellers in control',
|
|
58
|
-
'Short-side pressure',
|
|
59
|
-
'Offer distribution',
|
|
60
|
-
'Sell programs active',
|
|
61
|
-
];
|
|
81
|
+
function formatNumber(n, decimals = 2) {
|
|
82
|
+
if (n === undefined || n === null || isNaN(n)) return '--';
|
|
83
|
+
const num = parseFloat(n);
|
|
84
|
+
const sign = num >= 0 ? '+' : '';
|
|
85
|
+
return sign + num.toFixed(decimals);
|
|
86
|
+
}
|
|
62
87
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'No clear direction',
|
|
68
|
-
'Mixed signals',
|
|
69
|
-
];
|
|
88
|
+
function formatPnl(pnl) {
|
|
89
|
+
if (pnl === undefined || pnl === null) return '--';
|
|
90
|
+
return pnl >= 0 ? `+$${pnl.toFixed(2)}` : `-$${Math.abs(pnl).toFixed(2)}`;
|
|
91
|
+
}
|
|
70
92
|
|
|
71
93
|
// =============================================================================
|
|
72
|
-
// MESSAGE POOLS
|
|
94
|
+
// INSTITUTIONAL MESSAGE POOLS
|
|
73
95
|
// =============================================================================
|
|
74
96
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
'
|
|
97
|
+
// Flow Analysis - HF Grade
|
|
98
|
+
const FLOW_AGGRESSIVE_BUY = [
|
|
99
|
+
'Aggressive lifting offers',
|
|
100
|
+
'Heavy buy-side absorption',
|
|
101
|
+
'Institutional accumulation',
|
|
102
|
+
'Delta surge positive',
|
|
103
|
+
'Bid stacking detected',
|
|
104
|
+
'Buy programs sweeping',
|
|
81
105
|
];
|
|
82
106
|
|
|
83
|
-
const
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
'
|
|
87
|
-
'
|
|
88
|
-
'
|
|
107
|
+
const FLOW_AGGRESSIVE_SELL = [
|
|
108
|
+
'Aggressive hitting bids',
|
|
109
|
+
'Heavy sell-side distribution',
|
|
110
|
+
'Institutional liquidation',
|
|
111
|
+
'Delta surge negative',
|
|
112
|
+
'Offer stacking detected',
|
|
113
|
+
'Sell programs sweeping',
|
|
89
114
|
];
|
|
90
115
|
|
|
91
|
-
const
|
|
92
|
-
'
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
116
|
+
const FLOW_BALANCED = [
|
|
117
|
+
'Two-sided flow equilibrium',
|
|
118
|
+
'Balanced book rotation',
|
|
119
|
+
'Fair value discovery',
|
|
120
|
+
'Neutral order flow',
|
|
121
|
+
'Market making zone',
|
|
97
122
|
];
|
|
98
123
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
'
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
'
|
|
124
|
+
// Mean Reversion - HF Grade
|
|
125
|
+
const MR_OVERBOUGHT = [
|
|
126
|
+
'Extended above VWAP band',
|
|
127
|
+
'Z-score overbought extreme',
|
|
128
|
+
'Mean reversion fade setup',
|
|
129
|
+
'Exhaustion at upper band',
|
|
130
|
+
'Stretched deviation short',
|
|
105
131
|
];
|
|
106
132
|
|
|
107
|
-
const
|
|
108
|
-
'
|
|
109
|
-
'
|
|
110
|
-
'
|
|
111
|
-
'
|
|
112
|
-
'
|
|
133
|
+
const MR_OVERSOLD = [
|
|
134
|
+
'Extended below VWAP band',
|
|
135
|
+
'Z-score oversold extreme',
|
|
136
|
+
'Mean reversion bounce setup',
|
|
137
|
+
'Capitulation at lower band',
|
|
138
|
+
'Stretched deviation long',
|
|
113
139
|
];
|
|
114
140
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
'
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
'
|
|
141
|
+
// Momentum - HF Grade
|
|
142
|
+
const MOM_ACCELERATING_UP = [
|
|
143
|
+
'Momentum acceleration +',
|
|
144
|
+
'Velocity vector bullish',
|
|
145
|
+
'Trend thrust positive',
|
|
146
|
+
'Breakout momentum confirmed',
|
|
147
|
+
'Impulse wave up',
|
|
121
148
|
];
|
|
122
149
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
'
|
|
129
|
-
'Price accelerating',
|
|
130
|
-
'Bullish momentum',
|
|
131
|
-
'Upward thrust',
|
|
132
|
-
'Buying momentum',
|
|
150
|
+
const MOM_ACCELERATING_DOWN = [
|
|
151
|
+
'Momentum acceleration -',
|
|
152
|
+
'Velocity vector bearish',
|
|
153
|
+
'Trend thrust negative',
|
|
154
|
+
'Breakdown momentum confirmed',
|
|
155
|
+
'Impulse wave down',
|
|
133
156
|
];
|
|
134
157
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
'
|
|
138
|
-
'
|
|
139
|
-
'
|
|
140
|
-
'
|
|
158
|
+
// Volatility - HF Grade
|
|
159
|
+
const VOL_EXPANSION = [
|
|
160
|
+
'Volatility expansion cycle',
|
|
161
|
+
'ATR breakout detected',
|
|
162
|
+
'Range expansion phase',
|
|
163
|
+
'Vol regime shift high',
|
|
164
|
+
'Gamma event potential',
|
|
141
165
|
];
|
|
142
166
|
|
|
143
|
-
const
|
|
144
|
-
'
|
|
145
|
-
'
|
|
146
|
-
'
|
|
147
|
-
'
|
|
148
|
-
'
|
|
167
|
+
const VOL_COMPRESSION = [
|
|
168
|
+
'Volatility compression cycle',
|
|
169
|
+
'ATR contraction detected',
|
|
170
|
+
'Range compression phase',
|
|
171
|
+
'Vol regime shift low',
|
|
172
|
+
'Coiling for breakout',
|
|
149
173
|
];
|
|
150
174
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
175
|
+
// Position Status - HF Grade
|
|
176
|
+
const POS_PROFIT_RUNNING = [
|
|
177
|
+
'P&L positive running',
|
|
178
|
+
'Trade thesis confirmed',
|
|
179
|
+
'Alpha capture in progress',
|
|
180
|
+
'Edge realization phase',
|
|
181
|
+
'Position working target',
|
|
157
182
|
];
|
|
158
183
|
|
|
159
|
-
const
|
|
160
|
-
'
|
|
161
|
-
'
|
|
162
|
-
'
|
|
163
|
-
'
|
|
164
|
-
'
|
|
184
|
+
const POS_LOSS_MANAGING = [
|
|
185
|
+
'Adverse excursion mgmt',
|
|
186
|
+
'Drawdown within params',
|
|
187
|
+
'Risk budget consuming',
|
|
188
|
+
'Position under pressure',
|
|
189
|
+
'Defending stop level',
|
|
165
190
|
];
|
|
166
191
|
|
|
167
|
-
const
|
|
168
|
-
'
|
|
169
|
-
'
|
|
170
|
-
'
|
|
171
|
-
'
|
|
172
|
-
'
|
|
192
|
+
const POS_NEAR_TARGET = [
|
|
193
|
+
'Approaching profit target',
|
|
194
|
+
'Near TP execution zone',
|
|
195
|
+
'Exit liquidity scanning',
|
|
196
|
+
'Target strike imminent',
|
|
197
|
+
'Profit capture pending',
|
|
173
198
|
];
|
|
174
199
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
'
|
|
181
|
-
'Analyzing flow...',
|
|
182
|
-
'Monitoring price action...',
|
|
183
|
-
'Watching for setups...',
|
|
184
|
-
'Evaluating conditions...',
|
|
200
|
+
const POS_NEAR_STOP = [
|
|
201
|
+
'Near stop execution zone',
|
|
202
|
+
'SL liquidity scanning',
|
|
203
|
+
'Risk threshold proximity',
|
|
204
|
+
'Stop strike imminent',
|
|
205
|
+
'Loss containment pending',
|
|
185
206
|
];
|
|
186
207
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
'
|
|
190
|
-
'
|
|
191
|
-
'
|
|
192
|
-
'
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const IN_POSITION_PROFIT_MESSAGES = [
|
|
196
|
-
'Running profit',
|
|
197
|
-
'In the money',
|
|
198
|
-
'Trade working',
|
|
199
|
-
'Position profitable',
|
|
200
|
-
'Gains accumulating',
|
|
201
|
-
];
|
|
202
|
-
|
|
203
|
-
const IN_POSITION_LOSS_MESSAGES = [
|
|
204
|
-
'Managing drawdown',
|
|
205
|
-
'Underwater',
|
|
206
|
-
'Holding through',
|
|
207
|
-
'Defending position',
|
|
208
|
-
'Monitoring risk',
|
|
208
|
+
// Scanning - HF Grade
|
|
209
|
+
const SCANNING_ACTIVE = [
|
|
210
|
+
'Scanning microstructure',
|
|
211
|
+
'Analyzing order book',
|
|
212
|
+
'Evaluating flow signals',
|
|
213
|
+
'Pattern recognition active',
|
|
214
|
+
'Setup detection running',
|
|
209
215
|
];
|
|
210
216
|
|
|
211
217
|
// =============================================================================
|
|
212
|
-
//
|
|
218
|
+
// MAIN LOG GENERATORS - HF GRADE
|
|
213
219
|
// =============================================================================
|
|
214
220
|
|
|
215
221
|
/**
|
|
216
|
-
*
|
|
222
|
+
* Update microstructure state from strategy model values
|
|
217
223
|
*/
|
|
218
|
-
function
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
break;
|
|
227
|
-
default:
|
|
228
|
-
pool = FLAT_BIAS_MESSAGES;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const message = getVariedMessage(`bias_${direction}`, pool, `${direction} bias`);
|
|
232
|
-
const arrow = direction === 'LONG' ? '▲' : direction === 'SHORT' ? '▼' : '=';
|
|
233
|
-
|
|
234
|
-
let details;
|
|
235
|
-
if (delta !== undefined && ofi !== undefined) {
|
|
236
|
-
const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
|
|
237
|
-
details = `${arrow} Δ=${deltaStr} | OFI=${ofi.toFixed(2)}`;
|
|
224
|
+
function updateMicrostructure(data) {
|
|
225
|
+
if (data.ofi !== undefined) microstructure.ofi = data.ofi;
|
|
226
|
+
if (data.delta !== undefined) microstructure.delta = data.delta;
|
|
227
|
+
if (data.cumDelta !== undefined) microstructure.cumDelta = data.cumDelta;
|
|
228
|
+
if (data.zscore !== undefined) microstructure.zscore = data.zscore;
|
|
229
|
+
if (data.momentum !== undefined) {
|
|
230
|
+
microstructure.acceleration = data.momentum - microstructure.momentum;
|
|
231
|
+
microstructure.momentum = data.momentum;
|
|
238
232
|
}
|
|
239
|
-
|
|
240
|
-
|
|
233
|
+
if (data.spread !== undefined) microstructure.spread = data.spread;
|
|
234
|
+
if (data.atr !== undefined) microstructure.atr = data.atr;
|
|
235
|
+
if (data.tps !== undefined) microstructure.tps = data.tps;
|
|
236
|
+
if (data.buyVol !== undefined) microstructure.buyVol = data.buyVol;
|
|
237
|
+
if (data.sellVol !== undefined) microstructure.sellVol = data.sellVol;
|
|
238
|
+
if (data.vwap !== undefined) microstructure.vwap = data.vwap;
|
|
239
|
+
microstructure.lastUpdate = Date.now();
|
|
241
240
|
}
|
|
242
241
|
|
|
243
242
|
/**
|
|
244
|
-
*
|
|
243
|
+
* Update position state
|
|
245
244
|
*/
|
|
246
|
-
function
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
message: `${arrow} ${message}`,
|
|
259
|
-
details: `Mom=${mom.toFixed(1)} | Z=${z.toFixed(2)}`,
|
|
260
|
-
};
|
|
245
|
+
function updatePosition(data) {
|
|
246
|
+
if (data.side !== undefined) position.side = data.side;
|
|
247
|
+
if (data.symbol !== undefined) position.symbol = data.symbol;
|
|
248
|
+
if (data.size !== undefined) position.size = data.size;
|
|
249
|
+
if (data.entryPrice !== undefined) position.entryPrice = data.entryPrice;
|
|
250
|
+
if (data.currentPrice !== undefined) position.currentPrice = data.currentPrice;
|
|
251
|
+
if (data.stopLoss !== undefined) position.stopLoss = data.stopLoss;
|
|
252
|
+
if (data.takeProfit !== undefined) position.takeProfit = data.takeProfit;
|
|
253
|
+
if (data.entryTime !== undefined) position.entryTime = data.entryTime;
|
|
254
|
+
if (data.unrealizedPnl !== undefined) position.unrealizedPnl = data.unrealizedPnl;
|
|
255
|
+
if (data.tickSize !== undefined) position.tickSize = data.tickSize;
|
|
256
|
+
if (data.tickValue !== undefined) position.tickValue = data.tickValue;
|
|
261
257
|
}
|
|
262
258
|
|
|
263
259
|
/**
|
|
264
|
-
*
|
|
260
|
+
* Clear position state when flat
|
|
265
261
|
*/
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return {
|
|
277
|
-
message: `${arrow} ${message}`,
|
|
278
|
-
details: `Z-Score: ${z.toFixed(2)} | Looking for ${isOverbought ? 'SHORT' : 'LONG'}`,
|
|
279
|
-
};
|
|
262
|
+
function clearPosition() {
|
|
263
|
+
position.side = null;
|
|
264
|
+
position.symbol = '';
|
|
265
|
+
position.size = 0;
|
|
266
|
+
position.entryPrice = 0;
|
|
267
|
+
position.currentPrice = 0;
|
|
268
|
+
position.stopLoss = 0;
|
|
269
|
+
position.takeProfit = 0;
|
|
270
|
+
position.entryTime = 0;
|
|
271
|
+
position.unrealizedPnl = 0;
|
|
280
272
|
}
|
|
281
273
|
|
|
282
274
|
/**
|
|
283
|
-
*
|
|
275
|
+
* GET MARKET FLOW LOG - When NOT in position
|
|
276
|
+
* Shows: OFI | Delta | Z-Score | Momentum | Volume Ratio
|
|
284
277
|
*/
|
|
285
|
-
function
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
278
|
+
function getMarketFlowLog() {
|
|
279
|
+
const { ofi, delta, zscore, momentum, buyVol, sellVol, tps, atr } = microstructure;
|
|
280
|
+
|
|
281
|
+
const absOfi = Math.abs(ofi || 0);
|
|
282
|
+
const absZ = Math.abs(zscore || 0);
|
|
283
|
+
const absMom = Math.abs(momentum || 0);
|
|
284
|
+
|
|
285
|
+
// Determine primary signal
|
|
286
|
+
let pool, category, signal;
|
|
287
|
+
|
|
288
|
+
// Priority: Mean Reversion > OFI > Momentum
|
|
289
|
+
if (absZ > 2.0) {
|
|
290
|
+
pool = zscore > 0 ? MR_OVERBOUGHT : MR_OVERSOLD;
|
|
291
|
+
category = zscore > 0 ? 'mr_short' : 'mr_long';
|
|
292
|
+
signal = 'MEAN_REV';
|
|
293
|
+
} else if (absOfi > 0.4) {
|
|
294
|
+
pool = ofi > 0 ? FLOW_AGGRESSIVE_BUY : FLOW_AGGRESSIVE_SELL;
|
|
295
|
+
category = ofi > 0 ? 'flow_buy' : 'flow_sell';
|
|
296
|
+
signal = ofi > 0 ? 'BUY_FLOW' : 'SELL_FLOW';
|
|
297
|
+
} else if (absMom > 3) {
|
|
298
|
+
pool = momentum > 0 ? MOM_ACCELERATING_UP : MOM_ACCELERATING_DOWN;
|
|
299
|
+
category = momentum > 0 ? 'mom_up' : 'mom_down';
|
|
300
|
+
signal = momentum > 0 ? 'MOM_UP' : 'MOM_DOWN';
|
|
301
|
+
} else {
|
|
302
|
+
pool = SCANNING_ACTIVE;
|
|
303
|
+
category = 'scanning';
|
|
304
|
+
signal = 'SCANNING';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const message = getVariedMessage(category, pool, 'Analyzing...');
|
|
308
|
+
|
|
309
|
+
// Build HF-grade metrics string
|
|
310
|
+
const ofiStr = `OFI:${formatNumber(ofi, 2)}`;
|
|
311
|
+
const deltaStr = `Δ:${formatNumber(delta, 0)}`;
|
|
312
|
+
const zStr = `Z:${formatNumber(zscore, 2)}`;
|
|
313
|
+
const momStr = `Mom:${formatNumber(momentum, 1)}`;
|
|
314
|
+
|
|
315
|
+
// Volume ratio
|
|
316
|
+
const totalVol = (buyVol || 0) + (sellVol || 0);
|
|
317
|
+
const buyPct = totalVol > 0 ? Math.round((buyVol / totalVol) * 100) : 50;
|
|
318
|
+
const volStr = `B/S:${buyPct}/${100 - buyPct}`;
|
|
319
|
+
|
|
320
|
+
const details = `${ofiStr} | ${deltaStr} | ${zStr} | ${momStr} | ${volStr}`;
|
|
321
|
+
|
|
322
|
+
return { message, details, signal };
|
|
294
323
|
}
|
|
295
324
|
|
|
296
325
|
/**
|
|
297
|
-
*
|
|
326
|
+
* GET POSITION CONTEXT LOG - When IN position
|
|
327
|
+
* Shows: Side | P&L | Ticks to SL/TP | Hold Time | R-Multiple | Flow Alignment
|
|
298
328
|
*/
|
|
299
|
-
function
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
329
|
+
function getPositionContextLog() {
|
|
330
|
+
const { side, size, entryPrice, currentPrice, stopLoss, takeProfit, entryTime, unrealizedPnl, tickSize, tickValue } = position;
|
|
331
|
+
const { ofi, momentum, delta } = microstructure;
|
|
332
|
+
|
|
333
|
+
if (!side) return getMarketFlowLog();
|
|
334
|
+
|
|
335
|
+
// Calculate position Greeks
|
|
336
|
+
const holdMs = Date.now() - (entryTime || Date.now());
|
|
337
|
+
const holdSec = Math.floor(holdMs / 1000);
|
|
338
|
+
const holdStr = holdSec >= 60 ? `${Math.floor(holdSec / 60)}m${holdSec % 60}s` : `${holdSec}s`;
|
|
339
|
+
|
|
340
|
+
// Ticks from entry
|
|
341
|
+
const ticksFromEntry = tickSize > 0
|
|
342
|
+
? Math.round((currentPrice - entryPrice) / tickSize) * (side === 'LONG' ? 1 : -1)
|
|
343
|
+
: 0;
|
|
344
|
+
|
|
345
|
+
// Distance to SL/TP in ticks
|
|
346
|
+
let ticksToSL = 0, ticksToTP = 0, riskTicks = 0, rewardTicks = 0;
|
|
347
|
+
if (side === 'LONG' && stopLoss && takeProfit && tickSize > 0) {
|
|
348
|
+
ticksToSL = Math.round((currentPrice - stopLoss) / tickSize);
|
|
349
|
+
ticksToTP = Math.round((takeProfit - currentPrice) / tickSize);
|
|
350
|
+
riskTicks = Math.round((entryPrice - stopLoss) / tickSize);
|
|
351
|
+
rewardTicks = Math.round((takeProfit - entryPrice) / tickSize);
|
|
352
|
+
} else if (side === 'SHORT' && stopLoss && takeProfit && tickSize > 0) {
|
|
353
|
+
ticksToSL = Math.round((stopLoss - currentPrice) / tickSize);
|
|
354
|
+
ticksToTP = Math.round((currentPrice - takeProfit) / tickSize);
|
|
355
|
+
riskTicks = Math.round((stopLoss - entryPrice) / tickSize);
|
|
356
|
+
rewardTicks = Math.round((entryPrice - takeProfit) / tickSize);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// R-Multiple (how many R's of profit/loss)
|
|
360
|
+
const rMultiple = riskTicks > 0 ? (ticksFromEntry / riskTicks) : 0;
|
|
361
|
+
|
|
362
|
+
// Flow alignment score (-1 to +1)
|
|
363
|
+
let flowScore = 0;
|
|
364
|
+
if (side === 'LONG') {
|
|
365
|
+
flowScore = ((ofi || 0) + (momentum > 0 ? 0.3 : momentum < 0 ? -0.3 : 0) + (delta > 0 ? 0.2 : delta < 0 ? -0.2 : 0));
|
|
366
|
+
} else {
|
|
367
|
+
flowScore = (-(ofi || 0) + (momentum < 0 ? 0.3 : momentum > 0 ? -0.3 : 0) + (delta < 0 ? 0.2 : delta > 0 ? -0.2 : 0));
|
|
368
|
+
}
|
|
369
|
+
flowScore = Math.max(-1, Math.min(1, flowScore));
|
|
370
|
+
|
|
371
|
+
// Determine status message
|
|
372
|
+
let pool, category;
|
|
373
|
+
const pnl = unrealizedPnl || 0;
|
|
374
|
+
|
|
375
|
+
if (ticksToTP > 0 && ticksToTP <= 4) {
|
|
376
|
+
pool = POS_NEAR_TARGET;
|
|
377
|
+
category = 'near_tp';
|
|
378
|
+
} else if (ticksToSL > 0 && ticksToSL <= 4) {
|
|
379
|
+
pool = POS_NEAR_STOP;
|
|
380
|
+
category = 'near_sl';
|
|
381
|
+
} else if (pnl > 0) {
|
|
382
|
+
pool = POS_PROFIT_RUNNING;
|
|
383
|
+
category = 'pos_profit';
|
|
384
|
+
} else {
|
|
385
|
+
pool = POS_LOSS_MANAGING;
|
|
386
|
+
category = 'pos_loss';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const statusMsg = getVariedMessage(category, pool, pnl >= 0 ? 'Running profit' : 'Managing risk');
|
|
390
|
+
|
|
391
|
+
// Build message
|
|
392
|
+
const arrow = side === 'LONG' ? '▲' : '▼';
|
|
393
|
+
const message = `${arrow} ${side} ${size}x | ${statusMsg}`;
|
|
394
|
+
|
|
395
|
+
// Build HF-grade details
|
|
396
|
+
const pnlStr = formatPnl(pnl);
|
|
397
|
+
const ticksStr = ticksFromEntry >= 0 ? `+${ticksFromEntry}t` : `${ticksFromEntry}t`;
|
|
398
|
+
const slStr = ticksToSL > 0 ? `SL:${ticksToSL}t` : 'SL:--';
|
|
399
|
+
const tpStr = ticksToTP > 0 ? `TP:${ticksToTP}t` : 'TP:--';
|
|
400
|
+
const rStr = `R:${rMultiple >= 0 ? '+' : ''}${rMultiple.toFixed(2)}`;
|
|
401
|
+
const flowStr = `Flow:${flowScore >= 0 ? '+' : ''}${flowScore.toFixed(2)}`;
|
|
402
|
+
|
|
403
|
+
const details = `${pnlStr} (${ticksStr}) | ${slStr} ${tpStr} | ${rStr} | ${flowStr} | ${holdStr}`;
|
|
404
|
+
|
|
405
|
+
return { message, details };
|
|
308
406
|
}
|
|
309
407
|
|
|
310
408
|
/**
|
|
311
|
-
*
|
|
409
|
+
* GET VOLATILITY LOG
|
|
410
|
+
* Shows: ATR | Volatility Regime | Range Context
|
|
312
411
|
*/
|
|
313
|
-
function
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
412
|
+
function getVolatilityLog() {
|
|
413
|
+
const { atr, tps } = microstructure;
|
|
414
|
+
|
|
415
|
+
const isHigh = tps > 40 || atr > 2.0;
|
|
416
|
+
const isLow = tps < 10 || atr < 0.5;
|
|
417
|
+
|
|
418
|
+
let pool, category;
|
|
419
|
+
if (isHigh) {
|
|
420
|
+
pool = VOL_EXPANSION;
|
|
421
|
+
category = 'vol_high';
|
|
422
|
+
} else if (isLow) {
|
|
423
|
+
pool = VOL_COMPRESSION;
|
|
424
|
+
category = 'vol_low';
|
|
425
|
+
} else {
|
|
426
|
+
return { message: 'Normal volatility regime', details: `ATR:${atr?.toFixed(2) || '--'} | ${tps || '--'} tps` };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const message = getVariedMessage(category, pool, isHigh ? 'Vol expansion' : 'Vol compression');
|
|
430
|
+
const details = `ATR:${atr?.toFixed(2) || '--'} | ${tps || '--'} ticks/sec`;
|
|
431
|
+
|
|
432
|
+
return { message, details };
|
|
323
433
|
}
|
|
324
434
|
|
|
325
435
|
/**
|
|
326
|
-
*
|
|
436
|
+
* GET FLOW ANALYSIS LOG - Detailed order flow breakdown
|
|
437
|
+
* Shows: OFI | Delta | Cumulative Delta | Volume Breakdown
|
|
327
438
|
*/
|
|
328
|
-
function
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
439
|
+
function getFlowAnalysisLog() {
|
|
440
|
+
const { ofi, delta, cumDelta, buyVol, sellVol } = microstructure;
|
|
441
|
+
|
|
442
|
+
const absOfi = Math.abs(ofi || 0);
|
|
443
|
+
let pool, category;
|
|
444
|
+
|
|
445
|
+
if (absOfi > 0.5) {
|
|
446
|
+
pool = ofi > 0 ? FLOW_AGGRESSIVE_BUY : FLOW_AGGRESSIVE_SELL;
|
|
447
|
+
category = ofi > 0 ? 'flow_agg_buy' : 'flow_agg_sell';
|
|
448
|
+
} else if (absOfi > 0.2) {
|
|
449
|
+
pool = ofi > 0 ? FLOW_AGGRESSIVE_BUY : FLOW_AGGRESSIVE_SELL;
|
|
450
|
+
category = ofi > 0 ? 'flow_mod_buy' : 'flow_mod_sell';
|
|
451
|
+
} else {
|
|
452
|
+
pool = FLOW_BALANCED;
|
|
453
|
+
category = 'flow_balanced';
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const message = getVariedMessage(category, pool, 'Analyzing flow');
|
|
457
|
+
|
|
458
|
+
// Volume breakdown
|
|
459
|
+
const totalVol = (buyVol || 0) + (sellVol || 0);
|
|
460
|
+
const buyPct = totalVol > 0 ? Math.round((buyVol / totalVol) * 100) : 50;
|
|
461
|
+
|
|
462
|
+
const ofiStr = `OFI:${formatNumber(ofi, 2)}`;
|
|
463
|
+
const deltaStr = `Δ:${formatNumber(delta, 0)}`;
|
|
464
|
+
const cumStr = `ΣΔ:${formatNumber(cumDelta, 0)}`;
|
|
465
|
+
const volStr = `Vol B/S:${buyPct}/${100 - buyPct}%`;
|
|
466
|
+
|
|
467
|
+
const details = `${ofiStr} | ${deltaStr} | ${cumStr} | ${volStr}`;
|
|
468
|
+
|
|
469
|
+
return { message, details };
|
|
337
470
|
}
|
|
338
471
|
|
|
339
472
|
/**
|
|
340
|
-
*
|
|
473
|
+
* GET SCANNING LOG
|
|
341
474
|
*/
|
|
342
|
-
function getScanningLog(isScanning) {
|
|
343
|
-
const pool =
|
|
344
|
-
const
|
|
345
|
-
const message = getVariedMessage(category, pool, isScanning ? 'Scanning...' : 'Waiting...');
|
|
346
|
-
|
|
475
|
+
function getScanningLog(isScanning = true) {
|
|
476
|
+
const pool = SCANNING_ACTIVE;
|
|
477
|
+
const message = getVariedMessage('scanning', pool, 'Scanning...');
|
|
347
478
|
return { message, details: undefined };
|
|
348
479
|
}
|
|
349
480
|
|
|
350
481
|
/**
|
|
351
|
-
*
|
|
482
|
+
* MAIN ENTRY POINT: getSmartLog
|
|
483
|
+
* Automatically determines context and returns appropriate HF-grade log
|
|
352
484
|
*/
|
|
353
|
-
function
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
485
|
+
function getSmartLog(data = {}) {
|
|
486
|
+
// Update states if data provided
|
|
487
|
+
if (data.ofi !== undefined || data.delta !== undefined || data.zscore !== undefined) {
|
|
488
|
+
updateMicrostructure(data);
|
|
489
|
+
}
|
|
490
|
+
if (data.side !== undefined || data.entryPrice !== undefined) {
|
|
491
|
+
updatePosition(data);
|
|
492
|
+
}
|
|
357
493
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
details: `${prefix} ${pnlStr} @ ${currentPrice.toFixed(2)} | ${holdTime}s`,
|
|
365
|
-
};
|
|
494
|
+
// Return appropriate log based on position state
|
|
495
|
+
if (data.inPosition || position.side) {
|
|
496
|
+
return getPositionContextLog();
|
|
497
|
+
} else {
|
|
498
|
+
return getMarketFlowLog();
|
|
499
|
+
}
|
|
366
500
|
}
|
|
367
501
|
|
|
368
502
|
/**
|
|
369
|
-
*
|
|
503
|
+
* GET MARKET STATE LOG - Legacy compatibility
|
|
504
|
+
* Wrapper around getMarketFlowLog
|
|
370
505
|
*/
|
|
371
506
|
function getMarketStateLog(ofi, zscore, momentum, delta) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const absOfi = Math.abs(ofi);
|
|
375
|
-
const absMom = Math.abs(momentum);
|
|
376
|
-
|
|
377
|
-
// Priority: Mean reversion > OFI pressure > Momentum
|
|
378
|
-
if (absZ > 1.5) {
|
|
379
|
-
return getMeanReversionLog(zscore);
|
|
380
|
-
} else if (absOfi > 0.3) {
|
|
381
|
-
const direction = ofi > 0 ? 'LONG' : 'SHORT';
|
|
382
|
-
return getMarketBiasLog(direction, delta, ofi);
|
|
383
|
-
} else if (absMom > 2) {
|
|
384
|
-
return getMomentumLog(momentum, zscore);
|
|
385
|
-
} else {
|
|
386
|
-
return getScanningLog(true);
|
|
387
|
-
}
|
|
507
|
+
updateMicrostructure({ ofi, zscore, momentum, delta });
|
|
508
|
+
return getMarketFlowLog();
|
|
388
509
|
}
|
|
389
510
|
|
|
511
|
+
// =============================================================================
|
|
512
|
+
// LEGACY API - Backward Compatibility
|
|
513
|
+
// =============================================================================
|
|
514
|
+
|
|
515
|
+
const getMarketBiasLog = (direction, delta, ofi) => {
|
|
516
|
+
updateMicrostructure({ delta, ofi });
|
|
517
|
+
return getMarketFlowLog();
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const getMomentumLog = (momentum, zscore) => {
|
|
521
|
+
updateMicrostructure({ momentum, zscore });
|
|
522
|
+
return getMarketFlowLog();
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const getMeanReversionLog = (zscore) => {
|
|
526
|
+
updateMicrostructure({ zscore: zscore * 2.5 }); // Amplify to trigger MR message
|
|
527
|
+
return getMarketFlowLog();
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const getPositionUpdateLog = (side, size, unrealizedPnL, entryPrice, currentPrice, holdTime) => {
|
|
531
|
+
updatePosition({ side, size, unrealizedPnl: unrealizedPnL, entryPrice, currentPrice, entryTime: Date.now() - holdTime * 1000 });
|
|
532
|
+
return getPositionContextLog();
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
const getSignalLog = (direction, symbol, confidence, strategy) => {
|
|
536
|
+
const pool = direction === 'LONG' ? FLOW_AGGRESSIVE_BUY : FLOW_AGGRESSIVE_SELL;
|
|
537
|
+
const message = getVariedMessage(`signal_${direction}`, pool, `${direction} signal`);
|
|
538
|
+
const arrow = direction === 'LONG' ? '▲' : '▼';
|
|
539
|
+
return {
|
|
540
|
+
message: `${arrow} ${message}`,
|
|
541
|
+
details: `${symbol} | Conf:${confidence.toFixed(0)}% | ${strategy}`,
|
|
542
|
+
};
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const getEntryLog = (direction, symbol, size, price) => {
|
|
546
|
+
const arrow = direction === 'LONG' ? '▲' : '▼';
|
|
547
|
+
return {
|
|
548
|
+
message: `${arrow} ${direction} initiated`,
|
|
549
|
+
details: `${size}x ${symbol} @ ${price.toFixed(2)}`,
|
|
550
|
+
};
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
const getExitLog = (isProfit, symbol, size, price, pnl) => {
|
|
554
|
+
return {
|
|
555
|
+
message: isProfit ? 'Profit captured' : 'Loss contained',
|
|
556
|
+
details: `${size}x ${symbol} @ ${price.toFixed(2)} | ${formatPnl(pnl)}`,
|
|
557
|
+
};
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const getVolatilityLog_legacy = (isHigh, atr) => {
|
|
561
|
+
updateMicrostructure({ atr });
|
|
562
|
+
return getVolatilityLog();
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
// =============================================================================
|
|
566
|
+
// EXPORTS
|
|
567
|
+
// =============================================================================
|
|
568
|
+
|
|
390
569
|
module.exports = {
|
|
570
|
+
// HF-Grade API
|
|
571
|
+
getSmartLog,
|
|
572
|
+
getMarketFlowLog,
|
|
573
|
+
getPositionContextLog,
|
|
574
|
+
getFlowAnalysisLog,
|
|
575
|
+
getVolatilityLog,
|
|
576
|
+
getScanningLog,
|
|
577
|
+
|
|
578
|
+
// State management
|
|
579
|
+
updateMicrostructure,
|
|
580
|
+
updatePosition,
|
|
581
|
+
clearPosition,
|
|
582
|
+
|
|
583
|
+
// Legacy API (backward compatible)
|
|
584
|
+
getMarketStateLog,
|
|
391
585
|
getMarketBiasLog,
|
|
392
586
|
getMomentumLog,
|
|
393
587
|
getMeanReversionLog,
|
|
588
|
+
getPositionUpdateLog,
|
|
394
589
|
getSignalLog,
|
|
395
590
|
getEntryLog,
|
|
396
591
|
getExitLog,
|
|
397
|
-
getVolatilityLog,
|
|
398
|
-
getScanningLog,
|
|
399
|
-
getPositionUpdateLog,
|
|
400
|
-
getMarketStateLog,
|
|
401
592
|
};
|