hedgequantx 2.6.163 → 2.7.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 +15 -88
- package/bin/cli.js +0 -11
- package/dist/lib/api.jsc +0 -0
- package/dist/lib/api2.jsc +0 -0
- package/dist/lib/core.jsc +0 -0
- package/dist/lib/core2.jsc +0 -0
- package/dist/lib/data.js +1 -1
- package/dist/lib/data.jsc +0 -0
- package/dist/lib/data2.jsc +0 -0
- package/dist/lib/decoder.jsc +0 -0
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.jsc +0 -0
- package/dist/lib/n/r1.jsc +0 -0
- package/dist/lib/n/r2.jsc +0 -0
- package/dist/lib/n/r3.jsc +0 -0
- package/dist/lib/n/r4.jsc +0 -0
- package/dist/lib/n/r5.jsc +0 -0
- package/dist/lib/n/r6.jsc +0 -0
- package/dist/lib/n/r7.jsc +0 -0
- package/dist/lib/o/util1.jsc +0 -0
- package/dist/lib/o/util2.jsc +0 -0
- package/package.json +6 -3
- package/src/app.js +40 -162
- package/src/config/constants.js +31 -33
- package/src/config/propfirms.js +13 -217
- package/src/config/settings.js +0 -43
- package/src/lib/api.js +198 -0
- package/src/lib/api2.js +353 -0
- package/src/lib/core.js +539 -0
- package/src/lib/core2.js +341 -0
- package/src/lib/data.js +555 -0
- package/src/lib/data2.js +492 -0
- package/src/lib/decoder.js +599 -0
- package/src/lib/m/s1.js +804 -0
- package/src/lib/m/s2.js +34 -0
- package/src/lib/n/r1.js +454 -0
- package/src/lib/n/r2.js +514 -0
- package/src/lib/n/r3.js +631 -0
- package/src/lib/n/r4.js +401 -0
- package/src/lib/n/r5.js +335 -0
- package/src/lib/n/r6.js +425 -0
- package/src/lib/n/r7.js +530 -0
- package/src/lib/o/l1.js +44 -0
- package/src/lib/o/l2.js +427 -0
- package/src/lib/python-bridge.js +206 -0
- package/src/menus/connect.js +14 -176
- package/src/menus/dashboard.js +65 -110
- package/src/pages/accounts.js +18 -18
- package/src/pages/algo/copy-trading.js +210 -240
- package/src/pages/algo/index.js +41 -104
- package/src/pages/algo/one-account.js +386 -33
- package/src/pages/algo/ui.js +312 -151
- package/src/pages/orders.js +3 -3
- package/src/pages/positions.js +3 -3
- package/src/pages/stats/chart.js +74 -0
- package/src/pages/stats/display.js +228 -0
- package/src/pages/stats/index.js +236 -0
- package/src/pages/stats/metrics.js +213 -0
- package/src/pages/user.js +6 -6
- package/src/services/hqx-server/constants.js +55 -0
- package/src/services/hqx-server/index.js +401 -0
- package/src/services/hqx-server/latency.js +81 -0
- package/src/services/index.js +12 -3
- package/src/services/rithmic/accounts.js +7 -32
- package/src/services/rithmic/connection.js +1 -204
- package/src/services/rithmic/contracts.js +116 -99
- package/src/services/rithmic/handlers.js +21 -196
- package/src/services/rithmic/index.js +63 -120
- package/src/services/rithmic/market.js +31 -0
- package/src/services/rithmic/orders.js +5 -111
- package/src/services/rithmic/protobuf.js +384 -138
- package/src/services/session.js +22 -173
- package/src/ui/box.js +10 -18
- package/src/ui/index.js +1 -3
- package/src/ui/menu.js +1 -1
- package/src/utils/prompts.js +2 -2
- package/dist/lib/m/s1.js +0 -1
- package/src/menus/ai-agent-connect.js +0 -181
- package/src/menus/ai-agent-models.js +0 -219
- package/src/menus/ai-agent-oauth.js +0 -292
- package/src/menus/ai-agent-ui.js +0 -141
- package/src/menus/ai-agent.js +0 -484
- package/src/pages/algo/algo-config.js +0 -195
- package/src/pages/algo/algo-multi.js +0 -801
- package/src/pages/algo/algo-utils.js +0 -58
- package/src/pages/algo/copy-engine.js +0 -449
- package/src/pages/algo/custom-strategy.js +0 -459
- package/src/pages/algo/logger.js +0 -245
- package/src/pages/algo/smart-logs-data.js +0 -218
- package/src/pages/algo/smart-logs.js +0 -387
- package/src/pages/algo/ui-constants.js +0 -144
- package/src/pages/algo/ui-summary.js +0 -184
- package/src/pages/stats-calculations.js +0 -191
- package/src/pages/stats-ui.js +0 -381
- package/src/pages/stats.js +0 -339
- package/src/services/ai/client-analysis.js +0 -194
- package/src/services/ai/client-models.js +0 -333
- package/src/services/ai/client.js +0 -343
- package/src/services/ai/index.js +0 -384
- package/src/services/ai/oauth-anthropic.js +0 -265
- package/src/services/ai/oauth-gemini.js +0 -223
- package/src/services/ai/oauth-iflow.js +0 -269
- package/src/services/ai/oauth-openai.js +0 -233
- package/src/services/ai/oauth-qwen.js +0 -279
- package/src/services/ai/providers/direct-providers.js +0 -323
- package/src/services/ai/providers/index.js +0 -62
- package/src/services/ai/providers/other-providers.js +0 -104
- package/src/services/ai/proxy-install.js +0 -249
- package/src/services/ai/proxy-manager.js +0 -494
- package/src/services/ai/proxy-remote.js +0 -161
- package/src/services/ai/strategy-supervisor.js +0 -1312
- package/src/services/ai/supervisor-data.js +0 -195
- package/src/services/ai/supervisor-optimize.js +0 -215
- package/src/services/ai/supervisor-sync.js +0 -178
- package/src/services/ai/supervisor-utils.js +0 -158
- package/src/services/ai/supervisor.js +0 -484
- package/src/services/ai/validation.js +0 -250
- package/src/services/hqx-server-events.js +0 -110
- package/src/services/hqx-server-handlers.js +0 -217
- package/src/services/hqx-server-latency.js +0 -136
- package/src/services/hqx-server.js +0 -403
- package/src/services/position-constants.js +0 -28
- package/src/services/position-exit-logic.js +0 -174
- package/src/services/position-manager.js +0 -438
- package/src/services/position-momentum.js +0 -206
- package/src/services/projectx/accounts.js +0 -142
- package/src/services/projectx/index.js +0 -443
- package/src/services/projectx/market.js +0 -172
- package/src/services/projectx/stats.js +0 -110
- package/src/services/projectx/trading.js +0 -180
- package/src/services/rithmic/latency-tracker.js +0 -182
- package/src/services/rithmic/market-data-decoders.js +0 -229
- package/src/services/rithmic/market-data.js +0 -272
- package/src/services/rithmic/orders-fast.js +0 -246
- package/src/services/rithmic/proto-decoders.js +0 -403
- package/src/services/rithmic/specs.js +0 -146
- package/src/services/rithmic/trade-history.js +0 -254
- package/src/services/session-history.js +0 -475
- package/src/services/strategy/hft-signal-calc.js +0 -147
- package/src/services/strategy/hft-tick.js +0 -407
- package/src/services/strategy/recovery-math.js +0 -402
- package/src/services/tradovate/constants.js +0 -109
- package/src/services/tradovate/index.js +0 -392
- package/src/services/tradovate/market.js +0 -47
- package/src/services/tradovate/orders.js +0 -145
- package/src/services/tradovate/websocket.js +0 -97
|
@@ -1,459 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom Strategy - AI-Generated Trading Strategy
|
|
3
|
-
*
|
|
4
|
-
* Flow:
|
|
5
|
-
* 1. User describes strategy in natural language
|
|
6
|
-
* 2. Connected AI agent generates the code
|
|
7
|
-
* 3. Agent tests and validates the code
|
|
8
|
-
* 4. User confirms, enters TARGET/RISK
|
|
9
|
-
* 5. Execution same as one-account
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
const chalk = require('chalk');
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
const os = require('os');
|
|
18
|
-
const readline = require('readline');
|
|
19
|
-
|
|
20
|
-
const { connections } = require('../../services');
|
|
21
|
-
const { AlgoUI, renderSessionSummary, renderMultiSymbolSummary } = require('./ui');
|
|
22
|
-
const { displayBanner, drawBoxHeader, drawBoxHeaderContinue, drawBoxFooter, drawBoxRow, getLogoWidth, centerText } = require('../../ui');
|
|
23
|
-
const { prompts } = require('../../utils');
|
|
24
|
-
const { checkMarketHours } = require('../../services/projectx/market');
|
|
25
|
-
const { FAST_SCALPING } = require('../../config/settings');
|
|
26
|
-
const { PositionManager } = require('../../services/position-manager');
|
|
27
|
-
const { RithmicMarketDataFeed } = require('../../services/rithmic/market-data');
|
|
28
|
-
const { algoLogger } = require('./logger');
|
|
29
|
-
const { recoveryMath } = require('../../services/strategy/recovery-math');
|
|
30
|
-
const aiService = require('../../services/ai');
|
|
31
|
-
const { launchMultiSymbolRithmic, selectSymbol, configureAlgo } = require('./one-account');
|
|
32
|
-
const aiClient = require('../../services/ai/client');
|
|
33
|
-
|
|
34
|
-
// Strategy template that the AI will fill
|
|
35
|
-
const STRATEGY_TEMPLATE = `/**
|
|
36
|
-
* Custom Strategy: {{STRATEGY_NAME}}
|
|
37
|
-
* Generated by AI Agent
|
|
38
|
-
*
|
|
39
|
-
* Description: {{STRATEGY_DESCRIPTION}}
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
const EventEmitter = require('events');
|
|
43
|
-
|
|
44
|
-
class CustomStrategy extends EventEmitter {
|
|
45
|
-
constructor() {
|
|
46
|
-
super();
|
|
47
|
-
this.tickSize = null;
|
|
48
|
-
this.tickValue = null;
|
|
49
|
-
this.contractId = null;
|
|
50
|
-
this.initialized = false;
|
|
51
|
-
this.tickCount = 0;
|
|
52
|
-
this.lastSignalTime = 0;
|
|
53
|
-
this.cooldownMs = 5000;
|
|
54
|
-
|
|
55
|
-
// Strategy-specific state
|
|
56
|
-
{{STRATEGY_STATE}}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
initialize(contractId, tickSize, tickValue) {
|
|
60
|
-
if (!contractId || tickSize === undefined || tickValue === undefined) {
|
|
61
|
-
throw new Error('Strategy requires contractId, tickSize, and tickValue from API');
|
|
62
|
-
}
|
|
63
|
-
this.contractId = contractId;
|
|
64
|
-
this.tickSize = tickSize;
|
|
65
|
-
this.tickValue = tickValue;
|
|
66
|
-
this.initialized = true;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
processTick(tick) {
|
|
70
|
-
if (!this.initialized) return;
|
|
71
|
-
this.tickCount++;
|
|
72
|
-
|
|
73
|
-
const { price, bid, ask, volume, side, timestamp } = tick;
|
|
74
|
-
|
|
75
|
-
// Strategy logic
|
|
76
|
-
{{STRATEGY_LOGIC}}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
_emitSignal(direction, confidence, entry) {
|
|
80
|
-
const now = Date.now();
|
|
81
|
-
if (now - this.lastSignalTime < this.cooldownMs) return;
|
|
82
|
-
this.lastSignalTime = now;
|
|
83
|
-
|
|
84
|
-
const signal = {
|
|
85
|
-
id: \`custom-\${now}\`,
|
|
86
|
-
timestamp: now,
|
|
87
|
-
contractId: this.contractId,
|
|
88
|
-
direction,
|
|
89
|
-
confidence,
|
|
90
|
-
entry,
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
this.emit('signal', signal);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
getState() {
|
|
97
|
-
return {
|
|
98
|
-
tickCount: this.tickCount,
|
|
99
|
-
{{STRATEGY_GET_STATE}}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
getModelValues() {
|
|
104
|
-
return this.getState();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
reset() {
|
|
108
|
-
this.tickCount = 0;
|
|
109
|
-
this.lastSignalTime = 0;
|
|
110
|
-
{{STRATEGY_RESET}}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
module.exports = { CustomStrategy };
|
|
115
|
-
`;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get strategy directory
|
|
119
|
-
*/
|
|
120
|
-
function getStrategyDir() {
|
|
121
|
-
const dir = path.join(os.homedir(), '.hqx', 'strategies');
|
|
122
|
-
if (!fs.existsSync(dir)) {
|
|
123
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
124
|
-
}
|
|
125
|
-
return dir;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Generate strategy code using AI agent
|
|
130
|
-
*/
|
|
131
|
-
async function generateStrategyCode(description, agentName) {
|
|
132
|
-
const agents = aiService.getAgents();
|
|
133
|
-
const agent = agents.find(a => a.name === agentName || a.provider === agentName);
|
|
134
|
-
if (!agent) {
|
|
135
|
-
return { success: false, error: 'No AI agent available' };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const systemPrompt = `You are a trading strategy code generator for HQX-CLI.
|
|
139
|
-
Generate trading strategies based on user descriptions.
|
|
140
|
-
You must output ONLY valid JSON with the strategy components, no markdown, no explanation.`;
|
|
141
|
-
|
|
142
|
-
const prompt = `Generate a trading strategy based on this description:
|
|
143
|
-
|
|
144
|
-
"${description}"
|
|
145
|
-
|
|
146
|
-
Fill in these template sections:
|
|
147
|
-
|
|
148
|
-
1. STRATEGY_NAME: A short name for the strategy (string)
|
|
149
|
-
2. STRATEGY_DESCRIPTION: One line description (string)
|
|
150
|
-
3. STRATEGY_STATE: Variable declarations for strategy state (e.g., "this.ema = 0; this.prices = [];")
|
|
151
|
-
4. STRATEGY_LOGIC: The main logic that processes each tick and calls this._emitSignal(direction, confidence, price) when conditions are met
|
|
152
|
-
- direction: 'long' or 'short'
|
|
153
|
-
- confidence: number between 0 and 1
|
|
154
|
-
- price: the entry price
|
|
155
|
-
5. STRATEGY_GET_STATE: Return additional state variables (e.g., "ema: this.ema,")
|
|
156
|
-
6. STRATEGY_RESET: Reset strategy-specific state (e.g., "this.ema = 0; this.prices = [];")
|
|
157
|
-
|
|
158
|
-
Available in processTick:
|
|
159
|
-
- tick.price, tick.bid, tick.ask, tick.volume, tick.side, tick.timestamp
|
|
160
|
-
- this.tickSize, this.tickValue (contract specs)
|
|
161
|
-
- this.tickCount (number of ticks processed)
|
|
162
|
-
|
|
163
|
-
Output format (JSON only):
|
|
164
|
-
{
|
|
165
|
-
"STRATEGY_NAME": "...",
|
|
166
|
-
"STRATEGY_DESCRIPTION": "...",
|
|
167
|
-
"STRATEGY_STATE": "...",
|
|
168
|
-
"STRATEGY_LOGIC": "...",
|
|
169
|
-
"STRATEGY_GET_STATE": "...",
|
|
170
|
-
"STRATEGY_RESET": "..."
|
|
171
|
-
}`;
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
// Use aiClient.callAI which handles all providers correctly
|
|
175
|
-
const response = await aiClient.callAI(agent, prompt, systemPrompt);
|
|
176
|
-
|
|
177
|
-
if (!response) {
|
|
178
|
-
return { success: false, error: 'AI request failed - no response' };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Parse JSON response
|
|
182
|
-
let strategyParts;
|
|
183
|
-
try {
|
|
184
|
-
// Extract JSON from response (might have markdown)
|
|
185
|
-
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
186
|
-
if (jsonMatch) {
|
|
187
|
-
strategyParts = JSON.parse(jsonMatch[0]);
|
|
188
|
-
} else {
|
|
189
|
-
return { success: false, error: 'AI response did not contain valid JSON' };
|
|
190
|
-
}
|
|
191
|
-
} catch (parseError) {
|
|
192
|
-
return { success: false, error: `Failed to parse AI response: ${parseError.message}` };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Build the code
|
|
196
|
-
let code = STRATEGY_TEMPLATE;
|
|
197
|
-
code = code.replace('{{STRATEGY_NAME}}', strategyParts.STRATEGY_NAME || 'Custom Strategy');
|
|
198
|
-
code = code.replace('{{STRATEGY_DESCRIPTION}}', strategyParts.STRATEGY_DESCRIPTION || description);
|
|
199
|
-
code = code.replace('{{STRATEGY_STATE}}', strategyParts.STRATEGY_STATE || '');
|
|
200
|
-
code = code.replace('{{STRATEGY_LOGIC}}', strategyParts.STRATEGY_LOGIC || '// No logic generated');
|
|
201
|
-
code = code.replace('{{STRATEGY_GET_STATE}}', strategyParts.STRATEGY_GET_STATE || '');
|
|
202
|
-
code = code.replace('{{STRATEGY_RESET}}', strategyParts.STRATEGY_RESET || '');
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
success: true,
|
|
206
|
-
code,
|
|
207
|
-
name: strategyParts.STRATEGY_NAME || 'Custom Strategy',
|
|
208
|
-
};
|
|
209
|
-
} catch (error) {
|
|
210
|
-
return { success: false, error: error.message };
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Validate generated strategy code
|
|
216
|
-
*/
|
|
217
|
-
async function validateStrategyCode(code, filepath) {
|
|
218
|
-
const errors = [];
|
|
219
|
-
|
|
220
|
-
// 1. Write to file
|
|
221
|
-
try {
|
|
222
|
-
fs.writeFileSync(filepath, code, 'utf8');
|
|
223
|
-
} catch (e) {
|
|
224
|
-
return { success: false, errors: [`Failed to write file: ${e.message}`] };
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// 2. Syntax check - try to require it
|
|
228
|
-
try {
|
|
229
|
-
// Clear require cache
|
|
230
|
-
delete require.cache[require.resolve(filepath)];
|
|
231
|
-
const mod = require(filepath);
|
|
232
|
-
|
|
233
|
-
if (!mod.CustomStrategy) {
|
|
234
|
-
errors.push('Module does not export CustomStrategy class');
|
|
235
|
-
} else {
|
|
236
|
-
// 3. Instantiation test
|
|
237
|
-
const instance = new mod.CustomStrategy();
|
|
238
|
-
|
|
239
|
-
// 4. Check required methods
|
|
240
|
-
if (typeof instance.initialize !== 'function') {
|
|
241
|
-
errors.push('Missing initialize() method');
|
|
242
|
-
}
|
|
243
|
-
if (typeof instance.processTick !== 'function') {
|
|
244
|
-
errors.push('Missing processTick() method');
|
|
245
|
-
}
|
|
246
|
-
if (typeof instance.getState !== 'function') {
|
|
247
|
-
errors.push('Missing getState() method');
|
|
248
|
-
}
|
|
249
|
-
if (typeof instance.on !== 'function') {
|
|
250
|
-
errors.push('Strategy must extend EventEmitter');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 5. Dry-run with fake tick
|
|
254
|
-
try {
|
|
255
|
-
instance.initialize('TEST', 0.25, 12.50);
|
|
256
|
-
instance.processTick({
|
|
257
|
-
price: 5000,
|
|
258
|
-
bid: 4999.75,
|
|
259
|
-
ask: 5000.25,
|
|
260
|
-
volume: 1,
|
|
261
|
-
side: 'BUY',
|
|
262
|
-
timestamp: Date.now(),
|
|
263
|
-
});
|
|
264
|
-
// Process a few more ticks
|
|
265
|
-
for (let i = 0; i < 10; i++) {
|
|
266
|
-
instance.processTick({
|
|
267
|
-
price: 5000 + (Math.random() - 0.5) * 2,
|
|
268
|
-
bid: 4999.75,
|
|
269
|
-
ask: 5000.25,
|
|
270
|
-
volume: 1,
|
|
271
|
-
side: Math.random() > 0.5 ? 'BUY' : 'SELL',
|
|
272
|
-
timestamp: Date.now(),
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
} catch (e) {
|
|
276
|
-
errors.push(`Runtime error in processTick: ${e.message}`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
} catch (e) {
|
|
280
|
-
errors.push(`Syntax/Import error: ${e.message}`);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (errors.length > 0) {
|
|
284
|
-
return { success: false, errors };
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return { success: true, errors: [] };
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Custom Strategy Menu
|
|
292
|
-
*/
|
|
293
|
-
const customStrategyMenu = async (service) => {
|
|
294
|
-
console.clear();
|
|
295
|
-
displayBanner();
|
|
296
|
-
|
|
297
|
-
// Check if AI agent is connected
|
|
298
|
-
const agents = aiService.getAgents();
|
|
299
|
-
if (!agents || agents.length === 0) {
|
|
300
|
-
console.log(chalk.red('\n No AI agent connected.'));
|
|
301
|
-
console.log(chalk.gray(' Connect an AI agent in AI SETTINGS first.\n'));
|
|
302
|
-
await prompts.waitForEnter();
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const agentName = agents[0].name || agents[0].provider;
|
|
307
|
-
const boxWidth = Math.max(getLogoWidth(), 98);
|
|
308
|
-
|
|
309
|
-
// Header aligned with main banner
|
|
310
|
-
drawBoxHeaderContinue('CUSTOM STRATEGY - AI GENERATED', boxWidth);
|
|
311
|
-
drawBoxRow(`Agent: ${agentName}`, boxWidth);
|
|
312
|
-
drawBoxFooter(boxWidth);
|
|
313
|
-
|
|
314
|
-
// Step 1: Get strategy description from user
|
|
315
|
-
console.log(chalk.yellow(' Describe your trading strategy in natural language:'));
|
|
316
|
-
console.log(chalk.gray(' (Example: "Buy when price crosses above 20-period EMA and RSI < 30")'));
|
|
317
|
-
console.log(chalk.gray(' (Example: "Scalp long when 3 consecutive green ticks with increasing volume")\n'));
|
|
318
|
-
|
|
319
|
-
const description = await prompts.textInput(chalk.cyan(' STRATEGY: '));
|
|
320
|
-
if (!description || description.trim().length < 10) {
|
|
321
|
-
console.log(chalk.red('\n Strategy description too short. Minimum 10 characters.\n'));
|
|
322
|
-
await prompts.waitForEnter();
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Step 2: Generate strategy code
|
|
327
|
-
console.log(chalk.cyan('\n Generating strategy code...'));
|
|
328
|
-
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
329
|
-
let spinnerIdx = 0;
|
|
330
|
-
const spinnerInterval = setInterval(() => {
|
|
331
|
-
process.stdout.write(`\r ${spinner[spinnerIdx++ % spinner.length]} Generating...`);
|
|
332
|
-
}, 100);
|
|
333
|
-
|
|
334
|
-
const genResult = await generateStrategyCode(description, agentName);
|
|
335
|
-
clearInterval(spinnerInterval);
|
|
336
|
-
process.stdout.write('\r \r');
|
|
337
|
-
|
|
338
|
-
if (!genResult.success) {
|
|
339
|
-
console.log(chalk.red(`\n Failed to generate strategy: ${genResult.error}\n`));
|
|
340
|
-
await prompts.waitForEnter();
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
console.log(chalk.green(` ✓ Strategy "${genResult.name}" generated`));
|
|
345
|
-
|
|
346
|
-
// Step 3: Save and validate
|
|
347
|
-
const timestamp = Date.now();
|
|
348
|
-
const filename = `custom_${timestamp}.js`;
|
|
349
|
-
const filepath = path.join(getStrategyDir(), filename);
|
|
350
|
-
|
|
351
|
-
console.log(chalk.cyan(' Validating strategy...'));
|
|
352
|
-
|
|
353
|
-
const validation = await validateStrategyCode(genResult.code, filepath);
|
|
354
|
-
|
|
355
|
-
if (!validation.success) {
|
|
356
|
-
console.log(chalk.red('\n Strategy validation FAILED:'));
|
|
357
|
-
for (const err of validation.errors) {
|
|
358
|
-
console.log(chalk.red(` - ${err}`));
|
|
359
|
-
}
|
|
360
|
-
console.log(chalk.gray(`\n Code saved to: ${filepath}`));
|
|
361
|
-
console.log(chalk.gray(' You can manually fix and retry.\n'));
|
|
362
|
-
await prompts.waitForEnter();
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
console.log(chalk.green(' ✓ Strategy validated successfully'));
|
|
367
|
-
console.log(chalk.gray(` Saved to: ${filepath}\n`));
|
|
368
|
-
|
|
369
|
-
// Step 4: Show strategy summary and confirm
|
|
370
|
-
console.log('');
|
|
371
|
-
drawBoxHeader('STRATEGY READY', boxWidth);
|
|
372
|
-
drawBoxRow(` Name: ${genResult.name}`, boxWidth);
|
|
373
|
-
drawBoxRow(` File: ${filename}`, boxWidth);
|
|
374
|
-
drawBoxFooter(boxWidth);
|
|
375
|
-
console.log('');
|
|
376
|
-
|
|
377
|
-
const confirm = await prompts.textInput(chalk.cyan(' Continue with this strategy? (Y/n): '));
|
|
378
|
-
if (confirm.toLowerCase() === 'n') {
|
|
379
|
-
console.log(chalk.yellow('\n Cancelled.\n'));
|
|
380
|
-
await prompts.waitForEnter();
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Step 5: Continue with normal one-account flow
|
|
385
|
-
// Load the custom strategy
|
|
386
|
-
const CustomStrategyModule = require(filepath);
|
|
387
|
-
|
|
388
|
-
// Pass to execution with the custom strategy
|
|
389
|
-
await executeWithCustomStrategy(service, CustomStrategyModule.CustomStrategy, genResult.name);
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Execute trading with custom strategy
|
|
394
|
-
* Uses same flow as one-account.js
|
|
395
|
-
*/
|
|
396
|
-
async function executeWithCustomStrategy(service, StrategyClass, strategyName) {
|
|
397
|
-
const { connections } = require('../../services');
|
|
398
|
-
|
|
399
|
-
// Get all accounts (same as one-account.js)
|
|
400
|
-
const allAccounts = await connections.getAllAccounts();
|
|
401
|
-
if (!allAccounts || allAccounts.length === 0) {
|
|
402
|
-
console.log(chalk.red('\n No trading accounts available.\n'));
|
|
403
|
-
await prompts.waitForEnter();
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Filter active accounts
|
|
408
|
-
const activeAccounts = allAccounts.filter(acc => acc.status === 'active' || !acc.status);
|
|
409
|
-
if (activeAccounts.length === 0) {
|
|
410
|
-
console.log(chalk.red('\n No active trading accounts.\n'));
|
|
411
|
-
await prompts.waitForEnter();
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Account selection - same format as one-account.js
|
|
416
|
-
const options = activeAccounts.map(acc => {
|
|
417
|
-
const name = acc.accountName || acc.rithmicAccountId || acc.accountId;
|
|
418
|
-
const balance = acc.balance !== null && acc.balance !== undefined
|
|
419
|
-
? ` - $${acc.balance.toLocaleString()}`
|
|
420
|
-
: '';
|
|
421
|
-
return {
|
|
422
|
-
label: `${name} (${acc.propfirm || acc.platform || 'Unknown'})${balance}`,
|
|
423
|
-
value: acc
|
|
424
|
-
};
|
|
425
|
-
});
|
|
426
|
-
options.push({ label: '< BACK', value: 'back' });
|
|
427
|
-
|
|
428
|
-
const selectedAccount = await prompts.selectOption('SELECT ACCOUNT:', options);
|
|
429
|
-
if (!selectedAccount || selectedAccount === 'back') return;
|
|
430
|
-
|
|
431
|
-
// Get the service for this account
|
|
432
|
-
const accountService = selectedAccount.service || connections.getServiceForAccount(selectedAccount.accountId) || service;
|
|
433
|
-
|
|
434
|
-
// Select symbol(s) - same as one-account.js
|
|
435
|
-
console.log();
|
|
436
|
-
const useMultiSymbol = await prompts.confirmPrompt(chalk.cyan('MULTI-SYMBOL MODE? (up to 5 symbols)'), false);
|
|
437
|
-
if (useMultiSymbol === null) return;
|
|
438
|
-
|
|
439
|
-
const contracts = await selectSymbol(accountService, selectedAccount, useMultiSymbol);
|
|
440
|
-
if (!contracts) return;
|
|
441
|
-
|
|
442
|
-
// Normalize to array
|
|
443
|
-
const contractList = Array.isArray(contracts) ? contracts : [contracts];
|
|
444
|
-
|
|
445
|
-
// Configure algo - same as one-account.js
|
|
446
|
-
const config = await configureAlgo(selectedAccount, contractList);
|
|
447
|
-
if (!config) return;
|
|
448
|
-
|
|
449
|
-
// Override: Custom strategy doesn't need AI supervisor (it IS the AI strategy)
|
|
450
|
-
config.enableAI = false;
|
|
451
|
-
|
|
452
|
-
// Show custom strategy info
|
|
453
|
-
console.log(chalk.magenta(`\n CUSTOM STRATEGY: ${strategyName}\n`));
|
|
454
|
-
|
|
455
|
-
// Launch with custom strategy class
|
|
456
|
-
await launchMultiSymbolRithmic(accountService, selectedAccount, contractList, config, StrategyClass);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
module.exports = { customStrategyMenu, generateStrategyCode, validateStrategyCode };
|
package/src/pages/algo/logger.js
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* =============================================================================
|
|
3
|
-
* HQX Algo Trading System - Smart Algo Logger
|
|
4
|
-
* =============================================================================
|
|
5
|
-
* Provides rich, detailed logs for the algo UI
|
|
6
|
-
* Copied from HQX-TG algo-logger.ts - All 47 log types
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
'use strict';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Smart Algo Logger - All 47 log types from HQX-TG
|
|
13
|
-
*/
|
|
14
|
-
const algoLogger = {
|
|
15
|
-
// === MARKET DATA LOGS ===
|
|
16
|
-
quote(ui, symbol, bid, ask, spread) {
|
|
17
|
-
ui.addLog('info', `QUOTE ${symbol} - bid: ${bid.toFixed(2)} | ask: ${ask.toFixed(2)} | spread: ${spread.toFixed(2)}`);
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
tape(ui, symbol, buyVol, sellVol, delta) {
|
|
21
|
-
const sign = delta > 0 ? '+' : '';
|
|
22
|
-
ui.addLog('info', `TAPE ${symbol} - buy: ${buyVol} | sell: ${sellVol} | delta: ${sign}${delta}`);
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
dom(ui, symbol, bidDepth, askDepth, imbalance) {
|
|
26
|
-
const direction = imbalance > 0 ? '▲' : imbalance < 0 ? '▼' : '=';
|
|
27
|
-
ui.addLog('info', `DOM ${symbol} - bidDepth: ${bidDepth} | askDepth: ${askDepth} | imbalance: ${direction}${Math.abs(imbalance).toFixed(1)}%`);
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
volumeSpike(ui, symbol, volume, avgVolume, ratio) {
|
|
31
|
-
ui.addLog('analysis', `VOLUME SPIKE ${symbol} - vol: ${volume} | avg: ${avgVolume.toFixed(0)} | ratio: ${ratio.toFixed(1)}x`);
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
// === ANALYSIS LOGS ===
|
|
35
|
-
orderFlow(ui, symbol, score, direction) {
|
|
36
|
-
const arrow = direction === 'LONG' ? '▲' : direction === 'SHORT' ? '▼' : '=';
|
|
37
|
-
ui.addLog('analysis', `ORDER FLOW ${symbol} - score: ${score.toFixed(0)} | direction: ${arrow} ${direction}`);
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
absorption(ui, symbol, level, side, strength) {
|
|
41
|
-
ui.addLog('analysis', `ABSORPTION ${side} - price: ${level.toFixed(2)} | strength: ${strength.toFixed(0)}%`);
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
sweep(ui, symbol, side, levels, volume) {
|
|
45
|
-
ui.addLog('analysis', `SWEEP ${side} - levels: ${levels} | volume: ${volume}`);
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
iceberg(ui, symbol, price, hiddenSize) {
|
|
49
|
-
ui.addLog('analysis', `ICEBERG DETECTED - price: ${price.toFixed(2)} | hidden: ${hiddenSize}`);
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
deltaDivergence(ui, symbol, priceDir, deltaDir) {
|
|
53
|
-
ui.addLog('analysis', `DELTA DIVERGENCE - price: ${priceDir} | delta: ${deltaDir}`);
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
vpoc(ui, symbol, poc, valueHigh, valueLow) {
|
|
57
|
-
ui.addLog('analysis', `VPOC ${symbol} - poc: ${poc.toFixed(2)} | VAH: ${valueHigh.toFixed(2)} | VAL: ${valueLow.toFixed(2)}`);
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
regime(ui, symbol, regime, confidence) {
|
|
61
|
-
ui.addLog('analysis', `REGIME ${symbol} - ${regime} | confidence: ${confidence.toFixed(0)}%`);
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
volatility(ui, symbol, atr, regime) {
|
|
65
|
-
ui.addLog('analysis', `VOLATILITY ${symbol} - ATR: ${atr.toFixed(2)} | regime: ${regime}`);
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
// === SIGNAL LOGS ===
|
|
69
|
-
signalGenerated(ui, symbol, direction, confidence, strategy) {
|
|
70
|
-
const arrow = direction === 'LONG' ? '▲' : '▼';
|
|
71
|
-
ui.addLog('signal', `SIGNAL ${arrow} ${direction} - ${symbol} | conf: ${confidence.toFixed(0)}% | strategy: ${strategy}`);
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
signalRejected(ui, symbol, reason) {
|
|
75
|
-
ui.addLog('warning', `SIGNAL REJECTED - ${symbol} | reason: ${reason}`);
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
signalExpired(ui, symbol) {
|
|
79
|
-
ui.addLog('info', `SIGNAL EXPIRED - ${symbol}`);
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
// === TRADE LOGS ===
|
|
83
|
-
orderSubmitted(ui, symbol, side, size, price) {
|
|
84
|
-
ui.addLog('trade', `ORDER SUBMITTED - ${side} ${size} ${symbol} @ ${price.toFixed(2)}`);
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
orderFilled(ui, symbol, side, size, price) {
|
|
88
|
-
ui.addLog('trade', `ORDER FILLED - ${side} ${size} ${symbol} @ ${price.toFixed(2)}`);
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
orderRejected(ui, symbol, reason) {
|
|
92
|
-
ui.addLog('error', `ORDER REJECTED - ${symbol} | ${reason}`);
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
orderCancelled(ui, symbol, orderId) {
|
|
96
|
-
ui.addLog('warning', `ORDER CANCELLED - ${symbol} | id: ${orderId}`);
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
positionOpened(ui, symbol, side, size, entry) {
|
|
100
|
-
const arrow = side === 'LONG' ? '▲' : '▼';
|
|
101
|
-
ui.addLog('trade', `POSITION OPENED ${arrow} - ${side} ${size} ${symbol} @ ${entry.toFixed(2)}`);
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
positionClosed(ui, symbol, side, size, exit, pnl) {
|
|
105
|
-
const pnlStr = pnl >= 0 ? `+$${pnl.toFixed(2)}` : `-$${Math.abs(pnl).toFixed(2)}`;
|
|
106
|
-
const type = pnl >= 0 ? 'trade' : 'warning';
|
|
107
|
-
ui.addLog(type, `POSITION CLOSED - ${side} ${size} ${symbol} @ ${exit.toFixed(2)} | PnL: ${pnlStr}`);
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
stopHit(ui, symbol, price, loss) {
|
|
111
|
-
ui.addLog('warning', `STOP LOSS HIT - ${symbol} @ ${price.toFixed(2)} | loss: -$${Math.abs(loss).toFixed(2)}`);
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
targetHit(ui, symbol, price, profit) {
|
|
115
|
-
ui.addLog('trade', `TARGET HIT - ${symbol} @ ${price.toFixed(2)} | profit: +$${profit.toFixed(2)}`);
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
trailingStopMoved(ui, symbol, oldStop, newStop) {
|
|
119
|
-
ui.addLog('info', `TRAILING STOP MOVED - ${symbol} | ${oldStop.toFixed(2)} → ${newStop.toFixed(2)}`);
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
fillSync(ui, side, size, price, orderId) {
|
|
123
|
-
ui.addLog('trade', `FILL (SYNC) - ${side} ${size} @ ${price.toFixed(2)} (order #${orderId})`);
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
entryConfirmed(ui, side, size, symbol, price) {
|
|
127
|
-
ui.addLog('info', `ENTRY CONFIRMED - ${side} ${size}x ${symbol} @ ${price.toFixed(2)}`);
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
stopsSet(ui, sl, tp) {
|
|
131
|
-
ui.addLog('info', `STOPS SET - SL: ${sl.toFixed(2)} | TP: ${tp.toFixed(2)}`);
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
// === RISK LOGS ===
|
|
135
|
-
riskCheck(ui, passed, reason) {
|
|
136
|
-
const type = passed ? 'info' : 'warning';
|
|
137
|
-
ui.addLog(type, `RISK CHECK ${passed ? 'PASSED' : 'BLOCKED'} - ${reason}`);
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
dailyLimitWarning(ui, current, limit) {
|
|
141
|
-
ui.addLog('warning', `DAILY LIMIT WARNING - PnL: $${current.toFixed(2)} / limit: $${limit.toFixed(2)}`);
|
|
142
|
-
},
|
|
143
|
-
|
|
144
|
-
maxDrawdownWarning(ui, current, max) {
|
|
145
|
-
ui.addLog('warning', `DRAWDOWN WARNING - current: ${current.toFixed(1)}% / max: ${max.toFixed(1)}%`);
|
|
146
|
-
},
|
|
147
|
-
|
|
148
|
-
positionSized(ui, contracts, kelly, riskAmount, riskPct) {
|
|
149
|
-
ui.addLog('info', `POSITION SIZE - ${contracts} contracts | kelly: ${kelly.toFixed(2)} | risk: $${riskAmount} (${riskPct}% of max)`);
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
bracketSet(ui, stopTicks, targetTicks, rr) {
|
|
153
|
-
ui.addLog('info', `BRACKET SET - SL: ${stopTicks}t | TP: ${targetTicks}t | R:R: ${rr.toFixed(1)}`);
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
// === STRATEGY LOGS ===
|
|
157
|
-
strategySelected(ui, strategy, session, regime) {
|
|
158
|
-
ui.addLog('info', `STRATEGY SELECTED - ${strategy} | session: ${session} | regime: ${regime}`);
|
|
159
|
-
},
|
|
160
|
-
|
|
161
|
-
strategySwitch(ui, from, to, reason) {
|
|
162
|
-
ui.addLog('info', `STRATEGY SWITCH - ${from} → ${to} | ${reason}`);
|
|
163
|
-
},
|
|
164
|
-
|
|
165
|
-
// === SESSION LOGS ===
|
|
166
|
-
sessionStart(ui, session) {
|
|
167
|
-
ui.addLog('info', `SESSION START - ${session}`);
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
sessionEnd(ui, session) {
|
|
171
|
-
ui.addLog('info', `SESSION END - ${session}`);
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
marketOpen(ui, session, etTime) {
|
|
175
|
-
ui.addLog('info', `MARKET OPEN - ${session} SESSION | ET: ${etTime} ET`);
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
marketClosed(ui) {
|
|
179
|
-
ui.addLog('warning', `MARKET CLOSED - Trading paused`);
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
// === SYSTEM LOGS ===
|
|
183
|
-
connectingToEngine(ui, accountId) {
|
|
184
|
-
ui.addLog('info', `CONNECTING TO ALGO ENGINE... - Account: ${accountId}`);
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
engineStarting(ui, platform, dailyTarget, dailyRisk) {
|
|
188
|
-
ui.addLog('info', `ALGO ENGINE STARTING... - Platform: ${platform} | Daily Target: $${dailyTarget} | Daily Risk: $${dailyRisk}`);
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
engineStarted(ui, platform, status) {
|
|
192
|
-
ui.addLog('info', `ALGO ENGINE STARTED - Status: ${status}`);
|
|
193
|
-
},
|
|
194
|
-
|
|
195
|
-
engineStopped(ui, reason) {
|
|
196
|
-
ui.addLog('info', `ENGINE STOPPED - ${reason || 'All positions flat'}`);
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
algoOperational(ui, platform) {
|
|
200
|
-
ui.addLog('info', `ALGO FULLY OPERATIONAL - Connected to ${platform} - Scanning for alpha...`);
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
dataConnected(ui, source) {
|
|
204
|
-
ui.addLog('info', `WEBSOCKET CONNECTED - Real-time updates enabled`);
|
|
205
|
-
},
|
|
206
|
-
|
|
207
|
-
dataDisconnected(ui, source, reason) {
|
|
208
|
-
ui.addLog('warning', `WEBSOCKET DISCONNECTED - Attempting to reconnect...`);
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
heartbeat(ui, tps, latency) {
|
|
212
|
-
ui.addLog('info', `HEARTBEAT - tps: ${tps} | latency: ${latency}ms`);
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
latencyReport(ui, latency) {
|
|
216
|
-
ui.addLog('analysis', `LATENCY - ${latency}ms order-to-fill`);
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
// === GENERIC ===
|
|
220
|
-
info(ui, message, details) {
|
|
221
|
-
ui.addLog('info', details ? `${message} - ${details}` : message);
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
warning(ui, message, details) {
|
|
225
|
-
ui.addLog('warning', details ? `${message} - ${details}` : message);
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
error(ui, message, details) {
|
|
229
|
-
ui.addLog('error', details ? `${message} - ${details}` : message);
|
|
230
|
-
},
|
|
231
|
-
|
|
232
|
-
signal(ui, message, details) {
|
|
233
|
-
ui.addLog('signal', details ? `${message} - ${details}` : message);
|
|
234
|
-
},
|
|
235
|
-
|
|
236
|
-
trade(ui, message, details) {
|
|
237
|
-
ui.addLog('trade', details ? `${message} - ${details}` : message);
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
analysis(ui, message, details) {
|
|
241
|
-
ui.addLog('analysis', details ? `${message} - ${details}` : message);
|
|
242
|
-
},
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
module.exports = { algoLogger };
|