backtest-kit 2.1.3 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/build/index.cjs +121 -2252
  2. package/build/index.mjs +124 -2246
  3. package/package.json +1 -1
  4. package/types.d.ts +22 -1327
package/build/index.cjs CHANGED
@@ -8,10 +8,8 @@ var path = require('path');
8
8
  var crypto = require('crypto');
9
9
  var os = require('os');
10
10
  var fs$1 = require('fs');
11
- var module$1 = require('module');
12
11
  var util = require('util');
13
12
 
14
- var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
13
  function _interopNamespaceDefault(e) {
16
14
  var n = Object.create(null);
17
15
  if (e) {
@@ -73,7 +71,6 @@ const connectionServices$1 = {
73
71
  sizingConnectionService: Symbol('sizingConnectionService'),
74
72
  riskConnectionService: Symbol('riskConnectionService'),
75
73
  actionConnectionService: Symbol('actionConnectionService'),
76
- optimizerConnectionService: Symbol('optimizerConnectionService'),
77
74
  partialConnectionService: Symbol('partialConnectionService'),
78
75
  breakevenConnectionService: Symbol('breakevenConnectionService'),
79
76
  };
@@ -85,7 +82,6 @@ const schemaServices$1 = {
85
82
  sizingSchemaService: Symbol('sizingSchemaService'),
86
83
  riskSchemaService: Symbol('riskSchemaService'),
87
84
  actionSchemaService: Symbol('actionSchemaService'),
88
- optimizerSchemaService: Symbol('optimizerSchemaService'),
89
85
  };
90
86
  const coreServices$1 = {
91
87
  exchangeCoreService: Symbol('exchangeCoreService'),
@@ -96,7 +92,6 @@ const coreServices$1 = {
96
92
  const globalServices$1 = {
97
93
  sizingGlobalService: Symbol('sizingGlobalService'),
98
94
  riskGlobalService: Symbol('riskGlobalService'),
99
- optimizerGlobalService: Symbol('optimizerGlobalService'),
100
95
  partialGlobalService: Symbol('partialGlobalService'),
101
96
  breakevenGlobalService: Symbol('breakevenGlobalService'),
102
97
  };
@@ -146,16 +141,9 @@ const validationServices$1 = {
146
141
  sizingValidationService: Symbol('sizingValidationService'),
147
142
  riskValidationService: Symbol('riskValidationService'),
148
143
  actionValidationService: Symbol('actionValidationService'),
149
- optimizerValidationService: Symbol('optimizerValidationService'),
150
144
  configValidationService: Symbol('configValidationService'),
151
145
  columnValidationService: Symbol('columnValidationService'),
152
146
  };
153
- const templateServices$1 = {
154
- optimizerTemplateService: Symbol('optimizerTemplateService'),
155
- };
156
- const promptServices$1 = {
157
- signalPromptService: Symbol('signalPromptService'),
158
- };
159
147
  const TYPES = {
160
148
  ...baseServices$1,
161
149
  ...contextServices$1,
@@ -169,8 +157,6 @@ const TYPES = {
169
157
  ...markdownServices$1,
170
158
  ...reportServices$1,
171
159
  ...validationServices$1,
172
- ...templateServices$1,
173
- ...promptServices$1,
174
160
  };
175
161
 
176
162
  /**
@@ -494,11 +480,6 @@ const progressBacktestEmitter = new functoolsKit.Subject();
494
480
  * Emits progress updates during walker execution.
495
481
  */
496
482
  const progressWalkerEmitter = new functoolsKit.Subject();
497
- /**
498
- * Progress emitter for optimizer execution progress.
499
- * Emits progress updates during optimizer execution.
500
- */
501
- const progressOptimizerEmitter = new functoolsKit.Subject();
502
483
  /**
503
484
  * Performance emitter for execution metrics.
504
485
  * Emits performance metrics for profiling and bottleneck detection.
@@ -573,7 +554,6 @@ var emitters = /*#__PURE__*/Object.freeze({
573
554
  partialProfitSubject: partialProfitSubject,
574
555
  performanceEmitter: performanceEmitter,
575
556
  progressBacktestEmitter: progressBacktestEmitter,
576
- progressOptimizerEmitter: progressOptimizerEmitter,
577
557
  progressWalkerEmitter: progressWalkerEmitter,
578
558
  riskSubject: riskSubject,
579
559
  schedulePingSubject: schedulePingSubject,
@@ -18421,1480 +18401,131 @@ class ActionValidationService {
18421
18401
  }
18422
18402
 
18423
18403
  /**
18424
- * Default template service for generating optimizer code snippets.
18425
- * Implements all IOptimizerTemplate methods with Ollama LLM integration.
18404
+ * Symbol marker indicating that partial state needs initialization.
18405
+ * Used as sentinel value for _states before waitForInit() is called.
18406
+ */
18407
+ const NEED_FETCH$1 = Symbol("need_fetch");
18408
+ /**
18409
+ * Array of profit level milestones to track (10%, 20%, ..., 100%).
18410
+ * Each level is checked during profit() method to emit events for newly reached levels.
18411
+ */
18412
+ const PROFIT_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
18413
+ /**
18414
+ * Array of loss level milestones to track (-10%, -20%, ..., -100%).
18415
+ * Each level is checked during loss() method to emit events for newly reached levels.
18416
+ */
18417
+ const LOSS_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
18418
+ /**
18419
+ * Internal profit handler function for ClientPartial.
18426
18420
  *
18427
- * Features:
18428
- * - Multi-timeframe analysis (1m, 5m, 15m, 1h)
18429
- * - JSON structured output for signals
18430
- * - Debug logging to ./dump/strategy
18431
- * - CCXT exchange integration
18432
- * - Walker-based strategy comparison
18421
+ * Checks which profit levels have been reached and emits events for new levels only.
18422
+ * Uses Set-based deduplication to prevent duplicate events.
18433
18423
  *
18434
- * Can be partially overridden in optimizer schema configuration.
18424
+ * @param symbol - Trading pair symbol
18425
+ * @param data - Signal row data
18426
+ * @param currentPrice - Current market price
18427
+ * @param revenuePercent - Current profit percentage (positive value)
18428
+ * @param backtest - True if backtest mode
18429
+ * @param when - Event timestamp
18430
+ * @param self - ClientPartial instance reference
18435
18431
  */
18436
- class OptimizerTemplateService {
18437
- constructor() {
18438
- this.loggerService = inject(TYPES.loggerService);
18439
- /**
18440
- * Generates the top banner with imports and constants.
18441
- *
18442
- * @param symbol - Trading pair symbol
18443
- * @returns Shebang, imports, and WARN_KB constant
18444
- */
18445
- this.getTopBanner = async (symbol) => {
18446
- this.loggerService.log("optimizerTemplateService getTopBanner", {
18447
- symbol,
18448
- });
18449
- return [
18450
- "#!/usr/bin/env node",
18451
- "",
18452
- `import { Ollama } from "ollama";`,
18453
- `import ccxt from "ccxt";`,
18454
- `import {`,
18455
- ` addExchangeSchema,`,
18456
- ` addStrategySchema,`,
18457
- ` addFrameSchema,`,
18458
- ` addWalkerSchema,`,
18459
- ` Walker,`,
18460
- ` Backtest,`,
18461
- ` getCandles,`,
18462
- ` listenSignalBacktest,`,
18463
- ` listenWalkerComplete,`,
18464
- ` listenDoneBacktest,`,
18465
- ` listenBacktestProgress,`,
18466
- ` listenWalkerProgress,`,
18467
- ` listenError,`,
18468
- ` Markdown,`,
18469
- `} from "backtest-kit";`,
18470
- `import { promises as fs } from "fs";`,
18471
- `import { v4 as uuid } from "uuid";`,
18472
- `import path from "path";`,
18473
- ``,
18474
- `const WARN_KB = 100;`,
18475
- ``,
18476
- `Markdown.enable()`,
18477
- ].join("\n");
18478
- };
18479
- /**
18480
- * Generates default user message for LLM conversation.
18481
- * Simple prompt to read and acknowledge data.
18482
- *
18483
- * @param symbol - Trading pair symbol
18484
- * @param data - Fetched data array
18485
- * @param name - Source name
18486
- * @returns User message with JSON data
18487
- */
18488
- this.getUserMessage = async (symbol, data, name) => {
18489
- this.loggerService.log("optimizerTemplateService getUserMessage", {
18490
- symbol,
18491
- data,
18492
- name,
18493
- });
18494
- return ["Прочитай данные и скажи ОК", "", JSON.stringify(data)].join("\n");
18495
- };
18496
- /**
18497
- * Generates default assistant message for LLM conversation.
18498
- * Simple acknowledgment response.
18499
- *
18500
- * @param symbol - Trading pair symbol
18501
- * @param data - Fetched data array
18502
- * @param name - Source name
18503
- * @returns Assistant acknowledgment message
18504
- */
18505
- this.getAssistantMessage = async (symbol, data, name) => {
18506
- this.loggerService.log("optimizerTemplateService getAssistantMessage", {
18507
- symbol,
18508
- data,
18509
- name,
18510
- });
18511
- return "ОК";
18512
- };
18513
- /**
18514
- * Generates Walker configuration code.
18515
- * Compares multiple strategies on test frame.
18516
- *
18517
- * @param walkerName - Unique walker identifier
18518
- * @param exchangeName - Exchange to use for backtesting
18519
- * @param frameName - Test frame name
18520
- * @param strategies - Array of strategy names to compare
18521
- * @returns Generated addWalker() call
18522
- */
18523
- this.getWalkerTemplate = async (walkerName, exchangeName, frameName, strategies) => {
18524
- this.loggerService.log("optimizerTemplateService getWalkerTemplate", {
18525
- walkerName,
18526
- exchangeName,
18527
- frameName,
18528
- strategies,
18529
- });
18530
- // Escape special characters to prevent code injection
18531
- const escapedWalkerName = String(walkerName)
18532
- .replace(/\\/g, '\\\\')
18533
- .replace(/"/g, '\\"');
18534
- const escapedExchangeName = String(exchangeName)
18535
- .replace(/\\/g, '\\\\')
18536
- .replace(/"/g, '\\"');
18537
- const escapedFrameName = String(frameName)
18538
- .replace(/\\/g, '\\\\')
18539
- .replace(/"/g, '\\"');
18540
- const escapedStrategies = strategies.map((s) => String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"'));
18541
- return [
18542
- `addWalkerSchema({`,
18543
- ` walkerName: "${escapedWalkerName}",`,
18544
- ` exchangeName: "${escapedExchangeName}",`,
18545
- ` frameName: "${escapedFrameName}",`,
18546
- ` strategies: [${escapedStrategies.map((s) => `"${s}"`).join(", ")}],`,
18547
- `});`
18548
- ].join("\n");
18549
- };
18550
- /**
18551
- * Generates Strategy configuration with LLM integration.
18552
- * Includes multi-timeframe analysis and signal generation.
18553
- *
18554
- * @param strategyName - Unique strategy identifier
18555
- * @param interval - Signal throttling interval (e.g., "5m")
18556
- * @param prompt - Strategy logic from getPrompt()
18557
- * @returns Generated addStrategy() call with getSignal() function
18558
- */
18559
- this.getStrategyTemplate = async (strategyName, interval, prompt) => {
18560
- this.loggerService.log("optimizerTemplateService getStrategyTemplate", {
18561
- strategyName,
18562
- interval,
18563
- prompt,
18564
- });
18565
- // Convert prompt to plain text first
18566
- const plainPrompt = toPlainString(prompt);
18567
- // Escape special characters to prevent code injection
18568
- const escapedStrategyName = String(strategyName)
18569
- .replace(/\\/g, '\\\\')
18570
- .replace(/"/g, '\\"');
18571
- const escapedInterval = String(interval)
18572
- .replace(/\\/g, '\\\\')
18573
- .replace(/"/g, '\\"');
18574
- const escapedPrompt = String(plainPrompt)
18575
- .replace(/\\/g, '\\\\')
18576
- .replace(/`/g, '\\`')
18577
- .replace(/\$/g, '\\$');
18578
- return [
18579
- `addStrategySchema({`,
18580
- ` strategyName: "${escapedStrategyName}",`,
18581
- ` interval: "${escapedInterval}",`,
18582
- ` getSignal: async (symbol) => {`,
18583
- ` const messages = [];`,
18584
- ``,
18585
- ` // Загружаем данные всех таймфреймов`,
18586
- ` const microTermCandles = await getCandles(symbol, "1m", 30);`,
18587
- ` const mainTermCandles = await getCandles(symbol, "5m", 24);`,
18588
- ` const shortTermCandles = await getCandles(symbol, "15m", 24);`,
18589
- ` const mediumTermCandles = await getCandles(symbol, "1h", 24);`,
18590
- ``,
18591
- ` function formatCandles(candles, timeframe) {`,
18592
- ` return candles.map((c) =>`,
18593
- ` \`\${new Date(c.timestamp).toISOString()}[\${timeframe}]: O:\${c.open} H:\${c.high} L:\${c.low} C:\${c.close} V:\${c.volume}\``,
18594
- ` ).join("\\n");`,
18595
- ` }`,
18596
- ``,
18597
- ` // Сообщение 1: Среднесрочный тренд`,
18598
- ` messages.push(`,
18599
- ` {`,
18600
- ` role: "user",`,
18601
- ` content: [`,
18602
- ` \`\${symbol}\`,`,
18603
- ` "Проанализируй свечи 1h:",`,
18604
- ` "",`,
18605
- ` formatCandles(mediumTermCandles, "1h")`,
18606
- ` ].join("\\n"),`,
18607
- ` },`,
18608
- ` {`,
18609
- ` role: "assistant",`,
18610
- ` content: "Тренд 1h проанализирован",`,
18611
- ` }`,
18612
- ` );`,
18613
- ``,
18614
- ` // Сообщение 2: Краткосрочный тренд`,
18615
- ` messages.push(`,
18616
- ` {`,
18617
- ` role: "user",`,
18618
- ` content: [`,
18619
- ` "Проанализируй свечи 15m:",`,
18620
- ` "",`,
18621
- ` formatCandles(shortTermCandles, "15m")`,
18622
- ` ].join("\\n"),`,
18623
- ` },`,
18624
- ` {`,
18625
- ` role: "assistant",`,
18626
- ` content: "Тренд 15m проанализирован",`,
18627
- ` }`,
18628
- ` );`,
18629
- ``,
18630
- ` // Сообщение 3: Основной таймфрейм`,
18631
- ` messages.push(`,
18632
- ` {`,
18633
- ` role: "user",`,
18634
- ` content: [`,
18635
- ` "Проанализируй свечи 5m:",`,
18636
- ` "",`,
18637
- ` formatCandles(mainTermCandles, "5m")`,
18638
- ` ].join("\\n")`,
18639
- ` },`,
18640
- ` {`,
18641
- ` role: "assistant",`,
18642
- ` content: "Таймфрейм 5m проанализирован",`,
18643
- ` }`,
18644
- ` );`,
18645
- ``,
18646
- ` // Сообщение 4: Микро-структура`,
18647
- ` messages.push(`,
18648
- ` {`,
18649
- ` role: "user",`,
18650
- ` content: [`,
18651
- ` "Проанализируй свечи 1m:",`,
18652
- ` "",`,
18653
- ` formatCandles(microTermCandles, "1m")`,
18654
- ` ].join("\\n")`,
18655
- ` },`,
18656
- ` {`,
18657
- ` role: "assistant",`,
18658
- ` content: "Микроструктура 1m проанализирована",`,
18659
- ` }`,
18660
- ` );`,
18661
- ``,
18662
- ` // Сообщение 5: Запрос сигнала`,
18663
- ` messages.push(`,
18664
- ` {`,
18665
- ` role: "user",`,
18666
- ` content: [`,
18667
- ` "Проанализируй все таймфреймы и сгенерируй торговый сигнал согласно этой стратегии. Открывай позицию ТОЛЬКО при четком сигнале.",`,
18668
- ` "",`,
18669
- ` \`${escapedPrompt}\`,`,
18670
- ` "",`,
18671
- ` "Если сигналы противоречивы или тренд слабый то position: wait"`,
18672
- ` ].join("\\n"),`,
18673
- ` }`,
18674
- ` );`,
18675
- ``,
18676
- ` const resultId = uuid();`,
18677
- ``,
18678
- ` const result = await json(messages);`,
18679
- ``,
18680
- ` await dumpJson(resultId, messages, result);`,
18681
- ``,
18682
- ` result.id = resultId;`,
18683
- ``,
18684
- ` return result;`,
18685
- ` },`,
18686
- `});`
18687
- ].join("\n");
18688
- };
18689
- /**
18690
- * Generates Exchange configuration code.
18691
- * Uses CCXT Binance with standard formatters.
18692
- *
18693
- * @param symbol - Trading pair symbol (unused, for consistency)
18694
- * @param exchangeName - Unique exchange identifier
18695
- * @returns Generated addExchange() call with CCXT integration
18696
- */
18697
- this.getExchangeTemplate = async (symbol, exchangeName) => {
18698
- this.loggerService.log("optimizerTemplateService getExchangeTemplate", {
18699
- exchangeName,
18700
- symbol,
18701
- });
18702
- // Escape special characters to prevent code injection
18703
- const escapedExchangeName = String(exchangeName)
18704
- .replace(/\\/g, '\\\\')
18705
- .replace(/"/g, '\\"');
18706
- return [
18707
- `addExchangeSchema({`,
18708
- ` exchangeName: "${escapedExchangeName}",`,
18709
- ` getCandles: async (symbol, interval, since, limit) => {`,
18710
- ` const exchange = new ccxt.binance();`,
18711
- ` const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);`,
18712
- ` return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({`,
18713
- ` timestamp, open, high, low, close, volume`,
18714
- ` }));`,
18715
- ` },`,
18716
- ` formatPrice: async (symbol, price) => price.toFixed(2),`,
18717
- ` formatQuantity: async (symbol, quantity) => quantity.toFixed(8),`,
18718
- `});`
18719
- ].join("\n");
18720
- };
18721
- /**
18722
- * Generates Frame (timeframe) configuration code.
18723
- *
18724
- * @param symbol - Trading pair symbol (unused, for consistency)
18725
- * @param frameName - Unique frame identifier
18726
- * @param interval - Candle interval (e.g., "1m")
18727
- * @param startDate - Frame start date
18728
- * @param endDate - Frame end date
18729
- * @returns Generated addFrame() call
18730
- */
18731
- this.getFrameTemplate = async (symbol, frameName, interval, startDate, endDate) => {
18732
- this.loggerService.log("optimizerTemplateService getFrameTemplate", {
18733
- symbol,
18734
- frameName,
18735
- interval,
18736
- startDate,
18737
- endDate,
18738
- });
18739
- // Escape special characters to prevent code injection
18740
- const escapedFrameName = String(frameName)
18741
- .replace(/\\/g, '\\\\')
18742
- .replace(/"/g, '\\"');
18743
- const escapedInterval = String(interval)
18744
- .replace(/\\/g, '\\\\')
18745
- .replace(/"/g, '\\"');
18746
- return [
18747
- `addFrameSchema({`,
18748
- ` frameName: "${escapedFrameName}",`,
18749
- ` interval: "${escapedInterval}",`,
18750
- ` startDate: new Date("${startDate.toISOString()}"),`,
18751
- ` endDate: new Date("${endDate.toISOString()}"),`,
18752
- `});`
18753
- ].join("\n");
18754
- };
18755
- /**
18756
- * Generates launcher code to run Walker with event listeners.
18757
- * Includes progress tracking and completion handlers.
18758
- *
18759
- * @param symbol - Trading pair symbol
18760
- * @param walkerName - Walker name to launch
18761
- * @returns Generated Walker.background() call with listeners
18762
- */
18763
- this.getLauncherTemplate = async (symbol, walkerName) => {
18764
- this.loggerService.log("optimizerTemplateService getLauncherTemplate", {
18765
- symbol,
18766
- walkerName,
18767
- });
18768
- // Escape special characters to prevent code injection
18769
- const escapedSymbol = String(symbol)
18770
- .replace(/\\/g, '\\\\')
18771
- .replace(/"/g, '\\"');
18772
- const escapedWalkerName = String(walkerName)
18773
- .replace(/\\/g, '\\\\')
18774
- .replace(/"/g, '\\"');
18775
- return [
18776
- `Walker.background("${escapedSymbol}", {`,
18777
- ` walkerName: "${escapedWalkerName}"`,
18778
- `});`,
18779
- ``,
18780
- `listenSignalBacktest((event) => {`,
18781
- ` console.log(event);`,
18782
- `});`,
18783
- ``,
18784
- `listenBacktestProgress((event) => {`,
18785
- ` console.log(\`Progress: \${(event.progress * 100).toFixed(2)}%\`);`,
18786
- ` console.log(\`Processed: \${event.processedFrames} / \${event.totalFrames}\`);`,
18787
- `});`,
18788
- ``,
18789
- `listenWalkerProgress((event) => {`,
18790
- ` console.log(\`Progress: \${(event.progress * 100).toFixed(2)}%\`);`,
18791
- ` console.log(\`\${event.processedStrategies} / \${event.totalStrategies} strategies\`);`,
18792
- ` console.log(\`Walker: \${event.walkerName}, Symbol: \${event.symbol}\`);`,
18793
- `});`,
18794
- ``,
18795
- `listenWalkerComplete((results) => {`,
18796
- ` console.log("Walker completed:", results.bestStrategy);`,
18797
- ` Walker.dump(results.symbol, { walkerName: results.walkerName });`,
18798
- `});`,
18799
- ``,
18800
- `listenDoneBacktest((event) => {`,
18801
- ` console.log("Backtest completed:", event.symbol);`,
18802
- ` Backtest.dump(event.symbol, {`,
18803
- ` strategyName: event.strategyName,`,
18804
- ` exchangeName: event.exchangeName,`,
18805
- ` frameName: event.frameName`,
18806
- ` });`,
18807
- `});`,
18808
- ``,
18809
- `listenError((error) => {`,
18810
- ` console.error("Error occurred:", error);`,
18811
- `});`
18812
- ].join("\n");
18813
- };
18814
- /**
18815
- * Generates dumpJson() helper function for debug output.
18816
- * Saves LLM conversations and results to ./dump/strategy/{resultId}/
18817
- *
18818
- * @param symbol - Trading pair symbol (unused, for consistency)
18819
- * @returns Generated async dumpJson() function
18820
- */
18821
- this.getJsonDumpTemplate = async (symbol) => {
18822
- this.loggerService.log("optimizerTemplateService getJsonDumpTemplate", {
18823
- symbol,
18824
- });
18825
- return [
18826
- `async function dumpJson(resultId, history, result, outputDir = "./dump/strategy") {`,
18827
- ` // Extract system messages and system reminders from existing data`,
18828
- ` const systemMessages = history.filter((m) => m.role === "system");`,
18829
- ` const userMessages = history.filter((m) => m.role === "user");`,
18830
- ` const subfolderPath = path.join(outputDir, resultId);`,
18831
- ``,
18832
- ` try {`,
18833
- ` await fs.access(subfolderPath);`,
18834
- ` return;`,
18835
- ` } catch {`,
18836
- ` await fs.mkdir(subfolderPath, { recursive: true });`,
18837
- ` }`,
18838
- ``,
18839
- ` {`,
18840
- ` let summary = "# Outline Result Summary\\n\\n";`,
18841
- ``,
18842
- ` {`,
18843
- ` summary += \`**ResultId**: \${resultId}\\n\\n\`;`,
18844
- ` }`,
18845
- ``,
18846
- ` if (result) {`,
18847
- ` summary += "## Output Data\\n\\n";`,
18848
- ` summary += "\`\`\`json\\n";`,
18849
- ` summary += JSON.stringify(result, null, 2);`,
18850
- ` summary += "\\n\`\`\`\\n\\n";`,
18851
- ` }`,
18852
- ``,
18853
- ` // Add system messages to summary`,
18854
- ` if (systemMessages.length > 0) {`,
18855
- ` summary += "## System Messages\\n\\n";`,
18856
- ` systemMessages.forEach((msg, idx) => {`,
18857
- ` summary += \`### System Message \${idx + 1}\\n\\n\`;`,
18858
- ` summary += msg.content;`,
18859
- ` summary += "\\n\\n";`,
18860
- ` });`,
18861
- ` }`,
18862
- ``,
18863
- ` const summaryFile = path.join(subfolderPath, "00_system_prompt.md");`,
18864
- ` await fs.writeFile(summaryFile, summary, "utf8");`,
18865
- ` }`,
18866
- ``,
18867
- ` {`,
18868
- ` await Promise.all(`,
18869
- ` Array.from(userMessages.entries()).map(async ([idx, message]) => {`,
18870
- ` const messageNum = String(idx + 1).padStart(2, "0");`,
18871
- ` const contentFileName = \`\${messageNum}_user_message.md\`;`,
18872
- ` const contentFilePath = path.join(subfolderPath, contentFileName);`,
18873
- ``,
18874
- ` {`,
18875
- ` const messageSizeBytes = Buffer.byteLength(message.content, "utf8");`,
18876
- ` const messageSizeKb = Math.floor(messageSizeBytes / 1024);`,
18877
- ` if (messageSizeKb > WARN_KB) {`,
18878
- ` console.warn(`,
18879
- ` \`User message \${idx + 1} is \${messageSizeBytes} bytes (\${messageSizeKb}kb), which exceeds warning limit\``,
18880
- ` );`,
18881
- ` }`,
18882
- ` }`,
18883
- ``,
18884
- ` let content = \`# User Input \${idx + 1}\\n\\n\`;`,
18885
- ` content += \`**ResultId**: \${resultId}\\n\\n\`;`,
18886
- ` content += message.content;`,
18887
- ` content += "\\n";`,
18888
- ``,
18889
- ` await fs.writeFile(contentFilePath, content, "utf8");`,
18890
- ` })`,
18891
- ` );`,
18892
- ` }`,
18893
- ``,
18894
- ` {`,
18895
- ` const messageNum = String(userMessages.length + 1).padStart(2, "0");`,
18896
- ` const contentFileName = \`\${messageNum}_llm_output.md\`;`,
18897
- ` const contentFilePath = path.join(subfolderPath, contentFileName);`,
18898
- ``,
18899
- ` let content = "# Full Outline Result\\n\\n";`,
18900
- ` content += \`**ResultId**: \${resultId}\\n\\n\`;`,
18901
- ``,
18902
- ` if (result) {`,
18903
- ` content += "## Output Data\\n\\n";`,
18904
- ` content += "\`\`\`json\\n";`,
18905
- ` content += JSON.stringify(result, null, 2);`,
18906
- ` content += "\\n\`\`\`\\n";`,
18907
- ` }`,
18908
- ``,
18909
- ` await fs.writeFile(contentFilePath, content, "utf8");`,
18910
- ` }`,
18911
- `}`
18912
- ].join("\n");
18913
- };
18914
- /**
18915
- * Generates text() helper for LLM text generation.
18916
- * Uses Ollama deepseek-v3.1:671b model for market analysis.
18917
- *
18918
- * @param symbol - Trading pair symbol (used in prompt)
18919
- * @returns Generated async text() function
18920
- */
18921
- this.getTextTemplate = async (symbol) => {
18922
- this.loggerService.log("optimizerTemplateService getTextTemplate", {
18923
- symbol,
18924
- });
18925
- // Escape special characters in symbol to prevent code injection
18926
- const escapedSymbol = String(symbol)
18927
- .replace(/\\/g, '\\\\')
18928
- .replace(/`/g, '\\`')
18929
- .replace(/\$/g, '\\$')
18930
- .toUpperCase();
18931
- return [
18932
- `async function text(messages) {`,
18933
- ` const ollama = new Ollama({`,
18934
- ` host: "https://ollama.com",`,
18935
- ` headers: {`,
18936
- ` Authorization: \`Bearer \${process.env.OLLAMA_API_KEY}\`,`,
18937
- ` },`,
18938
- ` });`,
18939
- ``,
18940
- ` const response = await ollama.chat({`,
18941
- ` model: "deepseek-v3.1:671b",`,
18942
- ` messages: [`,
18943
- ` {`,
18944
- ` role: "system",`,
18945
- ` content: [`,
18946
- ` "В ответ напиши торговую стратегию где нет ничего лишнего,",`,
18947
- ` "только отчёт готовый для копипасты целиком",`,
18948
- ` "",`,
18949
- ` "**ВАЖНО**: Не здоровайся, не говори что делаешь - только отчёт!"`,
18950
- ` ].join("\\n"),`,
18951
- ` },`,
18952
- ` ...messages,`,
18953
- ` {`,
18954
- ` role: "user",`,
18955
- ` content: [`,
18956
- ` "На каких условиях мне купить ${escapedSymbol}?",`,
18957
- ` "Дай анализ рынка на основе поддержки/сопротивления, точек входа в LONG/SHORT позиции.",`,
18958
- ` "Какой RR ставить для позиций?",`,
18959
- ` "Предпочтительны LONG или SHORT позиции?",`,
18960
- ` "",`,
18961
- ` "Сделай не сухой технический, а фундаментальный анализ, содержащий стратигическую рекомендацию, например, покупать на низу боковика"`,
18962
- ` ].join("\\n")`,
18963
- ` }`,
18964
- ` ]`,
18965
- ` });`,
18966
- ``,
18967
- ` const content = response.message.content.trim();`,
18968
- ` return content`,
18969
- ` .replace(/\\\\/g, '\\\\\\\\')`,
18970
- ` .replace(/\`/g, '\\\\\`')`,
18971
- ` .replace(/\\$/g, '\\\\$')`,
18972
- ` .replace(/"/g, '\\\\"')`,
18973
- ` .replace(/'/g, "\\\\'");`,
18974
- `}`
18975
- ].join("\n");
18432
+ const HANDLE_PROFIT_FN = async (symbol, data, currentPrice, revenuePercent, backtest, when, self) => {
18433
+ if (self._states === NEED_FETCH$1) {
18434
+ throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
18435
+ }
18436
+ if (data.id !== self.params.signalId) {
18437
+ throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
18438
+ }
18439
+ let state = self._states.get(data.id);
18440
+ if (!state) {
18441
+ state = {
18442
+ profitLevels: new Set(),
18443
+ lossLevels: new Set(),
18976
18444
  };
18977
- /**
18978
- * Generates json() helper for structured LLM output.
18979
- * Uses Ollama with JSON schema for trading signals.
18980
- *
18981
- * Signal schema:
18982
- * - position: "wait" | "long" | "short"
18983
- * - note: strategy explanation
18984
- * - priceOpen: entry price
18985
- * - priceTakeProfit: target price
18986
- * - priceStopLoss: stop price
18987
- * - minuteEstimatedTime: expected duration (max 360 min)
18988
- *
18989
- * @param symbol - Trading pair symbol (unused, for consistency)
18990
- * @returns Generated async json() function with signal schema
18991
- */
18992
- this.getJsonTemplate = async (symbol) => {
18993
- this.loggerService.log("optimizerTemplateService getJsonTemplate", {
18445
+ self._states.set(data.id, state);
18446
+ }
18447
+ let shouldPersist = false;
18448
+ for (const level of PROFIT_LEVELS) {
18449
+ if (revenuePercent >= level && !state.profitLevels.has(level)) {
18450
+ state.profitLevels.add(level);
18451
+ shouldPersist = true;
18452
+ self.params.logger.debug("ClientPartial profit level reached", {
18994
18453
  symbol,
18454
+ signalId: data.id,
18455
+ level,
18456
+ revenuePercent,
18457
+ backtest,
18995
18458
  });
18996
- return [
18997
- `async function json(messages) {`,
18998
- ` const ollama = new Ollama({`,
18999
- ` host: "https://ollama.com",`,
19000
- ` headers: {`,
19001
- ` Authorization: \`Bearer \${process.env.OLLAMA_API_KEY}\`,`,
19002
- ` },`,
19003
- ` });`,
19004
- ``,
19005
- ` const response = await ollama.chat({`,
19006
- ` model: "deepseek-v3.1:671b",`,
19007
- ` messages: [`,
19008
- ` {`,
19009
- ` role: "system",`,
19010
- ` content: [`,
19011
- ` "Проанализируй торговую стратегию и верни торговый сигнал.",`,
19012
- ` "",`,
19013
- ` "ПРАВИЛА ОТКРЫТИЯ ПОЗИЦИЙ:",`,
19014
- ` "",`,
19015
- ` "1. ТИПЫ ПОЗИЦИЙ:",`,
19016
- ` " - position='wait': нет четкого сигнала, жди лучших условий",`,
19017
- ` " - position='long': бычий сигнал, цена будет расти",`,
19018
- ` " - position='short': медвежий сигнал, цена будет падать",`,
19019
- ` "",`,
19020
- ` "2. ЦЕНА ВХОДА (priceOpen):",`,
19021
- ` " - Может быть текущей рыночной ценой для немедленного входа",`,
19022
- ` " - Может быть отложенной ценой для входа при достижении уровня",`,
19023
- ` " - Укажи оптимальную цену входа согласно технического анализа",`,
19024
- ` "",`,
19025
- ` "3. УРОВНИ ВЫХОДА:",`,
19026
- ` " - LONG: priceTakeProfit > priceOpen > priceStopLoss",`,
19027
- ` " - SHORT: priceStopLoss > priceOpen > priceTakeProfit",`,
19028
- ` " - Уровни должны иметь техническое обоснование (Fibonacci, S/R, Bollinger)",`,
19029
- ` "",`,
19030
- ` "4. ВРЕМЕННЫЕ РАМКИ:",`,
19031
- ` " - minuteEstimatedTime: прогноз времени до TP (макс 360 минут)",`,
19032
- ` " - Расчет на основе ATR, ADX, MACD, Momentum, Slope",`,
19033
- ` " - Если индикаторов, осциллятор или других метрик нет, посчитай их самостоятельно",`,
19034
- ` ].join("\\n"),`,
19035
- ` },`,
19036
- ` ...messages,`,
19037
- ` ],`,
19038
- ` format: {`,
19039
- ` type: "object",`,
19040
- ` properties: {`,
19041
- ` position: {`,
19042
- ` type: "string",`,
19043
- ` enum: ["wait", "long", "short"],`,
19044
- ` description: "Trade decision: wait (no signal), long (buy), or short (sell)",`,
19045
- ` },`,
19046
- ` note: {`,
19047
- ` type: "string",`,
19048
- ` description: "Professional trading recommendation with price levels",`,
19049
- ` },`,
19050
- ` priceOpen: {`,
19051
- ` type: "number",`,
19052
- ` description: "Entry price (current market price or limit order price)",`,
19053
- ` },`,
19054
- ` priceTakeProfit: {`,
19055
- ` type: "number",`,
19056
- ` description: "Take profit target price",`,
19057
- ` },`,
19058
- ` priceStopLoss: {`,
19059
- ` type: "number",`,
19060
- ` description: "Stop loss exit price",`,
19061
- ` },`,
19062
- ` minuteEstimatedTime: {`,
19063
- ` type: "number",`,
19064
- ` description: "Expected time to reach TP in minutes (max 360)",`,
19065
- ` },`,
19066
- ` },`,
19067
- ` required: ["position", "note", "priceOpen", "priceTakeProfit", "priceStopLoss", "minuteEstimatedTime"],`,
19068
- ` },`,
19069
- ` });`,
19070
- ``,
19071
- ` const jsonResponse = JSON.parse(response.message.content.trim());`,
19072
- ` return jsonResponse;`,
19073
- `}`
19074
- ].join("\n");
19075
- };
18459
+ await self.params.onProfit(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
18460
+ }
19076
18461
  }
19077
- }
19078
-
18462
+ if (shouldPersist) {
18463
+ await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
18464
+ }
18465
+ };
19079
18466
  /**
19080
- * Service for managing optimizer schema registration and retrieval.
19081
- * Provides validation and registry management for optimizer configurations.
18467
+ * Internal loss handler function for ClientPartial.
18468
+ *
18469
+ * Checks which loss levels have been reached and emits events for new levels only.
18470
+ * Uses Set-based deduplication to prevent duplicate events.
18471
+ * Converts negative lossPercent to absolute value for level comparison.
19082
18472
  *
19083
- * Uses ToolRegistry for immutable schema storage.
18473
+ * @param symbol - Trading pair symbol
18474
+ * @param data - Signal row data
18475
+ * @param currentPrice - Current market price
18476
+ * @param lossPercent - Current loss percentage (negative value)
18477
+ * @param backtest - True if backtest mode
18478
+ * @param when - Event timestamp
18479
+ * @param self - ClientPartial instance reference
19084
18480
  */
19085
- class OptimizerSchemaService {
19086
- constructor() {
19087
- this.loggerService = inject(TYPES.loggerService);
19088
- this._registry = new functoolsKit.ToolRegistry("optimizerSchema");
19089
- /**
19090
- * Registers a new optimizer schema.
19091
- * Validates required fields before registration.
19092
- *
19093
- * @param key - Unique optimizer name
19094
- * @param value - Optimizer schema configuration
19095
- * @throws Error if schema validation fails
19096
- */
19097
- this.register = (key, value) => {
19098
- this.loggerService.log(`optimizerSchemaService register`, { key });
19099
- this.validateShallow(value);
19100
- this._registry = this._registry.register(key, value);
18481
+ const HANDLE_LOSS_FN = async (symbol, data, currentPrice, lossPercent, backtest, when, self) => {
18482
+ if (self._states === NEED_FETCH$1) {
18483
+ throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
18484
+ }
18485
+ if (data.id !== self.params.signalId) {
18486
+ throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
18487
+ }
18488
+ let state = self._states.get(data.id);
18489
+ if (!state) {
18490
+ state = {
18491
+ profitLevels: new Set(),
18492
+ lossLevels: new Set(),
19101
18493
  };
19102
- /**
19103
- * Validates optimizer schema structure.
19104
- * Checks required fields: optimizerName, rangeTrain, source, getPrompt.
19105
- *
19106
- * @param optimizerSchema - Schema to validate
19107
- * @throws Error if validation fails
19108
- */
19109
- this.validateShallow = (optimizerSchema) => {
19110
- this.loggerService.log(`optimizerTemplateService validateShallow`, {
19111
- optimizerSchema,
18494
+ self._states.set(data.id, state);
18495
+ }
18496
+ const absLoss = Math.abs(lossPercent);
18497
+ let shouldPersist = false;
18498
+ for (const level of LOSS_LEVELS) {
18499
+ if (absLoss >= level && !state.lossLevels.has(level)) {
18500
+ state.lossLevels.add(level);
18501
+ shouldPersist = true;
18502
+ self.params.logger.debug("ClientPartial loss level reached", {
18503
+ symbol,
18504
+ signalId: data.id,
18505
+ level,
18506
+ lossPercent,
18507
+ backtest,
19112
18508
  });
19113
- if (typeof optimizerSchema.optimizerName !== "string") {
19114
- throw new Error(`optimizer template validation failed: missing optimizerName`);
19115
- }
19116
- if (!Array.isArray(optimizerSchema.rangeTrain) || optimizerSchema.rangeTrain.length === 0) {
19117
- throw new Error(`optimizer template validation failed: rangeTrain must be a non-empty array for optimizerName=${optimizerSchema.optimizerName}`);
19118
- }
19119
- if (!Array.isArray(optimizerSchema.source) || optimizerSchema.source.length === 0) {
19120
- throw new Error(`optimizer template validation failed: source must be a non-empty array for optimizerName=${optimizerSchema.optimizerName}`);
19121
- }
19122
- if (typeof optimizerSchema.getPrompt !== "function") {
19123
- throw new Error(`optimizer template validation failed: getPrompt must be a function for optimizerName=${optimizerSchema.optimizerName}`);
19124
- }
19125
- };
19126
- /**
19127
- * Partially overrides an existing optimizer schema.
19128
- * Merges provided values with existing schema.
19129
- *
19130
- * @param key - Optimizer name to override
19131
- * @param value - Partial schema values to merge
19132
- * @returns Updated complete schema
19133
- * @throws Error if optimizer not found
19134
- */
19135
- this.override = (key, value) => {
19136
- this.loggerService.log(`optimizerSchemaService override`, { key });
19137
- this._registry = this._registry.override(key, value);
19138
- return this._registry.get(key);
19139
- };
19140
- /**
19141
- * Retrieves optimizer schema by name.
19142
- *
19143
- * @param key - Optimizer name
19144
- * @returns Complete optimizer schema
19145
- * @throws Error if optimizer not found
19146
- */
19147
- this.get = (key) => {
19148
- this.loggerService.log(`optimizerSchemaService get`, { key });
19149
- return this._registry.get(key);
19150
- };
18509
+ await self.params.onLoss(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
18510
+ }
19151
18511
  }
19152
- }
19153
-
18512
+ if (shouldPersist) {
18513
+ await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
18514
+ }
18515
+ };
19154
18516
  /**
19155
- * Service for validating optimizer existence and managing optimizer registry.
19156
- * Maintains a Map of registered optimizers for validation purposes.
18517
+ * Internal initialization function for ClientPartial.
18518
+ *
18519
+ * Loads persisted partial state from disk and restores in-memory Maps.
18520
+ * Converts serialized arrays back to Sets for O(1) lookups.
18521
+ *
18522
+ * ONLY runs in LIVE mode (backtest=false). In backtest mode, state is not persisted.
19157
18523
  *
19158
- * Uses memoization for efficient repeated validation checks.
19159
- */
19160
- class OptimizerValidationService {
19161
- constructor() {
19162
- this.loggerService = inject(TYPES.loggerService);
19163
- this._optimizerMap = new Map();
19164
- /**
19165
- * Adds optimizer to validation registry.
19166
- * Prevents duplicate optimizer names.
19167
- *
19168
- * @param optimizerName - Unique optimizer identifier
19169
- * @param optimizerSchema - Complete optimizer schema
19170
- * @throws Error if optimizer with same name already exists
19171
- */
19172
- this.addOptimizer = (optimizerName, optimizerSchema) => {
19173
- this.loggerService.log("optimizerValidationService addOptimizer", {
19174
- optimizerName,
19175
- optimizerSchema,
19176
- });
19177
- if (this._optimizerMap.has(optimizerName)) {
19178
- throw new Error(`optimizer ${optimizerName} already exist`);
19179
- }
19180
- this._optimizerMap.set(optimizerName, optimizerSchema);
19181
- };
19182
- /**
19183
- * Validates that optimizer exists in registry.
19184
- * Memoized for performance on repeated checks.
19185
- *
19186
- * @param optimizerName - Optimizer name to validate
19187
- * @param source - Source method name for error messages
19188
- * @throws Error if optimizer not found
19189
- */
19190
- this.validate = functoolsKit.memoize(([optimizerName]) => optimizerName, (optimizerName, source) => {
19191
- this.loggerService.log("optimizerValidationService validate", {
19192
- optimizerName,
19193
- source,
19194
- });
19195
- const optimizer = this._optimizerMap.get(optimizerName);
19196
- if (!optimizer) {
19197
- throw new Error(`optimizer ${optimizerName} not found source=${source}`);
19198
- }
19199
- return true;
19200
- });
19201
- /**
19202
- * Lists all registered optimizer schemas.
19203
- *
19204
- * @returns Array of all optimizer schemas
19205
- */
19206
- this.list = async () => {
19207
- this.loggerService.log("optimizerValidationService list");
19208
- return Array.from(this._optimizerMap.values());
19209
- };
19210
- }
19211
- }
19212
-
19213
- const METHOD_NAME_GET_DATA = "optimizerGlobalService getData";
19214
- const METHOD_NAME_GET_CODE = "optimizerGlobalService getCode";
19215
- const METHOD_NAME_DUMP = "optimizerGlobalService dump";
19216
- /**
19217
- * Global service for optimizer operations with validation.
19218
- * Entry point for public API, performs validation before delegating to ConnectionService.
19219
- *
19220
- * Workflow:
19221
- * 1. Log operation
19222
- * 2. Validate optimizer exists
19223
- * 3. Delegate to OptimizerConnectionService
19224
- */
19225
- class OptimizerGlobalService {
19226
- constructor() {
19227
- this.loggerService = inject(TYPES.loggerService);
19228
- this.optimizerConnectionService = inject(TYPES.optimizerConnectionService);
19229
- this.optimizerValidationService = inject(TYPES.optimizerValidationService);
19230
- /**
19231
- * Fetches data from all sources and generates strategy metadata.
19232
- * Validates optimizer existence before execution.
19233
- *
19234
- * @param symbol - Trading pair symbol
19235
- * @param optimizerName - Optimizer identifier
19236
- * @returns Array of generated strategies with conversation context
19237
- * @throws Error if optimizer not found
19238
- */
19239
- this.getData = async (symbol, optimizerName) => {
19240
- this.loggerService.log(METHOD_NAME_GET_DATA, {
19241
- symbol,
19242
- optimizerName,
19243
- });
19244
- this.optimizerValidationService.validate(optimizerName, METHOD_NAME_GET_DATA);
19245
- return await this.optimizerConnectionService.getData(symbol, optimizerName);
19246
- };
19247
- /**
19248
- * Generates complete executable strategy code.
19249
- * Validates optimizer existence before execution.
19250
- *
19251
- * @param symbol - Trading pair symbol
19252
- * @param optimizerName - Optimizer identifier
19253
- * @returns Generated TypeScript/JavaScript code as string
19254
- * @throws Error if optimizer not found
19255
- */
19256
- this.getCode = async (symbol, optimizerName) => {
19257
- this.loggerService.log(METHOD_NAME_GET_CODE, {
19258
- symbol,
19259
- optimizerName,
19260
- });
19261
- this.optimizerValidationService.validate(optimizerName, METHOD_NAME_GET_CODE);
19262
- return await this.optimizerConnectionService.getCode(symbol, optimizerName);
19263
- };
19264
- /**
19265
- * Generates and saves strategy code to file.
19266
- * Validates optimizer existence before execution.
19267
- *
19268
- * @param symbol - Trading pair symbol
19269
- * @param optimizerName - Optimizer identifier
19270
- * @param path - Output directory path (optional)
19271
- * @throws Error if optimizer not found
19272
- */
19273
- this.dump = async (symbol, optimizerName, path) => {
19274
- this.loggerService.log(METHOD_NAME_DUMP, {
19275
- symbol,
19276
- optimizerName,
19277
- path,
19278
- });
19279
- this.optimizerValidationService.validate(optimizerName, METHOD_NAME_DUMP);
19280
- return await this.optimizerConnectionService.dump(symbol, optimizerName, path);
19281
- };
19282
- }
19283
- }
19284
-
19285
- const ITERATION_LIMIT = 25;
19286
- const DEFAULT_SOURCE_NAME = "unknown";
19287
- const CREATE_PREFIX_FN = () => (Math.random() + 1).toString(36).substring(7);
19288
- /**
19289
- * Wrapper to call onSourceData callback with error handling.
19290
- * Catches and logs any errors thrown by the user-provided callback.
19291
- */
19292
- const CALL_SOURCE_DATA_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, name, data, startDate, endDate) => {
19293
- if (self.params.callbacks?.onSourceData) {
19294
- await self.params.callbacks.onSourceData(symbol, name, data, startDate, endDate);
19295
- }
19296
- }, {
19297
- fallback: (error) => {
19298
- const message = "ClientOptimizer CALL_SOURCE_DATA_CALLBACKS_FN thrown";
19299
- const payload = {
19300
- error: functoolsKit.errorData(error),
19301
- message: functoolsKit.getErrorMessage(error),
19302
- };
19303
- bt.loggerService.warn(message, payload);
19304
- console.warn(message, payload);
19305
- errorEmitter.next(error);
19306
- },
19307
- });
19308
- /**
19309
- * Wrapper to call onData callback with error handling.
19310
- * Catches and logs any errors thrown by the user-provided callback.
19311
- */
19312
- const CALL_DATA_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, strategyList) => {
19313
- if (self.params.callbacks?.onData) {
19314
- await self.params.callbacks.onData(symbol, strategyList);
19315
- }
19316
- }, {
19317
- fallback: (error) => {
19318
- const message = "ClientOptimizer CALL_DATA_CALLBACKS_FN thrown";
19319
- const payload = {
19320
- error: functoolsKit.errorData(error),
19321
- message: functoolsKit.getErrorMessage(error),
19322
- };
19323
- bt.loggerService.warn(message, payload);
19324
- console.warn(message, payload);
19325
- errorEmitter.next(error);
19326
- },
19327
- });
19328
- /**
19329
- * Wrapper to call onCode callback with error handling.
19330
- * Catches and logs any errors thrown by the user-provided callback.
19331
- */
19332
- const CALL_CODE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, code) => {
19333
- if (self.params.callbacks?.onCode) {
19334
- await self.params.callbacks.onCode(symbol, code);
19335
- }
19336
- }, {
19337
- fallback: (error) => {
19338
- const message = "ClientOptimizer CALL_CODE_CALLBACKS_FN thrown";
19339
- const payload = {
19340
- error: functoolsKit.errorData(error),
19341
- message: functoolsKit.getErrorMessage(error),
19342
- };
19343
- bt.loggerService.warn(message, payload);
19344
- console.warn(message, payload);
19345
- errorEmitter.next(error);
19346
- },
19347
- });
19348
- /**
19349
- * Wrapper to call onDump callback with error handling.
19350
- * Catches and logs any errors thrown by the user-provided callback.
19351
- */
19352
- const CALL_DUMP_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, filepath) => {
19353
- if (self.params.callbacks?.onDump) {
19354
- await self.params.callbacks.onDump(symbol, filepath);
19355
- }
19356
- }, {
19357
- fallback: (error) => {
19358
- const message = "ClientOptimizer CALL_DUMP_CALLBACKS_FN thrown";
19359
- const payload = {
19360
- error: functoolsKit.errorData(error),
19361
- message: functoolsKit.getErrorMessage(error),
19362
- };
19363
- bt.loggerService.warn(message, payload);
19364
- console.warn(message, payload);
19365
- errorEmitter.next(error);
19366
- },
19367
- });
19368
- /**
19369
- * Default user message formatter.
19370
- * Delegates to template's getUserMessage method.
19371
- *
19372
- * @param symbol - Trading pair symbol
19373
- * @param data - Fetched data array
19374
- * @param name - Source name
19375
- * @param self - ClientOptimizer instance
19376
- * @returns Formatted user message content
19377
- */
19378
- const DEFAULT_USER_FN = async (symbol, data, name, self) => {
19379
- return await self.params.template.getUserMessage(symbol, data, name);
19380
- };
19381
- /**
19382
- * Default assistant message formatter.
19383
- * Delegates to template's getAssistantMessage method.
19384
- *
19385
- * @param symbol - Trading pair symbol
19386
- * @param data - Fetched data array
19387
- * @param name - Source name
19388
- * @param self - ClientOptimizer instance
19389
- * @returns Formatted assistant message content
19390
- */
19391
- const DEFAULT_ASSISTANT_FN = async (symbol, data, name, self) => {
19392
- return await self.params.template.getAssistantMessage(symbol, data, name);
19393
- };
19394
- /**
19395
- * Resolves paginated data from source with deduplication.
19396
- * Uses iterateDocuments to handle pagination automatically.
19397
- *
19398
- * @param fetch - Source fetch function
19399
- * @param filterData - Filter arguments (symbol, dates)
19400
- * @returns Deduplicated array of all fetched data
19401
- */
19402
- const RESOLVE_PAGINATION_FN = async (fetch, filterData) => {
19403
- const iterator = functoolsKit.iterateDocuments({
19404
- limit: ITERATION_LIMIT,
19405
- async createRequest({ limit, offset }) {
19406
- return await fetch({
19407
- symbol: filterData.symbol,
19408
- startDate: filterData.startDate,
19409
- endDate: filterData.endDate,
19410
- limit,
19411
- offset,
19412
- });
19413
- },
19414
- });
19415
- const distinct = functoolsKit.distinctDocuments(iterator, (data) => data.id);
19416
- return await functoolsKit.resolveDocuments(distinct);
19417
- };
19418
- /**
19419
- * Collects data from all sources and generates strategy metadata.
19420
- * Iterates through training ranges, fetches data from each source,
19421
- * builds LLM conversation history, and generates strategy prompts.
19422
- *
19423
- * @param symbol - Trading pair symbol
19424
- * @param self - ClientOptimizer instance
19425
- * @returns Array of generated strategies with conversation context
19426
- */
19427
- const GET_STRATEGY_DATA_FN = async (symbol, self) => {
19428
- const strategyList = [];
19429
- const totalSources = self.params.rangeTrain.length * self.params.source.length;
19430
- let processedSources = 0;
19431
- for (const { startDate, endDate } of self.params.rangeTrain) {
19432
- const messageList = [];
19433
- for (const source of self.params.source) {
19434
- // Emit progress event at the start of processing each source
19435
- await self.onProgress({
19436
- optimizerName: self.params.optimizerName,
19437
- symbol,
19438
- totalSources,
19439
- processedSources,
19440
- progress: totalSources > 0 ? processedSources / totalSources : 0,
19441
- });
19442
- if (typeof source === "function") {
19443
- const data = await RESOLVE_PAGINATION_FN(source, {
19444
- symbol,
19445
- startDate,
19446
- endDate,
19447
- });
19448
- await CALL_SOURCE_DATA_CALLBACKS_FN(self, symbol, DEFAULT_SOURCE_NAME, data, startDate, endDate);
19449
- const [userContent, assistantContent] = await Promise.all([
19450
- DEFAULT_USER_FN(symbol, data, DEFAULT_SOURCE_NAME, self),
19451
- DEFAULT_ASSISTANT_FN(symbol, data, DEFAULT_SOURCE_NAME, self),
19452
- ]);
19453
- messageList.push({
19454
- role: "user",
19455
- content: userContent,
19456
- }, {
19457
- role: "assistant",
19458
- content: assistantContent,
19459
- });
19460
- processedSources++;
19461
- }
19462
- else {
19463
- const { fetch, name = DEFAULT_SOURCE_NAME, assistant = DEFAULT_ASSISTANT_FN, user = DEFAULT_USER_FN, } = source;
19464
- const data = await RESOLVE_PAGINATION_FN(fetch, {
19465
- symbol,
19466
- startDate,
19467
- endDate,
19468
- });
19469
- await CALL_SOURCE_DATA_CALLBACKS_FN(self, symbol, name, data, startDate, endDate);
19470
- const [userContent, assistantContent] = await Promise.all([
19471
- user(symbol, data, name, self),
19472
- assistant(symbol, data, name, self),
19473
- ]);
19474
- messageList.push({
19475
- role: "user",
19476
- content: userContent,
19477
- }, {
19478
- role: "assistant",
19479
- content: assistantContent,
19480
- });
19481
- processedSources++;
19482
- }
19483
- const name = "name" in source
19484
- ? source.name || DEFAULT_SOURCE_NAME
19485
- : DEFAULT_SOURCE_NAME;
19486
- strategyList.push({
19487
- symbol,
19488
- name,
19489
- messages: messageList,
19490
- strategy: await self.params.getPrompt(symbol, messageList),
19491
- });
19492
- }
19493
- }
19494
- // Emit final progress event (100%)
19495
- await self.onProgress({
19496
- optimizerName: self.params.optimizerName,
19497
- symbol,
19498
- totalSources,
19499
- processedSources: totalSources,
19500
- progress: 1.0,
19501
- });
19502
- await CALL_DATA_CALLBACKS_FN(self, symbol, strategyList);
19503
- return strategyList;
19504
- };
19505
- /**
19506
- * Generates complete executable strategy code.
19507
- * Assembles all components: imports, helpers, exchange, frames, strategies, walker, launcher.
19508
- *
19509
- * @param symbol - Trading pair symbol
19510
- * @param self - ClientOptimizer instance
19511
- * @returns Generated TypeScript/JavaScript code as string
19512
- */
19513
- const GET_STRATEGY_CODE_FN = async (symbol, self) => {
19514
- const strategyData = await self.getData(symbol);
19515
- const prefix = CREATE_PREFIX_FN();
19516
- const sections = [];
19517
- const exchangeName = `${prefix}_exchange`;
19518
- // 1. Top banner with imports
19519
- {
19520
- sections.push(await self.params.template.getTopBanner(symbol));
19521
- sections.push("");
19522
- }
19523
- // 2. JSON dump helper function
19524
- {
19525
- sections.push(await self.params.template.getJsonDumpTemplate(symbol));
19526
- sections.push("");
19527
- }
19528
- // 3. Helper functions (text and json)
19529
- {
19530
- sections.push(await self.params.template.getTextTemplate(symbol));
19531
- sections.push("");
19532
- }
19533
- {
19534
- sections.push(await self.params.template.getJsonTemplate(symbol));
19535
- sections.push("");
19536
- }
19537
- // 4. Exchange template (assuming first strategy has exchange info)
19538
- {
19539
- sections.push(await self.params.template.getExchangeTemplate(symbol, exchangeName));
19540
- sections.push("");
19541
- }
19542
- // 5. Train frame templates
19543
- {
19544
- for (let i = 0; i < self.params.rangeTrain.length; i++) {
19545
- const range = self.params.rangeTrain[i];
19546
- const frameName = `${prefix}_train_frame-${i + 1}`;
19547
- sections.push(await self.params.template.getFrameTemplate(symbol, frameName, "1m", // default interval
19548
- range.startDate, range.endDate));
19549
- sections.push("");
19550
- }
19551
- }
19552
- // 6. Test frame template
19553
- {
19554
- const testFrameName = `${prefix}_test_frame`;
19555
- sections.push(await self.params.template.getFrameTemplate(symbol, testFrameName, "1m", // default interval
19556
- self.params.rangeTest.startDate, self.params.rangeTest.endDate));
19557
- sections.push("");
19558
- }
19559
- // 7. Strategy templates for each generated strategy
19560
- {
19561
- for (let i = 0; i < strategyData.length; i++) {
19562
- const strategy = strategyData[i];
19563
- const strategyName = `${prefix}_strategy-${i + 1}`;
19564
- const interval = "5m"; // default interval
19565
- sections.push(await self.params.template.getStrategyTemplate(strategyName, interval, strategy.strategy));
19566
- sections.push("");
19567
- }
19568
- }
19569
- // 8. Walker template (uses test frame for validation)
19570
- {
19571
- const walkerName = `${prefix}_walker`;
19572
- const testFrameName = `${prefix}_test_frame`;
19573
- const strategies = strategyData.map((_, i) => `${prefix}_strategy-${i + 1}`);
19574
- sections.push(await self.params.template.getWalkerTemplate(walkerName, `${exchangeName}`, testFrameName, strategies));
19575
- sections.push("");
19576
- }
19577
- // 9. Launcher template
19578
- {
19579
- const walkerName = `${prefix}_walker`;
19580
- sections.push(await self.params.template.getLauncherTemplate(symbol, walkerName));
19581
- sections.push("");
19582
- }
19583
- const code = sections.join("\n");
19584
- await CALL_CODE_CALLBACKS_FN(self, symbol, code);
19585
- return code;
19586
- };
19587
- /**
19588
- * Saves generated strategy code to file.
19589
- * Creates directory if needed, writes .mjs file with generated code.
19590
- *
19591
- * @param symbol - Trading pair symbol
19592
- * @param path - Output directory path
19593
- * @param self - ClientOptimizer instance
19594
- */
19595
- const GET_STRATEGY_DUMP_FN = async (symbol, path$1, self) => {
19596
- const report = await self.getCode(symbol);
19597
- try {
19598
- const dir = path.join(process.cwd(), path$1);
19599
- await fs.mkdir(dir, { recursive: true });
19600
- const filename = `${self.params.optimizerName}_${symbol}.mjs`;
19601
- const filepath = path.join(dir, filename);
19602
- await fs.writeFile(filepath, report, "utf-8");
19603
- self.params.logger.info(`Optimizer report saved: ${filepath}`);
19604
- await CALL_DUMP_CALLBACKS_FN(self, symbol, filepath);
19605
- }
19606
- catch (error) {
19607
- self.params.logger.warn(`Failed to save optimizer report:`, error);
19608
- throw error;
19609
- }
19610
- };
19611
- /**
19612
- * Client implementation for optimizer operations.
19613
- *
19614
- * Features:
19615
- * - Data collection from multiple sources with pagination
19616
- * - LLM conversation history building
19617
- * - Strategy code generation with templates
19618
- * - File export with callbacks
19619
- *
19620
- * Used by OptimizerConnectionService to create optimizer instances.
19621
- */
19622
- class ClientOptimizer {
19623
- constructor(params, onProgress) {
19624
- this.params = params;
19625
- this.onProgress = onProgress;
19626
- /**
19627
- * Fetches data from all sources and generates strategy metadata.
19628
- * Processes each training range and builds LLM conversation history.
19629
- *
19630
- * @param symbol - Trading pair symbol
19631
- * @returns Array of generated strategies with conversation context
19632
- */
19633
- this.getData = async (symbol) => {
19634
- this.params.logger.debug("ClientOptimizer getData", {
19635
- symbol,
19636
- });
19637
- return await GET_STRATEGY_DATA_FN(symbol, this);
19638
- };
19639
- /**
19640
- * Generates complete executable strategy code.
19641
- * Includes imports, helpers, strategies, walker, and launcher.
19642
- *
19643
- * @param symbol - Trading pair symbol
19644
- * @returns Generated TypeScript/JavaScript code as string
19645
- */
19646
- this.getCode = async (symbol) => {
19647
- this.params.logger.debug("ClientOptimizer getCode", {
19648
- symbol,
19649
- });
19650
- return await GET_STRATEGY_CODE_FN(symbol, this);
19651
- };
19652
- /**
19653
- * Generates and saves strategy code to file.
19654
- * Creates directory if needed, writes .mjs file.
19655
- *
19656
- * @param symbol - Trading pair symbol
19657
- * @param path - Output directory path (default: "./")
19658
- */
19659
- this.dump = async (symbol, path = "./") => {
19660
- this.params.logger.debug("ClientOptimizer dump", {
19661
- symbol,
19662
- path,
19663
- });
19664
- return await GET_STRATEGY_DUMP_FN(symbol, path, this);
19665
- };
19666
- }
19667
- }
19668
-
19669
- /**
19670
- * Callback function for emitting progress events to progressOptimizerEmitter.
19671
- */
19672
- const COMMIT_PROGRESS_FN = async (progress) => progressOptimizerEmitter.next(progress);
19673
- /**
19674
- * Service for creating and caching optimizer client instances.
19675
- * Handles dependency injection and template merging.
19676
- *
19677
- * Features:
19678
- * - Memoized optimizer instances (one per optimizerName)
19679
- * - Template merging (custom + defaults)
19680
- * - Logger injection
19681
- * - Delegates to ClientOptimizer for actual operations
19682
- */
19683
- class OptimizerConnectionService {
19684
- constructor() {
19685
- this.loggerService = inject(TYPES.loggerService);
19686
- this.optimizerSchemaService = inject(TYPES.optimizerSchemaService);
19687
- this.optimizerTemplateService = inject(TYPES.optimizerTemplateService);
19688
- /**
19689
- * Creates or retrieves cached optimizer instance.
19690
- * Memoized by optimizerName for performance.
19691
- *
19692
- * Merges custom templates from schema with defaults from OptimizerTemplateService.
19693
- *
19694
- * @param optimizerName - Unique optimizer identifier
19695
- * @returns ClientOptimizer instance with resolved dependencies
19696
- */
19697
- this.getOptimizer = functoolsKit.memoize(([optimizerName]) => `${optimizerName}`, (optimizerName) => {
19698
- const { getPrompt, rangeTest, rangeTrain, source, template: rawTemplate = {}, callbacks, } = this.optimizerSchemaService.get(optimizerName);
19699
- const { getAssistantMessage = this.optimizerTemplateService.getAssistantMessage, getExchangeTemplate = this.optimizerTemplateService.getExchangeTemplate, getFrameTemplate = this.optimizerTemplateService.getFrameTemplate, getJsonDumpTemplate = this.optimizerTemplateService.getJsonDumpTemplate, getJsonTemplate = this.optimizerTemplateService.getJsonTemplate, getLauncherTemplate = this.optimizerTemplateService.getLauncherTemplate, getStrategyTemplate = this.optimizerTemplateService.getStrategyTemplate, getTextTemplate = this.optimizerTemplateService.getTextTemplate, getWalkerTemplate = this.optimizerTemplateService.getWalkerTemplate, getTopBanner = this.optimizerTemplateService.getTopBanner, getUserMessage = this.optimizerTemplateService.getUserMessage, } = rawTemplate;
19700
- const template = {
19701
- getAssistantMessage,
19702
- getExchangeTemplate,
19703
- getFrameTemplate,
19704
- getJsonDumpTemplate,
19705
- getJsonTemplate,
19706
- getLauncherTemplate,
19707
- getStrategyTemplate,
19708
- getTextTemplate,
19709
- getWalkerTemplate,
19710
- getTopBanner,
19711
- getUserMessage,
19712
- };
19713
- return new ClientOptimizer({
19714
- optimizerName,
19715
- logger: this.loggerService,
19716
- getPrompt,
19717
- rangeTest,
19718
- rangeTrain,
19719
- source,
19720
- template,
19721
- callbacks,
19722
- }, COMMIT_PROGRESS_FN);
19723
- });
19724
- /**
19725
- * Fetches data from all sources and generates strategy metadata.
19726
- *
19727
- * @param symbol - Trading pair symbol
19728
- * @param optimizerName - Optimizer identifier
19729
- * @returns Array of generated strategies with conversation context
19730
- */
19731
- this.getData = async (symbol, optimizerName) => {
19732
- this.loggerService.log("optimizerConnectionService getData", {
19733
- symbol,
19734
- optimizerName,
19735
- });
19736
- const optimizer = this.getOptimizer(optimizerName);
19737
- return await optimizer.getData(symbol);
19738
- };
19739
- /**
19740
- * Generates complete executable strategy code.
19741
- *
19742
- * @param symbol - Trading pair symbol
19743
- * @param optimizerName - Optimizer identifier
19744
- * @returns Generated TypeScript/JavaScript code as string
19745
- */
19746
- this.getCode = async (symbol, optimizerName) => {
19747
- this.loggerService.log("optimizerConnectionService getCode", {
19748
- symbol,
19749
- optimizerName,
19750
- });
19751
- const optimizer = this.getOptimizer(optimizerName);
19752
- return await optimizer.getCode(symbol);
19753
- };
19754
- /**
19755
- * Generates and saves strategy code to file.
19756
- *
19757
- * @param symbol - Trading pair symbol
19758
- * @param optimizerName - Optimizer identifier
19759
- * @param path - Output directory path (optional)
19760
- */
19761
- this.dump = async (symbol, optimizerName, path) => {
19762
- this.loggerService.log("optimizerConnectionService getCode", {
19763
- symbol,
19764
- optimizerName,
19765
- });
19766
- const optimizer = this.getOptimizer(optimizerName);
19767
- return await optimizer.dump(symbol, path);
19768
- };
19769
- }
19770
- }
19771
-
19772
- /**
19773
- * Symbol marker indicating that partial state needs initialization.
19774
- * Used as sentinel value for _states before waitForInit() is called.
19775
- */
19776
- const NEED_FETCH$1 = Symbol("need_fetch");
19777
- /**
19778
- * Array of profit level milestones to track (10%, 20%, ..., 100%).
19779
- * Each level is checked during profit() method to emit events for newly reached levels.
19780
- */
19781
- const PROFIT_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
19782
- /**
19783
- * Array of loss level milestones to track (-10%, -20%, ..., -100%).
19784
- * Each level is checked during loss() method to emit events for newly reached levels.
19785
- */
19786
- const LOSS_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
19787
- /**
19788
- * Internal profit handler function for ClientPartial.
19789
- *
19790
- * Checks which profit levels have been reached and emits events for new levels only.
19791
- * Uses Set-based deduplication to prevent duplicate events.
19792
- *
19793
- * @param symbol - Trading pair symbol
19794
- * @param data - Signal row data
19795
- * @param currentPrice - Current market price
19796
- * @param revenuePercent - Current profit percentage (positive value)
19797
- * @param backtest - True if backtest mode
19798
- * @param when - Event timestamp
19799
- * @param self - ClientPartial instance reference
19800
- */
19801
- const HANDLE_PROFIT_FN = async (symbol, data, currentPrice, revenuePercent, backtest, when, self) => {
19802
- if (self._states === NEED_FETCH$1) {
19803
- throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
19804
- }
19805
- if (data.id !== self.params.signalId) {
19806
- throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
19807
- }
19808
- let state = self._states.get(data.id);
19809
- if (!state) {
19810
- state = {
19811
- profitLevels: new Set(),
19812
- lossLevels: new Set(),
19813
- };
19814
- self._states.set(data.id, state);
19815
- }
19816
- let shouldPersist = false;
19817
- for (const level of PROFIT_LEVELS) {
19818
- if (revenuePercent >= level && !state.profitLevels.has(level)) {
19819
- state.profitLevels.add(level);
19820
- shouldPersist = true;
19821
- self.params.logger.debug("ClientPartial profit level reached", {
19822
- symbol,
19823
- signalId: data.id,
19824
- level,
19825
- revenuePercent,
19826
- backtest,
19827
- });
19828
- await self.params.onProfit(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
19829
- }
19830
- }
19831
- if (shouldPersist) {
19832
- await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
19833
- }
19834
- };
19835
- /**
19836
- * Internal loss handler function for ClientPartial.
19837
- *
19838
- * Checks which loss levels have been reached and emits events for new levels only.
19839
- * Uses Set-based deduplication to prevent duplicate events.
19840
- * Converts negative lossPercent to absolute value for level comparison.
19841
- *
19842
- * @param symbol - Trading pair symbol
19843
- * @param data - Signal row data
19844
- * @param currentPrice - Current market price
19845
- * @param lossPercent - Current loss percentage (negative value)
19846
- * @param backtest - True if backtest mode
19847
- * @param when - Event timestamp
19848
- * @param self - ClientPartial instance reference
19849
- */
19850
- const HANDLE_LOSS_FN = async (symbol, data, currentPrice, lossPercent, backtest, when, self) => {
19851
- if (self._states === NEED_FETCH$1) {
19852
- throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
19853
- }
19854
- if (data.id !== self.params.signalId) {
19855
- throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
19856
- }
19857
- let state = self._states.get(data.id);
19858
- if (!state) {
19859
- state = {
19860
- profitLevels: new Set(),
19861
- lossLevels: new Set(),
19862
- };
19863
- self._states.set(data.id, state);
19864
- }
19865
- const absLoss = Math.abs(lossPercent);
19866
- let shouldPersist = false;
19867
- for (const level of LOSS_LEVELS) {
19868
- if (absLoss >= level && !state.lossLevels.has(level)) {
19869
- state.lossLevels.add(level);
19870
- shouldPersist = true;
19871
- self.params.logger.debug("ClientPartial loss level reached", {
19872
- symbol,
19873
- signalId: data.id,
19874
- level,
19875
- lossPercent,
19876
- backtest,
19877
- });
19878
- await self.params.onLoss(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
19879
- }
19880
- }
19881
- if (shouldPersist) {
19882
- await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
19883
- }
19884
- };
19885
- /**
19886
- * Internal initialization function for ClientPartial.
19887
- *
19888
- * Loads persisted partial state from disk and restores in-memory Maps.
19889
- * Converts serialized arrays back to Sets for O(1) lookups.
19890
- *
19891
- * ONLY runs in LIVE mode (backtest=false). In backtest mode, state is not persisted.
19892
- *
19893
- * @param symbol - Trading pair symbol
19894
- * @param strategyName - Strategy identifier
19895
- * @param exchangeName - Exchange identifier
19896
- * @param backtest - True if backtest mode, false if live mode
19897
- * @param self - ClientPartial instance reference
18524
+ * @param symbol - Trading pair symbol
18525
+ * @param strategyName - Strategy identifier
18526
+ * @param exchangeName - Exchange identifier
18527
+ * @param backtest - True if backtest mode, false if live mode
18528
+ * @param self - ClientPartial instance reference
19898
18529
  */
19899
18530
  const WAIT_FOR_INIT_FN$1 = async (symbol, strategyName, exchangeName, backtest, self) => {
19900
18531
  self.params.logger.debug("ClientPartial waitForInit", {
@@ -22052,172 +20683,16 @@ class BreakevenGlobalService {
22052
20683
  this.clear = async (symbol, data, priceClose, backtest) => {
22053
20684
  this.loggerService.log("breakevenGlobalService clear", {
22054
20685
  symbol,
22055
- data,
22056
- priceClose,
22057
- backtest,
22058
- });
22059
- this.validate({
22060
- strategyName: data.strategyName,
22061
- exchangeName: data.exchangeName,
22062
- frameName: data.frameName
22063
- }, "breakevenGlobalService clear");
22064
- return await this.breakevenConnectionService.clear(symbol, data, priceClose, backtest);
22065
- };
22066
- }
22067
- }
22068
-
22069
- /**
22070
- * Warning threshold for message size in kilobytes.
22071
- * Messages exceeding this size trigger console warnings.
22072
- */
22073
- const WARN_KB = 30;
22074
- /**
22075
- * Internal function for dumping signal data to markdown files.
22076
- * Creates a directory structure with system prompts, user messages, and LLM output.
22077
- *
22078
- * @param signalId - Unique identifier for the result
22079
- * @param history - Array of message models from LLM conversation
22080
- * @param signal - Signal DTO with trade parameters
22081
- * @param outputDir - Output directory path (default: "./dump/strategy")
22082
- * @returns Promise that resolves when all files are written
22083
- */
22084
- const DUMP_SIGNAL_FN = async (signalId, history, signal, outputDir = "./dump/outline") => {
22085
- // Extract system messages and system reminders from existing data
22086
- const systemMessages = history.filter((m) => m.role === "system");
22087
- const userMessages = history.filter((m) => m.role === "user");
22088
- const subfolderPath = path.join(outputDir, String(signalId));
22089
- // Generate system prompt markdown
22090
- {
22091
- let summary = "# Outline Result Summary\n";
22092
- {
22093
- summary += "\n";
22094
- summary += `**ResultId**: ${String(signalId)}\n`;
22095
- summary += "\n";
22096
- }
22097
- if (signal) {
22098
- summary += "## Output Data\n\n";
22099
- summary += "```json\n";
22100
- summary += JSON.stringify(signal, null, 2);
22101
- summary += "\n```\n\n";
22102
- }
22103
- // Add system messages to summary
22104
- if (systemMessages.length > 0) {
22105
- summary += "## System Messages\n\n";
22106
- systemMessages.forEach((msg, idx) => {
22107
- summary += `### System Message ${idx + 1}\n\n`;
22108
- summary += msg.content;
22109
- summary += "\n";
22110
- });
22111
- }
22112
- await Markdown.writeData("outline", summary, {
22113
- path: subfolderPath,
22114
- file: "00_system_prompt.md",
22115
- symbol: "",
22116
- signalId: String(signalId),
22117
- strategyName: "",
22118
- exchangeName: "",
22119
- frameName: ""
22120
- });
22121
- }
22122
- // Generate user messages
22123
- {
22124
- await Promise.all(Array.from(userMessages.entries()).map(async ([idx, message]) => {
22125
- const messageNum = String(idx + 1).padStart(2, "0");
22126
- const contentFileName = `${messageNum}_user_message.md`;
22127
- {
22128
- const messageSizeBytes = Buffer.byteLength(message.content, "utf8");
22129
- const messageSizeKb = Math.floor(messageSizeBytes / 1024);
22130
- if (messageSizeKb > WARN_KB) {
22131
- console.warn(`User message ${idx + 1} is ${messageSizeBytes} bytes (${messageSizeKb}kb), which exceeds warning limit`);
22132
- }
22133
- }
22134
- let content = `# User Input ${idx + 1}\n\n`;
22135
- content += `**ResultId**: ${String(signalId)}\n\n`;
22136
- content += message.content;
22137
- content += "\n";
22138
- await Markdown.writeData("outline", content, {
22139
- path: subfolderPath,
22140
- file: contentFileName,
22141
- signalId: String(signalId),
22142
- symbol: "",
22143
- strategyName: "",
22144
- exchangeName: "",
22145
- frameName: ""
22146
- });
22147
- }));
22148
- }
22149
- // Generate LLM output
22150
- {
22151
- const messageNum = String(userMessages.length + 1).padStart(2, "0");
22152
- const contentFileName = `${messageNum}_llm_output.md`;
22153
- let content = "# Full Outline Result\n\n";
22154
- content += `**ResultId**: ${String(signalId)}\n\n`;
22155
- if (signal) {
22156
- content += "## Output Data\n\n";
22157
- content += "```json\n";
22158
- content += JSON.stringify(signal, null, 2);
22159
- content += "\n```\n";
22160
- }
22161
- await Markdown.writeData("outline", content, {
22162
- path: subfolderPath,
22163
- file: contentFileName,
22164
- symbol: "",
22165
- signalId: String(signalId),
22166
- strategyName: "",
22167
- exchangeName: "",
22168
- frameName: ""
22169
- });
22170
- }
22171
- };
22172
- /**
22173
- * Service for generating markdown documentation from LLM outline results.
22174
- * Used by AI Strategy Optimizer to save debug logs and conversation history.
22175
- *
22176
- * Creates directory structure:
22177
- * - ./dump/strategy/{signalId}/00_system_prompt.md - System messages and output data
22178
- * - ./dump/strategy/{signalId}/01_user_message.md - First user input
22179
- * - ./dump/strategy/{signalId}/02_user_message.md - Second user input
22180
- * - ./dump/strategy/{signalId}/XX_llm_output.md - Final LLM output
22181
- */
22182
- class OutlineMarkdownService {
22183
- constructor() {
22184
- /** Logger service injected via DI */
22185
- this.loggerService = inject(TYPES.loggerService);
22186
- /**
22187
- * Dumps signal data and conversation history to markdown files.
22188
- * Skips if directory already exists to avoid overwriting previous results.
22189
- *
22190
- * Generated files:
22191
- * - 00_system_prompt.md - System messages and output summary
22192
- * - XX_user_message.md - Each user message in separate file (numbered)
22193
- * - XX_llm_output.md - Final LLM output with signal data
22194
- *
22195
- * @param signalId - Unique identifier for the result (used as directory name)
22196
- * @param history - Array of message models from LLM conversation
22197
- * @param signal - Signal DTO with trade parameters (priceOpen, TP, SL, etc.)
22198
- * @param outputDir - Output directory path (default: "./dump/strategy")
22199
- * @returns Promise that resolves when all files are written
22200
- *
22201
- * @example
22202
- * ```typescript
22203
- * await outlineService.dumpSignal(
22204
- * "strategy-1",
22205
- * conversationHistory,
22206
- * { position: "long", priceTakeProfit: 51000, priceStopLoss: 49000, minuteEstimatedTime: 60 }
22207
- * );
22208
- * // Creates: ./dump/strategy/strategy-1/00_system_prompt.md
22209
- * // ./dump/strategy/strategy-1/01_user_message.md
22210
- * // ./dump/strategy/strategy-1/02_llm_output.md
22211
- * ```
22212
- */
22213
- this.dumpSignal = async (signalId, history, signal, outputDir = "./dump/strategy") => {
22214
- this.loggerService.log("outlineMarkdownService dumpSignal", {
22215
- signalId,
22216
- history,
22217
- signal,
22218
- outputDir,
20686
+ data,
20687
+ priceClose,
20688
+ backtest,
22219
20689
  });
22220
- return await DUMP_SIGNAL_FN(signalId, history, signal, outputDir);
20690
+ this.validate({
20691
+ strategyName: data.strategyName,
20692
+ exchangeName: data.exchangeName,
20693
+ frameName: data.frameName
20694
+ }, "breakevenGlobalService clear");
20695
+ return await this.breakevenConnectionService.clear(symbol, data, priceClose, backtest);
22221
20696
  };
22222
20697
  }
22223
20698
  }
@@ -24607,111 +23082,6 @@ class RiskReportService {
24607
23082
  }
24608
23083
  }
24609
23084
 
24610
- const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
24611
- /**
24612
- * Default fallback prompt configuration.
24613
- * Used when signal.prompt.cjs file is not found.
24614
- */
24615
- const DEFAULT_PROMPT = {
24616
- user: "",
24617
- system: [],
24618
- };
24619
- /**
24620
- * Lazy-loads and caches signal prompt configuration.
24621
- * Attempts to load from config/prompt/signal.prompt.cjs, falls back to DEFAULT_PROMPT if not found.
24622
- * Uses singleshot pattern to ensure configuration is loaded only once.
24623
- * @returns Prompt configuration with system and user prompts
24624
- */
24625
- const GET_PROMPT_FN = functoolsKit.singleshot(() => {
24626
- try {
24627
- const modulePath = require$1.resolve(path.join(process.cwd(), `./config/prompt/signal.prompt.cjs`));
24628
- console.log(`Using ${modulePath} implementation as signal.prompt.cjs`);
24629
- return require$1(modulePath);
24630
- }
24631
- catch (error) {
24632
- console.log(`Using empty fallback for signal.prompt.cjs`, error);
24633
- return DEFAULT_PROMPT;
24634
- }
24635
- });
24636
- /**
24637
- * Service for managing signal prompts for AI/LLM integrations.
24638
- *
24639
- * Provides access to system and user prompts configured in signal.prompt.cjs.
24640
- * Supports both static prompt arrays and dynamic prompt functions.
24641
- *
24642
- * Key responsibilities:
24643
- * - Lazy-loads prompt configuration from config/prompt/signal.prompt.cjs
24644
- * - Resolves system prompts (static arrays or async functions)
24645
- * - Provides user prompt strings
24646
- * - Falls back to empty prompts if configuration is missing
24647
- *
24648
- * Used for AI-powered signal analysis and strategy recommendations.
24649
- */
24650
- class SignalPromptService {
24651
- constructor() {
24652
- this.loggerService = inject(TYPES.loggerService);
24653
- /**
24654
- * Retrieves system prompts for AI context.
24655
- *
24656
- * System prompts can be:
24657
- * - Static array of strings (returned directly)
24658
- * - Async/sync function returning string array (executed and awaited)
24659
- * - Undefined (returns empty array)
24660
- *
24661
- * @param symbol - Trading symbol (e.g., "BTCUSDT")
24662
- * @param strategyName - Strategy identifier
24663
- * @param exchangeName - Exchange identifier
24664
- * @param frameName - Timeframe identifier
24665
- * @param backtest - Whether running in backtest mode
24666
- * @returns Promise resolving to array of system prompt strings
24667
- */
24668
- this.getSystemPrompt = async (symbol, strategyName, exchangeName, frameName, backtest) => {
24669
- this.loggerService.log("signalPromptService getSystemPrompt", {
24670
- symbol,
24671
- strategyName,
24672
- exchangeName,
24673
- frameName,
24674
- backtest,
24675
- });
24676
- const { system } = GET_PROMPT_FN();
24677
- if (Array.isArray(system)) {
24678
- return system;
24679
- }
24680
- if (typeof system === "function") {
24681
- return await system(symbol, strategyName, exchangeName, frameName, backtest);
24682
- }
24683
- return [];
24684
- };
24685
- /**
24686
- * Retrieves user prompt string for AI input.
24687
- *
24688
- * @param symbol - Trading symbol (e.g., "BTCUSDT")
24689
- * @param strategyName - Strategy identifier
24690
- * @param exchangeName - Exchange identifier
24691
- * @param frameName - Timeframe identifier
24692
- * @param backtest - Whether running in backtest mode
24693
- * @returns Promise resolving to user prompt string
24694
- */
24695
- this.getUserPrompt = async (symbol, strategyName, exchangeName, frameName, backtest) => {
24696
- this.loggerService.log("signalPromptService getUserPrompt", {
24697
- symbol,
24698
- strategyName,
24699
- exchangeName,
24700
- frameName,
24701
- backtest,
24702
- });
24703
- const { user } = GET_PROMPT_FN();
24704
- if (typeof user === "string") {
24705
- return user;
24706
- }
24707
- if (typeof user === "function") {
24708
- return await user(symbol, strategyName, exchangeName, frameName, backtest);
24709
- }
24710
- return "";
24711
- };
24712
- }
24713
- }
24714
-
24715
23085
  {
24716
23086
  provide(TYPES.loggerService, () => new LoggerService());
24717
23087
  }
@@ -24726,7 +23096,6 @@ class SignalPromptService {
24726
23096
  provide(TYPES.sizingConnectionService, () => new SizingConnectionService());
24727
23097
  provide(TYPES.riskConnectionService, () => new RiskConnectionService());
24728
23098
  provide(TYPES.actionConnectionService, () => new ActionConnectionService());
24729
- provide(TYPES.optimizerConnectionService, () => new OptimizerConnectionService());
24730
23099
  provide(TYPES.partialConnectionService, () => new PartialConnectionService());
24731
23100
  provide(TYPES.breakevenConnectionService, () => new BreakevenConnectionService());
24732
23101
  }
@@ -24738,7 +23107,6 @@ class SignalPromptService {
24738
23107
  provide(TYPES.sizingSchemaService, () => new SizingSchemaService());
24739
23108
  provide(TYPES.riskSchemaService, () => new RiskSchemaService());
24740
23109
  provide(TYPES.actionSchemaService, () => new ActionSchemaService());
24741
- provide(TYPES.optimizerSchemaService, () => new OptimizerSchemaService());
24742
23110
  }
24743
23111
  {
24744
23112
  provide(TYPES.exchangeCoreService, () => new ExchangeCoreService());
@@ -24749,7 +23117,6 @@ class SignalPromptService {
24749
23117
  {
24750
23118
  provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
24751
23119
  provide(TYPES.riskGlobalService, () => new RiskGlobalService());
24752
- provide(TYPES.optimizerGlobalService, () => new OptimizerGlobalService());
24753
23120
  provide(TYPES.partialGlobalService, () => new PartialGlobalService());
24754
23121
  provide(TYPES.breakevenGlobalService, () => new BreakevenGlobalService());
24755
23122
  }
@@ -24777,7 +23144,6 @@ class SignalPromptService {
24777
23144
  provide(TYPES.heatMarkdownService, () => new HeatMarkdownService());
24778
23145
  provide(TYPES.partialMarkdownService, () => new PartialMarkdownService());
24779
23146
  provide(TYPES.breakevenMarkdownService, () => new BreakevenMarkdownService());
24780
- provide(TYPES.outlineMarkdownService, () => new OutlineMarkdownService());
24781
23147
  provide(TYPES.riskMarkdownService, () => new RiskMarkdownService());
24782
23148
  }
24783
23149
  {
@@ -24799,16 +23165,9 @@ class SignalPromptService {
24799
23165
  provide(TYPES.sizingValidationService, () => new SizingValidationService());
24800
23166
  provide(TYPES.riskValidationService, () => new RiskValidationService());
24801
23167
  provide(TYPES.actionValidationService, () => new ActionValidationService());
24802
- provide(TYPES.optimizerValidationService, () => new OptimizerValidationService());
24803
23168
  provide(TYPES.configValidationService, () => new ConfigValidationService());
24804
23169
  provide(TYPES.columnValidationService, () => new ColumnValidationService());
24805
23170
  }
24806
- {
24807
- provide(TYPES.optimizerTemplateService, () => new OptimizerTemplateService());
24808
- }
24809
- {
24810
- provide(TYPES.signalPromptService, () => new SignalPromptService());
24811
- }
24812
23171
 
24813
23172
  const baseServices = {
24814
23173
  loggerService: inject(TYPES.loggerService),
@@ -24824,7 +23183,6 @@ const connectionServices = {
24824
23183
  sizingConnectionService: inject(TYPES.sizingConnectionService),
24825
23184
  riskConnectionService: inject(TYPES.riskConnectionService),
24826
23185
  actionConnectionService: inject(TYPES.actionConnectionService),
24827
- optimizerConnectionService: inject(TYPES.optimizerConnectionService),
24828
23186
  partialConnectionService: inject(TYPES.partialConnectionService),
24829
23187
  breakevenConnectionService: inject(TYPES.breakevenConnectionService),
24830
23188
  };
@@ -24836,7 +23194,6 @@ const schemaServices = {
24836
23194
  sizingSchemaService: inject(TYPES.sizingSchemaService),
24837
23195
  riskSchemaService: inject(TYPES.riskSchemaService),
24838
23196
  actionSchemaService: inject(TYPES.actionSchemaService),
24839
- optimizerSchemaService: inject(TYPES.optimizerSchemaService),
24840
23197
  };
24841
23198
  const coreServices = {
24842
23199
  exchangeCoreService: inject(TYPES.exchangeCoreService),
@@ -24847,7 +23204,6 @@ const coreServices = {
24847
23204
  const globalServices = {
24848
23205
  sizingGlobalService: inject(TYPES.sizingGlobalService),
24849
23206
  riskGlobalService: inject(TYPES.riskGlobalService),
24850
- optimizerGlobalService: inject(TYPES.optimizerGlobalService),
24851
23207
  partialGlobalService: inject(TYPES.partialGlobalService),
24852
23208
  breakevenGlobalService: inject(TYPES.breakevenGlobalService),
24853
23209
  };
@@ -24875,7 +23231,6 @@ const markdownServices = {
24875
23231
  heatMarkdownService: inject(TYPES.heatMarkdownService),
24876
23232
  partialMarkdownService: inject(TYPES.partialMarkdownService),
24877
23233
  breakevenMarkdownService: inject(TYPES.breakevenMarkdownService),
24878
- outlineMarkdownService: inject(TYPES.outlineMarkdownService),
24879
23234
  riskMarkdownService: inject(TYPES.riskMarkdownService),
24880
23235
  };
24881
23236
  const reportServices = {
@@ -24897,16 +23252,9 @@ const validationServices = {
24897
23252
  sizingValidationService: inject(TYPES.sizingValidationService),
24898
23253
  riskValidationService: inject(TYPES.riskValidationService),
24899
23254
  actionValidationService: inject(TYPES.actionValidationService),
24900
- optimizerValidationService: inject(TYPES.optimizerValidationService),
24901
23255
  configValidationService: inject(TYPES.configValidationService),
24902
23256
  columnValidationService: inject(TYPES.columnValidationService),
24903
23257
  };
24904
- const templateServices = {
24905
- optimizerTemplateService: inject(TYPES.optimizerTemplateService),
24906
- };
24907
- const promptServices = {
24908
- signalPromptService: inject(TYPES.signalPromptService),
24909
- };
24910
23258
  const backtest = {
24911
23259
  ...baseServices,
24912
23260
  ...contextServices,
@@ -24920,8 +23268,6 @@ const backtest = {
24920
23268
  ...markdownServices,
24921
23269
  ...reportServices,
24922
23270
  ...validationServices,
24923
- ...templateServices,
24924
- ...promptServices,
24925
23271
  };
24926
23272
  init();
24927
23273
  var bt = backtest;
@@ -25020,18 +23366,6 @@ const getSizingMap = async () => {
25020
23366
  }
25021
23367
  return sizingMap;
25022
23368
  };
25023
- /**
25024
- * Retrieves all registered optimizers as a map
25025
- * @private
25026
- * @returns Map of optimizer names
25027
- */
25028
- const getOptimizerMap = async () => {
25029
- const optimizerMap = {};
25030
- for (const { optimizerName } of await bt.optimizerValidationService.list()) {
25031
- Object.assign(optimizerMap, { [optimizerName]: optimizerName });
25032
- }
25033
- return optimizerMap;
25034
- };
25035
23369
  /**
25036
23370
  * Retrieves all registered walkers as a map
25037
23371
  * @private
@@ -25058,7 +23392,7 @@ const getWalkerMap = async () => {
25058
23392
  * @throws {Error} If any entity name is not found in its registry
25059
23393
  */
25060
23394
  const validateInternal = async (args) => {
25061
- const { ExchangeName = await getExchangeMap(), FrameName = await getFrameMap(), StrategyName = await getStrategyMap(), RiskName = await getRiskMap(), ActionName = await getActionMap(), SizingName = await getSizingMap(), OptimizerName = await getOptimizerMap(), WalkerName = await getWalkerMap(), } = args;
23395
+ const { ExchangeName = await getExchangeMap(), FrameName = await getFrameMap(), StrategyName = await getStrategyMap(), RiskName = await getRiskMap(), ActionName = await getActionMap(), SizingName = await getSizingMap(), WalkerName = await getWalkerMap(), } = args;
25062
23396
  for (const exchangeName of Object.values(ExchangeName)) {
25063
23397
  bt.exchangeValidationService.validate(exchangeName, METHOD_NAME);
25064
23398
  }
@@ -25077,9 +23411,6 @@ const validateInternal = async (args) => {
25077
23411
  for (const sizingName of Object.values(SizingName)) {
25078
23412
  bt.sizingValidationService.validate(sizingName, METHOD_NAME);
25079
23413
  }
25080
- for (const optimizerName of Object.values(OptimizerName)) {
25081
- bt.optimizerValidationService.validate(optimizerName, METHOD_NAME);
25082
- }
25083
23414
  for (const walkerName of Object.values(WalkerName)) {
25084
23415
  bt.walkerValidationService.validate(walkerName, METHOD_NAME);
25085
23416
  }
@@ -25088,7 +23419,7 @@ const validateInternal = async (args) => {
25088
23419
  * Validates the existence of all provided entity names across validation services.
25089
23420
  *
25090
23421
  * This function accepts enum objects for various entity types (exchanges, frames,
25091
- * strategies, risks, sizings, optimizers, walkers) and validates that each entity
23422
+ * strategies, risks, sizings, walkers) and validates that each entity
25092
23423
  * name exists in its respective registry. Validation results are memoized for performance.
25093
23424
  *
25094
23425
  * If no arguments are provided (or specific entity types are omitted), the function
@@ -25148,7 +23479,6 @@ const GET_FRAME_METHOD_NAME = "get.getFrameSchema";
25148
23479
  const GET_WALKER_METHOD_NAME = "get.getWalkerSchema";
25149
23480
  const GET_SIZING_METHOD_NAME = "get.getSizingSchema";
25150
23481
  const GET_RISK_METHOD_NAME = "get.getRiskSchema";
25151
- const GET_OPTIMIZER_METHOD_NAME = "get.getOptimizerSchema";
25152
23482
  const GET_ACTION_METHOD_NAME = "get.getActionSchema";
25153
23483
  /**
25154
23484
  * Retrieves a registered strategy schema by name.
@@ -25280,29 +23610,6 @@ function getRiskSchema(riskName) {
25280
23610
  bt.riskValidationService.validate(riskName, GET_RISK_METHOD_NAME);
25281
23611
  return bt.riskSchemaService.get(riskName);
25282
23612
  }
25283
- /**
25284
- * Retrieves a registered optimizer schema by name.
25285
- *
25286
- * @param optimizerName - Unique optimizer identifier
25287
- * @returns The optimizer schema configuration object
25288
- * @throws Error if optimizer is not registered
25289
- *
25290
- * @example
25291
- * ```typescript
25292
- * const optimizer = getOptimizer("llm-strategy-generator");
25293
- * console.log(optimizer.rangeTrain); // Array of training ranges
25294
- * console.log(optimizer.rangeTest); // Testing range
25295
- * console.log(optimizer.source); // Array of data sources
25296
- * console.log(optimizer.getPrompt); // async function
25297
- * ```
25298
- */
25299
- function getOptimizerSchema(optimizerName) {
25300
- bt.loggerService.log(GET_OPTIMIZER_METHOD_NAME, {
25301
- optimizerName,
25302
- });
25303
- bt.optimizerValidationService.validate(optimizerName, GET_OPTIMIZER_METHOD_NAME);
25304
- return bt.optimizerSchemaService.get(optimizerName);
25305
- }
25306
23613
  /**
25307
23614
  * Retrieves a registered action schema by name.
25308
23615
  *
@@ -26163,7 +24470,6 @@ const ADD_FRAME_METHOD_NAME = "add.addFrameSchema";
26163
24470
  const ADD_WALKER_METHOD_NAME = "add.addWalkerSchema";
26164
24471
  const ADD_SIZING_METHOD_NAME = "add.addSizingSchema";
26165
24472
  const ADD_RISK_METHOD_NAME = "add.addRiskSchema";
26166
- const ADD_OPTIMIZER_METHOD_NAME = "add.addOptimizerSchema";
26167
24473
  const ADD_ACTION_METHOD_NAME = "add.addActionSchema";
26168
24474
  /**
26169
24475
  * Registers a trading strategy in the framework.
@@ -26456,100 +24762,6 @@ function addRiskSchema(riskSchema) {
26456
24762
  bt.riskValidationService.addRisk(riskSchema.riskName, riskSchema);
26457
24763
  bt.riskSchemaService.register(riskSchema.riskName, riskSchema);
26458
24764
  }
26459
- /**
26460
- * Registers an optimizer configuration in the framework.
26461
- *
26462
- * The optimizer generates trading strategies by:
26463
- * - Collecting data from multiple sources across training periods
26464
- * - Building LLM conversation history with fetched data
26465
- * - Generating strategy prompts using getPrompt()
26466
- * - Creating executable backtest code with templates
26467
- *
26468
- * The optimizer produces a complete .mjs file containing:
26469
- * - Exchange, Frame, Strategy, and Walker configurations
26470
- * - Multi-timeframe analysis logic
26471
- * - LLM integration for signal generation
26472
- * - Event listeners for progress tracking
26473
- *
26474
- * @param optimizerSchema - Optimizer configuration object
26475
- * @param optimizerSchema.optimizerName - Unique optimizer identifier
26476
- * @param optimizerSchema.rangeTrain - Array of training time ranges (each generates a strategy variant)
26477
- * @param optimizerSchema.rangeTest - Testing time range for strategy validation
26478
- * @param optimizerSchema.source - Array of data sources (functions or source objects with custom formatters)
26479
- * @param optimizerSchema.getPrompt - Function to generate strategy prompt from conversation history
26480
- * @param optimizerSchema.template - Optional custom template overrides (top banner, helpers, strategy logic, etc.)
26481
- * @param optimizerSchema.callbacks - Optional lifecycle callbacks (onData, onCode, onDump, onSourceData)
26482
- *
26483
- * @example
26484
- * ```typescript
26485
- * // Basic optimizer with single data source
26486
- * addOptimizer({
26487
- * optimizerName: "llm-strategy-generator",
26488
- * rangeTrain: [
26489
- * {
26490
- * note: "Bull market period",
26491
- * startDate: new Date("2024-01-01"),
26492
- * endDate: new Date("2024-01-31"),
26493
- * },
26494
- * {
26495
- * note: "Bear market period",
26496
- * startDate: new Date("2024-02-01"),
26497
- * endDate: new Date("2024-02-28"),
26498
- * },
26499
- * ],
26500
- * rangeTest: {
26501
- * note: "Validation period",
26502
- * startDate: new Date("2024-03-01"),
26503
- * endDate: new Date("2024-03-31"),
26504
- * },
26505
- * source: [
26506
- * {
26507
- * name: "historical-backtests",
26508
- * fetch: async ({ symbol, startDate, endDate, limit, offset }) => {
26509
- * // Fetch historical backtest results from database
26510
- * return await db.backtests.find({
26511
- * symbol,
26512
- * date: { $gte: startDate, $lte: endDate },
26513
- * })
26514
- * .skip(offset)
26515
- * .limit(limit);
26516
- * },
26517
- * user: async (symbol, data, name) => {
26518
- * return `Analyze these ${data.length} backtest results for ${symbol}:\n${JSON.stringify(data)}`;
26519
- * },
26520
- * assistant: async (symbol, data, name) => {
26521
- * return "Historical data analyzed successfully";
26522
- * },
26523
- * },
26524
- * ],
26525
- * getPrompt: async (symbol, messages) => {
26526
- * // Generate strategy prompt from conversation
26527
- * return `"Analyze ${symbol} using RSI and MACD. Enter LONG when RSI < 30 and MACD crosses above signal."`;
26528
- * },
26529
- * callbacks: {
26530
- * onData: (symbol, strategyData) => {
26531
- * console.log(`Generated ${strategyData.length} strategies for ${symbol}`);
26532
- * },
26533
- * onCode: (symbol, code) => {
26534
- * console.log(`Generated ${code.length} characters of code for ${symbol}`);
26535
- * },
26536
- * onDump: (symbol, filepath) => {
26537
- * console.log(`Saved strategy to ${filepath}`);
26538
- * },
26539
- * onSourceData: (symbol, sourceName, data, startDate, endDate) => {
26540
- * console.log(`Fetched ${data.length} rows from ${sourceName} for ${symbol}`);
26541
- * },
26542
- * },
26543
- * });
26544
- * ```
26545
- */
26546
- function addOptimizerSchema(optimizerSchema) {
26547
- bt.loggerService.info(ADD_OPTIMIZER_METHOD_NAME, {
26548
- optimizerSchema,
26549
- });
26550
- bt.optimizerValidationService.addOptimizer(optimizerSchema.optimizerName, optimizerSchema);
26551
- bt.optimizerSchemaService.register(optimizerSchema.optimizerName, optimizerSchema);
26552
- }
26553
24765
  /**
26554
24766
  * Registers an action handler in the framework.
26555
24767
  *
@@ -26632,7 +24844,6 @@ const METHOD_NAME_OVERRIDE_FRAME = "function.override.overrideFrameSchema";
26632
24844
  const METHOD_NAME_OVERRIDE_WALKER = "function.override.overrideWalkerSchema";
26633
24845
  const METHOD_NAME_OVERRIDE_SIZING = "function.override.overrideSizingSchema";
26634
24846
  const METHOD_NAME_OVERRIDE_RISK = "function.override.overrideRiskSchema";
26635
- const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.overrideOptimizerSchema";
26636
24847
  const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideActionSchema";
26637
24848
  /**
26638
24849
  * Overrides an existing trading strategy in the framework.
@@ -26805,40 +25016,6 @@ async function overrideRiskSchema(riskSchema) {
26805
25016
  await bt.riskValidationService.validate(riskSchema.riskName, METHOD_NAME_OVERRIDE_RISK);
26806
25017
  return bt.riskSchemaService.override(riskSchema.riskName, riskSchema);
26807
25018
  }
26808
- /**
26809
- * Overrides an existing optimizer configuration in the framework.
26810
- *
26811
- * This function partially updates a previously registered optimizer with new configuration.
26812
- * Only the provided fields will be updated, other fields remain unchanged.
26813
- *
26814
- * @param optimizerSchema - Partial optimizer configuration object
26815
- * @param optimizerSchema.optimizerName - Unique optimizer identifier (must exist)
26816
- * @param optimizerSchema.rangeTrain - Optional: Array of training time ranges
26817
- * @param optimizerSchema.rangeTest - Optional: Testing time range
26818
- * @param optimizerSchema.source - Optional: Array of data sources
26819
- * @param optimizerSchema.getPrompt - Optional: Function to generate strategy prompt
26820
- * @param optimizerSchema.template - Optional: Custom template overrides
26821
- * @param optimizerSchema.callbacks - Optional: Lifecycle callbacks
26822
- *
26823
- * @example
26824
- * ```typescript
26825
- * overrideOptimizer({
26826
- * optimizerName: "llm-strategy-generator",
26827
- * rangeTest: {
26828
- * note: "Updated validation period",
26829
- * startDate: new Date("2024-04-01"),
26830
- * endDate: new Date("2024-04-30"),
26831
- * },
26832
- * });
26833
- * ```
26834
- */
26835
- async function overrideOptimizerSchema(optimizerSchema) {
26836
- bt.loggerService.log(METHOD_NAME_OVERRIDE_OPTIMIZER, {
26837
- optimizerSchema,
26838
- });
26839
- await bt.optimizerValidationService.validate(optimizerSchema.optimizerName, METHOD_NAME_OVERRIDE_OPTIMIZER);
26840
- return bt.optimizerSchemaService.override(optimizerSchema.optimizerName, optimizerSchema);
26841
- }
26842
25019
  /**
26843
25020
  * Overrides an existing action handler configuration in the framework.
26844
25021
  *
@@ -26913,7 +25090,6 @@ const LIST_FRAMES_METHOD_NAME = "list.listFrameSchema";
26913
25090
  const LIST_WALKERS_METHOD_NAME = "list.listWalkerSchema";
26914
25091
  const LIST_SIZINGS_METHOD_NAME = "list.listSizingSchema";
26915
25092
  const LIST_RISKS_METHOD_NAME = "list.listRiskSchema";
26916
- const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizerSchema";
26917
25093
  /**
26918
25094
  * Returns a list of all registered exchange schemas.
26919
25095
  *
@@ -27111,46 +25287,6 @@ async function listRiskSchema() {
27111
25287
  bt.loggerService.log(LIST_RISKS_METHOD_NAME);
27112
25288
  return await bt.riskValidationService.list();
27113
25289
  }
27114
- /**
27115
- * Returns a list of all registered optimizer schemas.
27116
- *
27117
- * Retrieves all optimizers that have been registered via addOptimizer().
27118
- * Useful for debugging, documentation, or building dynamic UIs.
27119
- *
27120
- * @returns Array of optimizer schemas with their configurations
27121
- *
27122
- * @example
27123
- * ```typescript
27124
- * import { listOptimizers, addOptimizer } from "backtest-kit";
27125
- *
27126
- * addOptimizer({
27127
- * optimizerName: "llm-strategy-generator",
27128
- * note: "Generates trading strategies using LLM",
27129
- * rangeTrain: [
27130
- * {
27131
- * note: "Training period 1",
27132
- * startDate: new Date("2024-01-01"),
27133
- * endDate: new Date("2024-01-31"),
27134
- * },
27135
- * ],
27136
- * rangeTest: {
27137
- * note: "Testing period",
27138
- * startDate: new Date("2024-02-01"),
27139
- * endDate: new Date("2024-02-28"),
27140
- * },
27141
- * source: [],
27142
- * getPrompt: async (symbol, messages) => "Generate strategy",
27143
- * });
27144
- *
27145
- * const optimizers = listOptimizers();
27146
- * console.log(optimizers);
27147
- * // [{ optimizerName: "llm-strategy-generator", note: "Generates...", ... }]
27148
- * ```
27149
- */
27150
- async function listOptimizerSchema() {
27151
- bt.loggerService.log(LIST_OPTIMIZERS_METHOD_NAME);
27152
- return await bt.optimizerValidationService.list();
27153
- }
27154
25290
 
27155
25291
  const LISTEN_SIGNAL_METHOD_NAME = "event.listenSignal";
27156
25292
  const LISTEN_SIGNAL_ONCE_METHOD_NAME = "event.listenSignalOnce";
@@ -27168,7 +25304,6 @@ const LISTEN_DONE_WALKER_METHOD_NAME = "event.listenDoneWalker";
27168
25304
  const LISTEN_DONE_WALKER_ONCE_METHOD_NAME = "event.listenDoneWalkerOnce";
27169
25305
  const LISTEN_PROGRESS_METHOD_NAME = "event.listenBacktestProgress";
27170
25306
  const LISTEN_PROGRESS_WALKER_METHOD_NAME = "event.listenWalkerProgress";
27171
- const LISTEN_PROGRESS_OPTIMIZER_METHOD_NAME = "event.listenOptimizerProgress";
27172
25307
  const LISTEN_PERFORMANCE_METHOD_NAME = "event.listenPerformance";
27173
25308
  const LISTEN_WALKER_METHOD_NAME = "event.listenWalker";
27174
25309
  const LISTEN_WALKER_ONCE_METHOD_NAME = "event.listenWalkerOnce";
@@ -27656,34 +25791,6 @@ function listenWalkerProgress(fn) {
27656
25791
  bt.loggerService.log(LISTEN_PROGRESS_WALKER_METHOD_NAME);
27657
25792
  return progressWalkerEmitter.subscribe(functoolsKit.queued(async (event) => fn(event)));
27658
25793
  }
27659
- /**
27660
- * Subscribes to optimizer progress events with queued async processing.
27661
- *
27662
- * Emits during optimizer execution to track data source processing progress.
27663
- * Events are processed sequentially in order received, even if callback is async.
27664
- * Uses queued wrapper to prevent concurrent execution of the callback.
27665
- *
27666
- * @param fn - Callback function to handle optimizer progress events
27667
- * @returns Unsubscribe function to stop listening to events
27668
- *
27669
- * @example
27670
- * ```typescript
27671
- * import { listenOptimizerProgress } from "backtest-kit";
27672
- *
27673
- * const unsubscribe = listenOptimizerProgress((event) => {
27674
- * console.log(`Progress: ${(event.progress * 100).toFixed(2)}%`);
27675
- * console.log(`${event.processedSources} / ${event.totalSources} sources`);
27676
- * console.log(`Optimizer: ${event.optimizerName}, Symbol: ${event.symbol}`);
27677
- * });
27678
- *
27679
- * // Later: stop listening
27680
- * unsubscribe();
27681
- * ```
27682
- */
27683
- function listenOptimizerProgress(fn) {
27684
- bt.loggerService.log(LISTEN_PROGRESS_OPTIMIZER_METHOD_NAME);
27685
- return progressOptimizerEmitter.subscribe(functoolsKit.queued(async (event) => fn(event)));
27686
- }
27687
25794
  /**
27688
25795
  * Subscribes to performance metric events with queued async processing.
27689
25796
  *
@@ -28244,138 +26351,6 @@ function listenActivePingOnce(filterFn, fn) {
28244
26351
  return activePingSubject.filter(filterFn).once(fn);
28245
26352
  }
28246
26353
 
28247
- const METHOD_NAME_SIGNAL = "history.commitSignalPromptHistory";
28248
- /**
28249
- * Commits signal prompt history to the message array.
28250
- *
28251
- * Extracts trading context from ExecutionContext and MethodContext,
28252
- * then adds signal-specific system prompts at the beginning and user prompt
28253
- * at the end of the history array if they are not empty.
28254
- *
28255
- * Context extraction:
28256
- * - symbol: Provided as parameter for debugging convenience
28257
- * - backtest mode: From ExecutionContext
28258
- * - strategyName, exchangeName, frameName: From MethodContext
28259
- *
28260
- * @param symbol - Trading symbol (e.g., "BTCUSDT") for debugging convenience
28261
- * @param history - Message array to append prompts to
28262
- * @returns Promise that resolves when prompts are added
28263
- * @throws Error if ExecutionContext or MethodContext is not active
28264
- *
28265
- * @example
28266
- * ```typescript
28267
- * const messages: MessageModel[] = [];
28268
- * await commitSignalPromptHistory("BTCUSDT", messages);
28269
- * // messages now contains system prompts at start and user prompt at end
28270
- * ```
28271
- */
28272
- async function commitSignalPromptHistory(symbol, history) {
28273
- bt.loggerService.log(METHOD_NAME_SIGNAL, {
28274
- symbol,
28275
- });
28276
- if (!ExecutionContextService.hasContext()) {
28277
- throw new Error("commitSignalPromptHistory requires an execution context");
28278
- }
28279
- if (!MethodContextService.hasContext()) {
28280
- throw new Error("commitSignalPromptHistory requires a method context");
28281
- }
28282
- const { backtest: isBacktest } = bt.executionContextService.context;
28283
- const { strategyName, exchangeName, frameName } = bt.methodContextService.context;
28284
- const systemPrompts = await bt.signalPromptService.getSystemPrompt(symbol, strategyName, exchangeName, frameName, isBacktest);
28285
- const userPrompt = await bt.signalPromptService.getUserPrompt(symbol, strategyName, exchangeName, frameName, isBacktest);
28286
- if (systemPrompts.length > 0) {
28287
- for (const content of systemPrompts) {
28288
- history.unshift({
28289
- role: "system",
28290
- content,
28291
- });
28292
- }
28293
- }
28294
- if (userPrompt && userPrompt.trim() !== "") {
28295
- history.push({
28296
- role: "user",
28297
- content: userPrompt,
28298
- });
28299
- }
28300
- }
28301
-
28302
- const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
28303
- /**
28304
- * Dumps signal data and LLM conversation history to markdown files.
28305
- * Used by AI-powered strategies to save debug logs for analysis.
28306
- *
28307
- * Creates a directory structure with:
28308
- * - 00_system_prompt.md - System messages and output summary
28309
- * - XX_user_message.md - Each user message in separate file (numbered)
28310
- * - XX_llm_output.md - Final LLM output with signal data
28311
- *
28312
- * Skips if directory already exists to avoid overwriting previous results.
28313
- *
28314
- * @param signalId - Unique identifier for the result (used as directory name, e.g., UUID)
28315
- * @param history - Array of message models from LLM conversation
28316
- * @param signal - Signal DTO returned by LLM (position, priceOpen, TP, SL, etc.)
28317
- * @param outputDir - Output directory path (default: "./dump/strategy")
28318
- * @returns Promise that resolves when all files are written
28319
- *
28320
- * @example
28321
- * ```typescript
28322
- * import { dumpSignal, getCandles } from "backtest-kit";
28323
- * import { v4 as uuid } from "uuid";
28324
- *
28325
- * addStrategy({
28326
- * strategyName: "llm-strategy",
28327
- * interval: "5m",
28328
- * getSignal: async (symbol) => {
28329
- * const messages = [];
28330
- *
28331
- * // Build multi-timeframe analysis conversation
28332
- * const candles1h = await getCandles(symbol, "1h", 24);
28333
- * messages.push(
28334
- * { role: "user", content: `Analyze 1h trend:\n${formatCandles(candles1h)}` },
28335
- * { role: "assistant", content: "Trend analyzed" }
28336
- * );
28337
- *
28338
- * const candles5m = await getCandles(symbol, "5m", 24);
28339
- * messages.push(
28340
- * { role: "user", content: `Analyze 5m structure:\n${formatCandles(candles5m)}` },
28341
- * { role: "assistant", content: "Structure analyzed" }
28342
- * );
28343
- *
28344
- * // Request signal
28345
- * messages.push({
28346
- * role: "user",
28347
- * content: "Generate trading signal. Use position: 'wait' if uncertain."
28348
- * });
28349
- *
28350
- * const resultId = uuid();
28351
- * const signal = await llmRequest(messages);
28352
- *
28353
- * // Save conversation and result for debugging
28354
- * await dumpSignal(resultId, messages, signal);
28355
- *
28356
- * return signal;
28357
- * }
28358
- * });
28359
- *
28360
- * // Creates: ./dump/strategy/{uuid}/00_system_prompt.md
28361
- * // ./dump/strategy/{uuid}/01_user_message.md (1h analysis)
28362
- * // ./dump/strategy/{uuid}/02_assistant_message.md
28363
- * // ./dump/strategy/{uuid}/03_user_message.md (5m analysis)
28364
- * // ./dump/strategy/{uuid}/04_assistant_message.md
28365
- * // ./dump/strategy/{uuid}/05_user_message.md (signal request)
28366
- * // ./dump/strategy/{uuid}/06_llm_output.md (final signal)
28367
- * ```
28368
- */
28369
- async function dumpSignalData(signalId, history, signal, outputDir = "./dump/strategy") {
28370
- bt.loggerService.info(DUMP_SIGNAL_METHOD_NAME, {
28371
- signalId,
28372
- history,
28373
- signal,
28374
- outputDir,
28375
- });
28376
- return await bt.outlineMarkdownService.dumpSignal(signalId, history, signal, outputDir);
28377
- }
28378
-
28379
26354
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
28380
26355
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
28381
26356
  const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
@@ -31535,104 +29510,6 @@ PositionSizeUtils.atrBased = async (symbol, accountBalance, priceOpen, atr, cont
31535
29510
  };
31536
29511
  const PositionSize = PositionSizeUtils;
31537
29512
 
31538
- const OPTIMIZER_METHOD_NAME_GET_DATA = "OptimizerUtils.getData";
31539
- const OPTIMIZER_METHOD_NAME_GET_CODE = "OptimizerUtils.getCode";
31540
- const OPTIMIZER_METHOD_NAME_DUMP = "OptimizerUtils.dump";
31541
- /**
31542
- * Public API utilities for optimizer operations.
31543
- * Provides high-level methods for strategy generation and code export.
31544
- *
31545
- * Usage:
31546
- * ```typescript
31547
- * import { Optimizer } from "backtest-kit";
31548
- *
31549
- * // Get strategy data
31550
- * const strategies = await Optimizer.getData("BTCUSDT", {
31551
- * optimizerName: "my-optimizer"
31552
- * });
31553
- *
31554
- * // Generate code
31555
- * const code = await Optimizer.getCode("BTCUSDT", {
31556
- * optimizerName: "my-optimizer"
31557
- * });
31558
- *
31559
- * // Save to file
31560
- * await Optimizer.dump("BTCUSDT", {
31561
- * optimizerName: "my-optimizer"
31562
- * }, "./output");
31563
- * ```
31564
- */
31565
- class OptimizerUtils {
31566
- constructor() {
31567
- /**
31568
- * Fetches data from all sources and generates strategy metadata.
31569
- * Processes each training range and builds LLM conversation history.
31570
- *
31571
- * @param symbol - Trading pair symbol
31572
- * @param context - Context with optimizerName
31573
- * @returns Array of generated strategies with conversation context
31574
- * @throws Error if optimizer not found
31575
- */
31576
- this.getData = async (symbol, context) => {
31577
- bt.loggerService.info(OPTIMIZER_METHOD_NAME_GET_DATA, {
31578
- symbol,
31579
- context,
31580
- });
31581
- bt.optimizerValidationService.validate(context.optimizerName, OPTIMIZER_METHOD_NAME_GET_DATA);
31582
- return await bt.optimizerGlobalService.getData(symbol, context.optimizerName);
31583
- };
31584
- /**
31585
- * Generates complete executable strategy code.
31586
- * Includes imports, helpers, strategies, walker, and launcher.
31587
- *
31588
- * @param symbol - Trading pair symbol
31589
- * @param context - Context with optimizerName
31590
- * @returns Generated TypeScript/JavaScript code as string
31591
- * @throws Error if optimizer not found
31592
- */
31593
- this.getCode = async (symbol, context) => {
31594
- bt.loggerService.info(OPTIMIZER_METHOD_NAME_GET_CODE, {
31595
- symbol,
31596
- context,
31597
- });
31598
- bt.optimizerValidationService.validate(context.optimizerName, OPTIMIZER_METHOD_NAME_GET_CODE);
31599
- return await bt.optimizerGlobalService.getCode(symbol, context.optimizerName);
31600
- };
31601
- /**
31602
- * Generates and saves strategy code to file.
31603
- * Creates directory if needed, writes .mjs file.
31604
- *
31605
- * Format: `{optimizerName}_{symbol}.mjs`
31606
- *
31607
- * @param symbol - Trading pair symbol
31608
- * @param context - Context with optimizerName
31609
- * @param path - Output directory path (default: "./")
31610
- * @throws Error if optimizer not found or file write fails
31611
- */
31612
- this.dump = async (symbol, context, path) => {
31613
- bt.loggerService.info(OPTIMIZER_METHOD_NAME_DUMP, {
31614
- symbol,
31615
- context,
31616
- path,
31617
- });
31618
- bt.optimizerValidationService.validate(context.optimizerName, OPTIMIZER_METHOD_NAME_DUMP);
31619
- await bt.optimizerGlobalService.dump(symbol, context.optimizerName, path);
31620
- };
31621
- }
31622
- }
31623
- /**
31624
- * Singleton instance of OptimizerUtils.
31625
- * Public API for optimizer operations.
31626
- *
31627
- * @example
31628
- * ```typescript
31629
- * import { Optimizer } from "backtest-kit";
31630
- *
31631
- * await Optimizer.dump("BTCUSDT", { optimizerName: "my-optimizer" });
31632
- * ```
31633
- */
31634
- const Optimizer = new OptimizerUtils();
31635
-
31636
29513
  const PARTIAL_METHOD_NAME_GET_DATA = "PartialUtils.getData";
31637
29514
  const PARTIAL_METHOD_NAME_GET_REPORT = "PartialUtils.getReport";
31638
29515
  const PARTIAL_METHOD_NAME_DUMP = "PartialUtils.dump";
@@ -33678,7 +31555,6 @@ exports.MarkdownFileBase = MarkdownFileBase;
33678
31555
  exports.MarkdownFolderBase = MarkdownFolderBase;
33679
31556
  exports.MethodContextService = MethodContextService;
33680
31557
  exports.Notification = Notification;
33681
- exports.Optimizer = Optimizer;
33682
31558
  exports.Partial = Partial;
33683
31559
  exports.Performance = Performance;
33684
31560
  exports.PersistBase = PersistBase;
@@ -33697,7 +31573,6 @@ exports.Walker = Walker;
33697
31573
  exports.addActionSchema = addActionSchema;
33698
31574
  exports.addExchangeSchema = addExchangeSchema;
33699
31575
  exports.addFrameSchema = addFrameSchema;
33700
- exports.addOptimizerSchema = addOptimizerSchema;
33701
31576
  exports.addRiskSchema = addRiskSchema;
33702
31577
  exports.addSizingSchema = addSizingSchema;
33703
31578
  exports.addStrategySchema = addStrategySchema;
@@ -33707,10 +31582,8 @@ exports.commitCancelScheduled = commitCancelScheduled;
33707
31582
  exports.commitClosePending = commitClosePending;
33708
31583
  exports.commitPartialLoss = commitPartialLoss;
33709
31584
  exports.commitPartialProfit = commitPartialProfit;
33710
- exports.commitSignalPromptHistory = commitSignalPromptHistory;
33711
31585
  exports.commitTrailingStop = commitTrailingStop;
33712
31586
  exports.commitTrailingTake = commitTrailingTake;
33713
- exports.dumpSignalData = dumpSignalData;
33714
31587
  exports.emitters = emitters;
33715
31588
  exports.formatPrice = formatPrice;
33716
31589
  exports.formatQuantity = formatQuantity;
@@ -33728,7 +31601,6 @@ exports.getDefaultConfig = getDefaultConfig;
33728
31601
  exports.getExchangeSchema = getExchangeSchema;
33729
31602
  exports.getFrameSchema = getFrameSchema;
33730
31603
  exports.getMode = getMode;
33731
- exports.getOptimizerSchema = getOptimizerSchema;
33732
31604
  exports.getOrderBook = getOrderBook;
33733
31605
  exports.getRawCandles = getRawCandles;
33734
31606
  exports.getRiskSchema = getRiskSchema;
@@ -33740,7 +31612,6 @@ exports.hasTradeContext = hasTradeContext;
33740
31612
  exports.lib = backtest;
33741
31613
  exports.listExchangeSchema = listExchangeSchema;
33742
31614
  exports.listFrameSchema = listFrameSchema;
33743
- exports.listOptimizerSchema = listOptimizerSchema;
33744
31615
  exports.listRiskSchema = listRiskSchema;
33745
31616
  exports.listSizingSchema = listSizingSchema;
33746
31617
  exports.listStrategySchema = listStrategySchema;
@@ -33758,7 +31629,6 @@ exports.listenDoneWalker = listenDoneWalker;
33758
31629
  exports.listenDoneWalkerOnce = listenDoneWalkerOnce;
33759
31630
  exports.listenError = listenError;
33760
31631
  exports.listenExit = listenExit;
33761
- exports.listenOptimizerProgress = listenOptimizerProgress;
33762
31632
  exports.listenPartialLossAvailable = listenPartialLossAvailable;
33763
31633
  exports.listenPartialLossAvailableOnce = listenPartialLossAvailableOnce;
33764
31634
  exports.listenPartialProfitAvailable = listenPartialProfitAvailable;
@@ -33782,7 +31652,6 @@ exports.listenWalkerProgress = listenWalkerProgress;
33782
31652
  exports.overrideActionSchema = overrideActionSchema;
33783
31653
  exports.overrideExchangeSchema = overrideExchangeSchema;
33784
31654
  exports.overrideFrameSchema = overrideFrameSchema;
33785
- exports.overrideOptimizerSchema = overrideOptimizerSchema;
33786
31655
  exports.overrideRiskSchema = overrideRiskSchema;
33787
31656
  exports.overrideSizingSchema = overrideSizingSchema;
33788
31657
  exports.overrideStrategySchema = overrideStrategySchema;