backtest-kit 3.1.0 → 3.1.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
@@ -441,6 +441,30 @@ Unlike cloud-based platforms, backtest-kit runs entirely in your environment. Yo
441
441
 
442
442
  The `backtest-kit` ecosystem extends beyond the core library, offering complementary packages and tools to enhance your trading system development experience:
443
443
 
444
+
445
+ ### @backtest-kit/cli
446
+
447
+ > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/cli)** 📟
448
+
449
+ The **@backtest-kit/cli** package is a zero-boilerplate CLI runner for backtest-kit strategies. Point it at your strategy file and run backtests, paper trading, or live bots — no infrastructure code required.
450
+
451
+ #### Key Features
452
+ - 🚀 **Zero Config**: Run a backtest with one command — no setup code needed
453
+ - 🔄 **Three Modes**: `--backtest`, `--paper`, `--live` with graceful SIGINT shutdown
454
+ - 💾 **Auto Cache**: Warms OHLCV candle cache for all intervals before the backtest starts
455
+ - 🌐 **Web Dashboard**: Launch `@backtest-kit/ui` with a single `--ui` flag
456
+ - 📬 **Telegram Alerts**: Formatted trade notifications with price charts via `--telegram`
457
+ - 🗂️ **Monorepo Ready**: Each strategy's `dump/`, `modules/`, and `template/` are automatically isolated by entry point directory
458
+
459
+ #### Use Case
460
+ The fastest way to run any backtest-kit strategy from the command line. Instead of writing boilerplate for storage, notifications, candle caching, and signal logging, add one dependency and wire up your `package.json` scripts. Works equally well for a single-strategy project or a monorepo with dozens of strategies in separate subdirectories.
461
+
462
+ #### Get Started
463
+ ```bash
464
+ npx -y @backtest-kit/cli --init
465
+ ```
466
+
467
+
444
468
  ### @backtest-kit/pinets
445
469
 
446
470
  > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/pinets)** 📜
@@ -531,9 +555,32 @@ npm install @backtest-kit/signals backtest-kit
531
555
  ```
532
556
 
533
557
 
558
+
559
+ ### @backtest-kit/graph
560
+
561
+ > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/graph)** 🔗
562
+
563
+ The **@backtest-kit/graph** package lets you compose backtest-kit computations as a typed directed acyclic graph (DAG). Define source nodes that fetch market data and output nodes that compute derived values — then resolve the whole graph in topological order with automatic parallelism.
564
+
565
+ #### Key Features
566
+ - 🔌 **DAG Execution**: Nodes are resolved bottom-up in topological order with `Promise.all` parallelism
567
+ - 🔒 **Type-Safe Values**: TypeScript infers the return type of every node through the graph via generics
568
+ - 🧱 **Two APIs**: Low-level `INode` for runtime/storage, high-level `sourceNode` + `outputNode` builders for authoring
569
+ - 💾 **DB-Ready Serialization**: `serialize` / `deserialize` convert the graph to a flat `IFlatNode[]` list with `id` / `nodeIds`
570
+ - 🌐 **Context-Aware Fetch**: `sourceNode` receives `(symbol, when, exchangeName)` from the execution context automatically
571
+
572
+ #### Use Case
573
+ Perfect for multi-timeframe strategies where multiple Pine Script or indicator computations must be combined. Instead of manually chaining async calls, define each computation as a node and let the graph resolve dependencies in parallel. Adding a new filter or timeframe requires no changes to the existing wiring.
574
+
575
+ #### Get Started
576
+ ```bash
577
+ npm install @backtest-kit/graph backtest-kit
578
+ ```
579
+
580
+
534
581
  ### @backtest-kit/sidekick
535
582
 
536
- > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/sidekick)** 🧿
583
+ > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/sidekick)** 🚀
537
584
 
538
585
  The **@backtest-kit/sidekick** package is the easiest way to create a new Backtest Kit trading bot project. Like create-react-app, but for algorithmic trading.
539
586
 
@@ -556,27 +603,6 @@ npm start
556
603
  ```
557
604
 
558
605
 
559
- ### @backtest-kit/graph
560
-
561
- > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/graph)** 🔗
562
-
563
- The **@backtest-kit/graph** package lets you compose backtest-kit computations as a typed directed acyclic graph (DAG). Define source nodes that fetch market data and output nodes that compute derived values — then resolve the whole graph in topological order with automatic parallelism.
564
-
565
- #### Key Features
566
- - 🔌 **DAG Execution**: Nodes are resolved bottom-up in topological order with `Promise.all` parallelism
567
- - 🔒 **Type-Safe Values**: TypeScript infers the return type of every node through the graph via generics
568
- - 🧱 **Two APIs**: Low-level `INode` for runtime/storage, high-level `sourceNode` + `outputNode` builders for authoring
569
- - 💾 **DB-Ready Serialization**: `serialize` / `deserialize` convert the graph to a flat `IFlatNode[]` list with `id` / `nodeIds`
570
- - 🌐 **Context-Aware Fetch**: `sourceNode` receives `(symbol, when, exchangeName)` from the execution context automatically
571
-
572
- #### Use Case
573
- Perfect for multi-timeframe strategies where multiple Pine Script or indicator computations must be combined. Instead of manually chaining async calls, define each computation as a node and let the graph resolve dependencies in parallel. Adding a new filter or timeframe requires no changes to the existing wiring.
574
-
575
- #### Get Started
576
- ```bash
577
- npm install @backtest-kit/graph backtest-kit
578
- ```
579
-
580
606
  ## 🤖 Are you a robot?
581
607
 
582
608
  **For language models**: Read extended description in [./LLMs.md](./LLMs.md)
package/build/index.cjs CHANGED
@@ -2905,8 +2905,9 @@ class ExchangeConnectionService {
2905
2905
  *
2906
2906
  * For signals with partial closes:
2907
2907
  * - Calculates weighted PNL: Σ(percent_i × pnl_i) for each partial + (remaining% × final_pnl)
2908
- * - Each partial close has its own fees and slippage
2909
- * - Total fees = 2 × (number of partial closes + 1 final close) × CC_PERCENT_FEE
2908
+ * - Each partial close has its own slippage
2909
+ * - Open fee is charged once; close fees are proportional to each partial's size
2910
+ * - Total fees = CC_PERCENT_FEE (open) + CC_PERCENT_FEE × 1 (closes sum to 100%) = 2 × CC_PERCENT_FEE
2910
2911
  *
2911
2912
  * Formula breakdown:
2912
2913
  * 1. Apply slippage to open/close prices (worse execution)
@@ -2953,7 +2954,8 @@ const toProfitLossDto = (signal, priceClose) => {
2953
2954
  // Calculate weighted PNL with partial closes
2954
2955
  if (signal._partial && signal._partial.length > 0) {
2955
2956
  let totalWeightedPnl = 0;
2956
- let totalFees = 0;
2957
+ // Open fee is paid once for the whole position
2958
+ let totalFees = GLOBAL_CONFIG.CC_PERCENT_FEE;
2957
2959
  // Calculate PNL for each partial close
2958
2960
  for (const partial of signal._partial) {
2959
2961
  const partialPercent = partial.percent;
@@ -2980,8 +2982,8 @@ const toProfitLossDto = (signal, priceClose) => {
2980
2982
  // Weight by percentage of position closed
2981
2983
  const weightedPnl = (partialPercent / 100) * partialPnl;
2982
2984
  totalWeightedPnl += weightedPnl;
2983
- // Each partial has fees for open + close (2 transactions)
2984
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
2985
+ // Close fee is proportional to the size of this partial
2986
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (partialPercent / 100);
2985
2987
  }
2986
2988
  // Calculate PNL for remaining position (if any)
2987
2989
  // Compute totalClosed from _partial array
@@ -3010,10 +3012,11 @@ const toProfitLossDto = (signal, priceClose) => {
3010
3012
  // Weight by remaining percentage
3011
3013
  const weightedRemainingPnl = (remainingPercent / 100) * remainingPnl;
3012
3014
  totalWeightedPnl += weightedRemainingPnl;
3013
- // Final close also has fees
3014
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
3015
+ // Close fee is proportional to the remaining size
3016
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (remainingPercent / 100);
3015
3017
  }
3016
3018
  // Subtract total fees from weighted PNL
3019
+ // totalFees = CC_PERCENT_FEE (open) + CC_PERCENT_FEE × 1 (all closes sum to 100%) = 2 × CC_PERCENT_FEE
3017
3020
  const pnlPercentage = totalWeightedPnl - totalFees;
3018
3021
  return {
3019
3022
  pnlPercentage,
@@ -30233,6 +30236,115 @@ function listenStrategyCommitOnce(filterFn, fn) {
30233
30236
  return strategyCommitSubject.filter(filterFn).once(fn);
30234
30237
  }
30235
30238
 
30239
+ const WARN_KB = 30;
30240
+ const DUMP_MESSAGES_METHOD_NAME = "dump.dumpMessages";
30241
+ /**
30242
+ * Dumps chat history and result data to markdown files in a structured directory.
30243
+ *
30244
+ * Creates a subfolder named after `resultId` inside `outputDir`.
30245
+ * If the subfolder already exists, the function returns early without overwriting.
30246
+ * Writes:
30247
+ * - `00_system_prompt.md` — system messages and output data summary
30248
+ * - `NN_user_message.md` — each user message as a separate file
30249
+ * - `NN_llm_output.md` — final LLM output data
30250
+ *
30251
+ * Warns via logger if any user message exceeds 30 KB.
30252
+ *
30253
+ * @param resultId - Unique identifier for the result (used as subfolder name)
30254
+ * @param history - Full chat history containing system, user, and assistant messages
30255
+ * @param result - Structured output data to include in the dump
30256
+ * @param outputDir - Base directory for output files (default: `./dump/strategy`)
30257
+ * @returns Promise that resolves when all files are written
30258
+ *
30259
+ * @example
30260
+ * ```typescript
30261
+ * import { dumpMessages } from "backtest-kit";
30262
+ *
30263
+ * await dumpMessages("result-123", history, { profit: 42 });
30264
+ * ```
30265
+ */
30266
+ async function dumpMessages(resultId, history, result, outputDir = "./dump/strategy") {
30267
+ bt.loggerService.info(DUMP_MESSAGES_METHOD_NAME, {
30268
+ resultId,
30269
+ outputDir,
30270
+ });
30271
+ // Extract system messages and system reminders from existing data
30272
+ const systemMessages = history.filter((m) => m.role === "system");
30273
+ const userMessages = history.filter((m) => m.role === "user");
30274
+ const subfolderPath = path.join(outputDir, String(resultId));
30275
+ try {
30276
+ await fs.access(subfolderPath);
30277
+ return;
30278
+ }
30279
+ catch {
30280
+ await fs.mkdir(subfolderPath, { recursive: true });
30281
+ }
30282
+ {
30283
+ let summary = "# Outline Result Summary\n";
30284
+ {
30285
+ summary += "\n";
30286
+ summary += `**ResultId**: ${resultId}\n`;
30287
+ summary += "\n";
30288
+ }
30289
+ if (result) {
30290
+ summary += "## Output Data\n\n";
30291
+ summary += "```json\n";
30292
+ summary += JSON.stringify(result, null, 2);
30293
+ summary += "\n```\n\n";
30294
+ }
30295
+ // Add system messages to summary
30296
+ if (systemMessages.length > 0) {
30297
+ summary += "## System Messages\n\n";
30298
+ systemMessages.forEach((msg, idx) => {
30299
+ summary += `### System Message ${idx + 1}\n\n`;
30300
+ summary += msg.content;
30301
+ summary += "\n";
30302
+ });
30303
+ }
30304
+ const summaryFile = path.join(subfolderPath, "00_system_prompt.md");
30305
+ await fs.writeFile(summaryFile, summary, "utf8");
30306
+ }
30307
+ {
30308
+ await Promise.all(Array.from(userMessages.entries()).map(async ([idx, message]) => {
30309
+ const messageNum = String(idx + 1).padStart(2, "0");
30310
+ const contentFileName = `${messageNum}_user_message.md`;
30311
+ const contentFilePath = path.join(subfolderPath, contentFileName);
30312
+ {
30313
+ const messageSizeBytes = Buffer.byteLength(message.content, "utf8");
30314
+ const messageSizeKb = Math.floor(messageSizeBytes / 1024);
30315
+ if (messageSizeKb > WARN_KB) {
30316
+ console.warn(`User message ${idx + 1} is ${messageSizeBytes} bytes (${messageSizeKb}kb), which exceeds warning limit`);
30317
+ bt.loggerService.warn(DUMP_MESSAGES_METHOD_NAME, {
30318
+ resultId,
30319
+ messageIndex: idx + 1,
30320
+ messageSizeBytes,
30321
+ messageSizeKb,
30322
+ });
30323
+ }
30324
+ }
30325
+ let content = `# User Input ${idx + 1}\n\n`;
30326
+ content += `**ResultId**: ${resultId}\n\n`;
30327
+ content += message.content;
30328
+ content += "\n";
30329
+ await fs.writeFile(contentFilePath, content, "utf8");
30330
+ }));
30331
+ }
30332
+ {
30333
+ const messageNum = String(userMessages.length + 1).padStart(2, "0");
30334
+ const contentFileName = `${messageNum}_llm_output.md`;
30335
+ const contentFilePath = path.join(subfolderPath, contentFileName);
30336
+ let content = "# Full Outline Result\n\n";
30337
+ content += `**ResultId**: ${resultId}\n\n`;
30338
+ if (result) {
30339
+ content += "## Output Data\n\n";
30340
+ content += "```json\n";
30341
+ content += JSON.stringify(result, null, 2);
30342
+ content += "\n```\n";
30343
+ }
30344
+ await fs.writeFile(contentFilePath, content, "utf8");
30345
+ }
30346
+ }
30347
+
30236
30348
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
30237
30349
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
30238
30350
  const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
@@ -37546,6 +37658,7 @@ exports.commitPartialLoss = commitPartialLoss;
37546
37658
  exports.commitPartialProfit = commitPartialProfit;
37547
37659
  exports.commitTrailingStop = commitTrailingStop;
37548
37660
  exports.commitTrailingTake = commitTrailingTake;
37661
+ exports.dumpMessages = dumpMessages;
37549
37662
  exports.emitters = emitters;
37550
37663
  exports.formatPrice = formatPrice;
37551
37664
  exports.formatQuantity = formatQuantity;
package/build/index.mjs CHANGED
@@ -2885,8 +2885,9 @@ class ExchangeConnectionService {
2885
2885
  *
2886
2886
  * For signals with partial closes:
2887
2887
  * - Calculates weighted PNL: Σ(percent_i × pnl_i) for each partial + (remaining% × final_pnl)
2888
- * - Each partial close has its own fees and slippage
2889
- * - Total fees = 2 × (number of partial closes + 1 final close) × CC_PERCENT_FEE
2888
+ * - Each partial close has its own slippage
2889
+ * - Open fee is charged once; close fees are proportional to each partial's size
2890
+ * - Total fees = CC_PERCENT_FEE (open) + CC_PERCENT_FEE × 1 (closes sum to 100%) = 2 × CC_PERCENT_FEE
2890
2891
  *
2891
2892
  * Formula breakdown:
2892
2893
  * 1. Apply slippage to open/close prices (worse execution)
@@ -2933,7 +2934,8 @@ const toProfitLossDto = (signal, priceClose) => {
2933
2934
  // Calculate weighted PNL with partial closes
2934
2935
  if (signal._partial && signal._partial.length > 0) {
2935
2936
  let totalWeightedPnl = 0;
2936
- let totalFees = 0;
2937
+ // Open fee is paid once for the whole position
2938
+ let totalFees = GLOBAL_CONFIG.CC_PERCENT_FEE;
2937
2939
  // Calculate PNL for each partial close
2938
2940
  for (const partial of signal._partial) {
2939
2941
  const partialPercent = partial.percent;
@@ -2960,8 +2962,8 @@ const toProfitLossDto = (signal, priceClose) => {
2960
2962
  // Weight by percentage of position closed
2961
2963
  const weightedPnl = (partialPercent / 100) * partialPnl;
2962
2964
  totalWeightedPnl += weightedPnl;
2963
- // Each partial has fees for open + close (2 transactions)
2964
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
2965
+ // Close fee is proportional to the size of this partial
2966
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (partialPercent / 100);
2965
2967
  }
2966
2968
  // Calculate PNL for remaining position (if any)
2967
2969
  // Compute totalClosed from _partial array
@@ -2990,10 +2992,11 @@ const toProfitLossDto = (signal, priceClose) => {
2990
2992
  // Weight by remaining percentage
2991
2993
  const weightedRemainingPnl = (remainingPercent / 100) * remainingPnl;
2992
2994
  totalWeightedPnl += weightedRemainingPnl;
2993
- // Final close also has fees
2994
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
2995
+ // Close fee is proportional to the remaining size
2996
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (remainingPercent / 100);
2995
2997
  }
2996
2998
  // Subtract total fees from weighted PNL
2999
+ // totalFees = CC_PERCENT_FEE (open) + CC_PERCENT_FEE × 1 (all closes sum to 100%) = 2 × CC_PERCENT_FEE
2997
3000
  const pnlPercentage = totalWeightedPnl - totalFees;
2998
3001
  return {
2999
3002
  pnlPercentage,
@@ -30213,6 +30216,115 @@ function listenStrategyCommitOnce(filterFn, fn) {
30213
30216
  return strategyCommitSubject.filter(filterFn).once(fn);
30214
30217
  }
30215
30218
 
30219
+ const WARN_KB = 30;
30220
+ const DUMP_MESSAGES_METHOD_NAME = "dump.dumpMessages";
30221
+ /**
30222
+ * Dumps chat history and result data to markdown files in a structured directory.
30223
+ *
30224
+ * Creates a subfolder named after `resultId` inside `outputDir`.
30225
+ * If the subfolder already exists, the function returns early without overwriting.
30226
+ * Writes:
30227
+ * - `00_system_prompt.md` — system messages and output data summary
30228
+ * - `NN_user_message.md` — each user message as a separate file
30229
+ * - `NN_llm_output.md` — final LLM output data
30230
+ *
30231
+ * Warns via logger if any user message exceeds 30 KB.
30232
+ *
30233
+ * @param resultId - Unique identifier for the result (used as subfolder name)
30234
+ * @param history - Full chat history containing system, user, and assistant messages
30235
+ * @param result - Structured output data to include in the dump
30236
+ * @param outputDir - Base directory for output files (default: `./dump/strategy`)
30237
+ * @returns Promise that resolves when all files are written
30238
+ *
30239
+ * @example
30240
+ * ```typescript
30241
+ * import { dumpMessages } from "backtest-kit";
30242
+ *
30243
+ * await dumpMessages("result-123", history, { profit: 42 });
30244
+ * ```
30245
+ */
30246
+ async function dumpMessages(resultId, history, result, outputDir = "./dump/strategy") {
30247
+ bt.loggerService.info(DUMP_MESSAGES_METHOD_NAME, {
30248
+ resultId,
30249
+ outputDir,
30250
+ });
30251
+ // Extract system messages and system reminders from existing data
30252
+ const systemMessages = history.filter((m) => m.role === "system");
30253
+ const userMessages = history.filter((m) => m.role === "user");
30254
+ const subfolderPath = path.join(outputDir, String(resultId));
30255
+ try {
30256
+ await fs__default.access(subfolderPath);
30257
+ return;
30258
+ }
30259
+ catch {
30260
+ await fs__default.mkdir(subfolderPath, { recursive: true });
30261
+ }
30262
+ {
30263
+ let summary = "# Outline Result Summary\n";
30264
+ {
30265
+ summary += "\n";
30266
+ summary += `**ResultId**: ${resultId}\n`;
30267
+ summary += "\n";
30268
+ }
30269
+ if (result) {
30270
+ summary += "## Output Data\n\n";
30271
+ summary += "```json\n";
30272
+ summary += JSON.stringify(result, null, 2);
30273
+ summary += "\n```\n\n";
30274
+ }
30275
+ // Add system messages to summary
30276
+ if (systemMessages.length > 0) {
30277
+ summary += "## System Messages\n\n";
30278
+ systemMessages.forEach((msg, idx) => {
30279
+ summary += `### System Message ${idx + 1}\n\n`;
30280
+ summary += msg.content;
30281
+ summary += "\n";
30282
+ });
30283
+ }
30284
+ const summaryFile = path.join(subfolderPath, "00_system_prompt.md");
30285
+ await fs__default.writeFile(summaryFile, summary, "utf8");
30286
+ }
30287
+ {
30288
+ await Promise.all(Array.from(userMessages.entries()).map(async ([idx, message]) => {
30289
+ const messageNum = String(idx + 1).padStart(2, "0");
30290
+ const contentFileName = `${messageNum}_user_message.md`;
30291
+ const contentFilePath = path.join(subfolderPath, contentFileName);
30292
+ {
30293
+ const messageSizeBytes = Buffer.byteLength(message.content, "utf8");
30294
+ const messageSizeKb = Math.floor(messageSizeBytes / 1024);
30295
+ if (messageSizeKb > WARN_KB) {
30296
+ console.warn(`User message ${idx + 1} is ${messageSizeBytes} bytes (${messageSizeKb}kb), which exceeds warning limit`);
30297
+ bt.loggerService.warn(DUMP_MESSAGES_METHOD_NAME, {
30298
+ resultId,
30299
+ messageIndex: idx + 1,
30300
+ messageSizeBytes,
30301
+ messageSizeKb,
30302
+ });
30303
+ }
30304
+ }
30305
+ let content = `# User Input ${idx + 1}\n\n`;
30306
+ content += `**ResultId**: ${resultId}\n\n`;
30307
+ content += message.content;
30308
+ content += "\n";
30309
+ await fs__default.writeFile(contentFilePath, content, "utf8");
30310
+ }));
30311
+ }
30312
+ {
30313
+ const messageNum = String(userMessages.length + 1).padStart(2, "0");
30314
+ const contentFileName = `${messageNum}_llm_output.md`;
30315
+ const contentFilePath = path.join(subfolderPath, contentFileName);
30316
+ let content = "# Full Outline Result\n\n";
30317
+ content += `**ResultId**: ${resultId}\n\n`;
30318
+ if (result) {
30319
+ content += "## Output Data\n\n";
30320
+ content += "```json\n";
30321
+ content += JSON.stringify(result, null, 2);
30322
+ content += "\n```\n";
30323
+ }
30324
+ await fs__default.writeFile(contentFilePath, content, "utf8");
30325
+ }
30326
+ }
30327
+
30216
30328
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
30217
30329
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
30218
30330
  const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
@@ -37472,4 +37584,4 @@ const set = (object, path, value) => {
37472
37584
  }
37473
37585
  };
37474
37586
 
37475
- export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
37587
+ export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "3.1.0",
3
+ "version": "3.1.1",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -6739,6 +6739,55 @@ declare function getRawCandles(symbol: string, interval: CandleInterval, limit?:
6739
6739
  */
6740
6740
  declare function getNextCandles(symbol: string, interval: CandleInterval, limit: number): Promise<ICandleData[]>;
6741
6741
 
6742
+ /** Unique identifier for a dump result. Can be a string or numeric ID. */
6743
+ type ResultId = string | number;
6744
+ /** Role of the message sender in LLM chat history. */
6745
+ type BaseRole = "assistant" | "system" | "user";
6746
+ /**
6747
+ * A single message in the chat history.
6748
+ * Used to represent system instructions, user input, or LLM responses.
6749
+ */
6750
+ interface Message<Role extends BaseRole = BaseRole> {
6751
+ /**
6752
+ * The sender of the message.
6753
+ * - "system": System instructions and context
6754
+ * - "user": User input and questions
6755
+ * - "assistant": LLM responses
6756
+ */
6757
+ role: Role;
6758
+ /**
6759
+ * The text content of the message.
6760
+ * Contains the actual message text sent or received.
6761
+ */
6762
+ content: string;
6763
+ }
6764
+ /**
6765
+ * Dumps chat history and result data to markdown files in a structured directory.
6766
+ *
6767
+ * Creates a subfolder named after `resultId` inside `outputDir`.
6768
+ * If the subfolder already exists, the function returns early without overwriting.
6769
+ * Writes:
6770
+ * - `00_system_prompt.md` — system messages and output data summary
6771
+ * - `NN_user_message.md` — each user message as a separate file
6772
+ * - `NN_llm_output.md` — final LLM output data
6773
+ *
6774
+ * Warns via logger if any user message exceeds 30 KB.
6775
+ *
6776
+ * @param resultId - Unique identifier for the result (used as subfolder name)
6777
+ * @param history - Full chat history containing system, user, and assistant messages
6778
+ * @param result - Structured output data to include in the dump
6779
+ * @param outputDir - Base directory for output files (default: `./dump/strategy`)
6780
+ * @returns Promise that resolves when all files are written
6781
+ *
6782
+ * @example
6783
+ * ```typescript
6784
+ * import { dumpMessages } from "backtest-kit";
6785
+ *
6786
+ * await dumpMessages("result-123", history, { profit: 42 });
6787
+ * ```
6788
+ */
6789
+ declare function dumpMessages<Data extends object = any>(resultId: ResultId, history: Message[], result: Data, outputDir?: string): Promise<void>;
6790
+
6742
6791
  /**
6743
6792
  * Portfolio heatmap statistics for a single symbol.
6744
6793
  * Aggregated metrics across all strategies for one trading pair.
@@ -20832,4 +20881,4 @@ declare const backtest: {
20832
20881
  loggerService: LoggerService;
20833
20882
  };
20834
20883
 
20835
- export { ActionBase, type ActivateScheduledCommit, type ActivateScheduledCommitNotification, type ActivePingContract, Backtest, type BacktestStatisticsModel, Breakeven, type BreakevenAvailableNotification, type BreakevenCommit, type BreakevenCommitNotification, type BreakevenContract, type BreakevenData, Cache, type CancelScheduledCommit, type CandleData, type CandleInterval, type ClosePendingCommit, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IActionSchema, type IActivateScheduledCommitRow, type IBidData, type IBreakevenCommitRow, type ICandleData, type ICommitRow, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IMarkdownDumpOptions, type INotificationUtils, type IOrderBookData, type IPartialLossCommitRow, type IPartialProfitCommitRow, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicAction, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskSignalRow, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingParams, type ISizingParamsATR, type ISizingParamsFixedPercentage, type ISizingParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStorageSignalRow, type IStorageUtils, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IStrategyTickResultWaiting, type ITrailingStopCommitRow, type ITrailingTakeCommitRow, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveStatisticsModel, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, MethodContextService, type MetricStats, Notification, NotificationBacktest, type NotificationData, NotificationLive, type NotificationModel, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossAvailableNotification, type PartialLossCommit, type PartialLossCommitNotification, type PartialLossContract, type PartialProfitAvailableNotification, type PartialProfitCommit, type PartialProfitCommitNotification, type PartialProfitContract, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, type ProgressBacktestContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type SchedulePingContract, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, Storage, StorageBacktest, type StorageData, StorageLive, Strategy, type StrategyActionType, type StrategyCancelReason, type StrategyCloseReason, type StrategyCommitContract, type StrategyEvent, type StrategyStatisticsModel, type TMarkdownBase, type TNotificationUtilsCtor, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TStorageUtilsCtor, type TickEvent, type TrailingStopCommit, type TrailingStopCommitNotification, type TrailingTakeCommit, type TrailingTakeCommitNotification, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
20884
+ export { ActionBase, type ActivateScheduledCommit, type ActivateScheduledCommitNotification, type ActivePingContract, Backtest, type BacktestStatisticsModel, Breakeven, type BreakevenAvailableNotification, type BreakevenCommit, type BreakevenCommitNotification, type BreakevenContract, type BreakevenData, Cache, type CancelScheduledCommit, type CandleData, type CandleInterval, type ClosePendingCommit, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IActionSchema, type IActivateScheduledCommitRow, type IBidData, type IBreakevenCommitRow, type ICandleData, type ICommitRow, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IMarkdownDumpOptions, type INotificationUtils, type IOrderBookData, type IPartialLossCommitRow, type IPartialProfitCommitRow, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicAction, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskSignalRow, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingParams, type ISizingParamsATR, type ISizingParamsFixedPercentage, type ISizingParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStorageSignalRow, type IStorageUtils, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IStrategyTickResultWaiting, type ITrailingStopCommitRow, type ITrailingTakeCommitRow, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveStatisticsModel, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, MethodContextService, type MetricStats, Notification, NotificationBacktest, type NotificationData, NotificationLive, type NotificationModel, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossAvailableNotification, type PartialLossCommit, type PartialLossCommitNotification, type PartialLossContract, type PartialProfitAvailableNotification, type PartialProfitCommit, type PartialProfitCommitNotification, type PartialProfitContract, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, type ProgressBacktestContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type SchedulePingContract, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, Storage, StorageBacktest, type StorageData, StorageLive, Strategy, type StrategyActionType, type StrategyCancelReason, type StrategyCloseReason, type StrategyCommitContract, type StrategyEvent, type StrategyStatisticsModel, type TMarkdownBase, type TNotificationUtilsCtor, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TStorageUtilsCtor, type TickEvent, type TrailingStopCommit, type TrailingStopCommitNotification, type TrailingTakeCommit, type TrailingTakeCommitNotification, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };