backtest-kit 1.5.24 → 1.5.26
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 +51 -7
- package/build/index.cjs +1618 -959
- package/build/index.mjs +1617 -961
- package/package.json +2 -2
- package/types.d.ts +4914 -4338
package/build/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, ToolRegistry, isObject, and, resolveDocuments,
|
|
1
|
+
import { str, errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, ToolRegistry, isObject, and, resolveDocuments, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
|
|
2
2
|
import { createActivator } from 'di-kit';
|
|
3
3
|
import { scoped } from 'di-scoped';
|
|
4
4
|
import fs, { mkdir, writeFile } from 'fs/promises';
|
|
@@ -120,6 +120,1081 @@ const GLOBAL_CONFIG = {
|
|
|
120
120
|
};
|
|
121
121
|
const DEFAULT_CONFIG = Object.freeze({ ...GLOBAL_CONFIG });
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Converts markdown content to plain text with minimal formatting
|
|
125
|
+
* @param content - Markdown string to convert
|
|
126
|
+
* @returns Plain text representation
|
|
127
|
+
*/
|
|
128
|
+
const toPlainString = (content) => {
|
|
129
|
+
if (!content) {
|
|
130
|
+
return "";
|
|
131
|
+
}
|
|
132
|
+
let text = content;
|
|
133
|
+
// Remove code blocks
|
|
134
|
+
text = text.replace(/```[\s\S]*?```/g, "");
|
|
135
|
+
text = text.replace(/`([^`]+)`/g, "$1");
|
|
136
|
+
// Remove images
|
|
137
|
+
text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1");
|
|
138
|
+
// Convert links to text only (keep link text, remove URL)
|
|
139
|
+
text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
140
|
+
// Remove headers (convert to plain text)
|
|
141
|
+
text = text.replace(/^#{1,6}\s+(.+)$/gm, "$1");
|
|
142
|
+
// Remove bold and italic markers
|
|
143
|
+
text = text.replace(/\*\*\*(.+?)\*\*\*/g, "$1");
|
|
144
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
145
|
+
text = text.replace(/\*(.+?)\*/g, "$1");
|
|
146
|
+
text = text.replace(/___(.+?)___/g, "$1");
|
|
147
|
+
text = text.replace(/__(.+?)__/g, "$1");
|
|
148
|
+
text = text.replace(/_(.+?)_/g, "$1");
|
|
149
|
+
// Remove strikethrough
|
|
150
|
+
text = text.replace(/~~(.+?)~~/g, "$1");
|
|
151
|
+
// Convert lists to plain text with bullets
|
|
152
|
+
text = text.replace(/^\s*[-*+]\s+/gm, "• ");
|
|
153
|
+
text = text.replace(/^\s*\d+\.\s+/gm, "• ");
|
|
154
|
+
// Remove blockquotes
|
|
155
|
+
text = text.replace(/^\s*>\s+/gm, "");
|
|
156
|
+
// Remove horizontal rules
|
|
157
|
+
text = text.replace(/^(\*{3,}|-{3,}|_{3,})$/gm, "");
|
|
158
|
+
// Remove HTML tags
|
|
159
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
160
|
+
// Remove excessive whitespace and normalize line breaks
|
|
161
|
+
text = text.replace(/\n[\s\n]*\n/g, "\n");
|
|
162
|
+
text = text.replace(/[ \t]+/g, " ");
|
|
163
|
+
// Remove all newline characters
|
|
164
|
+
text = text.replace(/\n/g, " ");
|
|
165
|
+
// Remove excessive spaces after newline removal
|
|
166
|
+
text = text.replace(/\s+/g, " ");
|
|
167
|
+
return text.trim();
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Column configuration for backtest markdown reports.
|
|
172
|
+
*
|
|
173
|
+
* Defines the table structure for displaying closed trading signals in backtest reports.
|
|
174
|
+
* Each column specifies how to format and display specific signal data fields.
|
|
175
|
+
*
|
|
176
|
+
* Used by {@link BacktestMarkdownService} to generate markdown tables showing:
|
|
177
|
+
* - Signal identification (ID, symbol, position)
|
|
178
|
+
* - Price levels (open, close, take profit, stop loss)
|
|
179
|
+
* - Performance metrics (PNL percentage, close reason)
|
|
180
|
+
* - Timing information (duration, timestamps)
|
|
181
|
+
*
|
|
182
|
+
* @remarks
|
|
183
|
+
* Columns can be conditionally visible based on {@link GLOBAL_CONFIG} settings.
|
|
184
|
+
* For example, the "note" column visibility is controlled by `CC_REPORT_SHOW_SIGNAL_NOTE`.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* import { backtest_columns } from "./assets/backtest.columns";
|
|
189
|
+
*
|
|
190
|
+
* // Use with BacktestMarkdownService
|
|
191
|
+
* const service = new BacktestMarkdownService();
|
|
192
|
+
* await service.getReport("BTCUSDT", "my-strategy", backtest_columns);
|
|
193
|
+
*
|
|
194
|
+
* // Or customize columns
|
|
195
|
+
* const customColumns = backtest_columns.filter(col => col.key !== "note");
|
|
196
|
+
* await service.getReport("BTCUSDT", "my-strategy", customColumns);
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* @see {@link BacktestMarkdownService} for usage in report generation
|
|
200
|
+
* @see {@link ColumnModel} for column interface definition
|
|
201
|
+
* @see {@link IStrategyTickResultClosed} for data structure
|
|
202
|
+
*/
|
|
203
|
+
const backtest_columns = [
|
|
204
|
+
{
|
|
205
|
+
key: "signalId",
|
|
206
|
+
label: "Signal ID",
|
|
207
|
+
format: (data) => data.signal.id,
|
|
208
|
+
isVisible: () => true,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
key: "symbol",
|
|
212
|
+
label: "Symbol",
|
|
213
|
+
format: (data) => data.signal.symbol,
|
|
214
|
+
isVisible: () => true,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
key: "position",
|
|
218
|
+
label: "Position",
|
|
219
|
+
format: (data) => data.signal.position.toUpperCase(),
|
|
220
|
+
isVisible: () => true,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
key: "note",
|
|
224
|
+
label: "Note",
|
|
225
|
+
format: (data) => toPlainString(data.signal.note ?? "N/A"),
|
|
226
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
key: "openPrice",
|
|
230
|
+
label: "Open Price",
|
|
231
|
+
format: (data) => `${data.signal.priceOpen.toFixed(8)} USD`,
|
|
232
|
+
isVisible: () => true,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
key: "closePrice",
|
|
236
|
+
label: "Close Price",
|
|
237
|
+
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
238
|
+
isVisible: () => true,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
key: "takeProfit",
|
|
242
|
+
label: "Take Profit",
|
|
243
|
+
format: (data) => `${data.signal.priceTakeProfit.toFixed(8)} USD`,
|
|
244
|
+
isVisible: () => true,
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
key: "stopLoss",
|
|
248
|
+
label: "Stop Loss",
|
|
249
|
+
format: (data) => `${data.signal.priceStopLoss.toFixed(8)} USD`,
|
|
250
|
+
isVisible: () => true,
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
key: "pnl",
|
|
254
|
+
label: "PNL (net)",
|
|
255
|
+
format: (data) => {
|
|
256
|
+
const pnlPercentage = data.pnl.pnlPercentage;
|
|
257
|
+
return `${pnlPercentage > 0 ? "+" : ""}${pnlPercentage.toFixed(2)}%`;
|
|
258
|
+
},
|
|
259
|
+
isVisible: () => true,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
key: "closeReason",
|
|
263
|
+
label: "Close Reason",
|
|
264
|
+
format: (data) => data.closeReason,
|
|
265
|
+
isVisible: () => true,
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
key: "duration",
|
|
269
|
+
label: "Duration (min)",
|
|
270
|
+
format: (data) => {
|
|
271
|
+
const durationMs = data.closeTimestamp - data.signal.pendingAt;
|
|
272
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
273
|
+
return `${durationMin}`;
|
|
274
|
+
},
|
|
275
|
+
isVisible: () => true,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
key: "openTimestamp",
|
|
279
|
+
label: "Open Time",
|
|
280
|
+
format: (data) => new Date(data.signal.pendingAt).toISOString(),
|
|
281
|
+
isVisible: () => true,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
key: "closeTimestamp",
|
|
285
|
+
label: "Close Time",
|
|
286
|
+
format: (data) => new Date(data.closeTimestamp).toISOString(),
|
|
287
|
+
isVisible: () => true,
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Column configuration for portfolio heatmap markdown reports.
|
|
293
|
+
*
|
|
294
|
+
* Defines the table structure for displaying aggregated per-symbol statistics in portfolio heatmap reports.
|
|
295
|
+
* Each column specifies how to format and display portfolio performance metrics across different trading symbols.
|
|
296
|
+
*
|
|
297
|
+
* Used by {@link HeatMarkdownService} to generate markdown tables showing:
|
|
298
|
+
* - Symbol identification
|
|
299
|
+
* - Performance metrics (Total PNL, Sharpe Ratio, Profit Factor, Expectancy)
|
|
300
|
+
* - Risk metrics (Win Rate, Average Win/Loss, Max Drawdown)
|
|
301
|
+
* - Trading activity (Total Trades, Win/Loss Streaks)
|
|
302
|
+
*
|
|
303
|
+
* @remarks
|
|
304
|
+
* This configuration is used to create portfolio-wide views that aggregate statistics per symbol.
|
|
305
|
+
* The heatmap service automatically sorts symbols by Sharpe Ratio (best performers first).
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* import { heat_columns } from "./assets/heat.columns";
|
|
310
|
+
*
|
|
311
|
+
* // Use with HeatMarkdownService
|
|
312
|
+
* const service = new HeatMarkdownService();
|
|
313
|
+
* await service.getReport("my-strategy", heat_columns);
|
|
314
|
+
*
|
|
315
|
+
* // Or customize to show only key metrics
|
|
316
|
+
* const customColumns = heat_columns.filter(col =>
|
|
317
|
+
* ["symbol", "totalPnl", "sharpeRatio", "totalTrades"].includes(col.key)
|
|
318
|
+
* );
|
|
319
|
+
* await service.getReport("my-strategy", customColumns);
|
|
320
|
+
* ```
|
|
321
|
+
*
|
|
322
|
+
* @see {@link HeatMarkdownService} for usage in report generation
|
|
323
|
+
* @see {@link ColumnModel} for column interface definition
|
|
324
|
+
* @see {@link IHeatmapRow} for data structure
|
|
325
|
+
*/
|
|
326
|
+
const heat_columns = [
|
|
327
|
+
{
|
|
328
|
+
key: "symbol",
|
|
329
|
+
label: "Symbol",
|
|
330
|
+
format: (data) => data.symbol,
|
|
331
|
+
isVisible: () => true,
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
key: "totalPnl",
|
|
335
|
+
label: "Total PNL",
|
|
336
|
+
format: (data) => data.totalPnl !== null ? str(data.totalPnl, "%+.2f%%") : "N/A",
|
|
337
|
+
isVisible: () => true,
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
key: "sharpeRatio",
|
|
341
|
+
label: "Sharpe",
|
|
342
|
+
format: (data) => data.sharpeRatio !== null ? str(data.sharpeRatio, "%.2f") : "N/A",
|
|
343
|
+
isVisible: () => true,
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
key: "profitFactor",
|
|
347
|
+
label: "PF",
|
|
348
|
+
format: (data) => data.profitFactor !== null ? str(data.profitFactor, "%.2f") : "N/A",
|
|
349
|
+
isVisible: () => true,
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
key: "expectancy",
|
|
353
|
+
label: "Expect",
|
|
354
|
+
format: (data) => data.expectancy !== null ? str(data.expectancy, "%+.2f%%") : "N/A",
|
|
355
|
+
isVisible: () => true,
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
key: "winRate",
|
|
359
|
+
label: "WR",
|
|
360
|
+
format: (data) => data.winRate !== null ? str(data.winRate, "%.1f%%") : "N/A",
|
|
361
|
+
isVisible: () => true,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
key: "avgWin",
|
|
365
|
+
label: "Avg Win",
|
|
366
|
+
format: (data) => data.avgWin !== null ? str(data.avgWin, "%+.2f%%") : "N/A",
|
|
367
|
+
isVisible: () => true,
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
key: "avgLoss",
|
|
371
|
+
label: "Avg Loss",
|
|
372
|
+
format: (data) => data.avgLoss !== null ? str(data.avgLoss, "%+.2f%%") : "N/A",
|
|
373
|
+
isVisible: () => true,
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
key: "maxDrawdown",
|
|
377
|
+
label: "Max DD",
|
|
378
|
+
format: (data) => data.maxDrawdown !== null ? str(-data.maxDrawdown, "%.2f%%") : "N/A",
|
|
379
|
+
isVisible: () => true,
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
key: "maxWinStreak",
|
|
383
|
+
label: "W Streak",
|
|
384
|
+
format: (data) => data.maxWinStreak.toString(),
|
|
385
|
+
isVisible: () => true,
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
key: "maxLossStreak",
|
|
389
|
+
label: "L Streak",
|
|
390
|
+
format: (data) => data.maxLossStreak.toString(),
|
|
391
|
+
isVisible: () => true,
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
key: "totalTrades",
|
|
395
|
+
label: "Trades",
|
|
396
|
+
format: (data) => data.totalTrades.toString(),
|
|
397
|
+
isVisible: () => true,
|
|
398
|
+
},
|
|
399
|
+
];
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Column configuration for live trading markdown reports.
|
|
403
|
+
*
|
|
404
|
+
* Defines the table structure for displaying real-time trading events in live trading reports.
|
|
405
|
+
* Each column specifies how to format and display live trading event data fields.
|
|
406
|
+
*
|
|
407
|
+
* Used by {@link LiveMarkdownService} to generate markdown tables showing:
|
|
408
|
+
* - Event information (timestamp, action type)
|
|
409
|
+
* - Signal identification (symbol, signal ID, position)
|
|
410
|
+
* - Price data (current price, open price, take profit, stop loss)
|
|
411
|
+
* - Performance metrics (PNL percentage, close reason, duration)
|
|
412
|
+
* - Position tracking (percentage to TP/SL)
|
|
413
|
+
*
|
|
414
|
+
* @remarks
|
|
415
|
+
* This configuration tracks all event types: idle, opened, active, and closed signals.
|
|
416
|
+
* The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```typescript
|
|
420
|
+
* import { live_columns } from "./assets/live.columns";
|
|
421
|
+
*
|
|
422
|
+
* // Use with LiveMarkdownService
|
|
423
|
+
* const service = new LiveMarkdownService();
|
|
424
|
+
* await service.getReport("BTCUSDT", "my-strategy", live_columns);
|
|
425
|
+
*
|
|
426
|
+
* // Or customize for minimal display
|
|
427
|
+
* const customColumns = live_columns.filter(col =>
|
|
428
|
+
* ["timestamp", "action", "symbol", "pnl"].includes(col.key)
|
|
429
|
+
* );
|
|
430
|
+
* await service.getReport("BTCUSDT", "my-strategy", customColumns);
|
|
431
|
+
* ```
|
|
432
|
+
*
|
|
433
|
+
* @see {@link LiveMarkdownService} for usage in report generation
|
|
434
|
+
* @see {@link ColumnModel} for column interface definition
|
|
435
|
+
* @see {@link TickEvent} for data structure
|
|
436
|
+
*/
|
|
437
|
+
const live_columns = [
|
|
438
|
+
{
|
|
439
|
+
key: "timestamp",
|
|
440
|
+
label: "Timestamp",
|
|
441
|
+
format: (data) => new Date(data.timestamp).toISOString(),
|
|
442
|
+
isVisible: () => true,
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
key: "action",
|
|
446
|
+
label: "Action",
|
|
447
|
+
format: (data) => data.action.toUpperCase(),
|
|
448
|
+
isVisible: () => true,
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
key: "symbol",
|
|
452
|
+
label: "Symbol",
|
|
453
|
+
format: (data) => data.symbol ?? "N/A",
|
|
454
|
+
isVisible: () => true,
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
key: "signalId",
|
|
458
|
+
label: "Signal ID",
|
|
459
|
+
format: (data) => data.signalId ?? "N/A",
|
|
460
|
+
isVisible: () => true,
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
key: "position",
|
|
464
|
+
label: "Position",
|
|
465
|
+
format: (data) => data.position?.toUpperCase() ?? "N/A",
|
|
466
|
+
isVisible: () => true,
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
key: "note",
|
|
470
|
+
label: "Note",
|
|
471
|
+
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
472
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
key: "currentPrice",
|
|
476
|
+
label: "Current Price",
|
|
477
|
+
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
478
|
+
isVisible: () => true,
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
key: "openPrice",
|
|
482
|
+
label: "Open Price",
|
|
483
|
+
format: (data) => data.openPrice !== undefined ? `${data.openPrice.toFixed(8)} USD` : "N/A",
|
|
484
|
+
isVisible: () => true,
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
key: "takeProfit",
|
|
488
|
+
label: "Take Profit",
|
|
489
|
+
format: (data) => data.takeProfit !== undefined
|
|
490
|
+
? `${data.takeProfit.toFixed(8)} USD`
|
|
491
|
+
: "N/A",
|
|
492
|
+
isVisible: () => true,
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
key: "stopLoss",
|
|
496
|
+
label: "Stop Loss",
|
|
497
|
+
format: (data) => data.stopLoss !== undefined ? `${data.stopLoss.toFixed(8)} USD` : "N/A",
|
|
498
|
+
isVisible: () => true,
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
key: "percentTp",
|
|
502
|
+
label: "% to TP",
|
|
503
|
+
format: (data) => data.percentTp !== undefined ? `${data.percentTp.toFixed(2)}%` : "N/A",
|
|
504
|
+
isVisible: () => true,
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
key: "percentSl",
|
|
508
|
+
label: "% to SL",
|
|
509
|
+
format: (data) => data.percentSl !== undefined ? `${data.percentSl.toFixed(2)}%` : "N/A",
|
|
510
|
+
isVisible: () => true,
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
key: "pnl",
|
|
514
|
+
label: "PNL (net)",
|
|
515
|
+
format: (data) => {
|
|
516
|
+
if (data.pnl === undefined)
|
|
517
|
+
return "N/A";
|
|
518
|
+
return `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`;
|
|
519
|
+
},
|
|
520
|
+
isVisible: () => true,
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
key: "closeReason",
|
|
524
|
+
label: "Close Reason",
|
|
525
|
+
format: (data) => data.closeReason ?? "N/A",
|
|
526
|
+
isVisible: () => true,
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
key: "duration",
|
|
530
|
+
label: "Duration (min)",
|
|
531
|
+
format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
|
|
532
|
+
isVisible: () => true,
|
|
533
|
+
},
|
|
534
|
+
];
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Column configuration for partial profit/loss markdown reports.
|
|
538
|
+
*
|
|
539
|
+
* Defines the table structure for displaying partial position exit events in trading reports.
|
|
540
|
+
* Each column specifies how to format and display partial profit and loss level events.
|
|
541
|
+
*
|
|
542
|
+
* Used by {@link PartialMarkdownService} to generate markdown tables showing:
|
|
543
|
+
* - Event information (action type: profit or loss)
|
|
544
|
+
* - Signal identification (symbol, strategy name, signal ID, position)
|
|
545
|
+
* - Exit level information (percentage level reached)
|
|
546
|
+
* - Price data (current price at partial exit)
|
|
547
|
+
* - Timing information (timestamp, mode: backtest or live)
|
|
548
|
+
*
|
|
549
|
+
* @remarks
|
|
550
|
+
* This configuration tracks partial position exits at predefined profit/loss levels.
|
|
551
|
+
* The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
|
|
552
|
+
* Useful for analyzing risk management strategies and partial exit performance.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```typescript
|
|
556
|
+
* import { partial_columns } from "./assets/partial.columns";
|
|
557
|
+
*
|
|
558
|
+
* // Use with PartialMarkdownService
|
|
559
|
+
* const service = new PartialMarkdownService();
|
|
560
|
+
* await service.getReport("BTCUSDT", "my-strategy", partial_columns);
|
|
561
|
+
*
|
|
562
|
+
* // Or customize to show only key fields
|
|
563
|
+
* const customColumns = partial_columns.filter(col =>
|
|
564
|
+
* ["action", "symbol", "level", "timestamp"].includes(col.key)
|
|
565
|
+
* );
|
|
566
|
+
* await service.getReport("BTCUSDT", "my-strategy", customColumns);
|
|
567
|
+
* ```
|
|
568
|
+
*
|
|
569
|
+
* @see {@link PartialMarkdownService} for usage in report generation
|
|
570
|
+
* @see {@link ColumnModel} for column interface definition
|
|
571
|
+
* @see {@link PartialEvent} for data structure
|
|
572
|
+
*/
|
|
573
|
+
const partial_columns = [
|
|
574
|
+
{
|
|
575
|
+
key: "action",
|
|
576
|
+
label: "Action",
|
|
577
|
+
format: (data) => data.action.toUpperCase(),
|
|
578
|
+
isVisible: () => true,
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
key: "symbol",
|
|
582
|
+
label: "Symbol",
|
|
583
|
+
format: (data) => data.symbol,
|
|
584
|
+
isVisible: () => true,
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
key: "strategyName",
|
|
588
|
+
label: "Strategy",
|
|
589
|
+
format: (data) => data.strategyName,
|
|
590
|
+
isVisible: () => true,
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
key: "signalId",
|
|
594
|
+
label: "Signal ID",
|
|
595
|
+
format: (data) => data.signalId,
|
|
596
|
+
isVisible: () => true,
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
key: "position",
|
|
600
|
+
label: "Position",
|
|
601
|
+
format: (data) => data.position.toUpperCase(),
|
|
602
|
+
isVisible: () => true,
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
key: "level",
|
|
606
|
+
label: "Level %",
|
|
607
|
+
format: (data) => data.action === "profit" ? `+${data.level}%` : `-${data.level}%`,
|
|
608
|
+
isVisible: () => true,
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
key: "currentPrice",
|
|
612
|
+
label: "Current Price",
|
|
613
|
+
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
614
|
+
isVisible: () => true,
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
key: "timestamp",
|
|
618
|
+
label: "Timestamp",
|
|
619
|
+
format: (data) => new Date(data.timestamp).toISOString(),
|
|
620
|
+
isVisible: () => true,
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
key: "mode",
|
|
624
|
+
label: "Mode",
|
|
625
|
+
format: (data) => (data.backtest ? "Backtest" : "Live"),
|
|
626
|
+
isVisible: () => true,
|
|
627
|
+
},
|
|
628
|
+
];
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Column configuration for performance metrics markdown reports.
|
|
632
|
+
*
|
|
633
|
+
* Defines the table structure for displaying performance statistics and timing metrics in performance reports.
|
|
634
|
+
* Each column specifies how to format and display aggregated performance data for different metric types.
|
|
635
|
+
*
|
|
636
|
+
* Used by {@link PerformanceMarkdownService} to generate markdown tables showing:
|
|
637
|
+
* - Metric identification (metric type)
|
|
638
|
+
* - Execution statistics (count, total duration, average, min, max)
|
|
639
|
+
* - Distribution metrics (standard deviation, median, percentiles)
|
|
640
|
+
* - Wait time statistics (average, min, max wait times between events)
|
|
641
|
+
*
|
|
642
|
+
* @remarks
|
|
643
|
+
* This configuration helps identify performance bottlenecks in strategy execution.
|
|
644
|
+
* The service automatically sorts metrics by total duration to highlight the slowest operations.
|
|
645
|
+
* All durations are measured in milliseconds.
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* ```typescript
|
|
649
|
+
* import { performance_columns } from "./assets/performance.columns";
|
|
650
|
+
*
|
|
651
|
+
* // Use with PerformanceMarkdownService
|
|
652
|
+
* const service = new PerformanceMarkdownService();
|
|
653
|
+
* await service.getReport("BTCUSDT", "my-strategy", performance_columns);
|
|
654
|
+
*
|
|
655
|
+
* // Or customize for bottleneck analysis
|
|
656
|
+
* const customColumns = performance_columns.filter(col =>
|
|
657
|
+
* ["metricType", "count", "avgDuration", "maxDuration", "p95"].includes(col.key)
|
|
658
|
+
* );
|
|
659
|
+
* await service.getReport("BTCUSDT", "my-strategy", customColumns);
|
|
660
|
+
* ```
|
|
661
|
+
*
|
|
662
|
+
* @see {@link PerformanceMarkdownService} for usage in report generation
|
|
663
|
+
* @see {@link ColumnModel} for column interface definition
|
|
664
|
+
* @see {@link MetricStats} for data structure
|
|
665
|
+
*/
|
|
666
|
+
const performance_columns = [
|
|
667
|
+
{
|
|
668
|
+
key: "metricType",
|
|
669
|
+
label: "Metric Type",
|
|
670
|
+
format: (data) => data.metricType,
|
|
671
|
+
isVisible: () => true,
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
key: "count",
|
|
675
|
+
label: "Count",
|
|
676
|
+
format: (data) => data.count.toString(),
|
|
677
|
+
isVisible: () => true,
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
key: "totalDuration",
|
|
681
|
+
label: "Total (ms)",
|
|
682
|
+
format: (data) => data.totalDuration.toFixed(2),
|
|
683
|
+
isVisible: () => true,
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
key: "avgDuration",
|
|
687
|
+
label: "Avg (ms)",
|
|
688
|
+
format: (data) => data.avgDuration.toFixed(2),
|
|
689
|
+
isVisible: () => true,
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
key: "minDuration",
|
|
693
|
+
label: "Min (ms)",
|
|
694
|
+
format: (data) => data.minDuration.toFixed(2),
|
|
695
|
+
isVisible: () => true,
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
key: "maxDuration",
|
|
699
|
+
label: "Max (ms)",
|
|
700
|
+
format: (data) => data.maxDuration.toFixed(2),
|
|
701
|
+
isVisible: () => true,
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
key: "stdDev",
|
|
705
|
+
label: "Std Dev (ms)",
|
|
706
|
+
format: (data) => data.stdDev.toFixed(2),
|
|
707
|
+
isVisible: () => true,
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
key: "median",
|
|
711
|
+
label: "Median (ms)",
|
|
712
|
+
format: (data) => data.median.toFixed(2),
|
|
713
|
+
isVisible: () => true,
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
key: "p95",
|
|
717
|
+
label: "P95 (ms)",
|
|
718
|
+
format: (data) => data.p95.toFixed(2),
|
|
719
|
+
isVisible: () => true,
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
key: "p99",
|
|
723
|
+
label: "P99 (ms)",
|
|
724
|
+
format: (data) => data.p99.toFixed(2),
|
|
725
|
+
isVisible: () => true,
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
key: "avgWaitTime",
|
|
729
|
+
label: "Avg Wait (ms)",
|
|
730
|
+
format: (data) => data.avgWaitTime.toFixed(2),
|
|
731
|
+
isVisible: () => true,
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
key: "minWaitTime",
|
|
735
|
+
label: "Min Wait (ms)",
|
|
736
|
+
format: (data) => data.minWaitTime.toFixed(2),
|
|
737
|
+
isVisible: () => true,
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
key: "maxWaitTime",
|
|
741
|
+
label: "Max Wait (ms)",
|
|
742
|
+
format: (data) => data.maxWaitTime.toFixed(2),
|
|
743
|
+
isVisible: () => true,
|
|
744
|
+
},
|
|
745
|
+
];
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Column configuration for risk management markdown reports.
|
|
749
|
+
*
|
|
750
|
+
* Defines the table structure for displaying risk rejection events in risk management reports.
|
|
751
|
+
* Each column specifies how to format and display signals that were rejected due to risk limits.
|
|
752
|
+
*
|
|
753
|
+
* Used by {@link RiskMarkdownService} to generate markdown tables showing:
|
|
754
|
+
* - Signal identification (symbol, strategy name, signal ID, position)
|
|
755
|
+
* - Exchange information (exchange name, active position count)
|
|
756
|
+
* - Price data (open price, take profit, stop loss, current price)
|
|
757
|
+
* - Rejection details (rejection reason/comment, timestamp)
|
|
758
|
+
*
|
|
759
|
+
* @remarks
|
|
760
|
+
* This configuration helps analyze when and why the risk management system rejected signals.
|
|
761
|
+
* The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
|
|
762
|
+
* Useful for tuning risk parameters and understanding risk control effectiveness.
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* ```typescript
|
|
766
|
+
* import { risk_columns } from "./assets/risk.columns";
|
|
767
|
+
*
|
|
768
|
+
* // Use with RiskMarkdownService
|
|
769
|
+
* const service = new RiskMarkdownService();
|
|
770
|
+
* await service.getReport("BTCUSDT", "my-strategy", risk_columns);
|
|
771
|
+
*
|
|
772
|
+
* // Or customize to focus on rejection reasons
|
|
773
|
+
* const customColumns = risk_columns.filter(col =>
|
|
774
|
+
* ["symbol", "strategyName", "comment", "activePositionCount", "timestamp"].includes(col.key)
|
|
775
|
+
* );
|
|
776
|
+
* await service.getReport("BTCUSDT", "my-strategy", customColumns);
|
|
777
|
+
* ```
|
|
778
|
+
*
|
|
779
|
+
* @see {@link RiskMarkdownService} for usage in report generation
|
|
780
|
+
* @see {@link ColumnModel} for column interface definition
|
|
781
|
+
* @see {@link RiskEvent} for data structure
|
|
782
|
+
*/
|
|
783
|
+
const risk_columns = [
|
|
784
|
+
{
|
|
785
|
+
key: "symbol",
|
|
786
|
+
label: "Symbol",
|
|
787
|
+
format: (data) => data.symbol,
|
|
788
|
+
isVisible: () => true,
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
key: "strategyName",
|
|
792
|
+
label: "Strategy",
|
|
793
|
+
format: (data) => data.strategyName,
|
|
794
|
+
isVisible: () => true,
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
key: "signalId",
|
|
798
|
+
label: "Signal ID",
|
|
799
|
+
format: (data) => data.pendingSignal.id || "N/A",
|
|
800
|
+
isVisible: () => true,
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
key: "position",
|
|
804
|
+
label: "Position",
|
|
805
|
+
format: (data) => data.pendingSignal.position.toUpperCase(),
|
|
806
|
+
isVisible: () => true,
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
key: "note",
|
|
810
|
+
label: "Note",
|
|
811
|
+
format: (data) => toPlainString(data.pendingSignal.note ?? "N/A"),
|
|
812
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
key: "exchangeName",
|
|
816
|
+
label: "Exchange",
|
|
817
|
+
format: (data) => data.exchangeName,
|
|
818
|
+
isVisible: () => true,
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
key: "openPrice",
|
|
822
|
+
label: "Open Price",
|
|
823
|
+
format: (data) => data.pendingSignal.priceOpen !== undefined
|
|
824
|
+
? `${data.pendingSignal.priceOpen.toFixed(8)} USD`
|
|
825
|
+
: "N/A",
|
|
826
|
+
isVisible: () => true,
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
key: "takeProfit",
|
|
830
|
+
label: "Take Profit",
|
|
831
|
+
format: (data) => data.pendingSignal.priceTakeProfit !== undefined
|
|
832
|
+
? `${data.pendingSignal.priceTakeProfit.toFixed(8)} USD`
|
|
833
|
+
: "N/A",
|
|
834
|
+
isVisible: () => true,
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
key: "stopLoss",
|
|
838
|
+
label: "Stop Loss",
|
|
839
|
+
format: (data) => data.pendingSignal.priceStopLoss !== undefined
|
|
840
|
+
? `${data.pendingSignal.priceStopLoss.toFixed(8)} USD`
|
|
841
|
+
: "N/A",
|
|
842
|
+
isVisible: () => true,
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
key: "currentPrice",
|
|
846
|
+
label: "Current Price",
|
|
847
|
+
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
848
|
+
isVisible: () => true,
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
key: "activePositionCount",
|
|
852
|
+
label: "Active Positions",
|
|
853
|
+
format: (data) => data.activePositionCount.toString(),
|
|
854
|
+
isVisible: () => true,
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
key: "comment",
|
|
858
|
+
label: "Reason",
|
|
859
|
+
format: (data) => data.comment,
|
|
860
|
+
isVisible: () => true,
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
key: "timestamp",
|
|
864
|
+
label: "Timestamp",
|
|
865
|
+
format: (data) => new Date(data.timestamp).toISOString(),
|
|
866
|
+
isVisible: () => true,
|
|
867
|
+
},
|
|
868
|
+
];
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Column configuration for scheduled signals markdown reports.
|
|
872
|
+
*
|
|
873
|
+
* Defines the table structure for displaying scheduled, opened, and cancelled signal events.
|
|
874
|
+
* Each column specifies how to format and display signal scheduling and activation data.
|
|
875
|
+
*
|
|
876
|
+
* Used by {@link ScheduleMarkdownService} to generate markdown tables showing:
|
|
877
|
+
* - Event information (timestamp, action: scheduled/opened/cancelled)
|
|
878
|
+
* - Signal identification (symbol, signal ID, position)
|
|
879
|
+
* - Price data (current price, entry price, take profit, stop loss)
|
|
880
|
+
* - Timing information (wait time in minutes before activation or cancellation)
|
|
881
|
+
*
|
|
882
|
+
* @remarks
|
|
883
|
+
* This configuration tracks the lifecycle of scheduled signals from creation to activation or cancellation.
|
|
884
|
+
* The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
|
|
885
|
+
* Helps analyze signal scheduling effectiveness and cancellation patterns.
|
|
886
|
+
*
|
|
887
|
+
* @example
|
|
888
|
+
* ```typescript
|
|
889
|
+
* import { schedule_columns } from "./assets/schedule.columns";
|
|
890
|
+
*
|
|
891
|
+
* // Use with ScheduleMarkdownService
|
|
892
|
+
* const service = new ScheduleMarkdownService();
|
|
893
|
+
* await service.getReport("BTCUSDT", "my-strategy", schedule_columns);
|
|
894
|
+
*
|
|
895
|
+
* // Or customize for timing analysis
|
|
896
|
+
* const customColumns = schedule_columns.filter(col =>
|
|
897
|
+
* ["timestamp", "action", "symbol", "duration"].includes(col.key)
|
|
898
|
+
* );
|
|
899
|
+
* await service.getReport("BTCUSDT", "my-strategy", customColumns);
|
|
900
|
+
* ```
|
|
901
|
+
*
|
|
902
|
+
* @see {@link ScheduleMarkdownService} for usage in report generation
|
|
903
|
+
* @see {@link ColumnModel} for column interface definition
|
|
904
|
+
* @see {@link ScheduledEvent} for data structure
|
|
905
|
+
*/
|
|
906
|
+
const schedule_columns = [
|
|
907
|
+
{
|
|
908
|
+
key: "timestamp",
|
|
909
|
+
label: "Timestamp",
|
|
910
|
+
format: (data) => new Date(data.timestamp).toISOString(),
|
|
911
|
+
isVisible: () => true,
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
key: "action",
|
|
915
|
+
label: "Action",
|
|
916
|
+
format: (data) => data.action.toUpperCase(),
|
|
917
|
+
isVisible: () => true,
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
key: "symbol",
|
|
921
|
+
label: "Symbol",
|
|
922
|
+
format: (data) => data.symbol,
|
|
923
|
+
isVisible: () => true,
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
key: "signalId",
|
|
927
|
+
label: "Signal ID",
|
|
928
|
+
format: (data) => data.signalId,
|
|
929
|
+
isVisible: () => true,
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
key: "position",
|
|
933
|
+
label: "Position",
|
|
934
|
+
format: (data) => data.position.toUpperCase(),
|
|
935
|
+
isVisible: () => true,
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
key: "note",
|
|
939
|
+
label: "Note",
|
|
940
|
+
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
941
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
key: "currentPrice",
|
|
945
|
+
label: "Current Price",
|
|
946
|
+
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
947
|
+
isVisible: () => true,
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
key: "priceOpen",
|
|
951
|
+
label: "Entry Price",
|
|
952
|
+
format: (data) => `${data.priceOpen.toFixed(8)} USD`,
|
|
953
|
+
isVisible: () => true,
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
key: "takeProfit",
|
|
957
|
+
label: "Take Profit",
|
|
958
|
+
format: (data) => `${data.takeProfit.toFixed(8)} USD`,
|
|
959
|
+
isVisible: () => true,
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
key: "stopLoss",
|
|
963
|
+
label: "Stop Loss",
|
|
964
|
+
format: (data) => `${data.stopLoss.toFixed(8)} USD`,
|
|
965
|
+
isVisible: () => true,
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
key: "duration",
|
|
969
|
+
label: "Wait Time (min)",
|
|
970
|
+
format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
|
|
971
|
+
isVisible: () => true,
|
|
972
|
+
},
|
|
973
|
+
];
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Column configuration for walker strategy comparison table in markdown reports.
|
|
977
|
+
*
|
|
978
|
+
* Defines the table structure for displaying strategy comparison results in walker backtest reports.
|
|
979
|
+
* Each column specifies how to format and display aggregated strategy performance metrics.
|
|
980
|
+
*
|
|
981
|
+
* Used by {@link WalkerMarkdownService} to generate markdown tables showing:
|
|
982
|
+
* - Strategy ranking and identification
|
|
983
|
+
* - Optimization metric value (generic "Metric" column)
|
|
984
|
+
* - Performance statistics (Total Signals, Win Rate, Average PNL, Total PNL)
|
|
985
|
+
* - Risk metrics (Sharpe Ratio, Standard Deviation)
|
|
986
|
+
*
|
|
987
|
+
* @remarks
|
|
988
|
+
* This configuration is used in walker reports to compare multiple strategy configurations.
|
|
989
|
+
* The "Metric" column displays the value of the metric being optimized (Sharpe, PNL, etc.).
|
|
990
|
+
* Strategies are automatically sorted by metric value (best performers first).
|
|
991
|
+
*
|
|
992
|
+
* @example
|
|
993
|
+
* ```typescript
|
|
994
|
+
* import { walker_strategy_columns } from "./assets/walker.columns";
|
|
995
|
+
*
|
|
996
|
+
* // Use with WalkerMarkdownService for strategy comparison
|
|
997
|
+
* const service = new WalkerMarkdownService();
|
|
998
|
+
* await service.getReport(
|
|
999
|
+
* "my-walker",
|
|
1000
|
+
* "BTCUSDT",
|
|
1001
|
+
* "sharpeRatio",
|
|
1002
|
+
* { exchangeName: "binance", frameName: "1d" },
|
|
1003
|
+
* walker_strategy_columns
|
|
1004
|
+
* );
|
|
1005
|
+
* ```
|
|
1006
|
+
*
|
|
1007
|
+
* @see {@link WalkerMarkdownService} for usage in report generation
|
|
1008
|
+
* @see {@link ColumnModel} for column interface definition
|
|
1009
|
+
* @see {@link IStrategyResult} for data structure
|
|
1010
|
+
*/
|
|
1011
|
+
const walker_strategy_columns = [
|
|
1012
|
+
{
|
|
1013
|
+
key: "rank",
|
|
1014
|
+
label: "Rank",
|
|
1015
|
+
format: (_data, index) => `${index + 1}`,
|
|
1016
|
+
isVisible: () => true,
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
key: "strategy",
|
|
1020
|
+
label: "Strategy",
|
|
1021
|
+
format: (data) => data.strategyName,
|
|
1022
|
+
isVisible: () => true,
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
key: "metric",
|
|
1026
|
+
label: "Metric",
|
|
1027
|
+
format: (data) => data.metricValue !== null ? data.metricValue.toFixed(2) : "N/A",
|
|
1028
|
+
isVisible: () => true,
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
key: "totalSignals",
|
|
1032
|
+
label: "Total Signals",
|
|
1033
|
+
format: (data) => `${data.stats.totalSignals}`,
|
|
1034
|
+
isVisible: () => true,
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
key: "winRate",
|
|
1038
|
+
label: "Win Rate",
|
|
1039
|
+
format: (data) => data.stats.winRate !== null
|
|
1040
|
+
? `${data.stats.winRate.toFixed(2)}%`
|
|
1041
|
+
: "N/A",
|
|
1042
|
+
isVisible: () => true,
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
key: "avgPnl",
|
|
1046
|
+
label: "Avg PNL",
|
|
1047
|
+
format: (data) => data.stats.avgPnl !== null
|
|
1048
|
+
? `${data.stats.avgPnl > 0 ? "+" : ""}${data.stats.avgPnl.toFixed(2)}%`
|
|
1049
|
+
: "N/A",
|
|
1050
|
+
isVisible: () => true,
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
key: "totalPnl",
|
|
1054
|
+
label: "Total PNL",
|
|
1055
|
+
format: (data) => data.stats.totalPnl !== null
|
|
1056
|
+
? `${data.stats.totalPnl > 0 ? "+" : ""}${data.stats.totalPnl.toFixed(2)}%`
|
|
1057
|
+
: "N/A",
|
|
1058
|
+
isVisible: () => true,
|
|
1059
|
+
},
|
|
1060
|
+
{
|
|
1061
|
+
key: "sharpeRatio",
|
|
1062
|
+
label: "Sharpe Ratio",
|
|
1063
|
+
format: (data) => data.stats.sharpeRatio !== null
|
|
1064
|
+
? `${data.stats.sharpeRatio.toFixed(3)}`
|
|
1065
|
+
: "N/A",
|
|
1066
|
+
isVisible: () => true,
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
key: "stdDev",
|
|
1070
|
+
label: "Std Dev",
|
|
1071
|
+
format: (data) => data.stats.stdDev !== null
|
|
1072
|
+
? `${data.stats.stdDev.toFixed(3)}%`
|
|
1073
|
+
: "N/A",
|
|
1074
|
+
isVisible: () => true,
|
|
1075
|
+
},
|
|
1076
|
+
];
|
|
1077
|
+
/**
|
|
1078
|
+
* Column configuration for walker PNL table in markdown reports.
|
|
1079
|
+
*
|
|
1080
|
+
* Defines the table structure for displaying all closed signals across all strategies in walker backtest reports.
|
|
1081
|
+
* Each column specifies how to format and display individual signal trade data.
|
|
1082
|
+
*
|
|
1083
|
+
* Used by {@link WalkerMarkdownService} to generate markdown tables showing:
|
|
1084
|
+
* - Strategy identification for each signal
|
|
1085
|
+
* - Signal details (signal ID, symbol, position)
|
|
1086
|
+
* - Trade performance (PNL percentage, close reason)
|
|
1087
|
+
* - Timing information (open time, close time)
|
|
1088
|
+
*
|
|
1089
|
+
* @remarks
|
|
1090
|
+
* This configuration aggregates all signals from all tested strategies into a single comprehensive table.
|
|
1091
|
+
* Useful for detailed analysis of individual trades across different strategy configurations.
|
|
1092
|
+
*
|
|
1093
|
+
* @example
|
|
1094
|
+
* ```typescript
|
|
1095
|
+
* import { walker_pnl_columns } from "./assets/walker.columns";
|
|
1096
|
+
*
|
|
1097
|
+
* // Use with WalkerMarkdownService for signal-level analysis
|
|
1098
|
+
* const service = new WalkerMarkdownService();
|
|
1099
|
+
* await service.getReport(
|
|
1100
|
+
* "my-walker",
|
|
1101
|
+
* "BTCUSDT",
|
|
1102
|
+
* "sharpeRatio",
|
|
1103
|
+
* { exchangeName: "binance", frameName: "1d" },
|
|
1104
|
+
* undefined, // use default strategy columns
|
|
1105
|
+
* walker_pnl_columns
|
|
1106
|
+
* );
|
|
1107
|
+
* ```
|
|
1108
|
+
*
|
|
1109
|
+
* @see {@link WalkerMarkdownService} for usage in report generation
|
|
1110
|
+
* @see {@link ColumnModel} for column interface definition
|
|
1111
|
+
* @see {@link SignalData} for data structure
|
|
1112
|
+
*/
|
|
1113
|
+
const walker_pnl_columns = [
|
|
1114
|
+
{
|
|
1115
|
+
key: "strategy",
|
|
1116
|
+
label: "Strategy",
|
|
1117
|
+
format: (data) => data.strategyName,
|
|
1118
|
+
isVisible: () => true,
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
key: "signalId",
|
|
1122
|
+
label: "Signal ID",
|
|
1123
|
+
format: (data) => data.signalId,
|
|
1124
|
+
isVisible: () => true,
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
key: "symbol",
|
|
1128
|
+
label: "Symbol",
|
|
1129
|
+
format: (data) => data.symbol,
|
|
1130
|
+
isVisible: () => true,
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
key: "position",
|
|
1134
|
+
label: "Position",
|
|
1135
|
+
format: (data) => data.position.toUpperCase(),
|
|
1136
|
+
isVisible: () => true,
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
key: "pnl",
|
|
1140
|
+
label: "PNL (net)",
|
|
1141
|
+
format: (data) => `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`,
|
|
1142
|
+
isVisible: () => true,
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
key: "closeReason",
|
|
1146
|
+
label: "Close Reason",
|
|
1147
|
+
format: (data) => data.closeReason,
|
|
1148
|
+
isVisible: () => true,
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
key: "openTime",
|
|
1152
|
+
label: "Open Time",
|
|
1153
|
+
format: (data) => new Date(data.openTime).toISOString(),
|
|
1154
|
+
isVisible: () => true,
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
key: "closeTime",
|
|
1158
|
+
label: "Close Time",
|
|
1159
|
+
format: (data) => new Date(data.closeTime).toISOString(),
|
|
1160
|
+
isVisible: () => true,
|
|
1161
|
+
},
|
|
1162
|
+
];
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Mapping of available table/markdown reports to their column definitions.
|
|
1166
|
+
*
|
|
1167
|
+
* Each property references a column definition object imported from
|
|
1168
|
+
* `src/assets/*.columns`. These are used by markdown/report generators
|
|
1169
|
+
* (backtest, live, schedule, risk, heat, performance, partial, walker).
|
|
1170
|
+
*/
|
|
1171
|
+
const COLUMN_CONFIG = {
|
|
1172
|
+
/** Columns used in backtest markdown tables and reports */
|
|
1173
|
+
backtest_columns,
|
|
1174
|
+
/** Columns used by heatmap / heat reports */
|
|
1175
|
+
heat_columns,
|
|
1176
|
+
/** Columns for live trading reports and logs */
|
|
1177
|
+
live_columns,
|
|
1178
|
+
/** Columns for partial-results / incremental reports */
|
|
1179
|
+
partial_columns,
|
|
1180
|
+
/** Columns for performance summary reports */
|
|
1181
|
+
performance_columns,
|
|
1182
|
+
/** Columns for risk-related reports */
|
|
1183
|
+
risk_columns,
|
|
1184
|
+
/** Columns for scheduled report output */
|
|
1185
|
+
schedule_columns,
|
|
1186
|
+
/** Walker: PnL summary columns */
|
|
1187
|
+
walker_pnl_columns,
|
|
1188
|
+
/** Walker: strategy-level summary columns */
|
|
1189
|
+
walker_strategy_columns,
|
|
1190
|
+
};
|
|
1191
|
+
/**
|
|
1192
|
+
* Immutable default columns mapping used across the application.
|
|
1193
|
+
* Use `DEFAULT_COLUMNS` when you need a read-only reference to the
|
|
1194
|
+
* canonical column configuration.
|
|
1195
|
+
*/
|
|
1196
|
+
const DEFAULT_COLUMNS = Object.freeze({ ...COLUMN_CONFIG });
|
|
1197
|
+
|
|
123
1198
|
const { init, inject, provide } = createActivator("backtest");
|
|
124
1199
|
|
|
125
1200
|
/**
|
|
@@ -219,6 +1294,7 @@ const validationServices$1 = {
|
|
|
219
1294
|
riskValidationService: Symbol('riskValidationService'),
|
|
220
1295
|
optimizerValidationService: Symbol('optimizerValidationService'),
|
|
221
1296
|
configValidationService: Symbol('configValidationService'),
|
|
1297
|
+
columnValidationService: Symbol('columnValidationService'),
|
|
222
1298
|
};
|
|
223
1299
|
const templateServices$1 = {
|
|
224
1300
|
optimizerTemplateService: Symbol('optimizerTemplateService'),
|
|
@@ -1763,53 +2839,6 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
1763
2839
|
walkerStopSubject: walkerStopSubject
|
|
1764
2840
|
});
|
|
1765
2841
|
|
|
1766
|
-
/**
|
|
1767
|
-
* Converts markdown content to plain text with minimal formatting
|
|
1768
|
-
* @param content - Markdown string to convert
|
|
1769
|
-
* @returns Plain text representation
|
|
1770
|
-
*/
|
|
1771
|
-
const toPlainString = (content) => {
|
|
1772
|
-
if (!content) {
|
|
1773
|
-
return "";
|
|
1774
|
-
}
|
|
1775
|
-
let text = content;
|
|
1776
|
-
// Remove code blocks
|
|
1777
|
-
text = text.replace(/```[\s\S]*?```/g, "");
|
|
1778
|
-
text = text.replace(/`([^`]+)`/g, "$1");
|
|
1779
|
-
// Remove images
|
|
1780
|
-
text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1");
|
|
1781
|
-
// Convert links to text only (keep link text, remove URL)
|
|
1782
|
-
text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
1783
|
-
// Remove headers (convert to plain text)
|
|
1784
|
-
text = text.replace(/^#{1,6}\s+(.+)$/gm, "$1");
|
|
1785
|
-
// Remove bold and italic markers
|
|
1786
|
-
text = text.replace(/\*\*\*(.+?)\*\*\*/g, "$1");
|
|
1787
|
-
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
1788
|
-
text = text.replace(/\*(.+?)\*/g, "$1");
|
|
1789
|
-
text = text.replace(/___(.+?)___/g, "$1");
|
|
1790
|
-
text = text.replace(/__(.+?)__/g, "$1");
|
|
1791
|
-
text = text.replace(/_(.+?)_/g, "$1");
|
|
1792
|
-
// Remove strikethrough
|
|
1793
|
-
text = text.replace(/~~(.+?)~~/g, "$1");
|
|
1794
|
-
// Convert lists to plain text with bullets
|
|
1795
|
-
text = text.replace(/^\s*[-*+]\s+/gm, "• ");
|
|
1796
|
-
text = text.replace(/^\s*\d+\.\s+/gm, "• ");
|
|
1797
|
-
// Remove blockquotes
|
|
1798
|
-
text = text.replace(/^\s*>\s+/gm, "");
|
|
1799
|
-
// Remove horizontal rules
|
|
1800
|
-
text = text.replace(/^(\*{3,}|-{3,}|_{3,})$/gm, "");
|
|
1801
|
-
// Remove HTML tags
|
|
1802
|
-
text = text.replace(/<[^>]+>/g, "");
|
|
1803
|
-
// Remove excessive whitespace and normalize line breaks
|
|
1804
|
-
text = text.replace(/\n[\s\n]*\n/g, "\n");
|
|
1805
|
-
text = text.replace(/[ \t]+/g, " ");
|
|
1806
|
-
// Remove all newline characters
|
|
1807
|
-
text = text.replace(/\n/g, " ");
|
|
1808
|
-
// Remove excessive spaces after newline removal
|
|
1809
|
-
text = text.replace(/\s+/g, " ");
|
|
1810
|
-
return text.trim();
|
|
1811
|
-
};
|
|
1812
|
-
|
|
1813
2842
|
const INTERVAL_MINUTES$1 = {
|
|
1814
2843
|
"1m": 1,
|
|
1815
2844
|
"3m": 3,
|
|
@@ -3388,7 +4417,7 @@ class RiskUtils {
|
|
|
3388
4417
|
*
|
|
3389
4418
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
3390
4419
|
* @param strategyName - Strategy name (e.g., "my-strategy")
|
|
3391
|
-
* @returns Promise resolving to
|
|
4420
|
+
* @returns Promise resolving to RiskStatisticsModel object with counts and event list
|
|
3392
4421
|
*
|
|
3393
4422
|
* @example
|
|
3394
4423
|
* ```typescript
|
|
@@ -3435,6 +4464,7 @@ class RiskUtils {
|
|
|
3435
4464
|
*
|
|
3436
4465
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
3437
4466
|
* @param strategyName - Strategy name (e.g., "my-strategy")
|
|
4467
|
+
* @param columns - Optional columns configuration for the report
|
|
3438
4468
|
* @returns Promise resolving to markdown formatted report string
|
|
3439
4469
|
*
|
|
3440
4470
|
* @example
|
|
@@ -3458,7 +4488,7 @@ class RiskUtils {
|
|
|
3458
4488
|
* // - my-strategy: 1
|
|
3459
4489
|
* ```
|
|
3460
4490
|
*/
|
|
3461
|
-
this.getReport = async (symbol, strategyName) => {
|
|
4491
|
+
this.getReport = async (symbol, strategyName, columns) => {
|
|
3462
4492
|
backtest$1.loggerService.info(RISK_METHOD_NAME_GET_REPORT, {
|
|
3463
4493
|
symbol,
|
|
3464
4494
|
strategyName,
|
|
@@ -3470,7 +4500,7 @@ class RiskUtils {
|
|
|
3470
4500
|
backtest$1.riskValidationService.validate(riskName, RISK_METHOD_NAME_GET_REPORT);
|
|
3471
4501
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, RISK_METHOD_NAME_GET_REPORT));
|
|
3472
4502
|
}
|
|
3473
|
-
return await backtest$1.riskMarkdownService.getReport(symbol, strategyName);
|
|
4503
|
+
return await backtest$1.riskMarkdownService.getReport(symbol, strategyName, columns);
|
|
3474
4504
|
};
|
|
3475
4505
|
/**
|
|
3476
4506
|
* Generates and saves markdown report to file.
|
|
@@ -3487,6 +4517,7 @@ class RiskUtils {
|
|
|
3487
4517
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
3488
4518
|
* @param strategyName - Strategy name (e.g., "my-strategy")
|
|
3489
4519
|
* @param path - Output directory path (default: "./dump/risk")
|
|
4520
|
+
* @param columns - Optional columns configuration for the report
|
|
3490
4521
|
* @returns Promise that resolves when file is written
|
|
3491
4522
|
*
|
|
3492
4523
|
* @example
|
|
@@ -3503,7 +4534,7 @@ class RiskUtils {
|
|
|
3503
4534
|
* }
|
|
3504
4535
|
* ```
|
|
3505
4536
|
*/
|
|
3506
|
-
this.dump = async (symbol, strategyName, path) => {
|
|
4537
|
+
this.dump = async (symbol, strategyName, path, columns) => {
|
|
3507
4538
|
backtest$1.loggerService.info(RISK_METHOD_NAME_DUMP, {
|
|
3508
4539
|
symbol,
|
|
3509
4540
|
strategyName,
|
|
@@ -3516,7 +4547,7 @@ class RiskUtils {
|
|
|
3516
4547
|
backtest$1.riskValidationService.validate(riskName, RISK_METHOD_NAME_DUMP);
|
|
3517
4548
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, RISK_METHOD_NAME_DUMP));
|
|
3518
4549
|
}
|
|
3519
|
-
await backtest$1.riskMarkdownService.dump(symbol, strategyName, path);
|
|
4550
|
+
await backtest$1.riskMarkdownService.dump(symbol, strategyName, path, columns);
|
|
3520
4551
|
};
|
|
3521
4552
|
}
|
|
3522
4553
|
}
|
|
@@ -5033,6 +6064,19 @@ class StrategySchemaService {
|
|
|
5033
6064
|
if (typeof strategySchema.strategyName !== "string") {
|
|
5034
6065
|
throw new Error(`strategy schema validation failed: missing strategyName`);
|
|
5035
6066
|
}
|
|
6067
|
+
if (strategySchema.riskName && typeof strategySchema.riskName !== "string") {
|
|
6068
|
+
throw new Error(`strategy schema validation failed: invalid riskName`);
|
|
6069
|
+
}
|
|
6070
|
+
if (strategySchema.riskList && !Array.isArray(strategySchema.riskList)) {
|
|
6071
|
+
throw new Error(`strategy schema validation failed: invalid riskList for strategyName=${strategySchema.strategyName} system=${strategySchema.riskList}`);
|
|
6072
|
+
}
|
|
6073
|
+
if (strategySchema.riskList &&
|
|
6074
|
+
strategySchema.riskList.length !== new Set(strategySchema.riskList).size) {
|
|
6075
|
+
throw new Error(`strategy schema validation failed: found duplicate riskList for strategyName=${strategySchema.strategyName} riskList=[${strategySchema.riskList}]`);
|
|
6076
|
+
}
|
|
6077
|
+
if (strategySchema.riskList?.some((value) => typeof value !== "string")) {
|
|
6078
|
+
throw new Error(`strategy schema validation failed: invalid riskList for strategyName=${strategySchema.strategyName} riskList=[${strategySchema.riskList}]`);
|
|
6079
|
+
}
|
|
5036
6080
|
if (typeof strategySchema.interval !== "string") {
|
|
5037
6081
|
throw new Error(`strategy schema validation failed: missing interval for strategyName=${strategySchema.strategyName}`);
|
|
5038
6082
|
}
|
|
@@ -6263,178 +7307,91 @@ class BacktestCommandService {
|
|
|
6263
7307
|
* @returns Async generator yielding closed signals with PNL
|
|
6264
7308
|
*/
|
|
6265
7309
|
this.run = (symbol, context) => {
|
|
6266
|
-
this.loggerService.log(METHOD_NAME_RUN$1, {
|
|
6267
|
-
symbol,
|
|
6268
|
-
context,
|
|
6269
|
-
});
|
|
6270
|
-
{
|
|
6271
|
-
this.strategyValidationService.validate(context.strategyName, METHOD_NAME_RUN$1);
|
|
6272
|
-
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_RUN$1);
|
|
6273
|
-
this.frameValidationService.validate(context.frameName, METHOD_NAME_RUN$1);
|
|
6274
|
-
}
|
|
6275
|
-
{
|
|
6276
|
-
const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
|
|
6277
|
-
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_RUN$1);
|
|
6278
|
-
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_RUN$1));
|
|
6279
|
-
}
|
|
6280
|
-
return this.backtestLogicPublicService.run(symbol, context);
|
|
6281
|
-
};
|
|
6282
|
-
}
|
|
6283
|
-
}
|
|
6284
|
-
|
|
6285
|
-
const METHOD_NAME_RUN = "walkerCommandService run";
|
|
6286
|
-
/**
|
|
6287
|
-
* Global service providing access to walker functionality.
|
|
6288
|
-
*
|
|
6289
|
-
* Simple wrapper around WalkerLogicPublicService for dependency injection.
|
|
6290
|
-
* Used by public API exports.
|
|
6291
|
-
*/
|
|
6292
|
-
class WalkerCommandService {
|
|
6293
|
-
constructor() {
|
|
6294
|
-
this.loggerService = inject(TYPES.loggerService);
|
|
6295
|
-
this.walkerLogicPublicService = inject(TYPES.walkerLogicPublicService);
|
|
6296
|
-
this.walkerSchemaService = inject(TYPES.walkerSchemaService);
|
|
6297
|
-
this.strategyValidationService = inject(TYPES.strategyValidationService);
|
|
6298
|
-
this.exchangeValidationService = inject(TYPES.exchangeValidationService);
|
|
6299
|
-
this.frameValidationService = inject(TYPES.frameValidationService);
|
|
6300
|
-
this.walkerValidationService = inject(TYPES.walkerValidationService);
|
|
6301
|
-
this.strategySchemaService = inject(TYPES.strategySchemaService);
|
|
6302
|
-
this.riskValidationService = inject(TYPES.riskValidationService);
|
|
6303
|
-
/**
|
|
6304
|
-
* Runs walker comparison for a symbol with context propagation.
|
|
6305
|
-
*
|
|
6306
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
6307
|
-
* @param context - Walker context with strategies and metric
|
|
6308
|
-
*/
|
|
6309
|
-
this.run = (symbol, context) => {
|
|
6310
|
-
this.loggerService.log(METHOD_NAME_RUN, {
|
|
6311
|
-
symbol,
|
|
6312
|
-
context,
|
|
6313
|
-
});
|
|
6314
|
-
{
|
|
6315
|
-
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_RUN);
|
|
6316
|
-
this.frameValidationService.validate(context.frameName, METHOD_NAME_RUN);
|
|
6317
|
-
this.walkerValidationService.validate(context.walkerName, METHOD_NAME_RUN);
|
|
6318
|
-
}
|
|
6319
|
-
{
|
|
6320
|
-
const walkerSchema = this.walkerSchemaService.get(context.walkerName);
|
|
6321
|
-
for (const strategyName of walkerSchema.strategies) {
|
|
6322
|
-
const { riskName, riskList } = this.strategySchemaService.get(strategyName);
|
|
6323
|
-
this.strategyValidationService.validate(strategyName, METHOD_NAME_RUN);
|
|
6324
|
-
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_RUN);
|
|
6325
|
-
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_RUN));
|
|
6326
|
-
}
|
|
6327
|
-
}
|
|
6328
|
-
return this.walkerLogicPublicService.run(symbol, context);
|
|
6329
|
-
};
|
|
6330
|
-
}
|
|
6331
|
-
}
|
|
6332
|
-
|
|
6333
|
-
/**
|
|
6334
|
-
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
6335
|
-
*
|
|
6336
|
-
* @param value - Value to check
|
|
6337
|
-
* @returns true if value is unsafe, false otherwise
|
|
6338
|
-
*/
|
|
6339
|
-
function isUnsafe$3(value) {
|
|
6340
|
-
if (typeof value !== "number") {
|
|
6341
|
-
return true;
|
|
6342
|
-
}
|
|
6343
|
-
if (isNaN(value)) {
|
|
6344
|
-
return true;
|
|
6345
|
-
}
|
|
6346
|
-
if (!isFinite(value)) {
|
|
6347
|
-
return true;
|
|
6348
|
-
}
|
|
6349
|
-
return false;
|
|
6350
|
-
}
|
|
6351
|
-
const columns$6 = [
|
|
6352
|
-
{
|
|
6353
|
-
key: "signalId",
|
|
6354
|
-
label: "Signal ID",
|
|
6355
|
-
format: (data) => data.signal.id,
|
|
6356
|
-
isVisible: () => true,
|
|
6357
|
-
},
|
|
6358
|
-
{
|
|
6359
|
-
key: "symbol",
|
|
6360
|
-
label: "Symbol",
|
|
6361
|
-
format: (data) => data.signal.symbol,
|
|
6362
|
-
isVisible: () => true,
|
|
6363
|
-
},
|
|
6364
|
-
{
|
|
6365
|
-
key: "position",
|
|
6366
|
-
label: "Position",
|
|
6367
|
-
format: (data) => data.signal.position.toUpperCase(),
|
|
6368
|
-
isVisible: () => true,
|
|
6369
|
-
},
|
|
6370
|
-
{
|
|
6371
|
-
key: "note",
|
|
6372
|
-
label: "Note",
|
|
6373
|
-
format: (data) => toPlainString(data.signal.note ?? "N/A"),
|
|
6374
|
-
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
6375
|
-
},
|
|
6376
|
-
{
|
|
6377
|
-
key: "openPrice",
|
|
6378
|
-
label: "Open Price",
|
|
6379
|
-
format: (data) => `${data.signal.priceOpen.toFixed(8)} USD`,
|
|
6380
|
-
isVisible: () => true,
|
|
6381
|
-
},
|
|
6382
|
-
{
|
|
6383
|
-
key: "closePrice",
|
|
6384
|
-
label: "Close Price",
|
|
6385
|
-
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
6386
|
-
isVisible: () => true,
|
|
6387
|
-
},
|
|
6388
|
-
{
|
|
6389
|
-
key: "takeProfit",
|
|
6390
|
-
label: "Take Profit",
|
|
6391
|
-
format: (data) => `${data.signal.priceTakeProfit.toFixed(8)} USD`,
|
|
6392
|
-
isVisible: () => true,
|
|
6393
|
-
},
|
|
6394
|
-
{
|
|
6395
|
-
key: "stopLoss",
|
|
6396
|
-
label: "Stop Loss",
|
|
6397
|
-
format: (data) => `${data.signal.priceStopLoss.toFixed(8)} USD`,
|
|
6398
|
-
isVisible: () => true,
|
|
6399
|
-
},
|
|
6400
|
-
{
|
|
6401
|
-
key: "pnl",
|
|
6402
|
-
label: "PNL (net)",
|
|
6403
|
-
format: (data) => {
|
|
6404
|
-
const pnlPercentage = data.pnl.pnlPercentage;
|
|
6405
|
-
return `${pnlPercentage > 0 ? "+" : ""}${pnlPercentage.toFixed(2)}%`;
|
|
6406
|
-
},
|
|
6407
|
-
isVisible: () => true,
|
|
6408
|
-
},
|
|
6409
|
-
{
|
|
6410
|
-
key: "closeReason",
|
|
6411
|
-
label: "Close Reason",
|
|
6412
|
-
format: (data) => data.closeReason,
|
|
6413
|
-
isVisible: () => true,
|
|
6414
|
-
},
|
|
6415
|
-
{
|
|
6416
|
-
key: "duration",
|
|
6417
|
-
label: "Duration (min)",
|
|
6418
|
-
format: (data) => {
|
|
6419
|
-
const durationMs = data.closeTimestamp - data.signal.pendingAt;
|
|
6420
|
-
const durationMin = Math.round(durationMs / 60000);
|
|
6421
|
-
return `${durationMin}`;
|
|
6422
|
-
},
|
|
6423
|
-
isVisible: () => true,
|
|
6424
|
-
},
|
|
6425
|
-
{
|
|
6426
|
-
key: "openTimestamp",
|
|
6427
|
-
label: "Open Time",
|
|
6428
|
-
format: (data) => new Date(data.signal.pendingAt).toISOString(),
|
|
6429
|
-
isVisible: () => true,
|
|
6430
|
-
},
|
|
6431
|
-
{
|
|
6432
|
-
key: "closeTimestamp",
|
|
6433
|
-
label: "Close Time",
|
|
6434
|
-
format: (data) => new Date(data.closeTimestamp).toISOString(),
|
|
6435
|
-
isVisible: () => true,
|
|
6436
|
-
},
|
|
6437
|
-
];
|
|
7310
|
+
this.loggerService.log(METHOD_NAME_RUN$1, {
|
|
7311
|
+
symbol,
|
|
7312
|
+
context,
|
|
7313
|
+
});
|
|
7314
|
+
{
|
|
7315
|
+
this.strategyValidationService.validate(context.strategyName, METHOD_NAME_RUN$1);
|
|
7316
|
+
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_RUN$1);
|
|
7317
|
+
this.frameValidationService.validate(context.frameName, METHOD_NAME_RUN$1);
|
|
7318
|
+
}
|
|
7319
|
+
{
|
|
7320
|
+
const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
|
|
7321
|
+
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_RUN$1);
|
|
7322
|
+
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_RUN$1));
|
|
7323
|
+
}
|
|
7324
|
+
return this.backtestLogicPublicService.run(symbol, context);
|
|
7325
|
+
};
|
|
7326
|
+
}
|
|
7327
|
+
}
|
|
7328
|
+
|
|
7329
|
+
const METHOD_NAME_RUN = "walkerCommandService run";
|
|
7330
|
+
/**
|
|
7331
|
+
* Global service providing access to walker functionality.
|
|
7332
|
+
*
|
|
7333
|
+
* Simple wrapper around WalkerLogicPublicService for dependency injection.
|
|
7334
|
+
* Used by public API exports.
|
|
7335
|
+
*/
|
|
7336
|
+
class WalkerCommandService {
|
|
7337
|
+
constructor() {
|
|
7338
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
7339
|
+
this.walkerLogicPublicService = inject(TYPES.walkerLogicPublicService);
|
|
7340
|
+
this.walkerSchemaService = inject(TYPES.walkerSchemaService);
|
|
7341
|
+
this.strategyValidationService = inject(TYPES.strategyValidationService);
|
|
7342
|
+
this.exchangeValidationService = inject(TYPES.exchangeValidationService);
|
|
7343
|
+
this.frameValidationService = inject(TYPES.frameValidationService);
|
|
7344
|
+
this.walkerValidationService = inject(TYPES.walkerValidationService);
|
|
7345
|
+
this.strategySchemaService = inject(TYPES.strategySchemaService);
|
|
7346
|
+
this.riskValidationService = inject(TYPES.riskValidationService);
|
|
7347
|
+
/**
|
|
7348
|
+
* Runs walker comparison for a symbol with context propagation.
|
|
7349
|
+
*
|
|
7350
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
7351
|
+
* @param context - Walker context with strategies and metric
|
|
7352
|
+
*/
|
|
7353
|
+
this.run = (symbol, context) => {
|
|
7354
|
+
this.loggerService.log(METHOD_NAME_RUN, {
|
|
7355
|
+
symbol,
|
|
7356
|
+
context,
|
|
7357
|
+
});
|
|
7358
|
+
{
|
|
7359
|
+
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_RUN);
|
|
7360
|
+
this.frameValidationService.validate(context.frameName, METHOD_NAME_RUN);
|
|
7361
|
+
this.walkerValidationService.validate(context.walkerName, METHOD_NAME_RUN);
|
|
7362
|
+
}
|
|
7363
|
+
{
|
|
7364
|
+
const walkerSchema = this.walkerSchemaService.get(context.walkerName);
|
|
7365
|
+
for (const strategyName of walkerSchema.strategies) {
|
|
7366
|
+
const { riskName, riskList } = this.strategySchemaService.get(strategyName);
|
|
7367
|
+
this.strategyValidationService.validate(strategyName, METHOD_NAME_RUN);
|
|
7368
|
+
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_RUN);
|
|
7369
|
+
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_RUN));
|
|
7370
|
+
}
|
|
7371
|
+
}
|
|
7372
|
+
return this.walkerLogicPublicService.run(symbol, context);
|
|
7373
|
+
};
|
|
7374
|
+
}
|
|
7375
|
+
}
|
|
7376
|
+
|
|
7377
|
+
/**
|
|
7378
|
+
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
7379
|
+
*
|
|
7380
|
+
* @param value - Value to check
|
|
7381
|
+
* @returns true if value is unsafe, false otherwise
|
|
7382
|
+
*/
|
|
7383
|
+
function isUnsafe$3(value) {
|
|
7384
|
+
if (typeof value !== "number") {
|
|
7385
|
+
return true;
|
|
7386
|
+
}
|
|
7387
|
+
if (isNaN(value)) {
|
|
7388
|
+
return true;
|
|
7389
|
+
}
|
|
7390
|
+
if (!isFinite(value)) {
|
|
7391
|
+
return true;
|
|
7392
|
+
}
|
|
7393
|
+
return false;
|
|
7394
|
+
}
|
|
6438
7395
|
/** Maximum number of signals to store in backtest reports */
|
|
6439
7396
|
const MAX_EVENTS$6 = 250;
|
|
6440
7397
|
/**
|
|
@@ -6528,9 +7485,10 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
6528
7485
|
* Generates markdown report with all closed signals for a strategy (View).
|
|
6529
7486
|
*
|
|
6530
7487
|
* @param strategyName - Strategy name
|
|
7488
|
+
* @param columns - Column configuration for formatting the table
|
|
6531
7489
|
* @returns Markdown formatted report with all signals
|
|
6532
7490
|
*/
|
|
6533
|
-
async getReport(strategyName) {
|
|
7491
|
+
async getReport(strategyName, columns = COLUMN_CONFIG.backtest_columns) {
|
|
6534
7492
|
const stats = await this.getData();
|
|
6535
7493
|
if (stats.totalSignals === 0) {
|
|
6536
7494
|
return [
|
|
@@ -6539,10 +7497,15 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
6539
7497
|
"No signals closed yet."
|
|
6540
7498
|
].join("\n");
|
|
6541
7499
|
}
|
|
6542
|
-
const visibleColumns =
|
|
7500
|
+
const visibleColumns = [];
|
|
7501
|
+
for (const col of columns) {
|
|
7502
|
+
if (await col.isVisible()) {
|
|
7503
|
+
visibleColumns.push(col);
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
6543
7506
|
const header = visibleColumns.map((col) => col.label);
|
|
6544
7507
|
const separator = visibleColumns.map(() => "---");
|
|
6545
|
-
const rows = this._signalList.map((closedSignal) => visibleColumns.map((col) => col.format(closedSignal)));
|
|
7508
|
+
const rows = await Promise.all(this._signalList.map(async (closedSignal, index) => Promise.all(visibleColumns.map((col) => col.format(closedSignal, index)))));
|
|
6546
7509
|
const tableData = [header, separator, ...rows];
|
|
6547
7510
|
const table = tableData.map(row => `| ${row.join(" | ")} |`).join("\n");
|
|
6548
7511
|
return [
|
|
@@ -6567,9 +7530,10 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
6567
7530
|
*
|
|
6568
7531
|
* @param strategyName - Strategy name
|
|
6569
7532
|
* @param path - Directory path to save report (default: "./dump/backtest")
|
|
7533
|
+
* @param columns - Column configuration for formatting the table
|
|
6570
7534
|
*/
|
|
6571
|
-
async dump(strategyName, path = "./dump/backtest") {
|
|
6572
|
-
const markdown = await this.getReport(strategyName);
|
|
7535
|
+
async dump(strategyName, path = "./dump/backtest", columns = COLUMN_CONFIG.backtest_columns) {
|
|
7536
|
+
const markdown = await this.getReport(strategyName, columns);
|
|
6573
7537
|
try {
|
|
6574
7538
|
const dir = join(process.cwd(), path);
|
|
6575
7539
|
await mkdir(dir, { recursive: true });
|
|
@@ -6677,6 +7641,7 @@ class BacktestMarkdownService {
|
|
|
6677
7641
|
*
|
|
6678
7642
|
* @param symbol - Trading pair symbol
|
|
6679
7643
|
* @param strategyName - Strategy name to generate report for
|
|
7644
|
+
* @param columns - Column configuration for formatting the table
|
|
6680
7645
|
* @returns Markdown formatted report string with table of all closed signals
|
|
6681
7646
|
*
|
|
6682
7647
|
* @example
|
|
@@ -6686,13 +7651,13 @@ class BacktestMarkdownService {
|
|
|
6686
7651
|
* console.log(markdown);
|
|
6687
7652
|
* ```
|
|
6688
7653
|
*/
|
|
6689
|
-
this.getReport = async (symbol, strategyName) => {
|
|
7654
|
+
this.getReport = async (symbol, strategyName, columns = COLUMN_CONFIG.backtest_columns) => {
|
|
6690
7655
|
this.loggerService.log("backtestMarkdownService getReport", {
|
|
6691
7656
|
symbol,
|
|
6692
7657
|
strategyName,
|
|
6693
7658
|
});
|
|
6694
7659
|
const storage = this.getStorage(symbol, strategyName);
|
|
6695
|
-
return storage.getReport(strategyName);
|
|
7660
|
+
return storage.getReport(strategyName, columns);
|
|
6696
7661
|
};
|
|
6697
7662
|
/**
|
|
6698
7663
|
* Saves symbol-strategy report to disk.
|
|
@@ -6702,6 +7667,7 @@ class BacktestMarkdownService {
|
|
|
6702
7667
|
* @param symbol - Trading pair symbol
|
|
6703
7668
|
* @param strategyName - Strategy name to save report for
|
|
6704
7669
|
* @param path - Directory path to save report (default: "./dump/backtest")
|
|
7670
|
+
* @param columns - Column configuration for formatting the table
|
|
6705
7671
|
*
|
|
6706
7672
|
* @example
|
|
6707
7673
|
* ```typescript
|
|
@@ -6714,14 +7680,14 @@ class BacktestMarkdownService {
|
|
|
6714
7680
|
* await service.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
6715
7681
|
* ```
|
|
6716
7682
|
*/
|
|
6717
|
-
this.dump = async (symbol, strategyName, path = "./dump/backtest") => {
|
|
7683
|
+
this.dump = async (symbol, strategyName, path = "./dump/backtest", columns = COLUMN_CONFIG.backtest_columns) => {
|
|
6718
7684
|
this.loggerService.log("backtestMarkdownService dump", {
|
|
6719
7685
|
symbol,
|
|
6720
7686
|
strategyName,
|
|
6721
7687
|
path,
|
|
6722
7688
|
});
|
|
6723
7689
|
const storage = this.getStorage(symbol, strategyName);
|
|
6724
|
-
await storage.dump(strategyName, path);
|
|
7690
|
+
await storage.dump(strategyName, path, columns);
|
|
6725
7691
|
};
|
|
6726
7692
|
/**
|
|
6727
7693
|
* Clears accumulated signal data from storage.
|
|
@@ -6789,104 +7755,6 @@ function isUnsafe$2(value) {
|
|
|
6789
7755
|
}
|
|
6790
7756
|
return false;
|
|
6791
7757
|
}
|
|
6792
|
-
const columns$5 = [
|
|
6793
|
-
{
|
|
6794
|
-
key: "timestamp",
|
|
6795
|
-
label: "Timestamp",
|
|
6796
|
-
format: (data) => new Date(data.timestamp).toISOString(),
|
|
6797
|
-
isVisible: () => true,
|
|
6798
|
-
},
|
|
6799
|
-
{
|
|
6800
|
-
key: "action",
|
|
6801
|
-
label: "Action",
|
|
6802
|
-
format: (data) => data.action.toUpperCase(),
|
|
6803
|
-
isVisible: () => true,
|
|
6804
|
-
},
|
|
6805
|
-
{
|
|
6806
|
-
key: "symbol",
|
|
6807
|
-
label: "Symbol",
|
|
6808
|
-
format: (data) => data.symbol ?? "N/A",
|
|
6809
|
-
isVisible: () => true,
|
|
6810
|
-
},
|
|
6811
|
-
{
|
|
6812
|
-
key: "signalId",
|
|
6813
|
-
label: "Signal ID",
|
|
6814
|
-
format: (data) => data.signalId ?? "N/A",
|
|
6815
|
-
isVisible: () => true,
|
|
6816
|
-
},
|
|
6817
|
-
{
|
|
6818
|
-
key: "position",
|
|
6819
|
-
label: "Position",
|
|
6820
|
-
format: (data) => data.position?.toUpperCase() ?? "N/A",
|
|
6821
|
-
isVisible: () => true,
|
|
6822
|
-
},
|
|
6823
|
-
{
|
|
6824
|
-
key: "note",
|
|
6825
|
-
label: "Note",
|
|
6826
|
-
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
6827
|
-
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
6828
|
-
},
|
|
6829
|
-
{
|
|
6830
|
-
key: "currentPrice",
|
|
6831
|
-
label: "Current Price",
|
|
6832
|
-
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
6833
|
-
isVisible: () => true,
|
|
6834
|
-
},
|
|
6835
|
-
{
|
|
6836
|
-
key: "openPrice",
|
|
6837
|
-
label: "Open Price",
|
|
6838
|
-
format: (data) => data.openPrice !== undefined ? `${data.openPrice.toFixed(8)} USD` : "N/A",
|
|
6839
|
-
isVisible: () => true,
|
|
6840
|
-
},
|
|
6841
|
-
{
|
|
6842
|
-
key: "takeProfit",
|
|
6843
|
-
label: "Take Profit",
|
|
6844
|
-
format: (data) => data.takeProfit !== undefined
|
|
6845
|
-
? `${data.takeProfit.toFixed(8)} USD`
|
|
6846
|
-
: "N/A",
|
|
6847
|
-
isVisible: () => true,
|
|
6848
|
-
},
|
|
6849
|
-
{
|
|
6850
|
-
key: "stopLoss",
|
|
6851
|
-
label: "Stop Loss",
|
|
6852
|
-
format: (data) => data.stopLoss !== undefined ? `${data.stopLoss.toFixed(8)} USD` : "N/A",
|
|
6853
|
-
isVisible: () => true,
|
|
6854
|
-
},
|
|
6855
|
-
{
|
|
6856
|
-
key: "percentTp",
|
|
6857
|
-
label: "% to TP",
|
|
6858
|
-
format: (data) => data.percentTp !== undefined ? `${data.percentTp.toFixed(2)}%` : "N/A",
|
|
6859
|
-
isVisible: () => true,
|
|
6860
|
-
},
|
|
6861
|
-
{
|
|
6862
|
-
key: "percentSl",
|
|
6863
|
-
label: "% to SL",
|
|
6864
|
-
format: (data) => data.percentSl !== undefined ? `${data.percentSl.toFixed(2)}%` : "N/A",
|
|
6865
|
-
isVisible: () => true,
|
|
6866
|
-
},
|
|
6867
|
-
{
|
|
6868
|
-
key: "pnl",
|
|
6869
|
-
label: "PNL (net)",
|
|
6870
|
-
format: (data) => {
|
|
6871
|
-
if (data.pnl === undefined)
|
|
6872
|
-
return "N/A";
|
|
6873
|
-
return `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`;
|
|
6874
|
-
},
|
|
6875
|
-
isVisible: () => true,
|
|
6876
|
-
},
|
|
6877
|
-
{
|
|
6878
|
-
key: "closeReason",
|
|
6879
|
-
label: "Close Reason",
|
|
6880
|
-
format: (data) => data.closeReason ?? "N/A",
|
|
6881
|
-
isVisible: () => true,
|
|
6882
|
-
},
|
|
6883
|
-
{
|
|
6884
|
-
key: "duration",
|
|
6885
|
-
label: "Duration (min)",
|
|
6886
|
-
format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
|
|
6887
|
-
isVisible: () => true,
|
|
6888
|
-
},
|
|
6889
|
-
];
|
|
6890
7758
|
/** Maximum number of events to store in live trading reports */
|
|
6891
7759
|
const MAX_EVENTS$5 = 250;
|
|
6892
7760
|
/**
|
|
@@ -7098,9 +7966,10 @@ let ReportStorage$4 = class ReportStorage {
|
|
|
7098
7966
|
* Generates markdown report with all tick events for a strategy (View).
|
|
7099
7967
|
*
|
|
7100
7968
|
* @param strategyName - Strategy name
|
|
7969
|
+
* @param columns - Column configuration for formatting the table
|
|
7101
7970
|
* @returns Markdown formatted report with all events
|
|
7102
7971
|
*/
|
|
7103
|
-
async getReport(strategyName) {
|
|
7972
|
+
async getReport(strategyName, columns = COLUMN_CONFIG.live_columns) {
|
|
7104
7973
|
const stats = await this.getData();
|
|
7105
7974
|
if (stats.totalEvents === 0) {
|
|
7106
7975
|
return [
|
|
@@ -7109,10 +7978,15 @@ let ReportStorage$4 = class ReportStorage {
|
|
|
7109
7978
|
"No events recorded yet."
|
|
7110
7979
|
].join("\n");
|
|
7111
7980
|
}
|
|
7112
|
-
const visibleColumns =
|
|
7981
|
+
const visibleColumns = [];
|
|
7982
|
+
for (const col of columns) {
|
|
7983
|
+
if (await col.isVisible()) {
|
|
7984
|
+
visibleColumns.push(col);
|
|
7985
|
+
}
|
|
7986
|
+
}
|
|
7113
7987
|
const header = visibleColumns.map((col) => col.label);
|
|
7114
7988
|
const separator = visibleColumns.map(() => "---");
|
|
7115
|
-
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
7989
|
+
const rows = await Promise.all(this._eventList.map(async (event, index) => Promise.all(visibleColumns.map((col) => col.format(event, index)))));
|
|
7116
7990
|
const tableData = [header, separator, ...rows];
|
|
7117
7991
|
const table = tableData.map(row => `| ${row.join(" | ")} |`).join("\n");
|
|
7118
7992
|
return [
|
|
@@ -7137,9 +8011,10 @@ let ReportStorage$4 = class ReportStorage {
|
|
|
7137
8011
|
*
|
|
7138
8012
|
* @param strategyName - Strategy name
|
|
7139
8013
|
* @param path - Directory path to save report (default: "./dump/live")
|
|
8014
|
+
* @param columns - Column configuration for formatting the table
|
|
7140
8015
|
*/
|
|
7141
|
-
async dump(strategyName, path = "./dump/live") {
|
|
7142
|
-
const markdown = await this.getReport(strategyName);
|
|
8016
|
+
async dump(strategyName, path = "./dump/live", columns = COLUMN_CONFIG.live_columns) {
|
|
8017
|
+
const markdown = await this.getReport(strategyName, columns);
|
|
7143
8018
|
try {
|
|
7144
8019
|
const dir = join(process.cwd(), path);
|
|
7145
8020
|
await mkdir(dir, { recursive: true });
|
|
@@ -7260,6 +8135,7 @@ class LiveMarkdownService {
|
|
|
7260
8135
|
*
|
|
7261
8136
|
* @param symbol - Trading pair symbol
|
|
7262
8137
|
* @param strategyName - Strategy name to generate report for
|
|
8138
|
+
* @param columns - Column configuration for formatting the table
|
|
7263
8139
|
* @returns Markdown formatted report string with table of all events
|
|
7264
8140
|
*
|
|
7265
8141
|
* @example
|
|
@@ -7269,13 +8145,13 @@ class LiveMarkdownService {
|
|
|
7269
8145
|
* console.log(markdown);
|
|
7270
8146
|
* ```
|
|
7271
8147
|
*/
|
|
7272
|
-
this.getReport = async (symbol, strategyName) => {
|
|
8148
|
+
this.getReport = async (symbol, strategyName, columns = COLUMN_CONFIG.live_columns) => {
|
|
7273
8149
|
this.loggerService.log("liveMarkdownService getReport", {
|
|
7274
8150
|
symbol,
|
|
7275
8151
|
strategyName,
|
|
7276
8152
|
});
|
|
7277
8153
|
const storage = this.getStorage(symbol, strategyName);
|
|
7278
|
-
return storage.getReport(strategyName);
|
|
8154
|
+
return storage.getReport(strategyName, columns);
|
|
7279
8155
|
};
|
|
7280
8156
|
/**
|
|
7281
8157
|
* Saves symbol-strategy report to disk.
|
|
@@ -7285,6 +8161,7 @@ class LiveMarkdownService {
|
|
|
7285
8161
|
* @param symbol - Trading pair symbol
|
|
7286
8162
|
* @param strategyName - Strategy name to save report for
|
|
7287
8163
|
* @param path - Directory path to save report (default: "./dump/live")
|
|
8164
|
+
* @param columns - Column configuration for formatting the table
|
|
7288
8165
|
*
|
|
7289
8166
|
* @example
|
|
7290
8167
|
* ```typescript
|
|
@@ -7297,14 +8174,14 @@ class LiveMarkdownService {
|
|
|
7297
8174
|
* await service.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
7298
8175
|
* ```
|
|
7299
8176
|
*/
|
|
7300
|
-
this.dump = async (symbol, strategyName, path = "./dump/live") => {
|
|
8177
|
+
this.dump = async (symbol, strategyName, path = "./dump/live", columns = COLUMN_CONFIG.live_columns) => {
|
|
7301
8178
|
this.loggerService.log("liveMarkdownService dump", {
|
|
7302
8179
|
symbol,
|
|
7303
8180
|
strategyName,
|
|
7304
8181
|
path,
|
|
7305
8182
|
});
|
|
7306
8183
|
const storage = this.getStorage(symbol, strategyName);
|
|
7307
|
-
await storage.dump(strategyName, path);
|
|
8184
|
+
await storage.dump(strategyName, path, columns);
|
|
7308
8185
|
};
|
|
7309
8186
|
/**
|
|
7310
8187
|
* Clears accumulated event data from storage.
|
|
@@ -7351,77 +8228,9 @@ class LiveMarkdownService {
|
|
|
7351
8228
|
this.loggerService.log("liveMarkdownService init");
|
|
7352
8229
|
signalLiveEmitter.subscribe(this.tick);
|
|
7353
8230
|
});
|
|
7354
|
-
}
|
|
7355
|
-
}
|
|
7356
|
-
|
|
7357
|
-
const columns$4 = [
|
|
7358
|
-
{
|
|
7359
|
-
key: "timestamp",
|
|
7360
|
-
label: "Timestamp",
|
|
7361
|
-
format: (data) => new Date(data.timestamp).toISOString(),
|
|
7362
|
-
isVisible: () => true,
|
|
7363
|
-
},
|
|
7364
|
-
{
|
|
7365
|
-
key: "action",
|
|
7366
|
-
label: "Action",
|
|
7367
|
-
format: (data) => data.action.toUpperCase(),
|
|
7368
|
-
isVisible: () => true,
|
|
7369
|
-
},
|
|
7370
|
-
{
|
|
7371
|
-
key: "symbol",
|
|
7372
|
-
label: "Symbol",
|
|
7373
|
-
format: (data) => data.symbol,
|
|
7374
|
-
isVisible: () => true,
|
|
7375
|
-
},
|
|
7376
|
-
{
|
|
7377
|
-
key: "signalId",
|
|
7378
|
-
label: "Signal ID",
|
|
7379
|
-
format: (data) => data.signalId,
|
|
7380
|
-
isVisible: () => true,
|
|
7381
|
-
},
|
|
7382
|
-
{
|
|
7383
|
-
key: "position",
|
|
7384
|
-
label: "Position",
|
|
7385
|
-
format: (data) => data.position.toUpperCase(),
|
|
7386
|
-
isVisible: () => true,
|
|
7387
|
-
},
|
|
7388
|
-
{
|
|
7389
|
-
key: "note",
|
|
7390
|
-
label: "Note",
|
|
7391
|
-
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
7392
|
-
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
7393
|
-
},
|
|
7394
|
-
{
|
|
7395
|
-
key: "currentPrice",
|
|
7396
|
-
label: "Current Price",
|
|
7397
|
-
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
7398
|
-
isVisible: () => true,
|
|
7399
|
-
},
|
|
7400
|
-
{
|
|
7401
|
-
key: "priceOpen",
|
|
7402
|
-
label: "Entry Price",
|
|
7403
|
-
format: (data) => `${data.priceOpen.toFixed(8)} USD`,
|
|
7404
|
-
isVisible: () => true,
|
|
7405
|
-
},
|
|
7406
|
-
{
|
|
7407
|
-
key: "takeProfit",
|
|
7408
|
-
label: "Take Profit",
|
|
7409
|
-
format: (data) => `${data.takeProfit.toFixed(8)} USD`,
|
|
7410
|
-
isVisible: () => true,
|
|
7411
|
-
},
|
|
7412
|
-
{
|
|
7413
|
-
key: "stopLoss",
|
|
7414
|
-
label: "Stop Loss",
|
|
7415
|
-
format: (data) => `${data.stopLoss.toFixed(8)} USD`,
|
|
7416
|
-
isVisible: () => true,
|
|
7417
|
-
},
|
|
7418
|
-
{
|
|
7419
|
-
key: "duration",
|
|
7420
|
-
label: "Wait Time (min)",
|
|
7421
|
-
format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
|
|
7422
|
-
isVisible: () => true,
|
|
7423
|
-
},
|
|
7424
|
-
];
|
|
8231
|
+
}
|
|
8232
|
+
}
|
|
8233
|
+
|
|
7425
8234
|
/** Maximum number of events to store in schedule reports */
|
|
7426
8235
|
const MAX_EVENTS$4 = 250;
|
|
7427
8236
|
/**
|
|
@@ -7566,9 +8375,10 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
7566
8375
|
* Generates markdown report with all scheduled events for a strategy (View).
|
|
7567
8376
|
*
|
|
7568
8377
|
* @param strategyName - Strategy name
|
|
8378
|
+
* @param columns - Column configuration for formatting the table
|
|
7569
8379
|
* @returns Markdown formatted report with all events
|
|
7570
8380
|
*/
|
|
7571
|
-
async getReport(strategyName) {
|
|
8381
|
+
async getReport(strategyName, columns = COLUMN_CONFIG.schedule_columns) {
|
|
7572
8382
|
const stats = await this.getData();
|
|
7573
8383
|
if (stats.totalEvents === 0) {
|
|
7574
8384
|
return [
|
|
@@ -7577,10 +8387,15 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
7577
8387
|
"No scheduled signals recorded yet."
|
|
7578
8388
|
].join("\n");
|
|
7579
8389
|
}
|
|
7580
|
-
const visibleColumns =
|
|
8390
|
+
const visibleColumns = [];
|
|
8391
|
+
for (const col of columns) {
|
|
8392
|
+
if (await col.isVisible()) {
|
|
8393
|
+
visibleColumns.push(col);
|
|
8394
|
+
}
|
|
8395
|
+
}
|
|
7581
8396
|
const header = visibleColumns.map((col) => col.label);
|
|
7582
8397
|
const separator = visibleColumns.map(() => "---");
|
|
7583
|
-
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
8398
|
+
const rows = await Promise.all(this._eventList.map(async (event, index) => Promise.all(visibleColumns.map((col) => col.format(event, index)))));
|
|
7584
8399
|
const tableData = [header, separator, ...rows];
|
|
7585
8400
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
7586
8401
|
return [
|
|
@@ -7603,9 +8418,10 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
7603
8418
|
*
|
|
7604
8419
|
* @param strategyName - Strategy name
|
|
7605
8420
|
* @param path - Directory path to save report (default: "./dump/schedule")
|
|
8421
|
+
* @param columns - Column configuration for formatting the table
|
|
7606
8422
|
*/
|
|
7607
|
-
async dump(strategyName, path = "./dump/schedule") {
|
|
7608
|
-
const markdown = await this.getReport(strategyName);
|
|
8423
|
+
async dump(strategyName, path = "./dump/schedule", columns = COLUMN_CONFIG.schedule_columns) {
|
|
8424
|
+
const markdown = await this.getReport(strategyName, columns);
|
|
7609
8425
|
try {
|
|
7610
8426
|
const dir = join(process.cwd(), path);
|
|
7611
8427
|
await mkdir(dir, { recursive: true });
|
|
@@ -7711,6 +8527,7 @@ class ScheduleMarkdownService {
|
|
|
7711
8527
|
*
|
|
7712
8528
|
* @param symbol - Trading pair symbol
|
|
7713
8529
|
* @param strategyName - Strategy name to generate report for
|
|
8530
|
+
* @param columns - Column configuration for formatting the table
|
|
7714
8531
|
* @returns Markdown formatted report string with table of all events
|
|
7715
8532
|
*
|
|
7716
8533
|
* @example
|
|
@@ -7720,13 +8537,13 @@ class ScheduleMarkdownService {
|
|
|
7720
8537
|
* console.log(markdown);
|
|
7721
8538
|
* ```
|
|
7722
8539
|
*/
|
|
7723
|
-
this.getReport = async (symbol, strategyName) => {
|
|
8540
|
+
this.getReport = async (symbol, strategyName, columns = COLUMN_CONFIG.schedule_columns) => {
|
|
7724
8541
|
this.loggerService.log("scheduleMarkdownService getReport", {
|
|
7725
8542
|
symbol,
|
|
7726
8543
|
strategyName,
|
|
7727
8544
|
});
|
|
7728
8545
|
const storage = this.getStorage(symbol, strategyName);
|
|
7729
|
-
return storage.getReport(strategyName);
|
|
8546
|
+
return storage.getReport(strategyName, columns);
|
|
7730
8547
|
};
|
|
7731
8548
|
/**
|
|
7732
8549
|
* Saves symbol-strategy report to disk.
|
|
@@ -7736,6 +8553,7 @@ class ScheduleMarkdownService {
|
|
|
7736
8553
|
* @param symbol - Trading pair symbol
|
|
7737
8554
|
* @param strategyName - Strategy name to save report for
|
|
7738
8555
|
* @param path - Directory path to save report (default: "./dump/schedule")
|
|
8556
|
+
* @param columns - Column configuration for formatting the table
|
|
7739
8557
|
*
|
|
7740
8558
|
* @example
|
|
7741
8559
|
* ```typescript
|
|
@@ -7748,14 +8566,14 @@ class ScheduleMarkdownService {
|
|
|
7748
8566
|
* await service.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
7749
8567
|
* ```
|
|
7750
8568
|
*/
|
|
7751
|
-
this.dump = async (symbol, strategyName, path = "./dump/schedule") => {
|
|
8569
|
+
this.dump = async (symbol, strategyName, path = "./dump/schedule", columns = COLUMN_CONFIG.schedule_columns) => {
|
|
7752
8570
|
this.loggerService.log("scheduleMarkdownService dump", {
|
|
7753
8571
|
symbol,
|
|
7754
8572
|
strategyName,
|
|
7755
8573
|
path,
|
|
7756
8574
|
});
|
|
7757
8575
|
const storage = this.getStorage(symbol, strategyName);
|
|
7758
|
-
await storage.dump(strategyName, path);
|
|
8576
|
+
await storage.dump(strategyName, path, columns);
|
|
7759
8577
|
};
|
|
7760
8578
|
/**
|
|
7761
8579
|
* Clears accumulated event data from storage.
|
|
@@ -7814,86 +8632,6 @@ function percentile(sortedArray, p) {
|
|
|
7814
8632
|
const index = Math.ceil((sortedArray.length * p) / 100) - 1;
|
|
7815
8633
|
return sortedArray[Math.max(0, index)];
|
|
7816
8634
|
}
|
|
7817
|
-
const columns$3 = [
|
|
7818
|
-
{
|
|
7819
|
-
key: "metricType",
|
|
7820
|
-
label: "Metric Type",
|
|
7821
|
-
format: (data) => data.metricType,
|
|
7822
|
-
isVisible: () => true,
|
|
7823
|
-
},
|
|
7824
|
-
{
|
|
7825
|
-
key: "count",
|
|
7826
|
-
label: "Count",
|
|
7827
|
-
format: (data) => data.count.toString(),
|
|
7828
|
-
isVisible: () => true,
|
|
7829
|
-
},
|
|
7830
|
-
{
|
|
7831
|
-
key: "totalDuration",
|
|
7832
|
-
label: "Total (ms)",
|
|
7833
|
-
format: (data) => data.totalDuration.toFixed(2),
|
|
7834
|
-
isVisible: () => true,
|
|
7835
|
-
},
|
|
7836
|
-
{
|
|
7837
|
-
key: "avgDuration",
|
|
7838
|
-
label: "Avg (ms)",
|
|
7839
|
-
format: (data) => data.avgDuration.toFixed(2),
|
|
7840
|
-
isVisible: () => true,
|
|
7841
|
-
},
|
|
7842
|
-
{
|
|
7843
|
-
key: "minDuration",
|
|
7844
|
-
label: "Min (ms)",
|
|
7845
|
-
format: (data) => data.minDuration.toFixed(2),
|
|
7846
|
-
isVisible: () => true,
|
|
7847
|
-
},
|
|
7848
|
-
{
|
|
7849
|
-
key: "maxDuration",
|
|
7850
|
-
label: "Max (ms)",
|
|
7851
|
-
format: (data) => data.maxDuration.toFixed(2),
|
|
7852
|
-
isVisible: () => true,
|
|
7853
|
-
},
|
|
7854
|
-
{
|
|
7855
|
-
key: "stdDev",
|
|
7856
|
-
label: "Std Dev (ms)",
|
|
7857
|
-
format: (data) => data.stdDev.toFixed(2),
|
|
7858
|
-
isVisible: () => true,
|
|
7859
|
-
},
|
|
7860
|
-
{
|
|
7861
|
-
key: "median",
|
|
7862
|
-
label: "Median (ms)",
|
|
7863
|
-
format: (data) => data.median.toFixed(2),
|
|
7864
|
-
isVisible: () => true,
|
|
7865
|
-
},
|
|
7866
|
-
{
|
|
7867
|
-
key: "p95",
|
|
7868
|
-
label: "P95 (ms)",
|
|
7869
|
-
format: (data) => data.p95.toFixed(2),
|
|
7870
|
-
isVisible: () => true,
|
|
7871
|
-
},
|
|
7872
|
-
{
|
|
7873
|
-
key: "p99",
|
|
7874
|
-
label: "P99 (ms)",
|
|
7875
|
-
format: (data) => data.p99.toFixed(2),
|
|
7876
|
-
isVisible: () => true,
|
|
7877
|
-
},
|
|
7878
|
-
{
|
|
7879
|
-
key: "avgWaitTime",
|
|
7880
|
-
label: "Avg Wait (ms)",
|
|
7881
|
-
format: (data) => data.avgWaitTime.toFixed(2),
|
|
7882
|
-
isVisible: () => true,
|
|
7883
|
-
},
|
|
7884
|
-
{
|
|
7885
|
-
key: "minWaitTime",
|
|
7886
|
-
label: "Min Wait (ms)",
|
|
7887
|
-
format: (data) => data.minWaitTime.toFixed(2),
|
|
7888
|
-
isVisible: () => true,
|
|
7889
|
-
},
|
|
7890
|
-
{
|
|
7891
|
-
key: "maxWaitTime",
|
|
7892
|
-
label: "Max Wait (ms)",
|
|
7893
|
-
format: (data) => data.maxWaitTime.toFixed(2),
|
|
7894
|
-
isVisible: () => true,
|
|
7895
|
-
},
|
|
7896
|
-
];
|
|
7897
8635
|
/** Maximum number of performance events to store per strategy */
|
|
7898
8636
|
const MAX_EVENTS$3 = 10000;
|
|
7899
8637
|
/**
|
|
@@ -7996,9 +8734,10 @@ class PerformanceStorage {
|
|
|
7996
8734
|
* Generates markdown report with performance statistics.
|
|
7997
8735
|
*
|
|
7998
8736
|
* @param strategyName - Strategy name
|
|
8737
|
+
* @param columns - Column configuration for formatting the table
|
|
7999
8738
|
* @returns Markdown formatted report
|
|
8000
8739
|
*/
|
|
8001
|
-
async getReport(strategyName) {
|
|
8740
|
+
async getReport(strategyName, columns = COLUMN_CONFIG.performance_columns) {
|
|
8002
8741
|
const stats = await this.getData(strategyName);
|
|
8003
8742
|
if (stats.totalEvents === 0) {
|
|
8004
8743
|
return [
|
|
@@ -8010,10 +8749,15 @@ class PerformanceStorage {
|
|
|
8010
8749
|
// Sort metrics by total duration (descending) to show bottlenecks first
|
|
8011
8750
|
const sortedMetrics = Object.values(stats.metricStats).sort((a, b) => b.totalDuration - a.totalDuration);
|
|
8012
8751
|
// Generate summary table using Column interface
|
|
8013
|
-
const visibleColumns =
|
|
8752
|
+
const visibleColumns = [];
|
|
8753
|
+
for (const col of columns) {
|
|
8754
|
+
if (await col.isVisible()) {
|
|
8755
|
+
visibleColumns.push(col);
|
|
8756
|
+
}
|
|
8757
|
+
}
|
|
8014
8758
|
const header = visibleColumns.map((col) => col.label);
|
|
8015
8759
|
const separator = visibleColumns.map(() => "---");
|
|
8016
|
-
const rows = sortedMetrics.map((metric) => visibleColumns.map((col) => col.format(metric)));
|
|
8760
|
+
const rows = await Promise.all(sortedMetrics.map(async (metric, index) => Promise.all(visibleColumns.map((col) => col.format(metric, index)))));
|
|
8017
8761
|
const tableData = [header, separator, ...rows];
|
|
8018
8762
|
const summaryTable = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
8019
8763
|
// Calculate percentage of total time for each metric
|
|
@@ -8044,9 +8788,10 @@ class PerformanceStorage {
|
|
|
8044
8788
|
*
|
|
8045
8789
|
* @param strategyName - Strategy name
|
|
8046
8790
|
* @param path - Directory path to save report
|
|
8791
|
+
* @param columns - Column configuration for formatting the table
|
|
8047
8792
|
*/
|
|
8048
|
-
async dump(strategyName, path = "./dump/performance") {
|
|
8049
|
-
const markdown = await this.getReport(strategyName);
|
|
8793
|
+
async dump(strategyName, path = "./dump/performance", columns = COLUMN_CONFIG.performance_columns) {
|
|
8794
|
+
const markdown = await this.getReport(strategyName, columns);
|
|
8050
8795
|
try {
|
|
8051
8796
|
const dir = join(process.cwd(), path);
|
|
8052
8797
|
await mkdir(dir, { recursive: true });
|
|
@@ -8139,6 +8884,7 @@ class PerformanceMarkdownService {
|
|
|
8139
8884
|
*
|
|
8140
8885
|
* @param symbol - Trading pair symbol
|
|
8141
8886
|
* @param strategyName - Strategy name to generate report for
|
|
8887
|
+
* @param columns - Column configuration for formatting the table
|
|
8142
8888
|
* @returns Markdown formatted report string
|
|
8143
8889
|
*
|
|
8144
8890
|
* @example
|
|
@@ -8147,13 +8893,13 @@ class PerformanceMarkdownService {
|
|
|
8147
8893
|
* console.log(markdown);
|
|
8148
8894
|
* ```
|
|
8149
8895
|
*/
|
|
8150
|
-
this.getReport = async (symbol, strategyName) => {
|
|
8896
|
+
this.getReport = async (symbol, strategyName, columns = COLUMN_CONFIG.performance_columns) => {
|
|
8151
8897
|
this.loggerService.log("performanceMarkdownService getReport", {
|
|
8152
8898
|
symbol,
|
|
8153
8899
|
strategyName,
|
|
8154
8900
|
});
|
|
8155
8901
|
const storage = this.getStorage(symbol, strategyName);
|
|
8156
|
-
return storage.getReport(strategyName);
|
|
8902
|
+
return storage.getReport(strategyName, columns);
|
|
8157
8903
|
};
|
|
8158
8904
|
/**
|
|
8159
8905
|
* Saves performance report to disk.
|
|
@@ -8161,6 +8907,7 @@ class PerformanceMarkdownService {
|
|
|
8161
8907
|
* @param symbol - Trading pair symbol
|
|
8162
8908
|
* @param strategyName - Strategy name to save report for
|
|
8163
8909
|
* @param path - Directory path to save report
|
|
8910
|
+
* @param columns - Column configuration for formatting the table
|
|
8164
8911
|
*
|
|
8165
8912
|
* @example
|
|
8166
8913
|
* ```typescript
|
|
@@ -8171,14 +8918,14 @@ class PerformanceMarkdownService {
|
|
|
8171
8918
|
* await performanceService.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
8172
8919
|
* ```
|
|
8173
8920
|
*/
|
|
8174
|
-
this.dump = async (symbol, strategyName, path = "./dump/performance") => {
|
|
8921
|
+
this.dump = async (symbol, strategyName, path = "./dump/performance", columns = COLUMN_CONFIG.performance_columns) => {
|
|
8175
8922
|
this.loggerService.log("performanceMarkdownService dump", {
|
|
8176
8923
|
symbol,
|
|
8177
8924
|
strategyName,
|
|
8178
8925
|
path,
|
|
8179
8926
|
});
|
|
8180
8927
|
const storage = this.getStorage(symbol, strategyName);
|
|
8181
|
-
await storage.dump(strategyName, path);
|
|
8928
|
+
await storage.dump(strategyName, path, columns);
|
|
8182
8929
|
};
|
|
8183
8930
|
/**
|
|
8184
8931
|
* Clears accumulated performance data from storage.
|
|
@@ -8237,135 +8984,6 @@ function formatMetric(value) {
|
|
|
8237
8984
|
}
|
|
8238
8985
|
return value.toFixed(2);
|
|
8239
8986
|
}
|
|
8240
|
-
/**
|
|
8241
|
-
* Creates strategy comparison columns based on metric name.
|
|
8242
|
-
* Dynamically builds column configuration with metric-specific header.
|
|
8243
|
-
*
|
|
8244
|
-
* @param metric - Metric being optimized
|
|
8245
|
-
* @returns Array of column configurations for strategy comparison table
|
|
8246
|
-
*/
|
|
8247
|
-
function createStrategyColumns(metric) {
|
|
8248
|
-
return [
|
|
8249
|
-
{
|
|
8250
|
-
key: "rank",
|
|
8251
|
-
label: "Rank",
|
|
8252
|
-
format: (data, index) => `${index + 1}`,
|
|
8253
|
-
isVisible: () => true,
|
|
8254
|
-
},
|
|
8255
|
-
{
|
|
8256
|
-
key: "strategy",
|
|
8257
|
-
label: "Strategy",
|
|
8258
|
-
format: (data) => data.strategyName,
|
|
8259
|
-
isVisible: () => true,
|
|
8260
|
-
},
|
|
8261
|
-
{
|
|
8262
|
-
key: "metric",
|
|
8263
|
-
label: metric,
|
|
8264
|
-
format: (data) => formatMetric(data.metricValue),
|
|
8265
|
-
isVisible: () => true,
|
|
8266
|
-
},
|
|
8267
|
-
{
|
|
8268
|
-
key: "totalSignals",
|
|
8269
|
-
label: "Total Signals",
|
|
8270
|
-
format: (data) => `${data.stats.totalSignals}`,
|
|
8271
|
-
isVisible: () => true,
|
|
8272
|
-
},
|
|
8273
|
-
{
|
|
8274
|
-
key: "winRate",
|
|
8275
|
-
label: "Win Rate",
|
|
8276
|
-
format: (data) => data.stats.winRate !== null
|
|
8277
|
-
? `${data.stats.winRate.toFixed(2)}%`
|
|
8278
|
-
: "N/A",
|
|
8279
|
-
isVisible: () => true,
|
|
8280
|
-
},
|
|
8281
|
-
{
|
|
8282
|
-
key: "avgPnl",
|
|
8283
|
-
label: "Avg PNL",
|
|
8284
|
-
format: (data) => data.stats.avgPnl !== null
|
|
8285
|
-
? `${data.stats.avgPnl > 0 ? "+" : ""}${data.stats.avgPnl.toFixed(2)}%`
|
|
8286
|
-
: "N/A",
|
|
8287
|
-
isVisible: () => true,
|
|
8288
|
-
},
|
|
8289
|
-
{
|
|
8290
|
-
key: "totalPnl",
|
|
8291
|
-
label: "Total PNL",
|
|
8292
|
-
format: (data) => data.stats.totalPnl !== null
|
|
8293
|
-
? `${data.stats.totalPnl > 0 ? "+" : ""}${data.stats.totalPnl.toFixed(2)}%`
|
|
8294
|
-
: "N/A",
|
|
8295
|
-
isVisible: () => true,
|
|
8296
|
-
},
|
|
8297
|
-
{
|
|
8298
|
-
key: "sharpeRatio",
|
|
8299
|
-
label: "Sharpe Ratio",
|
|
8300
|
-
format: (data) => data.stats.sharpeRatio !== null
|
|
8301
|
-
? `${data.stats.sharpeRatio.toFixed(3)}`
|
|
8302
|
-
: "N/A",
|
|
8303
|
-
isVisible: () => true,
|
|
8304
|
-
},
|
|
8305
|
-
{
|
|
8306
|
-
key: "stdDev",
|
|
8307
|
-
label: "Std Dev",
|
|
8308
|
-
format: (data) => data.stats.stdDev !== null
|
|
8309
|
-
? `${data.stats.stdDev.toFixed(3)}%`
|
|
8310
|
-
: "N/A",
|
|
8311
|
-
isVisible: () => true,
|
|
8312
|
-
},
|
|
8313
|
-
];
|
|
8314
|
-
}
|
|
8315
|
-
/**
|
|
8316
|
-
* Column configuration for PNL table.
|
|
8317
|
-
* Defines all columns for displaying closed signals across strategies.
|
|
8318
|
-
*/
|
|
8319
|
-
const pnlColumns = [
|
|
8320
|
-
{
|
|
8321
|
-
key: "strategy",
|
|
8322
|
-
label: "Strategy",
|
|
8323
|
-
format: (data) => data.strategyName,
|
|
8324
|
-
isVisible: () => true,
|
|
8325
|
-
},
|
|
8326
|
-
{
|
|
8327
|
-
key: "signalId",
|
|
8328
|
-
label: "Signal ID",
|
|
8329
|
-
format: (data) => data.signalId,
|
|
8330
|
-
isVisible: () => true,
|
|
8331
|
-
},
|
|
8332
|
-
{
|
|
8333
|
-
key: "symbol",
|
|
8334
|
-
label: "Symbol",
|
|
8335
|
-
format: (data) => data.symbol,
|
|
8336
|
-
isVisible: () => true,
|
|
8337
|
-
},
|
|
8338
|
-
{
|
|
8339
|
-
key: "position",
|
|
8340
|
-
label: "Position",
|
|
8341
|
-
format: (data) => data.position.toUpperCase(),
|
|
8342
|
-
isVisible: () => true,
|
|
8343
|
-
},
|
|
8344
|
-
{
|
|
8345
|
-
key: "pnl",
|
|
8346
|
-
label: "PNL (net)",
|
|
8347
|
-
format: (data) => `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`,
|
|
8348
|
-
isVisible: () => true,
|
|
8349
|
-
},
|
|
8350
|
-
{
|
|
8351
|
-
key: "closeReason",
|
|
8352
|
-
label: "Close Reason",
|
|
8353
|
-
format: (data) => data.closeReason,
|
|
8354
|
-
isVisible: () => true,
|
|
8355
|
-
},
|
|
8356
|
-
{
|
|
8357
|
-
key: "openTime",
|
|
8358
|
-
label: "Open Time",
|
|
8359
|
-
format: (data) => new Date(data.openTime).toISOString(),
|
|
8360
|
-
isVisible: () => true,
|
|
8361
|
-
},
|
|
8362
|
-
{
|
|
8363
|
-
key: "closeTime",
|
|
8364
|
-
label: "Close Time",
|
|
8365
|
-
format: (data) => new Date(data.closeTime).toISOString(),
|
|
8366
|
-
isVisible: () => true,
|
|
8367
|
-
},
|
|
8368
|
-
];
|
|
8369
8987
|
/**
|
|
8370
8988
|
* Storage class for accumulating walker results.
|
|
8371
8989
|
* Maintains a list of all strategy results and provides methods to generate reports.
|
|
@@ -8397,7 +9015,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8397
9015
|
this._strategyResults.unshift({
|
|
8398
9016
|
strategyName: data.strategyName,
|
|
8399
9017
|
stats: data.stats,
|
|
8400
|
-
metricValue: data.metricValue,
|
|
9018
|
+
metricValue: isUnsafe$1(data.metricValue) ? null : data.metricValue,
|
|
8401
9019
|
});
|
|
8402
9020
|
}
|
|
8403
9021
|
/**
|
|
@@ -8430,11 +9048,11 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8430
9048
|
* Generates comparison table for top N strategies (View).
|
|
8431
9049
|
* Sorts strategies by metric value and formats as markdown table.
|
|
8432
9050
|
*
|
|
8433
|
-
* @param metric - Metric being optimized
|
|
8434
9051
|
* @param topN - Number of top strategies to include (default: 10)
|
|
9052
|
+
* @param columns - Column configuration for formatting the strategy comparison table
|
|
8435
9053
|
* @returns Markdown formatted comparison table
|
|
8436
9054
|
*/
|
|
8437
|
-
getComparisonTable(
|
|
9055
|
+
async getComparisonTable(topN = 10, columns = COLUMN_CONFIG.walker_strategy_columns) {
|
|
8438
9056
|
if (this._strategyResults.length === 0) {
|
|
8439
9057
|
return "No strategy results available.";
|
|
8440
9058
|
}
|
|
@@ -8447,13 +9065,17 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8447
9065
|
// Take top N strategies
|
|
8448
9066
|
const topStrategies = sortedResults.slice(0, topN);
|
|
8449
9067
|
// Get columns configuration
|
|
8450
|
-
const
|
|
8451
|
-
const
|
|
9068
|
+
const visibleColumns = [];
|
|
9069
|
+
for (const col of columns) {
|
|
9070
|
+
if (await col.isVisible()) {
|
|
9071
|
+
visibleColumns.push(col);
|
|
9072
|
+
}
|
|
9073
|
+
}
|
|
8452
9074
|
// Build table header
|
|
8453
9075
|
const header = visibleColumns.map((col) => col.label);
|
|
8454
9076
|
const separator = visibleColumns.map(() => "---");
|
|
8455
9077
|
// Build table rows
|
|
8456
|
-
const rows = topStrategies.map((result, index) => visibleColumns.map((col) => col.format(result, index)));
|
|
9078
|
+
const rows = await Promise.all(topStrategies.map(async (result, index) => Promise.all(visibleColumns.map((col) => col.format(result, index)))));
|
|
8457
9079
|
const tableData = [header, separator, ...rows];
|
|
8458
9080
|
return tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
8459
9081
|
}
|
|
@@ -8461,9 +9083,10 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8461
9083
|
* Generates PNL table showing all closed signals across all strategies (View).
|
|
8462
9084
|
* Collects all signals from all strategies and formats as markdown table.
|
|
8463
9085
|
*
|
|
9086
|
+
* @param columns - Column configuration for formatting the PNL table
|
|
8464
9087
|
* @returns Markdown formatted PNL table
|
|
8465
9088
|
*/
|
|
8466
|
-
getPnlTable() {
|
|
9089
|
+
async getPnlTable(columns = COLUMN_CONFIG.walker_pnl_columns) {
|
|
8467
9090
|
if (this._strategyResults.length === 0) {
|
|
8468
9091
|
return "No strategy results available.";
|
|
8469
9092
|
}
|
|
@@ -8487,11 +9110,16 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8487
9110
|
return "No closed signals available.";
|
|
8488
9111
|
}
|
|
8489
9112
|
// Build table header
|
|
8490
|
-
const visibleColumns =
|
|
9113
|
+
const visibleColumns = [];
|
|
9114
|
+
for (const col of columns) {
|
|
9115
|
+
if (await col.isVisible()) {
|
|
9116
|
+
visibleColumns.push(col);
|
|
9117
|
+
}
|
|
9118
|
+
}
|
|
8491
9119
|
const header = visibleColumns.map((col) => col.label);
|
|
8492
9120
|
const separator = visibleColumns.map(() => "---");
|
|
8493
9121
|
// Build table rows
|
|
8494
|
-
const rows = allSignals.map((signal) => visibleColumns.map((col) => col.format(signal)));
|
|
9122
|
+
const rows = await Promise.all(allSignals.map(async (signal, index) => Promise.all(visibleColumns.map((col) => col.format(signal, index)))));
|
|
8495
9123
|
const tableData = [header, separator, ...rows];
|
|
8496
9124
|
return tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
8497
9125
|
}
|
|
@@ -8502,9 +9130,11 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8502
9130
|
* @param symbol - Trading symbol
|
|
8503
9131
|
* @param metric - Metric being optimized
|
|
8504
9132
|
* @param context - Context with exchangeName and frameName
|
|
9133
|
+
* @param strategyColumns - Column configuration for strategy comparison table
|
|
9134
|
+
* @param pnlColumns - Column configuration for PNL table
|
|
8505
9135
|
* @returns Markdown formatted report with all results
|
|
8506
9136
|
*/
|
|
8507
|
-
async getReport(symbol, metric, context) {
|
|
9137
|
+
async getReport(symbol, metric, context, strategyColumns = COLUMN_CONFIG.walker_strategy_columns, pnlColumns = COLUMN_CONFIG.walker_pnl_columns) {
|
|
8508
9138
|
const results = await this.getData(symbol, metric, context);
|
|
8509
9139
|
// Get total signals for best strategy
|
|
8510
9140
|
const bestStrategySignals = results.bestStats?.totalSignals ?? 0;
|
|
@@ -8524,11 +9154,11 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8524
9154
|
"",
|
|
8525
9155
|
"## Top Strategies Comparison",
|
|
8526
9156
|
"",
|
|
8527
|
-
this.getComparisonTable(
|
|
9157
|
+
await this.getComparisonTable(10, strategyColumns),
|
|
8528
9158
|
"",
|
|
8529
9159
|
"## All Signals (PNL Table)",
|
|
8530
9160
|
"",
|
|
8531
|
-
this.getPnlTable(),
|
|
9161
|
+
await this.getPnlTable(pnlColumns),
|
|
8532
9162
|
"",
|
|
8533
9163
|
"**Note:** Higher values are better for all metrics except Standard Deviation (lower is better)."
|
|
8534
9164
|
].join("\n");
|
|
@@ -8540,9 +9170,11 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8540
9170
|
* @param metric - Metric being optimized
|
|
8541
9171
|
* @param context - Context with exchangeName and frameName
|
|
8542
9172
|
* @param path - Directory path to save report (default: "./dump/walker")
|
|
9173
|
+
* @param strategyColumns - Column configuration for strategy comparison table
|
|
9174
|
+
* @param pnlColumns - Column configuration for PNL table
|
|
8543
9175
|
*/
|
|
8544
|
-
async dump(symbol, metric, context, path = "./dump/walker") {
|
|
8545
|
-
const markdown = await this.getReport(symbol, metric, context);
|
|
9176
|
+
async dump(symbol, metric, context, path = "./dump/walker", strategyColumns = COLUMN_CONFIG.walker_strategy_columns, pnlColumns = COLUMN_CONFIG.walker_pnl_columns) {
|
|
9177
|
+
const markdown = await this.getReport(symbol, metric, context, strategyColumns, pnlColumns);
|
|
8546
9178
|
try {
|
|
8547
9179
|
const dir = join(process.cwd(), path);
|
|
8548
9180
|
await mkdir(dir, { recursive: true });
|
|
@@ -8635,6 +9267,8 @@ class WalkerMarkdownService {
|
|
|
8635
9267
|
* @param symbol - Trading symbol
|
|
8636
9268
|
* @param metric - Metric being optimized
|
|
8637
9269
|
* @param context - Context with exchangeName and frameName
|
|
9270
|
+
* @param strategyColumns - Column configuration for strategy comparison table
|
|
9271
|
+
* @param pnlColumns - Column configuration for PNL table
|
|
8638
9272
|
* @returns Markdown formatted report string
|
|
8639
9273
|
*
|
|
8640
9274
|
* @example
|
|
@@ -8644,7 +9278,7 @@ class WalkerMarkdownService {
|
|
|
8644
9278
|
* console.log(markdown);
|
|
8645
9279
|
* ```
|
|
8646
9280
|
*/
|
|
8647
|
-
this.getReport = async (walkerName, symbol, metric, context) => {
|
|
9281
|
+
this.getReport = async (walkerName, symbol, metric, context, strategyColumns = COLUMN_CONFIG.walker_strategy_columns, pnlColumns = COLUMN_CONFIG.walker_pnl_columns) => {
|
|
8648
9282
|
this.loggerService.log("walkerMarkdownService getReport", {
|
|
8649
9283
|
walkerName,
|
|
8650
9284
|
symbol,
|
|
@@ -8652,7 +9286,7 @@ class WalkerMarkdownService {
|
|
|
8652
9286
|
context,
|
|
8653
9287
|
});
|
|
8654
9288
|
const storage = this.getStorage(walkerName);
|
|
8655
|
-
return storage.getReport(symbol, metric, context);
|
|
9289
|
+
return storage.getReport(symbol, metric, context, strategyColumns, pnlColumns);
|
|
8656
9290
|
};
|
|
8657
9291
|
/**
|
|
8658
9292
|
* Saves walker report to disk.
|
|
@@ -8664,6 +9298,8 @@ class WalkerMarkdownService {
|
|
|
8664
9298
|
* @param metric - Metric being optimized
|
|
8665
9299
|
* @param context - Context with exchangeName and frameName
|
|
8666
9300
|
* @param path - Directory path to save report (default: "./dump/walker")
|
|
9301
|
+
* @param strategyColumns - Column configuration for strategy comparison table
|
|
9302
|
+
* @param pnlColumns - Column configuration for PNL table
|
|
8667
9303
|
*
|
|
8668
9304
|
* @example
|
|
8669
9305
|
* ```typescript
|
|
@@ -8676,7 +9312,7 @@ class WalkerMarkdownService {
|
|
|
8676
9312
|
* await service.dump("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" }, "./custom/path");
|
|
8677
9313
|
* ```
|
|
8678
9314
|
*/
|
|
8679
|
-
this.dump = async (walkerName, symbol, metric, context, path = "./dump/walker") => {
|
|
9315
|
+
this.dump = async (walkerName, symbol, metric, context, path = "./dump/walker", strategyColumns = COLUMN_CONFIG.walker_strategy_columns, pnlColumns = COLUMN_CONFIG.walker_pnl_columns) => {
|
|
8680
9316
|
this.loggerService.log("walkerMarkdownService dump", {
|
|
8681
9317
|
walkerName,
|
|
8682
9318
|
symbol,
|
|
@@ -8685,7 +9321,7 @@ class WalkerMarkdownService {
|
|
|
8685
9321
|
path,
|
|
8686
9322
|
});
|
|
8687
9323
|
const storage = this.getStorage(walkerName);
|
|
8688
|
-
await storage.dump(symbol, metric, context, path);
|
|
9324
|
+
await storage.dump(symbol, metric, context, path, strategyColumns, pnlColumns);
|
|
8689
9325
|
};
|
|
8690
9326
|
/**
|
|
8691
9327
|
* Clears accumulated result data from storage.
|
|
@@ -8732,99 +9368,25 @@ class WalkerMarkdownService {
|
|
|
8732
9368
|
const HEATMAP_METHOD_NAME_GET_DATA = "HeatMarkdownService.getData";
|
|
8733
9369
|
const HEATMAP_METHOD_NAME_GET_REPORT = "HeatMarkdownService.getReport";
|
|
8734
9370
|
const HEATMAP_METHOD_NAME_DUMP = "HeatMarkdownService.dump";
|
|
8735
|
-
const HEATMAP_METHOD_NAME_CLEAR = "HeatMarkdownService.clear";
|
|
8736
|
-
/**
|
|
8737
|
-
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
8738
|
-
*
|
|
8739
|
-
* @param value - Value to check
|
|
8740
|
-
* @returns true if value is unsafe, false otherwise
|
|
8741
|
-
*/
|
|
8742
|
-
function isUnsafe(value) {
|
|
8743
|
-
if (typeof value !== "number") {
|
|
8744
|
-
return true;
|
|
8745
|
-
}
|
|
8746
|
-
if (isNaN(value)) {
|
|
8747
|
-
return true;
|
|
8748
|
-
}
|
|
8749
|
-
if (!isFinite(value)) {
|
|
8750
|
-
return true;
|
|
8751
|
-
}
|
|
8752
|
-
return false;
|
|
8753
|
-
}
|
|
8754
|
-
const columns$2 = [
|
|
8755
|
-
{
|
|
8756
|
-
key: "symbol",
|
|
8757
|
-
label: "Symbol",
|
|
8758
|
-
format: (data) => data.symbol,
|
|
8759
|
-
isVisible: () => true,
|
|
8760
|
-
},
|
|
8761
|
-
{
|
|
8762
|
-
key: "totalPnl",
|
|
8763
|
-
label: "Total PNL",
|
|
8764
|
-
format: (data) => data.totalPnl !== null ? str(data.totalPnl, "%+.2f%%") : "N/A",
|
|
8765
|
-
isVisible: () => true,
|
|
8766
|
-
},
|
|
8767
|
-
{
|
|
8768
|
-
key: "sharpeRatio",
|
|
8769
|
-
label: "Sharpe",
|
|
8770
|
-
format: (data) => data.sharpeRatio !== null ? str(data.sharpeRatio, "%.2f") : "N/A",
|
|
8771
|
-
isVisible: () => true,
|
|
8772
|
-
},
|
|
8773
|
-
{
|
|
8774
|
-
key: "profitFactor",
|
|
8775
|
-
label: "PF",
|
|
8776
|
-
format: (data) => data.profitFactor !== null ? str(data.profitFactor, "%.2f") : "N/A",
|
|
8777
|
-
isVisible: () => true,
|
|
8778
|
-
},
|
|
8779
|
-
{
|
|
8780
|
-
key: "expectancy",
|
|
8781
|
-
label: "Expect",
|
|
8782
|
-
format: (data) => data.expectancy !== null ? str(data.expectancy, "%+.2f%%") : "N/A",
|
|
8783
|
-
isVisible: () => true,
|
|
8784
|
-
},
|
|
8785
|
-
{
|
|
8786
|
-
key: "winRate",
|
|
8787
|
-
label: "WR",
|
|
8788
|
-
format: (data) => data.winRate !== null ? str(data.winRate, "%.1f%%") : "N/A",
|
|
8789
|
-
isVisible: () => true,
|
|
8790
|
-
},
|
|
8791
|
-
{
|
|
8792
|
-
key: "avgWin",
|
|
8793
|
-
label: "Avg Win",
|
|
8794
|
-
format: (data) => data.avgWin !== null ? str(data.avgWin, "%+.2f%%") : "N/A",
|
|
8795
|
-
isVisible: () => true,
|
|
8796
|
-
},
|
|
8797
|
-
{
|
|
8798
|
-
key: "avgLoss",
|
|
8799
|
-
label: "Avg Loss",
|
|
8800
|
-
format: (data) => data.avgLoss !== null ? str(data.avgLoss, "%+.2f%%") : "N/A",
|
|
8801
|
-
isVisible: () => true,
|
|
8802
|
-
},
|
|
8803
|
-
{
|
|
8804
|
-
key: "maxDrawdown",
|
|
8805
|
-
label: "Max DD",
|
|
8806
|
-
format: (data) => data.maxDrawdown !== null ? str(-data.maxDrawdown, "%.2f%%") : "N/A",
|
|
8807
|
-
isVisible: () => true,
|
|
8808
|
-
},
|
|
8809
|
-
{
|
|
8810
|
-
key: "maxWinStreak",
|
|
8811
|
-
label: "W Streak",
|
|
8812
|
-
format: (data) => data.maxWinStreak.toString(),
|
|
8813
|
-
isVisible: () => true,
|
|
8814
|
-
},
|
|
8815
|
-
{
|
|
8816
|
-
key: "maxLossStreak",
|
|
8817
|
-
label: "L Streak",
|
|
8818
|
-
format: (data) => data.maxLossStreak.toString(),
|
|
8819
|
-
isVisible: () => true,
|
|
8820
|
-
},
|
|
8821
|
-
{
|
|
8822
|
-
key: "totalTrades",
|
|
8823
|
-
label: "Trades",
|
|
8824
|
-
format: (data) => data.totalTrades.toString(),
|
|
8825
|
-
isVisible: () => true,
|
|
8826
|
-
},
|
|
8827
|
-
];
|
|
9371
|
+
const HEATMAP_METHOD_NAME_CLEAR = "HeatMarkdownService.clear";
|
|
9372
|
+
/**
|
|
9373
|
+
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
9374
|
+
*
|
|
9375
|
+
* @param value - Value to check
|
|
9376
|
+
* @returns true if value is unsafe, false otherwise
|
|
9377
|
+
*/
|
|
9378
|
+
function isUnsafe(value) {
|
|
9379
|
+
if (typeof value !== "number") {
|
|
9380
|
+
return true;
|
|
9381
|
+
}
|
|
9382
|
+
if (isNaN(value)) {
|
|
9383
|
+
return true;
|
|
9384
|
+
}
|
|
9385
|
+
if (!isFinite(value)) {
|
|
9386
|
+
return true;
|
|
9387
|
+
}
|
|
9388
|
+
return false;
|
|
9389
|
+
}
|
|
8828
9390
|
/** Maximum number of signals to store per symbol in heatmap reports */
|
|
8829
9391
|
const MAX_EVENTS$2 = 250;
|
|
8830
9392
|
/**
|
|
@@ -9059,9 +9621,10 @@ class HeatmapStorage {
|
|
|
9059
9621
|
* Generates markdown report with portfolio heatmap table (View).
|
|
9060
9622
|
*
|
|
9061
9623
|
* @param strategyName - Strategy name for report title
|
|
9624
|
+
* @param columns - Column configuration for formatting the table
|
|
9062
9625
|
* @returns Promise resolving to markdown formatted report string
|
|
9063
9626
|
*/
|
|
9064
|
-
async getReport(strategyName) {
|
|
9627
|
+
async getReport(strategyName, columns = COLUMN_CONFIG.heat_columns) {
|
|
9065
9628
|
const data = await this.getData();
|
|
9066
9629
|
if (data.symbols.length === 0) {
|
|
9067
9630
|
return [
|
|
@@ -9070,10 +9633,15 @@ class HeatmapStorage {
|
|
|
9070
9633
|
"*No data available*"
|
|
9071
9634
|
].join("\n");
|
|
9072
9635
|
}
|
|
9073
|
-
const visibleColumns =
|
|
9636
|
+
const visibleColumns = [];
|
|
9637
|
+
for (const col of columns) {
|
|
9638
|
+
if (await col.isVisible()) {
|
|
9639
|
+
visibleColumns.push(col);
|
|
9640
|
+
}
|
|
9641
|
+
}
|
|
9074
9642
|
const header = visibleColumns.map((col) => col.label);
|
|
9075
9643
|
const separator = visibleColumns.map(() => "---");
|
|
9076
|
-
const rows = data.symbols.map((row) => visibleColumns.map((col) => col.format(row)));
|
|
9644
|
+
const rows = await Promise.all(data.symbols.map(async (row, index) => Promise.all(visibleColumns.map((col) => col.format(row, index)))));
|
|
9077
9645
|
const tableData = [header, separator, ...rows];
|
|
9078
9646
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
9079
9647
|
return [
|
|
@@ -9089,9 +9657,10 @@ class HeatmapStorage {
|
|
|
9089
9657
|
*
|
|
9090
9658
|
* @param strategyName - Strategy name for filename
|
|
9091
9659
|
* @param path - Directory path to save report (default: "./dump/heatmap")
|
|
9660
|
+
* @param columns - Column configuration for formatting the table
|
|
9092
9661
|
*/
|
|
9093
|
-
async dump(strategyName, path = "./dump/heatmap") {
|
|
9094
|
-
const markdown = await this.getReport(strategyName);
|
|
9662
|
+
async dump(strategyName, path = "./dump/heatmap", columns = COLUMN_CONFIG.heat_columns) {
|
|
9663
|
+
const markdown = await this.getReport(strategyName, columns);
|
|
9095
9664
|
try {
|
|
9096
9665
|
const dir = join(process.cwd(), path);
|
|
9097
9666
|
await mkdir(dir, { recursive: true });
|
|
@@ -9188,6 +9757,7 @@ class HeatMarkdownService {
|
|
|
9188
9757
|
* Generates markdown report with portfolio heatmap table for a strategy.
|
|
9189
9758
|
*
|
|
9190
9759
|
* @param strategyName - Strategy name to generate heatmap report for
|
|
9760
|
+
* @param columns - Column configuration for formatting the table
|
|
9191
9761
|
* @returns Promise resolving to markdown formatted report string
|
|
9192
9762
|
*
|
|
9193
9763
|
* @example
|
|
@@ -9207,12 +9777,12 @@ class HeatMarkdownService {
|
|
|
9207
9777
|
* // ...
|
|
9208
9778
|
* ```
|
|
9209
9779
|
*/
|
|
9210
|
-
this.getReport = async (strategyName) => {
|
|
9780
|
+
this.getReport = async (strategyName, columns = COLUMN_CONFIG.heat_columns) => {
|
|
9211
9781
|
this.loggerService.log(HEATMAP_METHOD_NAME_GET_REPORT, {
|
|
9212
9782
|
strategyName,
|
|
9213
9783
|
});
|
|
9214
9784
|
const storage = this.getStorage(strategyName);
|
|
9215
|
-
return storage.getReport(strategyName);
|
|
9785
|
+
return storage.getReport(strategyName, columns);
|
|
9216
9786
|
};
|
|
9217
9787
|
/**
|
|
9218
9788
|
* Saves heatmap report to disk for a strategy.
|
|
@@ -9222,6 +9792,7 @@ class HeatMarkdownService {
|
|
|
9222
9792
|
*
|
|
9223
9793
|
* @param strategyName - Strategy name to save heatmap report for
|
|
9224
9794
|
* @param path - Optional directory path to save report (default: "./dump/heatmap")
|
|
9795
|
+
* @param columns - Column configuration for formatting the table
|
|
9225
9796
|
*
|
|
9226
9797
|
* @example
|
|
9227
9798
|
* ```typescript
|
|
@@ -9234,13 +9805,13 @@ class HeatMarkdownService {
|
|
|
9234
9805
|
* await service.dump("my-strategy", "./reports");
|
|
9235
9806
|
* ```
|
|
9236
9807
|
*/
|
|
9237
|
-
this.dump = async (strategyName, path = "./dump/heatmap") => {
|
|
9808
|
+
this.dump = async (strategyName, path = "./dump/heatmap", columns = COLUMN_CONFIG.heat_columns) => {
|
|
9238
9809
|
this.loggerService.log(HEATMAP_METHOD_NAME_DUMP, {
|
|
9239
9810
|
strategyName,
|
|
9240
9811
|
path,
|
|
9241
9812
|
});
|
|
9242
9813
|
const storage = this.getStorage(strategyName);
|
|
9243
|
-
await storage.dump(strategyName, path);
|
|
9814
|
+
await storage.dump(strategyName, path, columns);
|
|
9244
9815
|
};
|
|
9245
9816
|
/**
|
|
9246
9817
|
* Clears accumulated heatmap data from storage.
|
|
@@ -11631,62 +12202,6 @@ class PartialConnectionService {
|
|
|
11631
12202
|
}
|
|
11632
12203
|
}
|
|
11633
12204
|
|
|
11634
|
-
const columns$1 = [
|
|
11635
|
-
{
|
|
11636
|
-
key: "action",
|
|
11637
|
-
label: "Action",
|
|
11638
|
-
format: (data) => data.action.toUpperCase(),
|
|
11639
|
-
isVisible: () => true,
|
|
11640
|
-
},
|
|
11641
|
-
{
|
|
11642
|
-
key: "symbol",
|
|
11643
|
-
label: "Symbol",
|
|
11644
|
-
format: (data) => data.symbol,
|
|
11645
|
-
isVisible: () => true,
|
|
11646
|
-
},
|
|
11647
|
-
{
|
|
11648
|
-
key: "strategyName",
|
|
11649
|
-
label: "Strategy",
|
|
11650
|
-
format: (data) => data.strategyName,
|
|
11651
|
-
isVisible: () => true,
|
|
11652
|
-
},
|
|
11653
|
-
{
|
|
11654
|
-
key: "signalId",
|
|
11655
|
-
label: "Signal ID",
|
|
11656
|
-
format: (data) => data.signalId,
|
|
11657
|
-
isVisible: () => true,
|
|
11658
|
-
},
|
|
11659
|
-
{
|
|
11660
|
-
key: "position",
|
|
11661
|
-
label: "Position",
|
|
11662
|
-
format: (data) => data.position.toUpperCase(),
|
|
11663
|
-
isVisible: () => true,
|
|
11664
|
-
},
|
|
11665
|
-
{
|
|
11666
|
-
key: "level",
|
|
11667
|
-
label: "Level %",
|
|
11668
|
-
format: (data) => data.action === "profit" ? `+${data.level}%` : `-${data.level}%`,
|
|
11669
|
-
isVisible: () => true,
|
|
11670
|
-
},
|
|
11671
|
-
{
|
|
11672
|
-
key: "currentPrice",
|
|
11673
|
-
label: "Current Price",
|
|
11674
|
-
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
11675
|
-
isVisible: () => true,
|
|
11676
|
-
},
|
|
11677
|
-
{
|
|
11678
|
-
key: "timestamp",
|
|
11679
|
-
label: "Timestamp",
|
|
11680
|
-
format: (data) => new Date(data.timestamp).toISOString(),
|
|
11681
|
-
isVisible: () => true,
|
|
11682
|
-
},
|
|
11683
|
-
{
|
|
11684
|
-
key: "mode",
|
|
11685
|
-
label: "Mode",
|
|
11686
|
-
format: (data) => (data.backtest ? "Backtest" : "Live"),
|
|
11687
|
-
isVisible: () => true,
|
|
11688
|
-
},
|
|
11689
|
-
];
|
|
11690
12205
|
/** Maximum number of events to store in partial reports */
|
|
11691
12206
|
const MAX_EVENTS$1 = 250;
|
|
11692
12207
|
/**
|
|
@@ -11776,9 +12291,10 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
11776
12291
|
*
|
|
11777
12292
|
* @param symbol - Trading pair symbol
|
|
11778
12293
|
* @param strategyName - Strategy name
|
|
12294
|
+
* @param columns - Column configuration for formatting the table
|
|
11779
12295
|
* @returns Markdown formatted report with all events
|
|
11780
12296
|
*/
|
|
11781
|
-
async getReport(symbol, strategyName) {
|
|
12297
|
+
async getReport(symbol, strategyName, columns = COLUMN_CONFIG.partial_columns) {
|
|
11782
12298
|
const stats = await this.getData();
|
|
11783
12299
|
if (stats.totalEvents === 0) {
|
|
11784
12300
|
return [
|
|
@@ -11787,10 +12303,15 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
11787
12303
|
"No partial profit/loss events recorded yet."
|
|
11788
12304
|
].join("\n");
|
|
11789
12305
|
}
|
|
11790
|
-
const visibleColumns =
|
|
12306
|
+
const visibleColumns = [];
|
|
12307
|
+
for (const col of columns) {
|
|
12308
|
+
if (await col.isVisible()) {
|
|
12309
|
+
visibleColumns.push(col);
|
|
12310
|
+
}
|
|
12311
|
+
}
|
|
11791
12312
|
const header = visibleColumns.map((col) => col.label);
|
|
11792
12313
|
const separator = visibleColumns.map(() => "---");
|
|
11793
|
-
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
12314
|
+
const rows = await Promise.all(this._eventList.map(async (event, index) => Promise.all(visibleColumns.map((col) => col.format(event, index)))));
|
|
11794
12315
|
const tableData = [header, separator, ...rows];
|
|
11795
12316
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
11796
12317
|
return [
|
|
@@ -11809,9 +12330,10 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
11809
12330
|
* @param symbol - Trading pair symbol
|
|
11810
12331
|
* @param strategyName - Strategy name
|
|
11811
12332
|
* @param path - Directory path to save report (default: "./dump/partial")
|
|
12333
|
+
* @param columns - Column configuration for formatting the table
|
|
11812
12334
|
*/
|
|
11813
|
-
async dump(symbol, strategyName, path = "./dump/partial") {
|
|
11814
|
-
const markdown = await this.getReport(symbol, strategyName);
|
|
12335
|
+
async dump(symbol, strategyName, path = "./dump/partial", columns = COLUMN_CONFIG.partial_columns) {
|
|
12336
|
+
const markdown = await this.getReport(symbol, strategyName, columns);
|
|
11815
12337
|
try {
|
|
11816
12338
|
const dir = join(process.cwd(), path);
|
|
11817
12339
|
await mkdir(dir, { recursive: true });
|
|
@@ -11922,6 +12444,7 @@ class PartialMarkdownService {
|
|
|
11922
12444
|
*
|
|
11923
12445
|
* @param symbol - Trading pair symbol to generate report for
|
|
11924
12446
|
* @param strategyName - Strategy name to generate report for
|
|
12447
|
+
* @param columns - Column configuration for formatting the table
|
|
11925
12448
|
* @returns Markdown formatted report string with table of all events
|
|
11926
12449
|
*
|
|
11927
12450
|
* @example
|
|
@@ -11931,13 +12454,13 @@ class PartialMarkdownService {
|
|
|
11931
12454
|
* console.log(markdown);
|
|
11932
12455
|
* ```
|
|
11933
12456
|
*/
|
|
11934
|
-
this.getReport = async (symbol, strategyName) => {
|
|
12457
|
+
this.getReport = async (symbol, strategyName, columns = COLUMN_CONFIG.partial_columns) => {
|
|
11935
12458
|
this.loggerService.log("partialMarkdownService getReport", {
|
|
11936
12459
|
symbol,
|
|
11937
12460
|
strategyName,
|
|
11938
12461
|
});
|
|
11939
12462
|
const storage = this.getStorage(symbol, strategyName);
|
|
11940
|
-
return storage.getReport(symbol, strategyName);
|
|
12463
|
+
return storage.getReport(symbol, strategyName, columns);
|
|
11941
12464
|
};
|
|
11942
12465
|
/**
|
|
11943
12466
|
* Saves symbol-strategy report to disk.
|
|
@@ -11947,6 +12470,7 @@ class PartialMarkdownService {
|
|
|
11947
12470
|
* @param symbol - Trading pair symbol to save report for
|
|
11948
12471
|
* @param strategyName - Strategy name to save report for
|
|
11949
12472
|
* @param path - Directory path to save report (default: "./dump/partial")
|
|
12473
|
+
* @param columns - Column configuration for formatting the table
|
|
11950
12474
|
*
|
|
11951
12475
|
* @example
|
|
11952
12476
|
* ```typescript
|
|
@@ -11959,14 +12483,14 @@ class PartialMarkdownService {
|
|
|
11959
12483
|
* await service.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
11960
12484
|
* ```
|
|
11961
12485
|
*/
|
|
11962
|
-
this.dump = async (symbol, strategyName, path = "./dump/partial") => {
|
|
12486
|
+
this.dump = async (symbol, strategyName, path = "./dump/partial", columns = COLUMN_CONFIG.partial_columns) => {
|
|
11963
12487
|
this.loggerService.log("partialMarkdownService dump", {
|
|
11964
12488
|
symbol,
|
|
11965
12489
|
strategyName,
|
|
11966
12490
|
path,
|
|
11967
12491
|
});
|
|
11968
12492
|
const storage = this.getStorage(symbol, strategyName);
|
|
11969
|
-
await storage.dump(symbol, strategyName, path);
|
|
12493
|
+
await storage.dump(symbol, strategyName, path, columns);
|
|
11970
12494
|
};
|
|
11971
12495
|
/**
|
|
11972
12496
|
* Clears accumulated event data from storage.
|
|
@@ -12426,92 +12950,6 @@ class ConfigValidationService {
|
|
|
12426
12950
|
}
|
|
12427
12951
|
}
|
|
12428
12952
|
|
|
12429
|
-
const columns = [
|
|
12430
|
-
{
|
|
12431
|
-
key: "symbol",
|
|
12432
|
-
label: "Symbol",
|
|
12433
|
-
format: (data) => data.symbol,
|
|
12434
|
-
isVisible: () => true,
|
|
12435
|
-
},
|
|
12436
|
-
{
|
|
12437
|
-
key: "strategyName",
|
|
12438
|
-
label: "Strategy",
|
|
12439
|
-
format: (data) => data.strategyName,
|
|
12440
|
-
isVisible: () => true,
|
|
12441
|
-
},
|
|
12442
|
-
{
|
|
12443
|
-
key: "signalId",
|
|
12444
|
-
label: "Signal ID",
|
|
12445
|
-
format: (data) => data.pendingSignal.id || "N/A",
|
|
12446
|
-
isVisible: () => true,
|
|
12447
|
-
},
|
|
12448
|
-
{
|
|
12449
|
-
key: "position",
|
|
12450
|
-
label: "Position",
|
|
12451
|
-
format: (data) => data.pendingSignal.position.toUpperCase(),
|
|
12452
|
-
isVisible: () => true,
|
|
12453
|
-
},
|
|
12454
|
-
{
|
|
12455
|
-
key: "note",
|
|
12456
|
-
label: "Note",
|
|
12457
|
-
format: (data) => toPlainString(data.pendingSignal.note ?? "N/A"),
|
|
12458
|
-
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
12459
|
-
},
|
|
12460
|
-
{
|
|
12461
|
-
key: "exchangeName",
|
|
12462
|
-
label: "Exchange",
|
|
12463
|
-
format: (data) => data.exchangeName,
|
|
12464
|
-
isVisible: () => true,
|
|
12465
|
-
},
|
|
12466
|
-
{
|
|
12467
|
-
key: "openPrice",
|
|
12468
|
-
label: "Open Price",
|
|
12469
|
-
format: (data) => data.pendingSignal.priceOpen !== undefined
|
|
12470
|
-
? `${data.pendingSignal.priceOpen.toFixed(8)} USD`
|
|
12471
|
-
: "N/A",
|
|
12472
|
-
isVisible: () => true,
|
|
12473
|
-
},
|
|
12474
|
-
{
|
|
12475
|
-
key: "takeProfit",
|
|
12476
|
-
label: "Take Profit",
|
|
12477
|
-
format: (data) => data.pendingSignal.priceTakeProfit !== undefined
|
|
12478
|
-
? `${data.pendingSignal.priceTakeProfit.toFixed(8)} USD`
|
|
12479
|
-
: "N/A",
|
|
12480
|
-
isVisible: () => true,
|
|
12481
|
-
},
|
|
12482
|
-
{
|
|
12483
|
-
key: "stopLoss",
|
|
12484
|
-
label: "Stop Loss",
|
|
12485
|
-
format: (data) => data.pendingSignal.priceStopLoss !== undefined
|
|
12486
|
-
? `${data.pendingSignal.priceStopLoss.toFixed(8)} USD`
|
|
12487
|
-
: "N/A",
|
|
12488
|
-
isVisible: () => true,
|
|
12489
|
-
},
|
|
12490
|
-
{
|
|
12491
|
-
key: "currentPrice",
|
|
12492
|
-
label: "Current Price",
|
|
12493
|
-
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
12494
|
-
isVisible: () => true,
|
|
12495
|
-
},
|
|
12496
|
-
{
|
|
12497
|
-
key: "activePositionCount",
|
|
12498
|
-
label: "Active Positions",
|
|
12499
|
-
format: (data) => data.activePositionCount.toString(),
|
|
12500
|
-
isVisible: () => true,
|
|
12501
|
-
},
|
|
12502
|
-
{
|
|
12503
|
-
key: "comment",
|
|
12504
|
-
label: "Reason",
|
|
12505
|
-
format: (data) => data.comment,
|
|
12506
|
-
isVisible: () => true,
|
|
12507
|
-
},
|
|
12508
|
-
{
|
|
12509
|
-
key: "timestamp",
|
|
12510
|
-
label: "Timestamp",
|
|
12511
|
-
format: (data) => new Date(data.timestamp).toISOString(),
|
|
12512
|
-
isVisible: () => true,
|
|
12513
|
-
},
|
|
12514
|
-
];
|
|
12515
12953
|
/** Maximum number of events to store in risk reports */
|
|
12516
12954
|
const MAX_EVENTS = 250;
|
|
12517
12955
|
/**
|
|
@@ -12567,9 +13005,10 @@ class ReportStorage {
|
|
|
12567
13005
|
*
|
|
12568
13006
|
* @param symbol - Trading pair symbol
|
|
12569
13007
|
* @param strategyName - Strategy name
|
|
13008
|
+
* @param columns - Column configuration for formatting the table
|
|
12570
13009
|
* @returns Markdown formatted report with all events
|
|
12571
13010
|
*/
|
|
12572
|
-
async getReport(symbol, strategyName) {
|
|
13011
|
+
async getReport(symbol, strategyName, columns = COLUMN_CONFIG.risk_columns) {
|
|
12573
13012
|
const stats = await this.getData();
|
|
12574
13013
|
if (stats.totalRejections === 0) {
|
|
12575
13014
|
return [
|
|
@@ -12578,10 +13017,15 @@ class ReportStorage {
|
|
|
12578
13017
|
"No risk rejections recorded yet.",
|
|
12579
13018
|
].join("\n");
|
|
12580
13019
|
}
|
|
12581
|
-
const visibleColumns =
|
|
13020
|
+
const visibleColumns = [];
|
|
13021
|
+
for (const col of columns) {
|
|
13022
|
+
if (await col.isVisible()) {
|
|
13023
|
+
visibleColumns.push(col);
|
|
13024
|
+
}
|
|
13025
|
+
}
|
|
12582
13026
|
const header = visibleColumns.map((col) => col.label);
|
|
12583
13027
|
const separator = visibleColumns.map(() => "---");
|
|
12584
|
-
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
13028
|
+
const rows = await Promise.all(this._eventList.map(async (event, index) => Promise.all(visibleColumns.map((col) => col.format(event, index)))));
|
|
12585
13029
|
const tableData = [header, separator, ...rows];
|
|
12586
13030
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
12587
13031
|
return [
|
|
@@ -12604,9 +13048,10 @@ class ReportStorage {
|
|
|
12604
13048
|
* @param symbol - Trading pair symbol
|
|
12605
13049
|
* @param strategyName - Strategy name
|
|
12606
13050
|
* @param path - Directory path to save report (default: "./dump/risk")
|
|
13051
|
+
* @param columns - Column configuration for formatting the table
|
|
12607
13052
|
*/
|
|
12608
|
-
async dump(symbol, strategyName, path = "./dump/risk") {
|
|
12609
|
-
const markdown = await this.getReport(symbol, strategyName);
|
|
13053
|
+
async dump(symbol, strategyName, path = "./dump/risk", columns = COLUMN_CONFIG.risk_columns) {
|
|
13054
|
+
const markdown = await this.getReport(symbol, strategyName, columns);
|
|
12610
13055
|
try {
|
|
12611
13056
|
const dir = join(process.cwd(), path);
|
|
12612
13057
|
await mkdir(dir, { recursive: true });
|
|
@@ -12698,6 +13143,7 @@ class RiskMarkdownService {
|
|
|
12698
13143
|
*
|
|
12699
13144
|
* @param symbol - Trading pair symbol to generate report for
|
|
12700
13145
|
* @param strategyName - Strategy name to generate report for
|
|
13146
|
+
* @param columns - Column configuration for formatting the table
|
|
12701
13147
|
* @returns Markdown formatted report string with table of all events
|
|
12702
13148
|
*
|
|
12703
13149
|
* @example
|
|
@@ -12707,13 +13153,13 @@ class RiskMarkdownService {
|
|
|
12707
13153
|
* console.log(markdown);
|
|
12708
13154
|
* ```
|
|
12709
13155
|
*/
|
|
12710
|
-
this.getReport = async (symbol, strategyName) => {
|
|
13156
|
+
this.getReport = async (symbol, strategyName, columns = COLUMN_CONFIG.risk_columns) => {
|
|
12711
13157
|
this.loggerService.log("riskMarkdownService getReport", {
|
|
12712
13158
|
symbol,
|
|
12713
13159
|
strategyName,
|
|
12714
13160
|
});
|
|
12715
13161
|
const storage = this.getStorage(symbol, strategyName);
|
|
12716
|
-
return storage.getReport(symbol, strategyName);
|
|
13162
|
+
return storage.getReport(symbol, strategyName, columns);
|
|
12717
13163
|
};
|
|
12718
13164
|
/**
|
|
12719
13165
|
* Saves symbol-strategy report to disk.
|
|
@@ -12723,6 +13169,7 @@ class RiskMarkdownService {
|
|
|
12723
13169
|
* @param symbol - Trading pair symbol to save report for
|
|
12724
13170
|
* @param strategyName - Strategy name to save report for
|
|
12725
13171
|
* @param path - Directory path to save report (default: "./dump/risk")
|
|
13172
|
+
* @param columns - Column configuration for formatting the table
|
|
12726
13173
|
*
|
|
12727
13174
|
* @example
|
|
12728
13175
|
* ```typescript
|
|
@@ -12735,14 +13182,14 @@ class RiskMarkdownService {
|
|
|
12735
13182
|
* await service.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
12736
13183
|
* ```
|
|
12737
13184
|
*/
|
|
12738
|
-
this.dump = async (symbol, strategyName, path = "./dump/risk") => {
|
|
13185
|
+
this.dump = async (symbol, strategyName, path = "./dump/risk", columns = COLUMN_CONFIG.risk_columns) => {
|
|
12739
13186
|
this.loggerService.log("riskMarkdownService dump", {
|
|
12740
13187
|
symbol,
|
|
12741
13188
|
strategyName,
|
|
12742
13189
|
path,
|
|
12743
13190
|
});
|
|
12744
13191
|
const storage = this.getStorage(symbol, strategyName);
|
|
12745
|
-
await storage.dump(symbol, strategyName, path);
|
|
13192
|
+
await storage.dump(symbol, strategyName, path, columns);
|
|
12746
13193
|
};
|
|
12747
13194
|
/**
|
|
12748
13195
|
* Clears accumulated event data from storage.
|
|
@@ -12792,6 +13239,118 @@ class RiskMarkdownService {
|
|
|
12792
13239
|
}
|
|
12793
13240
|
}
|
|
12794
13241
|
|
|
13242
|
+
/**
|
|
13243
|
+
* Service for validating column configurations to ensure consistency with ColumnModel interface
|
|
13244
|
+
* and prevent invalid column definitions.
|
|
13245
|
+
*
|
|
13246
|
+
* Performs comprehensive validation on all column definitions in COLUMN_CONFIG:
|
|
13247
|
+
* - **Required fields**: All columns must have key, label, format, and isVisible properties
|
|
13248
|
+
* - **Unique keys**: All key values must be unique within each column collection
|
|
13249
|
+
* - **Function validation**: format and isVisible must be callable functions
|
|
13250
|
+
* - **Data types**: key and label must be non-empty strings
|
|
13251
|
+
*
|
|
13252
|
+
* @throws {Error} If any validation fails, throws with detailed breakdown of all errors
|
|
13253
|
+
*
|
|
13254
|
+
* @example
|
|
13255
|
+
* ```typescript
|
|
13256
|
+
* const validator = new ColumnValidationService();
|
|
13257
|
+
* validator.validate(); // Throws if column configuration is invalid
|
|
13258
|
+
* ```
|
|
13259
|
+
*
|
|
13260
|
+
* @example Validation failure output:
|
|
13261
|
+
* ```
|
|
13262
|
+
* Column configuration validation failed:
|
|
13263
|
+
* 1. backtest_columns[0]: Missing required field "format"
|
|
13264
|
+
* 2. heat_columns: Duplicate key "symbol" at indexes 1, 5
|
|
13265
|
+
* 3. live_columns[3].isVisible must be a function, got "boolean"
|
|
13266
|
+
* ```
|
|
13267
|
+
*/
|
|
13268
|
+
class ColumnValidationService {
|
|
13269
|
+
constructor() {
|
|
13270
|
+
/**
|
|
13271
|
+
* @private
|
|
13272
|
+
* @readonly
|
|
13273
|
+
* Injected logger service instance
|
|
13274
|
+
*/
|
|
13275
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
13276
|
+
/**
|
|
13277
|
+
* Validates all column configurations in COLUMN_CONFIG for structural correctness.
|
|
13278
|
+
*
|
|
13279
|
+
* Checks:
|
|
13280
|
+
* 1. All required fields (key, label, format, isVisible) are present in each column
|
|
13281
|
+
* 2. key and label are non-empty strings
|
|
13282
|
+
* 3. format and isVisible are functions (not other types)
|
|
13283
|
+
* 4. All keys are unique within each column collection
|
|
13284
|
+
*
|
|
13285
|
+
* @throws Error if configuration is invalid
|
|
13286
|
+
*/
|
|
13287
|
+
this.validate = () => {
|
|
13288
|
+
this.loggerService.log("columnValidationService validate");
|
|
13289
|
+
const errors = [];
|
|
13290
|
+
// Iterate through all column collections in COLUMN_CONFIG
|
|
13291
|
+
for (const [configKey, columns] of Object.entries(COLUMN_CONFIG)) {
|
|
13292
|
+
if (!Array.isArray(columns)) {
|
|
13293
|
+
errors.push(`${configKey} is not an array, got ${typeof columns}`);
|
|
13294
|
+
continue;
|
|
13295
|
+
}
|
|
13296
|
+
// Track keys for uniqueness check
|
|
13297
|
+
const keyMap = new Map();
|
|
13298
|
+
// Validate each column in the collection
|
|
13299
|
+
columns.forEach((column, index) => {
|
|
13300
|
+
if (!column || typeof column !== "object") {
|
|
13301
|
+
errors.push(`${configKey}[${index}]: Column must be an object, got ${typeof column}`);
|
|
13302
|
+
return;
|
|
13303
|
+
}
|
|
13304
|
+
// Check for all required fields
|
|
13305
|
+
const requiredFields = ["key", "label", "format", "isVisible"];
|
|
13306
|
+
for (const field of requiredFields) {
|
|
13307
|
+
if (!(field in column)) {
|
|
13308
|
+
errors.push(`${configKey}[${index}]: Missing required field "${field}"`);
|
|
13309
|
+
}
|
|
13310
|
+
}
|
|
13311
|
+
// Validate key and label are non-empty strings
|
|
13312
|
+
if (typeof column.key !== "string" || column.key.trim() === "") {
|
|
13313
|
+
errors.push(`${configKey}[${index}].key must be a non-empty string, got ${typeof column.key === "string" ? `"${column.key}"` : typeof column.key}`);
|
|
13314
|
+
}
|
|
13315
|
+
else {
|
|
13316
|
+
// Track key for uniqueness check
|
|
13317
|
+
if (!keyMap.has(column.key)) {
|
|
13318
|
+
keyMap.set(column.key, []);
|
|
13319
|
+
}
|
|
13320
|
+
keyMap.get(column.key).push(index);
|
|
13321
|
+
}
|
|
13322
|
+
if (typeof column.label !== "string" || column.label.trim() === "") {
|
|
13323
|
+
errors.push(`${configKey}[${index}].label must be a non-empty string, got ${typeof column.label === "string" ? `"${column.label}"` : typeof column.label}`);
|
|
13324
|
+
}
|
|
13325
|
+
// Validate format is a function
|
|
13326
|
+
if (typeof column.format !== "function") {
|
|
13327
|
+
errors.push(`${configKey}[${index}].format must be a function, got "${typeof column.format}"`);
|
|
13328
|
+
}
|
|
13329
|
+
// Validate isVisible is a function
|
|
13330
|
+
if (typeof column.isVisible !== "function") {
|
|
13331
|
+
errors.push(`${configKey}[${index}].isVisible must be a function, got "${typeof column.isVisible}"`);
|
|
13332
|
+
}
|
|
13333
|
+
});
|
|
13334
|
+
// Check for duplicate keys
|
|
13335
|
+
for (const [key, indexes] of keyMap.entries()) {
|
|
13336
|
+
if (indexes.length > 1) {
|
|
13337
|
+
errors.push(`${configKey}: Duplicate key "${key}" at indexes ${indexes.join(", ")}`);
|
|
13338
|
+
}
|
|
13339
|
+
}
|
|
13340
|
+
}
|
|
13341
|
+
// Throw aggregated errors if any
|
|
13342
|
+
if (errors.length > 0) {
|
|
13343
|
+
const errorMessage = `Column configuration validation failed:\n${errors
|
|
13344
|
+
.map((e, i) => ` ${i + 1}. ${e}`)
|
|
13345
|
+
.join("\n")}`;
|
|
13346
|
+
this.loggerService.warn(errorMessage);
|
|
13347
|
+
throw new Error(errorMessage);
|
|
13348
|
+
}
|
|
13349
|
+
this.loggerService.log("columnValidationService validation passed");
|
|
13350
|
+
};
|
|
13351
|
+
}
|
|
13352
|
+
}
|
|
13353
|
+
|
|
12795
13354
|
{
|
|
12796
13355
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
12797
13356
|
}
|
|
@@ -12863,6 +13422,7 @@ class RiskMarkdownService {
|
|
|
12863
13422
|
provide(TYPES.riskValidationService, () => new RiskValidationService());
|
|
12864
13423
|
provide(TYPES.optimizerValidationService, () => new OptimizerValidationService());
|
|
12865
13424
|
provide(TYPES.configValidationService, () => new ConfigValidationService());
|
|
13425
|
+
provide(TYPES.columnValidationService, () => new ColumnValidationService());
|
|
12866
13426
|
}
|
|
12867
13427
|
{
|
|
12868
13428
|
provide(TYPES.optimizerTemplateService, () => new OptimizerTemplateService());
|
|
@@ -12939,6 +13499,7 @@ const validationServices = {
|
|
|
12939
13499
|
riskValidationService: inject(TYPES.riskValidationService),
|
|
12940
13500
|
optimizerValidationService: inject(TYPES.optimizerValidationService),
|
|
12941
13501
|
configValidationService: inject(TYPES.configValidationService),
|
|
13502
|
+
columnValidationService: inject(TYPES.columnValidationService),
|
|
12942
13503
|
};
|
|
12943
13504
|
const templateServices = {
|
|
12944
13505
|
optimizerTemplateService: inject(TYPES.optimizerTemplateService),
|
|
@@ -13038,6 +13599,77 @@ function getConfig() {
|
|
|
13038
13599
|
function getDefaultConfig() {
|
|
13039
13600
|
return DEFAULT_CONFIG;
|
|
13040
13601
|
}
|
|
13602
|
+
/**
|
|
13603
|
+
* Sets custom column configurations for markdown report generation.
|
|
13604
|
+
*
|
|
13605
|
+
* Allows overriding default column definitions for any report type.
|
|
13606
|
+
* All columns are validated before assignment to ensure structural correctness.
|
|
13607
|
+
*
|
|
13608
|
+
* @param columns - Partial column configuration object to override default column settings
|
|
13609
|
+
* @param _unsafe - Skip column validations - required for testbed
|
|
13610
|
+
*
|
|
13611
|
+
* @example
|
|
13612
|
+
* ```typescript
|
|
13613
|
+
* setColumns({
|
|
13614
|
+
* backtest_columns: [
|
|
13615
|
+
* {
|
|
13616
|
+
* key: "customId",
|
|
13617
|
+
* label: "Custom ID",
|
|
13618
|
+
* format: (data) => data.signal.id,
|
|
13619
|
+
* isVisible: () => true
|
|
13620
|
+
* }
|
|
13621
|
+
* ],
|
|
13622
|
+
* });
|
|
13623
|
+
* ```
|
|
13624
|
+
*
|
|
13625
|
+
* @throws {Error} If column configuration is invalid
|
|
13626
|
+
*/
|
|
13627
|
+
function setColumns(columns, _unsafe) {
|
|
13628
|
+
const prevConfig = Object.assign({}, COLUMN_CONFIG);
|
|
13629
|
+
try {
|
|
13630
|
+
Object.assign(COLUMN_CONFIG, columns);
|
|
13631
|
+
!_unsafe && backtest$1.columnValidationService.validate();
|
|
13632
|
+
}
|
|
13633
|
+
catch (error) {
|
|
13634
|
+
console.warn(`backtest-kit setColumns failed: ${getErrorMessage(error)}`, columns);
|
|
13635
|
+
Object.assign(COLUMN_CONFIG, prevConfig);
|
|
13636
|
+
throw error;
|
|
13637
|
+
}
|
|
13638
|
+
}
|
|
13639
|
+
/**
|
|
13640
|
+
* Retrieves a copy of the current column configuration for markdown report generation.
|
|
13641
|
+
*
|
|
13642
|
+
* Returns a shallow copy of the current COLUMN_CONFIG to prevent accidental mutations.
|
|
13643
|
+
* Use this to inspect the current column definitions without modifying them.
|
|
13644
|
+
*
|
|
13645
|
+
* @returns {ColumnConfig} A copy of the current column configuration object
|
|
13646
|
+
*
|
|
13647
|
+
* @example
|
|
13648
|
+
* ```typescript
|
|
13649
|
+
* const currentColumns = getColumns();
|
|
13650
|
+
* console.log(currentColumns.backtest_columns.length);
|
|
13651
|
+
* ```
|
|
13652
|
+
*/
|
|
13653
|
+
function getColumns() {
|
|
13654
|
+
return Object.assign({}, COLUMN_CONFIG);
|
|
13655
|
+
}
|
|
13656
|
+
/**
|
|
13657
|
+
* Retrieves the default column configuration object for markdown report generation.
|
|
13658
|
+
*
|
|
13659
|
+
* Returns a reference to the default column definitions with all preset values.
|
|
13660
|
+
* Use this to see what column options are available and their default definitions.
|
|
13661
|
+
*
|
|
13662
|
+
* @returns {ColumnConfig} The default column configuration object
|
|
13663
|
+
*
|
|
13664
|
+
* @example
|
|
13665
|
+
* ```typescript
|
|
13666
|
+
* const defaultColumns = getDefaultColumns();
|
|
13667
|
+
* console.log(defaultColumns.backtest_columns);
|
|
13668
|
+
* ```
|
|
13669
|
+
*/
|
|
13670
|
+
function getDefaultColumns() {
|
|
13671
|
+
return DEFAULT_COLUMNS;
|
|
13672
|
+
}
|
|
13041
13673
|
|
|
13042
13674
|
const ADD_STRATEGY_METHOD_NAME = "add.addStrategy";
|
|
13043
13675
|
const ADD_EXCHANGE_METHOD_NAME = "add.addExchange";
|
|
@@ -15023,6 +15655,7 @@ class BacktestInstance {
|
|
|
15023
15655
|
*
|
|
15024
15656
|
* @param symbol - Trading pair symbol
|
|
15025
15657
|
* @param strategyName - Strategy name to generate report for
|
|
15658
|
+
* @param columns - Optional columns configuration for the report
|
|
15026
15659
|
* @returns Promise resolving to markdown formatted report string
|
|
15027
15660
|
*
|
|
15028
15661
|
* @example
|
|
@@ -15032,12 +15665,12 @@ class BacktestInstance {
|
|
|
15032
15665
|
* console.log(markdown);
|
|
15033
15666
|
* ```
|
|
15034
15667
|
*/
|
|
15035
|
-
this.getReport = async (symbol, strategyName) => {
|
|
15668
|
+
this.getReport = async (symbol, strategyName, columns) => {
|
|
15036
15669
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_REPORT, {
|
|
15037
15670
|
symbol,
|
|
15038
15671
|
strategyName,
|
|
15039
15672
|
});
|
|
15040
|
-
return await backtest$1.backtestMarkdownService.getReport(symbol, strategyName);
|
|
15673
|
+
return await backtest$1.backtestMarkdownService.getReport(symbol, strategyName, columns);
|
|
15041
15674
|
};
|
|
15042
15675
|
/**
|
|
15043
15676
|
* Saves strategy report to disk.
|
|
@@ -15045,6 +15678,7 @@ class BacktestInstance {
|
|
|
15045
15678
|
* @param symbol - Trading pair symbol
|
|
15046
15679
|
* @param strategyName - Strategy name to save report for
|
|
15047
15680
|
* @param path - Optional directory path to save report (default: "./dump/backtest")
|
|
15681
|
+
* @param columns - Optional columns configuration for the report
|
|
15048
15682
|
*
|
|
15049
15683
|
* @example
|
|
15050
15684
|
* ```typescript
|
|
@@ -15056,13 +15690,13 @@ class BacktestInstance {
|
|
|
15056
15690
|
* await instance.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
15057
15691
|
* ```
|
|
15058
15692
|
*/
|
|
15059
|
-
this.dump = async (symbol, strategyName, path) => {
|
|
15693
|
+
this.dump = async (symbol, strategyName, path, columns) => {
|
|
15060
15694
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_DUMP, {
|
|
15061
15695
|
symbol,
|
|
15062
15696
|
strategyName,
|
|
15063
15697
|
path,
|
|
15064
15698
|
});
|
|
15065
|
-
await backtest$1.backtestMarkdownService.dump(symbol, strategyName, path);
|
|
15699
|
+
await backtest$1.backtestMarkdownService.dump(symbol, strategyName, path, columns);
|
|
15066
15700
|
};
|
|
15067
15701
|
}
|
|
15068
15702
|
}
|
|
@@ -15201,6 +15835,7 @@ class BacktestUtils {
|
|
|
15201
15835
|
*
|
|
15202
15836
|
* @param symbol - Trading pair symbol
|
|
15203
15837
|
* @param strategyName - Strategy name to generate report for
|
|
15838
|
+
* @param columns - Optional columns configuration for the report
|
|
15204
15839
|
* @returns Promise resolving to markdown formatted report string
|
|
15205
15840
|
*
|
|
15206
15841
|
* @example
|
|
@@ -15209,7 +15844,7 @@ class BacktestUtils {
|
|
|
15209
15844
|
* console.log(markdown);
|
|
15210
15845
|
* ```
|
|
15211
15846
|
*/
|
|
15212
|
-
this.getReport = async (symbol, strategyName) => {
|
|
15847
|
+
this.getReport = async (symbol, strategyName, columns) => {
|
|
15213
15848
|
backtest$1.strategyValidationService.validate(strategyName, BACKTEST_METHOD_NAME_GET_REPORT);
|
|
15214
15849
|
{
|
|
15215
15850
|
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
@@ -15217,7 +15852,7 @@ class BacktestUtils {
|
|
|
15217
15852
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_REPORT));
|
|
15218
15853
|
}
|
|
15219
15854
|
const instance = this._getInstance(symbol, strategyName);
|
|
15220
|
-
return await instance.getReport(symbol, strategyName);
|
|
15855
|
+
return await instance.getReport(symbol, strategyName, columns);
|
|
15221
15856
|
};
|
|
15222
15857
|
/**
|
|
15223
15858
|
* Saves strategy report to disk.
|
|
@@ -15225,6 +15860,7 @@ class BacktestUtils {
|
|
|
15225
15860
|
* @param symbol - Trading pair symbol
|
|
15226
15861
|
* @param strategyName - Strategy name to save report for
|
|
15227
15862
|
* @param path - Optional directory path to save report (default: "./dump/backtest")
|
|
15863
|
+
* @param columns - Optional columns configuration for the report
|
|
15228
15864
|
*
|
|
15229
15865
|
* @example
|
|
15230
15866
|
* ```typescript
|
|
@@ -15235,7 +15871,7 @@ class BacktestUtils {
|
|
|
15235
15871
|
* await Backtest.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
15236
15872
|
* ```
|
|
15237
15873
|
*/
|
|
15238
|
-
this.dump = async (symbol, strategyName, path) => {
|
|
15874
|
+
this.dump = async (symbol, strategyName, path, columns) => {
|
|
15239
15875
|
backtest$1.strategyValidationService.validate(strategyName, BACKTEST_METHOD_NAME_DUMP);
|
|
15240
15876
|
{
|
|
15241
15877
|
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
@@ -15243,7 +15879,7 @@ class BacktestUtils {
|
|
|
15243
15879
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_DUMP));
|
|
15244
15880
|
}
|
|
15245
15881
|
const instance = this._getInstance(symbol, strategyName);
|
|
15246
|
-
return await instance.dump(symbol, strategyName, path);
|
|
15882
|
+
return await instance.dump(symbol, strategyName, path, columns);
|
|
15247
15883
|
};
|
|
15248
15884
|
/**
|
|
15249
15885
|
* Lists all active backtest instances with their current status.
|
|
@@ -15523,6 +16159,7 @@ class LiveInstance {
|
|
|
15523
16159
|
*
|
|
15524
16160
|
* @param symbol - Trading pair symbol
|
|
15525
16161
|
* @param strategyName - Strategy name to generate report for
|
|
16162
|
+
* @param columns - Optional columns configuration for the report
|
|
15526
16163
|
* @returns Promise resolving to markdown formatted report string
|
|
15527
16164
|
*
|
|
15528
16165
|
* @example
|
|
@@ -15532,12 +16169,12 @@ class LiveInstance {
|
|
|
15532
16169
|
* console.log(markdown);
|
|
15533
16170
|
* ```
|
|
15534
16171
|
*/
|
|
15535
|
-
this.getReport = async (symbol, strategyName) => {
|
|
16172
|
+
this.getReport = async (symbol, strategyName, columns) => {
|
|
15536
16173
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_REPORT, {
|
|
15537
16174
|
symbol,
|
|
15538
16175
|
strategyName,
|
|
15539
16176
|
});
|
|
15540
|
-
return await backtest$1.liveMarkdownService.getReport(symbol, strategyName);
|
|
16177
|
+
return await backtest$1.liveMarkdownService.getReport(symbol, strategyName, columns);
|
|
15541
16178
|
};
|
|
15542
16179
|
/**
|
|
15543
16180
|
* Saves strategy report to disk.
|
|
@@ -15545,6 +16182,7 @@ class LiveInstance {
|
|
|
15545
16182
|
* @param symbol - Trading pair symbol
|
|
15546
16183
|
* @param strategyName - Strategy name to save report for
|
|
15547
16184
|
* @param path - Optional directory path to save report (default: "./dump/live")
|
|
16185
|
+
* @param columns - Optional columns configuration for the report
|
|
15548
16186
|
*
|
|
15549
16187
|
* @example
|
|
15550
16188
|
* ```typescript
|
|
@@ -15556,13 +16194,13 @@ class LiveInstance {
|
|
|
15556
16194
|
* await instance.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
15557
16195
|
* ```
|
|
15558
16196
|
*/
|
|
15559
|
-
this.dump = async (symbol, strategyName, path) => {
|
|
16197
|
+
this.dump = async (symbol, strategyName, path, columns) => {
|
|
15560
16198
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_DUMP, {
|
|
15561
16199
|
symbol,
|
|
15562
16200
|
strategyName,
|
|
15563
16201
|
path,
|
|
15564
16202
|
});
|
|
15565
|
-
await backtest$1.liveMarkdownService.dump(symbol, strategyName, path);
|
|
16203
|
+
await backtest$1.liveMarkdownService.dump(symbol, strategyName, path, columns);
|
|
15566
16204
|
};
|
|
15567
16205
|
}
|
|
15568
16206
|
}
|
|
@@ -15712,6 +16350,7 @@ class LiveUtils {
|
|
|
15712
16350
|
*
|
|
15713
16351
|
* @param symbol - Trading pair symbol
|
|
15714
16352
|
* @param strategyName - Strategy name to generate report for
|
|
16353
|
+
* @param columns - Optional columns configuration for the report
|
|
15715
16354
|
* @returns Promise resolving to markdown formatted report string
|
|
15716
16355
|
*
|
|
15717
16356
|
* @example
|
|
@@ -15720,7 +16359,7 @@ class LiveUtils {
|
|
|
15720
16359
|
* console.log(markdown);
|
|
15721
16360
|
* ```
|
|
15722
16361
|
*/
|
|
15723
|
-
this.getReport = async (symbol, strategyName) => {
|
|
16362
|
+
this.getReport = async (symbol, strategyName, columns) => {
|
|
15724
16363
|
backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_GET_REPORT);
|
|
15725
16364
|
{
|
|
15726
16365
|
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
@@ -15728,7 +16367,7 @@ class LiveUtils {
|
|
|
15728
16367
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_REPORT));
|
|
15729
16368
|
}
|
|
15730
16369
|
const instance = this._getInstance(symbol, strategyName);
|
|
15731
|
-
return await instance.getReport(symbol, strategyName);
|
|
16370
|
+
return await instance.getReport(symbol, strategyName, columns);
|
|
15732
16371
|
};
|
|
15733
16372
|
/**
|
|
15734
16373
|
* Saves strategy report to disk.
|
|
@@ -15736,6 +16375,7 @@ class LiveUtils {
|
|
|
15736
16375
|
* @param symbol - Trading pair symbol
|
|
15737
16376
|
* @param strategyName - Strategy name to save report for
|
|
15738
16377
|
* @param path - Optional directory path to save report (default: "./dump/live")
|
|
16378
|
+
* @param columns - Optional columns configuration for the report
|
|
15739
16379
|
*
|
|
15740
16380
|
* @example
|
|
15741
16381
|
* ```typescript
|
|
@@ -15746,7 +16386,7 @@ class LiveUtils {
|
|
|
15746
16386
|
* await Live.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
15747
16387
|
* ```
|
|
15748
16388
|
*/
|
|
15749
|
-
this.dump = async (symbol, strategyName, path) => {
|
|
16389
|
+
this.dump = async (symbol, strategyName, path, columns) => {
|
|
15750
16390
|
backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_DUMP);
|
|
15751
16391
|
{
|
|
15752
16392
|
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
@@ -15754,7 +16394,7 @@ class LiveUtils {
|
|
|
15754
16394
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_DUMP));
|
|
15755
16395
|
}
|
|
15756
16396
|
const instance = this._getInstance(symbol, strategyName);
|
|
15757
|
-
return await instance.dump(symbol, strategyName, path);
|
|
16397
|
+
return await instance.dump(symbol, strategyName, path, columns);
|
|
15758
16398
|
};
|
|
15759
16399
|
/**
|
|
15760
16400
|
* Lists all active live trading instances with their current status.
|
|
@@ -15853,6 +16493,7 @@ class ScheduleUtils {
|
|
|
15853
16493
|
*
|
|
15854
16494
|
* @param symbol - Trading pair symbol
|
|
15855
16495
|
* @param strategyName - Strategy name to generate report for
|
|
16496
|
+
* @param columns - Optional columns configuration for the report
|
|
15856
16497
|
* @returns Promise resolving to markdown formatted report string
|
|
15857
16498
|
*
|
|
15858
16499
|
* @example
|
|
@@ -15861,7 +16502,7 @@ class ScheduleUtils {
|
|
|
15861
16502
|
* console.log(markdown);
|
|
15862
16503
|
* ```
|
|
15863
16504
|
*/
|
|
15864
|
-
this.getReport = async (symbol, strategyName) => {
|
|
16505
|
+
this.getReport = async (symbol, strategyName, columns) => {
|
|
15865
16506
|
backtest$1.loggerService.info(SCHEDULE_METHOD_NAME_GET_REPORT, {
|
|
15866
16507
|
symbol,
|
|
15867
16508
|
strategyName,
|
|
@@ -15872,7 +16513,7 @@ class ScheduleUtils {
|
|
|
15872
16513
|
riskName && backtest$1.riskValidationService.validate(riskName, SCHEDULE_METHOD_NAME_GET_REPORT);
|
|
15873
16514
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, SCHEDULE_METHOD_NAME_GET_REPORT));
|
|
15874
16515
|
}
|
|
15875
|
-
return await backtest$1.scheduleMarkdownService.getReport(symbol, strategyName);
|
|
16516
|
+
return await backtest$1.scheduleMarkdownService.getReport(symbol, strategyName, columns);
|
|
15876
16517
|
};
|
|
15877
16518
|
/**
|
|
15878
16519
|
* Saves strategy report to disk.
|
|
@@ -15880,6 +16521,7 @@ class ScheduleUtils {
|
|
|
15880
16521
|
* @param symbol - Trading pair symbol
|
|
15881
16522
|
* @param strategyName - Strategy name to save report for
|
|
15882
16523
|
* @param path - Optional directory path to save report (default: "./dump/schedule")
|
|
16524
|
+
* @param columns - Optional columns configuration for the report
|
|
15883
16525
|
*
|
|
15884
16526
|
* @example
|
|
15885
16527
|
* ```typescript
|
|
@@ -15890,7 +16532,7 @@ class ScheduleUtils {
|
|
|
15890
16532
|
* await Schedule.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
15891
16533
|
* ```
|
|
15892
16534
|
*/
|
|
15893
|
-
this.dump = async (symbol, strategyName, path) => {
|
|
16535
|
+
this.dump = async (symbol, strategyName, path, columns) => {
|
|
15894
16536
|
backtest$1.loggerService.info(SCHEDULE_METHOD_NAME_DUMP, {
|
|
15895
16537
|
symbol,
|
|
15896
16538
|
strategyName,
|
|
@@ -15902,7 +16544,7 @@ class ScheduleUtils {
|
|
|
15902
16544
|
riskName && backtest$1.riskValidationService.validate(riskName, SCHEDULE_METHOD_NAME_DUMP);
|
|
15903
16545
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, SCHEDULE_METHOD_NAME_DUMP));
|
|
15904
16546
|
}
|
|
15905
|
-
await backtest$1.scheduleMarkdownService.dump(symbol, strategyName, path);
|
|
16547
|
+
await backtest$1.scheduleMarkdownService.dump(symbol, strategyName, path, columns);
|
|
15906
16548
|
};
|
|
15907
16549
|
}
|
|
15908
16550
|
}
|
|
@@ -16002,6 +16644,7 @@ class Performance {
|
|
|
16002
16644
|
*
|
|
16003
16645
|
* @param symbol - Trading pair symbol
|
|
16004
16646
|
* @param strategyName - Strategy name to generate report for
|
|
16647
|
+
* @param columns - Optional columns configuration for the report
|
|
16005
16648
|
* @returns Markdown formatted report string
|
|
16006
16649
|
*
|
|
16007
16650
|
* @example
|
|
@@ -16014,14 +16657,14 @@ class Performance {
|
|
|
16014
16657
|
* await fs.writeFile("performance-report.md", markdown);
|
|
16015
16658
|
* ```
|
|
16016
16659
|
*/
|
|
16017
|
-
static async getReport(symbol, strategyName) {
|
|
16660
|
+
static async getReport(symbol, strategyName, columns) {
|
|
16018
16661
|
backtest$1.strategyValidationService.validate(strategyName, PERFORMANCE_METHOD_NAME_GET_REPORT);
|
|
16019
16662
|
{
|
|
16020
16663
|
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
16021
16664
|
riskName && backtest$1.riskValidationService.validate(riskName, PERFORMANCE_METHOD_NAME_GET_REPORT);
|
|
16022
16665
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, PERFORMANCE_METHOD_NAME_GET_REPORT));
|
|
16023
16666
|
}
|
|
16024
|
-
return backtest$1.performanceMarkdownService.getReport(symbol, strategyName);
|
|
16667
|
+
return backtest$1.performanceMarkdownService.getReport(symbol, strategyName, columns);
|
|
16025
16668
|
}
|
|
16026
16669
|
/**
|
|
16027
16670
|
* Saves performance report to disk.
|
|
@@ -16032,6 +16675,7 @@ class Performance {
|
|
|
16032
16675
|
* @param symbol - Trading pair symbol
|
|
16033
16676
|
* @param strategyName - Strategy name to save report for
|
|
16034
16677
|
* @param path - Optional custom directory path
|
|
16678
|
+
* @param columns - Optional columns configuration for the report
|
|
16035
16679
|
*
|
|
16036
16680
|
* @example
|
|
16037
16681
|
* ```typescript
|
|
@@ -16042,14 +16686,14 @@ class Performance {
|
|
|
16042
16686
|
* await Performance.dump("BTCUSDT", "my-strategy", "./reports/perf");
|
|
16043
16687
|
* ```
|
|
16044
16688
|
*/
|
|
16045
|
-
static async dump(symbol, strategyName, path = "./dump/performance") {
|
|
16689
|
+
static async dump(symbol, strategyName, path = "./dump/performance", columns) {
|
|
16046
16690
|
backtest$1.strategyValidationService.validate(strategyName, PERFORMANCE_METHOD_NAME_DUMP);
|
|
16047
16691
|
{
|
|
16048
16692
|
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
16049
16693
|
riskName && backtest$1.riskValidationService.validate(riskName, PERFORMANCE_METHOD_NAME_DUMP);
|
|
16050
16694
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, PERFORMANCE_METHOD_NAME_DUMP));
|
|
16051
16695
|
}
|
|
16052
|
-
return backtest$1.performanceMarkdownService.dump(symbol, strategyName, path);
|
|
16696
|
+
return backtest$1.performanceMarkdownService.dump(symbol, strategyName, path, columns);
|
|
16053
16697
|
}
|
|
16054
16698
|
}
|
|
16055
16699
|
|
|
@@ -16311,6 +16955,8 @@ class WalkerInstance {
|
|
|
16311
16955
|
*
|
|
16312
16956
|
* @param symbol - Trading symbol
|
|
16313
16957
|
* @param walkerName - Walker name to generate report for
|
|
16958
|
+
* @param strategyColumns - Optional strategy columns configuration
|
|
16959
|
+
* @param pnlColumns - Optional PNL columns configuration
|
|
16314
16960
|
* @returns Promise resolving to markdown formatted report string
|
|
16315
16961
|
*
|
|
16316
16962
|
* @example
|
|
@@ -16320,7 +16966,7 @@ class WalkerInstance {
|
|
|
16320
16966
|
* console.log(markdown);
|
|
16321
16967
|
* ```
|
|
16322
16968
|
*/
|
|
16323
|
-
this.getReport = async (symbol, walkerName) => {
|
|
16969
|
+
this.getReport = async (symbol, walkerName, strategyColumns, pnlColumns) => {
|
|
16324
16970
|
backtest$1.loggerService.info(WALKER_METHOD_NAME_GET_REPORT, {
|
|
16325
16971
|
symbol,
|
|
16326
16972
|
walkerName,
|
|
@@ -16329,7 +16975,7 @@ class WalkerInstance {
|
|
|
16329
16975
|
return await backtest$1.walkerMarkdownService.getReport(walkerName, symbol, walkerSchema.metric || "sharpeRatio", {
|
|
16330
16976
|
exchangeName: walkerSchema.exchangeName,
|
|
16331
16977
|
frameName: walkerSchema.frameName,
|
|
16332
|
-
});
|
|
16978
|
+
}, strategyColumns, pnlColumns);
|
|
16333
16979
|
};
|
|
16334
16980
|
/**
|
|
16335
16981
|
* Saves walker report to disk.
|
|
@@ -16337,6 +16983,8 @@ class WalkerInstance {
|
|
|
16337
16983
|
* @param symbol - Trading symbol
|
|
16338
16984
|
* @param walkerName - Walker name to save report for
|
|
16339
16985
|
* @param path - Optional directory path to save report (default: "./dump/walker")
|
|
16986
|
+
* @param strategyColumns - Optional strategy columns configuration
|
|
16987
|
+
* @param pnlColumns - Optional PNL columns configuration
|
|
16340
16988
|
*
|
|
16341
16989
|
* @example
|
|
16342
16990
|
* ```typescript
|
|
@@ -16348,7 +16996,7 @@ class WalkerInstance {
|
|
|
16348
16996
|
* await instance.dump("BTCUSDT", "my-walker", "./custom/path");
|
|
16349
16997
|
* ```
|
|
16350
16998
|
*/
|
|
16351
|
-
this.dump = async (symbol, walkerName, path) => {
|
|
16999
|
+
this.dump = async (symbol, walkerName, path, strategyColumns, pnlColumns) => {
|
|
16352
17000
|
backtest$1.loggerService.info(WALKER_METHOD_NAME_DUMP, {
|
|
16353
17001
|
symbol,
|
|
16354
17002
|
walkerName,
|
|
@@ -16358,7 +17006,7 @@ class WalkerInstance {
|
|
|
16358
17006
|
await backtest$1.walkerMarkdownService.dump(walkerName, symbol, walkerSchema.metric || "sharpeRatio", {
|
|
16359
17007
|
exchangeName: walkerSchema.exchangeName,
|
|
16360
17008
|
frameName: walkerSchema.frameName,
|
|
16361
|
-
}, path);
|
|
17009
|
+
}, path, strategyColumns, pnlColumns);
|
|
16362
17010
|
};
|
|
16363
17011
|
}
|
|
16364
17012
|
}
|
|
@@ -16507,6 +17155,8 @@ class WalkerUtils {
|
|
|
16507
17155
|
*
|
|
16508
17156
|
* @param symbol - Trading symbol
|
|
16509
17157
|
* @param walkerName - Walker name to generate report for
|
|
17158
|
+
* @param strategyColumns - Optional strategy columns configuration
|
|
17159
|
+
* @param pnlColumns - Optional PNL columns configuration
|
|
16510
17160
|
* @returns Promise resolving to markdown formatted report string
|
|
16511
17161
|
*
|
|
16512
17162
|
* @example
|
|
@@ -16515,7 +17165,7 @@ class WalkerUtils {
|
|
|
16515
17165
|
* console.log(markdown);
|
|
16516
17166
|
* ```
|
|
16517
17167
|
*/
|
|
16518
|
-
this.getReport = async (symbol, walkerName) => {
|
|
17168
|
+
this.getReport = async (symbol, walkerName, strategyColumns, pnlColumns) => {
|
|
16519
17169
|
backtest$1.walkerValidationService.validate(walkerName, WALKER_METHOD_NAME_GET_REPORT);
|
|
16520
17170
|
const walkerSchema = backtest$1.walkerSchemaService.get(walkerName);
|
|
16521
17171
|
for (const strategyName of walkerSchema.strategies) {
|
|
@@ -16525,7 +17175,7 @@ class WalkerUtils {
|
|
|
16525
17175
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, WALKER_METHOD_NAME_GET_REPORT));
|
|
16526
17176
|
}
|
|
16527
17177
|
const instance = this._getInstance(symbol, walkerName);
|
|
16528
|
-
return await instance.getReport(symbol, walkerName);
|
|
17178
|
+
return await instance.getReport(symbol, walkerName, strategyColumns, pnlColumns);
|
|
16529
17179
|
};
|
|
16530
17180
|
/**
|
|
16531
17181
|
* Saves walker report to disk.
|
|
@@ -16533,6 +17183,8 @@ class WalkerUtils {
|
|
|
16533
17183
|
* @param symbol - Trading symbol
|
|
16534
17184
|
* @param walkerName - Walker name to save report for
|
|
16535
17185
|
* @param path - Optional directory path to save report (default: "./dump/walker")
|
|
17186
|
+
* @param strategyColumns - Optional strategy columns configuration
|
|
17187
|
+
* @param pnlColumns - Optional PNL columns configuration
|
|
16536
17188
|
*
|
|
16537
17189
|
* @example
|
|
16538
17190
|
* ```typescript
|
|
@@ -16543,7 +17195,7 @@ class WalkerUtils {
|
|
|
16543
17195
|
* await Walker.dump("BTCUSDT", "my-walker", "./custom/path");
|
|
16544
17196
|
* ```
|
|
16545
17197
|
*/
|
|
16546
|
-
this.dump = async (symbol, walkerName, path) => {
|
|
17198
|
+
this.dump = async (symbol, walkerName, path, strategyColumns, pnlColumns) => {
|
|
16547
17199
|
backtest$1.walkerValidationService.validate(walkerName, WALKER_METHOD_NAME_DUMP);
|
|
16548
17200
|
const walkerSchema = backtest$1.walkerSchemaService.get(walkerName);
|
|
16549
17201
|
for (const strategyName of walkerSchema.strategies) {
|
|
@@ -16553,7 +17205,7 @@ class WalkerUtils {
|
|
|
16553
17205
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, WALKER_METHOD_NAME_DUMP));
|
|
16554
17206
|
}
|
|
16555
17207
|
const instance = this._getInstance(symbol, walkerName);
|
|
16556
|
-
return await instance.dump(symbol, walkerName, path);
|
|
17208
|
+
return await instance.dump(symbol, walkerName, path, strategyColumns, pnlColumns);
|
|
16557
17209
|
};
|
|
16558
17210
|
/**
|
|
16559
17211
|
* Lists all active walker instances with their current status.
|
|
@@ -16659,6 +17311,7 @@ class HeatUtils {
|
|
|
16659
17311
|
* Symbols are sorted by Total PNL descending.
|
|
16660
17312
|
*
|
|
16661
17313
|
* @param strategyName - Strategy name to generate heatmap report for
|
|
17314
|
+
* @param columns - Optional columns configuration for the report
|
|
16662
17315
|
* @returns Promise resolving to markdown formatted report string
|
|
16663
17316
|
*
|
|
16664
17317
|
* @example
|
|
@@ -16677,7 +17330,7 @@ class HeatUtils {
|
|
|
16677
17330
|
* // ...
|
|
16678
17331
|
* ```
|
|
16679
17332
|
*/
|
|
16680
|
-
this.getReport = async (strategyName) => {
|
|
17333
|
+
this.getReport = async (strategyName, columns) => {
|
|
16681
17334
|
backtest$1.loggerService.info(HEAT_METHOD_NAME_GET_REPORT, { strategyName });
|
|
16682
17335
|
backtest$1.strategyValidationService.validate(strategyName, HEAT_METHOD_NAME_GET_REPORT);
|
|
16683
17336
|
{
|
|
@@ -16685,7 +17338,7 @@ class HeatUtils {
|
|
|
16685
17338
|
riskName && backtest$1.riskValidationService.validate(riskName, HEAT_METHOD_NAME_GET_REPORT);
|
|
16686
17339
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, HEAT_METHOD_NAME_GET_REPORT));
|
|
16687
17340
|
}
|
|
16688
|
-
return await backtest$1.heatMarkdownService.getReport(strategyName);
|
|
17341
|
+
return await backtest$1.heatMarkdownService.getReport(strategyName, columns);
|
|
16689
17342
|
};
|
|
16690
17343
|
/**
|
|
16691
17344
|
* Saves heatmap report to disk for a strategy.
|
|
@@ -16695,6 +17348,7 @@ class HeatUtils {
|
|
|
16695
17348
|
*
|
|
16696
17349
|
* @param strategyName - Strategy name to save heatmap report for
|
|
16697
17350
|
* @param path - Optional directory path to save report (default: "./dump/heatmap")
|
|
17351
|
+
* @param columns - Optional columns configuration for the report
|
|
16698
17352
|
*
|
|
16699
17353
|
* @example
|
|
16700
17354
|
* ```typescript
|
|
@@ -16705,7 +17359,7 @@ class HeatUtils {
|
|
|
16705
17359
|
* await Heat.dump("my-strategy", "./reports");
|
|
16706
17360
|
* ```
|
|
16707
17361
|
*/
|
|
16708
|
-
this.dump = async (strategyName, path) => {
|
|
17362
|
+
this.dump = async (strategyName, path, columns) => {
|
|
16709
17363
|
backtest$1.loggerService.info(HEAT_METHOD_NAME_DUMP, { strategyName, path });
|
|
16710
17364
|
backtest$1.strategyValidationService.validate(strategyName, HEAT_METHOD_NAME_DUMP);
|
|
16711
17365
|
{
|
|
@@ -16713,7 +17367,7 @@ class HeatUtils {
|
|
|
16713
17367
|
riskName && backtest$1.riskValidationService.validate(riskName, HEAT_METHOD_NAME_DUMP);
|
|
16714
17368
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, HEAT_METHOD_NAME_DUMP));
|
|
16715
17369
|
}
|
|
16716
|
-
await backtest$1.heatMarkdownService.dump(strategyName, path);
|
|
17370
|
+
await backtest$1.heatMarkdownService.dump(strategyName, path, columns);
|
|
16717
17371
|
};
|
|
16718
17372
|
}
|
|
16719
17373
|
}
|
|
@@ -17013,7 +17667,7 @@ class PartialUtils {
|
|
|
17013
17667
|
*
|
|
17014
17668
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
17015
17669
|
* @param strategyName - Strategy name (e.g., "my-strategy")
|
|
17016
|
-
* @returns Promise resolving to
|
|
17670
|
+
* @returns Promise resolving to PartialStatisticsModel object with counts and event list
|
|
17017
17671
|
*
|
|
17018
17672
|
* @example
|
|
17019
17673
|
* ```typescript
|
|
@@ -17057,6 +17711,7 @@ class PartialUtils {
|
|
|
17057
17711
|
*
|
|
17058
17712
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
17059
17713
|
* @param strategyName - Strategy name (e.g., "my-strategy")
|
|
17714
|
+
* @param columns - Optional columns configuration for the report
|
|
17060
17715
|
* @returns Promise resolving to markdown formatted report string
|
|
17061
17716
|
*
|
|
17062
17717
|
* @example
|
|
@@ -17077,7 +17732,7 @@ class PartialUtils {
|
|
|
17077
17732
|
* // **Loss events:** 1
|
|
17078
17733
|
* ```
|
|
17079
17734
|
*/
|
|
17080
|
-
this.getReport = async (symbol, strategyName) => {
|
|
17735
|
+
this.getReport = async (symbol, strategyName, columns) => {
|
|
17081
17736
|
backtest$1.loggerService.info(PARTIAL_METHOD_NAME_GET_REPORT, { symbol, strategyName });
|
|
17082
17737
|
backtest$1.strategyValidationService.validate(strategyName, PARTIAL_METHOD_NAME_GET_REPORT);
|
|
17083
17738
|
{
|
|
@@ -17085,7 +17740,7 @@ class PartialUtils {
|
|
|
17085
17740
|
riskName && backtest$1.riskValidationService.validate(riskName, PARTIAL_METHOD_NAME_GET_REPORT);
|
|
17086
17741
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, PARTIAL_METHOD_NAME_GET_REPORT));
|
|
17087
17742
|
}
|
|
17088
|
-
return await backtest$1.partialMarkdownService.getReport(symbol, strategyName);
|
|
17743
|
+
return await backtest$1.partialMarkdownService.getReport(symbol, strategyName, columns);
|
|
17089
17744
|
};
|
|
17090
17745
|
/**
|
|
17091
17746
|
* Generates and saves markdown report to file.
|
|
@@ -17102,6 +17757,7 @@ class PartialUtils {
|
|
|
17102
17757
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
17103
17758
|
* @param strategyName - Strategy name (e.g., "my-strategy")
|
|
17104
17759
|
* @param path - Output directory path (default: "./dump/partial")
|
|
17760
|
+
* @param columns - Optional columns configuration for the report
|
|
17105
17761
|
* @returns Promise that resolves when file is written
|
|
17106
17762
|
*
|
|
17107
17763
|
* @example
|
|
@@ -17118,7 +17774,7 @@ class PartialUtils {
|
|
|
17118
17774
|
* }
|
|
17119
17775
|
* ```
|
|
17120
17776
|
*/
|
|
17121
|
-
this.dump = async (symbol, strategyName, path) => {
|
|
17777
|
+
this.dump = async (symbol, strategyName, path, columns) => {
|
|
17122
17778
|
backtest$1.loggerService.info(PARTIAL_METHOD_NAME_DUMP, { symbol, strategyName, path });
|
|
17123
17779
|
backtest$1.strategyValidationService.validate(strategyName, PARTIAL_METHOD_NAME_DUMP);
|
|
17124
17780
|
{
|
|
@@ -17126,7 +17782,7 @@ class PartialUtils {
|
|
|
17126
17782
|
riskName && backtest$1.riskValidationService.validate(riskName, PARTIAL_METHOD_NAME_DUMP);
|
|
17127
17783
|
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, PARTIAL_METHOD_NAME_DUMP));
|
|
17128
17784
|
}
|
|
17129
|
-
await backtest$1.partialMarkdownService.dump(symbol, strategyName, path);
|
|
17785
|
+
await backtest$1.partialMarkdownService.dump(symbol, strategyName, path, columns);
|
|
17130
17786
|
};
|
|
17131
17787
|
}
|
|
17132
17788
|
}
|
|
@@ -17227,4 +17883,4 @@ class ConstantUtils {
|
|
|
17227
17883
|
*/
|
|
17228
17884
|
const Constant = new ConstantUtils();
|
|
17229
17885
|
|
|
17230
|
-
export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Risk, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getConfig, getDate, getDefaultConfig, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
|
|
17886
|
+
export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Risk, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setColumns, setConfig, setLogger };
|