backtest-kit 1.1.9 β†’ 1.2.1

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 CHANGED
@@ -10,13 +10,9 @@ Build sophisticated trading systems with confidence. Backtest Kit empowers you t
10
10
 
11
11
  πŸ“š **[API Reference](https://github.com/tripolskypetr/backtest-kit)** | 🌟 **[Quick Start](#quick-start)**
12
12
 
13
- ## 🎯 Supported Order Types
14
-
15
- Backtest Kit supports multiple execution styles to match real trading behavior:
16
-
17
13
  ## ✨ Why Choose Backtest Kit?
18
14
 
19
- - πŸš€ **Production-Ready Architecture**: Seamlessly switch between backtest and live modes with robust error recovery and graceful shutdown mechanisms. Your strategy code remains identical across environments. βœ…
15
+ - πŸš€ **Production-Ready Architecture**: Seamlessly switch between backtest and live modes with robust error recovery and graceful shutdown mechanisms. Your strategy code remains identical across environments.
20
16
 
21
17
  - πŸ’Ύ **Crash-Safe Persistence**: Atomic file writes with automatic state recovery ensure no duplicate signals or lost dataβ€”even after crashes. Resume execution exactly where you left off. πŸ”„
22
18
 
@@ -26,7 +22,7 @@ Backtest Kit supports multiple execution styles to match real trading behavior:
26
22
 
27
23
  - πŸ“Š **VWAP Pricing**: Volume-weighted average price from last 5 1-minute candles ensures realistic backtest results that match live execution. πŸ“ˆ
28
24
 
29
- - 🎯 **Type-Safe Signal Lifecycle**: State machine with compile-time guarantees (idle β†’ opened β†’ active β†’ closed). No runtime state confusion. πŸ”’
25
+ - 🎯 **Type-Safe Signal Lifecycle**: State machine with compile-time guarantees (idle β†’ scheduled β†’ opened β†’ active β†’ closed/cancelled). No runtime state confusion. πŸ”’
30
26
 
31
27
  - πŸ“ˆ **Accurate PNL Calculation**: Realistic profit/loss with configurable fees (0.1%) and slippage (0.1%). Track gross and net returns separately. πŸ’°
32
28
 
@@ -50,26 +46,28 @@ Backtest Kit supports multiple execution styles to match real trading behavior:
50
46
 
51
47
  - πŸ”’ **Safe Math & Robustness**: All metrics protected against NaN/Infinity with unsafe numeric checks. Returns N/A for invalid calculations. ✨
52
48
 
53
- - πŸ§ͺ **Comprehensive Test Coverage**: 109 unit and integration tests covering validation, PNL, callbacks, reports, performance tracking, walker, heatmap, position sizing, risk management, and event system. βœ…
49
+ - πŸ§ͺ **Comprehensive Test Coverage**: 123 unit and integration tests covering validation, PNL, callbacks, reports, performance tracking, walker, heatmap, position sizing, risk management, scheduled signals, and event system.
54
50
 
55
51
  ---
56
52
 
57
- ### βœ… Built-in Order Types
53
+ ### 🎳 Supported Order Types
54
+
55
+ Backtest Kit supports multiple execution styles to match real trading behavior:
58
56
 
59
57
  - **Market** β€” instant execution using current VWAP
60
-
58
+
61
59
  - **Limit** β€” entry at a specified `priceOpen`
62
-
60
+
63
61
  - **Take Profit (TP)** β€” automatic exit at the target price
64
-
62
+
65
63
  - **Stop Loss (SL)** β€” protective exit at the stop level
66
-
64
+
67
65
  - **OCO (TP + SL)** β€” linked exits; one cancels the other
68
-
69
- - **Time-Expired** β€” automatic closure after `minuteEstimatedTime` ⏱️
66
+
67
+ - **Grid** β€” auto-cancel if price never reaches entry point or hits SL before activation
70
68
 
71
69
 
72
- ### βž• Extendable Order Types
70
+ ### πŸ†• Extendable Order Types
73
71
 
74
72
  Easy to add without modifying the core:
75
73
 
@@ -128,23 +126,32 @@ addStrategy({
128
126
  strategyName: "sma-crossover",
129
127
  interval: "5m", // Throttling: signals generated max once per 5 minutes
130
128
  getSignal: async (symbol) => {
131
- // Your signal generation logic
129
+ const price = await getAveragePrice(symbol);
132
130
  return {
133
131
  position: "long",
134
132
  note: "BTC breakout",
135
- priceOpen: 50000,
136
- priceTakeProfit: 51000, // Must be > priceOpen for long
137
- priceStopLoss: 49000, // Must be < priceOpen for long
138
- minuteEstimatedTime: 60, // Signal duration in minutes
133
+ priceOpen: price,
134
+ priceTakeProfit: price + 1_000, // Must be > priceOpen for long
135
+ priceStopLoss: price - 1_000, // Must be < priceOpen for long
136
+ minuteEstimatedTime: 60,
139
137
  };
140
138
  },
141
139
  callbacks: {
140
+ onSchedule: (symbol, signal, currentPrice, backtest) => {
141
+ console.log(`[${backtest ? "BT" : "LIVE"}] Scheduled signal created:`, signal.id);
142
+ },
142
143
  onOpen: (symbol, signal, currentPrice, backtest) => {
143
144
  console.log(`[${backtest ? "BT" : "LIVE"}] Signal opened:`, signal.id);
144
145
  },
146
+ onActive: (symbol, signal, currentPrice, backtest) => {
147
+ console.log(`[${backtest ? "BT" : "LIVE"}] Signal active:`, signal.id);
148
+ },
145
149
  onClose: (symbol, signal, priceClose, backtest) => {
146
150
  console.log(`[${backtest ? "BT" : "LIVE"}] Signal closed:`, priceClose);
147
151
  },
152
+ onCancel: (symbol, signal, currentPrice, backtest) => {
153
+ console.log(`[${backtest ? "BT" : "LIVE"}] Scheduled signal cancelled:`, signal.id);
154
+ },
148
155
  },
149
156
  });
150
157
 
@@ -220,6 +227,7 @@ Backtest.background("BTCUSDT", {
220
227
  - πŸ›‘οΈ **Signal Lifecycle**: Type-safe state machine prevents invalid state transitions. πŸš‘
221
228
  - πŸ“¦ **Dependency Inversion**: Lazy-load components at runtime for modular, scalable designs. 🧩
222
229
  - πŸ” **Schema Reflection**: Runtime introspection with `listExchanges()`, `listStrategies()`, `listFrames()`. πŸ“Š
230
+ - πŸ”¬ **Data Validation**: Automatic detection and rejection of incomplete candles from Binance API with anomaly checks.
223
231
 
224
232
  ---
225
233
 
@@ -238,6 +246,7 @@ Backtest.background("BTCUSDT", {
238
246
  - πŸ€– **`addStrategy`**: Create trading strategies with custom signals and callbacks. πŸ’‘
239
247
  - 🌐 **`addFrame`**: Configure timeframes for backtesting. πŸ“…
240
248
  - πŸ”„ **`Backtest` / `Live`**: Run strategies in backtest or live mode (generator or background). ⚑
249
+ - πŸ“… **`Schedule`**: Track scheduled signals and cancellation rate for limit orders. πŸ“Š
241
250
  - πŸƒ **`Walker`**: Compare multiple strategies in parallel with ranking. πŸ†
242
251
  - πŸ”₯ **`Heat`**: Portfolio-wide performance analysis across multiple symbols. πŸ“Š
243
252
  - πŸ’° **`PositionSize`**: Calculate position sizes with Fixed %, Kelly Criterion, or ATR-based methods. πŸ’΅
@@ -253,7 +262,7 @@ Check out the sections below for detailed examples! πŸ“š
253
262
 
254
263
  ### 1. Register Exchange Data Source
255
264
 
256
- You can plug any data sourceβ€”CCXT for live data or a database for faster backtesting:
265
+ You can plug any data source: CCXT for live data or a database for faster backtesting:
257
266
 
258
267
  ```typescript
259
268
  import { addExchange } from "backtest-kit";
@@ -303,14 +312,14 @@ addStrategy({
303
312
  strategyName: "my-strategy",
304
313
  interval: "5m", // Throttling: signals generated max once per 5 minutes
305
314
  getSignal: async (symbol) => {
306
- // Your signal generation logic
315
+ const price = await getAveragePrice(symbol);
307
316
  return {
308
317
  position: "long",
309
318
  note: "BTC breakout",
310
- priceOpen: 50000,
311
- priceTakeProfit: 51000, // Must be > priceOpen for long
312
- priceStopLoss: 49000, // Must be < priceOpen for long
313
- minuteEstimatedTime: 60, // Signal duration in minutes
319
+ priceOpen: price,
320
+ priceTakeProfit: price + 1_000, // Must be > priceOpen for long
321
+ priceStopLoss: price - 1_000, // Must be < priceOpen for long
322
+ minuteEstimatedTime: 60,
314
323
  };
315
324
  },
316
325
  callbacks: {
@@ -1062,24 +1071,377 @@ test("Custom Redis adapter works correctly", async ({ pass, fail }) => {
1062
1071
 
1063
1072
  ---
1064
1073
 
1074
+ ## πŸ“ Architecture Overview
1075
+
1076
+ The framework follows **clean architecture** with:
1077
+
1078
+ - **Client Layer** - Pure business logic without DI (ClientStrategy, ClientExchange, ClientFrame)
1079
+ - **Service Layer** - DI-based services organized by responsibility
1080
+ - **Schema Services** - Registry pattern for configuration
1081
+ - **Connection Services** - Memoized client instance creators
1082
+ - **Global Services** - Context wrappers for public API
1083
+ - **Logic Services** - Async generator orchestration (backtest/live)
1084
+ - **Persistence Layer** - Crash-safe atomic file writes with `PersistSignalAdapter`
1085
+
1086
+ ---
1087
+
1088
+ ## βœ… Signal Validation
1089
+
1090
+ All signals are validated automatically before execution:
1091
+
1092
+ ```typescript
1093
+ // βœ… Valid long signal
1094
+ {
1095
+ position: "long",
1096
+ priceOpen: 50000,
1097
+ priceTakeProfit: 51000, // βœ… 51000 > 50000
1098
+ priceStopLoss: 49000, // βœ… 49000 < 50000
1099
+ minuteEstimatedTime: 60, // βœ… positive
1100
+ }
1101
+
1102
+ // ❌ Invalid long signal - throws error
1103
+ {
1104
+ position: "long",
1105
+ priceOpen: 50000,
1106
+ priceTakeProfit: 49000, // ❌ 49000 < 50000 (must be higher for long)
1107
+ priceStopLoss: 51000, // ❌ 51000 > 50000 (must be lower for long)
1108
+ }
1109
+
1110
+ // βœ… Valid short signal
1111
+ {
1112
+ position: "short",
1113
+ priceOpen: 50000,
1114
+ priceTakeProfit: 49000, // βœ… 49000 < 50000 (profit goes down for short)
1115
+ priceStopLoss: 51000, // βœ… 51000 > 50000 (stop loss goes up for short)
1116
+ }
1117
+ ```
1118
+
1119
+ Validation errors include detailed messages for debugging.
1120
+
1121
+ ---
1122
+
1123
+ ## 🧠 Interval Throttling
1124
+
1125
+ Prevent signal spam with automatic throttling:
1126
+
1127
+ ```typescript
1128
+ addStrategy({
1129
+ strategyName: "my-strategy",
1130
+ interval: "5m", // Signals generated max once per 5 minutes
1131
+ getSignal: async (symbol) => {
1132
+ // This function will be called max once per 5 minutes
1133
+ // Even if tick() is called every second
1134
+ return signal;
1135
+ },
1136
+ });
1137
+ ```
1138
+
1139
+ Supported intervals: `"1m"`, `"3m"`, `"5m"`, `"15m"`, `"30m"`, `"1h"`
1140
+
1141
+ ---
1142
+
1143
+ ## πŸ“ Markdown Reports
1144
+
1145
+ Generate detailed trading reports with statistics:
1146
+
1147
+ ### Backtest Reports
1148
+
1149
+ ```typescript
1150
+ import { Backtest } from "backtest-kit";
1151
+
1152
+ // Get raw statistical data (Controller)
1153
+ const stats = await Backtest.getData("my-strategy");
1154
+ console.log(stats);
1155
+ // Returns:
1156
+ // {
1157
+ // signalList: [...], // All closed signals
1158
+ // totalSignals: 10,
1159
+ // winCount: 7,
1160
+ // lossCount: 3,
1161
+ // winRate: 70.0, // Percentage (higher is better)
1162
+ // avgPnl: 1.23, // Average PNL % (higher is better)
1163
+ // totalPnl: 12.30, // Total PNL % (higher is better)
1164
+ // stdDev: 2.45, // Standard deviation (lower is better)
1165
+ // sharpeRatio: 0.50, // Risk-adjusted return (higher is better)
1166
+ // annualizedSharpeRatio: 9.55, // Sharpe Γ— √365 (higher is better)
1167
+ // certaintyRatio: 1.75, // avgWin / |avgLoss| (higher is better)
1168
+ // expectedYearlyReturns: 156 // Estimated yearly trades (higher is better)
1169
+ // }
1170
+
1171
+ // Generate markdown report (View)
1172
+ const markdown = await Backtest.getReport("my-strategy");
1173
+
1174
+ // Save to disk (default: ./logs/backtest/my-strategy.md)
1175
+ await Backtest.dump("my-strategy");
1176
+ ```
1177
+
1178
+ ### Live Trading Reports
1179
+
1180
+ ```typescript
1181
+ import { Live } from "backtest-kit";
1182
+
1183
+ // Get raw statistical data (Controller)
1184
+ const stats = await Live.getData("my-strategy");
1185
+ console.log(stats);
1186
+ // Returns:
1187
+ // {
1188
+ // eventList: [...], // All events (idle, scheduled, opened, active, closed, cancelled)
1189
+ // totalEvents: 15,
1190
+ // totalClosed: 5,
1191
+ // winCount: 3,
1192
+ // lossCount: 2,
1193
+ // winRate: 60.0, // Percentage (higher is better)
1194
+ // avgPnl: 1.23, // Average PNL % (higher is better)
1195
+ // totalPnl: 6.15, // Total PNL % (higher is better)
1196
+ // stdDev: 1.85, // Standard deviation (lower is better)
1197
+ // sharpeRatio: 0.66, // Risk-adjusted return (higher is better)
1198
+ // annualizedSharpeRatio: 12.61,// Sharpe Γ— √365 (higher is better)
1199
+ // certaintyRatio: 2.10, // avgWin / |avgLoss| (higher is better)
1200
+ // expectedYearlyReturns: 365 // Estimated yearly trades (higher is better)
1201
+ // }
1202
+
1203
+ // Generate markdown report (View)
1204
+ const markdown = await Live.getReport("my-strategy");
1205
+
1206
+ // Save to disk (default: ./logs/live/my-strategy.md)
1207
+ await Live.dump("my-strategy");
1208
+ ```
1209
+
1210
+ ### Scheduled Signals Reports
1211
+
1212
+ ```typescript
1213
+ import { Schedule } from "backtest-kit";
1214
+
1215
+ // Get raw scheduled signals data (Controller)
1216
+ const stats = await Schedule.getData("my-strategy");
1217
+ console.log(stats);
1218
+ // Returns:
1219
+ // {
1220
+ // eventList: [...], // All scheduled/cancelled events
1221
+ // totalEvents: 8,
1222
+ // totalScheduled: 6, // Number of scheduled signals
1223
+ // totalCancelled: 2, // Number of cancelled signals
1224
+ // cancellationRate: 33.33, // Percentage (lower is better)
1225
+ // avgWaitTime: 45.5, // Average wait time for cancelled signals in minutes
1226
+ // }
1227
+
1228
+ // Generate markdown report (View)
1229
+ const markdown = await Schedule.getReport("my-strategy");
1230
+
1231
+ // Save to disk (default: ./logs/schedule/my-strategy.md)
1232
+ await Schedule.dump("my-strategy");
1233
+
1234
+ // Clear accumulated data
1235
+ await Schedule.clear("my-strategy");
1236
+ ```
1237
+
1238
+ **Scheduled Signals Report Example:**
1239
+ ```markdown
1240
+ # Scheduled Signals Report: my-strategy
1241
+
1242
+ | Timestamp | Action | Symbol | Signal ID | Position | Note | Current Price | Entry Price | Take Profit | Stop Loss | Wait Time (min) |
1243
+ |-----------|--------|--------|-----------|----------|------|---------------|-------------|-------------|-----------|-----------------|
1244
+ | 2024-01-15T10:30:00Z | SCHEDULED | BTCUSDT | sig-001 | LONG | BTC breakout | 42150.50 USD | 42000.00 USD | 43000.00 USD | 41000.00 USD | N/A |
1245
+ | 2024-01-15T10:35:00Z | CANCELLED | BTCUSDT | sig-002 | LONG | BTC breakout | 42350.80 USD | 10000.00 USD | 11000.00 USD | 9000.00 USD | 60 |
1246
+
1247
+ **Total events:** 8
1248
+ **Scheduled signals:** 6
1249
+ **Cancelled signals:** 2
1250
+ **Cancellation rate:** 33.33% (lower is better)
1251
+ **Average wait time (cancelled):** 45.50 minutes
1252
+ ```
1253
+
1254
+ ---
1255
+
1256
+ ## 🎧 Event Listeners
1257
+
1258
+ ### Listen to All Signals (Backtest + Live)
1259
+
1260
+ ```typescript
1261
+ import { listenSignal } from "backtest-kit";
1262
+
1263
+ // Listen to both backtest and live signals
1264
+ listenSignal((event) => {
1265
+ console.log(`[${event.backtest ? "BT" : "LIVE"}] ${event.action}:`, event.signal.id);
1266
+
1267
+ if (event.action === "closed") {
1268
+ console.log("PNL:", event.pnl.pnlPercentage);
1269
+ console.log("Close reason:", event.closeReason);
1270
+ }
1271
+ });
1272
+ ```
1273
+
1274
+ ### Listen Once with Filter
1275
+
1276
+ ```typescript
1277
+ import { listenSignalOnce, listenSignalLiveOnce } from "backtest-kit";
1278
+
1279
+ // Listen once with filter
1280
+ listenSignalOnce(
1281
+ (event) => event.action === "closed" && event.pnl.pnlPercentage > 5,
1282
+ (event) => {
1283
+ console.log("Big win detected:", event.pnl.pnlPercentage);
1284
+ }
1285
+ );
1286
+
1287
+ // Listen once for specific symbol in live mode
1288
+ listenSignalLiveOnce(
1289
+ (event) => event.signal.symbol === "BTCUSDT" && event.action === "opened",
1290
+ (event) => {
1291
+ console.log("BTC signal opened:", event.signal.id);
1292
+ }
1293
+ );
1294
+ ```
1295
+
1296
+ ### Listen to Background Completion
1297
+
1298
+ ```typescript
1299
+ import { listenDoneBacktest, listenDoneLive, listenDoneWalker } from "backtest-kit";
1300
+
1301
+ // Backtest completion
1302
+ listenDoneBacktest((event) => {
1303
+ console.log("Backtest completed:", event.strategyName);
1304
+ console.log("Symbol:", event.symbol);
1305
+ console.log("Exchange:", event.exchangeName);
1306
+ });
1307
+
1308
+ // Live trading completion
1309
+ listenDoneLive((event) => {
1310
+ console.log("Live trading stopped:", event.strategyName);
1311
+ });
1312
+
1313
+ // Walker completion
1314
+ listenDoneWalker((event) => {
1315
+ console.log("Walker completed:", event.strategyName);
1316
+ console.log("Best strategy:", event.bestStrategy);
1317
+ });
1318
+ ```
1319
+
1320
+ ---
1321
+
1322
+ ## βš™οΈ Global Configuration
1323
+
1324
+ You can customize framework behavior using the `setConfig()` function. This allows you to adjust global parameters without modifying the source code.
1325
+
1326
+ ### Available Configuration Options
1327
+
1328
+ ```typescript
1329
+ import { setConfig } from "backtest-kit";
1330
+
1331
+ // Configure global parameters
1332
+ await setConfig({
1333
+ // Time to wait for scheduled signal activation (in minutes)
1334
+ // If a scheduled signal doesn't activate within this time, it will be cancelled
1335
+ // Default: 120 minutes
1336
+ CC_SCHEDULE_AWAIT_MINUTES: 90,
1337
+
1338
+ // Number of candles to use for average price calculation (VWAP)
1339
+ // Used in both backtest and live modes for price calculations
1340
+ // Default: 5 candles (last 5 minutes when using 1m interval)
1341
+ CC_AVG_PRICE_CANDLES_COUNT: 10,
1342
+ });
1343
+ ```
1344
+
1345
+ ### Configuration Parameters
1346
+
1347
+ #### `CC_SCHEDULE_AWAIT_MINUTES`
1348
+
1349
+ Controls how long scheduled signals wait for activation before being cancelled.
1350
+
1351
+ - **Default:** `120` minutes (2 hours)
1352
+ - **Use case:** Adjust based on market volatility and strategy timeframe
1353
+ - **Example:** Lower for scalping strategies (30-60 min), higher for swing trading (180-360 min)
1354
+
1355
+ ```typescript
1356
+ // For scalping strategies with tight entry windows
1357
+ await setConfig({
1358
+ CC_SCHEDULE_AWAIT_MINUTES: 30,
1359
+ });
1360
+
1361
+ // For swing trading with wider entry windows
1362
+ await setConfig({
1363
+ CC_SCHEDULE_AWAIT_MINUTES: 240,
1364
+ });
1365
+ ```
1366
+
1367
+ #### `CC_AVG_PRICE_CANDLES_COUNT`
1368
+
1369
+ Controls the number of 1-minute candles used for VWAP (Volume Weighted Average Price) calculations.
1370
+
1371
+ - **Default:** `5` candles (5 minutes of data)
1372
+ - **Use case:** Adjust for more stable (higher) or responsive (lower) price calculations
1373
+ - **Impact:** Affects entry/exit prices in both backtest and live modes
1374
+
1375
+ ```typescript
1376
+ // More responsive to recent price changes (3 minutes)
1377
+ await setConfig({
1378
+ CC_AVG_PRICE_CANDLES_COUNT: 3,
1379
+ });
1380
+
1381
+ // More stable, less sensitive to spikes (10 minutes)
1382
+ await setConfig({
1383
+ CC_AVG_PRICE_CANDLES_COUNT: 10,
1384
+ });
1385
+ ```
1386
+
1387
+ ### When to Call `setConfig()`
1388
+
1389
+ Always call `setConfig()` **before** running any strategies to ensure configuration is applied:
1390
+
1391
+ ```typescript
1392
+ import { setConfig, Backtest, Live } from "backtest-kit";
1393
+
1394
+ // 1. Configure framework first
1395
+ await setConfig({
1396
+ CC_SCHEDULE_AWAIT_MINUTES: 90,
1397
+ CC_AVG_PRICE_CANDLES_COUNT: 7,
1398
+ });
1399
+
1400
+ // 2. Then run strategies
1401
+ Backtest.background("BTCUSDT", {
1402
+ strategyName: "my-strategy",
1403
+ exchangeName: "binance",
1404
+ frameName: "1d-backtest"
1405
+ });
1406
+
1407
+ Live.background("ETHUSDT", {
1408
+ strategyName: "my-strategy",
1409
+ exchangeName: "binance"
1410
+ });
1411
+ ```
1412
+
1413
+ ### Partial Configuration
1414
+
1415
+ You can update individual parameters without specifying all of them:
1416
+
1417
+ ```typescript
1418
+ // Only change candle count, keep other defaults
1419
+ await setConfig({
1420
+ CC_AVG_PRICE_CANDLES_COUNT: 8,
1421
+ });
1422
+
1423
+ // Later, only change timeout
1424
+ await setConfig({
1425
+ CC_SCHEDULE_AWAIT_MINUTES: 60,
1426
+ });
1427
+ ```
1428
+
1429
+ ---
1430
+
1065
1431
  ## βœ… Tested & Reliable
1066
1432
 
1067
- `backtest-kit` comes with a robust test suite covering:
1068
- - πŸ›‘οΈ **Validation**: Ensures all components (exchanges, strategies, frames, risk profiles) are properly configured. βœ…
1069
- - πŸš‘ **Recovery**: Handles edge cases like invalid signals or empty outputs. πŸ› οΈ
1070
- - πŸ”„ **Navigation**: Smoothly switches between backtest and live modes without errors. 🌐
1071
- - ⚑ **Performance**: Efficient memory usage and history management. πŸ“ˆ
1433
+ `backtest-kit` comes with **123 unit and integration tests** covering:
1072
1434
 
1073
- **109 unit and integration tests** covering:
1074
1435
  - Signal validation and throttling
1075
1436
  - PNL calculation with fees and slippage
1076
1437
  - Crash recovery and state persistence
1077
- - Callback execution order
1078
- - Markdown report generation
1438
+ - Callback execution order (onSchedule, onOpen, onActive, onClose, onCancel)
1439
+ - Markdown report generation (backtest, live, scheduled signals)
1079
1440
  - Walker strategy comparison
1080
1441
  - Heatmap portfolio analysis
1081
1442
  - Position sizing calculations
1082
1443
  - Risk management validation
1444
+ - Scheduled signals lifecycle and cancellation tracking
1083
1445
  - Event system
1084
1446
 
1085
1447
  ---