backtest-kit 1.4.11 → 1.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.mjs CHANGED
@@ -5,6 +5,7 @@ import fs, { mkdir, writeFile } from 'fs/promises';
5
5
  import path, { join } from 'path';
6
6
  import crypto from 'crypto';
7
7
  import os from 'os';
8
+ import { promises } from 'fs';
8
9
 
9
10
  const GLOBAL_CONFIG = {
10
11
  /**
@@ -160,6 +161,7 @@ const markdownServices$1 = {
160
161
  walkerMarkdownService: Symbol('walkerMarkdownService'),
161
162
  heatMarkdownService: Symbol('heatMarkdownService'),
162
163
  partialMarkdownService: Symbol('partialMarkdownService'),
164
+ outlineMarkdownService: Symbol('outlineMarkdownService'),
163
165
  };
164
166
  const validationServices$1 = {
165
167
  exchangeValidationService: Symbol('exchangeValidationService'),
@@ -10460,6 +10462,11 @@ const columns = [
10460
10462
  label: "Symbol",
10461
10463
  format: (data) => data.symbol,
10462
10464
  },
10465
+ {
10466
+ key: "strategyName",
10467
+ label: "Strategy",
10468
+ format: (data) => data.strategyName,
10469
+ },
10463
10470
  {
10464
10471
  key: "signalId",
10465
10472
  label: "Signal ID",
@@ -10494,7 +10501,7 @@ const columns = [
10494
10501
  /** Maximum number of events to store in partial reports */
10495
10502
  const MAX_EVENTS = 250;
10496
10503
  /**
10497
- * Storage class for accumulating partial profit/loss events per symbol.
10504
+ * Storage class for accumulating partial profit/loss events per symbol-strategy pair.
10498
10505
  * Maintains a chronological list of profit and loss level events.
10499
10506
  */
10500
10507
  class ReportStorage {
@@ -10505,17 +10512,17 @@ class ReportStorage {
10505
10512
  /**
10506
10513
  * Adds a profit event to the storage.
10507
10514
  *
10508
- * @param symbol - Trading pair symbol
10509
10515
  * @param data - Signal row data
10510
10516
  * @param currentPrice - Current market price
10511
10517
  * @param level - Profit level reached
10512
10518
  * @param backtest - True if backtest mode
10513
10519
  */
10514
- addProfitEvent(symbol, data, currentPrice, level, backtest, timestamp) {
10520
+ addProfitEvent(data, currentPrice, level, backtest, timestamp) {
10515
10521
  this._eventList.push({
10516
10522
  timestamp,
10517
10523
  action: "profit",
10518
- symbol,
10524
+ symbol: data.symbol,
10525
+ strategyName: data.strategyName,
10519
10526
  signalId: data.id,
10520
10527
  position: data.position,
10521
10528
  currentPrice,
@@ -10530,17 +10537,17 @@ class ReportStorage {
10530
10537
  /**
10531
10538
  * Adds a loss event to the storage.
10532
10539
  *
10533
- * @param symbol - Trading pair symbol
10534
10540
  * @param data - Signal row data
10535
10541
  * @param currentPrice - Current market price
10536
10542
  * @param level - Loss level reached
10537
10543
  * @param backtest - True if backtest mode
10538
10544
  */
10539
- addLossEvent(symbol, data, currentPrice, level, backtest, timestamp) {
10545
+ addLossEvent(data, currentPrice, level, backtest, timestamp) {
10540
10546
  this._eventList.push({
10541
10547
  timestamp,
10542
10548
  action: "loss",
10543
- symbol,
10549
+ symbol: data.symbol,
10550
+ strategyName: data.strategyName,
10544
10551
  signalId: data.id,
10545
10552
  position: data.position,
10546
10553
  currentPrice,
@@ -10576,35 +10583,37 @@ class ReportStorage {
10576
10583
  };
10577
10584
  }
10578
10585
  /**
10579
- * Generates markdown report with all partial events for a symbol (View).
10586
+ * Generates markdown report with all partial events for a symbol-strategy pair (View).
10580
10587
  *
10581
10588
  * @param symbol - Trading pair symbol
10589
+ * @param strategyName - Strategy name
10582
10590
  * @returns Markdown formatted report with all events
10583
10591
  */
10584
- async getReport(symbol) {
10592
+ async getReport(symbol, strategyName) {
10585
10593
  const stats = await this.getData();
10586
10594
  if (stats.totalEvents === 0) {
10587
- return str.newline(`# Partial Profit/Loss Report: ${symbol}`, "", "No partial profit/loss events recorded yet.");
10595
+ return str.newline(`# Partial Profit/Loss Report: ${symbol}:${strategyName}`, "", "No partial profit/loss events recorded yet.");
10588
10596
  }
10589
10597
  const header = columns.map((col) => col.label);
10590
10598
  const separator = columns.map(() => "---");
10591
10599
  const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
10592
10600
  const tableData = [header, separator, ...rows];
10593
10601
  const table = str.newline(tableData.map((row) => `| ${row.join(" | ")} |`));
10594
- return str.newline(`# Partial Profit/Loss Report: ${symbol}`, "", table, "", `**Total events:** ${stats.totalEvents}`, `**Profit events:** ${stats.totalProfit}`, `**Loss events:** ${stats.totalLoss}`);
10602
+ return str.newline(`# Partial Profit/Loss Report: ${symbol}:${strategyName}`, "", table, "", `**Total events:** ${stats.totalEvents}`, `**Profit events:** ${stats.totalProfit}`, `**Loss events:** ${stats.totalLoss}`);
10595
10603
  }
10596
10604
  /**
10597
- * Saves symbol report to disk.
10605
+ * Saves symbol-strategy report to disk.
10598
10606
  *
10599
10607
  * @param symbol - Trading pair symbol
10608
+ * @param strategyName - Strategy name
10600
10609
  * @param path - Directory path to save report (default: "./dump/partial")
10601
10610
  */
10602
- async dump(symbol, path = "./dump/partial") {
10603
- const markdown = await this.getReport(symbol);
10611
+ async dump(symbol, strategyName, path = "./dump/partial") {
10612
+ const markdown = await this.getReport(symbol, strategyName);
10604
10613
  try {
10605
10614
  const dir = join(process.cwd(), path);
10606
10615
  await mkdir(dir, { recursive: true });
10607
- const filename = `${symbol}.md`;
10616
+ const filename = `${symbol}_${strategyName}.md`;
10608
10617
  const filepath = join(dir, filename);
10609
10618
  await writeFile(filepath, markdown, "utf-8");
10610
10619
  console.log(`Partial profit/loss report saved: ${filepath}`);
@@ -10619,10 +10628,10 @@ class ReportStorage {
10619
10628
  *
10620
10629
  * Features:
10621
10630
  * - Listens to partial profit and loss events via partialProfitSubject/partialLossSubject
10622
- * - Accumulates all events (profit, loss) per symbol
10631
+ * - Accumulates all events (profit, loss) per symbol-strategy pair
10623
10632
  * - Generates markdown tables with detailed event information
10624
10633
  * - Provides statistics (total profit/loss events)
10625
- * - Saves reports to disk in dump/partial/{symbol}.md
10634
+ * - Saves reports to disk in dump/partial/{symbol}_{strategyName}.md
10626
10635
  *
10627
10636
  * @example
10628
10637
  * ```typescript
@@ -10632,7 +10641,7 @@ class ReportStorage {
10632
10641
  * // No manual callback setup needed
10633
10642
  *
10634
10643
  * // Later: generate and save report
10635
- * await service.dump("BTCUSDT");
10644
+ * await service.dump("BTCUSDT", "my-strategy");
10636
10645
  * ```
10637
10646
  */
10638
10647
  class PartialMarkdownService {
@@ -10640,10 +10649,10 @@ class PartialMarkdownService {
10640
10649
  /** Logger service for debug output */
10641
10650
  this.loggerService = inject(TYPES.loggerService);
10642
10651
  /**
10643
- * Memoized function to get or create ReportStorage for a symbol.
10644
- * Each symbol gets its own isolated storage instance.
10652
+ * Memoized function to get or create ReportStorage for a symbol-strategy pair.
10653
+ * Each symbol-strategy combination gets its own isolated storage instance.
10645
10654
  */
10646
- this.getStorage = memoize(([symbol]) => `${symbol}`, () => new ReportStorage());
10655
+ this.getStorage = memoize(([symbol, strategyName]) => JSON.stringify([symbol, strategyName]), () => new ReportStorage());
10647
10656
  /**
10648
10657
  * Processes profit events and accumulates them.
10649
10658
  * Should be called from partialProfitSubject subscription.
@@ -10660,8 +10669,8 @@ class PartialMarkdownService {
10660
10669
  this.loggerService.log("partialMarkdownService tickProfit", {
10661
10670
  data,
10662
10671
  });
10663
- const storage = this.getStorage(data.symbol);
10664
- storage.addProfitEvent(data.symbol, data.data, data.currentPrice, data.level, data.backtest, data.timestamp);
10672
+ const storage = this.getStorage(data.symbol, data.data.strategyName);
10673
+ storage.addProfitEvent(data.data, data.currentPrice, data.level, data.backtest, data.timestamp);
10665
10674
  };
10666
10675
  /**
10667
10676
  * Processes loss events and accumulates them.
@@ -10679,101 +10688,113 @@ class PartialMarkdownService {
10679
10688
  this.loggerService.log("partialMarkdownService tickLoss", {
10680
10689
  data,
10681
10690
  });
10682
- const storage = this.getStorage(data.symbol);
10683
- storage.addLossEvent(data.symbol, data.data, data.currentPrice, data.level, data.backtest, data.timestamp);
10691
+ const storage = this.getStorage(data.symbol, data.data.strategyName);
10692
+ storage.addLossEvent(data.data, data.currentPrice, data.level, data.backtest, data.timestamp);
10684
10693
  };
10685
10694
  /**
10686
- * Gets statistical data from all partial profit/loss events for a symbol.
10695
+ * Gets statistical data from all partial profit/loss events for a symbol-strategy pair.
10687
10696
  * Delegates to ReportStorage.getData().
10688
10697
  *
10689
10698
  * @param symbol - Trading pair symbol to get data for
10699
+ * @param strategyName - Strategy name to get data for
10690
10700
  * @returns Statistical data object with all metrics
10691
10701
  *
10692
10702
  * @example
10693
10703
  * ```typescript
10694
10704
  * const service = new PartialMarkdownService();
10695
- * const stats = await service.getData("BTCUSDT");
10705
+ * const stats = await service.getData("BTCUSDT", "my-strategy");
10696
10706
  * console.log(stats.totalProfit, stats.totalLoss);
10697
10707
  * ```
10698
10708
  */
10699
- this.getData = async (symbol) => {
10709
+ this.getData = async (symbol, strategyName) => {
10700
10710
  this.loggerService.log("partialMarkdownService getData", {
10701
10711
  symbol,
10712
+ strategyName,
10702
10713
  });
10703
- const storage = this.getStorage(symbol);
10714
+ const storage = this.getStorage(symbol, strategyName);
10704
10715
  return storage.getData();
10705
10716
  };
10706
10717
  /**
10707
- * Generates markdown report with all partial events for a symbol.
10718
+ * Generates markdown report with all partial events for a symbol-strategy pair.
10708
10719
  * Delegates to ReportStorage.getReport().
10709
10720
  *
10710
10721
  * @param symbol - Trading pair symbol to generate report for
10722
+ * @param strategyName - Strategy name to generate report for
10711
10723
  * @returns Markdown formatted report string with table of all events
10712
10724
  *
10713
10725
  * @example
10714
10726
  * ```typescript
10715
10727
  * const service = new PartialMarkdownService();
10716
- * const markdown = await service.getReport("BTCUSDT");
10728
+ * const markdown = await service.getReport("BTCUSDT", "my-strategy");
10717
10729
  * console.log(markdown);
10718
10730
  * ```
10719
10731
  */
10720
- this.getReport = async (symbol) => {
10732
+ this.getReport = async (symbol, strategyName) => {
10721
10733
  this.loggerService.log("partialMarkdownService getReport", {
10722
10734
  symbol,
10735
+ strategyName,
10723
10736
  });
10724
- const storage = this.getStorage(symbol);
10725
- return storage.getReport(symbol);
10737
+ const storage = this.getStorage(symbol, strategyName);
10738
+ return storage.getReport(symbol, strategyName);
10726
10739
  };
10727
10740
  /**
10728
- * Saves symbol report to disk.
10741
+ * Saves symbol-strategy report to disk.
10729
10742
  * Creates directory if it doesn't exist.
10730
10743
  * Delegates to ReportStorage.dump().
10731
10744
  *
10732
10745
  * @param symbol - Trading pair symbol to save report for
10746
+ * @param strategyName - Strategy name to save report for
10733
10747
  * @param path - Directory path to save report (default: "./dump/partial")
10734
10748
  *
10735
10749
  * @example
10736
10750
  * ```typescript
10737
10751
  * const service = new PartialMarkdownService();
10738
10752
  *
10739
- * // Save to default path: ./dump/partial/BTCUSDT.md
10740
- * await service.dump("BTCUSDT");
10753
+ * // Save to default path: ./dump/partial/BTCUSDT_my-strategy.md
10754
+ * await service.dump("BTCUSDT", "my-strategy");
10741
10755
  *
10742
- * // Save to custom path: ./custom/path/BTCUSDT.md
10743
- * await service.dump("BTCUSDT", "./custom/path");
10756
+ * // Save to custom path: ./custom/path/BTCUSDT_my-strategy.md
10757
+ * await service.dump("BTCUSDT", "my-strategy", "./custom/path");
10744
10758
  * ```
10745
10759
  */
10746
- this.dump = async (symbol, path = "./dump/partial") => {
10760
+ this.dump = async (symbol, strategyName, path = "./dump/partial") => {
10747
10761
  this.loggerService.log("partialMarkdownService dump", {
10748
10762
  symbol,
10763
+ strategyName,
10749
10764
  path,
10750
10765
  });
10751
- const storage = this.getStorage(symbol);
10752
- await storage.dump(symbol, path);
10766
+ const storage = this.getStorage(symbol, strategyName);
10767
+ await storage.dump(symbol, strategyName, path);
10753
10768
  };
10754
10769
  /**
10755
10770
  * Clears accumulated event data from storage.
10756
- * If symbol is provided, clears only that symbol's data.
10757
- * If symbol is omitted, clears all symbols' data.
10771
+ * If ctx is provided, clears only that specific symbol-strategy pair's data.
10772
+ * If nothing is provided, clears all data.
10758
10773
  *
10759
- * @param symbol - Optional symbol to clear specific symbol data
10774
+ * @param ctx - Optional context with symbol and strategyName
10760
10775
  *
10761
10776
  * @example
10762
10777
  * ```typescript
10763
10778
  * const service = new PartialMarkdownService();
10764
10779
  *
10765
- * // Clear specific symbol data
10766
- * await service.clear("BTCUSDT");
10780
+ * // Clear specific symbol-strategy pair
10781
+ * await service.clear({ symbol: "BTCUSDT", strategyName: "my-strategy" });
10767
10782
  *
10768
- * // Clear all symbols' data
10783
+ * // Clear all data
10769
10784
  * await service.clear();
10770
10785
  * ```
10771
10786
  */
10772
- this.clear = async (symbol) => {
10787
+ this.clear = async (ctx) => {
10773
10788
  this.loggerService.log("partialMarkdownService clear", {
10774
- symbol,
10789
+ ctx,
10775
10790
  });
10776
- this.getStorage.clear(symbol);
10791
+ if (ctx) {
10792
+ const key = JSON.stringify([ctx.symbol, ctx.strategyName]);
10793
+ this.getStorage.clear(key);
10794
+ }
10795
+ else {
10796
+ this.getStorage.clear();
10797
+ }
10777
10798
  };
10778
10799
  /**
10779
10800
  * Initializes the service by subscribing to partial profit/loss events.
@@ -10904,6 +10925,145 @@ class PartialGlobalService {
10904
10925
  }
10905
10926
  }
10906
10927
 
10928
+ /**
10929
+ * Warning threshold for message size in kilobytes.
10930
+ * Messages exceeding this size trigger console warnings.
10931
+ */
10932
+ const WARN_KB = 100;
10933
+ /**
10934
+ * Internal function for dumping signal data to markdown files.
10935
+ * Creates a directory structure with system prompts, user messages, and LLM output.
10936
+ *
10937
+ * @param signalId - Unique identifier for the result
10938
+ * @param history - Array of message models from LLM conversation
10939
+ * @param signal - Signal DTO with trade parameters
10940
+ * @param outputDir - Output directory path (default: "./dump/strategy")
10941
+ * @returns Promise that resolves when all files are written
10942
+ */
10943
+ const DUMP_SIGNAL_FN = async (signalId, history, signal, outputDir = "./dump/strategy") => {
10944
+ // Extract system messages and system reminders from existing data
10945
+ const systemMessages = history.filter((m) => m.role === "system");
10946
+ const userMessages = history.filter((m) => m.role === "user");
10947
+ const subfolderPath = path.join(outputDir, String(signalId));
10948
+ try {
10949
+ await promises.access(subfolderPath);
10950
+ return;
10951
+ }
10952
+ catch {
10953
+ await promises.mkdir(subfolderPath, { recursive: true });
10954
+ }
10955
+ {
10956
+ let summary = "# Outline Result Summary\n";
10957
+ {
10958
+ summary += "\n";
10959
+ summary += `**ResultId**: ${String(signalId)}\n`;
10960
+ summary += "\n";
10961
+ }
10962
+ if (signal) {
10963
+ summary += "## Output Data\n\n";
10964
+ summary += "```json\n";
10965
+ summary += JSON.stringify(signal, null, 2);
10966
+ summary += "\n```\n\n";
10967
+ }
10968
+ // Add system messages to summary
10969
+ if (systemMessages.length > 0) {
10970
+ summary += "## System Messages\n\n";
10971
+ systemMessages.forEach((msg, idx) => {
10972
+ summary += `### System Message ${idx + 1}\n\n`;
10973
+ summary += msg.content;
10974
+ summary += "\n";
10975
+ });
10976
+ }
10977
+ const summaryFile = path.join(subfolderPath, "00_system_prompt.md");
10978
+ await promises.writeFile(summaryFile, summary, "utf8");
10979
+ }
10980
+ {
10981
+ await Promise.all(Array.from(userMessages.entries()).map(async ([idx, message]) => {
10982
+ const messageNum = String(idx + 1).padStart(2, "0");
10983
+ const contentFileName = `${messageNum}_user_message.md`;
10984
+ const contentFilePath = path.join(subfolderPath, contentFileName);
10985
+ {
10986
+ const messageSizeBytes = Buffer.byteLength(message.content, "utf8");
10987
+ const messageSizeKb = Math.floor(messageSizeBytes / 1024);
10988
+ if (messageSizeKb > WARN_KB) {
10989
+ console.warn(`User message ${idx + 1} is ${messageSizeBytes} bytes (${messageSizeKb}kb), which exceeds warning limit`);
10990
+ }
10991
+ }
10992
+ let content = `# User Input ${idx + 1}\n\n`;
10993
+ content += `**ResultId**: ${String(signalId)}\n\n`;
10994
+ content += message.content;
10995
+ content += "\n";
10996
+ await promises.writeFile(contentFilePath, content, "utf8");
10997
+ }));
10998
+ }
10999
+ {
11000
+ const messageNum = String(userMessages.length + 1).padStart(2, "0");
11001
+ const contentFileName = `${messageNum}_llm_output.md`;
11002
+ const contentFilePath = path.join(subfolderPath, contentFileName);
11003
+ let content = "# Full Outline Result\n\n";
11004
+ content += `**ResultId**: ${String(signalId)}\n\n`;
11005
+ if (signal) {
11006
+ content += "## Output Data\n\n";
11007
+ content += "```json\n";
11008
+ content += JSON.stringify(signal, null, 2);
11009
+ content += "\n```\n";
11010
+ }
11011
+ await promises.writeFile(contentFilePath, content, "utf8");
11012
+ }
11013
+ };
11014
+ /**
11015
+ * Service for generating markdown documentation from LLM outline results.
11016
+ * Used by AI Strategy Optimizer to save debug logs and conversation history.
11017
+ *
11018
+ * Creates directory structure:
11019
+ * - ./dump/strategy/{signalId}/00_system_prompt.md - System messages and output data
11020
+ * - ./dump/strategy/{signalId}/01_user_message.md - First user input
11021
+ * - ./dump/strategy/{signalId}/02_user_message.md - Second user input
11022
+ * - ./dump/strategy/{signalId}/XX_llm_output.md - Final LLM output
11023
+ */
11024
+ class OutlineMarkdownService {
11025
+ constructor() {
11026
+ /** Logger service injected via DI */
11027
+ this.loggerService = inject(TYPES.loggerService);
11028
+ /**
11029
+ * Dumps signal data and conversation history to markdown files.
11030
+ * Skips if directory already exists to avoid overwriting previous results.
11031
+ *
11032
+ * Generated files:
11033
+ * - 00_system_prompt.md - System messages and output summary
11034
+ * - XX_user_message.md - Each user message in separate file (numbered)
11035
+ * - XX_llm_output.md - Final LLM output with signal data
11036
+ *
11037
+ * @param signalId - Unique identifier for the result (used as directory name)
11038
+ * @param history - Array of message models from LLM conversation
11039
+ * @param signal - Signal DTO with trade parameters (priceOpen, TP, SL, etc.)
11040
+ * @param outputDir - Output directory path (default: "./dump/strategy")
11041
+ * @returns Promise that resolves when all files are written
11042
+ *
11043
+ * @example
11044
+ * ```typescript
11045
+ * await outlineService.dumpSignal(
11046
+ * "strategy-1",
11047
+ * conversationHistory,
11048
+ * { position: "long", priceTakeProfit: 51000, priceStopLoss: 49000, minuteEstimatedTime: 60 }
11049
+ * );
11050
+ * // Creates: ./dump/strategy/strategy-1/00_system_prompt.md
11051
+ * // ./dump/strategy/strategy-1/01_user_message.md
11052
+ * // ./dump/strategy/strategy-1/02_llm_output.md
11053
+ * ```
11054
+ */
11055
+ this.dumpSignal = async (signalId, history, signal, outputDir = "./dump/strategy") => {
11056
+ this.loggerService.log("outlineMarkdownService dumpSignal", {
11057
+ signalId,
11058
+ history,
11059
+ signal,
11060
+ outputDir,
11061
+ });
11062
+ return await DUMP_SIGNAL_FN(signalId, history, signal, outputDir);
11063
+ };
11064
+ }
11065
+ }
11066
+
10907
11067
  {
10908
11068
  provide(TYPES.loggerService, () => new LoggerService());
10909
11069
  }
@@ -10961,6 +11121,7 @@ class PartialGlobalService {
10961
11121
  provide(TYPES.walkerMarkdownService, () => new WalkerMarkdownService());
10962
11122
  provide(TYPES.heatMarkdownService, () => new HeatMarkdownService());
10963
11123
  provide(TYPES.partialMarkdownService, () => new PartialMarkdownService());
11124
+ provide(TYPES.outlineMarkdownService, () => new OutlineMarkdownService());
10964
11125
  }
10965
11126
  {
10966
11127
  provide(TYPES.exchangeValidationService, () => new ExchangeValidationService());
@@ -11032,6 +11193,7 @@ const markdownServices = {
11032
11193
  walkerMarkdownService: inject(TYPES.walkerMarkdownService),
11033
11194
  heatMarkdownService: inject(TYPES.heatMarkdownService),
11034
11195
  partialMarkdownService: inject(TYPES.partialMarkdownService),
11196
+ outlineMarkdownService: inject(TYPES.outlineMarkdownService),
11035
11197
  };
11036
11198
  const validationServices = {
11037
11199
  exchangeValidationService: inject(TYPES.exchangeValidationService),
@@ -12698,6 +12860,83 @@ async function getMode() {
12698
12860
  return bt ? "backtest" : "live";
12699
12861
  }
12700
12862
 
12863
+ const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
12864
+ /**
12865
+ * Dumps signal data and LLM conversation history to markdown files.
12866
+ * Used by AI-powered strategies to save debug logs for analysis.
12867
+ *
12868
+ * Creates a directory structure with:
12869
+ * - 00_system_prompt.md - System messages and output summary
12870
+ * - XX_user_message.md - Each user message in separate file (numbered)
12871
+ * - XX_llm_output.md - Final LLM output with signal data
12872
+ *
12873
+ * Skips if directory already exists to avoid overwriting previous results.
12874
+ *
12875
+ * @param signalId - Unique identifier for the result (used as directory name, e.g., UUID)
12876
+ * @param history - Array of message models from LLM conversation
12877
+ * @param signal - Signal DTO returned by LLM (position, priceOpen, TP, SL, etc.)
12878
+ * @param outputDir - Output directory path (default: "./dump/strategy")
12879
+ * @returns Promise that resolves when all files are written
12880
+ *
12881
+ * @example
12882
+ * ```typescript
12883
+ * import { dumpSignal, getCandles } from "backtest-kit";
12884
+ * import { v4 as uuid } from "uuid";
12885
+ *
12886
+ * addStrategy({
12887
+ * strategyName: "llm-strategy",
12888
+ * interval: "5m",
12889
+ * getSignal: async (symbol) => {
12890
+ * const messages = [];
12891
+ *
12892
+ * // Build multi-timeframe analysis conversation
12893
+ * const candles1h = await getCandles(symbol, "1h", 24);
12894
+ * messages.push(
12895
+ * { role: "user", content: `Analyze 1h trend:\n${formatCandles(candles1h)}` },
12896
+ * { role: "assistant", content: "Trend analyzed" }
12897
+ * );
12898
+ *
12899
+ * const candles5m = await getCandles(symbol, "5m", 24);
12900
+ * messages.push(
12901
+ * { role: "user", content: `Analyze 5m structure:\n${formatCandles(candles5m)}` },
12902
+ * { role: "assistant", content: "Structure analyzed" }
12903
+ * );
12904
+ *
12905
+ * // Request signal
12906
+ * messages.push({
12907
+ * role: "user",
12908
+ * content: "Generate trading signal. Use position: 'wait' if uncertain."
12909
+ * });
12910
+ *
12911
+ * const resultId = uuid();
12912
+ * const signal = await llmRequest(messages);
12913
+ *
12914
+ * // Save conversation and result for debugging
12915
+ * await dumpSignal(resultId, messages, signal);
12916
+ *
12917
+ * return signal;
12918
+ * }
12919
+ * });
12920
+ *
12921
+ * // Creates: ./dump/strategy/{uuid}/00_system_prompt.md
12922
+ * // ./dump/strategy/{uuid}/01_user_message.md (1h analysis)
12923
+ * // ./dump/strategy/{uuid}/02_assistant_message.md
12924
+ * // ./dump/strategy/{uuid}/03_user_message.md (5m analysis)
12925
+ * // ./dump/strategy/{uuid}/04_assistant_message.md
12926
+ * // ./dump/strategy/{uuid}/05_user_message.md (signal request)
12927
+ * // ./dump/strategy/{uuid}/06_llm_output.md (final signal)
12928
+ * ```
12929
+ */
12930
+ async function dumpSignal(signalId, history, signal, outputDir = "./dump/strategy") {
12931
+ backtest$1.loggerService.info(DUMP_SIGNAL_METHOD_NAME, {
12932
+ signalId,
12933
+ history,
12934
+ signal,
12935
+ outputDir,
12936
+ });
12937
+ return await backtest$1.outlineMarkdownService.dumpSignal(signalId, history, signal, outputDir);
12938
+ }
12939
+
12701
12940
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
12702
12941
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
12703
12942
  const BACKTEST_METHOD_NAME_GET_REPORT = "BacktestUtils.getReport";
@@ -13918,26 +14157,26 @@ const PARTIAL_METHOD_NAME_DUMP = "PartialUtils.dump";
13918
14157
  *
13919
14158
  * Data source:
13920
14159
  * - PartialMarkdownService listens to partialProfitSubject/partialLossSubject
13921
- * - Accumulates events in ReportStorage (max 250 events per symbol)
13922
- * - Events include: timestamp, action, symbol, signalId, position, level, price, mode
14160
+ * - Accumulates events in ReportStorage (max 250 events per symbol-strategy pair)
14161
+ * - Events include: timestamp, action, symbol, strategyName, signalId, position, level, price, mode
13923
14162
  *
13924
14163
  * @example
13925
14164
  * ```typescript
13926
14165
  * import { Partial } from "./classes/Partial";
13927
14166
  *
13928
- * // Get statistical data for BTCUSDT
13929
- * const stats = await Partial.getData("BTCUSDT");
14167
+ * // Get statistical data for BTCUSDT:my-strategy
14168
+ * const stats = await Partial.getData("BTCUSDT", "my-strategy");
13930
14169
  * console.log(`Total events: ${stats.totalEvents}`);
13931
14170
  * console.log(`Profit events: ${stats.totalProfit}`);
13932
14171
  * console.log(`Loss events: ${stats.totalLoss}`);
13933
14172
  *
13934
14173
  * // Generate markdown report
13935
- * const markdown = await Partial.getReport("BTCUSDT");
14174
+ * const markdown = await Partial.getReport("BTCUSDT", "my-strategy");
13936
14175
  * console.log(markdown); // Formatted table with all events
13937
14176
  *
13938
14177
  * // Export report to file
13939
- * await Partial.dump("BTCUSDT"); // Saves to ./dump/partial/BTCUSDT.md
13940
- * await Partial.dump("BTCUSDT", "./custom/path"); // Custom directory
14178
+ * await Partial.dump("BTCUSDT", "my-strategy"); // Saves to ./dump/partial/BTCUSDT_my-strategy.md
14179
+ * await Partial.dump("BTCUSDT", "my-strategy", "./custom/path"); // Custom directory
13941
14180
  * ```
13942
14181
  */
13943
14182
  class PartialUtils {
@@ -13949,11 +14188,12 @@ class PartialUtils {
13949
14188
  * Returns aggregated metrics calculated from all profit and loss events.
13950
14189
  *
13951
14190
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14191
+ * @param strategyName - Strategy name (e.g., "my-strategy")
13952
14192
  * @returns Promise resolving to PartialStatistics object with counts and event list
13953
14193
  *
13954
14194
  * @example
13955
14195
  * ```typescript
13956
- * const stats = await Partial.getData("BTCUSDT");
14196
+ * const stats = await Partial.getData("BTCUSDT", "my-strategy");
13957
14197
  *
13958
14198
  * console.log(`Total events: ${stats.totalEvents}`);
13959
14199
  * console.log(`Profit events: ${stats.totalProfit} (${(stats.totalProfit / stats.totalEvents * 100).toFixed(1)}%)`);
@@ -13965,16 +14205,17 @@ class PartialUtils {
13965
14205
  * }
13966
14206
  * ```
13967
14207
  */
13968
- this.getData = async (symbol) => {
13969
- backtest$1.loggerService.info(PARTIAL_METHOD_NAME_GET_DATA, { symbol });
13970
- return await backtest$1.partialMarkdownService.getData(symbol);
14208
+ this.getData = async (symbol, strategyName) => {
14209
+ backtest$1.loggerService.info(PARTIAL_METHOD_NAME_GET_DATA, { symbol, strategyName });
14210
+ return await backtest$1.partialMarkdownService.getData(symbol, strategyName);
13971
14211
  };
13972
14212
  /**
13973
- * Generates markdown report with all partial profit/loss events for a symbol.
14213
+ * Generates markdown report with all partial profit/loss events for a symbol-strategy pair.
13974
14214
  *
13975
14215
  * Creates formatted table containing:
13976
14216
  * - Action (PROFIT/LOSS)
13977
14217
  * - Symbol
14218
+ * - Strategy
13978
14219
  * - Signal ID
13979
14220
  * - Position (LONG/SHORT)
13980
14221
  * - Level % (+10%, -20%, etc)
@@ -13985,35 +14226,36 @@ class PartialUtils {
13985
14226
  * Also includes summary statistics at the end.
13986
14227
  *
13987
14228
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14229
+ * @param strategyName - Strategy name (e.g., "my-strategy")
13988
14230
  * @returns Promise resolving to markdown formatted report string
13989
14231
  *
13990
14232
  * @example
13991
14233
  * ```typescript
13992
- * const markdown = await Partial.getReport("BTCUSDT");
14234
+ * const markdown = await Partial.getReport("BTCUSDT", "my-strategy");
13993
14235
  * console.log(markdown);
13994
14236
  *
13995
14237
  * // Output:
13996
- * // # Partial Profit/Loss Report: BTCUSDT
14238
+ * // # Partial Profit/Loss Report: BTCUSDT:my-strategy
13997
14239
  * //
13998
- * // | Action | Symbol | Signal ID | Position | Level % | Current Price | Timestamp | Mode |
13999
- * // | --- | --- | --- | --- | --- | --- | --- | --- |
14000
- * // | PROFIT | BTCUSDT | abc123 | LONG | +10% | 51500.00000000 USD | 2024-01-15T10:30:00.000Z | Backtest |
14001
- * // | LOSS | BTCUSDT | abc123 | LONG | -10% | 49000.00000000 USD | 2024-01-15T11:00:00.000Z | Backtest |
14240
+ * // | Action | Symbol | Strategy | Signal ID | Position | Level % | Current Price | Timestamp | Mode |
14241
+ * // | --- | --- | --- | --- | --- | --- | --- | --- | --- |
14242
+ * // | PROFIT | BTCUSDT | my-strategy | abc123 | LONG | +10% | 51500.00000000 USD | 2024-01-15T10:30:00.000Z | Backtest |
14243
+ * // | LOSS | BTCUSDT | my-strategy | abc123 | LONG | -10% | 49000.00000000 USD | 2024-01-15T11:00:00.000Z | Backtest |
14002
14244
  * //
14003
14245
  * // **Total events:** 2
14004
14246
  * // **Profit events:** 1
14005
14247
  * // **Loss events:** 1
14006
14248
  * ```
14007
14249
  */
14008
- this.getReport = async (symbol) => {
14009
- backtest$1.loggerService.info(PARTIAL_METHOD_NAME_GET_REPORT, { symbol });
14010
- return await backtest$1.partialMarkdownService.getReport(symbol);
14250
+ this.getReport = async (symbol, strategyName) => {
14251
+ backtest$1.loggerService.info(PARTIAL_METHOD_NAME_GET_REPORT, { symbol, strategyName });
14252
+ return await backtest$1.partialMarkdownService.getReport(symbol, strategyName);
14011
14253
  };
14012
14254
  /**
14013
14255
  * Generates and saves markdown report to file.
14014
14256
  *
14015
14257
  * Creates directory if it doesn't exist.
14016
- * Filename format: {symbol}.md (e.g., "BTCUSDT.md")
14258
+ * Filename format: {symbol}_{strategyName}.md (e.g., "BTCUSDT_my-strategy.md")
14017
14259
  *
14018
14260
  * Delegates to PartialMarkdownService.dump() which:
14019
14261
  * 1. Generates markdown report via getReport()
@@ -14022,26 +14264,27 @@ class PartialUtils {
14022
14264
  * 4. Logs success/failure to console
14023
14265
  *
14024
14266
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14267
+ * @param strategyName - Strategy name (e.g., "my-strategy")
14025
14268
  * @param path - Output directory path (default: "./dump/partial")
14026
14269
  * @returns Promise that resolves when file is written
14027
14270
  *
14028
14271
  * @example
14029
14272
  * ```typescript
14030
- * // Save to default path: ./dump/partial/BTCUSDT.md
14031
- * await Partial.dump("BTCUSDT");
14273
+ * // Save to default path: ./dump/partial/BTCUSDT_my-strategy.md
14274
+ * await Partial.dump("BTCUSDT", "my-strategy");
14032
14275
  *
14033
- * // Save to custom path: ./reports/partial/BTCUSDT.md
14034
- * await Partial.dump("BTCUSDT", "./reports/partial");
14276
+ * // Save to custom path: ./reports/partial/BTCUSDT_my-strategy.md
14277
+ * await Partial.dump("BTCUSDT", "my-strategy", "./reports/partial");
14035
14278
  *
14036
14279
  * // After multiple symbols backtested, export all reports
14037
14280
  * for (const symbol of ["BTCUSDT", "ETHUSDT", "BNBUSDT"]) {
14038
- * await Partial.dump(symbol, "./backtest-results");
14281
+ * await Partial.dump(symbol, "my-strategy", "./backtest-results");
14039
14282
  * }
14040
14283
  * ```
14041
14284
  */
14042
- this.dump = async (symbol, path) => {
14043
- backtest$1.loggerService.info(PARTIAL_METHOD_NAME_DUMP, { symbol, path });
14044
- await backtest$1.partialMarkdownService.dump(symbol, path);
14285
+ this.dump = async (symbol, strategyName, path) => {
14286
+ backtest$1.loggerService.info(PARTIAL_METHOD_NAME_DUMP, { symbol, strategyName, path });
14287
+ await backtest$1.partialMarkdownService.dump(symbol, strategyName, path);
14045
14288
  };
14046
14289
  }
14047
14290
  }
@@ -14054,9 +14297,9 @@ class PartialUtils {
14054
14297
  * import { Partial } from "backtest-kit";
14055
14298
  *
14056
14299
  * // Usage same as PartialUtils methods
14057
- * const stats = await Partial.getData("BTCUSDT");
14058
- * const report = await Partial.getReport("BTCUSDT");
14059
- * await Partial.dump("BTCUSDT");
14300
+ * const stats = await Partial.getData("BTCUSDT", "my-strategy");
14301
+ * const report = await Partial.getReport("BTCUSDT", "my-strategy");
14302
+ * await Partial.dump("BTCUSDT", "my-strategy");
14060
14303
  * ```
14061
14304
  */
14062
14305
  const Partial = new PartialUtils();
@@ -14142,4 +14385,4 @@ class ConstantUtils {
14142
14385
  */
14143
14386
  const Constant = new ConstantUtils();
14144
14387
 
14145
- export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
14388
+ export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };