backtest-kit 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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 × (partial% / 100) × (closeWithSlip / openWithSlip)
2910
2911
  *
2911
2912
  * Formula breakdown:
2912
2913
  * 1. Apply slippage to open/close prices (worse execution)
@@ -2915,7 +2916,7 @@ class ExchangeConnectionService {
2915
2916
  * 2. Calculate raw PNL percentage
2916
2917
  * - LONG: ((closePrice - openPrice) / openPrice) * 100
2917
2918
  * - SHORT: ((openPrice - closePrice) / openPrice) * 100
2918
- * 3. Subtract total fees (0.1% * 2 = 0.2% per transaction)
2919
+ * 3. Subtract total fees: open fee + close fee adjusted for slippage-affected execution price
2919
2920
  *
2920
2921
  * @param signal - Closed signal with position details and optional partial history
2921
2922
  * @param priceClose - Actual close price at final exit
@@ -2953,67 +2954,49 @@ 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;
2959
+ // priceOpenWithSlippage is the same for all partials — compute once
2960
+ const priceOpenWithSlippage = signal.position === "long"
2961
+ ? priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
2962
+ : priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2957
2963
  // Calculate PNL for each partial close
2958
2964
  for (const partial of signal._partial) {
2959
2965
  const partialPercent = partial.percent;
2960
- const partialPrice = partial.price;
2961
- // Apply slippage to prices
2962
- let priceOpenWithSlippage;
2963
- let priceCloseWithSlippage;
2964
- if (signal.position === "long") {
2965
- priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2966
- priceCloseWithSlippage = partialPrice * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2967
- }
2968
- else {
2969
- priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2970
- priceCloseWithSlippage = partialPrice * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2971
- }
2966
+ const priceCloseWithSlippage = signal.position === "long"
2967
+ ? partial.price * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
2968
+ : partial.price * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2972
2969
  // Calculate PNL for this partial
2973
- let partialPnl;
2974
- if (signal.position === "long") {
2975
- partialPnl = ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100;
2976
- }
2977
- else {
2978
- partialPnl = ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
2979
- }
2970
+ const partialPnl = signal.position === "long"
2971
+ ? ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100
2972
+ : ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
2980
2973
  // Weight by percentage of position closed
2981
- const weightedPnl = (partialPercent / 100) * partialPnl;
2982
- totalWeightedPnl += weightedPnl;
2983
- // Each partial has fees for open + close (2 transactions)
2984
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
2974
+ totalWeightedPnl += (partialPercent / 100) * partialPnl;
2975
+ // Close fee is proportional to the size of this partial and adjusted for slippage
2976
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (partialPercent / 100) * (priceCloseWithSlippage / priceOpenWithSlippage);
2985
2977
  }
2986
2978
  // Calculate PNL for remaining position (if any)
2987
2979
  // Compute totalClosed from _partial array
2988
2980
  const totalClosed = signal._partial.reduce((sum, p) => sum + p.percent, 0);
2981
+ if (totalClosed > 100) {
2982
+ throw new Error(`Partial closes exceed 100%: ${totalClosed}% (signal id: ${signal.id})`);
2983
+ }
2989
2984
  const remainingPercent = 100 - totalClosed;
2990
2985
  if (remainingPercent > 0) {
2991
- // Apply slippage
2992
- let priceOpenWithSlippage;
2993
- let priceCloseWithSlippage;
2994
- if (signal.position === "long") {
2995
- priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2996
- priceCloseWithSlippage = priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2997
- }
2998
- else {
2999
- priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
3000
- priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
3001
- }
2986
+ const priceCloseWithSlippage = signal.position === "long"
2987
+ ? priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
2988
+ : priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
3002
2989
  // Calculate PNL for remaining
3003
- let remainingPnl;
3004
- if (signal.position === "long") {
3005
- remainingPnl = ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100;
3006
- }
3007
- else {
3008
- remainingPnl = ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
3009
- }
2990
+ const remainingPnl = signal.position === "long"
2991
+ ? ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100
2992
+ : ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
3010
2993
  // Weight by remaining percentage
3011
- const weightedRemainingPnl = (remainingPercent / 100) * remainingPnl;
3012
- totalWeightedPnl += weightedRemainingPnl;
3013
- // Final close also has fees
3014
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
2994
+ totalWeightedPnl += (remainingPercent / 100) * remainingPnl;
2995
+ // Close fee is proportional to the remaining size and adjusted for slippage
2996
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (remainingPercent / 100) * (priceCloseWithSlippage / priceOpenWithSlippage);
3015
2997
  }
3016
2998
  // Subtract total fees from weighted PNL
2999
+ // totalFees = CC_PERCENT_FEE (open) + Σ CC_PERCENT_FEE × (partialPercent/100) × (closeWithSlip/openWithSlip)
3017
3000
  const pnlPercentage = totalWeightedPnl - totalFees;
3018
3001
  return {
3019
3002
  pnlPercentage,
@@ -3034,8 +3017,8 @@ const toProfitLossDto = (signal, priceClose) => {
3034
3017
  priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
3035
3018
  priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
3036
3019
  }
3037
- // Применяем комиссию дважды (при открытии и закрытии)
3038
- const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
3020
+ // Открытие: комиссия от цены входа; закрытие: комиссия от фактической цены выхода (с учётом slippage)
3021
+ const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE * (1 + priceCloseWithSlippage / priceOpenWithSlippage);
3039
3022
  let pnlPercentage;
3040
3023
  if (signal.position === "long") {
3041
3024
  // LONG: прибыль при росте цены
@@ -30233,6 +30216,115 @@ function listenStrategyCommitOnce(filterFn, fn) {
30233
30216
  return strategyCommitSubject.filter(filterFn).once(fn);
30234
30217
  }
30235
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.access(subfolderPath);
30257
+ return;
30258
+ }
30259
+ catch {
30260
+ await fs.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.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.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.writeFile(contentFilePath, content, "utf8");
30325
+ }
30326
+ }
30327
+
30236
30328
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
30237
30329
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
30238
30330
  const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
@@ -37546,6 +37638,7 @@ exports.commitPartialLoss = commitPartialLoss;
37546
37638
  exports.commitPartialProfit = commitPartialProfit;
37547
37639
  exports.commitTrailingStop = commitTrailingStop;
37548
37640
  exports.commitTrailingTake = commitTrailingTake;
37641
+ exports.dumpMessages = dumpMessages;
37549
37642
  exports.emitters = emitters;
37550
37643
  exports.formatPrice = formatPrice;
37551
37644
  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 × (partial% / 100) × (closeWithSlip / openWithSlip)
2890
2891
  *
2891
2892
  * Formula breakdown:
2892
2893
  * 1. Apply slippage to open/close prices (worse execution)
@@ -2895,7 +2896,7 @@ class ExchangeConnectionService {
2895
2896
  * 2. Calculate raw PNL percentage
2896
2897
  * - LONG: ((closePrice - openPrice) / openPrice) * 100
2897
2898
  * - SHORT: ((openPrice - closePrice) / openPrice) * 100
2898
- * 3. Subtract total fees (0.1% * 2 = 0.2% per transaction)
2899
+ * 3. Subtract total fees: open fee + close fee adjusted for slippage-affected execution price
2899
2900
  *
2900
2901
  * @param signal - Closed signal with position details and optional partial history
2901
2902
  * @param priceClose - Actual close price at final exit
@@ -2933,67 +2934,49 @@ 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;
2939
+ // priceOpenWithSlippage is the same for all partials — compute once
2940
+ const priceOpenWithSlippage = signal.position === "long"
2941
+ ? priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
2942
+ : priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2937
2943
  // Calculate PNL for each partial close
2938
2944
  for (const partial of signal._partial) {
2939
2945
  const partialPercent = partial.percent;
2940
- const partialPrice = partial.price;
2941
- // Apply slippage to prices
2942
- let priceOpenWithSlippage;
2943
- let priceCloseWithSlippage;
2944
- if (signal.position === "long") {
2945
- priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2946
- priceCloseWithSlippage = partialPrice * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2947
- }
2948
- else {
2949
- priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2950
- priceCloseWithSlippage = partialPrice * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2951
- }
2946
+ const priceCloseWithSlippage = signal.position === "long"
2947
+ ? partial.price * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
2948
+ : partial.price * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2952
2949
  // Calculate PNL for this partial
2953
- let partialPnl;
2954
- if (signal.position === "long") {
2955
- partialPnl = ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100;
2956
- }
2957
- else {
2958
- partialPnl = ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
2959
- }
2950
+ const partialPnl = signal.position === "long"
2951
+ ? ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100
2952
+ : ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
2960
2953
  // Weight by percentage of position closed
2961
- const weightedPnl = (partialPercent / 100) * partialPnl;
2962
- totalWeightedPnl += weightedPnl;
2963
- // Each partial has fees for open + close (2 transactions)
2964
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
2954
+ totalWeightedPnl += (partialPercent / 100) * partialPnl;
2955
+ // Close fee is proportional to the size of this partial and adjusted for slippage
2956
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (partialPercent / 100) * (priceCloseWithSlippage / priceOpenWithSlippage);
2965
2957
  }
2966
2958
  // Calculate PNL for remaining position (if any)
2967
2959
  // Compute totalClosed from _partial array
2968
2960
  const totalClosed = signal._partial.reduce((sum, p) => sum + p.percent, 0);
2961
+ if (totalClosed > 100) {
2962
+ throw new Error(`Partial closes exceed 100%: ${totalClosed}% (signal id: ${signal.id})`);
2963
+ }
2969
2964
  const remainingPercent = 100 - totalClosed;
2970
2965
  if (remainingPercent > 0) {
2971
- // Apply slippage
2972
- let priceOpenWithSlippage;
2973
- let priceCloseWithSlippage;
2974
- if (signal.position === "long") {
2975
- priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2976
- priceCloseWithSlippage = priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2977
- }
2978
- else {
2979
- priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2980
- priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2981
- }
2966
+ const priceCloseWithSlippage = signal.position === "long"
2967
+ ? priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
2968
+ : priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
2982
2969
  // Calculate PNL for remaining
2983
- let remainingPnl;
2984
- if (signal.position === "long") {
2985
- remainingPnl = ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100;
2986
- }
2987
- else {
2988
- remainingPnl = ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
2989
- }
2970
+ const remainingPnl = signal.position === "long"
2971
+ ? ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100
2972
+ : ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
2990
2973
  // Weight by remaining percentage
2991
- const weightedRemainingPnl = (remainingPercent / 100) * remainingPnl;
2992
- totalWeightedPnl += weightedRemainingPnl;
2993
- // Final close also has fees
2994
- totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
2974
+ totalWeightedPnl += (remainingPercent / 100) * remainingPnl;
2975
+ // Close fee is proportional to the remaining size and adjusted for slippage
2976
+ totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (remainingPercent / 100) * (priceCloseWithSlippage / priceOpenWithSlippage);
2995
2977
  }
2996
2978
  // Subtract total fees from weighted PNL
2979
+ // totalFees = CC_PERCENT_FEE (open) + Σ CC_PERCENT_FEE × (partialPercent/100) × (closeWithSlip/openWithSlip)
2997
2980
  const pnlPercentage = totalWeightedPnl - totalFees;
2998
2981
  return {
2999
2982
  pnlPercentage,
@@ -3014,8 +2997,8 @@ const toProfitLossDto = (signal, priceClose) => {
3014
2997
  priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
3015
2998
  priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
3016
2999
  }
3017
- // Применяем комиссию дважды (при открытии и закрытии)
3018
- const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
3000
+ // Открытие: комиссия от цены входа; закрытие: комиссия от фактической цены выхода (с учётом slippage)
3001
+ const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE * (1 + priceCloseWithSlippage / priceOpenWithSlippage);
3019
3002
  let pnlPercentage;
3020
3003
  if (signal.position === "long") {
3021
3004
  // LONG: прибыль при росте цены
@@ -30213,6 +30196,115 @@ function listenStrategyCommitOnce(filterFn, fn) {
30213
30196
  return strategyCommitSubject.filter(filterFn).once(fn);
30214
30197
  }
30215
30198
 
30199
+ const WARN_KB = 30;
30200
+ const DUMP_MESSAGES_METHOD_NAME = "dump.dumpMessages";
30201
+ /**
30202
+ * Dumps chat history and result data to markdown files in a structured directory.
30203
+ *
30204
+ * Creates a subfolder named after `resultId` inside `outputDir`.
30205
+ * If the subfolder already exists, the function returns early without overwriting.
30206
+ * Writes:
30207
+ * - `00_system_prompt.md` — system messages and output data summary
30208
+ * - `NN_user_message.md` — each user message as a separate file
30209
+ * - `NN_llm_output.md` — final LLM output data
30210
+ *
30211
+ * Warns via logger if any user message exceeds 30 KB.
30212
+ *
30213
+ * @param resultId - Unique identifier for the result (used as subfolder name)
30214
+ * @param history - Full chat history containing system, user, and assistant messages
30215
+ * @param result - Structured output data to include in the dump
30216
+ * @param outputDir - Base directory for output files (default: `./dump/strategy`)
30217
+ * @returns Promise that resolves when all files are written
30218
+ *
30219
+ * @example
30220
+ * ```typescript
30221
+ * import { dumpMessages } from "backtest-kit";
30222
+ *
30223
+ * await dumpMessages("result-123", history, { profit: 42 });
30224
+ * ```
30225
+ */
30226
+ async function dumpMessages(resultId, history, result, outputDir = "./dump/strategy") {
30227
+ bt.loggerService.info(DUMP_MESSAGES_METHOD_NAME, {
30228
+ resultId,
30229
+ outputDir,
30230
+ });
30231
+ // Extract system messages and system reminders from existing data
30232
+ const systemMessages = history.filter((m) => m.role === "system");
30233
+ const userMessages = history.filter((m) => m.role === "user");
30234
+ const subfolderPath = path.join(outputDir, String(resultId));
30235
+ try {
30236
+ await fs__default.access(subfolderPath);
30237
+ return;
30238
+ }
30239
+ catch {
30240
+ await fs__default.mkdir(subfolderPath, { recursive: true });
30241
+ }
30242
+ {
30243
+ let summary = "# Outline Result Summary\n";
30244
+ {
30245
+ summary += "\n";
30246
+ summary += `**ResultId**: ${resultId}\n`;
30247
+ summary += "\n";
30248
+ }
30249
+ if (result) {
30250
+ summary += "## Output Data\n\n";
30251
+ summary += "```json\n";
30252
+ summary += JSON.stringify(result, null, 2);
30253
+ summary += "\n```\n\n";
30254
+ }
30255
+ // Add system messages to summary
30256
+ if (systemMessages.length > 0) {
30257
+ summary += "## System Messages\n\n";
30258
+ systemMessages.forEach((msg, idx) => {
30259
+ summary += `### System Message ${idx + 1}\n\n`;
30260
+ summary += msg.content;
30261
+ summary += "\n";
30262
+ });
30263
+ }
30264
+ const summaryFile = path.join(subfolderPath, "00_system_prompt.md");
30265
+ await fs__default.writeFile(summaryFile, summary, "utf8");
30266
+ }
30267
+ {
30268
+ await Promise.all(Array.from(userMessages.entries()).map(async ([idx, message]) => {
30269
+ const messageNum = String(idx + 1).padStart(2, "0");
30270
+ const contentFileName = `${messageNum}_user_message.md`;
30271
+ const contentFilePath = path.join(subfolderPath, contentFileName);
30272
+ {
30273
+ const messageSizeBytes = Buffer.byteLength(message.content, "utf8");
30274
+ const messageSizeKb = Math.floor(messageSizeBytes / 1024);
30275
+ if (messageSizeKb > WARN_KB) {
30276
+ console.warn(`User message ${idx + 1} is ${messageSizeBytes} bytes (${messageSizeKb}kb), which exceeds warning limit`);
30277
+ bt.loggerService.warn(DUMP_MESSAGES_METHOD_NAME, {
30278
+ resultId,
30279
+ messageIndex: idx + 1,
30280
+ messageSizeBytes,
30281
+ messageSizeKb,
30282
+ });
30283
+ }
30284
+ }
30285
+ let content = `# User Input ${idx + 1}\n\n`;
30286
+ content += `**ResultId**: ${resultId}\n\n`;
30287
+ content += message.content;
30288
+ content += "\n";
30289
+ await fs__default.writeFile(contentFilePath, content, "utf8");
30290
+ }));
30291
+ }
30292
+ {
30293
+ const messageNum = String(userMessages.length + 1).padStart(2, "0");
30294
+ const contentFileName = `${messageNum}_llm_output.md`;
30295
+ const contentFilePath = path.join(subfolderPath, contentFileName);
30296
+ let content = "# Full Outline Result\n\n";
30297
+ content += `**ResultId**: ${resultId}\n\n`;
30298
+ if (result) {
30299
+ content += "## Output Data\n\n";
30300
+ content += "```json\n";
30301
+ content += JSON.stringify(result, null, 2);
30302
+ content += "\n```\n";
30303
+ }
30304
+ await fs__default.writeFile(contentFilePath, content, "utf8");
30305
+ }
30306
+ }
30307
+
30216
30308
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
30217
30309
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
30218
30310
  const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
@@ -37472,4 +37564,4 @@ const set = (object, path, value) => {
37472
37564
  }
37473
37565
  };
37474
37566
 
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 };
37567
+ 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.2.0",
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 };