backtest-kit 13.0.0 → 13.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.mjs CHANGED
@@ -6817,6 +6817,119 @@ const validateScheduledSignal = (signal, currentPrice) => {
6817
6817
  }
6818
6818
  };
6819
6819
 
6820
+ /**
6821
+ * Validates ISignalDto returned by getSignal, branching on the same logic as ClientStrategy GET_SIGNAL_FN.
6822
+ *
6823
+ * When priceOpen is provided:
6824
+ * - If currentPrice already reached priceOpen (shouldActivateImmediately) →
6825
+ * validates as pending: currentPrice must be between SL and TP
6826
+ * - Otherwise → validates as scheduled: priceOpen must be between SL and TP
6827
+ *
6828
+ * When priceOpen is absent:
6829
+ * - Validates as pending: currentPrice must be between SL and TP
6830
+ *
6831
+ * Checks:
6832
+ * - currentPrice is a finite positive number
6833
+ * - Common signal fields via validateCommonSignal (position, prices, TP/SL relationships, minuteEstimatedTime)
6834
+ * - Position-specific immediate-close protection (pending) or activation-close protection (scheduled)
6835
+ *
6836
+ * @param signal - Signal DTO returned by getSignal
6837
+ * @param currentPrice - Current market price at the moment of signal creation
6838
+ * @returns true if signal is valid, false if validation errors were found (errors logged to console.error)
6839
+ */
6840
+ const validateSignal = (signal, currentPrice) => {
6841
+ const errors = [];
6842
+ // ЗАЩИТА ОТ NaN/Infinity: currentPrice должна быть конечным числом
6843
+ {
6844
+ if (typeof currentPrice !== "number") {
6845
+ errors.push(`currentPrice must be a number type, got ${currentPrice} (${typeof currentPrice})`);
6846
+ }
6847
+ if (!isFinite(currentPrice)) {
6848
+ errors.push(`currentPrice must be a finite number, got ${currentPrice} (${typeof currentPrice})`);
6849
+ }
6850
+ if (isFinite(currentPrice) && currentPrice <= 0) {
6851
+ errors.push(`currentPrice must be positive, got ${currentPrice}`);
6852
+ }
6853
+ }
6854
+ if (errors.length > 0) {
6855
+ console.error(`Invalid signal for ${signal.position} position (${signal.symbol || "empty symbol"}):\n${errors.join("\n")}`);
6856
+ return false;
6857
+ }
6858
+ try {
6859
+ validateCommonSignal(signal);
6860
+ }
6861
+ catch (error) {
6862
+ console.error(getErrorMessage(error));
6863
+ return false;
6864
+ }
6865
+ // Определяем режим валидации по той же логике что в GET_SIGNAL_FN:
6866
+ // - нет priceOpen → pending (открывается по currentPrice)
6867
+ // - priceOpen задан и уже достигнут (shouldActivateImmediately) → pending
6868
+ // - priceOpen задан и ещё не достигнут → scheduled
6869
+ const hasPriceOpen = signal.priceOpen !== undefined;
6870
+ const shouldActivateImmediately = hasPriceOpen && ((signal.position === "long" && currentPrice <= signal.priceOpen) ||
6871
+ (signal.position === "short" && currentPrice >= signal.priceOpen));
6872
+ const isScheduled = hasPriceOpen && !shouldActivateImmediately;
6873
+ if (isScheduled) {
6874
+ // Scheduled: priceOpen должен быть между SL и TP (активация не даст моментального закрытия)
6875
+ if (signal.position === "long") {
6876
+ if (isFinite(signal.priceOpen)) {
6877
+ if (signal.priceOpen <= signal.priceStopLoss) {
6878
+ errors.push(`Long scheduled: priceOpen (${signal.priceOpen}) <= priceStopLoss (${signal.priceStopLoss}). ` +
6879
+ `Signal would be immediately cancelled on activation. Cannot activate position that is already stopped out.`);
6880
+ }
6881
+ if (signal.priceOpen >= signal.priceTakeProfit) {
6882
+ errors.push(`Long scheduled: priceOpen (${signal.priceOpen}) >= priceTakeProfit (${signal.priceTakeProfit}). ` +
6883
+ `Signal would close immediately on activation. This is logically impossible for LONG position.`);
6884
+ }
6885
+ }
6886
+ }
6887
+ if (signal.position === "short") {
6888
+ if (isFinite(signal.priceOpen)) {
6889
+ if (signal.priceOpen >= signal.priceStopLoss) {
6890
+ errors.push(`Short scheduled: priceOpen (${signal.priceOpen}) >= priceStopLoss (${signal.priceStopLoss}). ` +
6891
+ `Signal would be immediately cancelled on activation. Cannot activate position that is already stopped out.`);
6892
+ }
6893
+ if (signal.priceOpen <= signal.priceTakeProfit) {
6894
+ errors.push(`Short scheduled: priceOpen (${signal.priceOpen}) <= priceTakeProfit (${signal.priceTakeProfit}). ` +
6895
+ `Signal would close immediately on activation. This is logically impossible for SHORT position.`);
6896
+ }
6897
+ }
6898
+ }
6899
+ }
6900
+ else {
6901
+ // Pending: currentPrice должна быть между SL и TP (позиция не закроется сразу после открытия)
6902
+ if (signal.position === "long") {
6903
+ if (isFinite(currentPrice)) {
6904
+ if (currentPrice <= signal.priceStopLoss) {
6905
+ errors.push(`Long immediate: currentPrice (${currentPrice}) <= priceStopLoss (${signal.priceStopLoss}). ` +
6906
+ `Signal would be immediately closed by stop loss. Cannot open position that is already stopped out.`);
6907
+ }
6908
+ if (currentPrice >= signal.priceTakeProfit) {
6909
+ errors.push(`Long immediate: currentPrice (${currentPrice}) >= priceTakeProfit (${signal.priceTakeProfit}). ` +
6910
+ `Signal would be immediately closed by take profit. The profit opportunity has already passed.`);
6911
+ }
6912
+ }
6913
+ }
6914
+ if (signal.position === "short") {
6915
+ if (isFinite(currentPrice)) {
6916
+ if (currentPrice >= signal.priceStopLoss) {
6917
+ errors.push(`Short immediate: currentPrice (${currentPrice}) >= priceStopLoss (${signal.priceStopLoss}). ` +
6918
+ `Signal would be immediately closed by stop loss. Cannot open position that is already stopped out.`);
6919
+ }
6920
+ if (currentPrice <= signal.priceTakeProfit) {
6921
+ errors.push(`Short immediate: currentPrice (${currentPrice}) <= priceTakeProfit (${signal.priceTakeProfit}). ` +
6922
+ `Signal would be immediately closed by take profit. The profit opportunity has already passed.`);
6923
+ }
6924
+ }
6925
+ }
6926
+ }
6927
+ if (errors.length > 0) {
6928
+ console.error(`Invalid signal for ${signal.position} position (${signal.symbol || "empty symbol"}):\n${errors.join("\n")}`);
6929
+ }
6930
+ return !errors.length;
6931
+ };
6932
+
6820
6933
  const INTERVAL_MINUTES$8 = {
6821
6934
  "1m": 1,
6822
6935
  "3m": 3,
@@ -7283,11 +7396,27 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
7283
7396
  self._lastSignalTimestamp = alignedTime;
7284
7397
  }
7285
7398
  const currentPrice = await self.params.exchange.getAveragePrice(self.params.execution.context.symbol);
7286
- const timeoutMs = GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS * 1000;
7287
- const signal = await Promise.race([
7288
- self.params.getSignal(self.params.execution.context.symbol, self.params.execution.context.when, currentPrice),
7289
- sleep(timeoutMs).then(() => TIMEOUT_SYMBOL$1),
7290
- ]);
7399
+ // PRIORITY: a user-queued createPending/createScheduled DTO (set out of async-hooks
7400
+ // context) takes precedence over params.getSignal. When present it is consumed once
7401
+ // here — the slot is cleared and the snapshot rewritten — and then flows through the
7402
+ // exact same pipeline (risk check, priceOpen branching, onSignalSync on open) as a
7403
+ // signal returned by getSignal would.
7404
+ let signal;
7405
+ {
7406
+ if (!self._userSignal) {
7407
+ const timeoutMs = GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS * 1000;
7408
+ signal = await Promise.race([
7409
+ self.params.getSignal(self.params.execution.context.symbol, self.params.execution.context.when, currentPrice),
7410
+ sleep(timeoutMs).then(() => TIMEOUT_SYMBOL$1),
7411
+ ]);
7412
+ }
7413
+ if (self._userSignal) {
7414
+ signal = self._userSignal;
7415
+ self._userSignal = null;
7416
+ await PERSIST_STRATEGY_FN(self);
7417
+ }
7418
+ self._userSignal = null;
7419
+ }
7291
7420
  if (typeof signal === "symbol") {
7292
7421
  throw new Error(`Timeout for ${self.params.method.context.strategyName} symbol=${self.params.execution.context.symbol}`);
7293
7422
  }
@@ -7442,18 +7571,20 @@ const WAIT_FOR_INIT_FN$4 = async (self) => {
7442
7571
  self._lastPendingId = recentSignal.id;
7443
7572
  }
7444
7573
  }
7445
- // Restore deferred strategy state (commit queue + deferred user actions) so any
7574
+ // Read deferred strategy state (commit queue + deferred user actions) so any
7446
7575
  // confirmed-but-not-yet-forwarded broker operation survives a live crash and is
