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.mjs CHANGED
@@ -1,13 +1,12 @@
1
1
  import { createActivator } from 'di-kit';
2
2
  import { scoped } from 'di-scoped';
3
- import { Subject, makeExtendable, singleshot, getErrorMessage, memoize, not, trycatch, retry, errorData, queued, sleep, randomString, str, isObject, ToolRegistry, typo, and, resolveDocuments, timeout, TIMEOUT_SYMBOL as TIMEOUT_SYMBOL$1, compose, iterateDocuments, distinctDocuments, singlerun } from 'functools-kit';
3
+ import { Subject, makeExtendable, singleshot, getErrorMessage, memoize, not, trycatch, retry, errorData, queued, sleep, randomString, str, isObject, ToolRegistry, typo, and, resolveDocuments, timeout, TIMEOUT_SYMBOL as TIMEOUT_SYMBOL$1, compose, singlerun } from 'functools-kit';
4
4
  import * as fs from 'fs/promises';
5
- import fs__default, { mkdir, writeFile } from 'fs/promises';
5
+ import fs__default from 'fs/promises';
6
6
  import path, { join, dirname } from 'path';
7
7
  import crypto from 'crypto';
8
8
  import os from 'os';
9
9
  import { createWriteStream } from 'fs';
10
- import { createRequire } from 'module';
11
10
  import { parseArgs as parseArgs$1 } from 'util';
12
11
 
13
12
  const { init, inject, provide } = createActivator("backtest");
@@ -52,7 +51,6 @@ const connectionServices$1 = {
52
51
  sizingConnectionService: Symbol('sizingConnectionService'),
53
52
  riskConnectionService: Symbol('riskConnectionService'),
54
53
  actionConnectionService: Symbol('actionConnectionService'),
55
- optimizerConnectionService: Symbol('optimizerConnectionService'),
56
54
  partialConnectionService: Symbol('partialConnectionService'),
57
55
  breakevenConnectionService: Symbol('breakevenConnectionService'),
58
56
  };
@@ -64,7 +62,6 @@ const schemaServices$1 = {
64
62
  sizingSchemaService: Symbol('sizingSchemaService'),
65
63
  riskSchemaService: Symbol('riskSchemaService'),
66
64
  actionSchemaService: Symbol('actionSchemaService'),
67
- optimizerSchemaService: Symbol('optimizerSchemaService'),
68
65
  };
