backtest-kit 1.5.15 → 1.5.16
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/build/index.cjs +73 -14
- package/build/index.mjs +73 -14
- package/package.json +1 -1
- package/types.d.ts +17 -10
package/build/index.cjs
CHANGED
|
@@ -6487,7 +6487,7 @@ const columns$3 = [
|
|
|
6487
6487
|
},
|
|
6488
6488
|
];
|
|
6489
6489
|
/** Maximum number of events to store in live trading reports */
|
|
6490
|
-
const MAX_EVENTS$
|
|
6490
|
+
const MAX_EVENTS$4 = 250;
|
|
6491
6491
|
/**
|
|
6492
6492
|
* Storage class for accumulating all tick events per strategy.
|
|
6493
6493
|
* Maintains a chronological list of all events (idle, opened, active, closed).
|
|
@@ -6520,7 +6520,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6520
6520
|
}
|
|
6521
6521
|
{
|
|
6522
6522
|
this._eventList.push(newEvent);
|
|
6523
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6523
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6524
6524
|
this._eventList.shift();
|
|
6525
6525
|
}
|
|
6526
6526
|
}
|
|
@@ -6544,7 +6544,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6544
6544
|
stopLoss: data.signal.priceStopLoss,
|
|
6545
6545
|
});
|
|
6546
6546
|
// Trim queue if exceeded MAX_EVENTS
|
|
6547
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6547
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6548
6548
|
this._eventList.shift();
|
|
6549
6549
|
}
|
|
6550
6550
|
}
|
|
@@ -6570,7 +6570,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6570
6570
|
};
|
|
6571
6571
|
this._eventList.push(newEvent);
|
|
6572
6572
|
// Trim queue if exceeded MAX_EVENTS
|
|
6573
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6573
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6574
6574
|
this._eventList.shift();
|
|
6575
6575
|
}
|
|
6576
6576
|
}
|
|
@@ -6599,7 +6599,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6599
6599
|
};
|
|
6600
6600
|
this._eventList.push(newEvent);
|
|
6601
6601
|
// Trim queue if exceeded MAX_EVENTS
|
|
6602
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6602
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6603
6603
|
this._eventList.shift();
|
|
6604
6604
|
}
|
|
6605
6605
|
}
|
|
@@ -7001,7 +7001,7 @@ const columns$2 = [
|
|
|
7001
7001
|
},
|
|
7002
7002
|
];
|
|
7003
7003
|
/** Maximum number of events to store in schedule reports */
|
|
7004
|
-
const MAX_EVENTS$
|
|
7004
|
+
const MAX_EVENTS$3 = 250;
|
|
7005
7005
|
/**
|
|
7006
7006
|
* Storage class for accumulating scheduled signal events per strategy.
|
|
7007
7007
|
* Maintains a chronological list of scheduled and cancelled events.
|
|
@@ -7030,7 +7030,34 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7030
7030
|
stopLoss: data.signal.priceStopLoss,
|
|
7031
7031
|
});
|
|
7032
7032
|
// Trim queue if exceeded MAX_EVENTS
|
|
7033
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
7033
|
+
if (this._eventList.length > MAX_EVENTS$3) {
|
|
7034
|
+
this._eventList.shift();
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
/**
|
|
7038
|
+
* Adds an opened event to the storage.
|
|
7039
|
+
*
|
|
7040
|
+
* @param data - Opened tick result
|
|
7041
|
+
*/
|
|
7042
|
+
addOpenedEvent(data) {
|
|
7043
|
+
const durationMs = data.signal.pendingAt - data.signal.scheduledAt;
|
|
7044
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
7045
|
+
const newEvent = {
|
|
7046
|
+
timestamp: data.signal.pendingAt,
|
|
7047
|
+
action: "opened",
|
|
7048
|
+
symbol: data.signal.symbol,
|
|
7049
|
+
signalId: data.signal.id,
|
|
7050
|
+
position: data.signal.position,
|
|
7051
|
+
note: data.signal.note,
|
|
7052
|
+
currentPrice: data.currentPrice,
|
|
7053
|
+
priceOpen: data.signal.priceOpen,
|
|
7054
|
+
takeProfit: data.signal.priceTakeProfit,
|
|
7055
|
+
stopLoss: data.signal.priceStopLoss,
|
|
7056
|
+
duration: durationMin,
|
|
7057
|
+
};
|
|
7058
|
+
this._eventList.push(newEvent);
|
|
7059
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
7060
|
+
if (this._eventList.length > MAX_EVENTS$3) {
|
|
7034
7061
|
this._eventList.shift();
|
|
7035
7062
|
}
|
|
7036
7063
|
}
|
|
@@ -7058,7 +7085,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7058
7085
|
};
|
|
7059
7086
|
this._eventList.push(newEvent);
|
|
7060
7087
|
// Trim queue if exceeded MAX_EVENTS
|
|
7061
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
7088
|
+
if (this._eventList.length > MAX_EVENTS$3) {
|
|
7062
7089
|
this._eventList.shift();
|
|
7063
7090
|
}
|
|
7064
7091
|
}
|
|
@@ -7073,29 +7100,44 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7073
7100
|
eventList: [],
|
|
7074
7101
|
totalEvents: 0,
|
|
7075
7102
|
totalScheduled: 0,
|
|
7103
|
+
totalOpened: 0,
|
|
7076
7104
|
totalCancelled: 0,
|
|
7077
7105
|
cancellationRate: null,
|
|
7106
|
+
activationRate: null,
|
|
7078
7107
|
avgWaitTime: null,
|
|
7108
|
+
avgActivationTime: null,
|
|
7079
7109
|
};
|
|
7080
7110
|
}
|
|
7081
7111
|
const scheduledEvents = this._eventList.filter((e) => e.action === "scheduled");
|
|
7112
|
+
const openedEvents = this._eventList.filter((e) => e.action === "opened");
|
|
7082
7113
|
const cancelledEvents = this._eventList.filter((e) => e.action === "cancelled");
|
|
7083
7114
|
const totalScheduled = scheduledEvents.length;
|
|
7115
|
+
const totalOpened = openedEvents.length;
|
|
7084
7116
|
const totalCancelled = cancelledEvents.length;
|
|
7085
7117
|
// Calculate cancellation rate
|
|
7086
7118
|
const cancellationRate = totalScheduled > 0 ? (totalCancelled / totalScheduled) * 100 : null;
|
|
7119
|
+
// Calculate activation rate
|
|
7120
|
+
const activationRate = totalScheduled > 0 ? (totalOpened / totalScheduled) * 100 : null;
|
|
7087
7121
|
// Calculate average wait time for cancelled signals
|
|
7088
7122
|
const avgWaitTime = totalCancelled > 0
|
|
7089
7123
|
? cancelledEvents.reduce((sum, e) => sum + (e.duration || 0), 0) /
|
|
7090
7124
|
totalCancelled
|
|
7091
7125
|
: null;
|
|
7126
|
+
// Calculate average activation time for opened signals
|
|
7127
|
+
const avgActivationTime = totalOpened > 0
|
|
7128
|
+
? openedEvents.reduce((sum, e) => sum + (e.duration || 0), 0) /
|
|
7129
|
+
totalOpened
|
|
7130
|
+
: null;
|
|
7092
7131
|
return {
|
|
7093
7132
|
eventList: this._eventList,
|
|
7094
7133
|
totalEvents: this._eventList.length,
|
|
7095
7134
|
totalScheduled,
|
|
7135
|
+
totalOpened,
|
|
7096
7136
|
totalCancelled,
|
|
7097
7137
|
cancellationRate,
|
|
7138
|
+
activationRate,
|
|
7098
7139
|
avgWaitTime,
|
|
7140
|
+
avgActivationTime,
|
|
7099
7141
|
};
|
|
7100
7142
|
}
|
|
7101
7143
|
/**
|
|
@@ -7125,8 +7167,11 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7125
7167
|
"",
|
|
7126
7168
|
`**Total events:** ${stats.totalEvents}`,
|
|
7127
7169
|
`**Scheduled signals:** ${stats.totalScheduled}`,
|
|
7170
|
+
`**Opened signals:** ${stats.totalOpened}`,
|
|
7128
7171
|
`**Cancelled signals:** ${stats.totalCancelled}`,
|
|
7172
|
+
`**Activation rate:** ${stats.activationRate === null ? "N/A" : `${stats.activationRate.toFixed(2)}% (higher is better)`}`,
|
|
7129
7173
|
`**Cancellation rate:** ${stats.cancellationRate === null ? "N/A" : `${stats.cancellationRate.toFixed(2)}% (lower is better)`}`,
|
|
7174
|
+
`**Average activation time:** ${stats.avgActivationTime === null ? "N/A" : `${stats.avgActivationTime.toFixed(2)} minutes`}`,
|
|
7130
7175
|
`**Average wait time (cancelled):** ${stats.avgWaitTime === null ? "N/A" : `${stats.avgWaitTime.toFixed(2)} minutes`}`
|
|
7131
7176
|
].join("\n");
|
|
7132
7177
|
}
|
|
@@ -7182,10 +7227,10 @@ class ScheduleMarkdownService {
|
|
|
7182
7227
|
*/
|
|
7183
7228
|
this.getStorage = functoolsKit.memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new ReportStorage$2());
|
|
7184
7229
|
/**
|
|
7185
|
-
* Processes tick events and accumulates scheduled/cancelled events.
|
|
7186
|
-
* Should be called from
|
|
7230
|
+
* Processes tick events and accumulates scheduled/opened/cancelled events.
|
|
7231
|
+
* Should be called from signalEmitter subscription.
|
|
7187
7232
|
*
|
|
7188
|
-
* Processes only scheduled and cancelled event types.
|
|
7233
|
+
* Processes only scheduled, opened and cancelled event types.
|
|
7189
7234
|
*
|
|
7190
7235
|
* @param data - Tick result from strategy execution
|
|
7191
7236
|
*
|
|
@@ -7203,6 +7248,13 @@ class ScheduleMarkdownService {
|
|
|
7203
7248
|
if (data.action === "scheduled") {
|
|
7204
7249
|
storage.addScheduledEvent(data);
|
|
7205
7250
|
}
|
|
7251
|
+
else if (data.action === "opened") {
|
|
7252
|
+
// Check if this opened signal was previously scheduled
|
|
7253
|
+
// by checking if signal has scheduledAt != pendingAt
|
|
7254
|
+
if (data.signal.scheduledAt !== data.signal.pendingAt) {
|
|
7255
|
+
storage.addOpenedEvent(data);
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7206
7258
|
else if (data.action === "cancelled") {
|
|
7207
7259
|
storage.addCancelledEvent(data);
|
|
7208
7260
|
}
|
|
@@ -7340,7 +7392,7 @@ function percentile(sortedArray, p) {
|
|
|
7340
7392
|
return sortedArray[Math.max(0, index)];
|
|
7341
7393
|
}
|
|
7342
7394
|
/** Maximum number of performance events to store per strategy */
|
|
7343
|
-
const MAX_EVENTS$
|
|
7395
|
+
const MAX_EVENTS$2 = 10000;
|
|
7344
7396
|
/**
|
|
7345
7397
|
* Storage class for accumulating performance metrics per strategy.
|
|
7346
7398
|
* Maintains a list of all performance events and provides aggregated statistics.
|
|
@@ -7358,7 +7410,7 @@ class PerformanceStorage {
|
|
|
7358
7410
|
addEvent(event) {
|
|
7359
7411
|
this._events.push(event);
|
|
7360
7412
|
// Trim queue if exceeded MAX_EVENTS (keep most recent)
|
|
7361
|
-
if (this._events.length > MAX_EVENTS$
|
|
7413
|
+
if (this._events.length > MAX_EVENTS$2) {
|
|
7362
7414
|
this._events.shift();
|
|
7363
7415
|
}
|
|
7364
7416
|
}
|
|
@@ -8270,6 +8322,8 @@ const columns$1 = [
|
|
|
8270
8322
|
format: (data) => data.totalTrades.toString(),
|
|
8271
8323
|
},
|
|
8272
8324
|
];
|
|
8325
|
+
/** Maximum number of signals to store per symbol in heatmap reports */
|
|
8326
|
+
const MAX_EVENTS$1 = 250;
|
|
8273
8327
|
/**
|
|
8274
8328
|
* Storage class for accumulating closed signals per strategy and generating heatmap.
|
|
8275
8329
|
* Maintains symbol-level statistics and provides portfolio-wide metrics.
|
|
@@ -8289,7 +8343,12 @@ class HeatmapStorage {
|
|
|
8289
8343
|
if (!this.symbolData.has(symbol)) {
|
|
8290
8344
|
this.symbolData.set(symbol, []);
|
|
8291
8345
|
}
|
|
8292
|
-
this.symbolData.get(symbol)
|
|
8346
|
+
const signals = this.symbolData.get(symbol);
|
|
8347
|
+
signals.push(data);
|
|
8348
|
+
// Trim queue if exceeded MAX_EVENTS per symbol
|
|
8349
|
+
if (signals.length > MAX_EVENTS$1) {
|
|
8350
|
+
signals.shift();
|
|
8351
|
+
}
|
|
8293
8352
|
}
|
|
8294
8353
|
/**
|
|
8295
8354
|
* Calculates statistics for a single symbol.
|
package/build/index.mjs
CHANGED
|
@@ -6485,7 +6485,7 @@ const columns$3 = [
|
|
|
6485
6485
|
},
|
|
6486
6486
|
];
|
|
6487
6487
|
/** Maximum number of events to store in live trading reports */
|
|
6488
|
-
const MAX_EVENTS$
|
|
6488
|
+
const MAX_EVENTS$4 = 250;
|
|
6489
6489
|
/**
|
|
6490
6490
|
* Storage class for accumulating all tick events per strategy.
|
|
6491
6491
|
* Maintains a chronological list of all events (idle, opened, active, closed).
|
|
@@ -6518,7 +6518,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6518
6518
|
}
|
|
6519
6519
|
{
|
|
6520
6520
|
this._eventList.push(newEvent);
|
|
6521
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6521
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6522
6522
|
this._eventList.shift();
|
|
6523
6523
|
}
|
|
6524
6524
|
}
|
|
@@ -6542,7 +6542,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6542
6542
|
stopLoss: data.signal.priceStopLoss,
|
|
6543
6543
|
});
|
|
6544
6544
|
// Trim queue if exceeded MAX_EVENTS
|
|
6545
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6545
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6546
6546
|
this._eventList.shift();
|
|
6547
6547
|
}
|
|
6548
6548
|
}
|
|
@@ -6568,7 +6568,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6568
6568
|
};
|
|
6569
6569
|
this._eventList.push(newEvent);
|
|
6570
6570
|
// Trim queue if exceeded MAX_EVENTS
|
|
6571
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6571
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6572
6572
|
this._eventList.shift();
|
|
6573
6573
|
}
|
|
6574
6574
|
}
|
|
@@ -6597,7 +6597,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6597
6597
|
};
|
|
6598
6598
|
this._eventList.push(newEvent);
|
|
6599
6599
|
// Trim queue if exceeded MAX_EVENTS
|
|
6600
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
6600
|
+
if (this._eventList.length > MAX_EVENTS$4) {
|
|
6601
6601
|
this._eventList.shift();
|
|
6602
6602
|
}
|
|
6603
6603
|
}
|
|
@@ -6999,7 +6999,7 @@ const columns$2 = [
|
|
|
6999
6999
|
},
|
|
7000
7000
|
];
|
|
7001
7001
|
/** Maximum number of events to store in schedule reports */
|
|
7002
|
-
const MAX_EVENTS$
|
|
7002
|
+
const MAX_EVENTS$3 = 250;
|
|
7003
7003
|
/**
|
|
7004
7004
|
* Storage class for accumulating scheduled signal events per strategy.
|
|
7005
7005
|
* Maintains a chronological list of scheduled and cancelled events.
|
|
@@ -7028,7 +7028,34 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7028
7028
|
stopLoss: data.signal.priceStopLoss,
|
|
7029
7029
|
});
|
|
7030
7030
|
// Trim queue if exceeded MAX_EVENTS
|
|
7031
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
7031
|
+
if (this._eventList.length > MAX_EVENTS$3) {
|
|
7032
|
+
this._eventList.shift();
|
|
7033
|
+
}
|
|
7034
|
+
}
|
|
7035
|
+
/**
|
|
7036
|
+
* Adds an opened event to the storage.
|
|
7037
|
+
*
|
|
7038
|
+
* @param data - Opened tick result
|
|
7039
|
+
*/
|
|
7040
|
+
addOpenedEvent(data) {
|
|
7041
|
+
const durationMs = data.signal.pendingAt - data.signal.scheduledAt;
|
|
7042
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
7043
|
+
const newEvent = {
|
|
7044
|
+
timestamp: data.signal.pendingAt,
|
|
7045
|
+
action: "opened",
|
|
7046
|
+
symbol: data.signal.symbol,
|
|
7047
|
+
signalId: data.signal.id,
|
|
7048
|
+
position: data.signal.position,
|
|
7049
|
+
note: data.signal.note,
|
|
7050
|
+
currentPrice: data.currentPrice,
|
|
7051
|
+
priceOpen: data.signal.priceOpen,
|
|
7052
|
+
takeProfit: data.signal.priceTakeProfit,
|
|
7053
|
+
stopLoss: data.signal.priceStopLoss,
|
|
7054
|
+
duration: durationMin,
|
|
7055
|
+
};
|
|
7056
|
+
this._eventList.push(newEvent);
|
|
7057
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
7058
|
+
if (this._eventList.length > MAX_EVENTS$3) {
|
|
7032
7059
|
this._eventList.shift();
|
|
7033
7060
|
}
|
|
7034
7061
|
}
|
|
@@ -7056,7 +7083,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7056
7083
|
};
|
|
7057
7084
|
this._eventList.push(newEvent);
|
|
7058
7085
|
// Trim queue if exceeded MAX_EVENTS
|
|
7059
|
-
if (this._eventList.length > MAX_EVENTS$
|
|
7086
|
+
if (this._eventList.length > MAX_EVENTS$3) {
|
|
7060
7087
|
this._eventList.shift();
|
|
7061
7088
|
}
|
|
7062
7089
|
}
|
|
@@ -7071,29 +7098,44 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7071
7098
|
eventList: [],
|
|
7072
7099
|
totalEvents: 0,
|
|
7073
7100
|
totalScheduled: 0,
|
|
7101
|
+
totalOpened: 0,
|
|
7074
7102
|
totalCancelled: 0,
|
|
7075
7103
|
cancellationRate: null,
|
|
7104
|
+
activationRate: null,
|
|
7076
7105
|
avgWaitTime: null,
|
|
7106
|
+
avgActivationTime: null,
|
|
7077
7107
|
};
|
|
7078
7108
|
}
|
|
7079
7109
|
const scheduledEvents = this._eventList.filter((e) => e.action === "scheduled");
|
|
7110
|
+
const openedEvents = this._eventList.filter((e) => e.action === "opened");
|
|
7080
7111
|
const cancelledEvents = this._eventList.filter((e) => e.action === "cancelled");
|
|
7081
7112
|
const totalScheduled = scheduledEvents.length;
|
|
7113
|
+
const totalOpened = openedEvents.length;
|
|
7082
7114
|
const totalCancelled = cancelledEvents.length;
|
|
7083
7115
|
// Calculate cancellation rate
|
|
7084
7116
|
const cancellationRate = totalScheduled > 0 ? (totalCancelled / totalScheduled) * 100 : null;
|
|
7117
|
+
// Calculate activation rate
|
|
7118
|
+
const activationRate = totalScheduled > 0 ? (totalOpened / totalScheduled) * 100 : null;
|
|
7085
7119
|
// Calculate average wait time for cancelled signals
|
|
7086
7120
|
const avgWaitTime = totalCancelled > 0
|
|
7087
7121
|
? cancelledEvents.reduce((sum, e) => sum + (e.duration || 0), 0) /
|
|
7088
7122
|
totalCancelled
|
|
7089
7123
|
: null;
|
|
7124
|
+
// Calculate average activation time for opened signals
|
|
7125
|
+
const avgActivationTime = totalOpened > 0
|
|
7126
|
+
? openedEvents.reduce((sum, e) => sum + (e.duration || 0), 0) /
|
|
7127
|
+
totalOpened
|
|
7128
|
+
: null;
|
|
7090
7129
|
return {
|
|
7091
7130
|
eventList: this._eventList,
|
|
7092
7131
|
totalEvents: this._eventList.length,
|
|
7093
7132
|
totalScheduled,
|
|
7133
|
+
totalOpened,
|
|
7094
7134
|
totalCancelled,
|
|
7095
7135
|
cancellationRate,
|
|
7136
|
+
activationRate,
|
|
7096
7137
|
avgWaitTime,
|
|
7138
|
+
avgActivationTime,
|
|
7097
7139
|
};
|
|
7098
7140
|
}
|
|
7099
7141
|
/**
|
|
@@ -7123,8 +7165,11 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
7123
7165
|
"",
|
|
7124
7166
|
`**Total events:** ${stats.totalEvents}`,
|
|
7125
7167
|
`**Scheduled signals:** ${stats.totalScheduled}`,
|
|
7168
|
+
`**Opened signals:** ${stats.totalOpened}`,
|
|
7126
7169
|
`**Cancelled signals:** ${stats.totalCancelled}`,
|
|
7170
|
+
`**Activation rate:** ${stats.activationRate === null ? "N/A" : `${stats.activationRate.toFixed(2)}% (higher is better)`}`,
|
|
7127
7171
|
`**Cancellation rate:** ${stats.cancellationRate === null ? "N/A" : `${stats.cancellationRate.toFixed(2)}% (lower is better)`}`,
|
|
7172
|
+
`**Average activation time:** ${stats.avgActivationTime === null ? "N/A" : `${stats.avgActivationTime.toFixed(2)} minutes`}`,
|
|
7128
7173
|
`**Average wait time (cancelled):** ${stats.avgWaitTime === null ? "N/A" : `${stats.avgWaitTime.toFixed(2)} minutes`}`
|
|
7129
7174
|
].join("\n");
|
|
7130
7175
|
}
|
|
@@ -7180,10 +7225,10 @@ class ScheduleMarkdownService {
|
|
|
7180
7225
|
*/
|
|
7181
7226
|
this.getStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new ReportStorage$2());
|
|
7182
7227
|
/**
|
|
7183
|
-
* Processes tick events and accumulates scheduled/cancelled events.
|
|
7184
|
-
* Should be called from
|
|
7228
|
+
* Processes tick events and accumulates scheduled/opened/cancelled events.
|
|
7229
|
+
* Should be called from signalEmitter subscription.
|
|
7185
7230
|
*
|
|
7186
|
-
* Processes only scheduled and cancelled event types.
|
|
7231
|
+
* Processes only scheduled, opened and cancelled event types.
|
|
7187
7232
|
*
|
|
7188
7233
|
* @param data - Tick result from strategy execution
|
|
7189
7234
|
*
|
|
@@ -7201,6 +7246,13 @@ class ScheduleMarkdownService {
|
|
|
7201
7246
|
if (data.action === "scheduled") {
|
|
7202
7247
|
storage.addScheduledEvent(data);
|
|
7203
7248
|
}
|
|
7249
|
+
else if (data.action === "opened") {
|
|
7250
|
+
// Check if this opened signal was previously scheduled
|
|
7251
|
+
// by checking if signal has scheduledAt != pendingAt
|
|
7252
|
+
if (data.signal.scheduledAt !== data.signal.pendingAt) {
|
|
7253
|
+
storage.addOpenedEvent(data);
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7204
7256
|
else if (data.action === "cancelled") {
|
|
7205
7257
|
storage.addCancelledEvent(data);
|
|
7206
7258
|
}
|
|
@@ -7338,7 +7390,7 @@ function percentile(sortedArray, p) {
|
|
|
7338
7390
|
return sortedArray[Math.max(0, index)];
|
|
7339
7391
|
}
|
|
7340
7392
|
/** Maximum number of performance events to store per strategy */
|
|
7341
|
-
const MAX_EVENTS$
|
|
7393
|
+
const MAX_EVENTS$2 = 10000;
|
|
7342
7394
|
/**
|
|
7343
7395
|
* Storage class for accumulating performance metrics per strategy.
|
|
7344
7396
|
* Maintains a list of all performance events and provides aggregated statistics.
|
|
@@ -7356,7 +7408,7 @@ class PerformanceStorage {
|
|
|
7356
7408
|
addEvent(event) {
|
|
7357
7409
|
this._events.push(event);
|
|
7358
7410
|
// Trim queue if exceeded MAX_EVENTS (keep most recent)
|
|
7359
|
-
if (this._events.length > MAX_EVENTS$
|
|
7411
|
+
if (this._events.length > MAX_EVENTS$2) {
|
|
7360
7412
|
this._events.shift();
|
|
7361
7413
|
}
|
|
7362
7414
|
}
|
|
@@ -8268,6 +8320,8 @@ const columns$1 = [
|
|
|
8268
8320
|
format: (data) => data.totalTrades.toString(),
|
|
8269
8321
|
},
|
|
8270
8322
|
];
|
|
8323
|
+
/** Maximum number of signals to store per symbol in heatmap reports */
|
|
8324
|
+
const MAX_EVENTS$1 = 250;
|
|
8271
8325
|
/**
|
|
8272
8326
|
* Storage class for accumulating closed signals per strategy and generating heatmap.
|
|
8273
8327
|
* Maintains symbol-level statistics and provides portfolio-wide metrics.
|
|
@@ -8287,7 +8341,12 @@ class HeatmapStorage {
|
|
|
8287
8341
|
if (!this.symbolData.has(symbol)) {
|
|
8288
8342
|
this.symbolData.set(symbol, []);
|
|
8289
8343
|
}
|
|
8290
|
-
this.symbolData.get(symbol)
|
|
8344
|
+
const signals = this.symbolData.get(symbol);
|
|
8345
|
+
signals.push(data);
|
|
8346
|
+
// Trim queue if exceeded MAX_EVENTS per symbol
|
|
8347
|
+
if (signals.length > MAX_EVENTS$1) {
|
|
8348
|
+
signals.shift();
|
|
8349
|
+
}
|
|
8291
8350
|
}
|
|
8292
8351
|
/**
|
|
8293
8352
|
* Calculates statistics for a single symbol.
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -4079,13 +4079,13 @@ declare class LiveMarkdownService {
|
|
|
4079
4079
|
|
|
4080
4080
|
/**
|
|
4081
4081
|
* Unified scheduled signal event data for report generation.
|
|
4082
|
-
* Contains all information about scheduled and cancelled events.
|
|
4082
|
+
* Contains all information about scheduled, opened and cancelled events.
|
|
4083
4083
|
*/
|
|
4084
4084
|
interface ScheduledEvent {
|
|
4085
4085
|
/** Event timestamp in milliseconds (scheduledAt for scheduled/cancelled events) */
|
|
4086
4086
|
timestamp: number;
|
|
4087
4087
|
/** Event action type */
|
|
4088
|
-
action: "scheduled" | "cancelled";
|
|
4088
|
+
action: "scheduled" | "opened" | "cancelled";
|
|
4089
4089
|
/** Trading pair symbol */
|
|
4090
4090
|
symbol: string;
|
|
4091
4091
|
/** Signal ID */
|
|
@@ -4104,13 +4104,13 @@ interface ScheduledEvent {
|
|
|
4104
4104
|
stopLoss: number;
|
|
4105
4105
|
/** Close timestamp (only for cancelled) */
|
|
4106
4106
|
closeTimestamp?: number;
|
|
4107
|
-
/** Duration in minutes (only for cancelled) */
|
|
4107
|
+
/** Duration in minutes (only for cancelled/opened) */
|
|
4108
4108
|
duration?: number;
|
|
4109
4109
|
}
|
|
4110
4110
|
/**
|
|
4111
4111
|
* Statistical data calculated from scheduled signals.
|
|
4112
4112
|
*
|
|
4113
|
-
* Provides metrics for scheduled signal tracking and cancellation analysis.
|
|
4113
|
+
* Provides metrics for scheduled signal tracking, activation and cancellation analysis.
|
|
4114
4114
|
*
|
|
4115
4115
|
* @example
|
|
4116
4116
|
* ```typescript
|
|
@@ -4118,10 +4118,11 @@ interface ScheduledEvent {
|
|
|
4118
4118
|
*
|
|
4119
4119
|
* console.log(`Total events: ${stats.totalEvents}`);
|
|
4120
4120
|
* console.log(`Scheduled signals: ${stats.totalScheduled}`);
|
|
4121
|
+
* console.log(`Opened signals: ${stats.totalOpened}`);
|
|
4121
4122
|
* console.log(`Cancelled signals: ${stats.totalCancelled}`);
|
|
4122
4123
|
* console.log(`Cancellation rate: ${stats.cancellationRate}%`);
|
|
4123
4124
|
*
|
|
4124
|
-
* // Access raw event data (includes scheduled, cancelled)
|
|
4125
|
+
* // Access raw event data (includes scheduled, opened, cancelled)
|
|
4125
4126
|
* stats.eventList.forEach(event => {
|
|
4126
4127
|
* if (event.action === "cancelled") {
|
|
4127
4128
|
* console.log(`Cancelled signal: ${event.signalId}`);
|
|
@@ -4130,18 +4131,24 @@ interface ScheduledEvent {
|
|
|
4130
4131
|
* ```
|
|
4131
4132
|
*/
|
|
4132
4133
|
interface ScheduleStatistics {
|
|
4133
|
-
/** Array of all scheduled/cancelled events with full details */
|
|
4134
|
+
/** Array of all scheduled/opened/cancelled events with full details */
|
|
4134
4135
|
eventList: ScheduledEvent[];
|
|
4135
|
-
/** Total number of all events (includes scheduled, cancelled) */
|
|
4136
|
+
/** Total number of all events (includes scheduled, opened, cancelled) */
|
|
4136
4137
|
totalEvents: number;
|
|
4137
4138
|
/** Total number of scheduled signals */
|
|
4138
4139
|
totalScheduled: number;
|
|
4140
|
+
/** Total number of opened signals (activated from scheduled) */
|
|
4141
|
+
totalOpened: number;
|
|
4139
4142
|
/** Total number of cancelled signals */
|
|
4140
4143
|
totalCancelled: number;
|
|
4141
4144
|
/** Cancellation rate as percentage (0-100), null if no scheduled signals. Lower is better. */
|
|
4142
4145
|
cancellationRate: number | null;
|
|
4146
|
+
/** Activation rate as percentage (0-100), null if no scheduled signals. Higher is better. */
|
|
4147
|
+
activationRate: number | null;
|
|
4143
4148
|
/** Average waiting time for cancelled signals in minutes, null if no cancelled signals */
|
|
4144
4149
|
avgWaitTime: number | null;
|
|
4150
|
+
/** Average waiting time for opened signals in minutes, null if no opened signals */
|
|
4151
|
+
avgActivationTime: number | null;
|
|
4145
4152
|
}
|
|
4146
4153
|
/**
|
|
4147
4154
|
* Service for generating and saving scheduled signals markdown reports.
|
|
@@ -4173,10 +4180,10 @@ declare class ScheduleMarkdownService {
|
|
|
4173
4180
|
*/
|
|
4174
4181
|
private getStorage;
|
|
4175
4182
|
/**
|
|
4176
|
-
* Processes tick events and accumulates scheduled/cancelled events.
|
|
4177
|
-
* Should be called from
|
|
4183
|
+
* Processes tick events and accumulates scheduled/opened/cancelled events.
|
|
4184
|
+
* Should be called from signalEmitter subscription.
|
|
4178
4185
|
*
|
|
4179
|
-
* Processes only scheduled and cancelled event types.
|
|
4186
|
+
* Processes only scheduled, opened and cancelled event types.
|
|
4180
4187
|
*
|
|
4181
4188
|
* @param data - Tick result from strategy execution
|
|
4182
4189
|
*
|