7447
- // re-drained on the next tick. Placed before the pending/scheduled restore which
7448
- // may early-return on exchange/strategy mismatch.
7449
- {
7450
- const strategyData = await PersistStrategyAdapter.readStrategyData(self.params.execution.context.symbol, self.params.strategyName, self.params.exchangeName);
7451
- if (strategyData) {
7452
- self._commitQueue = strategyData.commitQueue ?? [];
7453
- self._closedSignal = strategyData.closedSignal;
7454
- self._cancelledSignal = strategyData.cancelledSignal;
7455
- self._activatedSignal = strategyData.activatedSignal;
7456
- }
7576
+ // re-drained on the next tick. Read here, before the pending restore which may
7577
+ // early-return on exchange/strategy mismatch.
7578
+ const strategyData = await PersistStrategyAdapter.readStrategyData(self.params.execution.context.symbol, self.params.strategyName, self.params.exchangeName);
7579
+ if (strategyData) {
7580
+ // Deferred user actions are restored unconditionally: a deferred close belongs to
7581
+ // an already-cleared _pendingSignal, and cancel/activate belong to the scheduled
7582
+ // signal — none of them are tied to the currently-pending signal's id. A queued
7583
+ // createSignal is a not-yet-consumed signal source and likewise stands on its own.
7584
+ self._userSignal = strategyData.createSignal;
7585
+ self._closedSignal = strategyData.closedSignal;
7586
+ self._cancelledSignal = strategyData.cancelledSignal;
7587
+ self._activatedSignal = strategyData.activatedSignal;
7457
7588
  }
7458
7589
  // Restore pending signal
7459
7590
  const pendingSignal = await PersistSignalAdapter.readSignalData(self.params.execution.context.symbol, self.params.strategyName, self.params.exchangeName);
@@ -7471,6 +7602,14 @@ const WAIT_FOR_INIT_FN$4 = async (self) => {
7471
7602
  pendingSignal.minuteEstimatedTime = Infinity;
7472
7603
  }
7473
7604
  self._pendingSignal = pendingSignal;
7605
+ // Restore the commit queue only if the snapshot belongs to this exact pending
7606
+ // signal (pendingSignalId === restored id). The queue holds confirmed-but-not-yet
7607
+ // forwarded broker ops (average-buy / partial-* / trailing-* / breakeven) that are
7608
+ // always tied to the active position; a mismatch means the snapshot is stale and
7609
+ // the queue is dropped to avoid replaying ops against the wrong position.
7610
+ if (strategyData && strategyData.pendingSignalId === pendingSignal.id) {
7611
+ self._commitQueue = strategyData.commitQueue ?? [];
7612
+ }
7474
7613
  // Call onActive callback for restored signal
7475
7614
  const currentPrice = await self.params.exchange.getAveragePrice(self.params.execution.context.symbol);
7476
7615
  const currentTime = self.params.execution.context.when.getTime();
@@ -7520,6 +7659,8 @@ const PERSIST_STRATEGY_FN = async (self) => {
7520
7659
  return;
7521
7660
  }
7522
7661
  await PersistStrategyAdapter.writeStrategyData({
7662
+ pendingSignalId: self._pendingSignal?.id ?? null,
7663
+ createSignal: self._userSignal,
7523
7664
  commitQueue: self._commitQueue,
7524
7665
  closedSignal: self._closedSignal,
7525
7666
  cancelledSignal: self._cancelledSignal,
@@ -9655,6 +9796,13 @@ class ClientStrategy {
9655
9796
  this._cancelledSignal = null;
9656
9797
  this._closedSignal = null;
9657
9798
  this._activatedSignal = null;
9799
+ /**
9800
+ * User-supplied signal DTO to be consumed by the next GET_SIGNAL_FN tick instead of
9801
+ * params.getSignal. Set via createSignal. When non-null, params.getSignal is NOT called
9802
+ * and the existing pipeline (priceOpen decides pending vs scheduled, onSignalSync on open)
9803
+ * is reused.
9804
+ */
9805
+ this._userSignal = null;
9658
9806
  /** Queue for commit events to be processed in tick()/backtest() with proper timestamp */
9659
9807
  this._commitQueue = [];
9660
9808
  /**
@@ -11305,6 +11453,88 @@ class ClientStrategy {
11305
11453
  await PERSIST_STRATEGY_FN(this);
11306
11454
  // Commit will be emitted in tick() with correct currentTime
11307
11455
  }
11456
+ /**
11457
+ * Queues a user-supplied signal DTO to be consumed by the next tick instead of
11458
+ * params.getSignal.
11459
+ *
11460
+ * Works OUT of the async-hooks execution context (uses this.params.symbol directly).
11461
+ * priceOpen decides the outcome in the existing pipeline: when omitted the position opens
11462
+ * immediately at currentPrice; when provided the pipeline opens immediately if priceOpen is
11463
+ * already reached, otherwise registers a scheduled (priceOpen-awaiting) signal. On the next
11464
+ * tick GET_SIGNAL_FN consumes _userSignal and runs the normal pipeline, so onSignalSync
11465
+ * delivery is checked by OPEN_NEW_PENDING_SIGNAL_FN exactly as for a getSignal-produced signal.
11466
+ *
11467
+ * Validation (BEFORE any state mutation — a rejected call stores nothing):
11468
+ * - The DTO must pass the intrinsic shape/price checks.
11469
+ * - There must be NO signal already in flight: no active pending signal, no scheduled signal,
11470
+ * no already-queued createSignal, and no deferred activate/close/cancel awaiting drain.
11471
+ * Creating a new signal on top of an existing one is rejected.
11472
+ *
11473
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
11474
+ * @param currentPrice - Current market price (priceOpen fallback for immediate signals)
11475
+ * @param dto - Signal DTO to open (priceOpen optional)
11476
+ * @returns Promise that resolves when the DTO is queued (and persisted in live mode)
11477
+ * @throws {Error} If the DTO is invalid or a signal/deferred action is already in flight
11478
+ */
11479
+ async createSignal(symbol, currentPrice, dto) {
11480
+ this.params.logger.debug("ClientStrategy createSignal", { symbol, currentPrice });
11481
+ // Validate BEFORE mutating state — a bad DTO or a busy strategy must store nothing.
11482
+ // Reuse validateSignal (the canonical getSignal-output validator, branching
11483
+ // pending-vs-scheduled by the same rule as GET_SIGNAL_FN). It forwards to
11484
+ // validateCommonSignal which requires priceOpen and minuteEstimatedTime, so normalize the
11485
+ // DTO to the defaults GET_SIGNAL_FN would apply (priceOpen → currentPrice for an immediate
11486
+ // signal, minuteEstimatedTime → CC_MAX_SIGNAL_LIFETIME_MINUTES). The original dto stored in
11487
+ // _userSignal is left untouched so GET_SIGNAL_FN applies its own defaults on consume.
11488
+ if (!validateSignal({
11489
+ ...dto,
11490
+ priceOpen: dto.priceOpen ?? currentPrice,
11491
+ minuteEstimatedTime: dto.minuteEstimatedTime ?? GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES,
11492
+ }, currentPrice)) {
11493
+ throw new Error(`ClientStrategy createSignal: invalid signal DTO for symbol=${symbol}`);
11494
+ }
11495
+ // Reject if any signal is already in flight or any deferred action is awaiting drain.
11496
+ if (this._pendingSignal) {
11497
+ throw new Error(`ClientStrategy createSignal: a pending signal already exists for symbol=${symbol}`);
11498
+ }
11499
+ if (this._scheduledSignal) {
11500
+ throw new Error(`ClientStrategy createSignal: a scheduled signal already exists for symbol=${symbol}`);
11501
+ }
11502
+ if (this._userSignal) {
11503
+ throw new Error(`ClientStrategy createSignal: a signal is already queued for creation for symbol=${symbol}`);
11504
+ }
11505
+ if (this._activatedSignal) {
11506
+ throw new Error(`ClientStrategy createSignal: a scheduled activation is pending for symbol=${symbol}`);
11507
+ }
11508
+ if (this._closedSignal) {
11509
+ throw new Error(`ClientStrategy createSignal: a pending close is awaiting for symbol=${symbol}`);
11510
+ }
11511
+ if (this._cancelledSignal) {
11512
+ throw new Error(`ClientStrategy createSignal: a scheduled cancel is awaiting for symbol=${symbol}`);
11513
+ }
11514
+ this._userSignal = dto;
11515
+ await PERSIST_STRATEGY_FN(this);
11516
+ }
11517
+ /**
11518
+ * Returns the deferred strategy-state snapshot exactly as it would be written to persist on
11519
+ * this iteration: the in-memory _userSignal, _commitQueue and deferred user-action flags,
11520
+ * plus the current pending signal id.
11521
+ *
11522
+ * Synchronous in-memory read (no disk access), so it works OUT of the async-hooks context.
11523
+ *
11524
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
11525
+ * @returns The current StrategyData snapshot held in memory
11526
+ */
11527
+ async getStatus(symbol) {
11528
+ this.params.logger.debug("ClientStrategy getStatus", { symbol });
11529
+ return {
11530
+ pendingSignalId: this._pendingSignal?.id ?? null,
11531
+ createSignal: this._userSignal,
11532
+ commitQueue: this._commitQueue,
11533
+ closedSignal: this._closedSignal,
11534
+ cancelledSignal: this._cancelledSignal,
11535
+ activatedSignal: this._activatedSignal,
11536
+ };
11537
+ }
11308
11538
  /**
11309
11539
  * Validates preconditions for partialProfit without mutating state.
11310
11540
  *
@@ -12603,6 +12833,8 @@ class MergeRisk {
12603
12833
 
12604
12834
  /** Default interval for strategies that do not specify one */
12605
12835
  const STRATEGY_DEFAULT_INTERVAL = "1m";
12836
+ /** If not specified strategy will not open positions until createSignal is called manually */
12837
+ const STRATEGY_DEFAULT_SIGNAL = () => null;
12606
12838
  /**
12607
12839
  * If syncSubject listener or any registered action throws, it means the signal was not properly synchronized
12608
12840
  * to the exchange (e.g. limit order failed to fill).
@@ -13036,7 +13268,7 @@ class StrategyConnectionService {
13036
13268
  * @returns Configured ClientStrategy instance
13037
13269
  */
13038
13270
  this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$A(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
13039
- const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
13271
+ const { riskName = "", riskList = [], getSignal = STRATEGY_DEFAULT_SIGNAL, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
13040
13272
  return new ClientStrategy({
13041
13273
  symbol,
13042
13274
  interval,
@@ -14055,6 +14287,47 @@ class StrategyConnectionService {
14055
14287
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
14056
14288
  await strategy.closePending(symbol, backtest, payload);
14057
14289
  };
14290
+ /**
14291
+ * Queues a user-supplied signal DTO to be consumed by the next tick instead of getSignal.
14292
+ *
14293
+ * Delegates to ClientStrategy.createSignal(). Validated and rejected if a signal/deferred
14294
+ * action is already in flight. Works out of the async-hooks execution context.
14295
+ *
14296
+ * @param backtest - Whether running in backtest mode
14297
+ * @param symbol - Trading pair symbol
14298
+ * @param dto - Signal DTO to open
14299
+ * @param context - Context with strategyName, exchangeName, frameName
14300
+ * @returns Promise that resolves when the DTO is queued
14301
+ */
14302
+ this.createSignal = async (backtest, symbol, dto, context) => {
14303
+ this.loggerService.log("strategyConnectionService createSignal", {
14304
+ symbol,
14305
+ context,
14306
+ backtest,
14307
+ });
14308
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
14309
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
14310
+ await strategy.createSignal(symbol, currentPrice, dto);
14311
+ };
14312
+ /**
14313
+ * Returns the in-memory deferred strategy-state snapshot for this iteration.
14314
+ *
14315
+ * Delegates to ClientStrategy.getStatus(). Synchronous in-memory read; works out of context.
14316
+ *
14317
+ * @param backtest - Whether running in backtest mode
14318
+ * @param symbol - Trading pair symbol
14319
+ * @param context - Context with strategyName, exchangeName, frameName
14320
+ * @returns Promise resolving to the current StrategyData snapshot
14321
+ */
14322
+ this.getStatus = async (backtest, symbol, context) => {
14323
+ this.loggerService.log("strategyConnectionService getStatus", {
14324
+ symbol,
14325
+ context,
14326
+ backtest,
14327
+ });
14328
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
14329
+ return await strategy.getStatus(symbol);
14330
+ };
14058
14331
  /**
14059
14332
  * Checks whether `partialProfit` would succeed without executing it.
14060
14333
  * Delegates to `ClientStrategy.validatePartialProfit()` — no throws, pure boolean result.
@@ -17658,6 +17931,47 @@ class StrategyCoreService {
17658
17931
  await this.validate(context);
17659
17932
  return await this.strategyConnectionService.closePending(backtest, symbol, context, payload);
17660
17933
  };
17934
+ /**
17935
+ * Queues a user-supplied signal DTO to be consumed by the next tick instead of getSignal.
17936
+ *
17937
+ * Validates the context, then delegates to StrategyConnectionService.createSignal().
17938
+ * Rejected if a signal or deferred action is already in flight. Does not require execution context.
17939
+ *
17940
+ * @param backtest - Whether running in backtest mode
17941
+ * @param symbol - Trading pair symbol
17942
+ * @param dto - Signal DTO to open
17943
+ * @param context - Context with strategyName, exchangeName, frameName
17944
+ * @returns Promise that resolves when the DTO is queued
17945
+ */
17946
+ this.createSignal = async (backtest, symbol, dto, context) => {
17947
+ this.loggerService.log("strategyCoreService createSignal", {
17948
+ symbol,
17949
+ context,
17950
+ backtest,
17951
+ });
17952
+ await this.validate(context);
17953
+ return await this.strategyConnectionService.createSignal(backtest, symbol, dto, context);
17954
+ };
17955
+ /**
17956
+ * Returns the in-memory deferred strategy-state snapshot for this iteration.
17957
+ *
17958
+ * Validates the context, then delegates to StrategyConnectionService.getStatus().
17959
+ * Does not require execution context.
17960
+ *
17961
+ * @param backtest - Whether running in backtest mode
17962
+ * @param symbol - Trading pair symbol
17963
+ * @param context - Context with strategyName, exchangeName, frameName
17964
+ * @returns Promise resolving to the current StrategyStatus snapshot
17965
+ */
17966
+ this.getStatus = async (backtest, symbol, context) => {
17967
+ this.loggerService.log("strategyCoreService getStatus", {
17968
+ symbol,
17969
+ context,
17970
+ backtest,
17971
+ });
17972
+ await this.validate(context);
17973
+ return await this.strategyConnectionService.getStatus(backtest, symbol, context);
17974
+ };
17661
17975
  /**
17662
17976
  * Disposes the ClientStrategy instance for the given context.
17663
17977
  *
@@ -19182,7 +19496,7 @@ class StrategySchemaService {
19182
19496
  if (strategySchema.interval && typeof strategySchema.interval !== "string") {
19183
19497
  throw new Error(`strategy schema validation failed: invalid interval for strategyName=${strategySchema.strategyName}`);
19184
19498
  }
19185
- if (typeof strategySchema.getSignal !== "function") {
19499
+ if (strategySchema.getSignal && typeof strategySchema.getSignal !== "function") {
19186
19500
  throw new Error(`strategy schema validation failed: missing getSignal for strategyName=${strategySchema.strategyName}`);
19187
19501
  }
19188
19502
  };
@@ -42896,6 +43210,8 @@ const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOve
42896
43210
  const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
42897
43211
  const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
42898
43212
  const COMMIT_SIGNAL_NOTIFY_METHOD_NAME = "strategy.commitSignalNotify";
43213
+ const CREATE_SIGNAL_METHOD_NAME = "strategy.createSignal";
43214
+ const GET_STRATEGY_STATUS_METHOD_NAME = "strategy.getStrategyStatus";
42899
43215
  /**
42900
43216
  * Cancels the scheduled signal without stopping the strategy.
42901
43217
  *
@@ -44960,6 +45276,69 @@ async function commitSignalNotify(symbol, payload = {}) {
44960
45276
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
44961
45277
  await bt.notificationHelperService.commitSignalNotify(payload, symbol, currentPrice, { strategyName, exchangeName, frameName }, isBacktest);
44962
45278
  }
45279
+ /**
45280
+ * Queues a user-supplied signal DTO to be consumed by the next tick instead of params.getSignal.
45281
+ *
45282
+ * priceOpen decides the outcome via the existing pipeline: omitted → opens immediately at the
45283
+ * current price; provided → opens immediately if already reached, otherwise registers a
45284
+ * scheduled (priceOpen-awaiting) signal. The DTO is validated (reusing validateSignal) and the
45285
+ * call is rejected if a signal or deferred action is already in flight.
45286
+ *
45287
+ * Automatically detects backtest/live mode from execution context.
45288
+ *
45289
+ * @param symbol - Trading pair symbol
45290
+ * @param dto - Signal DTO to open (priceOpen optional)
45291
+ * @returns Promise that resolves when the DTO is queued
45292
+ *
45293
+ * @example
45294
+ * ```typescript
45295
+ * import { createSignal } from "backtest-kit";
45296
+ *
45297
+ * // Open immediately at current price
45298
+ * await createSignal("BTCUSDT", { position: "long", priceTakeProfit: 110, priceStopLoss: 90 });
45299
+ * ```
45300
+ */
45301
+ async function createSignal(symbol, dto) {
45302
+ bt.loggerService.info(CREATE_SIGNAL_METHOD_NAME, { symbol });
45303
+ if (!ExecutionContextService.hasContext()) {
45304
+ throw new Error("createSignal requires an execution context");
45305
+ }
45306
+ if (!MethodContextService.hasContext()) {
45307
+ throw new Error("createSignal requires a method context");
45308
+ }
45309
+ const { backtest: isBacktest } = bt.executionContextService.context;
45310
+ const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
45311
+ await bt.strategyCoreService.createSignal(isBacktest, symbol, dto, { exchangeName, frameName, strategyName });
45312
+ }
45313
+ /**
45314
+ * Returns the in-memory deferred strategy-state snapshot for the current iteration: the queued
45315
+ * createSignal, commit queue and deferred user-action flags, plus the current pending signal id.
45316
+ *
45317
+ * Automatically detects backtest/live mode from execution context.
45318
+ *
45319
+ * @param symbol - Trading pair symbol
45320
+ * @returns Promise resolving to the current StrategyStatus snapshot
45321
+ *
45322
+ * @example
45323
+ * ```typescript
45324
+ * import { getStrategyStatus } from "backtest-kit";
45325
+ *
45326
+ * const status = await getStrategyStatus("BTCUSDT");
45327
+ * console.log(status.pendingSignalId, status.commitQueue.length);
45328
+ * ```
45329
+ */
45330
+ async function getStrategyStatus(symbol) {
45331
+ bt.loggerService.info(GET_STRATEGY_STATUS_METHOD_NAME, { symbol });
45332
+ if (!ExecutionContextService.hasContext()) {
45333
+ throw new Error("getStrategyStatus requires an execution context");
45334
+ }
45335
+ if (!MethodContextService.hasContext()) {
45336
+ throw new Error("getStrategyStatus requires a method context");
45337
+ }
45338
+ const { backtest: isBacktest } = bt.executionContextService.context;
45339
+ const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
45340
+ return await bt.strategyCoreService.getStatus(isBacktest, symbol, { exchangeName, frameName, strategyName });
45341
+ }
44963
45342
 
44964
45343
  const STOP_STRATEGY_METHOD_NAME = "control.stopStrategy";
44965
45344
  /**
@@ -46631,6 +47010,8 @@ const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPosi
46631
47010
  const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
46632
47011
  const BACKTEST_METHOD_NAME_CANCEL_SCHEDULED = "Backtest.commitCancelScheduled";
46633
47012
  const BACKTEST_METHOD_NAME_CLOSE_PENDING = "Backtest.commitClosePending";
47013
+ const BACKTEST_METHOD_NAME_CREATE_SIGNAL = "Backtest.createSignal";
47014
+ const BACKTEST_METHOD_NAME_GET_STRATEGY_STATUS = "Backtest.getStrategyStatus";
46634
47015
  const BACKTEST_METHOD_NAME_PARTIAL_PROFIT = "BacktestUtils.commitPartialProfit";
46635
47016
  const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.commitPartialLoss";
46636
47017
  const BACKTEST_METHOD_NAME_PARTIAL_PROFIT_COST = "BacktestUtils.commitPartialProfitCost";
@@ -48335,6 +48716,53 @@ class BacktestUtils {
48335
48716
  }
48336
48717
  await bt.strategyCoreService.closePending(true, symbol, context, payload);
48337
48718
  };
48719
+ /**
48720
+ * Queues a user-supplied signal DTO to be consumed by the next backtest tick instead of
48721
+ * params.getSignal.
48722
+ *
48723
+ * priceOpen decides the outcome via the existing pipeline: omitted → opens immediately at the
48724
+ * current price; provided → opens immediately if already reached, otherwise registers a
48725
+ * scheduled signal. Validated, and rejected if a signal or deferred action is already in flight.
48726
+ *
48727
+ * @param symbol - Trading pair symbol
48728
+ * @param context - Execution context with strategyName, exchangeName, and frameName
48729
+ * @param dto - Signal DTO to open (priceOpen optional)
48730
+ * @returns Promise that resolves when the DTO is queued
48731
+ */
48732
+ this.createSignal = async (symbol, context, dto) => {
48733
+ bt.loggerService.info(BACKTEST_METHOD_NAME_CREATE_SIGNAL, {
48734
+ symbol,
48735
+ context,
48736
+ });
48737
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CREATE_SIGNAL);
48738
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CREATE_SIGNAL);
48739
+ {
48740
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
48741
+ riskName &&
48742
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_CREATE_SIGNAL);
48743
+ riskList &&
48744
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_CREATE_SIGNAL));
48745
+ actions &&
48746
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CREATE_SIGNAL));
48747
+ }
48748
+ await bt.strategyCoreService.createSignal(true, symbol, dto, context);
48749
+ };
48750
+ /**
48751
+ * Returns the in-memory deferred strategy-state snapshot for the current backtest iteration.
48752
+ *
48753
+ * @param symbol - Trading pair symbol
48754
+ * @param context - Execution context with strategyName, exchangeName, and frameName
48755
+ * @returns Promise resolving to the current StrategyStatus snapshot
48756
+ */
48757
+ this.getStrategyStatus = async (symbol, context) => {
48758
+ bt.loggerService.info(BACKTEST_METHOD_NAME_GET_STRATEGY_STATUS, {
48759
+ symbol,
48760
+ context,
48761
+ });
48762
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_STRATEGY_STATUS);
48763
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_STRATEGY_STATUS);
48764
+ return await bt.strategyCoreService.getStatus(true, symbol, context);
48765
+ };
48338
48766
  /**
48339
48767
  * Executes partial close at profit level (moving toward TP).
48340
48768
  *
@@ -49293,6 +49721,8 @@ const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPart
49293
49721
  const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
49294
49722
  const LIVE_METHOD_NAME_CANCEL_SCHEDULED = "Live.cancelScheduled";
49295
49723
  const LIVE_METHOD_NAME_CLOSE_PENDING = "Live.closePending";
49724
+ const LIVE_METHOD_NAME_CREATE_SIGNAL = "Live.createSignal";
49725
+ const LIVE_METHOD_NAME_GET_STRATEGY_STATUS = "Live.getStrategyStatus";
49296
49726
  const LIVE_METHOD_NAME_PARTIAL_PROFIT = "LiveUtils.commitPartialProfit";
49297
49727
  const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.commitPartialLoss";
49298
49728
  const LIVE_METHOD_NAME_PARTIAL_PROFIT_COST = "LiveUtils.commitPartialProfitCost";
@@ -51170,6 +51600,61 @@ class LiveUtils {
51170
51600
  frameName: "",
51171
51601
  }, payload);
51172
51602
  };
51603
+ /**
51604
+ * Queues a user-supplied signal DTO to be consumed by the next live tick instead of
51605
+ * params.getSignal.
51606
+ *
51607
+ * priceOpen decides the outcome via the existing pipeline: omitted → opens immediately at the
51608
+ * current price; provided → opens immediately if already reached, otherwise registers a
51609
+ * scheduled signal. Validated, and rejected if a signal or deferred action is already in flight.
51610
+ *
51611
+ * @param symbol - Trading pair symbol
51612
+ * @param context - Execution context with strategyName and exchangeName
51613
+ * @param dto - Signal DTO to open (priceOpen optional)
51614
+ * @returns Promise that resolves when the DTO is queued
51615
+ */
51616
+ this.createSignal = async (symbol, context, dto) => {
51617
+ bt.loggerService.info(LIVE_METHOD_NAME_CREATE_SIGNAL, {
51618
+ symbol,
51619
+ context,
51620
+ });
51621
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CREATE_SIGNAL);
51622
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CREATE_SIGNAL);
51623
+ {
51624
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
51625
+ riskName &&
51626
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_CREATE_SIGNAL);
51627
+ riskList &&
51628
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_CREATE_SIGNAL));
51629
+ actions &&
51630
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_CREATE_SIGNAL));
51631
+ }
51632
+ await bt.strategyCoreService.createSignal(false, symbol, dto, {
51633
+ strategyName: context.strategyName,
51634
+ exchangeName: context.exchangeName,
51635
+ frameName: "",
51636
+ });
51637
+ };
51638
+ /**
51639
+ * Returns the in-memory deferred strategy-state snapshot for the current live iteration.
51640
+ *
51641
+ * @param symbol - Trading pair symbol
51642
+ * @param context - Execution context with strategyName and exchangeName
51643
+ * @returns Promise resolving to the current StrategyStatus snapshot
51644
+ */
51645
+ this.getStrategyStatus = async (symbol, context) => {
51646
+ bt.loggerService.info(LIVE_METHOD_NAME_GET_STRATEGY_STATUS, {
51647
+ symbol,
51648
+ context,
51649
+ });
51650
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_STRATEGY_STATUS);
51651
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_STRATEGY_STATUS);
51652
+ return await bt.strategyCoreService.getStatus(false, symbol, {
51653
+ strategyName: context.strategyName,
51654
+ exchangeName: context.exchangeName,
51655
+ frameName: "",
51656
+ });
51657
+ };
51173
51658
  /**
51174
51659
  * Executes partial close at profit level (moving toward TP).
51175
51660
  *
@@ -67816,117 +68301,4 @@ const percentValue = (yesterdayValue, todayValue) => {
67816
68301
  return yesterdayValue / todayValue - 1;
67817
68302
  };
67818
68303
 
67819
- /**
67820
- * Validates ISignalDto returned by getSignal, branching on the same logic as ClientStrategy GET_SIGNAL_FN.
67821
- *
67822
- * When priceOpen is provided:
67823
- * - If currentPrice already reached priceOpen (shouldActivateImmediately) →
67824
- * validates as pending: currentPrice must be between SL and TP
67825
- * - Otherwise → validates as scheduled: priceOpen must be between SL and TP
67826
- *
67827
- * When priceOpen is absent:
67828
- * - Validates as pending: currentPrice must be between SL and TP
67829
- *
67830
- * Checks:
67831
- * - currentPrice is a finite positive number
67832
- * - Common signal fields via validateCommonSignal (position, prices, TP/SL relationships, minuteEstimatedTime)
67833
- * - Position-specific immediate-close protection (pending) or activation-close protection (scheduled)
67834
- *
67835
- * @param signal - Signal DTO returned by getSignal
67836
- * @param currentPrice - Current market price at the moment of signal creation
67837
- * @returns true if signal is valid, false if validation errors were found (errors logged to console.error)
67838
- */
67839
- const validateSignal = (signal, currentPrice) => {
67840
- const errors = [];
67841
- // ЗАЩИТА ОТ NaN/Infinity: currentPrice должна быть конечным числом
67842
- {
67843
- if (typeof currentPrice !== "number") {
67844
- errors.push(`currentPrice must be a number type, got ${currentPrice} (${typeof currentPrice})`);
67845
- }
67846
- if (!isFinite(currentPrice)) {
67847
- errors.push(`currentPrice must be a finite number, got ${currentPrice} (${typeof currentPrice})`);
67848
- }
67849
- if (isFinite(currentPrice) && currentPrice <= 0) {
67850
- errors.push(`currentPrice must be positive, got ${currentPrice}`);
67851
- }
67852
- }
67853
- if (errors.length > 0) {
67854
- console.error(`Invalid signal for ${signal.position} position (${signal.symbol || "empty symbol"}):\n${errors.join("\n")}`);
67855
- return false;
67856
- }
67857
- try {
67858
- validateCommonSignal(signal);
67859
- }
67860
- catch (error) {
67861
- console.error(getErrorMessage(error));
67862
- return false;
67863
- }
67864
- // Определяем режим валидации по той же логике что в GET_SIGNAL_FN:
67865
- // - нет priceOpen → pending (открывается по currentPrice)
67866
- // - priceOpen задан и уже достигнут (shouldActivateImmediately) → pending
67867
- // - priceOpen задан и ещё не достигнут → scheduled
67868
- const hasPriceOpen = signal.priceOpen !== undefined;
67869
- const shouldActivateImmediately = hasPriceOpen && ((signal.position === "long" && currentPrice <= signal.priceOpen) ||
67870
- (signal.position === "short" && currentPrice >= signal.priceOpen));
67871
- const isScheduled = hasPriceOpen && !shouldActivateImmediately;
67872
- if (isScheduled) {
67873
- // Scheduled: priceOpen должен быть между SL и TP (активация не даст моментального закрытия)
67874
- if (signal.position === "long") {
67875
- if (isFinite(signal.priceOpen)) {
67876
- if (signal.priceOpen <= signal.priceStopLoss) {
67877
- errors.push(`Long scheduled: priceOpen (${signal.priceOpen}) <= priceStopLoss (${signal.priceStopLoss}). ` +
67878
- `Signal would be immediately cancelled on activation. Cannot activate position that is already stopped out.`);
67879
- }
67880
- if (signal.priceOpen >= signal.priceTakeProfit) {
67881
- errors.push(`Long scheduled: priceOpen (${signal.priceOpen}) >= priceTakeProfit (${signal.priceTakeProfit}). ` +
67882
- `Signal would close immediately on activation. This is logically impossible for LONG position.`);
67883
- }
67884
- }
67885
- }
67886
- if (signal.position === "short") {
67887
- if (isFinite(signal.priceOpen)) {
67888
- if (signal.priceOpen >= signal.priceStopLoss) {
67889
- errors.push(`Short scheduled: priceOpen (${signal.priceOpen}) >= priceStopLoss (${signal.priceStopLoss}). ` +
67890
- `Signal would be immediately cancelled on activation. Cannot activate position that is already stopped out.`);
67891
- }
67892
- if (signal.priceOpen <= signal.priceTakeProfit) {
67893
- errors.push(`Short scheduled: priceOpen (${signal.priceOpen}) <= priceTakeProfit (${signal.priceTakeProfit}). ` +
67894
- `Signal would close immediately on activation. This is logically impossible for SHORT position.`);
67895
- }
67896
- }
67897
- }
67898
- }
67899
- else {
67900
- // Pending: currentPrice должна быть между SL и TP (позиция не закроется сразу после открытия)
67901
- if (signal.position === "long") {
67902
- if (isFinite(currentPrice)) {
67903
- if (currentPrice <= signal.priceStopLoss) {
67904
- errors.push(`Long immediate: currentPrice (${currentPrice}) <= priceStopLoss (${signal.priceStopLoss}). ` +
67905
- `Signal would be immediately closed by stop loss. Cannot open position that is already stopped out.`);
67906
- }
67907
- if (currentPrice >= signal.priceTakeProfit) {
67908
- errors.push(`Long immediate: currentPrice (${currentPrice}) >= priceTakeProfit (${signal.priceTakeProfit}). ` +
67909
- `Signal would be immediately closed by take profit. The profit opportunity has already passed.`);
67910
- }
67911
- }
67912
- }
67913
- if (signal.position === "short") {
67914
- if (isFinite(currentPrice)) {
67915
- if (currentPrice >= signal.priceStopLoss) {
67916
- errors.push(`Short immediate: currentPrice (${currentPrice}) >= priceStopLoss (${signal.priceStopLoss}). ` +
67917
- `Signal would be immediately closed by stop loss. Cannot open position that is already stopped out.`);
67918
- }
67919
- if (currentPrice <= signal.priceTakeProfit) {
67920
- errors.push(`Short immediate: currentPrice (${currentPrice}) <= priceTakeProfit (${signal.priceTakeProfit}). ` +
67921
- `Signal would be immediately closed by take profit. The profit opportunity has already passed.`);
67922
- }
67923
- }
67924
- }
67925
- }
67926
- if (errors.length > 0) {
67927
- console.error(`Invalid signal for ${signal.position} position (${signal.symbol || "empty symbol"}):\n${errors.join("\n")}`);
67928
- }
67929
- return !errors.length;
67930
- };
67931
-
67932
- export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Cron, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Lookup, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, PersistStrategyAdapter, PersistStrategyInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, beginContext, beginTime, cacheCandles, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getPriceScale, getRawCandles, getRiskSchema, getRuntimeInfo, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, intervalStepMs, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenAfterEnd, listenAfterEndOnce, listenBacktestProgress, listenBeforeStart, listenBeforeStartOnce, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toPlainString, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCandles, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, waitForReady, warmCandles, writeMemory };
68304
+ export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Cron, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Lookup, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, PersistStrategyAdapter, PersistStrategyInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, beginContext, beginTime, cacheCandles, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignal, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getPriceScale, getRawCandles, getRiskSchema, getRuntimeInfo, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getStrategyStatus, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, intervalStepMs, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenAfterEnd, listenAfterEndOnce, listenBacktestProgress, listenBeforeStart, listenBeforeStartOnce, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toPlainString, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCandles, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, waitForReady, warmCandles, writeMemory };