backtest-kit 1.13.4 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createActivator } from 'di-kit';
2
2
  import { scoped } from 'di-scoped';
3
- import { Subject, trycatch, errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, retry, randomString, isObject, ToolRegistry, and, resolveDocuments, str, timeout, TIMEOUT_SYMBOL as TIMEOUT_SYMBOL$1, compose, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
3
+ import { Subject, trycatch, errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, retry, randomString, str, isObject, ToolRegistry, and, resolveDocuments, timeout, TIMEOUT_SYMBOL as TIMEOUT_SYMBOL$1, compose, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
4
4
  import * as fs from 'fs/promises';
5
5
  import fs__default, { mkdir, writeFile } from 'fs/promises';
6
6
  import path, { join, dirname } from 'path';
@@ -521,14 +521,21 @@ const breakevenSubject = new Subject();
521
521
  */
522
522
  const riskSubject = new Subject();
523
523
  /**
524
- * Ping emitter for scheduled signal monitoring events.
524
+ * Schedule ping emitter for scheduled signal monitoring events.
525
525
  * Emits every minute when a scheduled signal is being monitored (waiting for activation).
526
526
  * Allows users to track scheduled signal lifecycle and implement custom cancellation logic.
527
527
  */
528
- const pingSubject = new Subject();
528
+ const schedulePingSubject = new Subject();
529
+ /**
530
+ * Active ping emitter for active pending signal monitoring events.
531
+ * Emits every minute when an active pending signal is being monitored.
532
+ * Allows users to track active signal lifecycle and implement custom dynamic management logic.
533
+ */
534
+ const activePingSubject = new Subject();
529
535
 
530
536
  var emitters = /*#__PURE__*/Object.freeze({
531
537
  __proto__: null,
538
+ activePingSubject: activePingSubject,
532
539
  breakevenSubject: breakevenSubject,
533
540
  doneBacktestSubject: doneBacktestSubject,
534
541
  doneLiveSubject: doneLiveSubject,
@@ -538,11 +545,11 @@ var emitters = /*#__PURE__*/Object.freeze({
538
545
  partialLossSubject: partialLossSubject,
539
546
  partialProfitSubject: partialProfitSubject,
540
547
  performanceEmitter: performanceEmitter,
541
- pingSubject: pingSubject,
542
548
  progressBacktestEmitter: progressBacktestEmitter,
543
549
  progressOptimizerEmitter: progressOptimizerEmitter,
544
550
  progressWalkerEmitter: progressWalkerEmitter,
545
551
  riskSubject: riskSubject,
552
+ schedulePingSubject: schedulePingSubject,
546
553
  signalBacktestEmitter: signalBacktestEmitter,
547
554
  signalEmitter: signalEmitter,
548
555
  signalLiveEmitter: signalLiveEmitter,
@@ -753,11 +760,16 @@ class ClientExchange {
753
760
  const whenTimestamp = this.params.execution.context.when.getTime();
754
761
  const sinceTimestamp = since.getTime();
755
762
  const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= whenTimestamp);
756
- if (filteredData.length < limit) {
757
- this.params.logger.warn(`ClientExchange Expected ${limit} candles, got ${filteredData.length}`);
763
+ // Apply distinct by timestamp to remove duplicates
764
+ const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
765
+ if (filteredData.length !== uniqueData.length) {
766
+ this.params.logger.warn(`ClientExchange Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
758
767
  }
759
- await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, filteredData);
760
- return filteredData;
768
+ if (uniqueData.length < limit) {
769
+ this.params.logger.warn(`ClientExchange Expected ${limit} candles, got ${uniqueData.length}`);
770
+ }
771
+ await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, uniqueData);
772
+ return uniqueData;
761
773
  }
762
774
  /**
763
775
  * Fetches future candles forwards from execution context time.
@@ -806,11 +818,16 @@ class ClientExchange {
806
818
  // Filter candles to strictly match the requested range
807
819
  const sinceTimestamp = since.getTime();
808
820
  const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= endTime);
809
- if (filteredData.length < limit) {
810
- this.params.logger.warn(`ClientExchange getNextCandles: Expected ${limit} candles, got ${filteredData.length}`);
821
+ // Apply distinct by timestamp to remove duplicates
822
+ const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
823
+ if (filteredData.length !== uniqueData.length) {
824
+ this.params.logger.warn(`ClientExchange getNextCandles: Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
825
+ }
826
+ if (uniqueData.length < limit) {
827
+ this.params.logger.warn(`ClientExchange getNextCandles: Expected ${limit} candles, got ${uniqueData.length}`);
811
828
  }
812
- await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, filteredData);
813
- return filteredData;
829
+ await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, uniqueData);
830
+ return uniqueData;
814
831
  }
815
832
  /**
816
833
  * Calculates VWAP (Volume Weighted Average Price) from last N 1m candles.
@@ -3386,14 +3403,14 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
3386
3403
  await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, activationTime, self.params.execution.context.backtest);
3387
3404
  return result;
3388
3405
  };
3389
- const CALL_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, scheduled, timestamp, backtest) => {
3406
+ const CALL_SCHEDULE_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, scheduled, timestamp, backtest) => {
3390
3407
  await ExecutionContextService.runInContext(async () => {
3391
3408
  const publicSignal = TO_PUBLIC_SIGNAL(scheduled);
3392
- // Call system onPing callback first (emits to pingSubject)
3393
- await self.params.onPing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, self.params.execution.context.backtest, timestamp);
3394
- // Call user onPing callback only if signal is still active (not cancelled, not activated)
3395
- if (self.params.callbacks?.onPing) {
3396
- await self.params.callbacks.onPing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
3409
+ // Call system onSchedulePing callback first (emits to pingSubject)
3410
+ await self.params.onSchedulePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, self.params.execution.context.backtest, timestamp);
3411
+ // Call user onSchedulePing callback only if signal is still active (not cancelled, not activated)
3412
+ if (self.params.callbacks?.onSchedulePing) {
3413
+ await self.params.callbacks.onSchedulePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
3397
3414
  }
3398
3415
  }, {
3399
3416
  when: new Date(timestamp),
@@ -3402,7 +3419,33 @@ const CALL_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, scheduled
3402
3419
  });
3403
3420
  }), {
3404
3421
  fallback: (error) => {
3405
- const message = "ClientStrategy CALL_PING_CALLBACKS_FN thrown";
3422
+ const message = "ClientStrategy CALL_SCHEDULE_PING_CALLBACKS_FN thrown";
3423
+ const payload = {
3424
+ error: errorData(error),
3425
+ message: getErrorMessage(error),
3426
+ };
3427
+ backtest$1.loggerService.warn(message, payload);
3428
+ console.warn(message, payload);
3429
+ errorEmitter.next(error);
3430
+ },
3431
+ });
3432
+ const CALL_ACTIVE_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, pending, timestamp, backtest) => {
3433
+ await ExecutionContextService.runInContext(async () => {
3434
+ const publicSignal = TO_PUBLIC_SIGNAL(pending);
3435
+ // Call system onActivePing callback first (emits to activePingSubject)
3436
+ await self.params.onActivePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, self.params.execution.context.backtest, timestamp);
3437
+ // Call user onActivePing callback only if signal is still active (not closed)
3438
+ if (self.params.callbacks?.onActivePing) {
3439
+ await self.params.callbacks.onActivePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
3440
+ }
3441
+ }, {
3442
+ when: new Date(timestamp),
3443
+ symbol: symbol,
3444
+ backtest: backtest,
3445
+ });
3446
+ }), {
3447
+ fallback: (error) => {
3448
+ const message = "ClientStrategy CALL_ACTIVE_PING_CALLBACKS_FN thrown";
3406
3449
  const payload = {
3407
3450
  error: errorData(error),
3408
3451
  message: getErrorMessage(error),
@@ -3774,10 +3817,10 @@ const CALL_BREAKEVEN_CLEAR_FN = trycatch(beginTime(async (self, symbol, signal,
3774
3817
  });
3775
3818
  const RETURN_SCHEDULED_SIGNAL_ACTIVE_FN = async (self, scheduled, currentPrice) => {
3776
3819
  const currentTime = self.params.execution.context.when.getTime();
3777
- await CALL_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, currentTime, self.params.execution.context.backtest);
3820
+ await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, currentTime, self.params.execution.context.backtest);
3778
3821
  const pnl = toProfitLossDto(scheduled, currentPrice);
3779
3822
  const result = {
3780
- action: "active",
3823
+ action: "waiting",
3781
3824
  signal: TO_PUBLIC_SIGNAL(scheduled),
3782
3825
  currentPrice: currentPrice,
3783
3826
  strategyName: self.params.method.context.strategyName,
@@ -3904,6 +3947,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
3904
3947
  let percentTp = 0;
3905
3948
  let percentSl = 0;
3906
3949
  const currentTime = self.params.execution.context.when.getTime();
3950
+ await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentTime, self.params.execution.context.backtest);
3907
3951
  // Calculate percentage of path to TP/SL for partial fill/loss callbacks
3908
3952
  {
3909
3953
  if (signal.position === "long") {
@@ -4109,7 +4153,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
4109
4153
  const averagePrice = GET_AVG_PRICE_FN(recentCandles);
4110
4154
  // КРИТИЧНО: Проверяем был ли сигнал отменен пользователем через cancel()
4111
4155
  if (self._cancelledSignal) {
4112
- // Сигнал был отменен через cancel() в onPing
4156
+ // Сигнал был отменен через cancel() в onSchedulePing
4113
4157
  const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user");
4114
4158
  return { activated: false, cancelled: true, activationIndex: i, result };
4115
4159
  }
@@ -4166,7 +4210,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
4166
4210
  result: null,
4167
4211
  };
4168
4212
  }
4169
- await CALL_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true);
4213
+ await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true);
4170
4214
  }
4171
4215
  return {
4172
4216
  activated: false,
@@ -4192,6 +4236,7 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
4192
4236
  const startIndex = Math.max(0, i - (candlesCount - 1));
4193
4237
  const recentCandles = candles.slice(startIndex, i + 1);
4194
4238
  const averagePrice = GET_AVG_PRICE_FN(recentCandles);
4239
+ await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentCandleTimestamp, true);
4195
4240
  let shouldClose = false;
4196
4241
  let closeReason;
4197
4242
  // Check time expiration FIRST (КРИТИЧНО!)
@@ -4737,7 +4782,15 @@ class ClientStrategy {
4737
4782
  return result;
4738
4783
  }
4739
4784
  if (activated) {
4740
- const remainingCandles = candles.slice(activationIndex + 1);
4785
+ // КРИТИЧНО: activationIndex - индекс свечи активации в массиве candles
4786
+ // BacktestLogicPrivateService включил буфер в начало массива, поэтому перед activationIndex достаточно свечей
4787
+ // PROCESS_PENDING_SIGNAL_CANDLES_FN пропустит первые bufferCandlesCount свечей для VWAP
4788
+ // Чтобы обработка началась со СЛЕДУЮЩЕЙ свечи после активации (activationIndex + 1),
4789
+ // нужно взять срез начиная с (activationIndex + 1 - bufferCandlesCount)
4790
+ // Это даст буфер ИЗ scheduled фазы + свеча после активации как первая обрабатываемая
4791
+ const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
4792
+ const sliceStart = Math.max(0, activationIndex + 1 - bufferCandlesCount);
4793
+ const remainingCandles = candles.slice(sliceStart);
4741
4794
  if (remainingCandles.length === 0) {
4742
4795
  const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
4743
4796
  const recentCandles = candles.slice(Math.max(0, activationIndex - (candlesCount - 1)), activationIndex + 1);
@@ -4753,35 +4806,23 @@ class ClientStrategy {
4753
4806
  const lastCandleTimestamp = candles[candles.length - 1].timestamp;
4754
4807
  const elapsedTime = lastCandleTimestamp - scheduled.scheduledAt;
4755
4808
  if (elapsedTime < maxTimeToWait) {
4756
- // Timeout NOT reached yet - signal is still active (waiting for price)
4757
- // Return active result to continue monitoring in next backtest() call
4758
- const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
4759
- const lastCandles = candles.slice(-candlesCount);
4760
- const lastPrice = GET_AVG_PRICE_FN(lastCandles);
4761
- this.params.logger.debug("ClientStrategy backtest scheduled signal still waiting (not expired)", {
4762
- symbol: this.params.execution.context.symbol,
4763
- signalId: scheduled.id,
4764
- elapsedMinutes: Math.floor(elapsedTime / 60000),
4765
- maxMinutes: GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES,
4766
- });
4767
- // Don't cancel - just return last active state
4768
- // In real backtest flow this won't happen as we process all candles at once,
4769
- // but this is correct behavior if someone calls backtest() with partial data
4770
- const pnl = toProfitLossDto(scheduled, lastPrice);
4771
- const result = {
4772
- action: "active",
4773
- signal: TO_PUBLIC_SIGNAL(scheduled),
4774
- currentPrice: lastPrice,
4775
- percentSl: 0,
4776
- percentTp: 0,
4777
- pnl,
4778
- strategyName: this.params.method.context.strategyName,
4779
- exchangeName: this.params.method.context.exchangeName,
4780
- frameName: this.params.method.context.frameName,
4781
- symbol: this.params.execution.context.symbol,
4782
- backtest: this.params.execution.context.backtest,
4783
- };
4784
- return result; // Cast to IStrategyBacktestResult (which includes Active)
4809
+ // EDGE CASE: backtest() called with partial candle data (should never happen in production)
4810
+ // In real backtest flow this won't happen as we process all candles at once
4811
+ // This indicates incorrect usage of backtest() - throw error instead of returning partial result
4812
+ const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
4813
+ // For scheduled signal that has NOT activated: buffer + wait time only (no lifetime yet)
4814
+ const requiredCandlesCount = bufferCandlesCount + GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES + 1;
4815
+ throw new Error(str.newline(`ClientStrategy backtest: Insufficient candle data for scheduled signal (not yet activated). ` +
4816
+ `Signal scheduled at ${new Date(scheduled.scheduledAt).toISOString()}, ` +
4817
+ `last candle at ${new Date(lastCandleTimestamp).toISOString()}. ` +
4818
+ `Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}min wait time. ` +
4819
+ `Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
4820
+ `\nBreakdown: ` +
4821
+ `${bufferCandlesCount} buffer (VWAP) + ` +
4822
+ `${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES} wait (for activation) = ${requiredCandlesCount} total. ` +
4823
+ `\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
4824
+ `so first ${bufferCandlesCount} candles are skipped during scheduled phase processing. ` +
4825
+ `Provide complete candle range: [scheduledAt - ${bufferCandlesCount}min, scheduledAt + ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}min].`));
4785
4826
  }
4786
4827
  // Timeout reached - cancel the scheduled signal
4787
4828
  const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
@@ -4811,9 +4852,29 @@ class ClientStrategy {
4811
4852
  if (closedResult) {
4812
4853
  return closedResult;
4813
4854
  }
4855
+ // Signal didn't close during candle processing - check if we have enough data
4814
4856
  const lastCandles = candles.slice(-GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT);
4815
4857
  const lastPrice = GET_AVG_PRICE_FN(lastCandles);
4816
4858
  const closeTimestamp = lastCandles[lastCandles.length - 1].timestamp;
4859
+ const signalTime = signal.pendingAt;
4860
+ const maxTimeToWait = signal.minuteEstimatedTime * 60 * 1000;
4861
+ const elapsedTime = closeTimestamp - signalTime;
4862
+ // Check if we actually reached time expiration or just ran out of candles
4863
+ if (elapsedTime < maxTimeToWait) {
4864
+ // EDGE CASE: backtest() called with insufficient candle data
4865
+ const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
4866
+ const requiredCandlesCount = signal.minuteEstimatedTime + bufferCandlesCount + 1;
4867
+ throw new Error(str.newline(`ClientStrategy backtest: Insufficient candle data for pending signal. ` +
4868
+ `Signal opened at ${new Date(signal.pendingAt).toISOString()}, ` +
4869
+ `last candle at ${new Date(closeTimestamp).toISOString()}. ` +
4870
+ `Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${signal.minuteEstimatedTime}min required. ` +
4871
+ `Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
4872
+ `\nBreakdown: ${signal.minuteEstimatedTime} candles for signal lifetime + ${bufferCandlesCount} buffer candles. ` +
4873
+ `\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
4874
+ `so first ${bufferCandlesCount} candles are skipped to ensure accurate price averaging. ` +
4875
+ `Provide complete candle range: [pendingAt - ${bufferCandlesCount}min, pendingAt + ${signal.minuteEstimatedTime}min].`));
4876
+ }
4877
+ // Time actually expired - close with time_expired
4817
4878
  return await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(this, signal, lastPrice, "time_expired", closeTimestamp);
4818
4879
  }
4819
4880
  /**
@@ -5602,6 +5663,9 @@ class ClientStrategy {
5602
5663
  const RISK_METHOD_NAME_GET_DATA = "RiskUtils.getData";
5603
5664
  const RISK_METHOD_NAME_GET_REPORT = "RiskUtils.getReport";
5604
5665
  const RISK_METHOD_NAME_DUMP = "RiskUtils.dump";
5666
+ const RISK_METHOD_NAME_CHECK_SIGNAL = "MergeRisk.checkSignal";
5667
+ const RISK_METHOD_NAME_ADD_SIGNAL = "MergeRisk.addSignal";
5668
+ const RISK_METHOD_NAME_REMOVE_SIGNAL = "MergeRisk.removeSignal";
5605
5669
  /**
5606
5670
  * Composite risk management class that combines multiple risk profiles.
5607
5671
  *
@@ -5657,7 +5721,7 @@ class MergeRisk {
5657
5721
  * @returns Promise resolving to true if all risks approve, false if any risk rejects
5658
5722
  */
5659
5723
  async checkSignal(params) {
5660
- backtest$1.loggerService.info("MergeRisk checkSignal", {
5724
+ backtest$1.loggerService.info(RISK_METHOD_NAME_CHECK_SIGNAL, {
5661
5725
  params,
5662
5726
  });
5663
5727
  for (const [riskName, risk] of Object.entries(this._riskMap)) {
@@ -5681,7 +5745,7 @@ class MergeRisk {
5681
5745
  * @returns Promise that resolves when all risks have registered the signal
5682
5746
  */
5683
5747
  async addSignal(symbol, context, positionData) {
5684
- backtest$1.loggerService.info("MergeRisk addSignal", {
5748
+ backtest$1.loggerService.info(RISK_METHOD_NAME_ADD_SIGNAL, {
5685
5749
  symbol,
5686
5750
  context,
5687
5751
  });
@@ -5698,7 +5762,7 @@ class MergeRisk {
5698
5762
  * @returns Promise that resolves when all risks have removed the signal
5699
5763
  */
5700
5764
  async removeSignal(symbol, context) {
5701
- backtest$1.loggerService.info("MergeRisk removeSignal", {
5765
+ backtest$1.loggerService.info(RISK_METHOD_NAME_REMOVE_SIGNAL, {
5702
5766
  symbol,
5703
5767
  context,
5704
5768
  });
@@ -5981,15 +6045,15 @@ const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest
5981
6045
  return parts.join(":");
5982
6046
  };
5983
6047
  /**
5984
- * Creates a callback function for emitting ping events to pingSubject.
6048
+ * Creates a callback function for emitting schedule ping events to pingSubject.
5985
6049
  *
5986
6050
  * Called by ClientStrategy when a scheduled signal is being monitored every minute.
5987
6051
  * Emits PingContract event to all subscribers and calls ActionCoreService.
5988
6052
  *
5989
6053
  * @param self - Reference to StrategyConnectionService instance
5990
- * @returns Callback function for ping events
6054
+ * @returns Callback function for schedule ping events
5991
6055
  */
5992
- const CREATE_COMMIT_PING_FN = (self) => async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
6056
+ const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
5993
6057
  const event = {
5994
6058
  symbol,
5995
6059
  strategyName,
@@ -5998,8 +6062,29 @@ const CREATE_COMMIT_PING_FN = (self) => async (symbol, strategyName, exchangeNam
5998
6062
  backtest,
5999
6063
  timestamp,
6000
6064
  };
6001
- await pingSubject.next(event);
6002
- await self.actionCoreService.ping(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
6065
+ await schedulePingSubject.next(event);
6066
+ await self.actionCoreService.pingScheduled(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
6067
+ };
6068
+ /**
6069
+ * Creates a callback function for emitting active ping events.
6070
+ *
6071
+ * Called by ClientStrategy when an active pending signal is being monitored every minute.
6072
+ * Placeholder for future activePingSubject implementation.
6073
+ *
6074
+ * @param self - Reference to StrategyConnectionService instance
6075
+ * @returns Callback function for active ping events
6076
+ */
6077
+ const CREATE_COMMIT_ACTIVE_PING_FN = (self) => async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
6078
+ const event = {
6079
+ symbol,
6080
+ strategyName,
6081
+ exchangeName,
6082
+ data,
6083
+ backtest,
6084
+ timestamp,
6085
+ };
6086
+ await activePingSubject.next(event);
6087
+ await self.actionCoreService.pingActive(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
6003
6088
  };
6004
6089
  /**
6005
6090
  * Creates a callback function for emitting init events.
@@ -6096,7 +6181,8 @@ class StrategyConnectionService {
6096
6181
  getSignal,
6097
6182
  callbacks,
6098
6183
  onInit: CREATE_COMMIT_INIT_FN(this),
6099
- onPing: CREATE_COMMIT_PING_FN(this),
6184
+ onSchedulePing: CREATE_COMMIT_SCHEDULE_PING_FN(this),
6185
+ onActivePing: CREATE_COMMIT_ACTIVE_PING_FN(this),
6100
6186
  onDispose: CREATE_COMMIT_DISPOSE_FN(this),
6101
6187
  });
6102
6188
  });
@@ -7489,8 +7575,8 @@ const CALL_SIGNAL_BACKTEST_CALLBACK_FN = trycatch(async (self, event, strategyNa
7489
7575
  });
7490
7576
  /** Wrapper to call breakeven callback with error handling */
7491
7577
  const CALL_BREAKEVEN_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
7492
- if (self.params.callbacks?.onBreakeven) {
7493
- await self.params.callbacks.onBreakeven(event, self.params.actionName, strategyName, frameName, backtest);
7578
+ if (self.params.callbacks?.onBreakevenAvailable) {
7579
+ await self.params.callbacks.onBreakevenAvailable(event, self.params.actionName, strategyName, frameName, backtest);
7494
7580
  }
7495
7581
  }, {
7496
7582
  fallback: (error) => {
@@ -7506,8 +7592,8 @@ const CALL_BREAKEVEN_CALLBACK_FN = trycatch(async (self, event, strategyName, fr
7506
7592
  });
7507
7593
  /** Wrapper to call partialProfit callback with error handling */
7508
7594
  const CALL_PARTIAL_PROFIT_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
7509
- if (self.params.callbacks?.onPartialProfit) {
7510
- await self.params.callbacks.onPartialProfit(event, self.params.actionName, strategyName, frameName, backtest);
7595
+ if (self.params.callbacks?.onPartialProfitAvailable) {
7596
+ await self.params.callbacks.onPartialProfitAvailable(event, self.params.actionName, strategyName, frameName, backtest);
7511
7597
  }
7512
7598
  }, {
7513
7599
  fallback: (error) => {
@@ -7523,8 +7609,8 @@ const CALL_PARTIAL_PROFIT_CALLBACK_FN = trycatch(async (self, event, strategyNam
7523
7609
  });
7524
7610
  /** Wrapper to call partialLoss callback with error handling */
7525
7611
  const CALL_PARTIAL_LOSS_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
7526
- if (self.params.callbacks?.onPartialLoss) {
7527
- await self.params.callbacks.onPartialLoss(event, self.params.actionName, strategyName, frameName, backtest);
7612
+ if (self.params.callbacks?.onPartialLossAvailable) {
7613
+ await self.params.callbacks.onPartialLossAvailable(event, self.params.actionName, strategyName, frameName, backtest);
7528
7614
  }
7529
7615
  }, {
7530
7616
  fallback: (error) => {
@@ -7538,14 +7624,31 @@ const CALL_PARTIAL_LOSS_CALLBACK_FN = trycatch(async (self, event, strategyName,
7538
7624
  errorEmitter.next(error);
7539
7625
  },
7540
7626
  });
7541
- /** Wrapper to call ping callback with error handling */
7542
- const CALL_PING_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
7543
- if (self.params.callbacks?.onPing) {
7544
- await self.params.callbacks.onPing(event, self.params.actionName, strategyName, frameName, backtest);
7627
+ /** Wrapper to call scheduled ping callback with error handling */
7628
+ const CALL_PING_SCHEDULED_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
7629
+ if (self.params.callbacks?.onPingScheduled) {
7630
+ await self.params.callbacks.onPingScheduled(event, self.params.actionName, strategyName, frameName, backtest);
7545
7631
  }
7546
7632
  }, {
7547
7633
  fallback: (error) => {
7548
- const message = "ClientAction CALL_PING_CALLBACK_FN thrown";
7634
+ const message = "ClientAction CALL_PING_SCHEDULED_CALLBACK_FN thrown";
7635
+ const payload = {
7636
+ error: errorData(error),
7637
+ message: getErrorMessage(error),
7638
+ };
7639
+ backtest$1.loggerService.warn(message, payload);
7640
+ console.warn(message, payload);
7641
+ errorEmitter.next(error);
7642
+ },
7643
+ });
7644
+ /** Wrapper to call active ping callback with error handling */
7645
+ const CALL_PING_ACTIVE_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
7646
+ if (self.params.callbacks?.onPingActive) {
7647
+ await self.params.callbacks.onPingActive(event, self.params.actionName, strategyName, frameName, backtest);
7648
+ }
7649
+ }, {
7650
+ fallback: (error) => {
7651
+ const message = "ClientAction CALL_PING_ACTIVE_CALLBACK_FN thrown";
7549
7652
  const payload = {
7550
7653
  error: errorData(error),
7551
7654
  message: getErrorMessage(error),
@@ -7620,6 +7723,7 @@ const CREATE_HANDLER_FN = (self) => {
7620
7723
  self.params.strategyName,
7621
7724
  self.params.frameName,
7622
7725
  self.params.actionName,
7726
+ self.params.backtest,
7623
7727
  ]);
7624
7728
  }
7625
7729
  return self.params.handler;
@@ -7811,8 +7915,8 @@ class ClientAction {
7811
7915
  /**
7812
7916
  * Handles breakeven events when stop-loss is moved to entry price.
7813
7917
  */
7814
- async breakeven(event) {
7815
- this.params.logger.debug("ClientAction breakeven", {
7918
+ async breakevenAvailable(event) {
7919
+ this.params.logger.debug("ClientAction breakevenAvailable", {
7816
7920
  actionName: this.params.actionName,
7817
7921
  strategyName: this.params.strategyName,
7818
7922
  frameName: this.params.frameName,
@@ -7821,8 +7925,8 @@ class ClientAction {
7821
7925
  await this.waitForInit();
7822
7926
  }
7823
7927
  // Call handler method if defined
7824
- if (this._handlerInstance?.breakeven) {
7825
- await this._handlerInstance.breakeven(event);
7928
+ if (this._handlerInstance?.breakevenAvailable) {
7929
+ await this._handlerInstance.breakevenAvailable(event);
7826
7930
  }
7827
7931
  // Call callback if defined
7828
7932
  await CALL_BREAKEVEN_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
@@ -7831,8 +7935,8 @@ class ClientAction {
7831
7935
  /**
7832
7936
  * Handles partial profit level events (10%, 20%, 30%, etc).
7833
7937
  */
7834
- async partialProfit(event) {
7835
- this.params.logger.debug("ClientAction partialProfit", {
7938
+ async partialProfitAvailable(event) {
7939
+ this.params.logger.debug("ClientAction partialProfitAvailable", {
7836
7940
  actionName: this.params.actionName,
7837
7941
  strategyName: this.params.strategyName,
7838
7942
  frameName: this.params.frameName,
@@ -7841,8 +7945,8 @@ class ClientAction {
7841
7945
  await this.waitForInit();
7842
7946
  }
7843
7947
  // Call handler method if defined
7844
- if (this._handlerInstance?.partialProfit) {
7845
- await this._handlerInstance.partialProfit(event);
7948
+ if (this._handlerInstance?.partialProfitAvailable) {
7949
+ await this._handlerInstance.partialProfitAvailable(event);
7846
7950
  }
7847
7951
  // Call callback if defined
7848
7952
  await CALL_PARTIAL_PROFIT_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
@@ -7851,8 +7955,8 @@ class ClientAction {
7851
7955
  /**
7852
7956
  * Handles partial loss level events (-10%, -20%, -30%, etc).
7853
7957
  */
7854
- async partialLoss(event) {
7855
- this.params.logger.debug("ClientAction partialLoss", {
7958
+ async partialLossAvailable(event) {
7959
+ this.params.logger.debug("ClientAction partialLossAvailable", {
7856
7960
  actionName: this.params.actionName,
7857
7961
  strategyName: this.params.strategyName,
7858
7962
  frameName: this.params.frameName,
@@ -7861,18 +7965,18 @@ class ClientAction {
7861
7965
  await this.waitForInit();
7862
7966
  }
7863
7967
  // Call handler method if defined
7864
- if (this._handlerInstance?.partialLoss) {
7865
- await this._handlerInstance.partialLoss(event);
7968
+ if (this._handlerInstance?.partialLossAvailable) {
7969
+ await this._handlerInstance.partialLossAvailable(event);
7866
7970
  }
7867
7971
  // Call callback if defined
7868
7972
  await CALL_PARTIAL_LOSS_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
7869
7973
  }
7870
7974
  ;
7871
7975
  /**
7872
- * Handles ping events during scheduled signal monitoring.
7976
+ * Handles scheduled ping events during scheduled signal monitoring.
7873
7977
  */
7874
- async ping(event) {
7875
- this.params.logger.debug("ClientAction ping", {
7978
+ async pingScheduled(event) {
7979
+ this.params.logger.debug("ClientAction pingScheduled", {
7876
7980
  actionName: this.params.actionName,
7877
7981
  strategyName: this.params.strategyName,
7878
7982
  frameName: this.params.frameName,
@@ -7881,11 +7985,31 @@ class ClientAction {
7881
7985
  await this.waitForInit();
7882
7986
  }
7883
7987
  // Call handler method if defined
7884
- if (this._handlerInstance?.ping) {
7885
- await this._handlerInstance.ping(event);
7988
+ if (this._handlerInstance?.pingScheduled) {
7989
+ await this._handlerInstance.pingScheduled(event);
7886
7990
  }
7887
7991
  // Call callback if defined
7888
- await CALL_PING_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
7992
+ await CALL_PING_SCHEDULED_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
7993
+ }
7994
+ ;
7995
+ /**
7996
+ * Handles active ping events during active pending signal monitoring.
7997
+ */
7998
+ async pingActive(event) {
7999
+ this.params.logger.debug("ClientAction pingActive", {
8000
+ actionName: this.params.actionName,
8001
+ strategyName: this.params.strategyName,
8002
+ frameName: this.params.frameName,
8003
+ });
8004
+ if (!this._handlerInstance) {
8005
+ await this.waitForInit();
8006
+ }
8007
+ // Call handler method if defined
8008
+ if (this._handlerInstance?.pingActive) {
8009
+ await this._handlerInstance.pingActive(event);
8010
+ }
8011
+ // Call callback if defined
8012
+ await CALL_PING_ACTIVE_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
7889
8013
  }
7890
8014
  ;
7891
8015
  /**
@@ -8053,13 +8177,13 @@ class ActionConnectionService {
8053
8177
  * @param backtest - Whether running in backtest mode
8054
8178
  * @param context - Execution context with action name, strategy name, exchange name, frame name
8055
8179
  */
8056
- this.breakeven = async (event, backtest, context) => {
8057
- this.loggerService.log("actionConnectionService breakeven", {
8180
+ this.breakevenAvailable = async (event, backtest, context) => {
8181
+ this.loggerService.log("actionConnectionService breakevenAvailable", {
8058
8182
  backtest,
8059
8183
  context,
8060
8184
  });
8061
8185
  const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
8062
- await action.breakeven(event);
8186
+ await action.breakevenAvailable(event);
8063
8187
  };
8064
8188
  /**
8065
8189
  * Routes partialProfit event to appropriate ClientAction instance.
@@ -8068,13 +8192,13 @@ class ActionConnectionService {
8068
8192
  * @param backtest - Whether running in backtest mode
8069
8193
  * @param context - Execution context with action name, strategy name, exchange name, frame name
8070
8194
  */
8071
- this.partialProfit = async (event, backtest, context) => {
8072
- this.loggerService.log("actionConnectionService partialProfit", {
8195
+ this.partialProfitAvailable = async (event, backtest, context) => {
8196
+ this.loggerService.log("actionConnectionService partialProfitAvailable", {
8073
8197
  backtest,
8074
8198
  context,
8075
8199
  });
8076
8200
  const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
8077
- await action.partialProfit(event);
8201
+ await action.partialProfitAvailable(event);
8078
8202
  };
8079
8203
  /**
8080
8204
  * Routes partialLoss event to appropriate ClientAction instance.
@@ -8083,28 +8207,43 @@ class ActionConnectionService {
8083
8207
  * @param backtest - Whether running in backtest mode
8084
8208
  * @param context - Execution context with action name, strategy name, exchange name, frame name
8085
8209
  */
8086
- this.partialLoss = async (event, backtest, context) => {
8087
- this.loggerService.log("actionConnectionService partialLoss", {
8210
+ this.partialLossAvailable = async (event, backtest, context) => {
8211
+ this.loggerService.log("actionConnectionService partialLossAvailable", {
8088
8212
  backtest,
8089
8213
  context,
8090
8214
  });
8091
8215
  const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
8092
- await action.partialLoss(event);
8216
+ await action.partialLossAvailable(event);
8093
8217
  };
8094
8218
  /**
8095
- * Routes ping event to appropriate ClientAction instance.
8219
+ * Routes scheduled ping event to appropriate ClientAction instance.
8096
8220
  *
8097
- * @param event - Ping event data
8221
+ * @param event - Scheduled ping event data
8098
8222
  * @param backtest - Whether running in backtest mode
8099
8223
  * @param context - Execution context with action name, strategy name, exchange name, frame name
8100
8224
  */
8101
- this.ping = async (event, backtest, context) => {
8102
- this.loggerService.log("actionConnectionService ping", {
8225
+ this.pingScheduled = async (event, backtest, context) => {
8226
+ this.loggerService.log("actionConnectionService pingScheduled", {
8103
8227
  backtest,
8104
8228
  context,
8105
8229
  });
8106
8230
  const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
8107
- await action.ping(event);
8231
+ await action.pingScheduled(event);
8232
+ };
8233
+ /**
8234
+ * Routes active ping event to appropriate ClientAction instance.
8235
+ *
8236
+ * @param event - Active ping event data
8237
+ * @param backtest - Whether running in backtest mode
8238
+ * @param context - Execution context with action name, strategy name, exchange name, frame name
8239
+ */
8240
+ this.pingActive = async (event, backtest, context) => {
8241
+ this.loggerService.log("actionConnectionService pingActive", {
8242
+ backtest,
8243
+ context,
8244
+ });
8245
+ const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
8246
+ await action.pingActive(event);
8108
8247
  };
8109
8248
  /**
8110
8249
  * Routes riskRejection event to appropriate ClientAction instance.
@@ -9168,81 +9307,102 @@ class ActionCoreService {
9168
9307
  * Routes breakeven event to all registered actions for the strategy.
9169
9308
  *
9170
9309
  * Retrieves action list from strategy schema (IStrategySchema.actions)
9171
- * and invokes the breakeven handler on each ClientAction instance sequentially.
9310
+ * and invokes the breakevenAvailable handler on each ClientAction instance sequentially.
9172
9311
  *
9173
9312
  * @param backtest - Whether running in backtest mode (true) or live mode (false)
9174
9313
  * @param event - Breakeven milestone data (stop-loss moved to entry price)
9175
9314
  * @param context - Strategy execution context with strategyName, exchangeName, frameName
9176
9315
  */
9177
- this.breakeven = async (backtest, event, context) => {
9178
- this.loggerService.log("actionCoreService breakeven", {
9316
+ this.breakevenAvailable = async (backtest, event, context) => {
9317
+ this.loggerService.log("actionCoreService breakevenAvailable", {
9179
9318
  context,
9180
9319
  });
9181
9320
  await this.validate(context);
9182
9321
  const { actions = [] } = this.strategySchemaService.get(context.strategyName);
9183
9322
  for (const actionName of actions) {
9184
- await this.actionConnectionService.breakeven(event, backtest, { actionName, ...context });
9323
+ await this.actionConnectionService.breakevenAvailable(event, backtest, { actionName, ...context });
9185
9324
  }
9186
9325
  };
9187
9326
  /**
9188
9327
  * Routes partial profit event to all registered actions for the strategy.
9189
9328
  *
9190
9329
  * Retrieves action list from strategy schema (IStrategySchema.actions)
9191
- * and invokes the partialProfit handler on each ClientAction instance sequentially.
9330
+ * and invokes the partialProfitAvailable handler on each ClientAction instance sequentially.
9192
9331
  *
9193
9332
  * @param backtest - Whether running in backtest mode (true) or live mode (false)
9194
9333
  * @param event - Profit milestone data with level (10%, 20%, etc.) and price
9195
9334
  * @param context - Strategy execution context with strategyName, exchangeName, frameName
9196
9335
  */
9197
- this.partialProfit = async (backtest, event, context) => {
9198
- this.loggerService.log("actionCoreService partialProfit", {
9336
+ this.partialProfitAvailable = async (backtest, event, context) => {
9337
+ this.loggerService.log("actionCoreService partialProfitAvailable", {
9199
9338
  context,
9200
9339
  });
9201
9340
  await this.validate(context);
9202
9341
  const { actions = [] } = this.strategySchemaService.get(context.strategyName);
9203
9342
  for (const actionName of actions) {
9204
- await this.actionConnectionService.partialProfit(event, backtest, { actionName, ...context });
9343
+ await this.actionConnectionService.partialProfitAvailable(event, backtest, { actionName, ...context });
9205
9344
  }
9206
9345
  };
9207
9346
  /**
9208
9347
  * Routes partial loss event to all registered actions for the strategy.
9209
9348
  *
9210
9349
  * Retrieves action list from strategy schema (IStrategySchema.actions)
9211
- * and invokes the partialLoss handler on each ClientAction instance sequentially.
9350
+ * and invokes the partialLossAvailable handler on each ClientAction instance sequentially.
9212
9351
  *
9213
9352
  * @param backtest - Whether running in backtest mode (true) or live mode (false)
9214
9353
  * @param event - Loss milestone data with level (-10%, -20%, etc.) and price
9215
9354
  * @param context - Strategy execution context with strategyName, exchangeName, frameName
9216
9355
  */
9217
- this.partialLoss = async (backtest, event, context) => {
9218
- this.loggerService.log("actionCoreService partialLoss", {
9356
+ this.partialLossAvailable = async (backtest, event, context) => {
9357
+ this.loggerService.log("actionCoreService partialLossAvailable", {
9219
9358
  context,
9220
9359
  });
9221
9360
  await this.validate(context);
9222
9361
  const { actions = [] } = this.strategySchemaService.get(context.strategyName);
9223
9362
  for (const actionName of actions) {
9224
- await this.actionConnectionService.partialLoss(event, backtest, { actionName, ...context });
9363
+ await this.actionConnectionService.partialLossAvailable(event, backtest, { actionName, ...context });
9225
9364
  }
9226
9365
  };
9227
9366
  /**
9228
- * Routes ping event to all registered actions for the strategy.
9367
+ * Routes scheduled ping event to all registered actions for the strategy.
9229
9368
  *
9230
9369
  * Retrieves action list from strategy schema (IStrategySchema.actions)
9231
- * and invokes the ping handler on each ClientAction instance sequentially.
9370
+ * and invokes the pingScheduled handler on each ClientAction instance sequentially.
9232
9371
  * Called every minute during scheduled signal monitoring.
9233
9372
  *
9234
9373
  * @param backtest - Whether running in backtest mode (true) or live mode (false)
9235
9374
  * @param event - Scheduled signal monitoring data
9236
9375
  * @param context - Strategy execution context with strategyName, exchangeName, frameName
9237
9376
  */
9238
- this.ping = async (backtest, event, context) => {
9239
- this.loggerService.log("actionCoreService ping", {
9377
+ this.pingScheduled = async (backtest, event, context) => {
9378
+ this.loggerService.log("actionCoreService pingScheduled", {
9379
+ context,
9380
+ });
9381
+ await this.validate(context);
9382
+ const { actions = [] } = this.strategySchemaService.get(context.strategyName);
9383
+ for (const actionName of actions) {
9384
+ await this.actionConnectionService.pingScheduled(event, backtest, { actionName, ...context });
9385
+ }
9386
+ };
9387
+ /**
9388
+ * Routes active ping event to all registered actions for the strategy.
9389
+ *
9390
+ * Retrieves action list from strategy schema (IStrategySchema.actions)
9391
+ * and invokes the pingActive handler on each ClientAction instance sequentially.
9392
+ * Called every minute during active pending signal monitoring.
9393
+ *
9394
+ * @param backtest - Whether running in backtest mode (true) or live mode (false)
9395
+ * @param event - Active pending signal monitoring data
9396
+ * @param context - Strategy execution context with strategyName, exchangeName, frameName
9397
+ */
9398
+ this.pingActive = async (backtest, event, context) => {
9399
+ this.loggerService.log("actionCoreService pingActive", {
9240
9400
  context,
9241
9401
  });
9242
9402
  await this.validate(context);
9243
9403
  const { actions = [] } = this.strategySchemaService.get(context.strategyName);
9244
9404
  for (const actionName of actions) {
9245
- await this.actionConnectionService.ping(event, backtest, { actionName, ...context });
9405
+ await this.actionConnectionService.pingActive(event, backtest, { actionName, ...context });
9246
9406
  }
9247
9407
  };
9248
9408
  /**
@@ -10361,7 +10521,11 @@ class LiveLogicPrivateService {
10361
10521
  await sleep(TICK_TTL);
10362
10522
  continue;
10363
10523
  }
10364
- // Yield opened, closed results
10524
+ if (result.action === "waiting") {
10525
+ await sleep(TICK_TTL);
10526
+ continue;
10527
+ }
10528
+ // Yield opened, closed, cancelled results
10365
10529
  yield result;
10366
10530
  // Check if strategy should stop after signal is closed
10367
10531
  if (result.action === "closed") {
@@ -12296,6 +12460,12 @@ var _a$1, _b$1;
12296
12460
  const MARKDOWN_METHOD_NAME_ENABLE = "MarkdownUtils.enable";
12297
12461
  const MARKDOWN_METHOD_NAME_DISABLE = "MarkdownUtils.disable";
12298
12462
  const MARKDOWN_METHOD_NAME_USE_ADAPTER = "MarkdownAdapter.useMarkdownAdapter";
12463
+ const MARKDOWN_METHOD_NAME_FILE_DUMP = "MarkdownFileAdapter.dump";
12464
+ const MARKDOWN_METHOD_NAME_FOLDER_DUMP = "MarkdownFolderAdapter.dump";
12465
+ const MARKDOWN_METHOD_NAME_WRITE_DATA = "MarkdownAdapter.writeData";
12466
+ const MARKDOWN_METHOD_NAME_USE_MD = "MarkdownAdapter.useMd";
12467
+ const MARKDOWN_METHOD_NAME_USE_JSONL = "MarkdownAdapter.useJsonl";
12468
+ const MARKDOWN_METHOD_NAME_USE_DUMMY = "MarkdownAdapter.useDummy";
12299
12469
  const WAIT_FOR_INIT_SYMBOL$1 = Symbol("wait-for-init");
12300
12470
  const WRITE_SAFE_SYMBOL$1 = Symbol("write-safe");
12301
12471
  /**
@@ -12389,7 +12559,7 @@ class MarkdownFileBase {
12389
12559
  * @throws Error if stream not initialized or write timeout exceeded
12390
12560
  */
12391
12561
  async dump(data, options) {
12392
- backtest$1.loggerService.debug("MarkdownFileAdapter.dump", {
12562
+ backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_FILE_DUMP, {
12393
12563
  markdownName: this.markdownName,
12394
12564
  options,
12395
12565
  });
@@ -12471,7 +12641,7 @@ class MarkdownFolderBase {
12471
12641
  * @throws Error if directory creation or file write fails
12472
12642
  */
12473
12643
  async dump(content, options) {
12474
- backtest$1.loggerService.debug("MarkdownFolderAdapter.dump", {
12644
+ backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_FOLDER_DUMP, {
12475
12645
  markdownName: this.markdownName,
12476
12646
  options,
12477
12647
  });
@@ -12711,7 +12881,7 @@ class MarkdownAdapter extends MarkdownUtils {
12711
12881
  * @internal - Use service-specific dump methods instead (e.g., Backtest.dump)
12712
12882
  */
12713
12883
  async writeData(markdownName, content, options) {
12714
- backtest$1.loggerService.debug("MarkdownAdapter.writeData", {
12884
+ backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_WRITE_DATA, {
12715
12885
  markdownName,
12716
12886
  options,
12717
12887
  });
@@ -12726,7 +12896,7 @@ class MarkdownAdapter extends MarkdownUtils {
12726
12896
  * Each dump creates a separate .md file.
12727
12897
  */
12728
12898
  useMd() {
12729
- backtest$1.loggerService.debug("MarkdownAdapter.useMd");
12899
+ backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_MD);
12730
12900
  this.useMarkdownAdapter(MarkdownFolderBase);
12731
12901
  }
12732
12902
  /**
@@ -12735,7 +12905,7 @@ class MarkdownAdapter extends MarkdownUtils {
12735
12905
  * All dumps append to a single .jsonl file per markdown type.
12736
12906
  */
12737
12907
  useJsonl() {
12738
- backtest$1.loggerService.debug("MarkdownAdapter.useJsonl");
12908
+ backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_JSONL);
12739
12909
  this.useMarkdownAdapter(MarkdownFileBase);
12740
12910
  }
12741
12911
  /**
@@ -12743,7 +12913,7 @@ class MarkdownAdapter extends MarkdownUtils {
12743
12913
  * All future markdown writes will be no-ops.
12744
12914
  */
12745
12915
  useDummy() {
12746
- backtest$1.loggerService.debug("MarkdownAdapter.useDummy");
12916
+ backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_DUMMY);
12747
12917
  this.useMarkdownAdapter(MarkdownDummy);
12748
12918
  }
12749
12919
  }
@@ -13406,6 +13576,98 @@ let ReportStorage$5 = class ReportStorage {
13406
13576
  this._eventList.pop();
13407
13577
  }
13408
13578
  }
13579
+ /**
13580
+ * Adds a scheduled event to the storage.
13581
+ *
13582
+ * @param data - Scheduled tick result
13583
+ */
13584
+ addScheduledEvent(data) {
13585
+ this._eventList.unshift({
13586
+ timestamp: data.signal.scheduledAt,
13587
+ action: "scheduled",
13588
+ symbol: data.signal.symbol,
13589
+ signalId: data.signal.id,
13590
+ position: data.signal.position,
13591
+ note: data.signal.note,
13592
+ currentPrice: data.currentPrice,
13593
+ priceOpen: data.signal.priceOpen,
13594
+ priceTakeProfit: data.signal.priceTakeProfit,
13595
+ priceStopLoss: data.signal.priceStopLoss,
13596
+ originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
13597
+ originalPriceStopLoss: data.signal.originalPriceStopLoss,
13598
+ totalExecuted: data.signal.totalExecuted,
13599
+ });
13600
+ // Trim queue if exceeded MAX_EVENTS
13601
+ if (this._eventList.length > MAX_EVENTS$6) {
13602
+ this._eventList.pop();
13603
+ }
13604
+ }
13605
+ /**
13606
+ * Adds a waiting event to the storage.
13607
+ * Replaces the last waiting event with the same signalId.
13608
+ *
13609
+ * @param data - Waiting tick result
13610
+ */
13611
+ addWaitingEvent(data) {
13612
+ const newEvent = {
13613
+ timestamp: Date.now(),
13614
+ action: "waiting",
13615
+ symbol: data.signal.symbol,
13616
+ signalId: data.signal.id,
13617
+ position: data.signal.position,
13618
+ note: data.signal.note,
13619
+ currentPrice: data.currentPrice,
13620
+ priceOpen: data.signal.priceOpen,
13621
+ priceTakeProfit: data.signal.priceTakeProfit,
13622
+ priceStopLoss: data.signal.priceStopLoss,
13623
+ originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
13624
+ originalPriceStopLoss: data.signal.originalPriceStopLoss,
13625
+ totalExecuted: data.signal.totalExecuted,
13626
+ percentTp: data.percentTp,
13627
+ percentSl: data.percentSl,
13628
+ pnl: data.pnl.pnlPercentage,
13629
+ };
13630
+ // Find the last waiting event with the same signalId
13631
+ const lastWaitingIndex = this._eventList.findLastIndex((event) => event.action === "waiting" && event.signalId === data.signal.id);
13632
+ // Replace the last waiting event with the same signalId
13633
+ if (lastWaitingIndex !== -1) {
13634
+ this._eventList[lastWaitingIndex] = newEvent;
13635
+ return;
13636
+ }
13637
+ // If no previous waiting event found, add new event
13638
+ this._eventList.unshift(newEvent);
13639
+ // Trim queue if exceeded MAX_EVENTS
13640
+ if (this._eventList.length > MAX_EVENTS$6) {
13641
+ this._eventList.pop();
13642
+ }
13643
+ }
13644
+ /**
13645
+ * Adds a cancelled event to the storage.
13646
+ *
13647
+ * @param data - Cancelled tick result
13648
+ */
13649
+ addCancelledEvent(data) {
13650
+ this._eventList.unshift({
13651
+ timestamp: data.closeTimestamp,
13652
+ action: "cancelled",
13653
+ symbol: data.signal.symbol,
13654
+ signalId: data.signal.id,
13655
+ position: data.signal.position,
13656
+ note: data.signal.note,
13657
+ currentPrice: data.currentPrice,
13658
+ priceOpen: data.signal.priceOpen,
13659
+ priceTakeProfit: data.signal.priceTakeProfit,
13660
+ priceStopLoss: data.signal.priceStopLoss,
13661
+ originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
13662
+ originalPriceStopLoss: data.signal.originalPriceStopLoss,
13663
+ totalExecuted: data.signal.totalExecuted,
13664
+ cancelReason: data.reason,
13665
+ });
13666
+ // Trim queue if exceeded MAX_EVENTS
13667
+ if (this._eventList.length > MAX_EVENTS$6) {
13668
+ this._eventList.pop();
13669
+ }
13670
+ }
13409
13671
  /**
13410
13672
  * Calculates statistical data from live trading events (Controller).
13411
13673
  * Returns null for any unsafe numeric values (NaN, Infinity, etc).
@@ -13663,6 +13925,12 @@ class LiveMarkdownService {
13663
13925
  if (data.action === "idle") {
13664
13926
  storage.addIdleEvent(data.currentPrice);
13665
13927
  }
13928
+ else if (data.action === "scheduled") {
13929
+ storage.addScheduledEvent(data);
13930
+ }
13931
+ else if (data.action === "waiting") {
13932
+ storage.addWaitingEvent(data);
13933
+ }
13666
13934
  else if (data.action === "opened") {
13667
13935
  storage.addOpenedEvent(data);
13668
13936
  }
@@ -13672,6 +13940,9 @@ class LiveMarkdownService {
13672
13940
  else if (data.action === "closed") {
13673
13941
  storage.addClosedEvent(data);
13674
13942
  }
13943
+ else if (data.action === "cancelled") {
13944
+ storage.addCancelledEvent(data);
13945
+ }
13675
13946
  };
13676
13947
  /**
13677
13948
  * Gets statistical data from all live trading events for a symbol-strategy pair.
@@ -18098,7 +18369,7 @@ const CREATE_COMMIT_PROFIT_FN = (self) => async (symbol, strategyName, exchangeN
18098
18369
  timestamp,
18099
18370
  };
18100
18371
  await partialProfitSubject.next(event);
18101
- await self.actionCoreService.partialProfit(backtest, event, { strategyName, exchangeName, frameName });
18372
+ await self.actionCoreService.partialProfitAvailable(backtest, event, { strategyName, exchangeName, frameName });
18102
18373
  };
18103
18374
  /**
18104
18375
  * Creates a callback function for emitting loss events to partialLossSubject.
@@ -18122,7 +18393,7 @@ const CREATE_COMMIT_LOSS_FN = (self) => async (symbol, strategyName, exchangeNam
18122
18393
  timestamp,
18123
18394
  };
18124
18395
  await partialLossSubject.next(event);
18125
- await self.actionCoreService.partialLoss(backtest, event, { strategyName, exchangeName, frameName });
18396
+ await self.actionCoreService.partialLossAvailable(backtest, event, { strategyName, exchangeName, frameName });
18126
18397
  };
18127
18398
  /**
18128
18399
  * Connection service for partial profit/loss tracking.
@@ -19271,7 +19542,7 @@ const CREATE_COMMIT_BREAKEVEN_FN = (self) => async (symbol, strategyName, exchan
19271
19542
  timestamp,
19272
19543
  };
19273
19544
  await breakevenSubject.next(event);
19274
- await self.actionCoreService.breakeven(backtest, event, { strategyName, exchangeName, frameName });
19545
+ await self.actionCoreService.breakevenAvailable(backtest, event, { strategyName, exchangeName, frameName });
19275
19546
  };
19276
19547
  /**
19277
19548
  * Connection service for breakeven tracking.
@@ -21356,6 +21627,43 @@ class LiveReportService {
21356
21627
  if (data.action === "idle") {
21357
21628
  await Report.writeData("live", baseEvent, searchOptions);
21358
21629
  }
21630
+ else if (data.action === "scheduled") {
21631
+ await Report.writeData("live", {
21632
+ ...baseEvent,
21633
+ signalId: data.signal?.id,
21634
+ position: data.signal?.position,
21635
+ note: data.signal?.note,
21636
+ priceOpen: data.signal?.priceOpen,
21637
+ priceTakeProfit: data.signal?.priceTakeProfit,
21638
+ priceStopLoss: data.signal?.priceStopLoss,
21639
+ originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
21640
+ originalPriceStopLoss: data.signal?.originalPriceStopLoss,
21641
+ totalExecuted: data.signal?.totalExecuted,
21642
+ scheduledAt: data.signal?.scheduledAt,
21643
+ minuteEstimatedTime: data.signal?.minuteEstimatedTime,
21644
+ }, { ...searchOptions, signalId: data.signal?.id });
21645
+ }
21646
+ else if (data.action === "waiting") {
21647
+ await Report.writeData("live", {
21648
+ ...baseEvent,
21649
+ signalId: data.signal?.id,
21650
+ position: data.signal?.position,
21651
+ note: data.signal?.note,
21652
+ priceOpen: data.signal?.priceOpen,
21653
+ priceTakeProfit: data.signal?.priceTakeProfit,
21654
+ priceStopLoss: data.signal?.priceStopLoss,
21655
+ originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
21656
+ originalPriceStopLoss: data.signal?.originalPriceStopLoss,
21657
+ totalExecuted: data.signal?.totalExecuted,
21658
+ scheduledAt: data.signal?.scheduledAt,
21659
+ minuteEstimatedTime: data.signal?.minuteEstimatedTime,
21660
+ percentTp: data.percentTp,
21661
+ percentSl: data.percentSl,
21662
+ pnl: data.pnl.pnlPercentage,
21663
+ pnlPriceOpen: data.pnl.priceOpen,
21664
+ pnlPriceClose: data.pnl.priceClose,
21665
+ }, { ...searchOptions, signalId: data.signal?.id });
21666
+ }
21359
21667
  else if (data.action === "opened") {
21360
21668
  await Report.writeData("live", {
21361
21669
  ...baseEvent,
@@ -21422,6 +21730,24 @@ class LiveReportService {
21422
21730
  closeTime: data.closeTimestamp,
21423
21731
  }, { ...searchOptions, signalId: data.signal?.id });
21424
21732
  }
21733
+ else if (data.action === "cancelled") {
21734
+ await Report.writeData("live", {
21735
+ ...baseEvent,
21736
+ signalId: data.signal?.id,
21737
+ position: data.signal?.position,
21738
+ note: data.signal?.note,
21739
+ priceOpen: data.signal?.priceOpen,
21740
+ priceTakeProfit: data.signal?.priceTakeProfit,
21741
+ priceStopLoss: data.signal?.priceStopLoss,
21742
+ originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
21743
+ originalPriceStopLoss: data.signal?.originalPriceStopLoss,
21744
+ totalExecuted: data.signal?.totalExecuted,
21745
+ scheduledAt: data.signal?.scheduledAt,
21746
+ minuteEstimatedTime: data.signal?.minuteEstimatedTime,
21747
+ cancelReason: data.reason,
21748
+ closeTime: data.closeTimestamp,
21749
+ }, { ...searchOptions, signalId: data.signal?.id });
21750
+ }
21425
21751
  };
21426
21752
  /**
21427
21753
  * Subscribes to live signal emitter to receive tick events.
@@ -22611,23 +22937,23 @@ const backtest = {
22611
22937
  init();
22612
22938
  var backtest$1 = backtest;
22613
22939
 
22614
- const GET_TIMEFRAME_METHOD_NAME = "get.getTimeframe";
22940
+ const GET_TIMEFRAME_METHOD_NAME = "get.getBacktestTimeframe";
22615
22941
  /**
22616
22942
  * Retrieves current backtest timeframe for given symbol.
22617
22943
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
22618
22944
  * @returns Promise resolving to array of Date objects representing tick timestamps
22619
22945
  * @throws Error if called outside of backtest execution context
22620
22946
  */
22621
- async function getCurrentTimeframe(symbol) {
22947
+ async function getBacktestTimeframe(symbol) {
22622
22948
  backtest$1.loggerService.info(GET_TIMEFRAME_METHOD_NAME, { symbol });
22623
22949
  if (!ExecutionContextService.hasContext()) {
22624
- throw new Error("getCurrentTimeframe requires an execution context");
22950
+ throw new Error("getBacktestTimeframe requires an execution context");
22625
22951
  }
22626
22952
  if (!MethodContextService.hasContext()) {
22627
- throw new Error("getCurrentTimeframe requires a method context");
22953
+ throw new Error("getBacktestTimeframe requires a method context");
22628
22954
  }
22629
22955
  if (!backtest$1.executionContextService.context.backtest) {
22630
- throw new Error("getCurrentTimeframe can only be used during backtest execution");
22956
+ throw new Error("getBacktestTimeframe can only be used during backtest execution");
22631
22957
  }
22632
22958
  return await backtest$1.frameCoreService.getTimeframe(symbol, backtest$1.methodContextService.context.frameName);
22633
22959
  }
@@ -22827,14 +23153,14 @@ async function validate(args = {}) {
22827
23153
  return await validateInternal(args);
22828
23154
  }
22829
23155
 
22830
- const GET_STRATEGY_METHOD_NAME = "get.getStrategy";
22831
- const GET_EXCHANGE_METHOD_NAME = "get.getExchange";
22832
- const GET_FRAME_METHOD_NAME = "get.getFrame";
22833
- const GET_WALKER_METHOD_NAME = "get.getWalker";
22834
- const GET_SIZING_METHOD_NAME = "get.getSizing";
22835
- const GET_RISK_METHOD_NAME = "get.getRisk";
22836
- const GET_OPTIMIZER_METHOD_NAME = "get.getOptimizer";
22837
- const GET_ACTION_METHOD_NAME = "get.getAction";
23156
+ const GET_STRATEGY_METHOD_NAME = "get.getStrategySchema";
23157
+ const GET_EXCHANGE_METHOD_NAME = "get.getExchangeSchema";
23158
+ const GET_FRAME_METHOD_NAME = "get.getFrameSchema";
23159
+ const GET_WALKER_METHOD_NAME = "get.getWalkerSchema";
23160
+ const GET_SIZING_METHOD_NAME = "get.getSizingSchema";
23161
+ const GET_RISK_METHOD_NAME = "get.getRiskSchema";
23162
+ const GET_OPTIMIZER_METHOD_NAME = "get.getOptimizerSchema";
23163
+ const GET_ACTION_METHOD_NAME = "get.getActionSchema";
22838
23164
  /**
22839
23165
  * Retrieves a registered strategy schema by name.
22840
23166
  *
@@ -22849,7 +23175,7 @@ const GET_ACTION_METHOD_NAME = "get.getAction";
22849
23175
  * console.log(strategy.getSignal); // async function
22850
23176
  * ```
22851
23177
  */
22852
- function getStrategy(strategyName) {
23178
+ function getStrategySchema(strategyName) {
22853
23179
  backtest$1.loggerService.log(GET_STRATEGY_METHOD_NAME, {
22854
23180
  strategyName,
22855
23181
  });
@@ -22870,7 +23196,7 @@ function getStrategy(strategyName) {
22870
23196
  * console.log(exchange.formatPrice); // async function
22871
23197
  * ```
22872
23198
  */
22873
- function getExchange(exchangeName) {
23199
+ function getExchangeSchema(exchangeName) {
22874
23200
  backtest$1.loggerService.log(GET_EXCHANGE_METHOD_NAME, {
22875
23201
  exchangeName,
22876
23202
  });
@@ -22892,7 +23218,7 @@ function getExchange(exchangeName) {
22892
23218
  * console.log(frame.endDate); // Date object
22893
23219
  * ```
22894
23220
  */
22895
- function getFrame(frameName) {
23221
+ function getFrameSchema(frameName) {
22896
23222
  backtest$1.loggerService.log(GET_FRAME_METHOD_NAME, {
22897
23223
  frameName,
22898
23224
  });
@@ -22915,7 +23241,7 @@ function getFrame(frameName) {
22915
23241
  * console.log(walker.metric); // "sharpeRatio"
22916
23242
  * ```
22917
23243
  */
22918
- function getWalker(walkerName) {
23244
+ function getWalkerSchema(walkerName) {
22919
23245
  backtest$1.loggerService.log(GET_WALKER_METHOD_NAME, {
22920
23246
  walkerName,
22921
23247
  });
@@ -22937,7 +23263,7 @@ function getWalker(walkerName) {
22937
23263
  * console.log(sizing.maxPositionPercentage); // 10
22938
23264
  * ```
22939
23265
  */
22940
- function getSizing(sizingName) {
23266
+ function getSizingSchema(sizingName) {
22941
23267
  backtest$1.loggerService.log(GET_SIZING_METHOD_NAME, {
22942
23268
  sizingName,
22943
23269
  });
@@ -22958,7 +23284,7 @@ function getSizing(sizingName) {
22958
23284
  * console.log(risk.validations); // Array of validation functions
22959
23285
  * ```
22960
23286
  */
22961
- function getRisk(riskName) {
23287
+ function getRiskSchema(riskName) {
22962
23288
  backtest$1.loggerService.log(GET_RISK_METHOD_NAME, {
22963
23289
  riskName,
22964
23290
  });
@@ -22981,7 +23307,7 @@ function getRisk(riskName) {
22981
23307
  * console.log(optimizer.getPrompt); // async function
22982
23308
  * ```
22983
23309
  */
22984
- function getOptimizer(optimizerName) {
23310
+ function getOptimizerSchema(optimizerName) {
22985
23311
  backtest$1.loggerService.log(GET_OPTIMIZER_METHOD_NAME, {
22986
23312
  optimizerName,
22987
23313
  });
@@ -23002,7 +23328,7 @@ function getOptimizer(optimizerName) {
23002
23328
  * console.log(action.callbacks); // Optional lifecycle callbacks
23003
23329
  * ```
23004
23330
  */
23005
- function getAction(actionName) {
23331
+ function getActionSchema(actionName) {
23006
23332
  backtest$1.loggerService.log(GET_ACTION_METHOD_NAME, {
23007
23333
  actionName,
23008
23334
  });
@@ -23016,6 +23342,8 @@ const FORMAT_PRICE_METHOD_NAME = "exchange.formatPrice";
23016
23342
  const FORMAT_QUANTITY_METHOD_NAME = "exchange.formatQuantity";
23017
23343
  const GET_DATE_METHOD_NAME = "exchange.getDate";
23018
23344
  const GET_MODE_METHOD_NAME = "exchange.getMode";
23345
+ const GET_SYMBOL_METHOD_NAME = "exchange.getSymbol";
23346
+ const GET_CONTEXT_METHOD_NAME = "exchange.getContext";
23019
23347
  const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
23020
23348
  const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
23021
23349
  /**
@@ -23198,6 +23526,48 @@ async function getMode() {
23198
23526
  const { backtest: bt } = backtest$1.executionContextService.context;
23199
23527
  return bt ? "backtest" : "live";
23200
23528
  }
23529
+ /**
23530
+ * Gets the current trading symbol from execution context.
23531
+ *
23532
+ * @returns Promise resolving to the current trading symbol (e.g., "BTCUSDT")
23533
+ * @throws Error if execution context is not active
23534
+ *
23535
+ * @example
23536
+ * ```typescript
23537
+ * const symbol = await getSymbol();
23538
+ * console.log(symbol); // "BTCUSDT"
23539
+ * ```
23540
+ */
23541
+ async function getSymbol() {
23542
+ backtest$1.loggerService.info(GET_SYMBOL_METHOD_NAME);
23543
+ if (!ExecutionContextService.hasContext()) {
23544
+ throw new Error("getSymbol requires an execution context");
23545
+ }
23546
+ const { symbol } = backtest$1.executionContextService.context;
23547
+ return symbol;
23548
+ }
23549
+ /**
23550
+ * Gets the current method context.
23551
+ *
23552
+ * Returns the context object from the method context service, which contains
23553
+ * information about the current method execution environment.
23554
+ *
23555
+ * @returns Promise resolving to the current method context object
23556
+ * @throws Error if method context is not active
23557
+ *
23558
+ * @example
23559
+ * ```typescript
23560
+ * const context = await getContext();
23561
+ * console.log(context); // { ...method context data... }
23562
+ * ```
23563
+ */
23564
+ async function getContext() {
23565
+ backtest$1.loggerService.info(GET_CONTEXT_METHOD_NAME);
23566
+ if (!MethodContextService.hasContext()) {
23567
+ throw new Error("getContext requires a method context");
23568
+ }
23569
+ return backtest$1.methodContextService.context;
23570
+ }
23201
23571
  /**
23202
23572
  * Fetches order book for a trading pair from the registered exchange.
23203
23573
  *
@@ -23234,52 +23604,12 @@ async function getOrderBook(symbol, depth) {
23234
23604
  return await backtest$1.exchangeConnectionService.getOrderBook(symbol, depth);
23235
23605
  }
23236
23606
 
23237
- const STOP_METHOD_NAME = "strategy.stop";
23238
- const CANCEL_METHOD_NAME = "strategy.cancel";
23239
- const PARTIAL_PROFIT_METHOD_NAME = "strategy.partialProfit";
23240
- const PARTIAL_LOSS_METHOD_NAME = "strategy.partialLoss";
23241
- const TRAILING_STOP_METHOD_NAME = "strategy.trailingStop";
23242
- const TRAILING_PROFIT_METHOD_NAME = "strategy.trailingTake";
23243
- const BREAKEVEN_METHOD_NAME = "strategy.breakeven";
23244
- /**
23245
- * Stops the strategy from generating new signals.
23246
- *
23247
- * Sets internal flag to prevent strategy from opening new signals.
23248
- * Current active signal (if any) will complete normally.
23249
- * Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
23250
- *
23251
- * Automatically detects backtest/live mode from execution context.
23252
- *
23253
- * @param symbol - Trading pair symbol
23254
- * @param strategyName - Strategy name to stop
23255
- * @returns Promise that resolves when stop flag is set
23256
- *
23257
- * @example
23258
- * ```typescript
23259
- * import { stop } from "backtest-kit";
23260
- *
23261
- * // Stop strategy after some condition
23262
- * await stop("BTCUSDT", "my-strategy");
23263
- * ```
23264
- */
23265
- async function stop(symbol) {
23266
- backtest$1.loggerService.info(STOP_METHOD_NAME, {
23267
- symbol,
23268
- });
23269
- if (!ExecutionContextService.hasContext()) {
23270
- throw new Error("stop requires an execution context");
23271
- }
23272
- if (!MethodContextService.hasContext()) {
23273
- throw new Error("stop requires a method context");
23274
- }
23275
- const { backtest: isBacktest } = backtest$1.executionContextService.context;
23276
- const { exchangeName, frameName, strategyName } = backtest$1.methodContextService.context;
23277
- await backtest$1.strategyCoreService.stop(isBacktest, symbol, {
23278
- exchangeName,
23279
- frameName,
23280
- strategyName,
23281
- });
23282
- }
23607
+ const CANCEL_METHOD_NAME = "strategy.commitCancel";
23608
+ const PARTIAL_PROFIT_METHOD_NAME = "strategy.commitPartialProfit";
23609
+ const PARTIAL_LOSS_METHOD_NAME = "strategy.commitPartialLoss";
23610
+ const TRAILING_STOP_METHOD_NAME = "strategy.commitTrailingStop";
23611
+ const TRAILING_PROFIT_METHOD_NAME = "strategy.commitTrailingTake";
23612
+ const BREAKEVEN_METHOD_NAME = "strategy.commitBreakeven";
23283
23613
  /**
23284
23614
  * Cancels the scheduled signal without stopping the strategy.
23285
23615
  *
@@ -23302,7 +23632,7 @@ async function stop(symbol) {
23302
23632
  * await cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
23303
23633
  * ```
23304
23634
  */
23305
- async function cancel(symbol, cancelId) {
23635
+ async function commitCancel(symbol, cancelId) {
23306
23636
  backtest$1.loggerService.info(CANCEL_METHOD_NAME, {
23307
23637
  symbol,
23308
23638
  cancelId,
@@ -23344,7 +23674,7 @@ async function cancel(symbol, cancelId) {
23344
23674
  * }
23345
23675
  * ```
23346
23676
  */
23347
- async function partialProfit(symbol, percentToClose) {
23677
+ async function commitPartialProfit(symbol, percentToClose) {
23348
23678
  backtest$1.loggerService.info(PARTIAL_PROFIT_METHOD_NAME, {
23349
23679
  symbol,
23350
23680
  percentToClose,
@@ -23387,7 +23717,7 @@ async function partialProfit(symbol, percentToClose) {
23387
23717
  * }
23388
23718
  * ```
23389
23719
  */
23390
- async function partialLoss(symbol, percentToClose) {
23720
+ async function commitPartialLoss(symbol, percentToClose) {
23391
23721
  backtest$1.loggerService.info(PARTIAL_LOSS_METHOD_NAME, {
23392
23722
  symbol,
23393
23723
  percentToClose,
@@ -23446,7 +23776,7 @@ async function partialLoss(symbol, percentToClose) {
23446
23776
  * // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
23447
23777
  * ```
23448
23778
  */
23449
- async function trailingStop(symbol, percentShift, currentPrice) {
23779
+ async function commitTrailingStop(symbol, percentShift, currentPrice) {
23450
23780
  backtest$1.loggerService.info(TRAILING_STOP_METHOD_NAME, {
23451
23781
  symbol,
23452
23782
  percentShift,
@@ -23505,7 +23835,7 @@ async function trailingStop(symbol, percentShift, currentPrice) {
23505
23835
  * // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
23506
23836
  * ```
23507
23837
  */
23508
- async function trailingTake(symbol, percentShift, currentPrice) {
23838
+ async function commitTrailingTake(symbol, percentShift, currentPrice) {
23509
23839
  backtest$1.loggerService.info(TRAILING_PROFIT_METHOD_NAME, {
23510
23840
  symbol,
23511
23841
  percentShift,
@@ -23546,7 +23876,7 @@ async function trailingTake(symbol, percentShift, currentPrice) {
23546
23876
  * }
23547
23877
  * ```
23548
23878
  */
23549
- async function breakeven(symbol) {
23879
+ async function commitBreakeven(symbol) {
23550
23880
  backtest$1.loggerService.info(BREAKEVEN_METHOD_NAME, {
23551
23881
  symbol,
23552
23882
  });
@@ -23562,6 +23892,47 @@ async function breakeven(symbol) {
23562
23892
  return await backtest$1.strategyCoreService.breakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
23563
23893
  }
23564
23894
 
23895
+ const STOP_METHOD_NAME = "control.stop";
23896
+ /**
23897
+ * Stops the strategy from generating new signals.
23898
+ *
23899
+ * Sets internal flag to prevent strategy from opening new signals.
23900
+ * Current active signal (if any) will complete normally.
23901
+ * Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
23902
+ *
23903
+ * Automatically detects backtest/live mode from execution context.
23904
+ *
23905
+ * @param symbol - Trading pair symbol
23906
+ * @param strategyName - Strategy name to stop
23907
+ * @returns Promise that resolves when stop flag is set
23908
+ *
23909
+ * @example
23910
+ * ```typescript
23911
+ * import { stop } from "backtest-kit";
23912
+ *
23913
+ * // Stop strategy after some condition
23914
+ * await stop("BTCUSDT", "my-strategy");
23915
+ * ```
23916
+ */
23917
+ async function stop(symbol) {
23918
+ backtest$1.loggerService.info(STOP_METHOD_NAME, {
23919
+ symbol,
23920
+ });
23921
+ if (!ExecutionContextService.hasContext()) {
23922
+ throw new Error("stop requires an execution context");
23923
+ }
23924
+ if (!MethodContextService.hasContext()) {
23925
+ throw new Error("stop requires a method context");
23926
+ }
23927
+ const { backtest: isBacktest } = backtest$1.executionContextService.context;
23928
+ const { exchangeName, frameName, strategyName } = backtest$1.methodContextService.context;
23929
+ await backtest$1.strategyCoreService.stop(isBacktest, symbol, {
23930
+ exchangeName,
23931
+ frameName,
23932
+ strategyName,
23933
+ });
23934
+ }
23935
+
23565
23936
  /**
23566
23937
  * Sets custom logger implementation for the framework.
23567
23938
  *
@@ -23712,14 +24083,14 @@ function getDefaultColumns() {
23712
24083
  return DEFAULT_COLUMNS;
23713
24084
  }
23714
24085
 
23715
- const ADD_STRATEGY_METHOD_NAME = "add.addStrategy";
23716
- const ADD_EXCHANGE_METHOD_NAME = "add.addExchange";
23717
- const ADD_FRAME_METHOD_NAME = "add.addFrame";
23718
- const ADD_WALKER_METHOD_NAME = "add.addWalker";
23719
- const ADD_SIZING_METHOD_NAME = "add.addSizing";
23720
- const ADD_RISK_METHOD_NAME = "add.addRisk";
23721
- const ADD_OPTIMIZER_METHOD_NAME = "add.addOptimizer";
23722
- const ADD_ACTION_METHOD_NAME = "add.addAction";
24086
+ const ADD_STRATEGY_METHOD_NAME = "add.addStrategySchema";
24087
+ const ADD_EXCHANGE_METHOD_NAME = "add.addExchangeSchema";
24088
+ const ADD_FRAME_METHOD_NAME = "add.addFrameSchema";
24089
+ const ADD_WALKER_METHOD_NAME = "add.addWalkerSchema";
24090
+ const ADD_SIZING_METHOD_NAME = "add.addSizingSchema";
24091
+ const ADD_RISK_METHOD_NAME = "add.addRiskSchema";
24092
+ const ADD_OPTIMIZER_METHOD_NAME = "add.addOptimizerSchema";
24093
+ const ADD_ACTION_METHOD_NAME = "add.addActionSchema";
23723
24094
  /**
23724
24095
  * Registers a trading strategy in the framework.
23725
24096
  *
@@ -23754,7 +24125,7 @@ const ADD_ACTION_METHOD_NAME = "add.addAction";
23754
24125
  * });
23755
24126
  * ```
23756
24127
  */
23757
- function addStrategy(strategySchema) {
24128
+ function addStrategySchema(strategySchema) {
23758
24129
  backtest$1.loggerService.info(ADD_STRATEGY_METHOD_NAME, {
23759
24130
  strategySchema,
23760
24131
  });
@@ -23796,7 +24167,7 @@ function addStrategy(strategySchema) {
23796
24167
  * });
23797
24168
  * ```
23798
24169
  */
23799
- function addExchange(exchangeSchema) {
24170
+ function addExchangeSchema(exchangeSchema) {
23800
24171
  backtest$1.loggerService.info(ADD_EXCHANGE_METHOD_NAME, {
23801
24172
  exchangeSchema,
23802
24173
  });
@@ -23833,7 +24204,7 @@ function addExchange(exchangeSchema) {
23833
24204
  * });
23834
24205
  * ```
23835
24206
  */
23836
- function addFrame(frameSchema) {
24207
+ function addFrameSchema(frameSchema) {
23837
24208
  backtest$1.loggerService.info(ADD_FRAME_METHOD_NAME, {
23838
24209
  frameSchema,
23839
24210
  });
@@ -23877,7 +24248,7 @@ function addFrame(frameSchema) {
23877
24248
  * });
23878
24249
  * ```
23879
24250
  */
23880
- function addWalker(walkerSchema) {
24251
+ function addWalkerSchema(walkerSchema) {
23881
24252
  backtest$1.loggerService.info(ADD_WALKER_METHOD_NAME, {
23882
24253
  walkerSchema,
23883
24254
  });
@@ -23936,7 +24307,7 @@ function addWalker(walkerSchema) {
23936
24307
  * });
23937
24308
  * ```
23938
24309
  */
23939
- function addSizing(sizingSchema) {
24310
+ function addSizingSchema(sizingSchema) {
23940
24311
  backtest$1.loggerService.info(ADD_SIZING_METHOD_NAME, {
23941
24312
  sizingSchema,
23942
24313
  });
@@ -24004,7 +24375,7 @@ function addSizing(sizingSchema) {
24004
24375
  * });
24005
24376
  * ```
24006
24377
  */
24007
- function addRisk(riskSchema) {
24378
+ function addRiskSchema(riskSchema) {
24008
24379
  backtest$1.loggerService.info(ADD_RISK_METHOD_NAME, {
24009
24380
  riskSchema,
24010
24381
  });
@@ -24098,7 +24469,7 @@ function addRisk(riskSchema) {
24098
24469
  * });
24099
24470
  * ```
24100
24471
  */
24101
- function addOptimizer(optimizerSchema) {
24472
+ function addOptimizerSchema(optimizerSchema) {
24102
24473
  backtest$1.loggerService.info(ADD_OPTIMIZER_METHOD_NAME, {
24103
24474
  optimizerSchema,
24104
24475
  });
@@ -24173,7 +24544,7 @@ function addOptimizer(optimizerSchema) {
24173
24544
  * });
24174
24545
  * ```
24175
24546
  */
24176
- function addAction(actionSchema) {
24547
+ function addActionSchema(actionSchema) {
24177
24548
  backtest$1.loggerService.info(ADD_ACTION_METHOD_NAME, {
24178
24549
  actionSchema,
24179
24550
  });
@@ -24181,14 +24552,14 @@ function addAction(actionSchema) {
24181
24552
  backtest$1.actionSchemaService.register(actionSchema.actionName, actionSchema);
24182
24553
  }
24183
24554
 
24184
- const METHOD_NAME_OVERRIDE_STRATEGY = "function.override.overrideStrategy";
24185
- const METHOD_NAME_OVERRIDE_EXCHANGE = "function.override.overrideExchange";
24186
- const METHOD_NAME_OVERRIDE_FRAME = "function.override.overrideFrame";
24187
- const METHOD_NAME_OVERRIDE_WALKER = "function.override.overrideWalker";
24188
- const METHOD_NAME_OVERRIDE_SIZING = "function.override.overrideSizing";
24189
- const METHOD_NAME_OVERRIDE_RISK = "function.override.overrideRisk";
24190
- const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.overrideOptimizer";
24191
- const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideAction";
24555
+ const METHOD_NAME_OVERRIDE_STRATEGY = "function.override.overrideStrategySchema";
24556
+ const METHOD_NAME_OVERRIDE_EXCHANGE = "function.override.overrideExchangeSchema";
24557
+ const METHOD_NAME_OVERRIDE_FRAME = "function.override.overrideFrameSchema";
24558
+ const METHOD_NAME_OVERRIDE_WALKER = "function.override.overrideWalkerSchema";
24559
+ const METHOD_NAME_OVERRIDE_SIZING = "function.override.overrideSizingSchema";
24560
+ const METHOD_NAME_OVERRIDE_RISK = "function.override.overrideRiskSchema";
24561
+ const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.overrideOptimizerSchema";
24562
+ const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideActionSchema";
24192
24563
  /**
24193
24564
  * Overrides an existing trading strategy in the framework.
24194
24565
  *
@@ -24209,7 +24580,7 @@ const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideAction";
24209
24580
  * });
24210
24581
  * ```
24211
24582
  */
24212
- async function overrideStrategy(strategySchema) {
24583
+ async function overrideStrategySchema(strategySchema) {
24213
24584
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_STRATEGY, {
24214
24585
  strategySchema,
24215
24586
  });
@@ -24237,7 +24608,7 @@ async function overrideStrategy(strategySchema) {
24237
24608
  * });
24238
24609
  * ```
24239
24610
  */
24240
- async function overrideExchange(exchangeSchema) {
24611
+ async function overrideExchangeSchema(exchangeSchema) {
24241
24612
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_EXCHANGE, {
24242
24613
  exchangeSchema,
24243
24614
  });
@@ -24265,7 +24636,7 @@ async function overrideExchange(exchangeSchema) {
24265
24636
  * });
24266
24637
  * ```
24267
24638
  */
24268
- async function overrideFrame(frameSchema) {
24639
+ async function overrideFrameSchema(frameSchema) {
24269
24640
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_FRAME, {
24270
24641
  frameSchema,
24271
24642
  });
@@ -24294,7 +24665,7 @@ async function overrideFrame(frameSchema) {
24294
24665
  * });
24295
24666
  * ```
24296
24667
  */
24297
- async function overrideWalker(walkerSchema) {
24668
+ async function overrideWalkerSchema(walkerSchema) {
24298
24669
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_WALKER, {
24299
24670
  walkerSchema,
24300
24671
  });
@@ -24326,7 +24697,7 @@ async function overrideWalker(walkerSchema) {
24326
24697
  * });
24327
24698
  * ```
24328
24699
  */
24329
- async function overrideSizing(sizingSchema) {
24700
+ async function overrideSizingSchema(sizingSchema) {
24330
24701
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_SIZING, {
24331
24702
  sizingSchema,
24332
24703
  });
@@ -24353,7 +24724,7 @@ async function overrideSizing(sizingSchema) {
24353
24724
  * });
24354
24725
  * ```
24355
24726
  */
24356
- async function overrideRisk(riskSchema) {
24727
+ async function overrideRiskSchema(riskSchema) {
24357
24728
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_RISK, {
24358
24729
  riskSchema,
24359
24730
  });
@@ -24387,7 +24758,7 @@ async function overrideRisk(riskSchema) {
24387
24758
  * });
24388
24759
  * ```
24389
24760
  */
24390
- async function overrideOptimizer(optimizerSchema) {
24761
+ async function overrideOptimizerSchema(optimizerSchema) {
24391
24762
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_OPTIMIZER, {
24392
24763
  optimizerSchema,
24393
24764
  });
@@ -24454,7 +24825,7 @@ async function overrideOptimizer(optimizerSchema) {
24454
24825
  * });
24455
24826
  * ```
24456
24827
  */
24457
- async function overrideAction(actionSchema) {
24828
+ async function overrideActionSchema(actionSchema) {
24458
24829
  backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_ACTION, {
24459
24830
  actionSchema,
24460
24831
  });
@@ -24462,13 +24833,13 @@ async function overrideAction(actionSchema) {
24462
24833
  return backtest$1.actionSchemaService.override(actionSchema.actionName, actionSchema);
24463
24834
  }
24464
24835
 
24465
- const LIST_EXCHANGES_METHOD_NAME = "list.listExchanges";
24466
- const LIST_STRATEGIES_METHOD_NAME = "list.listStrategies";
24467
- const LIST_FRAMES_METHOD_NAME = "list.listFrames";
24468
- const LIST_WALKERS_METHOD_NAME = "list.listWalkers";
24469
- const LIST_SIZINGS_METHOD_NAME = "list.listSizings";
24470
- const LIST_RISKS_METHOD_NAME = "list.listRisks";
24471
- const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizers";
24836
+ const LIST_EXCHANGES_METHOD_NAME = "list.listExchangeSchema";
24837
+ const LIST_STRATEGIES_METHOD_NAME = "list.listStrategySchema";
24838
+ const LIST_FRAMES_METHOD_NAME = "list.listFrameSchema";
24839
+ const LIST_WALKERS_METHOD_NAME = "list.listWalkerSchema";
24840
+ const LIST_SIZINGS_METHOD_NAME = "list.listSizingSchema";
24841
+ const LIST_RISKS_METHOD_NAME = "list.listRiskSchema";
24842
+ const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizerSchema";
24472
24843
  /**
24473
24844
  * Returns a list of all registered exchange schemas.
24474
24845
  *
@@ -24494,7 +24865,7 @@ const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizers";
24494
24865
  * // [{ exchangeName: "binance", note: "Binance cryptocurrency exchange", ... }]
24495
24866
  * ```
24496
24867
  */
24497
- async function listExchanges() {
24868
+ async function listExchangeSchema() {
24498
24869
  backtest$1.loggerService.log(LIST_EXCHANGES_METHOD_NAME);
24499
24870
  return await backtest$1.exchangeValidationService.list();
24500
24871
  }
@@ -24528,7 +24899,7 @@ async function listExchanges() {
24528
24899
  * // [{ strategyName: "my-strategy", note: "Simple moving average...", ... }]
24529
24900
  * ```
24530
24901
  */
24531
- async function listStrategies() {
24902
+ async function listStrategySchema() {
24532
24903
  backtest$1.loggerService.log(LIST_STRATEGIES_METHOD_NAME);
24533
24904
  return await backtest$1.strategyValidationService.list();
24534
24905
  }
@@ -24557,7 +24928,7 @@ async function listStrategies() {
24557
24928
  * // [{ frameName: "1d-backtest", note: "One day backtest...", ... }]
24558
24929
  * ```
24559
24930
  */
24560
- async function listFrames() {
24931
+ async function listFrameSchema() {
24561
24932
  backtest$1.loggerService.log(LIST_FRAMES_METHOD_NAME);
24562
24933
  return await backtest$1.frameValidationService.list();
24563
24934
  }
@@ -24587,7 +24958,7 @@ async function listFrames() {
24587
24958
  * // [{ walkerName: "llm-prompt-optimizer", note: "Compare LLM...", ... }]
24588
24959
  * ```
24589
24960
  */
24590
- async function listWalkers() {
24961
+ async function listWalkerSchema() {
24591
24962
  backtest$1.loggerService.log(LIST_WALKERS_METHOD_NAME);
24592
24963
  return await backtest$1.walkerValidationService.list();
24593
24964
  }
@@ -24626,7 +24997,7 @@ async function listWalkers() {
24626
24997
  * // ]
24627
24998
  * ```
24628
24999
  */
24629
- async function listSizings() {
25000
+ async function listSizingSchema() {
24630
25001
  backtest$1.loggerService.log(LIST_SIZINGS_METHOD_NAME);
24631
25002
  return await backtest$1.sizingValidationService.list();
24632
25003
  }
@@ -24662,7 +25033,7 @@ async function listSizings() {
24662
25033
  * // ]
24663
25034
  * ```
24664
25035
  */
24665
- async function listRisks() {
25036
+ async function listRiskSchema() {
24666
25037
  backtest$1.loggerService.log(LIST_RISKS_METHOD_NAME);
24667
25038
  return await backtest$1.riskValidationService.list();
24668
25039
  }
@@ -24702,7 +25073,7 @@ async function listRisks() {
24702
25073
  * // [{ optimizerName: "llm-strategy-generator", note: "Generates...", ... }]
24703
25074
  * ```
24704
25075
  */
24705
- async function listOptimizers() {
25076
+ async function listOptimizerSchema() {
24706
25077
  backtest$1.loggerService.log(LIST_OPTIMIZERS_METHOD_NAME);
24707
25078
  return await backtest$1.optimizerValidationService.list();
24708
25079
  }
@@ -24737,8 +25108,10 @@ const LISTEN_BREAKEVEN_METHOD_NAME = "event.listenBreakeven";
24737
25108
  const LISTEN_BREAKEVEN_ONCE_METHOD_NAME = "event.listenBreakevenOnce";
24738
25109
  const LISTEN_RISK_METHOD_NAME = "event.listenRisk";
24739
25110
  const LISTEN_RISK_ONCE_METHOD_NAME = "event.listenRiskOnce";
24740
- const LISTEN_PING_METHOD_NAME = "event.listenPing";
24741
- const LISTEN_PING_ONCE_METHOD_NAME = "event.listenPingOnce";
25111
+ const LISTEN_SCHEDULE_PING_METHOD_NAME = "event.listenSchedulePing";
25112
+ const LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME = "event.listenSchedulePingOnce";
25113
+ const LISTEN_ACTIVE_PING_METHOD_NAME = "event.listenActivePing";
25114
+ const LISTEN_ACTIVE_PING_ONCE_METHOD_NAME = "event.listenActivePingOnce";
24742
25115
  /**
24743
25116
  * Subscribes to all signal events with queued async processing.
24744
25117
  *
@@ -25437,7 +25810,7 @@ function listenValidation(fn) {
25437
25810
  * unsubscribe();
25438
25811
  * ```
25439
25812
  */
25440
- function listenPartialProfit(fn) {
25813
+ function listenPartialProfitAvailable(fn) {
25441
25814
  backtest$1.loggerService.log(LISTEN_PARTIAL_PROFIT_METHOD_NAME);
25442
25815
  return partialProfitSubject.subscribe(queued(async (event) => fn(event)));
25443
25816
  }
@@ -25471,7 +25844,7 @@ function listenPartialProfit(fn) {
25471
25844
  * cancel();
25472
25845
  * ```
25473
25846
  */
25474
- function listenPartialProfitOnce(filterFn, fn) {
25847
+ function listenPartialProfitAvailableOnce(filterFn, fn) {
25475
25848
  backtest$1.loggerService.log(LISTEN_PARTIAL_PROFIT_ONCE_METHOD_NAME);
25476
25849
  return partialProfitSubject.filter(filterFn).once(fn);
25477
25850
  }
@@ -25499,7 +25872,7 @@ function listenPartialProfitOnce(filterFn, fn) {
25499
25872
  * unsubscribe();
25500
25873
  * ```
25501
25874
  */
25502
- function listenPartialLoss(fn) {
25875
+ function listenPartialLossAvailable(fn) {
25503
25876
  backtest$1.loggerService.log(LISTEN_PARTIAL_LOSS_METHOD_NAME);
25504
25877
  return partialLossSubject.subscribe(queued(async (event) => fn(event)));
25505
25878
  }
@@ -25533,7 +25906,7 @@ function listenPartialLoss(fn) {
25533
25906
  * cancel();
25534
25907
  * ```
25535
25908
  */
25536
- function listenPartialLossOnce(filterFn, fn) {
25909
+ function listenPartialLossAvailableOnce(filterFn, fn) {
25537
25910
  backtest$1.loggerService.log(LISTEN_PARTIAL_LOSS_ONCE_METHOD_NAME);
25538
25911
  return partialLossSubject.filter(filterFn).once(fn);
25539
25912
  }
@@ -25563,7 +25936,7 @@ function listenPartialLossOnce(filterFn, fn) {
25563
25936
  * unsubscribe();
25564
25937
  * ```
25565
25938
  */
25566
- function listenBreakeven(fn) {
25939
+ function listenBreakevenAvailable(fn) {
25567
25940
  backtest$1.loggerService.log(LISTEN_BREAKEVEN_METHOD_NAME);
25568
25941
  return breakevenSubject.subscribe(queued(async (event) => fn(event)));
25569
25942
  }
@@ -25597,7 +25970,7 @@ function listenBreakeven(fn) {
25597
25970
  * cancel();
25598
25971
  * ```
25599
25972
  */
25600
- function listenBreakevenOnce(filterFn, fn) {
25973
+ function listenBreakevenAvailableOnce(filterFn, fn) {
25601
25974
  backtest$1.loggerService.log(LISTEN_BREAKEVEN_ONCE_METHOD_NAME);
25602
25975
  return breakevenSubject.filter(filterFn).once(fn);
25603
25976
  }
@@ -25693,9 +26066,9 @@ function listenRiskOnce(filterFn, fn) {
25693
26066
  * unsubscribe();
25694
26067
  * ```
25695
26068
  */
25696
- function listenPing(fn) {
25697
- backtest$1.loggerService.log(LISTEN_PING_METHOD_NAME);
25698
- return pingSubject.subscribe(queued(async (event) => fn(event)));
26069
+ function listenSchedulePing(fn) {
26070
+ backtest$1.loggerService.log(LISTEN_SCHEDULE_PING_METHOD_NAME);
26071
+ return schedulePingSubject.subscribe(queued(async (event) => fn(event)));
25699
26072
  }
25700
26073
  /**
25701
26074
  * Subscribes to filtered ping events with one-time execution.
@@ -25727,9 +26100,74 @@ function listenPing(fn) {
25727
26100
  * cancel();
25728
26101
  * ```
25729
26102
  */
25730
- function listenPingOnce(filterFn, fn) {
25731
- backtest$1.loggerService.log(LISTEN_PING_ONCE_METHOD_NAME);
25732
- return pingSubject.filter(filterFn).once(fn);
26103
+ function listenSchedulePingOnce(filterFn, fn) {
26104
+ backtest$1.loggerService.log(LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME);
26105
+ return schedulePingSubject.filter(filterFn).once(fn);
26106
+ }
26107
+ /**
26108
+ * Subscribes to active ping events with queued async processing.
26109
+ *
26110
+ * Listens for active pending signal monitoring events emitted every minute.
26111
+ * Useful for tracking active signal lifecycle and implementing dynamic management logic.
26112
+ *
26113
+ * Events are processed sequentially in order received, even if callback is async.
26114
+ * Uses queued wrapper to prevent concurrent execution of the callback.
26115
+ *
26116
+ * @param fn - Callback function to handle active ping events
26117
+ * @returns Unsubscribe function to stop listening
26118
+ *
26119
+ * @example
26120
+ * ```typescript
26121
+ * import { listenActivePing } from "./function/event";
26122
+ *
26123
+ * const unsubscribe = listenActivePing((event) => {
26124
+ * console.log(`[${event.backtest ? "Backtest" : "Live"}] Active Ping`);
26125
+ * console.log(`Symbol: ${event.symbol}, Strategy: ${event.strategyName}`);
26126
+ * console.log(`Signal ID: ${event.data.id}, Position: ${event.data.position}`);
26127
+ * console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
26128
+ * });
26129
+ *
26130
+ * // Later: stop listening
26131
+ * unsubscribe();
26132
+ * ```
26133
+ */
26134
+ function listenActivePing(fn) {
26135
+ backtest$1.loggerService.log(LISTEN_ACTIVE_PING_METHOD_NAME);
26136
+ return activePingSubject.subscribe(queued(async (event) => fn(event)));
26137
+ }
26138
+ /**
26139
+ * Subscribes to filtered active ping events with one-time execution.
26140
+ *
26141
+ * Listens for events matching the filter predicate, then executes callback once
26142
+ * and automatically unsubscribes. Useful for waiting for specific active ping conditions.
26143
+ *
26144
+ * @param filterFn - Predicate to filter which events trigger the callback
26145
+ * @param fn - Callback function to handle the filtered event (called only once)
26146
+ * @returns Unsubscribe function to cancel the listener before it fires
26147
+ *
26148
+ * @example
26149
+ * ```typescript
26150
+ * import { listenActivePingOnce } from "./function/event";
26151
+ *
26152
+ * // Wait for first active ping on BTCUSDT
26153
+ * listenActivePingOnce(
26154
+ * (event) => event.symbol === "BTCUSDT",
26155
+ * (event) => console.log("First BTCUSDT active ping received")
26156
+ * );
26157
+ *
26158
+ * // Wait for active ping in backtest mode
26159
+ * const cancel = listenActivePingOnce(
26160
+ * (event) => event.backtest === true,
26161
+ * (event) => console.log("Backtest active ping received at", new Date(event.timestamp))
26162
+ * );
26163
+ *
26164
+ * // Cancel if needed before event fires
26165
+ * cancel();
26166
+ * ```
26167
+ */
26168
+ function listenActivePingOnce(filterFn, fn) {
26169
+ backtest$1.loggerService.log(LISTEN_ACTIVE_PING_ONCE_METHOD_NAME);
26170
+ return activePingSubject.filter(filterFn).once(fn);
25733
26171
  }
25734
26172
 
25735
26173
  const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
@@ -25799,7 +26237,7 @@ const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
25799
26237
  * // ./dump/strategy/{uuid}/06_llm_output.md (final signal)
25800
26238
  * ```
25801
26239
  */
25802
- async function dumpSignal(signalId, history, signal, outputDir = "./dump/strategy") {
26240
+ async function dumpSignalData(signalId, history, signal, outputDir = "./dump/strategy") {
25803
26241
  backtest$1.loggerService.info(DUMP_SIGNAL_METHOD_NAME, {
25804
26242
  signalId,
25805
26243
  history,
@@ -25819,11 +26257,12 @@ const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
25819
26257
  const BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL = "BacktestUtils.getPendingSignal";
25820
26258
  const BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL = "BacktestUtils.getScheduledSignal";
25821
26259
  const BACKTEST_METHOD_NAME_GET_BREAKEVEN = "BacktestUtils.getBreakeven";
25822
- const BACKTEST_METHOD_NAME_CANCEL = "BacktestUtils.cancel";
25823
- const BACKTEST_METHOD_NAME_PARTIAL_PROFIT = "BacktestUtils.partialProfit";
25824
- const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.partialLoss";
25825
- const BACKTEST_METHOD_NAME_TRAILING_STOP = "BacktestUtils.trailingStop";
25826
- const BACKTEST_METHOD_NAME_TRAILING_PROFIT = "BacktestUtils.trailingTake";
26260
+ const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
26261
+ const BACKTEST_METHOD_NAME_CANCEL = "BacktestUtils.commitCancel";
26262
+ const BACKTEST_METHOD_NAME_PARTIAL_PROFIT = "BacktestUtils.commitPartialProfit";
26263
+ const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.commitPartialLoss";
26264
+ const BACKTEST_METHOD_NAME_TRAILING_STOP = "BacktestUtils.commitTrailingStop";
26265
+ const BACKTEST_METHOD_NAME_TRAILING_PROFIT = "BacktestUtils.commitTrailingTake";
25827
26266
  const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
25828
26267
  /**
25829
26268
  * Internal task function that runs backtest and handles completion.
@@ -26347,14 +26786,14 @@ class BacktestUtils {
26347
26786
  * @example
26348
26787
  * ```typescript
26349
26788
  * // Cancel scheduled signal with custom ID
26350
- * await Backtest.cancel("BTCUSDT", "my-strategy", {
26789
+ * await Backtest.commitCancel("BTCUSDT", "my-strategy", {
26351
26790
  * exchangeName: "binance",
26352
26791
  * frameName: "frame1",
26353
26792
  * strategyName: "my-strategy"
26354
26793
  * }, "manual-cancel-001");
26355
26794
  * ```
26356
26795
  */
26357
- this.cancel = async (symbol, context, cancelId) => {
26796
+ this.commitCancel = async (symbol, context, cancelId) => {
26358
26797
  backtest$1.loggerService.info(BACKTEST_METHOD_NAME_CANCEL, {
26359
26798
  symbol,
26360
26799
  context,
@@ -26392,7 +26831,7 @@ class BacktestUtils {
26392
26831
  * @example
26393
26832
  * ```typescript
26394
26833
  * // Close 30% of LONG position at profit
26395
- * const success = await Backtest.partialProfit("BTCUSDT", 30, 45000, {
26834
+ * const success = await Backtest.commitPartialProfit("BTCUSDT", 30, 45000, {
26396
26835
  * exchangeName: "binance",
26397
26836
  * frameName: "frame1",
26398
26837
  * strategyName: "my-strategy"
@@ -26402,7 +26841,7 @@ class BacktestUtils {
26402
26841
  * }
26403
26842
  * ```
26404
26843
  */
26405
- this.partialProfit = async (symbol, percentToClose, currentPrice, context) => {
26844
+ this.commitPartialProfit = async (symbol, percentToClose, currentPrice, context) => {
26406
26845
  backtest$1.loggerService.info(BACKTEST_METHOD_NAME_PARTIAL_PROFIT, {
26407
26846
  symbol,
26408
26847
  percentToClose,
@@ -26441,7 +26880,7 @@ class BacktestUtils {
26441
26880
  * @example
26442
26881
  * ```typescript
26443
26882
  * // Close 40% of LONG position at loss
26444
- * const success = await Backtest.partialLoss("BTCUSDT", 40, 38000, {
26883
+ * const success = await Backtest.commitPartialLoss("BTCUSDT", 40, 38000, {
26445
26884
  * exchangeName: "binance",
26446
26885
  * frameName: "frame1",
26447
26886
  * strategyName: "my-strategy"
@@ -26451,7 +26890,7 @@ class BacktestUtils {
26451
26890
  * }
26452
26891
  * ```
26453
26892
  */
26454
- this.partialLoss = async (symbol, percentToClose, currentPrice, context) => {
26893
+ this.commitPartialLoss = async (symbol, percentToClose, currentPrice, context) => {
26455
26894
  backtest$1.loggerService.info(BACKTEST_METHOD_NAME_PARTIAL_LOSS, {
26456
26895
  symbol,
26457
26896
  percentToClose,
@@ -26499,7 +26938,7 @@ class BacktestUtils {
26499
26938
  * // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
26500
26939
  *
26501
26940
  * // First call: tighten by 5%
26502
- * await Backtest.trailingStop("BTCUSDT", -5, 102, {
26941
+ * await Backtest.commitTrailingStop("BTCUSDT", -5, 102, {
26503
26942
  * exchangeName: "binance",
26504
26943
  * frameName: "frame1",
26505
26944
  * strategyName: "my-strategy"
@@ -26507,15 +26946,15 @@ class BacktestUtils {
26507
26946
  * // newDistance = 10% - 5% = 5%, newSL = 95
26508
26947
  *
26509
26948
  * // Second call: try weaker protection (smaller percentShift)
26510
- * await Backtest.trailingStop("BTCUSDT", -3, 102, context);
26949
+ * await Backtest.commitTrailingStop("BTCUSDT", -3, 102, context);
26511
26950
  * // SKIPPED: newSL=97 < 95 (worse protection, larger % absorbs smaller)
26512
26951
  *
26513
26952
  * // Third call: stronger protection (larger percentShift)
26514
- * await Backtest.trailingStop("BTCUSDT", -7, 102, context);
26953
+ * await Backtest.commitTrailingStop("BTCUSDT", -7, 102, context);
26515
26954
  * // ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95 (better protection)
26516
26955
  * ```
26517
26956
  */
26518
- this.trailingStop = async (symbol, percentShift, currentPrice, context) => {
26957
+ this.commitTrailingStop = async (symbol, percentShift, currentPrice, context) => {
26519
26958
  backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TRAILING_STOP, {
26520
26959
  symbol,
26521
26960
  percentShift,
@@ -26563,7 +27002,7 @@ class BacktestUtils {
26563
27002
  * // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
26564
27003
  *
26565
27004
  * // First call: bring TP closer by 3%
26566
- * await Backtest.trailingTake("BTCUSDT", -3, 102, {
27005
+ * await Backtest.commitTrailingTake("BTCUSDT", -3, 102, {
26567
27006
  * exchangeName: "binance",
26568
27007
  * frameName: "frame1",
26569
27008
  * strategyName: "my-strategy"
@@ -26571,15 +27010,15 @@ class BacktestUtils {
26571
27010
  * // newDistance = 10% - 3% = 7%, newTP = 107
26572
27011
  *
26573
27012
  * // Second call: try to move TP further (less conservative)
26574
- * await Backtest.trailingTake("BTCUSDT", 2, 102, context);
27013
+ * await Backtest.commitTrailingTake("BTCUSDT", 2, 102, context);
26575
27014
  * // SKIPPED: newTP=112 > 107 (less conservative, larger % absorbs smaller)
26576
27015
  *
26577
27016
  * // Third call: even more conservative
26578
- * await Backtest.trailingTake("BTCUSDT", -5, 102, context);
27017
+ * await Backtest.commitTrailingTake("BTCUSDT", -5, 102, context);
26579
27018
  * // ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107 (more conservative)
26580
27019
  * ```
26581
27020
  */
26582
- this.trailingTake = async (symbol, percentShift, currentPrice, context) => {
27021
+ this.commitTrailingTake = async (symbol, percentShift, currentPrice, context) => {
26583
27022
  backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TRAILING_PROFIT, {
26584
27023
  symbol,
26585
27024
  percentShift,
@@ -26612,7 +27051,7 @@ class BacktestUtils {
26612
27051
  *
26613
27052
  * @example
26614
27053
  * ```typescript
26615
- * const moved = await Backtest.breakeven(
27054
+ * const moved = await Backtest.commitBreakeven(
26616
27055
  * "BTCUSDT",
26617
27056
  * 112,
26618
27057
  * { strategyName: "my-strategy", exchangeName: "binance", frameName: "1h" }
@@ -26620,22 +27059,22 @@ class BacktestUtils {
26620
27059
  * console.log(moved); // true (SL moved to entry price)
26621
27060
  * ```
26622
27061
  */
26623
- this.breakeven = async (symbol, currentPrice, context) => {
26624
- backtest$1.loggerService.info("Backtest.breakeven", {
27062
+ this.commitBreakeven = async (symbol, currentPrice, context) => {
27063
+ backtest$1.loggerService.info(BACKTEST_METHOD_NAME_BREAKEVEN, {
26625
27064
  symbol,
26626
27065
  currentPrice,
26627
27066
  context,
26628
27067
  });
26629
- backtest$1.strategyValidationService.validate(context.strategyName, "Backtest.breakeven");
26630
- backtest$1.exchangeValidationService.validate(context.exchangeName, "Backtest.breakeven");
27068
+ backtest$1.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_BREAKEVEN);
27069
+ backtest$1.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_BREAKEVEN);
26631
27070
  {
26632
27071
  const { riskName, riskList, actions } = backtest$1.strategySchemaService.get(context.strategyName);
26633
27072
  riskName &&
26634
- backtest$1.riskValidationService.validate(riskName, "Backtest.breakeven");
27073
+ backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_BREAKEVEN);
26635
27074
  riskList &&
26636
- riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, "Backtest.breakeven"));
27075
+ riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_BREAKEVEN));
26637
27076
  actions &&
26638
- actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, "Backtest.breakeven"));
27077
+ actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_BREAKEVEN));
26639
27078
  }
26640
27079
  return await backtest$1.strategyCoreService.breakeven(true, symbol, currentPrice, context);
26641
27080
  };
@@ -26807,11 +27246,12 @@ const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
26807
27246
  const LIVE_METHOD_NAME_GET_PENDING_SIGNAL = "LiveUtils.getPendingSignal";
26808
27247
  const LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL = "LiveUtils.getScheduledSignal";
26809
27248
  const LIVE_METHOD_NAME_GET_BREAKEVEN = "LiveUtils.getBreakeven";
26810
- const LIVE_METHOD_NAME_CANCEL = "LiveUtils.cancel";
26811
- const LIVE_METHOD_NAME_PARTIAL_PROFIT = "LiveUtils.partialProfit";
26812
- const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.partialLoss";
26813
- const LIVE_METHOD_NAME_TRAILING_STOP = "LiveUtils.trailingStop";
26814
- const LIVE_METHOD_NAME_TRAILING_PROFIT = "LiveUtils.trailingTake";
27249
+ const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
27250
+ const LIVE_METHOD_NAME_CANCEL = "LiveUtils.commitCancel";
27251
+ const LIVE_METHOD_NAME_PARTIAL_PROFIT = "LiveUtils.commitPartialProfit";
27252
+ const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.commitPartialLoss";
27253
+ const LIVE_METHOD_NAME_TRAILING_STOP = "LiveUtils.commitTrailingStop";
27254
+ const LIVE_METHOD_NAME_TRAILING_PROFIT = "LiveUtils.commitTrailingTake";
26815
27255
  /**
26816
27256
  * Internal task function that runs live trading and handles completion.
26817
27257
  * Consumes live trading results and updates instance state flags.
@@ -27301,14 +27741,14 @@ class LiveUtils {
27301
27741
  * @example
27302
27742
  * ```typescript
27303
27743
  * // Cancel scheduled signal in live trading with custom ID
27304
- * await Live.cancel("BTCUSDT", "my-strategy", {
27744
+ * await Live.commitCancel("BTCUSDT", "my-strategy", {
27305
27745
  * exchangeName: "binance",
27306
27746
  * frameName: "",
27307
27747
  * strategyName: "my-strategy"
27308
27748
  * }, "manual-cancel-001");
27309
27749
  * ```
27310
27750
  */
27311
- this.cancel = async (symbol, context, cancelId) => {
27751
+ this.commitCancel = async (symbol, context, cancelId) => {
27312
27752
  backtest$1.loggerService.info(LIVE_METHOD_NAME_CANCEL, {
27313
27753
  symbol,
27314
27754
  context,
@@ -27347,7 +27787,7 @@ class LiveUtils {
27347
27787
  * @example
27348
27788
  * ```typescript
27349
27789
  * // Close 30% of LONG position at profit
27350
- * const success = await Live.partialProfit("BTCUSDT", 30, 45000, {
27790
+ * const success = await Live.commitPartialProfit("BTCUSDT", 30, 45000, {
27351
27791
  * exchangeName: "binance",
27352
27792
  * strategyName: "my-strategy"
27353
27793
  * });
@@ -27356,7 +27796,7 @@ class LiveUtils {
27356
27796
  * }
27357
27797
  * ```
27358
27798
  */
27359
- this.partialProfit = async (symbol, percentToClose, currentPrice, context) => {
27799
+ this.commitPartialProfit = async (symbol, percentToClose, currentPrice, context) => {
27360
27800
  backtest$1.loggerService.info(LIVE_METHOD_NAME_PARTIAL_PROFIT, {
27361
27801
  symbol,
27362
27802
  percentToClose,
@@ -27396,7 +27836,7 @@ class LiveUtils {
27396
27836
  * @example
27397
27837
  * ```typescript
27398
27838
  * // Close 40% of LONG position at loss
27399
- * const success = await Live.partialLoss("BTCUSDT", 40, 38000, {
27839
+ * const success = await Live.commitPartialLoss("BTCUSDT", 40, 38000, {
27400
27840
  * exchangeName: "binance",
27401
27841
  * strategyName: "my-strategy"
27402
27842
  * });
@@ -27405,7 +27845,7 @@ class LiveUtils {
27405
27845
  * }
27406
27846
  * ```
27407
27847
  */
27408
- this.partialLoss = async (symbol, percentToClose, currentPrice, context) => {
27848
+ this.commitPartialLoss = async (symbol, percentToClose, currentPrice, context) => {
27409
27849
  backtest$1.loggerService.info(LIVE_METHOD_NAME_PARTIAL_LOSS, {
27410
27850
  symbol,
27411
27851
  percentToClose,
@@ -27454,22 +27894,22 @@ class LiveUtils {
27454
27894
  * // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
27455
27895
  *
27456
27896
  * // First call: tighten by 5%
27457
- * const success1 = await Live.trailingStop("BTCUSDT", -5, 102, {
27897
+ * const success1 = await Live.commitTrailingStop("BTCUSDT", -5, 102, {
27458
27898
  * exchangeName: "binance",
27459
27899
  * strategyName: "my-strategy"
27460
27900
  * });
27461
27901
  * // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
27462
27902
  *
27463
27903
  * // Second call: try weaker protection (smaller percentShift)
27464
- * const success2 = await Live.trailingStop("BTCUSDT", -3, 102, context);
27904
+ * const success2 = await Live.commitTrailingStop("BTCUSDT", -3, 102, context);
27465
27905
  * // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
27466
27906
  *
27467
27907
  * // Third call: stronger protection (larger percentShift)
27468
- * const success3 = await Live.trailingStop("BTCUSDT", -7, 102, context);
27908
+ * const success3 = await Live.commitTrailingStop("BTCUSDT", -7, 102, context);
27469
27909
  * // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
27470
27910
  * ```
27471
27911
  */
27472
- this.trailingStop = async (symbol, percentShift, currentPrice, context) => {
27912
+ this.commitTrailingStop = async (symbol, percentShift, currentPrice, context) => {
27473
27913
  backtest$1.loggerService.info(LIVE_METHOD_NAME_TRAILING_STOP, {
27474
27914
  symbol,
27475
27915
  percentShift,
@@ -27518,22 +27958,22 @@ class LiveUtils {
27518
27958
  * // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
27519
27959
  *
27520
27960
  * // First call: bring TP closer by 3%
27521
- * const success1 = await Live.trailingTake("BTCUSDT", -3, 102, {
27961
+ * const success1 = await Live.commitTrailingTake("BTCUSDT", -3, 102, {
27522
27962
  * exchangeName: "binance",
27523
27963
  * strategyName: "my-strategy"
27524
27964
  * });
27525
27965
  * // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
27526
27966
  *
27527
27967
  * // Second call: try to move TP further (less conservative)
27528
- * const success2 = await Live.trailingTake("BTCUSDT", 2, 102, context);
27968
+ * const success2 = await Live.commitTrailingTake("BTCUSDT", 2, 102, context);
27529
27969
  * // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
27530
27970
  *
27531
27971
  * // Third call: even more conservative
27532
- * const success3 = await Live.trailingTake("BTCUSDT", -5, 102, context);
27972
+ * const success3 = await Live.commitTrailingTake("BTCUSDT", -5, 102, context);
27533
27973
  * // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
27534
27974
  * ```
27535
27975
  */
27536
- this.trailingTake = async (symbol, percentShift, currentPrice, context) => {
27976
+ this.commitTrailingTake = async (symbol, percentShift, currentPrice, context) => {
27537
27977
  backtest$1.loggerService.info(LIVE_METHOD_NAME_TRAILING_PROFIT, {
27538
27978
  symbol,
27539
27979
  percentShift,
@@ -27567,7 +28007,7 @@ class LiveUtils {
27567
28007
  *
27568
28008
  * @example
27569
28009
  * ```typescript
27570
- * const moved = await Live.breakeven(
28010
+ * const moved = await Live.commitBreakeven(
27571
28011
  * "BTCUSDT",
27572
28012
  * 112,
27573
28013
  * { strategyName: "my-strategy", exchangeName: "binance" }
@@ -27575,19 +28015,19 @@ class LiveUtils {
27575
28015
  * console.log(moved); // true (SL moved to entry price)
27576
28016
  * ```
27577
28017
  */
27578
- this.breakeven = async (symbol, currentPrice, context) => {
27579
- backtest$1.loggerService.info("Live.breakeven", {
28018
+ this.commitBreakeven = async (symbol, currentPrice, context) => {
28019
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_BREAKEVEN, {
27580
28020
  symbol,
27581
28021
  currentPrice,
27582
28022
  context,
27583
28023
  });
27584
- backtest$1.strategyValidationService.validate(context.strategyName, "Live.breakeven");
27585
- backtest$1.exchangeValidationService.validate(context.exchangeName, "Live.breakeven");
28024
+ backtest$1.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_BREAKEVEN);
28025
+ backtest$1.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_BREAKEVEN);
27586
28026
  {
27587
28027
  const { riskName, riskList, actions } = backtest$1.strategySchemaService.get(context.strategyName);
27588
- riskName && backtest$1.riskValidationService.validate(riskName, "Live.breakeven");
27589
- riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, "Live.breakeven"));
27590
- actions && actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, "Live.breakeven"));
28028
+ riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_BREAKEVEN);
28029
+ riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_BREAKEVEN));
28030
+ actions && actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, LIVE_METHOD_NAME_BREAKEVEN));
27591
28031
  }
27592
28032
  return await backtest$1.strategyCoreService.breakeven(false, symbol, currentPrice, {
27593
28033
  strategyName: context.strategyName,
@@ -29415,10 +29855,15 @@ class ExchangeInstance {
29415
29855
  const whenTimestamp = when.getTime();
29416
29856
  const sinceTimestamp = since.getTime();
29417
29857
  const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= whenTimestamp);
29418
- if (filteredData.length < limit) {
29419
- backtest$1.loggerService.warn(`ExchangeInstance Expected ${limit} candles, got ${filteredData.length}`);
29858
+ // Apply distinct by timestamp to remove duplicates
29859
+ const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
29860
+ if (filteredData.length !== uniqueData.length) {
29861
+ backtest$1.loggerService.warn(`ExchangeInstance Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
29420
29862
  }
29421
- return filteredData;
29863
+ if (uniqueData.length < limit) {
29864
+ backtest$1.loggerService.warn(`ExchangeInstance Expected ${limit} candles, got ${uniqueData.length}`);
29865
+ }
29866
+ return uniqueData;
29422
29867
  };
29423
29868
  /**
29424
29869
  * Calculates VWAP (Volume Weighted Average Price) from last N 1m candles.
@@ -30597,10 +31042,11 @@ const METHOD_NAME_INIT = "ActionBase.init";
30597
31042
  const METHOD_NAME_EVENT = "ActionBase.event";
30598
31043
  const METHOD_NAME_SIGNAL_LIVE = "ActionBase.signalLive";
30599
31044
  const METHOD_NAME_SIGNAL_BACKTEST = "ActionBase.signalBacktest";
30600
- const METHOD_NAME_BREAKEVEN = "ActionBase.breakeven";
30601
- const METHOD_NAME_PARTIAL_PROFIT = "ActionBase.partialProfit";
30602
- const METHOD_NAME_PARTIAL_LOSS = "ActionBase.partialLoss";
30603
- const METHOD_NAME_PING = "ActionBase.ping";
31045
+ const METHOD_NAME_BREAKEVEN_AVAILABLE = "ActionBase.breakevenAvailable";
31046
+ const METHOD_NAME_PARTIAL_PROFIT_AVAILABLE = "ActionBase.partialProfitAvailable";
31047
+ const METHOD_NAME_PARTIAL_LOSS_AVAILABLE = "ActionBase.partialLossAvailable";
31048
+ const METHOD_NAME_PING_SCHEDULED = "ActionBase.pingScheduled";
31049
+ const METHOD_NAME_PING_ACTIVE = "ActionBase.pingActive";
30604
31050
  const METHOD_NAME_RISK_REJECTION = "ActionBase.riskRejection";
30605
31051
  const METHOD_NAME_DISPOSE = "ActionBase.dispose";
30606
31052
  const DEFAULT_SOURCE = "default";
@@ -30631,10 +31077,11 @@ const DEFAULT_SOURCE = "default";
30631
31077
  * - signal() - Called on every tick/candle (all modes)
30632
31078
  * - signalLive() - Called only in live mode
30633
31079
  * - signalBacktest() - Called only in backtest mode
30634
- * - breakeven() - Called when SL moved to entry
30635
- * - partialProfit() - Called on profit milestones (10%, 20%, etc.)
30636
- * - partialLoss() - Called on loss milestones (-10%, -20%, etc.)
30637
- * - ping() - Called every minute during scheduled signal monitoring
31080
+ * - breakevenAvailable() - Called when SL moved to entry
31081
+ * - partialProfitAvailable() - Called on profit milestones (10%, 20%, etc.)
31082
+ * - partialLossAvailable() - Called on loss milestones (-10%, -20%, etc.)
31083
+ * - pingScheduled() - Called every minute during scheduled signal monitoring
31084
+ * - pingActive() - Called every minute during active pending signal monitoring
30638
31085
  * - riskRejection() - Called when signal rejected by risk management
30639
31086
  *
30640
31087
  * @example
@@ -30675,7 +31122,7 @@ const DEFAULT_SOURCE = "default";
30675
31122
  * }
30676
31123
  *
30677
31124
  * // Register the action
30678
- * addAction({
31125
+ * addActionSchema({
30679
31126
  * actionName: "telegram-notifier",
30680
31127
  * handler: TelegramNotifier
30681
31128
  * });
@@ -30717,11 +31164,13 @@ class ActionBase {
30717
31164
  * @param strategyName - Strategy identifier this action is attached to
30718
31165
  * @param frameName - Timeframe identifier this action is attached to
30719
31166
  * @param actionName - Action identifier
31167
+ * @param backtest - If running in backtest
30720
31168
  */
30721
- constructor(strategyName, frameName, actionName) {
31169
+ constructor(strategyName, frameName, actionName, backtest) {
30722
31170
  this.strategyName = strategyName;
30723
31171
  this.frameName = frameName;
30724
31172
  this.actionName = actionName;
31173
+ this.backtest = backtest;
30725
31174
  }
30726
31175
  /**
30727
31176
  * Initializes the action handler.
@@ -30845,7 +31294,7 @@ class ActionBase {
30845
31294
  * Called once per signal when price moves far enough to cover fees and slippage.
30846
31295
  * Breakeven threshold: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2 + CC_BREAKEVEN_THRESHOLD
30847
31296
  *
30848
- * Triggered by: ActionCoreService.breakeven() via BreakevenConnectionService
31297
+ * Triggered by: ActionCoreService.breakevenAvailable() via BreakevenConnectionService
30849
31298
  * Source: breakevenSubject.next() in CREATE_COMMIT_BREAKEVEN_FN callback
30850
31299
  * Frequency: Once per signal when threshold reached
30851
31300
  *
@@ -30855,7 +31304,7 @@ class ActionBase {
30855
31304
  *
30856
31305
  * @example
30857
31306
  * ```typescript
30858
- * async breakeven(event: BreakevenContract) {
31307
+ * async breakevenAvailable(event: BreakevenContract) {
30859
31308
  * await this.telegram.send(
30860
31309
  * `[${event.strategyName}] Breakeven reached! ` +
30861
31310
  * `Signal: ${event.data.side} @ ${event.currentPrice}`
@@ -30863,8 +31312,8 @@ class ActionBase {
30863
31312
  * }
30864
31313
  * ```
30865
31314
  */
30866
- breakeven(event, source = DEFAULT_SOURCE) {
30867
- backtest$1.loggerService.info(METHOD_NAME_BREAKEVEN, {
31315
+ breakevenAvailable(event, source = DEFAULT_SOURCE) {
31316
+ backtest$1.loggerService.info(METHOD_NAME_BREAKEVEN_AVAILABLE, {
30868
31317
  event,
30869
31318
  source,
30870
31319
  });
@@ -30875,7 +31324,7 @@ class ActionBase {
30875
31324
  * Called once per profit level per signal (deduplicated).
30876
31325
  * Use to track profit milestones and adjust position management.
30877
31326
  *
30878
- * Triggered by: ActionCoreService.partialProfit() via PartialConnectionService
31327
+ * Triggered by: ActionCoreService.partialProfitAvailable() via PartialConnectionService
30879
31328
  * Source: partialProfitSubject.next() in CREATE_COMMIT_PROFIT_FN callback
30880
31329
  * Frequency: Once per profit level per signal
30881
31330
  *
@@ -30885,7 +31334,7 @@ class ActionBase {
30885
31334
  *
30886
31335
  * @example
30887
31336
  * ```typescript
30888
- * async partialProfit(event: PartialProfitContract) {
31337
+ * async partialProfitAvailable(event: PartialProfitContract) {
30889
31338
  * await this.telegram.send(
30890
31339
  * `[${event.strategyName}] Profit ${event.level}% reached! ` +
30891
31340
  * `Current price: ${event.currentPrice}`
@@ -30894,8 +31343,8 @@ class ActionBase {
30894
31343
  * }
30895
31344
  * ```
30896
31345
  */
30897
- partialProfit(event, source = DEFAULT_SOURCE) {
30898
- backtest$1.loggerService.info(METHOD_NAME_PARTIAL_PROFIT, {
31346
+ partialProfitAvailable(event, source = DEFAULT_SOURCE) {
31347
+ backtest$1.loggerService.info(METHOD_NAME_PARTIAL_PROFIT_AVAILABLE, {
30899
31348
  event,
30900
31349
  source,
30901
31350
  });
@@ -30906,7 +31355,7 @@ class ActionBase {
30906
31355
  * Called once per loss level per signal (deduplicated).
30907
31356
  * Use to track loss milestones and implement risk management actions.
30908
31357
  *
30909
- * Triggered by: ActionCoreService.partialLoss() via PartialConnectionService
31358
+ * Triggered by: ActionCoreService.partialLossAvailable() via PartialConnectionService
30910
31359
  * Source: partialLossSubject.next() in CREATE_COMMIT_LOSS_FN callback
30911
31360
  * Frequency: Once per loss level per signal
30912
31361
  *
@@ -30916,7 +31365,7 @@ class ActionBase {
30916
31365
  *
30917
31366
  * @example
30918
31367
  * ```typescript
30919
- * async partialLoss(event: PartialLossContract) {
31368
+ * async partialLossAvailable(event: PartialLossContract) {
30920
31369
  * await this.telegram.send(
30921
31370
  * `[${event.strategyName}] Loss ${event.level}% reached! ` +
30922
31371
  * `Current price: ${event.currentPrice}`
@@ -30925,37 +31374,66 @@ class ActionBase {
30925
31374
  * }
30926
31375
  * ```
30927
31376
  */
30928
- partialLoss(event, source = DEFAULT_SOURCE) {
30929
- backtest$1.loggerService.info(METHOD_NAME_PARTIAL_LOSS, {
31377
+ partialLossAvailable(event, source = DEFAULT_SOURCE) {
31378
+ backtest$1.loggerService.info(METHOD_NAME_PARTIAL_LOSS_AVAILABLE, {
30930
31379
  event,
30931
31380
  source,
30932
31381
  });
30933
31382
  }
30934
31383
  /**
30935
- * Handles ping events during scheduled signal monitoring.
31384
+ * Handles scheduled ping events during scheduled signal monitoring.
30936
31385
  *
30937
31386
  * Called every minute while a scheduled signal is waiting for activation.
30938
31387
  * Use to monitor pending signals and track wait time.
30939
31388
  *
30940
- * Triggered by: ActionCoreService.ping() via StrategyConnectionService
30941
- * Source: pingSubject.next() in CREATE_COMMIT_PING_FN callback
31389
+ * Triggered by: ActionCoreService.pingScheduled() via StrategyConnectionService
31390
+ * Source: schedulePingSubject.next() in CREATE_COMMIT_SCHEDULE_PING_FN callback
30942
31391
  * Frequency: Every minute while scheduled signal is waiting
30943
31392
  *
30944
- * Default implementation: Logs ping event.
31393
+ * Default implementation: Logs scheduled ping event.
30945
31394
  *
30946
31395
  * @param event - Scheduled signal monitoring data with symbol, strategy info, signal data, timestamp
30947
31396
  *
30948
31397
  * @example
30949
31398
  * ```typescript
30950
- * ping(event: PingContract) {
31399
+ * pingScheduled(event: SchedulePingContract) {
30951
31400
  * const waitTime = Date.now() - event.data.timestampScheduled;
30952
31401
  * const waitMinutes = Math.floor(waitTime / 60000);
30953
31402
  * console.log(`Scheduled signal waiting ${waitMinutes} minutes`);
30954
31403
  * }
30955
31404
  * ```
30956
31405
  */
30957
- ping(event, source = DEFAULT_SOURCE) {
30958
- backtest$1.loggerService.info(METHOD_NAME_PING, {
31406
+ pingScheduled(event, source = DEFAULT_SOURCE) {
31407
+ backtest$1.loggerService.info(METHOD_NAME_PING_SCHEDULED, {
31408
+ event,
31409
+ source,
31410
+ });
31411
+ }
31412
+ /**
31413
+ * Handles active ping events during active pending signal monitoring.
31414
+ *
31415
+ * Called every minute while a pending signal is active (position open).
31416
+ * Use to monitor active positions and track lifecycle.
31417
+ *
31418
+ * Triggered by: ActionCoreService.pingActive() via StrategyConnectionService
31419
+ * Source: activePingSubject.next() in CREATE_COMMIT_ACTIVE_PING_FN callback
31420
+ * Frequency: Every minute while pending signal is active
31421
+ *
31422
+ * Default implementation: Logs active ping event.
31423
+ *
31424
+ * @param event - Active pending signal monitoring data with symbol, strategy info, signal data, timestamp
31425
+ *
31426
+ * @example
31427
+ * ```typescript
31428
+ * pingActive(event: ActivePingContract) {
31429
+ * const holdTime = Date.now() - event.data.pendingAt;
31430
+ * const holdMinutes = Math.floor(holdTime / 60000);
31431
+ * console.log(`Active signal holding ${holdMinutes} minutes`);
31432
+ * }
31433
+ * ```
31434
+ */
31435
+ pingActive(event, source = DEFAULT_SOURCE) {
31436
+ backtest$1.loggerService.info(METHOD_NAME_PING_ACTIVE, {
30959
31437
  event,
30960
31438
  source,
30961
31439
  });
@@ -31074,4 +31552,4 @@ const set = (object, path, value) => {
31074
31552
  }
31075
31553
  };
31076
31554
 
31077
- export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker, addAction, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, breakeven, cancel, dumpSignal, emitters, formatPrice, formatQuantity, get, getAction, getAveragePrice, getCandles, getColumns, getConfig, getCurrentTimeframe, getDate, getDefaultColumns, getDefaultConfig, getExchange, getFrame, getMode, getOptimizer, getOrderBook, getRisk, getSizing, getStrategy, getWalker, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenBreakeven, listenBreakevenOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideAction, overrideExchange, overrideFrame, overrideOptimizer, overrideRisk, overrideSizing, overrideStrategy, overrideWalker, partialLoss, partialProfit, roundTicks, set, setColumns, setConfig, setLogger, stop, trailingStop, trailingTake, validate };
31555
+ export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addOptimizerSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, commitBreakeven, commitCancel, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpSignalData, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getOptimizerSchema, getOrderBook, 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, roundTicks, set, setColumns, setConfig, setLogger, stop, validate };