69
66
  const coreServices$1 = {
70
67
  exchangeCoreService: Symbol('exchangeCoreService'),
@@ -75,7 +72,6 @@ const coreServices$1 = {
75
72
  const globalServices$1 = {
76
73
  sizingGlobalService: Symbol('sizingGlobalService'),
77
74
  riskGlobalService: Symbol('riskGlobalService'),
78
- optimizerGlobalService: Symbol('optimizerGlobalService'),
79
75
  partialGlobalService: Symbol('partialGlobalService'),
80
76
  breakevenGlobalService: Symbol('breakevenGlobalService'),
81
77
  };
@@ -125,16 +121,9 @@ const validationServices$1 = {
125
121
  sizingValidationService: Symbol('sizingValidationService'),
126
122
  riskValidationService: Symbol('riskValidationService'),
127
123
  actionValidationService: Symbol('actionValidationService'),
128
- optimizerValidationService: Symbol('optimizerValidationService'),
129
124
  configValidationService: Symbol('configValidationService'),
130
125
  columnValidationService: Symbol('columnValidationService'),
131
126
  };
132
- const templateServices$1 = {
133
- optimizerTemplateService: Symbol('optimizerTemplateService'),
134
- };
135
- const promptServices$1 = {
136
- signalPromptService: Symbol('signalPromptService'),
137
- };
138
127
  const TYPES = {
139
128
  ...baseServices$1,
140
129
  ...contextServices$1,
@@ -148,8 +137,6 @@ const TYPES = {
148
137
  ...markdownServices$1,
149
138
  ...reportServices$1,
150
139
  ...validationServices$1,
151
- ...templateServices$1,
152
- ...promptServices$1,
153
140
  };
154
141
 
155
142
  /**
@@ -473,11 +460,6 @@ const progressBacktestEmitter = new Subject();
473
460
  * Emits progress updates during walker execution.
474
461
  */
475
462
  const progressWalkerEmitter = new Subject();
476
- /**
477
- * Progress emitter for optimizer execution progress.
478
- * Emits progress updates during optimizer execution.
479
- */
480
- const progressOptimizerEmitter = new Subject();
481
463
  /**
482
464
  * Performance emitter for execution metrics.
483
465
  * Emits performance metrics for profiling and bottleneck detection.
@@ -552,7 +534,6 @@ var emitters = /*#__PURE__*/Object.freeze({
552
534
  partialProfitSubject: partialProfitSubject,
553
535
  performanceEmitter: performanceEmitter,
554
536
  progressBacktestEmitter: progressBacktestEmitter,
555
- progressOptimizerEmitter: progressOptimizerEmitter,
556
537
  progressWalkerEmitter: progressWalkerEmitter,
557
538
  riskSubject: riskSubject,
558
539
  schedulePingSubject: schedulePingSubject,
@@ -18400,1480 +18381,131 @@ class ActionValidationService {
18400
18381
  }
18401
18382
 
18402
18383
  /**
18403
- * Default template service for generating optimizer code snippets.
18404
- * Implements all IOptimizerTemplate methods with Ollama LLM integration.
18384
+ * Symbol marker indicating that partial state needs initialization.
18385
+ * Used as sentinel value for _states before waitForInit() is called.
18386
+ */
18387
+ const NEED_FETCH$1 = Symbol("need_fetch");
18388
+ /**
18389
+ * Array of profit level milestones to track (10%, 20%, ..., 100%).
18390
+ * Each level is checked during profit() method to emit events for newly reached levels.
18391
+ */
18392
+ const PROFIT_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
18393
+ /**
18394
+ * Array of loss level milestones to track (-10%, -20%, ..., -100%).
18395
+ * Each level is checked during loss() method to emit events for newly reached levels.
18396
+ */
18397
+ const LOSS_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
18398
+ /**
18399
+ * Internal profit handler function for ClientPartial.
18405
18400
  *
18406
- * Features:
18407
- * - Multi-timeframe analysis (1m, 5m, 15m, 1h)
18408
- * - JSON structured output for signals
18409
- * - Debug logging to ./dump/strategy
18410
- * - CCXT exchange integration
18411
- * - Walker-based strategy comparison
18401
+ * Checks which profit levels have been reached and emits events for new levels only.
18402
+ * Uses Set-based deduplication to prevent duplicate events.
18412
18403
  *
18413
- * Can be partially overridden in optimizer schema configuration.
18404
+ * @param symbol - Trading pair symbol
18405
+ * @param data - Signal row data
18406
+ * @param currentPrice - Current market price
18407
+ * @param revenuePercent - Current profit percentage (positive value)
18408
+ * @param backtest - True if backtest mode
18409
+ * @param when - Event timestamp
18410
+ * @param self - ClientPartial instance reference
18414
18411
  */
18415
- class OptimizerTemplateService {
18416
- constructor() {
18417
- this.loggerService = inject(TYPES.loggerService);
18418
- /**
18419
- * Generates the top banner with imports and constants.
18420
- *
18421
- * @param symbol - Trading pair symbol
18422
- * @returns Shebang, imports, and WARN_KB constant
18423
- */
18424
- this.getTopBanner = async (symbol) => {
18425
- this.loggerService.log("optimizerTemplateService getTopBanner", {
18426
- symbol,
18427
- });
18428
- return [
18429
- "#!/usr/bin/env node",
18430
- "",
18431
- `import { Ollama } from "ollama";`,
18432
- `import ccxt from "ccxt";`,
18433
- `import {`,
18434
- ` addExchangeSchema,`,
18435
- ` addStrategySchema,`,
18436
- ` addFrameSchema,`,
18437
- ` addWalkerSchema,`,
18438
- ` Walker,`,
18439
- ` Backtest,`,
18440
- ` getCandles,`,
18441
- ` listenSignalBacktest,`,
18442
- ` listenWalkerComplete,`,
18443
- ` listenDoneBacktest,`,
18444
- ` listenBacktestProgress,`,
18445
- ` listenWalkerProgress,`,
18446
- ` listenError,`,
18447
- ` Markdown,`,
18448
- `} from "backtest-kit";`,
18449
- `import { promises as fs } from "fs";`,
18450
- `import { v4 as uuid } from "uuid";`,
18451
- `import path from "path";`,
18452
- ``,
18453
- `const WARN_KB = 100;`,
18454
- ``,
18455
- `Markdown.enable()`,
18456
- ].join("\n");
18457
- };
18458
- /**
18459
- * Generates default user message for LLM conversation.
18460
- * Simple prompt to read and acknowledge data.
18461
- *
18462
- * @param symbol - Trading pair symbol
18463
- * @param data - Fetched data array
18464
- * @param name - Source name
18465
- * @returns User message with JSON data
18466
- */
18467
- this.getUserMessage = async (symbol, data, name) => {
18468
- this.loggerService.log("optimizerTemplateService getUserMessage", {
18469
- symbol,
18470
- data,
18471
- name,
18472
- });
18473
- return ["Прочитай данные и скажи ОК", "", JSON.stringify(data)].join("\n");
18474
- };
18475
- /**
18476
- * Generates default assistant message for LLM conversation.
18477
- * Simple acknowledgment response.
18478
- *
18479
- * @param symbol - Trading pair symbol
18480
- * @param data - Fetched data array
18481
- * @param name - Source name
18482
- * @returns Assistant acknowledgment message
18483
- */
18484
- this.getAssistantMessage = async (symbol, data, name) => {
18485
- this.loggerService.log("optimizerTemplateService getAssistantMessage", {
18486
- symbol,
18487
- data,
18488
- name,
18489
- });
18490
- return "ОК";
18491
- };
18492
- /**
18493
- * Generates Walker configuration code.
18494
- * Compares multiple strategies on test frame.
18495
- *
18496
- * @param walkerName - Unique walker identifier
18497
- * @param exchangeName - Exchange to use for backtesting
18498
- * @param frameName - Test frame name
18499
- * @param strategies - Array of strategy names to compare
18500
- * @returns Generated addWalker() call
18501
- */
18502
- this.getWalkerTemplate = async (walkerName, exchangeName, frameName, strategies) => {
18503
- this.loggerService.log("optimizerTemplateService getWalkerTemplate", {
18504
- walkerName,
18505
- exchangeName,
18506
- frameName,
18507
- strategies,
18508
- });
18509
- // Escape special characters to prevent code injection
18510
- const escapedWalkerName = String(walkerName)
18511
- .replace(/\\/g, '\\\\')
18512
- .replace(/"/g, '\\"');
18513
- const escapedExchangeName = String(exchangeName)
18514
- .replace(/\\/g, '\\\\')
18515
- .replace(/"/g, '\\"');
18516
- const escapedFrameName = String(frameName)
18517
- .replace(/\\/g, '\\\\')
18518
- .replace(/"/g, '\\"');
18519
- const escapedStrategies = strategies.map((s) => String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"'));
18520
- return [
18521
- `addWalkerSchema({`,
18522
- ` walkerName: "${escapedWalkerName}",`,
18523
- ` exchangeName: "${escapedExchangeName}",`,
18524
- ` frameName: "${escapedFrameName}",`,
18525
- ` strategies: [${escapedStrategies.map((s) => `"${s}"`).join(", ")}],`,
18526
- `});`
18527
- ].join("\n");
18528
- };
18529
- /**
18530
- * Generates Strategy configuration with LLM integration.
18531
- * Includes multi-timeframe analysis and signal generation.
18532
- *
18533
- * @param strategyName - Unique strategy identifier
18534
- * @param interval - Signal throttling interval (e.g., "5m")
18535
- * @param prompt - Strategy logic from getPrompt()
18536
- * @returns Generated addStrategy() call with getSignal() function
18537
- */
18538
- this.getStrategyTemplate = async (strategyName, interval, prompt) => {
18539
- this.loggerService.log("optimizerTemplateService getStrategyTemplate", {
18540
- strategyName,
18541
- interval,
18542
- prompt,
18543
- });
18544
- // Convert prompt to plain text first
18545
- const plainPrompt = toPlainString(prompt);
18546
- // Escape special characters to prevent code injection
18547
- const escapedStrategyName = String(strategyName)
18548
- .replace(/\\/g, '\\\\')
18549
- .replace(/"/g, '\\"');
18550
- const escapedInterval = String(interval)
18551
- .replace(/\\/g, '\\\\')
18552
- .replace(/"/g, '\\"');
18553
- const escapedPrompt = String(plainPrompt)
18554
- .replace(/\\/g, '\\\\')
18555
- .replace(/`/g, '\\`')
18556
- .replace(/\$/g, '\\$');
18557
- return [
18558
- `addStrategySchema({`,
18559
- ` strategyName: "${escapedStrategyName}",`,
18560
- ` interval: "${escapedInterval}",`,
18561
- ` getSignal: async (symbol) => {`,
18562
- ` const messages = [];`,
18563
- ``,
18564
- ` // Загружаем данные всех таймфреймов`,
18565
- ` const microTermCandles = await getCandles(symbol, "1m", 30);`,
18566
- ` const mainTermCandles = await getCandles(symbol, "5m", 24);`,
18567
- ` const shortTermCandles = await getCandles(symbol, "15m", 24);`,
18568
- ` const mediumTermCandles = await getCandles(symbol, "1h", 24);`,
18569
- ``,
18570
- ` function formatCandles(candles, timeframe) {`,
18571
- ` return candles.map((c) =>`,
18572
- ` \`\${new Date(c.timestamp).toISOString()}[\${timeframe}]: O:\${c.open} H:\${c.high} L:\${c.low} C:\${c.close} V:\${c.volume}\``,
18573
- ` ).join("\\n");`,
18574
- ` }`,
18575
- ``,
18576
- ` // Сообщение 1: Среднесрочный тренд`,
18577
- ` messages.push(`,
18578
- ` {`,
18579
- ` role: "user",`,
18580
- ` content: [`,
18581
- ` \`\${symbol}\`,`,
18582
- ` "Проанализируй свечи 1h:",`,
18583
- ` "",`,
18584
- ` formatCandles(mediumTermCandles, "1h")`,
18585
- ` ].join("\\n"),`,
18586
- ` },`,
18587
- ` {`,
18588
- ` role: "assistant",`,
18589
- ` content: "Тренд 1h проанализирован",`,
18590
- ` }`,
18591
- ` );`,
18592
- ``,
18593
- ` // Сообщение 2: Краткосрочный тренд`,
18594
- ` messages.push(`,
18595
- ` {`,
18596
- ` role: "user",`,
18597
- ` content: [`,
18598
- ` "Проанализируй свечи 15m:",`,
18599
- ` "",`,
18600
- ` formatCandles(shortTermCandles, "15m")`,
18601
- ` ].join("\\n"),`,
18602
- ` },`,
18603
- ` {`,
18604
- ` role: "assistant",`,
18605
- ` content: "Тренд 15m проанализирован",`,
18606
- ` }`,
18607
- ` );`,
18608
- ``,
18609
- ` // Сообщение 3: Основной таймфрейм`,
18610
- ` messages.push(`,
18611
- ` {`,
18612
- ` role: "user",`,
18613
- ` content: [`,
18614
- ` "Проанализируй свечи 5m:",`,
18615
- ` "",`,
18616
- ` formatCandles(mainTermCandles, "5m")`,
18617
- ` ].join("\\n")`,
18618
- ` },`,
18619
- ` {`,
18620
- ` role: "assistant",`,
18621
- ` content: "Таймфрейм 5m проанализирован",`,
18622
- ` }`,
18623
- ` );`,
18624
- ``,
18625
- ` // Сообщение 4: Микро-структура`,
18626
- ` messages.push(`,
18627
- ` {`,
18628
- ` role: "user",`,
18629
- ` content: [`,
18630
- ` "Проанализируй свечи 1m:",`,
18631
- ` "",`,
18632
- ` formatCandles(microTermCandles, "1m")`,
18633
- ` ].join("\\n")`,
18634
- ` },`,
18635
- ` {`,
18636
- ` role: "assistant",`,
18637
- ` content: "Микроструктура 1m проанализирована",`,
18638
- ` }`,
18639
- ` );`,
18640
- ``,
18641
- ` // Сообщение 5: Запрос сигнала`,
18642
- ` messages.push(`,
18643
- ` {`,
18644
- ` role: "user",`,
18645
- ` content: [`,
18646
- ` "Проанализируй все таймфреймы и сгенерируй торговый сигнал согласно этой стратегии. Открывай позицию ТОЛЬКО при четком сигнале.",`,
18647
- ` "",`,
18648
- ` \`${escapedPrompt}\`,`,
18649
- ` "",`,
18650
- ` "Если сигналы противоречивы или тренд слабый то position: wait"`,
18651
- ` ].join("\\n"),`,
18652
- ` }`,
18653
- ` );`,
18654
- ``,
18655
- ` const resultId = uuid();`,
18656
- ``,
18657
- ` const result = await json(messages);`,
18658
- ``,
18659
- ` await dumpJson(resultId, messages, result);`,
18660
- ``,
18661
- ` result.id = resultId;`,
18662
- ``,
18663
- ` return result;`,
18664
- ` },`,
18665
- `});`
18666
- ].join("\n");
18667
- };
18668
- /**
18669
- * Generates Exchange configuration code.
18670
- * Uses CCXT Binance with standard formatters.
18671
- *
18672
- * @param symbol - Trading pair symbol (unused, for consistency)
18673
- * @param exchangeName - Unique exchange identifier
18674
- * @returns Generated addExchange() call with CCXT integration
18675
- */
18676
- this.getExchangeTemplate = async (symbol, exchangeName) => {
18677
- this.loggerService.log("optimizerTemplateService getExchangeTemplate", {
18678
- exchangeName,
18679
- symbol,
18680
- });
18681
- // Escape special characters to prevent code injection
18682
- const escapedExchangeName = String(exchangeName)
18683
- .replace(/\\/g, '\\\\')
18684
- .replace(/"/g, '\\"');
18685
- return [
18686
- `addExchangeSchema({`,
18687
- ` exchangeName: "${escapedExchangeName}",`,
18688
- ` getCandles: async (symbol, interval, since, limit) => {`,
18689
- ` const exchange = new ccxt.binance();`,
18690
- ` const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);`,
18691
- ` return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({`,
18692
- ` timestamp, open, high, low, close, volume`,
18693
- ` }));`,
18694
- ` },`,
18695
- ` formatPrice: async (symbol, price) => price.toFixed(2),`,
18696
- ` formatQuantity: async (symbol, quantity) => quantity.toFixed(8),`,
18697
- `});`
18698
- ].join("\n");
18699
- };
18700
- /**
18701
- * Generates Frame (timeframe) configuration code.
18702
- *
18703
- * @param symbol - Trading pair symbol (unused, for consistency)
18704
- * @param frameName - Unique frame identifier
18705
- * @param interval - Candle interval (e.g., "1m")
18706
- * @param startDate - Frame start date
18707
- * @param endDate - Frame end date
18708
- * @returns Generated addFrame() call
18709
- */
18710
- this.getFrameTemplate = async (symbol, frameName, interval, startDate, endDate) => {
18711
- this.loggerService.log("optimizerTemplateService getFrameTemplate", {
18712
- symbol,
18713
- frameName,
18714
- interval,
18715
- startDate,
18716
- endDate,
18717
- });
18718
- // Escape special characters to prevent code injection
18719
- const escapedFrameName = String(frameName)
18720
- .replace(/\\/g, '\\\\')
18721
- .replace(/"/g, '\\"');
18722
- const escapedInterval = String(interval)
18723
- .replace(/\\/g, '\\\\')
18724
- .replace(/"/g, '\\"');
18725
- return [
18726
- `addFrameSchema({`,
18727
- ` frameName: "${escapedFrameName}",`,
18728
- ` interval: "${escapedInterval}",`,
18729
- ` startDate: new Date("${startDate.toISOString()}"),`,
18730
- ` endDate: new Date("${endDate.toISOString()}"),`,
18731
- `});`
18732
- ].join("\n");
18733
- };
18734
- /**
18735
- * Generates launcher code to run Walker with event listeners.
18736
- * Includes progress tracking and completion handlers.
18737
- *
18738
- * @param symbol - Trading pair symbol
18739
- * @param walkerName - Walker name to launch
18740
- * @returns Generated Walker.background() call with listeners
18741
- */
18742
- this.getLauncherTemplate = async (symbol, walkerName) => {
18743
- this.loggerService.log("optimizerTemplateService getLauncherTemplate", {
18744
- symbol,
18745
- walkerName,
18746
- });
18747
- // Escape special characters to prevent code injection
18748
- const escapedSymbol = String(symbol)
18749
- .replace(/\\/g, '\\\\')
18750
- .replace(/"/g, '\\"');
18751
- const escapedWalkerName = String(walkerName)
18752
- .replace(/\\/g, '\\\\')
18753
- .replace(/"/g, '\\"');
18754
- return [
18755
- `Walker.background("${escapedSymbol}", {`,
18756
- ` walkerName: "${escapedWalkerName}"`,
18757
- `});`,
18758
- ``,
18759
- `listenSignalBacktest((event) => {`,
18760
- ` console.log(event);`,
18761
- `});`,
18762
- ``,
18763
- `listenBacktestProgress((event) => {`,
18764
- ` console.log(\`Progress: \${(event.progress * 100).toFixed(2)}%\`);`,
18765
- ` console.log(\`Processed: \${event.processedFrames} / \${event.totalFrames}\`);`,
18766
- `});`,
18767
- ``,
18768
- `listenWalkerProgress((event) => {`,
18769
- ` console.log(\`Progress: \${(event.progress * 100).toFixed(2)}%\`);`,
18770
- ` console.log(\`\${event.processedStrategies} / \${event.totalStrategies} strategies\`);`,
18771
- ` console.log(\`Walker: \${event.walkerName}, Symbol: \${event.symbol}\`);`,
18772
- `});`,
18773
- ``,
18774
- `listenWalkerComplete((results) => {`,
18775
- ` console.log("Walker completed:", results.bestStrategy);`,
18776
- ` Walker.dump(results.symbol, { walkerName: results.walkerName });`,
18777
- `});`,
18778
- ``,
18779
- `listenDoneBacktest((event) => {`,
18780
- ` console.log("Backtest completed:", event.symbol);`,
18781
- ` Backtest.dump(event.symbol, {`,
18782
- ` strategyName: event.strategyName,`,
18783
- ` exchangeName: event.exchangeName,`,
18784
- ` frameName: event.frameName`,
18785
- ` });`,
18786
- `});`,
18787
- ``,
18788
- `listenError((error) => {`,
18789
- ` console.error("Error occurred:", error);`,
18790
- `});`
18791
- ].join("\n");
18792
- };
18793
- /**
18794
- * Generates dumpJson() helper function for debug output.
18795
- * Saves LLM conversations and results to ./dump/strategy/{resultId}/
18796
- *
18797
- * @param symbol - Trading pair symbol (unused, for consistency)
18798
- * @returns Generated async dumpJson() function
18799
- */
18800
- this.getJsonDumpTemplate = async (symbol) => {
18801
- this.loggerService.log("optimizerTemplateService getJsonDumpTemplate", {
18802
- symbol,
18803
- });
18804
- return [
18805
- `async function dumpJson(resultId, history, result, outputDir = "./dump/strategy") {`,
18806
- ` // Extract system messages and system reminders from existing data`,
18807
- ` const systemMessages = history.filter((m) => m.role === "system");`,
18808
- ` const userMessages = history.filter((m) => m.role === "user");`,
18809
- ` const subfolderPath = path.join(outputDir, resultId);`,
18810
- ``,
18811
- ` try {`,
18812
- ` await fs.access(subfolderPath);`,
18813
- ` return;`,
18814
- ` } catch {`,
18815
- ` await fs.mkdir(subfolderPath, { recursive: true });`,
18816
- ` }`,
18817
- ``,
18818
- ` {`,
18819
- ` let summary = "# Outline Result Summary\\n\\n";`,
18820
- ``,
18821
- ` {`,
18822
- ` summary += \`**ResultId**: \${resultId}\\n\\n\`;`,
18823
- ` }`,
18824
- ``,
18825
- ` if (result) {`,
18826
- ` summary += "## Output Data\\n\\n";`,
18827
- ` summary += "\`\`\`json\\n";`,
18828
- ` summary += JSON.stringify(result, null, 2);`,
18829
- ` summary += "\\n\`\`\`\\n\\n";`,
18830
- ` }`,
18831
- ``,
18832
- ` // Add system messages to summary`,
18833
- ` if (systemMessages.length > 0) {`,
18834
- ` summary += "## System Messages\\n\\n";`,
18835
- ` systemMessages.forEach((msg, idx) => {`,
18836
- ` summary += \`### System Message \${idx + 1}\\n\\n\`;`,
18837
- ` summary += msg.content;`,
18838
- ` summary += "\\n\\n";`,
18839
- ` });`,
18840
- ` }`,
18841
- ``,
18842
- ` const summaryFile = path.join(subfolderPath, "00_system_prompt.md");`,
18843
- ` await fs.writeFile(summaryFile, summary, "utf8");`,
18844
- ` }`,
18845
- ``,
18846
- ` {`,
18847
- ` await Promise.all(`,
18848
- ` Array.from(userMessages.entries()).map(async ([idx, message]) => {`,
18849
- ` const messageNum = String(idx + 1).padStart(2, "0");`,
18850
- ` const contentFileName = \`\${messageNum}_user_message.md\`;`,
18851
- ` const contentFilePath = path.join(subfolderPath, contentFileName);`,
18852
- ``,
18853
- ` {`,
18854
- ` const messageSizeBytes = Buffer.byteLength(message.content, "utf8");`,
18855
- ` const messageSizeKb = Math.floor(messageSizeBytes / 1024);`,
18856
- ` if (messageSizeKb > WARN_KB) {`,
18857
- ` console.warn(`,
18858
- ` \`User message \${idx + 1} is \${messageSizeBytes} bytes (\${messageSizeKb}kb), which exceeds warning limit\``,
18859
- ` );`,
18860
- ` }`,
18861
- ` }`,
18862
- ``,
18863
- ` let content = \`# User Input \${idx + 1}\\n\\n\`;`,
18864
- ` content += \`**ResultId**: \${resultId}\\n\\n\`;`,
18865
- ` content += message.content;`,
18866
- ` content += "\\n";`,
18867
- ``,
18868
- ` await fs.writeFile(contentFilePath, content, "utf8");`,
18869
- ` })`,
18870
- ` );`,
18871
- ` }`,
18872
- ``,
18873
- ` {`,
18874
- ` const messageNum = String(userMessages.length + 1).padStart(2, "0");`,
18875
- ` const contentFileName = \`\${messageNum}_llm_output.md\`;`,
18876
- ` const contentFilePath = path.join(subfolderPath, contentFileName);`,
18877
- ``,
18878
- ` let content = "# Full Outline Result\\n\\n";`,
18879
- ` content += \`**ResultId**: \${resultId}\\n\\n\`;`,
18880
- ``,
18881
- ` if (result) {`,
18882
- ` content += "## Output Data\\n\\n";`,
18883
- ` content += "\`\`\`json\\n";`,
18884
- ` content += JSON.stringify(result, null, 2);`,
18885
- ` content += "\\n\`\`\`\\n";`,
18886
- ` }`,
18887
- ``,
18888
- ` await fs.writeFile(contentFilePath, content, "utf8");`,
18889
- ` }`,
18890
- `}`
18891
- ].join("\n");
18892
- };
18893
- /**
18894
- * Generates text() helper for LLM text generation.
18895
- * Uses Ollama deepseek-v3.1:671b model for market analysis.
18896
- *
18897
- * @param symbol - Trading pair symbol (used in prompt)
18898
- * @returns Generated async text() function
18899
- */
18900
- this.getTextTemplate = async (symbol) => {
18901
- this.loggerService.log("optimizerTemplateService getTextTemplate", {
18902
- symbol,
18903
- });
18904
- // Escape special characters in symbol to prevent code injection
18905
- const escapedSymbol = String(symbol)
18906
- .replace(/\\/g, '\\\\')
18907
- .replace(/`/g, '\\`')
18908
- .replace(/\$/g, '\\$')
18909
- .toUpperCase();
18910
- return [
18911
- `async function text(messages) {`,
18912
- ` const ollama = new Ollama({`,
18913
- ` host: "https://ollama.com",`,
18914
- ` headers: {`,
18915
- ` Authorization: \`Bearer \${process.env.OLLAMA_API_KEY}\`,`,
18916
- ` },`,
18917
- ` });`,
18918
- ``,
18919
- ` const response = await ollama.chat({`,
18920
- ` model: "deepseek-v3.1:671b",`,
18921
- ` messages: [`,
18922
- ` {`,
18923
- ` role: "system",`,
18924
- ` content: [`,
18925
- ` "В ответ напиши торговую стратегию где нет ничего лишнего,",`,
18926
- ` "только отчёт готовый для копипасты целиком",`,
18927
- ` "",`,
18928
- ` "**ВАЖНО**: Не здоровайся, не говори что делаешь - только отчёт!"`,
18929
- ` ].join("\\n"),`,
18930
- ` },`,
18931
- ` ...messages,`,
18932
- ` {`,
18933
- ` role: "user",`,
18934
- ` content: [`,
18935
- ` "На каких условиях мне купить ${escapedSymbol}?",`,
18936
- ` "Дай анализ рынка на основе поддержки/сопротивления, точек входа в LONG/SHORT позиции.",`,
18937
- ` "Какой RR ставить для позиций?",`,
18938
- ` "Предпочтительны LONG или SHORT позиции?",`,
18939
- ` "",`,
18940
- ` "Сделай не сухой технический, а фундаментальный анализ, содержащий стратигическую рекомендацию, например, покупать на низу боковика"`,
18941
- ` ].join("\\n")`,
18942
- ` }`,
18943
- ` ]`,
18944
- ` });`,
18945
- ``,
18946
- ` const content = response.message.content.trim();`,
18947
- ` return content`,
18948
- ` .replace(/\\\\/g, '\\\\\\\\')`,
18949
- ` .replace(/\`/g, '\\\\\`')`,
18950
- ` .replace(/\\$/g, '\\\\$')`,
18951
- ` .replace(/"/g, '\\\\"')`,
18952
- ` .replace(/'/g, "\\\\'");`,
18953
- `}`
18954
- ].join("\n");
18412
+ const HANDLE_PROFIT_FN = async (symbol, data, currentPrice, revenuePercent, backtest, when, self) => {
18413
+ if (self._states === NEED_FETCH$1) {
18414
+ throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
18415
+ }
18416
+ if (data.id !== self.params.signalId) {
18417
+ throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
18418
+ }
18419
+ let state = self._states.get(data.id);
18420
+ if (!state) {
18421
+ state = {
18422
+ profitLevels: new Set(),
18423
+ lossLevels: new Set(),
18955
18424
  };
18956
- /**
18957
- * Generates json() helper for structured LLM output.
18958
- * Uses Ollama with JSON schema for trading signals.
18959
- *
18960
- * Signal schema:
18961
- * - position: "wait" | "long" | "short"
18962
- * - note: strategy explanation
18963
- * - priceOpen: entry price
18964
- * - priceTakeProfit: target price
18965
- * - priceStopLoss: stop price
18966
- * - minuteEstimatedTime: expected duration (max 360 min)
18967
- *
18968
- * @param symbol - Trading pair symbol (unused, for consistency)
18969
- * @returns Generated async json() function with signal schema
18970
- */
18971
- this.getJsonTemplate = async (symbol) => {
18972
- this.loggerService.log("optimizerTemplateService getJsonTemplate", {
18425
+ self._states.set(data.id, state);
18426
+ }
18427
+ let shouldPersist = false;
18428
+ for (const level of PROFIT_LEVELS) {
18429
+ if (revenuePercent >= level && !state.profitLevels.has(level)) {
18430
+ state.profitLevels.add(level);
18431
+ shouldPersist = true;
18432
+ self.params.logger.debug("ClientPartial profit level reached", {
18973
18433
  symbol,
18434
+ signalId: data.id,
18435
+ level,
18436
+ revenuePercent,
18437
+ backtest,
18974
18438
  });
18975
- return [
18976
- `async function json(messages) {`,
18977
- ` const ollama = new Ollama({`,
18978
- ` host: "https://ollama.com",`,
18979
- ` headers: {`,
18980
- ` Authorization: \`Bearer \${process.env.OLLAMA_API_KEY}\`,`,
18981
- ` },`,
18982
- ` });`,
18983
- ``,
18984
- ` const response = await ollama.chat({`,
18985
- ` model: "deepseek-v3.1:671b",`,
18986
- ` messages: [`,
18987
- ` {`,
18988
- ` role: "system",`,
18989
- ` content: [`,
18990
- ` "Проанализируй торговую стратегию и верни торговый сигнал.",`,
18991
- ` "",`,
18992
- ` "ПРАВИЛА ОТКРЫТИЯ ПОЗИЦИЙ:",`,
18993
- ` "",`,
18994
- ` "1. ТИПЫ ПОЗИЦИЙ:",`,
18995
- ` " - position='wait': нет четкого сигнала, жди лучших условий",`,
18996
- ` " - position='long': бычий сигнал, цена будет расти",`,
18997
- ` " - position='short': медвежий сигнал, цена будет падать",`,
18998
- ` "",`,
18999
- ` "2. ЦЕНА ВХОДА (priceOpen):",`,
19000
- ` " - Может быть текущей рыночной ценой для немедленного входа",`,
19001
- ` " - Может быть отложенной ценой для входа при достижении уровня",`,
19002
- ` " - Укажи оптимальную цену входа согласно технического анализа",`,
19003
- ` "",`,
19004
- ` "3. УРОВНИ ВЫХОДА:",`,
19005
- ` " - LONG: priceTakeProfit > priceOpen > priceStopLoss",`,
19006
- ` " - SHORT: priceStopLoss > priceOpen > priceTakeProfit",`,
19007
- ` " - Уровни должны иметь техническое обоснование (Fibonacci, S/R, Bollinger)",`,
19008
- ` "",`,
19009
- ` "4. ВРЕМЕННЫЕ РАМКИ:",`,
19010
- ` " - minuteEstimatedTime: прогноз времени до TP (макс 360 минут)",`,
19011
- ` " - Расчет на основе ATR, ADX, MACD, Momentum, Slope",`,
19012
- ` " - Если индикаторов, осциллятор или других метрик нет, посчитай их самостоятельно",`,
19013
- ` ].join("\\n"),`,
19014
- ` },`,
19015
- ` ...messages,`,
19016
- ` ],`,
19017
- ` format: {`,
19018
- ` type: "object",`,
19019
- ` properties: {`,
19020
- ` position: {`,
19021
- ` type: "string",`,
19022
- ` enum: ["wait", "long", "short"],`,
19023
- ` description: "Trade decision: wait (no signal), long (buy), or short (sell)",`,
19024
- ` },`,
19025
- ` note: {`,
19026
- ` type: "string",`,
19027
- ` description: "Professional trading recommendation with price levels",`,
19028
- ` },`,
19029
- ` priceOpen: {`,
19030
- ` type: "number",`,
19031
- ` description: "Entry price (current market price or limit order price)",`,
19032
- ` },`,
19033
- ` priceTakeProfit: {`,
19034
- ` type: "number",`,
19035
- ` description: "Take profit target price",`,
19036
- ` },`,
19037
- ` priceStopLoss: {`,
19038
- ` type: "number",`,
19039
- ` description: "Stop loss exit price",`,
19040
- ` },`,
19041
- ` minuteEstimatedTime: {`,
19042
- ` type: "number",`,
19043
- ` description: "Expected time to reach TP in minutes (max 360)",`,
19044
- ` },`,
19045
- ` },`,
19046
- ` required: ["position", "note", "priceOpen", "priceTakeProfit", "priceStopLoss", "minuteEstimatedTime"],`,
19047
- ` },`,
19048
- ` });`,
19049
- ``,
19050
- ` const jsonResponse = JSON.parse(response.message.content.trim());`,
19051
- ` return jsonResponse;`,
19052
- `}`
19053
- ].join("\n");
19054
- };
18439
+ await self.params.onProfit(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
18440
+ }
19055
18441
  }
19056
- }
19057
-
18442
+ if (shouldPersist) {
18443
+ await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
18444
+ }
18445
+ };
19058
18446
  /**
19059
- * Service for managing optimizer schema registration and retrieval.
19060
- * Provides validation and registry management for optimizer configurations.
18447
+ * Internal loss handler function for ClientPartial.
18448
+ *
18449
+ * Checks which loss levels have been reached and emits events for new levels only.
18450
+ * Uses Set-based deduplication to prevent duplicate events.
18451
+ * Converts negative lossPercent to absolute value for level comparison.
19061
18452
  *
19062
- * Uses ToolRegistry for immutable schema storage.
18453
+ * @param symbol - Trading pair symbol
18454
+ * @param data - Signal row data
18455
+ * @param currentPrice - Current market price
18456
+ * @param lossPercent - Current loss percentage (negative value)
18457
+ * @param backtest - True if backtest mode
18458
+ * @param when - Event timestamp
18459
+ * @param self - ClientPartial instance reference
19063
18460
  */
19064
- class OptimizerSchemaService {
19065
- constructor() {
19066
- this.loggerService = inject(TYPES.loggerService);
19067
- this._registry = new ToolRegistry("optimizerSchema");
19068
- /**
19069
- * Registers a new optimizer schema.
19070
- * Validates required fields before registration.
19071
- *
19072
- * @param key - Unique optimizer name
19073
- * @param value - Optimizer schema configuration
19074
- * @throws Error if schema validation fails
19075
- */
19076
- this.register = (key, value) => {
19077
- this.loggerService.log(`optimizerSchemaService register`, { key });
19078
- this.validateShallow(value);
19079
- this._registry = this._registry.register(key, value);
18461
+ const HANDLE_LOSS_FN = async (symbol, data, currentPrice, lossPercent, backtest, when, self) => {
18462
+ if (self._states === NEED_FETCH$1) {
18463
+ throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
18464
+ }
18465
+ if (data.id !== self.params.signalId) {
18466
+ throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
18467
+ }
18468
+ let state = self._states.get(data.id);
18469
+ if (!state) {
18470
+ state = {
18471
+ profitLevels: new Set(),
18472
+ lossLevels: new Set(),
19080
18473
  };
19081
- /**
19082
- * Validates optimizer schema structure.
19083
- * Checks required fields: optimizerName, rangeTrain, source, getPrompt.
19084
- *
19085
- * @param optimizerSchema - Schema to validate
19086
- * @throws Error if validation fails
19087
- */
19088
- this.validateShallow = (optimizerSchema) => {
19089
- this.loggerService.log(`optimizerTemplateService validateShallow`, {
19090
- optimizerSchema,
18474
+ self._states.set(data.id, state);
18475
+ }
18476
+ const absLoss = Math.abs(lossPercent);
18477
+ let shouldPersist = false;
18478
+ for (const level of LOSS_LEVELS) {
18479
+ if (absLoss >= level && !state.lossLevels.has(level)) {
18480
+ state.lossLevels.add(level);
18481
+ shouldPersist = true;
18482
+ self.params.logger.debug("ClientPartial loss level reached", {
18483
+ symbol,
18484
+ signalId: data.id,
18485
+ level,
18486
+ lossPercent,
18487
+ backtest,
19091
18488
  });
19092
- if (typeof optimizerSchema.optimizerName !== "string") {
19093
- throw new Error(`optimizer template validation failed: missing optimizerName`);
19094
- }
19095
- if (!Array.isArray(optimizerSchema.rangeTrain) || optimizerSchema.rangeTrain.length === 0) {
19096
- throw new Error(`optimizer template validation failed: rangeTrain must be a non-empty array for optimizerName=${optimizerSchema.optimizerName}`);
19097
- }
19098
- if (!Array.isArray(optimizerSchema.source) || optimizerSchema.source.length === 0) {
19099
- throw new Error(`optimizer template validation failed: source must be a non-empty array for optimizerName=${optimizerSchema.optimizerName}`);
19100
- }
19101
- if (typeof optimizerSchema.getPrompt !== "function") {
19102
- throw new Error(`optimizer template validation failed: getPrompt must be a function for optimizerName=${optimizerSchema.optimizerName}`);
19103
- }
19104
- };
19105
- /**
19106
- * Partially overrides an existing optimizer schema.
19107
- * Merges provided values with existing schema.
19108
- *
19109
- * @param key - Optimizer name to override
19110
- * @param value - Partial schema values to merge
19111
- * @returns Updated complete schema
19112
- * @throws Error if optimizer not found
19113
- */
19114
- this.override = (key, value) => {
19115
- this.loggerService.log(`optimizerSchemaService override`, { key });
19116
- this._registry = this._registry.override(key, value);
19117
- return this._registry.get(key);
19118
- };
19119
- /**
19120
- * Retrieves optimizer schema by name.
19121
- *
19122
- * @param key - Optimizer name
19123
- * @returns Complete optimizer schema
19124
- * @throws Error if optimizer not found
19125
- */
19126
- this.get = (key) => {
19127
- this.loggerService.log(`optimizerSchemaService get`, { key });
19128
- return this._registry.get(key);
19129
- };
18489
+ await self.params.onLoss(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
18490
+ }
19130
18491
  }
19131
- }
19132
-
18492
+ if (shouldPersist) {
18493
+ await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
18494
+ }
18495
+ };
19133
18496
  /**
19134
- * Service for validating optimizer existence and managing optimizer registry.
19135
- * Maintains a Map of registered optimizers for validation purposes.
18497
+ * Internal initialization function for ClientPartial.
18498
+ *
18499
+ * Loads persisted partial state from disk and restores in-memory Maps.
18500
+ * Converts serialized arrays back to Sets for O(1) lookups.
18501
+ *
18502
+ * ONLY runs in LIVE mode (backtest=false). In backtest mode, state is not persisted.
19136
18503
  *
19137
- * Uses memoization for efficient repeated validation checks.
19138
- */
19139
- class OptimizerValidationService {
19140
- constructor() {
19141
- this.loggerService = inject(TYPES.loggerService);
19142
- this._optimizerMap = new Map();
19143
- /**
19144
- * Adds optimizer to validation registry.
19145
- * Prevents duplicate optimizer names.
19146
- *
19147
- * @param optimizerName - Unique optimizer identifier
19148
- * @param optimizerSchema - Complete optimizer schema
19149
- * @throws Error if optimizer with same name already exists
19150
- */
19151
- this.addOptimizer = (optimizerName, optimizerSchema) => {
19152
- this.loggerService.log("optimizerValidationService addOptimizer", {
19153
- optimizerName,
19154
- optimizerSchema,
19155
- });
19156
- if (this._optimizerMap.has(optimizerName)) {
19157
- throw new Error(`optimizer ${optimizerName} already exist`);
19158
- }
19159
- this._optimizerMap.set(optimizerName, optimizerSchema);
19160
- };
19161
- /**
19162
- * Validates that optimizer exists in registry.
19163
- * Memoized for performance on repeated checks.
19164
- *
19165
- * @param optimizerName - Optimizer name to validate
19166
- * @param source - Source method name for error messages
19167
- * @throws Error if optimizer not found
19168
- */
19169
- this.validate = memoize(([optimizerName]) => optimizerName, (optimizerName, source) => {
19170
- this.loggerService.log("optimizerValidationService validate", {
19171
- optimizerName,
19172
- source,
19173
- });
19174
- const optimizer = this._optimizerMap.get(optimizerName);
19175
- if (!optimizer) {
19176
- throw new Error(`optimizer ${optimizerName} not found source=${source}`);
19177
- }
19178
- return true;
19179
- });
19180
- /**
19181
- * Lists all registered optimizer schemas.
19182
- *
19183
- * @returns Array of all optimizer schemas
19184
- */
19185
- this.list = async () => {
19186
- this.loggerService.log("optimizerValidationService list");
19187
- return Array.from(this._optimizerMap.values());
19188
- };
19189
- }
19190
- }
19191
-
19192
- const METHOD_NAME_GET_DATA = "optimizerGlobalService getData";
19193
- const METHOD_NAME_GET_CODE = "optimizerGlobalService getCode";
19194
- const METHOD_NAME_DUMP = "optimizerGlobalService dump";
19195
- /**
19196
- * Global service for optimizer operations with validation.
19197
- * Entry point for public API, performs validation before delegating to ConnectionService.
19198
- *
19199
- * Workflow:
19200
- * 1. Log operation
19201
- * 2. Validate optimizer exists
19202
- * 3. Delegate to OptimizerConnectionService
19203
- */
19204
- class OptimizerGlobalService {
19205
- constructor() {
19206
- this.loggerService = inject(TYPES.loggerService);
19207
- this.optimizerConnectionService = inject(TYPES.optimizerConnectionService);
19208
- this.optimizerValidationService = inject(TYPES.optimizerValidationService);
19209
- /**
19210
- * Fetches data from all sources and generates strategy metadata.
19211
- * Validates optimizer existence before execution.
19212
- *
19213
- * @param symbol - Trading pair symbol
19214
- * @param optimizerName - Optimizer identifier
19215
- * @returns Array of generated strategies with conversation context
19216
- * @throws Error if optimizer not found
19217
- */
19218
- this.getData = async (symbol, optimizerName) => {
19219
- this.loggerService.log(METHOD_NAME_GET_DATA, {
19220
- symbol,
19221
- optimizerName,
19222
- });
19223
- this.optimizerValidationService.validate(optimizerName, METHOD_NAME_GET_DATA);
19224
- return await this.optimizerConnectionService.getData(symbol, optimizerName);
19225
- };
19226
- /**
19227
- * Generates complete executable strategy code.
19228
- * Validates optimizer existence before execution.
19229
- *
19230
- * @param symbol - Trading pair symbol
19231
- * @param optimizerName - Optimizer identifier
19232
- * @returns Generated TypeScript/JavaScript code as string
19233
- * @throws Error if optimizer not found
19234
- */
19235
- this.getCode = async (symbol, optimizerName) => {
19236
- this.loggerService.log(METHOD_NAME_GET_CODE, {
19237
- symbol,
19238
- optimizerName,
19239
- });
19240
- this.optimizerValidationService.validate(optimizerName, METHOD_NAME_GET_CODE);
19241
- return await this.optimizerConnectionService.getCode(symbol, optimizerName);
19242
- };
19243
- /**
19244
- * Generates and saves strategy code to file.
19245
- * Validates optimizer existence before execution.
19246
- *
19247
- * @param symbol - Trading pair symbol
19248
- * @param optimizerName - Optimizer identifier
19249
- * @param path - Output directory path (optional)
19250
- * @throws Error if optimizer not found
19251
- */
19252
- this.dump = async (symbol, optimizerName, path) => {
19253
- this.loggerService.log(METHOD_NAME_DUMP, {
19254
- symbol,
19255
- optimizerName,
19256
- path,
19257
- });
19258
- this.optimizerValidationService.validate(optimizerName, METHOD_NAME_DUMP);
19259
- return await this.optimizerConnectionService.dump(symbol, optimizerName, path);
19260
- };
19261
- }
19262
- }
19263
-
19264
- const ITERATION_LIMIT = 25;
19265
- const DEFAULT_SOURCE_NAME = "unknown";
19266
- const CREATE_PREFIX_FN = () => (Math.random() + 1).toString(36).substring(7);
19267
- /**
19268
- * Wrapper to call onSourceData callback with error handling.
19269
- * Catches and logs any errors thrown by the user-provided callback.
19270
- */
19271
- const CALL_SOURCE_DATA_CALLBACKS_FN = trycatch(async (self, symbol, name, data, startDate, endDate) => {
19272
- if (self.params.callbacks?.onSourceData) {
19273
- await self.params.callbacks.onSourceData(symbol, name, data, startDate, endDate);
19274
- }
19275
- }, {
19276
- fallback: (error) => {
19277
- const message = "ClientOptimizer CALL_SOURCE_DATA_CALLBACKS_FN thrown";
19278
- const payload = {
19279
- error: errorData(error),
19280
- message: getErrorMessage(error),
19281
- };
19282
- bt.loggerService.warn(message, payload);
19283
- console.warn(message, payload);
19284
- errorEmitter.next(error);
19285
- },
19286
- });
19287
- /**
19288
- * Wrapper to call onData callback with error handling.
19289
- * Catches and logs any errors thrown by the user-provided callback.
19290
- */
19291
- const CALL_DATA_CALLBACKS_FN = trycatch(async (self, symbol, strategyList) => {
19292
- if (self.params.callbacks?.onData) {
19293
- await self.params.callbacks.onData(symbol, strategyList);
19294
- }
19295
- }, {
19296
- fallback: (error) => {
19297
- const message = "ClientOptimizer CALL_DATA_CALLBACKS_FN thrown";
19298
- const payload = {
19299
- error: errorData(error),
19300
- message: getErrorMessage(error),
19301
- };
19302
- bt.loggerService.warn(message, payload);
19303
- console.warn(message, payload);
19304
- errorEmitter.next(error);
19305
- },
19306
- });
19307
- /**
19308
- * Wrapper to call onCode callback with error handling.
19309
- * Catches and logs any errors thrown by the user-provided callback.
19310
- */
19311
- const CALL_CODE_CALLBACKS_FN = trycatch(async (self, symbol, code) => {
19312
- if (self.params.callbacks?.onCode) {
19313
- await self.params.callbacks.onCode(symbol, code);
19314
- }
19315
- }, {
19316
- fallback: (error) => {
19317
- const message = "ClientOptimizer CALL_CODE_CALLBACKS_FN thrown";
19318
- const payload = {
19319
- error: errorData(error),
19320
- message: getErrorMessage(error),
19321
- };
19322
- bt.loggerService.warn(message, payload);
19323
- console.warn(message, payload);
19324
- errorEmitter.next(error);
19325
- },
19326
- });
19327
- /**
19328
- * Wrapper to call onDump callback with error handling.
19329
- * Catches and logs any errors thrown by the user-provided callback.
19330
- */
19331
- const CALL_DUMP_CALLBACKS_FN = trycatch(async (self, symbol, filepath) => {
19332
- if (self.params.callbacks?.onDump) {
19333
- await self.params.callbacks.onDump(symbol, filepath);
19334
- }
19335
- }, {
19336
- fallback: (error) => {
19337
- const message = "ClientOptimizer CALL_DUMP_CALLBACKS_FN thrown";
19338
- const payload = {
19339
- error: errorData(error),
19340
- message: getErrorMessage(error),
19341
- };
19342
- bt.loggerService.warn(message, payload);
19343
- console.warn(message, payload);
19344
- errorEmitter.next(error);
19345
- },
19346
- });
19347
- /**
19348
- * Default user message formatter.
19349
- * Delegates to template's getUserMessage method.
19350
- *
19351
- * @param symbol - Trading pair symbol
19352
- * @param data - Fetched data array
19353
- * @param name - Source name
19354
- * @param self - ClientOptimizer instance
19355
- * @returns Formatted user message content
19356
- */
19357
- const DEFAULT_USER_FN = async (symbol, data, name, self) => {
19358
- return await self.params.template.getUserMessage(symbol, data, name);
19359
- };
19360
- /**
19361
- * Default assistant message formatter.
19362
- * Delegates to template's getAssistantMessage method.
19363
- *
19364
- * @param symbol - Trading pair symbol
19365
- * @param data - Fetched data array
19366
- * @param name - Source name
19367
- * @param self - ClientOptimizer instance
19368
- * @returns Formatted assistant message content
19369
- */
19370
- const DEFAULT_ASSISTANT_FN = async (symbol, data, name, self) => {
19371
- return await self.params.template.getAssistantMessage(symbol, data, name);
19372
- };
19373
- /**
19374
- * Resolves paginated data from source with deduplication.
19375
- * Uses iterateDocuments to handle pagination automatically.
19376
- *
19377
- * @param fetch - Source fetch function
19378
- * @param filterData - Filter arguments (symbol, dates)
19379
- * @returns Deduplicated array of all fetched data
19380
- */
19381
- const RESOLVE_PAGINATION_FN = async (fetch, filterData) => {
19382
- const iterator = iterateDocuments({
19383
- limit: ITERATION_LIMIT,
19384
- async createRequest({ limit, offset }) {
19385
- return await fetch({
19386
- symbol: filterData.symbol,
19387
- startDate: filterData.startDate,
19388
- endDate: filterData.endDate,
19389
- limit,
19390
- offset,
19391
- });
19392
- },
19393
- });
19394
- const distinct = distinctDocuments(iterator, (data) => data.id);
19395
- return await resolveDocuments(distinct);
19396
- };
19397
- /**
19398
- * Collects data from all sources and generates strategy metadata.
19399
- * Iterates through training ranges, fetches data from each source,
19400
- * builds LLM conversation history, and generates strategy prompts.
19401
- *
19402
- * @param symbol - Trading pair symbol
19403
- * @param self - ClientOptimizer instance
19404
- * @returns Array of generated strategies with conversation context
19405
- */
19406
- const GET_STRATEGY_DATA_FN = async (symbol, self) => {
19407
- const strategyList = [];
19408
- const totalSources = self.params.rangeTrain.length * self.params.source.length;
19409
- let processedSources = 0;
19410
- for (const { startDate, endDate } of self.params.rangeTrain) {
19411
- const messageList = [];
19412
- for (const source of self.params.source) {
19413
- // Emit progress event at the start of processing each source
19414
- await self.onProgress({
19415
- optimizerName: self.params.optimizerName,
19416
- symbol,
19417
- totalSources,
19418
- processedSources,
19419
- progress: totalSources > 0 ? processedSources / totalSources : 0,
19420
- });
19421
- if (typeof source === "function") {
19422
- const data = await RESOLVE_PAGINATION_FN(source, {
19423
- symbol,
19424
- startDate,
19425
- endDate,
19426
- });
19427
- await CALL_SOURCE_DATA_CALLBACKS_FN(self, symbol, DEFAULT_SOURCE_NAME, data, startDate, endDate);
19428
- const [userContent, assistantContent] = await Promise.all([
19429
- DEFAULT_USER_FN(symbol, data, DEFAULT_SOURCE_NAME, self),
19430
- DEFAULT_ASSISTANT_FN(symbol, data, DEFAULT_SOURCE_NAME, self),
19431
- ]);
19432
- messageList.push({
19433
- role: "user",
19434
- content: userContent,
19435
- }, {
19436
- role: "assistant",
19437
- content: assistantContent,
19438
- });
19439
- processedSources++;
19440
- }
19441
- else {
19442
- const { fetch, name = DEFAULT_SOURCE_NAME, assistant = DEFAULT_ASSISTANT_FN, user = DEFAULT_USER_FN, } = source;
19443
- const data = await RESOLVE_PAGINATION_FN(fetch, {
19444
- symbol,
19445
- startDate,
19446
- endDate,
19447
- });
19448
- await CALL_SOURCE_DATA_CALLBACKS_FN(self, symbol, name, data, startDate, endDate);
19449
- const [userContent, assistantContent] = await Promise.all([
19450
- user(symbol, data, name, self),
19451
- assistant(symbol, data, name, self),
19452
- ]);
19453
- messageList.push({
19454
- role: "user",
19455
- content: userContent,
19456
- }, {
19457
- role: "assistant",
19458
- content: assistantContent,
19459
- });
19460
- processedSources++;
19461
- }
19462
- const name = "name" in source
19463
- ? source.name || DEFAULT_SOURCE_NAME
19464
- : DEFAULT_SOURCE_NAME;
19465
- strategyList.push({
19466
- symbol,
19467
- name,
19468
- messages: messageList,
19469
- strategy: await self.params.getPrompt(symbol, messageList),
19470
- });
19471
- }
19472
- }
19473
- // Emit final progress event (100%)
19474
- await self.onProgress({
19475
- optimizerName: self.params.optimizerName,
19476
- symbol,
19477
- totalSources,
19478
- processedSources: totalSources,
19479
- progress: 1.0,
19480
- });
19481
- await CALL_DATA_CALLBACKS_FN(self, symbol, strategyList);
19482
- return strategyList;
19483
- };
19484
- /**
19485
- * Generates complete executable strategy code.
19486
- * Assembles all components: imports, helpers, exchange, frames, strategies, walker, launcher.
19487
- *
19488
- * @param symbol - Trading pair symbol
19489
- * @param self - ClientOptimizer instance
19490
- * @returns Generated TypeScript/JavaScript code as string
19491
- */
19492
- const GET_STRATEGY_CODE_FN = async (symbol, self) => {
19493
- const strategyData = await self.getData(symbol);
19494
- const prefix = CREATE_PREFIX_FN();
19495
- const sections = [];
19496
- const exchangeName = `${prefix}_exchange`;
19497
- // 1. Top banner with imports
19498
- {
19499
- sections.push(await self.params.template.getTopBanner(symbol));
19500
- sections.push("");
19501
- }
19502
- // 2. JSON dump helper function
19503
- {
19504
- sections.push(await self.params.template.getJsonDumpTemplate(symbol));
19505
- sections.push("");
19506
- }
19507
- // 3. Helper functions (text and json)
19508
- {
19509
- sections.push(await self.params.template.getTextTemplate(symbol));
19510
- sections.push("");
19511
- }
19512
- {
19513
- sections.push(await self.params.template.getJsonTemplate(symbol));
19514
- sections.push("");
19515
- }
19516
- // 4. Exchange template (assuming first strategy has exchange info)
19517
- {
19518
- sections.push(await self.params.template.getExchangeTemplate(symbol, exchangeName));
19519
- sections.push("");
19520
- }
19521
- // 5. Train frame templates
19522
- {
19523
- for (let i = 0; i < self.params.rangeTrain.length; i++) {
19524
- const range = self.params.rangeTrain[i];
19525
- const frameName = `${prefix}_train_frame-${i + 1}`;
19526
- sections.push(await self.params.template.getFrameTemplate(symbol, frameName, "1m", // default interval
19527
- range.startDate, range.endDate));
19528
- sections.push("");
19529
- }
19530
- }
19531
- // 6. Test frame template
19532
- {
19533
- const testFrameName = `${prefix}_test_frame`;
19534
- sections.push(await self.params.template.getFrameTemplate(symbol, testFrameName, "1m", // default interval
19535
- self.params.rangeTest.startDate, self.params.rangeTest.endDate));
19536
- sections.push("");
19537
- }
19538
- // 7. Strategy templates for each generated strategy
19539
- {
19540
- for (let i = 0; i < strategyData.length; i++) {
19541
- const strategy = strategyData[i];
19542
- const strategyName = `${prefix}_strategy-${i + 1}`;
19543
- const interval = "5m"; // default interval
19544
- sections.push(await self.params.template.getStrategyTemplate(strategyName, interval, strategy.strategy));
19545
- sections.push("");
19546
- }
19547
- }
19548
- // 8. Walker template (uses test frame for validation)
19549
- {
19550
- const walkerName = `${prefix}_walker`;
19551
- const testFrameName = `${prefix}_test_frame`;
19552
- const strategies = strategyData.map((_, i) => `${prefix}_strategy-${i + 1}`);
19553
- sections.push(await self.params.template.getWalkerTemplate(walkerName, `${exchangeName}`, testFrameName, strategies));
19554
- sections.push("");
19555
- }
19556
- // 9. Launcher template
19557
- {
19558
- const walkerName = `${prefix}_walker`;
19559
- sections.push(await self.params.template.getLauncherTemplate(symbol, walkerName));
19560
- sections.push("");
19561
- }
19562
- const code = sections.join("\n");
19563
- await CALL_CODE_CALLBACKS_FN(self, symbol, code);
19564
- return code;
19565
- };
19566
- /**
19567
- * Saves generated strategy code to file.
19568
- * Creates directory if needed, writes .mjs file with generated code.
19569
- *
19570
- * @param symbol - Trading pair symbol
19571
- * @param path - Output directory path
19572
- * @param self - ClientOptimizer instance
19573
- */
19574
- const GET_STRATEGY_DUMP_FN = async (symbol, path, self) => {
19575
- const report = await self.getCode(symbol);
19576
- try {
19577
- const dir = join(process.cwd(), path);
19578
- await mkdir(dir, { recursive: true });
19579
- const filename = `${self.params.optimizerName}_${symbol}.mjs`;
19580
- const filepath = join(dir, filename);
19581
- await writeFile(filepath, report, "utf-8");
19582
- self.params.logger.info(`Optimizer report saved: ${filepath}`);
19583
- await CALL_DUMP_CALLBACKS_FN(self, symbol, filepath);
19584
- }
19585
- catch (error) {
19586
- self.params.logger.warn(`Failed to save optimizer report:`, error);
19587
- throw error;
19588
- }
19589
- };
19590
- /**
19591
- * Client implementation for optimizer operations.
19592
- *
19593
- * Features:
19594
- * - Data collection from multiple sources with pagination
19595
- * - LLM conversation history building
19596
- * - Strategy code generation with templates
19597
- * - File export with callbacks
19598
- *
19599
- * Used by OptimizerConnectionService to create optimizer instances.
19600
- */
19601
- class ClientOptimizer {
19602
- constructor(params, onProgress) {
19603
- this.params = params;
19604
- this.onProgress = onProgress;
19605
- /**
19606
- * Fetches data from all sources and generates strategy metadata.
19607
- * Processes each training range and builds LLM conversation history.
19608
- *
19609
- * @param symbol - Trading pair symbol
19610
- * @returns Array of generated strategies with conversation context
19611
- */
19612
- this.getData = async (symbol) => {
19613
- this.params.logger.debug("ClientOptimizer getData", {
19614
- symbol,
19615
- });
19616
- return await GET_STRATEGY_DATA_FN(symbol, this);
19617
- };
19618
- /**
19619
- * Generates complete executable strategy code.
19620
- * Includes imports, helpers, strategies, walker, and launcher.
19621
- *
19622
- * @param symbol - Trading pair symbol
19623
- * @returns Generated TypeScript/JavaScript code as string
19624
- */
19625
- this.getCode = async (symbol) => {
19626
- this.params.logger.debug("ClientOptimizer getCode", {
19627
- symbol,
19628
- });
19629
- return await GET_STRATEGY_CODE_FN(symbol, this);
19630
- };
19631
- /**
19632
- * Generates and saves strategy code to file.
19633
- * Creates directory if needed, writes .mjs file.
19634
- *
19635
- * @param symbol - Trading pair symbol
19636
- * @param path - Output directory path (default: "./")
19637
- */
19638
- this.dump = async (symbol, path = "./") => {
19639
- this.params.logger.debug("ClientOptimizer dump", {
19640
- symbol,
19641
- path,
19642
- });
19643
- return await GET_STRATEGY_DUMP_FN(symbol, path, this);
19644
- };
19645
- }
19646
- }
19647
-
19648
- /**
19649
- * Callback function for emitting progress events to progressOptimizerEmitter.
19650
- */
19651
- const COMMIT_PROGRESS_FN = async (progress) => progressOptimizerEmitter.next(progress);
19652
- /**
19653
- * Service for creating and caching optimizer client instances.
19654
- * Handles dependency injection and template merging.
19655
- *
19656
- * Features:
19657
- * - Memoized optimizer instances (one per optimizerName)
19658
- * - Template merging (custom + defaults)
19659
- * - Logger injection
19660
- * - Delegates to ClientOptimizer for actual operations
19661
- */
19662
- class OptimizerConnectionService {
19663
- constructor() {
19664
- this.loggerService = inject(TYPES.loggerService);
19665
- this.optimizerSchemaService = inject(TYPES.optimizerSchemaService);
19666
- this.optimizerTemplateService = inject(TYPES.optimizerTemplateService);
19667
- /**
19668
- * Creates or retrieves cached optimizer instance.
19669
- * Memoized by optimizerName for performance.
19670
- *
19671
- * Merges custom templates from schema with defaults from OptimizerTemplateService.
19672
- *
19673
- * @param optimizerName - Unique optimizer identifier
19674
- * @returns ClientOptimizer instance with resolved dependencies
19675
- */
19676
- this.getOptimizer = memoize(([optimizerName]) => `${optimizerName}`, (optimizerName) => {
19677
- const { getPrompt, rangeTest, rangeTrain, source, template: rawTemplate = {}, callbacks, } = this.optimizerSchemaService.get(optimizerName);
19678
- 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;
19679
- const template = {
19680
- getAssistantMessage,
19681
- getExchangeTemplate,
19682
- getFrameTemplate,
19683
- getJsonDumpTemplate,
19684
- getJsonTemplate,
19685
- getLauncherTemplate,
19686
- getStrategyTemplate,
19687
- getTextTemplate,
19688
- getWalkerTemplate,
19689
- getTopBanner,
19690
- getUserMessage,
19691
- };
19692
- return new ClientOptimizer({
19693
- optimizerName,
19694
- logger: this.loggerService,
19695
- getPrompt,
19696
- rangeTest,
19697
- rangeTrain,
19698
- source,
19699
- template,
19700
- callbacks,
19701
- }, COMMIT_PROGRESS_FN);
19702
- });
19703
- /**
19704
- * Fetches data from all sources and generates strategy metadata.
19705
- *
19706
- * @param symbol - Trading pair symbol
19707
- * @param optimizerName - Optimizer identifier
19708
- * @returns Array of generated strategies with conversation context
19709
- */
19710
- this.getData = async (symbol, optimizerName) => {
19711
- this.loggerService.log("optimizerConnectionService getData", {
19712
- symbol,
19713
- optimizerName,
19714
- });
19715
- const optimizer = this.getOptimizer(optimizerName);
19716
- return await optimizer.getData(symbol);
19717
- };
19718
- /**
19719
- * Generates complete executable strategy code.
19720
- *
19721
- * @param symbol - Trading pair symbol
19722
- * @param optimizerName - Optimizer identifier
19723
- * @returns Generated TypeScript/JavaScript code as string
19724
- */
19725
- this.getCode = async (symbol, optimizerName) => {
19726
- this.loggerService.log("optimizerConnectionService getCode", {
19727
- symbol,
19728
- optimizerName,
19729
- });
19730
- const optimizer = this.getOptimizer(optimizerName);
19731
- return await optimizer.getCode(symbol);
19732
- };
19733
- /**
19734
- * Generates and saves strategy code to file.
19735
- *
19736
- * @param symbol - Trading pair symbol
19737
- * @param optimizerName - Optimizer identifier
19738
- * @param path - Output directory path (optional)
19739
- */
19740
- this.dump = async (symbol, optimizerName, path) => {
19741
- this.loggerService.log("optimizerConnectionService getCode", {
19742
- symbol,
19743
- optimizerName,
19744
- });
19745
- const optimizer = this.getOptimizer(optimizerName);
19746
- return await optimizer.dump(symbol, path);
19747
- };
19748
- }
19749
- }
19750
-
19751
- /**
19752
- * Symbol marker indicating that partial state needs initialization.
19753
- * Used as sentinel value for _states before waitForInit() is called.
19754
- */
19755
- const NEED_FETCH$1 = Symbol("need_fetch");
19756
- /**
19757
- * Array of profit level milestones to track (10%, 20%, ..., 100%).
19758
- * Each level is checked during profit() method to emit events for newly reached levels.
19759
- */
19760
- const PROFIT_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
19761
- /**
19762
- * Array of loss level milestones to track (-10%, -20%, ..., -100%).
19763
- * Each level is checked during loss() method to emit events for newly reached levels.
19764
- */
19765
- const LOSS_LEVELS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
19766
- /**
19767
- * Internal profit handler function for ClientPartial.
19768
- *
19769
- * Checks which profit levels have been reached and emits events for new levels only.
19770
- * Uses Set-based deduplication to prevent duplicate events.
19771
- *
19772
- * @param symbol - Trading pair symbol
19773
- * @param data - Signal row data
19774
- * @param currentPrice - Current market price
19775
- * @param revenuePercent - Current profit percentage (positive value)
19776
- * @param backtest - True if backtest mode
19777
- * @param when - Event timestamp
19778
- * @param self - ClientPartial instance reference
19779
- */
19780
- const HANDLE_PROFIT_FN = async (symbol, data, currentPrice, revenuePercent, backtest, when, self) => {
19781
- if (self._states === NEED_FETCH$1) {
19782
- throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
19783
- }
19784
- if (data.id !== self.params.signalId) {
19785
- throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
19786
- }
19787
- let state = self._states.get(data.id);
19788
- if (!state) {
19789
- state = {
19790
- profitLevels: new Set(),
19791
- lossLevels: new Set(),
19792
- };
19793
- self._states.set(data.id, state);
19794
- }
19795
- let shouldPersist = false;
19796
- for (const level of PROFIT_LEVELS) {
19797
- if (revenuePercent >= level && !state.profitLevels.has(level)) {
19798
- state.profitLevels.add(level);
19799
- shouldPersist = true;
19800
- self.params.logger.debug("ClientPartial profit level reached", {
19801
- symbol,
19802
- signalId: data.id,
19803
- level,
19804
- revenuePercent,
19805
- backtest,
19806
- });
19807
- await self.params.onProfit(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
19808
- }
19809
- }
19810
- if (shouldPersist) {
19811
- await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
19812
- }
19813
- };
19814
- /**
19815
- * Internal loss handler function for ClientPartial.
19816
- *
19817
- * Checks which loss levels have been reached and emits events for new levels only.
19818
- * Uses Set-based deduplication to prevent duplicate events.
19819
- * Converts negative lossPercent to absolute value for level comparison.
19820
- *
19821
- * @param symbol - Trading pair symbol
19822
- * @param data - Signal row data
19823
- * @param currentPrice - Current market price
19824
- * @param lossPercent - Current loss percentage (negative value)
19825
- * @param backtest - True if backtest mode
19826
- * @param when - Event timestamp
19827
- * @param self - ClientPartial instance reference
19828
- */
19829
- const HANDLE_LOSS_FN = async (symbol, data, currentPrice, lossPercent, backtest, when, self) => {
19830
- if (self._states === NEED_FETCH$1) {
19831
- throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
19832
- }
19833
- if (data.id !== self.params.signalId) {
19834
- throw new Error(`Signal ID mismatch: expected ${self.params.signalId}, got ${data.id}`);
19835
- }
19836
- let state = self._states.get(data.id);
19837
- if (!state) {
19838
- state = {
19839
- profitLevels: new Set(),
19840
- lossLevels: new Set(),
19841
- };
19842
- self._states.set(data.id, state);
19843
- }
19844
- const absLoss = Math.abs(lossPercent);
19845
- let shouldPersist = false;
19846
- for (const level of LOSS_LEVELS) {
19847
- if (absLoss >= level && !state.lossLevels.has(level)) {
19848
- state.lossLevels.add(level);
19849
- shouldPersist = true;
19850
- self.params.logger.debug("ClientPartial loss level reached", {
19851
- symbol,
19852
- signalId: data.id,
19853
- level,
19854
- lossPercent,
19855
- backtest,
19856
- });
19857
- await self.params.onLoss(symbol, data.strategyName, data.exchangeName, data.frameName, data, currentPrice, level, backtest, when.getTime());
19858
- }
19859
- }
19860
- if (shouldPersist) {
19861
- await self._persistState(symbol, data.strategyName, data.exchangeName, self.params.signalId);
19862
- }
19863
- };
19864
- /**
19865
- * Internal initialization function for ClientPartial.
19866
- *
19867
- * Loads persisted partial state from disk and restores in-memory Maps.
19868
- * Converts serialized arrays back to Sets for O(1) lookups.
19869
- *
19870
- * ONLY runs in LIVE mode (backtest=false). In backtest mode, state is not persisted.
19871
- *
19872
- * @param symbol - Trading pair symbol
19873
- * @param strategyName - Strategy identifier
19874
- * @param exchangeName - Exchange identifier
19875
- * @param backtest - True if backtest mode, false if live mode
19876
- * @param self - ClientPartial instance reference
18504
+ * @param symbol - Trading pair symbol
18505
+ * @param strategyName - Strategy identifier
18506
+ * @param exchangeName - Exchange identifier
18507
+ * @param backtest - True if backtest mode, false if live mode
18508
+ * @param self - ClientPartial instance reference
19877
18509
  */
19878
18510
  const WAIT_FOR_INIT_FN$1 = async (symbol, strategyName, exchangeName, backtest, self) => {
19879
18511
  self.params.logger.debug("ClientPartial waitForInit", {
@@ -22031,172 +20663,16 @@ class BreakevenGlobalService {
22031
20663
  this.clear = async (symbol, data, priceClose, backtest) => {
22032
20664
  this.loggerService.log("breakevenGlobalService clear", {
22033
20665
  symbol,
22034
- data,
22035
- priceClose,
22036
- backtest,
22037
- });
22038
- this.validate({
22039
- strategyName: data.strategyName,
22040
- exchangeName: data.exchangeName,
22041
- frameName: data.frameName
22042
- }, "breakevenGlobalService clear");
22043
- return await this.breakevenConnectionService.clear(symbol, data, priceClose, backtest);
22044
- };
22045
- }
22046
- }
22047
-
22048
- /**
22049
- * Warning threshold for message size in kilobytes.
22050
- * Messages exceeding this size trigger console warnings.
22051
- */
22052
- const WARN_KB = 30;
22053
- /**
22054
- * Internal function for dumping signal data to markdown files.
22055
- * Creates a directory structure with system prompts, user messages, and LLM output.
22056
- *
22057
- * @param signalId - Unique identifier for the result
22058
- * @param history - Array of message models from LLM conversation
22059
- * @param signal - Signal DTO with trade parameters
22060
- * @param outputDir - Output directory path (default: "./dump/strategy")
22061
- * @returns Promise that resolves when all files are written
22062
- */
22063
- const DUMP_SIGNAL_FN = async (signalId, history, signal, outputDir = "./dump/outline") => {
22064
- // Extract system messages and system reminders from existing data
22065
- const systemMessages = history.filter((m) => m.role === "system");
22066
- const userMessages = history.filter((m) => m.role === "user");
22067
- const subfolderPath = path.join(outputDir, String(signalId));
22068
- // Generate system prompt markdown
22069
- {
22070
- let summary = "# Outline Result Summary\n";
22071
- {
22072
- summary += "\n";
22073
- summary += `**ResultId**: ${String(signalId)}\n`;
22074
- summary += "\n";
22075
- }
22076
- if (signal) {
22077
- summary += "## Output Data\n\n";
22078
- summary += "```json\n";
22079
- summary += JSON.stringify(signal, null, 2);
22080
- summary += "\n```\n\n";
22081
- }
22082
- // Add system messages to summary
22083
- if (systemMessages.length > 0) {
22084
- summary += "## System Messages\n\n";
22085
- systemMessages.forEach((msg, idx) => {
22086
- summary += `### System Message ${idx + 1}\n\n`;
22087
- summary += msg.content;
22088
- summary += "\n";
22089
- });
22090
- }
22091
- await Markdown.writeData("outline", summary, {
22092
- path: subfolderPath,
22093
- file: "00_system_prompt.md",
22094
- symbol: "",
22095
- signalId: String(signalId),
22096
- strategyName: "",
22097
- exchangeName: "",
22098
- frameName: ""
22099
- });
22100
- }
22101
- // Generate user messages
22102
- {
22103
- await Promise.all(Array.from(userMessages.entries()).map(async ([idx, message]) => {
22104
- const messageNum = String(idx + 1).padStart(2, "0");
22105
- const contentFileName = `${messageNum}_user_message.md`;
22106
- {
22107
- const messageSizeBytes = Buffer.byteLength(message.content, "utf8");
22108
- const messageSizeKb = Math.floor(messageSizeBytes / 1024);
22109
- if (messageSizeKb > WARN_KB) {
22110
- console.warn(`User message ${idx + 1} is ${messageSizeBytes} bytes (${messageSizeKb}kb), which exceeds warning limit`);
22111
- }
22112
- }
22113
- let content = `# User Input ${idx + 1}\n\n`;
22114
- content += `**ResultId**: ${String(signalId)}\n\n`;
22115
- content += message.content;
22116
- content += "\n";
22117
- await Markdown.writeData("outline", content, {
22118
- path: subfolderPath,
22119
- file: contentFileName,
22120
- signalId: String(signalId),
22121
- symbol: "",
22122
- strategyName: "",
22123
- exchangeName: "",
22124
- frameName: ""
22125
- });
22126
- }));
22127
- }
22128
- // Generate LLM output
22129
- {
22130
- const messageNum = String(userMessages.length + 1).padStart(2, "0");
22131
- const contentFileName = `${messageNum}_llm_output.md`;
22132
- let content = "# Full Outline Result\n\n";
22133
- content += `**ResultId**: ${String(signalId)}\n\n`;
22134
- if (signal) {
22135
- content += "## Output Data\n\n";
22136
- content += "```json\n";
22137
- content += JSON.stringify(signal, null, 2);
22138
- content += "\n```\n";
22139
- }
22140
- await Markdown.writeData("outline", content, {
22141
- path: subfolderPath,
22142
- file: contentFileName,
22143
- symbol: "",
22144
- signalId: String(signalId),
22145
- strategyName: "",
22146
- exchangeName: "",
22147
- frameName: ""
22148
- });
22149
- }
22150
- };
22151
- /**
22152
- * Service for generating markdown documentation from LLM outline results.
22153
- * Used by AI Strategy Optimizer to save debug logs and conversation history.
22154
- *
22155
- * Creates directory structure:
22156
- * - ./dump/strategy/{signalId}/00_system_prompt.md - System messages and output data
22157
- * - ./dump/strategy/{signalId}/01_user_message.md - First user input
22158
- * - ./dump/strategy/{signalId}/02_user_message.md - Second user input
22159
- * - ./dump/strategy/{signalId}/XX_llm_output.md - Final LLM output
22160
- */
22161
- class OutlineMarkdownService {
22162
- constructor() {
22163
- /** Logger service injected via DI */
22164
- this.loggerService = inject(TYPES.loggerService);
22165
- /**
22166
- * Dumps signal data and conversation history to markdown files.
22167
- * Skips if directory already exists to avoid overwriting previous results.
22168
- *
22169
- * Generated files:
22170
- * - 00_system_prompt.md - System messages and output summary
22171
- * - XX_user_message.md - Each user message in separate file (numbered)
22172
- * - XX_llm_output.md - Final LLM output with signal data
22173
- *
22174
- * @param signalId - Unique identifier for the result (used as directory name)
22175
- * @param history - Array of message models from LLM conversation
22176
- * @param signal - Signal DTO with trade parameters (priceOpen, TP, SL, etc.)
22177
- * @param outputDir - Output directory path (default: "./dump/strategy")
22178
- * @returns Promise that resolves when all files are written
22179
- *
22180
- * @example
22181
- * ```typescript
22182
- * await outlineService.dumpSignal(
22183
- * "strategy-1",
22184
- * conversationHistory,
22185
- * { position: "long", priceTakeProfit: 51000, priceStopLoss: 49000, minuteEstimatedTime: 60 }
22186
- * );
22187
- * // Creates: ./dump/strategy/strategy-1/00_system_prompt.md
22188
- * // ./dump/strategy/strategy-1/01_user_message.md
22189
- * // ./dump/strategy/strategy-1/02_llm_output.md
22190
- * ```
22191
- */
22192
- this.dumpSignal = async (signalId, history, signal, outputDir = "./dump/strategy") => {
22193
- this.loggerService.log("outlineMarkdownService dumpSignal", {
22194
- signalId,
22195
- history,
22196
- signal,
22197
- outputDir,
20666
+ data,
20667
+ priceClose,
20668
+ backtest,
22198
20669
  });
22199
- return await DUMP_SIGNAL_FN(signalId, history, signal, outputDir);
20670
+ this.validate({
20671
+ strategyName: data.strategyName,
20672
+ exchangeName: data.exchangeName,
20673
+ frameName: data.frameName
20674
+ }, "breakevenGlobalService clear");
20675
+ return await this.breakevenConnectionService.clear(symbol, data, priceClose, backtest);
22200
20676
  };
22201
20677
  }
22202
20678
  }
@@ -24586,111 +23062,6 @@ class RiskReportService {
24586
23062
  }
24587
23063
  }
24588
23064
 
24589
- const require = createRequire(import.meta.url);
24590
- /**
24591
- * Default fallback prompt configuration.
24592
- * Used when signal.prompt.cjs file is not found.
24593
- */
24594
- const DEFAULT_PROMPT = {
24595
- user: "",
24596
- system: [],
24597
- };
24598
- /**
24599
- * Lazy-loads and caches signal prompt configuration.
24600
- * Attempts to load from config/prompt/signal.prompt.cjs, falls back to DEFAULT_PROMPT if not found.
24601
- * Uses singleshot pattern to ensure configuration is loaded only once.
24602
- * @returns Prompt configuration with system and user prompts
24603
- */
24604
- const GET_PROMPT_FN = singleshot(() => {
24605
- try {
24606
- const modulePath = require.resolve(path.join(process.cwd(), `./config/prompt/signal.prompt.cjs`));
24607
- console.log(`Using ${modulePath} implementation as signal.prompt.cjs`);
24608
- return require(modulePath);
24609
- }
24610
- catch (error) {
24611
- console.log(`Using empty fallback for signal.prompt.cjs`, error);
24612
- return DEFAULT_PROMPT;
24613
- }
24614
- });
24615
- /**
24616
- * Service for managing signal prompts for AI/LLM integrations.
24617
- *
24618
- * Provides access to system and user prompts configured in signal.prompt.cjs.
24619
- * Supports both static prompt arrays and dynamic prompt functions.
24620
- *
24621
- * Key responsibilities:
24622
- * - Lazy-loads prompt configuration from config/prompt/signal.prompt.cjs
24623
- * - Resolves system prompts (static arrays or async functions)
24624
- * - Provides user prompt strings
24625
- * - Falls back to empty prompts if configuration is missing
24626
- *
24627
- * Used for AI-powered signal analysis and strategy recommendations.
24628
- */
24629
- class SignalPromptService {
24630
- constructor() {
24631
- this.loggerService = inject(TYPES.loggerService);
24632
- /**
24633
- * Retrieves system prompts for AI context.
24634
- *
24635
- * System prompts can be:
24636
- * - Static array of strings (returned directly)
24637
- * - Async/sync function returning string array (executed and awaited)
24638
- * - Undefined (returns empty array)
24639
- *
24640
- * @param symbol - Trading symbol (e.g., "BTCUSDT")
24641
- * @param strategyName - Strategy identifier
24642
- * @param exchangeName - Exchange identifier
24643
- * @param frameName - Timeframe identifier
24644
- * @param backtest - Whether running in backtest mode
24645
- * @returns Promise resolving to array of system prompt strings
24646
- */
24647
- this.getSystemPrompt = async (symbol, strategyName, exchangeName, frameName, backtest) => {
24648
- this.loggerService.log("signalPromptService getSystemPrompt", {
24649
- symbol,
24650
- strategyName,
24651
- exchangeName,
24652
- frameName,
24653
- backtest,
24654
- });
24655
- const { system } = GET_PROMPT_FN();
24656
- if (Array.isArray(system)) {
24657
- return system;
24658
- }
24659
- if (typeof system === "function") {
24660
- return await system(symbol, strategyName, exchangeName, frameName, backtest);
24661
- }
24662
- return [];
24663
- };
24664
- /**
24665
- * Retrieves user prompt string for AI input.
24666
- *
24667
- * @param symbol - Trading symbol (e.g., "BTCUSDT")
24668
- * @param strategyName - Strategy identifier
24669
- * @param exchangeName - Exchange identifier
24670
- * @param frameName - Timeframe identifier
24671
- * @param backtest - Whether running in backtest mode
24672
- * @returns Promise resolving to user prompt string
24673
- */
24674
- this.getUserPrompt = async (symbol, strategyName, exchangeName, frameName, backtest) => {
24675
- this.loggerService.log("signalPromptService getUserPrompt", {
24676
- symbol,
24677
- strategyName,
24678
- exchangeName,
24679
- frameName,
24680
- backtest,
24681
- });
24682
- const { user } = GET_PROMPT_FN();
24683
- if (typeof user === "string") {
24684
- return user;
24685
- }
24686
- if (typeof user === "function") {
24687
- return await user(symbol, strategyName, exchangeName, frameName, backtest);
24688
- }
24689
- return "";
24690
- };
24691
- }
24692
- }
24693
-
24694
23065
  {
24695
23066
  provide(TYPES.loggerService, () => new LoggerService());
24696
23067
  }
@@ -24705,7 +23076,6 @@ class SignalPromptService {
24705
23076
  provide(TYPES.sizingConnectionService, () => new SizingConnectionService());
24706
23077
  provide(TYPES.riskConnectionService, () => new RiskConnectionService());
24707
23078
  provide(TYPES.actionConnectionService, () => new ActionConnectionService());
24708
- provide(TYPES.optimizerConnectionService, () => new OptimizerConnectionService());
24709
23079
  provide(TYPES.partialConnectionService, () => new PartialConnectionService());
24710
23080
  provide(TYPES.breakevenConnectionService, () => new BreakevenConnectionService());
24711
23081
  }
@@ -24717,7 +23087,6 @@ class SignalPromptService {
24717
23087
  provide(TYPES.sizingSchemaService, () => new SizingSchemaService());
24718
23088
  provide(TYPES.riskSchemaService, () => new RiskSchemaService());
24719
23089
  provide(TYPES.actionSchemaService, () => new ActionSchemaService());
24720
- provide(TYPES.optimizerSchemaService, () => new OptimizerSchemaService());
24721
23090
  }
24722
23091
  {
24723
23092
  provide(TYPES.exchangeCoreService, () => new ExchangeCoreService());
@@ -24728,7 +23097,6 @@ class SignalPromptService {
24728
23097
  {
24729
23098
  provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
24730
23099
  provide(TYPES.riskGlobalService, () => new RiskGlobalService());
24731
- provide(TYPES.optimizerGlobalService, () => new OptimizerGlobalService());
24732
23100
  provide(TYPES.partialGlobalService, () => new PartialGlobalService());
24733
23101
  provide(TYPES.breakevenGlobalService, () => new BreakevenGlobalService());
24734
23102
  }
@@ -24756,7 +23124,6 @@ class SignalPromptService {
24756
23124
  provide(TYPES.heatMarkdownService, () => new HeatMarkdownService());
24757
23125
  provide(TYPES.partialMarkdownService, () => new PartialMarkdownService());
24758
23126
  provide(TYPES.breakevenMarkdownService, () => new BreakevenMarkdownService());
24759
- provide(TYPES.outlineMarkdownService, () => new OutlineMarkdownService());
24760
23127
  provide(TYPES.riskMarkdownService, () => new RiskMarkdownService());
24761
23128
  }
24762
23129
  {
@@ -24778,16 +23145,9 @@ class SignalPromptService {
24778
23145
  provide(TYPES.sizingValidationService, () => new SizingValidationService());
24779
23146
  provide(TYPES.riskValidationService, () => new RiskValidationService());
24780
23147
  provide(TYPES.actionValidationService, () => new ActionValidationService());
24781
- provide(TYPES.optimizerValidationService, () => new OptimizerValidationService());
24782
23148
  provide(TYPES.configValidationService, () => new ConfigValidationService());
24783
23149
  provide(TYPES.columnValidationService, () => new ColumnValidationService());
24784
23150
  }
24785
- {
24786
- provide(TYPES.optimizerTemplateService, () => new OptimizerTemplateService());
24787
- }
24788
- {
24789
- provide(TYPES.signalPromptService, () => new SignalPromptService());
24790
- }
24791
23151
 
24792
23152
  const baseServices = {
24793
23153
  loggerService: inject(TYPES.loggerService),
@@ -24803,7 +23163,6 @@ const connectionServices = {
24803
23163
  sizingConnectionService: inject(TYPES.sizingConnectionService),
24804
23164
  riskConnectionService: inject(TYPES.riskConnectionService),
24805
23165
  actionConnectionService: inject(TYPES.actionConnectionService),
24806
- optimizerConnectionService: inject(TYPES.optimizerConnectionService),
24807
23166
  partialConnectionService: inject(TYPES.partialConnectionService),
24808
23167
  breakevenConnectionService: inject(TYPES.breakevenConnectionService),
24809
23168
  };
@@ -24815,7 +23174,6 @@ const schemaServices = {
24815
23174
  sizingSchemaService: inject(TYPES.sizingSchemaService),
24816
23175
  riskSchemaService: inject(TYPES.riskSchemaService),
24817
23176
  actionSchemaService: inject(TYPES.actionSchemaService),
24818
- optimizerSchemaService: inject(TYPES.optimizerSchemaService),
24819
23177
  };
24820
23178
  const coreServices = {
24821
23179
  exchangeCoreService: inject(TYPES.exchangeCoreService),
@@ -24826,7 +23184,6 @@ const coreServices = {
24826
23184
  const globalServices = {
24827
23185
  sizingGlobalService: inject(TYPES.sizingGlobalService),
24828
23186
  riskGlobalService: inject(TYPES.riskGlobalService),
24829
- optimizerGlobalService: inject(TYPES.optimizerGlobalService),
24830
23187
  partialGlobalService: inject(TYPES.partialGlobalService),
24831
23188
  breakevenGlobalService: inject(TYPES.breakevenGlobalService),
24832
23189
  };
@@ -24854,7 +23211,6 @@ const markdownServices = {
24854
23211
  heatMarkdownService: inject(TYPES.heatMarkdownService),
24855
23212
  partialMarkdownService: inject(TYPES.partialMarkdownService),
24856
23213
  breakevenMarkdownService: inject(TYPES.breakevenMarkdownService),
24857
- outlineMarkdownService: inject(TYPES.outlineMarkdownService),
24858
23214
  riskMarkdownService: inject(TYPES.riskMarkdownService),
24859
23215
  };
24860
23216
  const reportServices = {
@@ -24876,16 +23232,9 @@ const validationServices = {
24876
23232
  sizingValidationService: inject(TYPES.sizingValidationService),
24877
23233
  riskValidationService: inject(TYPES.riskValidationService),
24878
23234
  actionValidationService: inject(TYPES.actionValidationService),
24879
- optimizerValidationService: inject(TYPES.optimizerValidationService),
24880
23235
  configValidationService: inject(TYPES.configValidationService),
24881
23236
  columnValidationService: inject(TYPES.columnValidationService),
24882
23237
  };
24883
- const templateServices = {
24884
- optimizerTemplateService: inject(TYPES.optimizerTemplateService),
24885
- };
24886
- const promptServices = {
24887
- signalPromptService: inject(TYPES.signalPromptService),
24888
- };
24889
23238
  const backtest = {
24890
23239
  ...baseServices,
24891
23240
  ...contextServices,
@@ -24899,8 +23248,6 @@ const backtest = {
24899
23248
  ...markdownServices,
24900
23249
  ...reportServices,
24901
23250
  ...validationServices,
24902
- ...templateServices,
24903
- ...promptServices,
24904
23251
  };
24905
23252
  init();
24906
23253
  var bt = backtest;
@@ -24999,18 +23346,6 @@ const getSizingMap = async () => {
24999
23346
  }
25000
23347
  return sizingMap;
25001
23348
  };
25002
- /**
25003
- * Retrieves all registered optimizers as a map
25004
- * @private
25005
- * @returns Map of optimizer names
25006
- */
25007
- const getOptimizerMap = async () => {
25008
- const optimizerMap = {};
25009
- for (const { optimizerName } of await bt.optimizerValidationService.list()) {
25010
- Object.assign(optimizerMap, { [optimizerName]: optimizerName });
25011
- }
25012
- return optimizerMap;
25013
- };
25014
23349
  /**
25015
23350
  * Retrieves all registered walkers as a map
25016
23351
  * @private
@@ -25037,7 +23372,7 @@ const getWalkerMap = async () => {
25037
23372
  * @throws {Error} If any entity name is not found in its registry
25038
23373
  */
25039
23374
  const validateInternal = async (args) => {
25040
- 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;
23375
+ const { ExchangeName = await getExchangeMap(), FrameName = await getFrameMap(), StrategyName = await getStrategyMap(), RiskName = await getRiskMap(), ActionName = await getActionMap(), SizingName = await getSizingMap(), WalkerName = await getWalkerMap(), } = args;
25041
23376
  for (const exchangeName of Object.values(ExchangeName)) {
25042
23377
  bt.exchangeValidationService.validate(exchangeName, METHOD_NAME);
25043
23378
  }
@@ -25056,9 +23391,6 @@ const validateInternal = async (args) => {
25056
23391
  for (const sizingName of Object.values(SizingName)) {
25057
23392
  bt.sizingValidationService.validate(sizingName, METHOD_NAME);
25058
23393
  }
25059
- for (const optimizerName of Object.values(OptimizerName)) {
25060
- bt.optimizerValidationService.validate(optimizerName, METHOD_NAME);
25061
- }
25062
23394
  for (const walkerName of Object.values(WalkerName)) {
25063
23395
  bt.walkerValidationService.validate(walkerName, METHOD_NAME);
25064
23396
  }
@@ -25067,7 +23399,7 @@ const validateInternal = async (args) => {
25067
23399
  * Validates the existence of all provided entity names across validation services.
25068
23400
  *
25069
23401
  * This function accepts enum objects for various entity types (exchanges, frames,
25070
- * strategies, risks, sizings, optimizers, walkers) and validates that each entity
23402
+ * strategies, risks, sizings, walkers) and validates that each entity
25071
23403
  * name exists in its respective registry. Validation results are memoized for performance.
25072
23404
  *
25073
23405
  * If no arguments are provided (or specific entity types are omitted), the function
@@ -25127,7 +23459,6 @@ const GET_FRAME_METHOD_NAME = "get.getFrameSchema";
25127
23459
  const GET_WALKER_METHOD_NAME = "get.getWalkerSchema";
25128
23460
  const GET_SIZING_METHOD_NAME = "get.getSizingSchema";
25129
23461
  const GET_RISK_METHOD_NAME = "get.getRiskSchema";
25130
- const GET_OPTIMIZER_METHOD_NAME = "get.getOptimizerSchema";
25131
23462
  const GET_ACTION_METHOD_NAME = "get.getActionSchema";
25132
23463
  /**
25133
23464
  * Retrieves a registered strategy schema by name.
@@ -25259,29 +23590,6 @@ function getRiskSchema(riskName) {
25259
23590
  bt.riskValidationService.validate(riskName, GET_RISK_METHOD_NAME);
25260
23591
  return bt.riskSchemaService.get(riskName);
25261
23592
  }
25262
- /**
25263
- * Retrieves a registered optimizer schema by name.
25264
- *
25265
- * @param optimizerName - Unique optimizer identifier
25266
- * @returns The optimizer schema configuration object
25267
- * @throws Error if optimizer is not registered
25268
- *
25269
- * @example
25270
- * ```typescript
25271
- * const optimizer = getOptimizer("llm-strategy-generator");
25272
- * console.log(optimizer.rangeTrain); // Array of training ranges
25273
- * console.log(optimizer.rangeTest); // Testing range
25274
- * console.log(optimizer.source); // Array of data sources
25275
- * console.log(optimizer.getPrompt); // async function
25276
- * ```
25277
- */
25278
- function getOptimizerSchema(optimizerName) {
25279
- bt.loggerService.log(GET_OPTIMIZER_METHOD_NAME, {
25280
- optimizerName,
25281
- });
25282
- bt.optimizerValidationService.validate(optimizerName, GET_OPTIMIZER_METHOD_NAME);
25283
- return bt.optimizerSchemaService.get(optimizerName);
25284
- }
25285
23593
  /**
25286
23594
  * Retrieves a registered action schema by name.
25287
23595
  *
@@ -26142,7 +24450,6 @@ const ADD_FRAME_METHOD_NAME = "add.addFrameSchema";
26142
24450
  const ADD_WALKER_METHOD_NAME = "add.addWalkerSchema";
26143
24451
  const ADD_SIZING_METHOD_NAME = "add.addSizingSchema";
26144
24452
  const ADD_RISK_METHOD_NAME = "add.addRiskSchema";
26145
- const ADD_OPTIMIZER_METHOD_NAME = "add.addOptimizerSchema";
26146
24453
  const ADD_ACTION_METHOD_NAME = "add.addActionSchema";
26147
24454
  /**
26148
24455
  * Registers a trading strategy in the framework.
@@ -26435,100 +24742,6 @@ function addRiskSchema(riskSchema) {
26435
24742
  bt.riskValidationService.addRisk(riskSchema.riskName, riskSchema);
26436
24743
  bt.riskSchemaService.register(riskSchema.riskName, riskSchema);
26437
24744
  }
26438
- /**
26439
- * Registers an optimizer configuration in the framework.
26440
- *
26441
- * The optimizer generates trading strategies by:
26442
- * - Collecting data from multiple sources across training periods
26443
- * - Building LLM conversation history with fetched data
26444
- * - Generating strategy prompts using getPrompt()
26445
- * - Creating executable backtest code with templates
26446
- *
26447
- * The optimizer produces a complete .mjs file containing:
26448
- * - Exchange, Frame, Strategy, and Walker configurations
26449
- * - Multi-timeframe analysis logic
26450
- * - LLM integration for signal generation
26451
- * - Event listeners for progress tracking
26452
- *
26453
- * @param optimizerSchema - Optimizer configuration object
26454
- * @param optimizerSchema.optimizerName - Unique optimizer identifier
26455
- * @param optimizerSchema.rangeTrain - Array of training time ranges (each generates a strategy variant)
26456
- * @param optimizerSchema.rangeTest - Testing time range for strategy validation
26457
- * @param optimizerSchema.source - Array of data sources (functions or source objects with custom formatters)
26458
- * @param optimizerSchema.getPrompt - Function to generate strategy prompt from conversation history
26459
- * @param optimizerSchema.template - Optional custom template overrides (top banner, helpers, strategy logic, etc.)
26460
- * @param optimizerSchema.callbacks - Optional lifecycle callbacks (onData, onCode, onDump, onSourceData)
26461
- *
26462
- * @example
26463
- * ```typescript
26464
- * // Basic optimizer with single data source
26465
- * addOptimizer({
26466
- * optimizerName: "llm-strategy-generator",
26467
- * rangeTrain: [
26468
- * {
26469
- * note: "Bull market period",
26470
- * startDate: new Date("2024-01-01"),
26471
- * endDate: new Date("2024-01-31"),
26472
- * },
26473
- * {
26474
- * note: "Bear market period",
26475
- * startDate: new Date("2024-02-01"),
26476
- * endDate: new Date("2024-02-28"),
26477
- * },
26478
- * ],
26479
- * rangeTest: {
26480
- * note: "Validation period",
26481
- * startDate: new Date("2024-03-01"),
26482
- * endDate: new Date("2024-03-31"),
26483
- * },
26484
- * source: [
26485
- * {
26486
- * name: "historical-backtests",
26487
- * fetch: async ({ symbol, startDate, endDate, limit, offset }) => {
26488
- * // Fetch historical backtest results from database
26489
- * return await db.backtests.find({
26490
- * symbol,
26491
- * date: { $gte: startDate, $lte: endDate },
26492
- * })
26493
- * .skip(offset)
26494
- * .limit(limit);
26495
- * },
26496
- * user: async (symbol, data, name) => {
26497
- * return `Analyze these ${data.length} backtest results for ${symbol}:\n${JSON.stringify(data)}`;
26498
- * },
26499
- * assistant: async (symbol, data, name) => {
26500
- * return "Historical data analyzed successfully";
26501
- * },
26502
- * },
26503
- * ],
26504
- * getPrompt: async (symbol, messages) => {
26505
- * // Generate strategy prompt from conversation
26506
- * return `"Analyze ${symbol} using RSI and MACD. Enter LONG when RSI < 30 and MACD crosses above signal."`;
26507
- * },
26508
- * callbacks: {
26509
- * onData: (symbol, strategyData) => {
26510
- * console.log(`Generated ${strategyData.length} strategies for ${symbol}`);
26511
- * },
26512
- * onCode: (symbol, code) => {
26513
- * console.log(`Generated ${code.length} characters of code for ${symbol}`);
26514
- * },
26515
- * onDump: (symbol, filepath) => {
26516
- * console.log(`Saved strategy to ${filepath}`);
26517
- * },
26518
- * onSourceData: (symbol, sourceName, data, startDate, endDate) => {
26519
- * console.log(`Fetched ${data.length} rows from ${sourceName} for ${symbol}`);
26520
- * },
26521
- * },
26522
- * });
26523
- * ```
26524
- */
26525
- function addOptimizerSchema(optimizerSchema) {
26526
- bt.loggerService.info(ADD_OPTIMIZER_METHOD_NAME, {
26527
- optimizerSchema,
26528
- });
26529
- bt.optimizerValidationService.addOptimizer(optimizerSchema.optimizerName, optimizerSchema);
26530
- bt.optimizerSchemaService.register(optimizerSchema.optimizerName, optimizerSchema);
26531
- }
26532
24745
  /**
26533
24746
  * Registers an action handler in the framework.
26534
24747
  *
@@ -26611,7 +24824,6 @@ const METHOD_NAME_OVERRIDE_FRAME = "function.override.overrideFrameSchema";
26611
24824
  const METHOD_NAME_OVERRIDE_WALKER = "function.override.overrideWalkerSchema";
26612
24825
  const METHOD_NAME_OVERRIDE_SIZING = "function.override.overrideSizingSchema";
26613
24826
  const METHOD_NAME_OVERRIDE_RISK = "function.override.overrideRiskSchema";
26614
- const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.overrideOptimizerSchema";
26615
24827
  const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideActionSchema";
26616
24828
  /**
26617
24829
  * Overrides an existing trading strategy in the framework.
@@ -26784,40 +24996,6 @@ async function overrideRiskSchema(riskSchema) {
26784
24996
  await bt.riskValidationService.validate(riskSchema.riskName, METHOD_NAME_OVERRIDE_RISK);
26785
24997
  return bt.riskSchemaService.override(riskSchema.riskName, riskSchema);
26786
24998
  }
26787
- /**
26788
- * Overrides an existing optimizer configuration in the framework.
26789
- *
26790
- * This function partially updates a previously registered optimizer with new configuration.
26791
- * Only the provided fields will be updated, other fields remain unchanged.
26792
- *
26793
- * @param optimizerSchema - Partial optimizer configuration object
26794
- * @param optimizerSchema.optimizerName - Unique optimizer identifier (must exist)
26795
- * @param optimizerSchema.rangeTrain - Optional: Array of training time ranges
26796
- * @param optimizerSchema.rangeTest - Optional: Testing time range
26797
- * @param optimizerSchema.source - Optional: Array of data sources
26798
- * @param optimizerSchema.getPrompt - Optional: Function to generate strategy prompt
26799
- * @param optimizerSchema.template - Optional: Custom template overrides
26800
- * @param optimizerSchema.callbacks - Optional: Lifecycle callbacks
26801
- *
26802
- * @example
26803
- * ```typescript
26804
- * overrideOptimizer({
26805
- * optimizerName: "llm-strategy-generator",
26806
- * rangeTest: {
26807
- * note: "Updated validation period",
26808
- * startDate: new Date("2024-04-01"),
26809
- * endDate: new Date("2024-04-30"),
26810
- * },
26811
- * });
26812
- * ```
26813
- */
26814
- async function overrideOptimizerSchema(optimizerSchema) {
26815
- bt.loggerService.log(METHOD_NAME_OVERRIDE_OPTIMIZER, {
26816
- optimizerSchema,
26817
- });
26818
- await bt.optimizerValidationService.validate(optimizerSchema.optimizerName, METHOD_NAME_OVERRIDE_OPTIMIZER);
26819
- return bt.optimizerSchemaService.override(optimizerSchema.optimizerName, optimizerSchema);
26820
- }
26821
24999
  /**
26822
25000
  * Overrides an existing action handler configuration in the framework.
26823
25001
  *
@@ -26892,7 +25070,6 @@ const LIST_FRAMES_METHOD_NAME = "list.listFrameSchema";
26892
25070
  const LIST_WALKERS_METHOD_NAME = "list.listWalkerSchema";
26893
25071
  const LIST_SIZINGS_METHOD_NAME = "list.listSizingSchema";
26894
25072
  const LIST_RISKS_METHOD_NAME = "list.listRiskSchema";
26895
- const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizerSchema";
26896
25073
  /**
26897
25074
  * Returns a list of all registered exchange schemas.
26898
25075
  *
@@ -27090,46 +25267,6 @@ async function listRiskSchema() {
27090
25267
  bt.loggerService.log(LIST_RISKS_METHOD_NAME);
27091
25268
  return await bt.riskValidationService.list();
27092
25269
  }
27093
- /**
27094
- * Returns a list of all registered optimizer schemas.
27095
- *
27096
- * Retrieves all optimizers that have been registered via addOptimizer().
27097
- * Useful for debugging, documentation, or building dynamic UIs.
27098
- *
27099
- * @returns Array of optimizer schemas with their configurations
27100
- *
27101
- * @example
27102
- * ```typescript
27103
- * import { listOptimizers, addOptimizer } from "backtest-kit";
27104
- *
27105
- * addOptimizer({
27106
- * optimizerName: "llm-strategy-generator",
27107
- * note: "Generates trading strategies using LLM",
27108
- * rangeTrain: [
27109
- * {
27110
- * note: "Training period 1",
27111
- * startDate: new Date("2024-01-01"),
27112
- * endDate: new Date("2024-01-31"),
27113
- * },
27114
- * ],
27115
- * rangeTest: {
27116
- * note: "Testing period",
27117
- * startDate: new Date("2024-02-01"),
27118
- * endDate: new Date("2024-02-28"),
27119
- * },
27120
- * source: [],
27121
- * getPrompt: async (symbol, messages) => "Generate strategy",
27122
- * });
27123
- *
27124
- * const optimizers = listOptimizers();
27125
- * console.log(optimizers);
27126
- * // [{ optimizerName: "llm-strategy-generator", note: "Generates...", ... }]
27127
- * ```
27128
- */
27129
- async function listOptimizerSchema() {
27130
- bt.loggerService.log(LIST_OPTIMIZERS_METHOD_NAME);
27131
- return await bt.optimizerValidationService.list();
27132
- }
27133
25270
 
27134
25271
  const LISTEN_SIGNAL_METHOD_NAME = "event.listenSignal";
27135
25272
  const LISTEN_SIGNAL_ONCE_METHOD_NAME = "event.listenSignalOnce";
@@ -27147,7 +25284,6 @@ const LISTEN_DONE_WALKER_METHOD_NAME = "event.listenDoneWalker";
27147
25284
  const LISTEN_DONE_WALKER_ONCE_METHOD_NAME = "event.listenDoneWalkerOnce";
27148
25285
  const LISTEN_PROGRESS_METHOD_NAME = "event.listenBacktestProgress";
27149
25286
  const LISTEN_PROGRESS_WALKER_METHOD_NAME = "event.listenWalkerProgress";
27150
- const LISTEN_PROGRESS_OPTIMIZER_METHOD_NAME = "event.listenOptimizerProgress";
27151
25287
  const LISTEN_PERFORMANCE_METHOD_NAME = "event.listenPerformance";
27152
25288
  const LISTEN_WALKER_METHOD_NAME = "event.listenWalker";
27153
25289
  const LISTEN_WALKER_ONCE_METHOD_NAME = "event.listenWalkerOnce";
@@ -27635,34 +25771,6 @@ function listenWalkerProgress(fn) {
27635
25771
  bt.loggerService.log(LISTEN_PROGRESS_WALKER_METHOD_NAME);
27636
25772
  return progressWalkerEmitter.subscribe(queued(async (event) => fn(event)));
27637
25773
  }
27638
- /**
27639
- * Subscribes to optimizer progress events with queued async processing.
27640
- *
27641
- * Emits during optimizer execution to track data source processing progress.
27642
- * Events are processed sequentially in order received, even if callback is async.
27643
- * Uses queued wrapper to prevent concurrent execution of the callback.
27644
- *
27645
- * @param fn - Callback function to handle optimizer progress events
27646
- * @returns Unsubscribe function to stop listening to events
27647
- *
27648
- * @example
27649
- * ```typescript
27650
- * import { listenOptimizerProgress } from "backtest-kit";
27651
- *
27652
- * const unsubscribe = listenOptimizerProgress((event) => {
27653
- * console.log(`Progress: ${(event.progress * 100).toFixed(2)}%`);
27654
- * console.log(`${event.processedSources} / ${event.totalSources} sources`);
27655
- * console.log(`Optimizer: ${event.optimizerName}, Symbol: ${event.symbol}`);
27656
- * });
27657
- *
27658
- * // Later: stop listening
27659
- * unsubscribe();
27660
- * ```
27661
- */
27662
- function listenOptimizerProgress(fn) {
27663
- bt.loggerService.log(LISTEN_PROGRESS_OPTIMIZER_METHOD_NAME);
27664
- return progressOptimizerEmitter.subscribe(queued(async (event) => fn(event)));
27665
- }
27666
25774
  /**
27667
25775
  * Subscribes to performance metric events with queued async processing.
27668
25776
  *
@@ -28223,138 +26331,6 @@ function listenActivePingOnce(filterFn, fn) {
28223
26331
  return activePingSubject.filter(filterFn).once(fn);
28224
26332
  }
28225
26333
 
28226
- const METHOD_NAME_SIGNAL = "history.commitSignalPromptHistory";
28227
- /**
28228
- * Commits signal prompt history to the message array.
28229
- *
28230
- * Extracts trading context from ExecutionContext and MethodContext,
28231
- * then adds signal-specific system prompts at the beginning and user prompt
28232
- * at the end of the history array if they are not empty.
28233
- *
28234
- * Context extraction:
28235
- * - symbol: Provided as parameter for debugging convenience
28236
- * - backtest mode: From ExecutionContext
28237
- * - strategyName, exchangeName, frameName: From MethodContext
28238
- *
28239
- * @param symbol - Trading symbol (e.g., "BTCUSDT") for debugging convenience
28240
- * @param history - Message array to append prompts to
28241
- * @returns Promise that resolves when prompts are added
28242
- * @throws Error if ExecutionContext or MethodContext is not active
28243
- *
28244
- * @example
28245
- * ```typescript
28246
- * const messages: MessageModel[] = [];
28247
- * await commitSignalPromptHistory("BTCUSDT", messages);
28248
- * // messages now contains system prompts at start and user prompt at end
28249
- * ```
28250
- */
28251
- async function commitSignalPromptHistory(symbol, history) {
28252
- bt.loggerService.log(METHOD_NAME_SIGNAL, {
28253
- symbol,
28254
- });
28255
- if (!ExecutionContextService.hasContext()) {
28256
- throw new Error("commitSignalPromptHistory requires an execution context");
28257
- }
28258
- if (!MethodContextService.hasContext()) {
28259
- throw new Error("commitSignalPromptHistory requires a method context");
28260
- }
28261
- const { backtest: isBacktest } = bt.executionContextService.context;
28262
- const { strategyName, exchangeName, frameName } = bt.methodContextService.context;
28263
- const systemPrompts = await bt.signalPromptService.getSystemPrompt(symbol, strategyName, exchangeName, frameName, isBacktest);
28264
- const userPrompt = await bt.signalPromptService.getUserPrompt(symbol, strategyName, exchangeName, frameName, isBacktest);
28265
- if (systemPrompts.length > 0) {
28266
- for (const content of systemPrompts) {
28267
- history.unshift({
28268
- role: "system",
28269
- content,
28270
- });
28271
- }
28272
- }
28273
- if (userPrompt && userPrompt.trim() !== "") {
28274
- history.push({
28275
- role: "user",
28276
- content: userPrompt,
28277
- });
28278
- }
28279
- }
28280
-
28281
- const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
28282
- /**
28283
- * Dumps signal data and LLM conversation history to markdown files.
28284
- * Used by AI-powered strategies to save debug logs for analysis.
28285
- *
28286
- * Creates a directory structure with:
28287
- * - 00_system_prompt.md - System messages and output summary
28288
- * - XX_user_message.md - Each user message in separate file (numbered)
28289
- * - XX_llm_output.md - Final LLM output with signal data
28290
- *
28291
- * Skips if directory already exists to avoid overwriting previous results.
28292
- *
28293
- * @param signalId - Unique identifier for the result (used as directory name, e.g., UUID)
28294
- * @param history - Array of message models from LLM conversation
28295
- * @param signal - Signal DTO returned by LLM (position, priceOpen, TP, SL, etc.)
28296
- * @param outputDir - Output directory path (default: "./dump/strategy")
28297
- * @returns Promise that resolves when all files are written
28298
- *
28299
- * @example
28300
- * ```typescript
28301
- * import { dumpSignal, getCandles } from "backtest-kit";
28302
- * import { v4 as uuid } from "uuid";
28303
- *
28304
- * addStrategy({
28305
- * strategyName: "llm-strategy",
28306
- * interval: "5m",
28307
- * getSignal: async (symbol) => {
28308
- * const messages = [];
28309
- *
28310
- * // Build multi-timeframe analysis conversation
28311
- * const candles1h = await getCandles(symbol, "1h", 24);
28312
- * messages.push(
28313
- * { role: "user", content: `Analyze 1h trend:\n${formatCandles(candles1h)}` },
28314
- * { role: "assistant", content: "Trend analyzed" }
28315
- * );
28316
- *
28317
- * const candles5m = await getCandles(symbol, "5m", 24);
28318
- * messages.push(
28319
- * { role: "user", content: `Analyze 5m structure:\n${formatCandles(candles5m)}` },
28320
- * { role: "assistant", content: "Structure analyzed" }
28321
- * );
28322
- *
28323
- * // Request signal
28324
- * messages.push({
28325
- * role: "user",
28326
- * content: "Generate trading signal. Use position: 'wait' if uncertain."
28327
- * });
28328
- *
28329
- * const resultId = uuid();
28330
- * const signal = await llmRequest(messages);
28331
- *
28332
- * // Save conversation and result for debugging
28333
- * await dumpSignal(resultId, messages, signal);
28334
- *
28335
- * return signal;
28336
- * }
28337
- * });
28338
- *
28339
- * // Creates: ./dump/strategy/{uuid}/00_system_prompt.md
28340
- * // ./dump/strategy/{uuid}/01_user_message.md (1h analysis)
28341
- * // ./dump/strategy/{uuid}/02_assistant_message.md
28342
- * // ./dump/strategy/{uuid}/03_user_message.md (5m analysis)
28343
- * // ./dump/strategy/{uuid}/04_assistant_message.md
28344
- * // ./dump/strategy/{uuid}/05_user_message.md (signal request)
28345
- * // ./dump/strategy/{uuid}/06_llm_output.md (final signal)
28346
- * ```
28347
- */
28348
- async function dumpSignalData(signalId, history, signal, outputDir = "./dump/strategy") {
28349
- bt.loggerService.info(DUMP_SIGNAL_METHOD_NAME, {
28350
- signalId,
28351
- history,
28352
- signal,
28353
- outputDir,
28354
- });
28355
- return await bt.outlineMarkdownService.dumpSignal(signalId, history, signal, outputDir);
28356
- }
28357
-
28358
26334
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
28359
26335
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
28360
26336
  const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
@@ -31514,104 +29490,6 @@ PositionSizeUtils.atrBased = async (symbol, accountBalance, priceOpen, atr, cont
31514
29490
  };
31515
29491
  const PositionSize = PositionSizeUtils;
31516
29492
 
31517
- const OPTIMIZER_METHOD_NAME_GET_DATA = "OptimizerUtils.getData";
31518
- const OPTIMIZER_METHOD_NAME_GET_CODE = "OptimizerUtils.getCode";
31519
- const OPTIMIZER_METHOD_NAME_DUMP = "OptimizerUtils.dump";
31520
- /**
31521
- * Public API utilities for optimizer operations.
31522
- * Provides high-level methods for strategy generation and code export.
31523
- *
31524
- * Usage:
31525
- * ```typescript
31526
- * import { Optimizer } from "backtest-kit";
31527
- *
31528
- * // Get strategy data
31529
- * const strategies = await Optimizer.getData("BTCUSDT", {
31530
- * optimizerName: "my-optimizer"
31531
- * });
31532
- *
31533
- * // Generate code
31534
- * const code = await Optimizer.getCode("BTCUSDT", {
31535
- * optimizerName: "my-optimizer"
31536
- * });
31537
- *
31538
- * // Save to file
31539
- * await Optimizer.dump("BTCUSDT", {
31540
- * optimizerName: "my-optimizer"
31541
- * }, "./output");
31542
- * ```
31543
- */
31544
- class OptimizerUtils {
31545
- constructor() {
31546
- /**
31547
- * Fetches data from all sources and generates strategy metadata.
31548
- * Processes each training range and builds LLM conversation history.
31549
- *
31550
- * @param symbol - Trading pair symbol
31551
- * @param context - Context with optimizerName
31552
- * @returns Array of generated strategies with conversation context
31553
- * @throws Error if optimizer not found
31554
- */
31555
- this.getData = async (symbol, context) => {
31556
- bt.loggerService.info(OPTIMIZER_METHOD_NAME_GET_DATA, {
31557
- symbol,
31558
- context,
31559
- });
31560
- bt.optimizerValidationService.validate(context.optimizerName, OPTIMIZER_METHOD_NAME_GET_DATA);
31561
- return await bt.optimizerGlobalService.getData(symbol, context.optimizerName);
31562
- };
31563
- /**
31564
- * Generates complete executable strategy code.
31565
- * Includes imports, helpers, strategies, walker, and launcher.
31566
- *
31567
- * @param symbol - Trading pair symbol
31568
- * @param context - Context with optimizerName
31569
- * @returns Generated TypeScript/JavaScript code as string
31570
- * @throws Error if optimizer not found
31571
- */
31572
- this.getCode = async (symbol, context) => {
31573
- bt.loggerService.info(OPTIMIZER_METHOD_NAME_GET_CODE, {
31574
- symbol,
31575
- context,
31576
- });
31577
- bt.optimizerValidationService.validate(context.optimizerName, OPTIMIZER_METHOD_NAME_GET_CODE);
31578
- return await bt.optimizerGlobalService.getCode(symbol, context.optimizerName);
31579
- };
31580
- /**
31581
- * Generates and saves strategy code to file.
31582
- * Creates directory if needed, writes .mjs file.
31583
- *
31584
- * Format: `{optimizerName}_{symbol}.mjs`
31585
- *
31586
- * @param symbol - Trading pair symbol
31587
- * @param context - Context with optimizerName
31588
- * @param path - Output directory path (default: "./")
31589
- * @throws Error if optimizer not found or file write fails
31590
- */
31591
- this.dump = async (symbol, context, path) => {
31592
- bt.loggerService.info(OPTIMIZER_METHOD_NAME_DUMP, {
31593
- symbol,
31594
- context,
31595
- path,
31596
- });
31597
- bt.optimizerValidationService.validate(context.optimizerName, OPTIMIZER_METHOD_NAME_DUMP);
31598
- await bt.optimizerGlobalService.dump(symbol, context.optimizerName, path);
31599
- };
31600
- }
31601
- }
31602
- /**
31603
- * Singleton instance of OptimizerUtils.
31604
- * Public API for optimizer operations.
31605
- *
31606
- * @example
31607
- * ```typescript
31608
- * import { Optimizer } from "backtest-kit";
31609
- *
31610
- * await Optimizer.dump("BTCUSDT", { optimizerName: "my-optimizer" });
31611
- * ```
31612
- */
31613
- const Optimizer = new OptimizerUtils();
31614
-
31615
29493
  const PARTIAL_METHOD_NAME_GET_DATA = "PartialUtils.getData";
31616
29494
  const PARTIAL_METHOD_NAME_GET_REPORT = "PartialUtils.getReport";
31617
29495
  const PARTIAL_METHOD_NAME_DUMP = "PartialUtils.dump";
@@ -33643,4 +31521,4 @@ const set = (object, path, value) => {
33643
31521
  }
33644
31522
  };
33645
31523
 
33646
- export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addOptimizerSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitSignalPromptHistory, commitTrailingStop, commitTrailingTake, dumpSignalData, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getOptimizerSchema, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listOptimizerSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideOptimizerSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate };
31524
+ export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate };