backtest-kit 11.8.0 → 12.0.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.
Files changed (6) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1996 -1996
  3. package/build/index.cjs +661 -136
  4. package/build/index.mjs +660 -137
  5. package/package.json +86 -86
  6. package/types.d.ts +199 -40
package/build/index.mjs CHANGED
@@ -4661,7 +4661,7 @@ const LOGGER_SERVICE$8 = new LoggerService();
4661
4661
  * @param backtest - `true` for backtest, `false` for live.
4662
4662
  * @returns Colon-joined composite key.
4663
4663
  */
4664
- const CREATE_KEY_FN$y = (symbol, strategyName, exchangeName, frameName, backtest) => {
4664
+ const CREATE_KEY_FN$B = (symbol, strategyName, exchangeName, frameName, backtest) => {
4665
4665
  const parts = [symbol, strategyName, exchangeName];
4666
4666
  if (frameName)
4667
4667
  parts.push(frameName);
@@ -4704,7 +4704,7 @@ class LookupUtils {
4704
4704
  LOGGER_SERVICE$8.info(METHOD_NAME_ADD_ACTIVITY, {
4705
4705
  activity,
4706
4706
  });
4707
- const key = CREATE_KEY_FN$y(activity.symbol, activity.context.strategyName, activity.context.exchangeName, activity.context.frameName, activity.backtest);
4707
+ const key = CREATE_KEY_FN$B(activity.symbol, activity.context.strategyName, activity.context.exchangeName, activity.context.frameName, activity.backtest);
4708
4708
  this._lookupMap.set(key, activity);
4709
4709
  };
4710
4710
  /**
@@ -4718,7 +4718,7 @@ class LookupUtils {
4718
4718
  LOGGER_SERVICE$8.info(METHOD_NAME_REMOVE_ACTIVITY, {
4719
4719
  activity,
4720
4720
  });
4721
- const key = CREATE_KEY_FN$y(activity.symbol, activity.context.strategyName, activity.context.exchangeName, activity.context.frameName, activity.backtest);
4721
+ const key = CREATE_KEY_FN$B(activity.symbol, activity.context.strategyName, activity.context.exchangeName, activity.context.frameName, activity.backtest);
4722
4722
  this._lookupMap.delete(key);
4723
4723
  };
4724
4724
  /**
@@ -12336,7 +12336,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
12336
12336
  * @param backtest - Whether running in backtest mode
12337
12337
  * @returns Unique string key for memoization
12338
12338
  */
12339
- const CREATE_KEY_FN$x = (symbol, strategyName, exchangeName, frameName, backtest) => {
12339
+ const CREATE_KEY_FN$A = (symbol, strategyName, exchangeName, frameName, backtest) => {
12340
12340
  const parts = [symbol, strategyName, exchangeName];
12341
12341
  if (frameName)
12342
12342
  parts.push(frameName);
@@ -12638,7 +12638,7 @@ class StrategyConnectionService {
12638
12638
  * @param backtest - Whether running in backtest mode
12639
12639
  * @returns Configured ClientStrategy instance
12640
12640
  */
12641
- this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$x(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
12641
+ this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$A(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
12642
12642
  const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
12643
12643
  return new ClientStrategy({
12644
12644
  symbol,
@@ -13600,7 +13600,7 @@ class StrategyConnectionService {
13600
13600
  }
13601
13601
  return;
13602
13602
  }
13603
- const key = CREATE_KEY_FN$x(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
13603
+ const key = CREATE_KEY_FN$A(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
13604
13604
  if (!this.getStrategy.has(key)) {
13605
13605
  return;
13606
13606
  }
@@ -14146,6 +14146,7 @@ class ClientFrame {
14146
14146
  }
14147
14147
  }
14148
14148
 
14149
+ const DEFAULT_INTERVAL = "1m";
14149
14150
  /**
14150
14151
  * Connection service routing frame operations to correct ClientFrame instance.
14151
14152
  *
@@ -14189,7 +14190,7 @@ class FrameConnectionService {
14189
14190
  logger: this.loggerService,
14190
14191
  startDate,
14191
14192
  endDate,
14192
- interval,
14193
+ interval: interval || DEFAULT_INTERVAL,
14193
14194
  callbacks,
14194
14195
  });
14195
14196
  });
@@ -14848,7 +14849,7 @@ class ClientRisk {
14848
14849
  * @param backtest - Whether running in backtest mode
14849
14850
  * @returns Unique string key for memoization
14850
14851
  */
14851
- const CREATE_KEY_FN$w = (riskName, exchangeName, frameName, backtest) => {
14852
+ const CREATE_KEY_FN$z = (riskName, exchangeName, frameName, backtest) => {
14852
14853
  const parts = [riskName, exchangeName];
14853
14854
  if (frameName)
14854
14855
  parts.push(frameName);
@@ -14948,7 +14949,7 @@ class RiskConnectionService {
14948
14949
  * @param backtest - True if backtest mode, false if live mode
14949
14950
  * @returns Configured ClientRisk instance
14950
14951
  */
14951
- this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$w(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
14952
+ this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$z(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
14952
14953
  const schema = this.riskSchemaService.get(riskName);
14953
14954
  return new ClientRisk({
14954
14955
  ...schema,
@@ -15038,7 +15039,7 @@ class RiskConnectionService {
15038
15039
  payload,
15039
15040
  });
15040
15041
  if (payload) {
15041
- const key = CREATE_KEY_FN$w(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
15042
+ const key = CREATE_KEY_FN$z(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
15042
15043
  this.getRisk.clear(key);
15043
15044
  }
15044
15045
  else {
@@ -16157,7 +16158,7 @@ class ClientAction {
16157
16158
  * @param backtest - Whether running in backtest mode
16158
16159
  * @returns Unique string key for memoization
16159
16160
  */
16160
- const CREATE_KEY_FN$v = (actionName, strategyName, exchangeName, frameName, backtest) => {
16161
+ const CREATE_KEY_FN$y = (actionName, strategyName, exchangeName, frameName, backtest) => {
16161
16162
  const parts = [actionName, strategyName, exchangeName];
16162
16163
  if (frameName)
16163
16164
  parts.push(frameName);
@@ -16209,7 +16210,7 @@ class ActionConnectionService {
16209
16210
  * @param backtest - True if backtest mode, false if live mode
16210
16211
  * @returns Configured ClientAction instance
16211
16212
  */
16212
- this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$v(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
16213
+ this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$y(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
16213
16214
  const schema = this.actionSchemaService.get(actionName);
16214
16215
  return new ClientAction({
16215
16216
  ...schema,
@@ -16435,7 +16436,7 @@ class ActionConnectionService {
16435
16436
  await Promise.all(actions.map(async (action) => await action.dispose()));
16436
16437
  return;
16437
16438
  }
16438
- const key = CREATE_KEY_FN$v(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
16439
+ const key = CREATE_KEY_FN$y(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
16439
16440
  if (!this.getAction.has(key)) {
16440
16441
  return;
16441
16442
  }
@@ -16446,14 +16447,14 @@ class ActionConnectionService {
16446
16447
  }
16447
16448
  }
16448
16449
 
16449
- const METHOD_NAME_VALIDATE$3 = "exchangeCoreService validate";
16450
+ const METHOD_NAME_VALIDATE$6 = "exchangeCoreService validate";
16450
16451
  /**
16451
16452
  * Creates a unique key for memoizing validate calls.
16452
16453
  * Key format: "exchangeName"
16453
16454
  * @param exchangeName - Exchange name
16454
16455
  * @returns Unique string key for memoization
16455
16456
  */
16456
- const CREATE_KEY_FN$u = (exchangeName) => {
16457
+ const CREATE_KEY_FN$x = (exchangeName) => {
16457
16458
  return exchangeName;
16458
16459
  };
16459
16460
  /**
@@ -16477,11 +16478,11 @@ class ExchangeCoreService {
16477
16478
  * @param exchangeName - Name of the exchange to validate
16478
16479
  * @returns Promise that resolves when validation is complete
16479
16480
  */
16480
- this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$u(exchangeName), async (exchangeName) => {
16481
- this.loggerService.log(METHOD_NAME_VALIDATE$3, {
16481
+ this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$x(exchangeName), async (exchangeName) => {
16482
+ this.loggerService.log(METHOD_NAME_VALIDATE$6, {
16482
16483
  exchangeName,
16483
16484
  });
16484
- this.exchangeValidationService.validate(exchangeName, METHOD_NAME_VALIDATE$3);
16485
+ this.exchangeValidationService.validate(exchangeName, METHOD_NAME_VALIDATE$6);
16485
16486
  });
16486
16487
  /**
16487
16488
  * Fetches historical candles with execution context.
@@ -16750,14 +16751,14 @@ class ExchangeCoreService {
16750
16751
  }
16751
16752
  }
16752
16753
 
16753
- const METHOD_NAME_VALIDATE$2 = "strategyCoreService validate";
16754
+ const METHOD_NAME_VALIDATE$5 = "strategyCoreService validate";
16754
16755
  /**
16755
16756
  * Creates a unique key for memoizing validate calls.
16756
16757
  * Key format: "strategyName:exchangeName:frameName"
16757
16758
  * @param context - Execution context with strategyName, exchangeName, frameName
16758
16759
  * @returns Unique string key for memoization
16759
16760
  */
16760
- const CREATE_KEY_FN$t = (context) => {
16761
+ const CREATE_KEY_FN$w = (context) => {
16761
16762
  const parts = [context.strategyName, context.exchangeName];
16762
16763
  if (context.frameName)
16763
16764
  parts.push(context.frameName);
@@ -16780,6 +16781,7 @@ class StrategyCoreService {
16780
16781
  this.strategyValidationService = inject(TYPES.strategyValidationService);
16781
16782
  this.exchangeValidationService = inject(TYPES.exchangeValidationService);
16782
16783
  this.frameValidationService = inject(TYPES.frameValidationService);
16784
+ this.actionValidationService = inject(TYPES.actionValidationService);
16783
16785
  /**
16784
16786
  * Validates strategy and associated risk configuration.
16785
16787
  *
@@ -16789,16 +16791,17 @@ class StrategyCoreService {
16789
16791
  * @param context - Execution context with strategyName, exchangeName, frameName
16790
16792
  * @returns Promise that resolves when validation is complete
16791
16793
  */
16792
- this.validate = memoize(([context]) => CREATE_KEY_FN$t(context), async (context) => {
16793
- this.loggerService.log(METHOD_NAME_VALIDATE$2, {
16794
+ this.validate = memoize(([context]) => CREATE_KEY_FN$w(context), async (context) => {
16795
+ this.loggerService.log(METHOD_NAME_VALIDATE$5, {
16794
16796
  context,
16795
16797
  });
16796
- const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
16797
- this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE$2);
16798
- this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$2);
16799
- context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$2);
16800
- riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$2);
16801
- riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$2));
16798
+ const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
16799
+ this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE$5);
16800
+ this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$5);
16801
+ context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$5);
16802
+ riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$5);
16803
+ riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$5));
16804
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_VALIDATE$5));
16802
16805
  });
16803
16806
  /**
16804
16807
  * Retrieves the currently active pending signal for the symbol.
@@ -18159,7 +18162,7 @@ class SizingGlobalService {
18159
18162
  * @param context - Context with riskName, exchangeName, frameName
18160
18163
  * @returns Unique string key for memoization
18161
18164
  */
18162
- const CREATE_KEY_FN$s = (context) => {
18165
+ const CREATE_KEY_FN$v = (context) => {
18163
18166
  const parts = [context.riskName, context.exchangeName];
18164
18167
  if (context.frameName)
18165
18168
  parts.push(context.frameName);
@@ -18185,7 +18188,7 @@ class RiskGlobalService {
18185
18188
  * @param payload - Payload with riskName, exchangeName and frameName
18186
18189
  * @returns Promise that resolves when validation is complete
18187
18190
  */
18188
- this.validate = memoize(([context]) => CREATE_KEY_FN$s(context), async (context) => {
18191
+ this.validate = memoize(([context]) => CREATE_KEY_FN$v(context), async (context) => {
18189
18192
  this.loggerService.log("riskGlobalService validate", {
18190
18193
  context,
18191
18194
  });
@@ -18277,14 +18280,14 @@ class RiskGlobalService {
18277
18280
  }
18278
18281
  }
18279
18282
 
18280
- const METHOD_NAME_VALIDATE$1 = "actionCoreService validate";
18283
+ const METHOD_NAME_VALIDATE$4 = "actionCoreService validate";
18281
18284
  /**
18282
18285
  * Creates a unique key for memoizing validate calls.
18283
18286
  * Key format: "strategyName:exchangeName:frameName"
18284
18287
  * @param context - Execution context with strategyName, exchangeName, frameName
18285
18288
  * @returns Unique string key for memoization
18286
18289
  */
18287
- const CREATE_KEY_FN$r = (context) => {
18290
+ const CREATE_KEY_FN$u = (context) => {
18288
18291
  const parts = [context.strategyName, context.exchangeName];
18289
18292
  if (context.frameName)
18290
18293
  parts.push(context.frameName);
@@ -18328,17 +18331,17 @@ class ActionCoreService {
18328
18331
  * @param context - Strategy execution context with strategyName, exchangeName and frameName
18329
18332
  * @returns Promise that resolves when all validations complete
18330
18333
  */
18331
- this.validate = memoize(([context]) => CREATE_KEY_FN$r(context), async (context) => {
18332
- this.loggerService.log(METHOD_NAME_VALIDATE$1, {
18334
+ this.validate = memoize(([context]) => CREATE_KEY_FN$u(context), async (context) => {
18335
+ this.loggerService.log(METHOD_NAME_VALIDATE$4, {
18333
18336
  context,
18334
18337
  });
18335
18338
  const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
18336
- this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE$1);
18337
- this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$1);
18338
- context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$1);
18339
- riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$1);
18340
- riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$1));
18341
- actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_VALIDATE$1));
18339
+ this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE$4);
18340
+ this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$4);
18341
+ context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$4);
18342
+ riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$4);
18343
+ riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$4));
18344
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_VALIDATE$4));
18342
18345
  });
18343
18346
  /**
18344
18347
  * Initializes all ClientAction instances for the strategy.
@@ -18834,8 +18837,8 @@ class FrameSchemaService {
18834
18837
  if (typeof frameSchema.frameName !== "string") {
18835
18838
  throw new Error(`frame schema validation failed: missing frameName`);
18836
18839
  }
18837
- if (typeof frameSchema.interval !== "string") {
18838
- throw new Error(`frame schema validation failed: missing interval for frameName=${frameSchema.frameName}`);
18840
+ if (frameSchema.interval && typeof frameSchema.interval !== "string") {
18841
+ throw new Error(`frame schema validation failed: invalid interval for frameName=${frameSchema.frameName}`);
18839
18842
  }
18840
18843
  if (!(frameSchema.startDate instanceof Date)) {
18841
18844
  throw new Error(`frame schema validation failed: missing startDate for frameName=${frameSchema.frameName}`);
@@ -20828,6 +20831,17 @@ class WalkerLogicPublicService {
20828
20831
  }
20829
20832
 
20830
20833
  const METHOD_NAME_RUN$2 = "liveCommandService run";
20834
+ const METHOD_NAME_VALIDATE$3 = "liveCommandService validate";
20835
+ /**
20836
+ * Creates a unique key for memoizing validate calls.
20837
+ * Key format: "strategyName:exchangeName:frameName"
20838
+ * @param context - Context with strategyName, exchangeName, frameName
20839
+ * @returns Unique string key for memoization
20840
+ */
20841
+ const CREATE_KEY_FN$t = (context) => {
20842
+ const parts = [context.strategyName, context.exchangeName];
20843
+ return parts.join(":");
20844
+ };
20831
20845
  /**
20832
20846
  * Global service providing access to live trading functionality.
20833
20847
  *
@@ -20843,6 +20857,25 @@ class LiveCommandService {
20843
20857
  this.strategySchemaService = inject(TYPES.strategySchemaService);
20844
20858
  this.riskValidationService = inject(TYPES.riskValidationService);
20845
20859
  this.actionValidationService = inject(TYPES.actionValidationService);
20860
+ /**
20861
+ * Validates strategy and associated risk configuration.
20862
+ * Memoized to avoid redundant validations for the same strategy-exchange combination.
20863
+ *
20864
+ * @param context - Context with strategyName, exchangeName
20865
+ * @param methodName - Name of the calling method for error tracking
20866
+ */
20867
+ this.validate = memoize(([context]) => CREATE_KEY_FN$t(context), (context, methodName) => {
20868
+ this.loggerService.log(METHOD_NAME_VALIDATE$3, {
20869
+ context,
20870
+ methodName,
20871
+ });
20872
+ this.strategyValidationService.validate(context.strategyName, methodName);
20873
+ this.exchangeValidationService.validate(context.exchangeName, methodName);
20874
+ const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
20875
+ riskName && this.riskValidationService.validate(riskName, methodName);
20876
+ riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, methodName));
20877
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, methodName));
20878
+ });
20846
20879
  /**
20847
20880
  * Runs live trading for a symbol with context propagation.
20848
20881
  *
@@ -20857,22 +20890,26 @@ class LiveCommandService {
20857
20890
  symbol,
20858
20891
  context,
20859
20892
  });
20860
- {
20861
- this.strategyValidationService.validate(context.strategyName, METHOD_NAME_RUN$2);
20862
- this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_RUN$2);
20863
- }
20864
- {
20865
- const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
20866
- riskName && this.riskValidationService.validate(riskName, METHOD_NAME_RUN$2);
20867
- riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_RUN$2));
20868
- actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_RUN$2));
20869
- }
20893
+ this.validate(context, METHOD_NAME_RUN$2);
20870
20894
  return this.liveLogicPublicService.run(symbol, context);
20871
20895
  };
20872
20896
  }
20873
20897
  }
20874
20898
 
20875
20899
  const METHOD_NAME_RUN$1 = "backtestCommandService run";
20900
+ const METHOD_NAME_VALIDATE$2 = "backtestCommandService validate";
20901
+ /**
20902
+ * Creates a unique key for memoizing validate calls.
20903
+ * Key format: "strategyName:exchangeName:frameName"
20904
+ * @param context - Context with strategyName, exchangeName, frameName
20905
+ * @returns Unique string key for memoization
20906
+ */
20907
+ const CREATE_KEY_FN$s = (context) => {
20908
+ const parts = [context.strategyName, context.exchangeName];
20909
+ if (context.frameName)
20910
+ parts.push(context.frameName);
20911
+ return parts.join(":");
20912
+ };
20876
20913
  /**
20877
20914
  * Global service providing access to backtest functionality.
20878
20915
  *
@@ -20889,6 +20926,26 @@ class BacktestCommandService {
20889
20926
  this.strategyValidationService = inject(TYPES.strategyValidationService);
20890
20927
  this.exchangeValidationService = inject(TYPES.exchangeValidationService);
20891
20928
  this.frameValidationService = inject(TYPES.frameValidationService);
20929
+ /**
20930
+ * Validates strategy and associated risk configuration.
20931
+ * Memoized to avoid redundant validations for the same strategy-exchange-frame combination.
20932
+ *
20933
+ * @param context - Context with strategyName, exchangeName and frameName
20934
+ * @param methodName - Name of the calling method for error tracking
20935
+ */
20936
+ this.validate = memoize(([context]) => CREATE_KEY_FN$s(context), (context, methodName) => {
20937
+ this.loggerService.log(METHOD_NAME_VALIDATE$2, {
20938
+ context,
20939
+ methodName,
20940
+ });
20941
+ this.strategyValidationService.validate(context.strategyName, methodName);
20942
+ this.exchangeValidationService.validate(context.exchangeName, methodName);
20943
+ this.frameValidationService.validate(context.frameName, methodName);
20944
+ const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
20945
+ riskName && this.riskValidationService.validate(riskName, methodName);
20946
+ riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, methodName));
20947
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, methodName));
20948
+ });
20892
20949
  /**
20893
20950
  * Runs backtest for a symbol with context propagation.
20894
20951
  *
@@ -20901,23 +20958,26 @@ class BacktestCommandService {
20901
20958
  symbol,
20902
20959
  context,
20903
20960
  });
20904
- {
20905
- this.strategyValidationService.validate(context.strategyName, METHOD_NAME_RUN$1);
20906
- this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_RUN$1);
20907
- this.frameValidationService.validate(context.frameName, METHOD_NAME_RUN$1);
20908
- }
20909
- {
20910
- const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
20911
- riskName && this.riskValidationService.validate(riskName, METHOD_NAME_RUN$1);
20912
- riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_RUN$1));
20913
- actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_RUN$1));
20914
- }
20961
+ this.validate(context, METHOD_NAME_RUN$1);
20915
20962
  return this.backtestLogicPublicService.run(symbol, context);
20916
20963
  };
20917
20964
  }
20918
20965
  }
20919
20966
 
20920
20967
  const METHOD_NAME_RUN = "walkerCommandService run";
20968
+ const METHOD_NAME_VALIDATE$1 = "walkerCommandService validate";
20969
+ /**
20970
+ * Creates a unique key for memoizing validate calls.
20971
+ * Key format: "walkerName:exchangeName:frameName"
20972
+ * @param context - Context with walkerName, exchangeName, frameName
20973
+ * @returns Unique string key for memoization
20974
+ */
20975
+ const CREATE_KEY_FN$r = (context) => {
20976
+ const parts = [context.walkerName, context.exchangeName];
20977
+ if (context.frameName)
20978
+ parts.push(context.frameName);
20979
+ return parts.join(":");
20980
+ };
20921
20981
  /**
20922
20982
  * Global service providing access to walker functionality.
20923
20983
  *
@@ -20936,6 +20996,34 @@ class WalkerCommandService {
20936
20996
  this.strategySchemaService = inject(TYPES.strategySchemaService);
20937
20997
  this.riskValidationService = inject(TYPES.riskValidationService);
20938
20998
  this.actionValidationService = inject(TYPES.actionValidationService);
20999
+ /**
21000
+ * Validates walker and associated strategy configurations.
21001
+ * Memoized to avoid redundant validations for the same walker-exchange-frame combination.
21002
+ *
21003
+ * Strategy/risk/action validation is performed explicitly here in addition to the
21004
+ * cascade inside WalkerValidationService — this is critical-path code and the
21005
+ * redundant check is intentional defense-in-depth.
21006
+ *
21007
+ * @param context - Context with walkerName, exchangeName and frameName
21008
+ * @param methodName - Name of the calling method for error tracking
21009
+ */
21010
+ this.validate = memoize(([context]) => CREATE_KEY_FN$r(context), (context, methodName) => {
21011
+ this.loggerService.log(METHOD_NAME_VALIDATE$1, {
21012
+ context,
21013
+ methodName,
21014
+ });
21015
+ this.exchangeValidationService.validate(context.exchangeName, methodName);
21016
+ this.frameValidationService.validate(context.frameName, methodName);
21017
+ this.walkerValidationService.validate(context.walkerName, methodName);
21018
+ const walkerSchema = this.walkerSchemaService.get(context.walkerName);
21019
+ for (const strategyName of walkerSchema.strategies) {
21020
+ const { riskName, riskList, actions } = this.strategySchemaService.get(strategyName);
21021
+ this.strategyValidationService.validate(strategyName, methodName);
21022
+ riskName && this.riskValidationService.validate(riskName, methodName);
21023
+ riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, methodName));
21024
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, methodName));
21025
+ }
21026
+ });
20939
21027
  /**
20940
21028
  * Runs walker comparison for a symbol with context propagation.
20941
21029
  *
@@ -20947,26 +21035,30 @@ class WalkerCommandService {
20947
21035
  symbol,
20948
21036
  context,
20949
21037
  });
20950
- {
20951
- this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_RUN);
20952
- this.frameValidationService.validate(context.frameName, METHOD_NAME_RUN);
20953
- this.walkerValidationService.validate(context.walkerName, METHOD_NAME_RUN);
20954
- }
20955
- {
20956
- const walkerSchema = this.walkerSchemaService.get(context.walkerName);
20957
- for (const strategyName of walkerSchema.strategies) {
20958
- const { riskName, riskList, actions } = this.strategySchemaService.get(strategyName);
20959
- this.strategyValidationService.validate(strategyName, METHOD_NAME_RUN);
20960
- riskName && this.riskValidationService.validate(riskName, METHOD_NAME_RUN);
20961
- riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_RUN));
20962
- actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_RUN));
20963
- }
20964
- }
21038
+ this.validate(context, METHOD_NAME_RUN);
20965
21039
  return this.walkerLogicPublicService.run(symbol, context);
20966
21040
  };
20967
21041
  }
20968
21042
  }
20969
21043
 
21044
+ /**
21045
+ * Derives the number of decimal places to show for a price based on the
21046
+ * magnitude of its integer part. Larger prices need fewer decimals; sub-dollar
21047
+ * prices keep full precision.
21048
+ * @param value - The price to derive the decimal scale for
21049
+ * @returns The number of digits after the decimal point
21050
+ */
21051
+ const getPriceScale = (value) => {
21052
+ const abs = Math.abs(value);
21053
+ if (abs >= 1) {
21054
+ // 1..9 -> 4, 10..99 -> 3, 100..999 -> 2, 1000+ -> 2 (floor), capped at 2
21055
+ const digits = Math.floor(Math.log10(abs)) + 1;
21056
+ return Math.max(2, 6 - digits);
21057
+ }
21058
+ // Sub-dollar prices need more precision.
21059
+ return 8;
21060
+ };
21061
+
20970
21062
  /**
20971
21063
  * Converts markdown content to plain text with minimal formatting
20972
21064
  * @param content - Markdown string to convert
@@ -21075,43 +21167,43 @@ const backtest_columns = [
21075
21167
  {
21076
21168
  key: "openPrice",
21077
21169
  label: "Open Price",
21078
- format: (data) => `${data.signal.priceOpen.toFixed(8)} USD`,
21170
+ format: (data) => `${data.signal.priceOpen.toFixed(getPriceScale(data.signal.priceOpen))} USD`,
21079
21171
  isVisible: () => true,
21080
21172
  },
21081
21173
  {
21082
21174
  key: "closePrice",
21083
21175
  label: "Close Price",
21084
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
21176
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
21085
21177
  isVisible: () => true,
21086
21178
  },
21087
21179
  {
21088
21180
  key: "takeProfit",
21089
21181
  label: "Take Profit",
21090
- format: (data) => `${data.signal.priceTakeProfit.toFixed(8)} USD`,
21182
+ format: (data) => `${data.signal.priceTakeProfit.toFixed(getPriceScale(data.signal.priceTakeProfit))} USD`,
21091
21183
  isVisible: () => true,
21092
21184
  },
21093
21185
  {
21094
21186
  key: "stopLoss",
21095
21187
  label: "Stop Loss",
21096
- format: (data) => `${data.signal.priceStopLoss.toFixed(8)} USD`,
21188
+ format: (data) => `${data.signal.priceStopLoss.toFixed(getPriceScale(data.signal.priceStopLoss))} USD`,
21097
21189
  isVisible: () => true,
21098
21190
  },
21099
21191
  {
21100
21192
  key: "originalPriceTakeProfit",
21101
21193
  label: "Original TP",
21102
- format: (data) => `${data.signal.originalPriceTakeProfit.toFixed(8)} USD`,
21194
+ format: (data) => `${data.signal.originalPriceTakeProfit.toFixed(getPriceScale(data.signal.originalPriceTakeProfit))} USD`,
21103
21195
  isVisible: () => true,
21104
21196
  },
21105
21197
  {
21106
21198
  key: "originalPriceStopLoss",
21107
21199
  label: "Original SL",
21108
- format: (data) => `${data.signal.originalPriceStopLoss.toFixed(8)} USD`,
21200
+ format: (data) => `${data.signal.originalPriceStopLoss.toFixed(getPriceScale(data.signal.originalPriceStopLoss))} USD`,
21109
21201
  isVisible: () => true,
21110
21202
  },
21111
21203
  {
21112
21204
  key: "originalPriceOpen",
21113
21205
  label: "Original Entry",
21114
- format: (data) => `${data.signal.originalPriceOpen.toFixed(8)} USD`,
21206
+ format: (data) => `${data.signal.originalPriceOpen.toFixed(getPriceScale(data.signal.originalPriceOpen))} USD`,
21115
21207
  isVisible: () => true,
21116
21208
  },
21117
21209
  {
@@ -21414,6 +21506,71 @@ const heat_columns = [
21414
21506
  : "N/A",
21415
21507
  isVisible: () => true,
21416
21508
  },
21509
+ {
21510
+ key: "trend",
21511
+ label: "Trend",
21512
+ format: (data) => data.trend ?? "N/A",
21513
+ isVisible: () => true,
21514
+ },
21515
+ {
21516
+ key: "trendStrength",
21517
+ label: "Trend %/d",
21518
+ format: (data) => data.trendStrength !== null ? str(data.trendStrength, "%") : "N/A",
21519
+ isVisible: () => true,
21520
+ },
21521
+ {
21522
+ key: "trendConfidence",
21523
+ label: "Trend R²",
21524
+ format: (data) => data.trendConfidence !== null ? data.trendConfidence.toFixed(3) : "N/A",
21525
+ isVisible: () => true,
21526
+ },
21527
+ {
21528
+ key: "buyerPressure",
21529
+ label: "Buyer Pres",
21530
+ format: (data) => data.buyerPressure !== null
21531
+ ? (data.buyerPressure * 100).toFixed(1) + "%"
21532
+ : "N/A",
21533
+ isVisible: () => true,
21534
+ },
21535
+ {
21536
+ key: "sellerPressure",
21537
+ label: "Seller Pres",
21538
+ format: (data) => data.sellerPressure !== null
21539
+ ? (data.sellerPressure * 100).toFixed(1) + "%"
21540
+ : "N/A",
21541
+ isVisible: () => true,
21542
+ },
21543
+ {
21544
+ key: "buyerStrength",
21545
+ label: "Buyer Str",
21546
+ format: (data) => data.buyerStrength !== null
21547
+ ? (data.buyerStrength * 100).toFixed(1) + "%"
21548
+ : "N/A",
21549
+ isVisible: () => true,
21550
+ },
21551
+ {
21552
+ key: "sellerStrength",
21553
+ label: "Seller Str",
21554
+ format: (data) => data.sellerStrength !== null
21555
+ ? (data.sellerStrength * 100).toFixed(1) + "%"
21556
+ : "N/A",
21557
+ isVisible: () => true,
21558
+ },
21559
+ {
21560
+ key: "pressureImbalance",
21561
+ label: "Pres Imb",
21562
+ format: (data) => data.pressureImbalance !== null
21563
+ ? (data.pressureImbalance > 0 ? "+" : "") +
21564
+ data.pressureImbalance.toFixed(3)
21565
+ : "N/A",
21566
+ isVisible: () => true,
21567
+ },
21568
+ {
21569
+ key: "medianStepSize",
21570
+ label: "Median Step",
21571
+ format: (data) => data.medianStepSize !== null ? str(data.medianStepSize, "%") : "N/A",
21572
+ isVisible: () => true,
21573
+ },
21417
21574
  {
21418
21575
  key: "sortinoRatio",
21419
21576
  label: "Sortino",
@@ -21510,34 +21667,34 @@ const live_columns = [
21510
21667
  {
21511
21668
  key: "currentPrice",
21512
21669
  label: "Current Price",
21513
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
21670
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
21514
21671
  isVisible: () => true,
21515
21672
  },
21516
21673
  {
21517
21674
  key: "openPrice",
21518
21675
  label: "Open Price",
21519
- format: (data) => data.priceOpen !== undefined ? `${data.priceOpen.toFixed(8)} USD` : "N/A",
21676
+ format: (data) => data.priceOpen !== undefined ? `${data.priceOpen.toFixed(getPriceScale(data.priceOpen))} USD` : "N/A",
21520
21677
  isVisible: () => true,
21521
21678
  },
21522
21679
  {
21523
21680
  key: "takeProfit",
21524
21681
  label: "Take Profit",
21525
21682
  format: (data) => data.priceTakeProfit !== undefined
21526
- ? `${data.priceTakeProfit.toFixed(8)} USD`
21683
+ ? `${data.priceTakeProfit.toFixed(getPriceScale(data.priceTakeProfit))} USD`
21527
21684
  : "N/A",
21528
21685
  isVisible: () => true,
21529
21686
  },
21530
21687
  {
21531
21688
  key: "stopLoss",
21532
21689
  label: "Stop Loss",
21533
- format: (data) => data.priceStopLoss !== undefined ? `${data.priceStopLoss.toFixed(8)} USD` : "N/A",
21690
+ format: (data) => data.priceStopLoss !== undefined ? `${data.priceStopLoss.toFixed(getPriceScale(data.priceStopLoss))} USD` : "N/A",
21534
21691
  isVisible: () => true,
21535
21692
  },
21536
21693
  {
21537
21694
  key: "originalPriceTakeProfit",
21538
21695
  label: "Original TP",
21539
21696
  format: (data) => data.originalPriceTakeProfit !== undefined
21540
- ? `${data.originalPriceTakeProfit.toFixed(8)} USD`
21697
+ ? `${data.originalPriceTakeProfit.toFixed(getPriceScale(data.originalPriceTakeProfit))} USD`
21541
21698
  : "N/A",
21542
21699
  isVisible: () => true,
21543
21700
  },
@@ -21545,7 +21702,7 @@ const live_columns = [
21545
21702
  key: "originalPriceStopLoss",
21546
21703
  label: "Original SL",
21547
21704
  format: (data) => data.originalPriceStopLoss !== undefined
21548
- ? `${data.originalPriceStopLoss.toFixed(8)} USD`
21705
+ ? `${data.originalPriceStopLoss.toFixed(getPriceScale(data.originalPriceStopLoss))} USD`
21549
21706
  : "N/A",
21550
21707
  isVisible: () => true,
21551
21708
  },
@@ -21553,7 +21710,7 @@ const live_columns = [
21553
21710
  key: "originalPriceOpen",
21554
21711
  label: "Original Entry",
21555
21712
  format: (data) => data.originalPriceOpen !== undefined
21556
- ? `${data.originalPriceOpen.toFixed(8)} USD`
21713
+ ? `${data.originalPriceOpen.toFixed(getPriceScale(data.originalPriceOpen))} USD`
21557
21714
  : "N/A",
21558
21715
  isVisible: () => true,
21559
21716
  },
@@ -21724,43 +21881,43 @@ const partial_columns = [
21724
21881
  {
21725
21882
  key: "currentPrice",
21726
21883
  label: "Current Price",
21727
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
21884
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
21728
21885
  isVisible: () => true,
21729
21886
  },
21730
21887
  {
21731
21888
  key: "priceOpen",
21732
21889
  label: "Entry Price",
21733
- format: (data) => (data.priceOpen ? `${data.priceOpen.toFixed(8)} USD` : "N/A"),
21890
+ format: (data) => (data.priceOpen ? `${data.priceOpen.toFixed(getPriceScale(data.priceOpen))} USD` : "N/A"),
21734
21891
  isVisible: () => true,
21735
21892
  },
21736
21893
  {
21737
21894
  key: "priceTakeProfit",
21738
21895
  label: "Take Profit",
21739
- format: (data) => (data.priceTakeProfit ? `${data.priceTakeProfit.toFixed(8)} USD` : "N/A"),
21896
+ format: (data) => (data.priceTakeProfit ? `${data.priceTakeProfit.toFixed(getPriceScale(data.priceTakeProfit))} USD` : "N/A"),
21740
21897
  isVisible: () => true,
21741
21898
  },
21742
21899
  {
21743
21900
  key: "priceStopLoss",
21744
21901
  label: "Stop Loss",
21745
- format: (data) => (data.priceStopLoss ? `${data.priceStopLoss.toFixed(8)} USD` : "N/A"),
21902
+ format: (data) => (data.priceStopLoss ? `${data.priceStopLoss.toFixed(getPriceScale(data.priceStopLoss))} USD` : "N/A"),
21746
21903
  isVisible: () => true,
21747
21904
  },
21748
21905
  {
21749
21906
  key: "originalPriceTakeProfit",
21750
21907
  label: "Original TP",
21751
- format: (data) => (data.originalPriceTakeProfit ? `${data.originalPriceTakeProfit.toFixed(8)} USD` : "N/A"),
21908
+ format: (data) => (data.originalPriceTakeProfit ? `${data.originalPriceTakeProfit.toFixed(getPriceScale(data.originalPriceTakeProfit))} USD` : "N/A"),
21752
21909
  isVisible: () => true,
21753
21910
  },
21754
21911
  {
21755
21912
  key: "originalPriceStopLoss",
21756
21913
  label: "Original SL",
21757
- format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(8)} USD` : "N/A"),
21914
+ format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(getPriceScale(data.originalPriceStopLoss))} USD` : "N/A"),
21758
21915
  isVisible: () => true,
21759
21916
  },
21760
21917
  {
21761
21918
  key: "originalPriceOpen",
21762
21919
  label: "Original Entry",
21763
- format: (data) => (data.originalPriceOpen ? `${data.originalPriceOpen.toFixed(8)} USD` : "N/A"),
21920
+ format: (data) => (data.originalPriceOpen ? `${data.originalPriceOpen.toFixed(getPriceScale(data.originalPriceOpen))} USD` : "N/A"),
21764
21921
  isVisible: () => true,
21765
21922
  },
21766
21923
  {
@@ -21884,43 +22041,43 @@ const breakeven_columns = [
21884
22041
  {
21885
22042
  key: "priceOpen",
21886
22043
  label: "Entry Price",
21887
- format: (data) => `${data.priceOpen.toFixed(8)} USD`,
22044
+ format: (data) => `${data.priceOpen.toFixed(getPriceScale(data.priceOpen))} USD`,
21888
22045
  isVisible: () => true,
21889
22046
  },
21890
22047
  {
21891
22048
  key: "currentPrice",
21892
22049
  label: "Breakeven Price",
21893
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
22050
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
21894
22051
  isVisible: () => true,
21895
22052
  },
21896
22053
  {
21897
22054
  key: "priceTakeProfit",
21898
22055
  label: "Take Profit",
21899
- format: (data) => (data.priceTakeProfit ? `${data.priceTakeProfit.toFixed(8)} USD` : "N/A"),
22056
+ format: (data) => (data.priceTakeProfit ? `${data.priceTakeProfit.toFixed(getPriceScale(data.priceTakeProfit))} USD` : "N/A"),
21900
22057
  isVisible: () => true,
21901
22058
  },
21902
22059
  {
21903
22060
  key: "priceStopLoss",
21904
22061
  label: "Stop Loss",
21905
- format: (data) => (data.priceStopLoss ? `${data.priceStopLoss.toFixed(8)} USD` : "N/A"),
22062
+ format: (data) => (data.priceStopLoss ? `${data.priceStopLoss.toFixed(getPriceScale(data.priceStopLoss))} USD` : "N/A"),
21906
22063
  isVisible: () => true,
21907
22064
  },
21908
22065
  {
21909
22066
  key: "originalPriceTakeProfit",
21910
22067
  label: "Original TP",
21911
- format: (data) => (data.originalPriceTakeProfit ? `${data.originalPriceTakeProfit.toFixed(8)} USD` : "N/A"),
22068
+ format: (data) => (data.originalPriceTakeProfit ? `${data.originalPriceTakeProfit.toFixed(getPriceScale(data.originalPriceTakeProfit))} USD` : "N/A"),
21912
22069
  isVisible: () => true,
21913
22070
  },
21914
22071
  {
21915
22072
  key: "originalPriceStopLoss",
21916
22073
  label: "Original SL",
21917
- format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(8)} USD` : "N/A"),
22074
+ format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(getPriceScale(data.originalPriceStopLoss))} USD` : "N/A"),
21918
22075
  isVisible: () => true,
21919
22076
  },
21920
22077
  {
21921
22078
  key: "originalPriceOpen",
21922
22079
  label: "Original Entry",
21923
- format: (data) => (data.originalPriceOpen ? `${data.originalPriceOpen.toFixed(8)} USD` : "N/A"),
22080
+ format: (data) => (data.originalPriceOpen ? `${data.originalPriceOpen.toFixed(getPriceScale(data.originalPriceOpen))} USD` : "N/A"),
21924
22081
  isVisible: () => true,
21925
22082
  },
21926
22083
  {
@@ -22177,7 +22334,7 @@ const risk_columns = [
22177
22334
  key: "openPrice",
22178
22335
  label: "Open Price",
22179
22336
  format: (data) => data.currentSignal.priceOpen !== undefined
22180
- ? `${data.currentSignal.priceOpen.toFixed(8)} USD`
22337
+ ? `${data.currentSignal.priceOpen.toFixed(getPriceScale(data.currentSignal.priceOpen))} USD`
22181
22338
  : "N/A",
22182
22339
  isVisible: () => true,
22183
22340
  },
@@ -22185,7 +22342,7 @@ const risk_columns = [
22185
22342
  key: "takeProfit",
22186
22343
  label: "Take Profit",
22187
22344
  format: (data) => data.currentSignal.priceTakeProfit !== undefined
22188
- ? `${data.currentSignal.priceTakeProfit.toFixed(8)} USD`
22345
+ ? `${data.currentSignal.priceTakeProfit.toFixed(getPriceScale(data.currentSignal.priceTakeProfit))} USD`
22189
22346
  : "N/A",
22190
22347
  isVisible: () => true,
22191
22348
  },
@@ -22193,7 +22350,7 @@ const risk_columns = [
22193
22350
  key: "stopLoss",
22194
22351
  label: "Stop Loss",
22195
22352
  format: (data) => data.currentSignal.priceStopLoss !== undefined
22196
- ? `${data.currentSignal.priceStopLoss.toFixed(8)} USD`
22353
+ ? `${data.currentSignal.priceStopLoss.toFixed(getPriceScale(data.currentSignal.priceStopLoss))} USD`
22197
22354
  : "N/A",
22198
22355
  isVisible: () => true,
22199
22356
  },
@@ -22201,7 +22358,7 @@ const risk_columns = [
22201
22358
  key: "originalPriceTakeProfit",
22202
22359
  label: "Original TP",
22203
22360
  format: (data) => data.currentSignal.originalPriceTakeProfit !== undefined
22204
- ? `${data.currentSignal.originalPriceTakeProfit.toFixed(8)} USD`
22361
+ ? `${data.currentSignal.originalPriceTakeProfit.toFixed(getPriceScale(data.currentSignal.originalPriceTakeProfit))} USD`
22205
22362
  : "N/A",
22206
22363
  isVisible: () => true,
22207
22364
  },
@@ -22209,7 +22366,7 @@ const risk_columns = [
22209
22366
  key: "originalPriceStopLoss",
22210
22367
  label: "Original SL",
22211
22368
  format: (data) => data.currentSignal.originalPriceStopLoss !== undefined
22212
- ? `${data.currentSignal.originalPriceStopLoss.toFixed(8)} USD`
22369
+ ? `${data.currentSignal.originalPriceStopLoss.toFixed(getPriceScale(data.currentSignal.originalPriceStopLoss))} USD`
22213
22370
  : "N/A",
22214
22371
  isVisible: () => true,
22215
22372
  },
@@ -22217,7 +22374,7 @@ const risk_columns = [
22217
22374
  key: "originalPriceOpen",
22218
22375
  label: "Original Entry",
22219
22376
  format: (data) => data.currentSignal.originalPriceOpen !== undefined
22220
- ? `${data.currentSignal.originalPriceOpen.toFixed(8)} USD`
22377
+ ? `${data.currentSignal.originalPriceOpen.toFixed(getPriceScale(data.currentSignal.originalPriceOpen))} USD`
22221
22378
  : "N/A",
22222
22379
  isVisible: () => true,
22223
22380
  },
@@ -22248,7 +22405,7 @@ const risk_columns = [
22248
22405
  {
22249
22406
  key: "currentPrice",
22250
22407
  label: "Current Price",
22251
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
22408
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
22252
22409
  isVisible: () => true,
22253
22410
  },
22254
22411
  {
@@ -22374,32 +22531,32 @@ const schedule_columns = [
22374
22531
  {
22375
22532
  key: "currentPrice",
22376
22533
  label: "Current Price",
22377
- format: (data) => data.currentPrice ? `${data.currentPrice.toFixed(8)} USD` : "N/A",
22534
+ format: (data) => data.currentPrice ? `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD` : "N/A",
22378
22535
  isVisible: () => true,
22379
22536
  },
22380
22537
  {
22381
22538
  key: "priceOpen",
22382
22539
  label: "Entry Price",
22383
- format: (data) => `${data.priceOpen.toFixed(8)} USD`,
22540
+ format: (data) => `${data.priceOpen.toFixed(getPriceScale(data.priceOpen))} USD`,
22384
22541
  isVisible: () => true,
22385
22542
  },
22386
22543
  {
22387
22544
  key: "takeProfit",
22388
22545
  label: "Take Profit",
22389
- format: (data) => `${data.priceTakeProfit.toFixed(8)} USD`,
22546
+ format: (data) => `${data.priceTakeProfit.toFixed(getPriceScale(data.priceTakeProfit))} USD`,
22390
22547
  isVisible: () => true,
22391
22548
  },
22392
22549
  {
22393
22550
  key: "stopLoss",
22394
22551
  label: "Stop Loss",
22395
- format: (data) => `${data.priceStopLoss.toFixed(8)} USD`,
22552
+ format: (data) => `${data.priceStopLoss.toFixed(getPriceScale(data.priceStopLoss))} USD`,
22396
22553
  isVisible: () => true,
22397
22554
  },
22398
22555
  {
22399
22556
  key: "originalPriceTakeProfit",
22400
22557
  label: "Original TP",
22401
22558
  format: (data) => data.originalPriceTakeProfit !== undefined
22402
- ? `${data.originalPriceTakeProfit.toFixed(8)} USD`
22559
+ ? `${data.originalPriceTakeProfit.toFixed(getPriceScale(data.originalPriceTakeProfit))} USD`
22403
22560
  : "N/A",
22404
22561
  isVisible: () => true,
22405
22562
  },
@@ -22407,7 +22564,7 @@ const schedule_columns = [
22407
22564
  key: "originalPriceStopLoss",
22408
22565
  label: "Original SL",
22409
22566
  format: (data) => data.originalPriceStopLoss !== undefined
22410
- ? `${data.originalPriceStopLoss.toFixed(8)} USD`
22567
+ ? `${data.originalPriceStopLoss.toFixed(getPriceScale(data.originalPriceStopLoss))} USD`
22411
22568
  : "N/A",
22412
22569
  isVisible: () => true,
22413
22570
  },
@@ -22415,7 +22572,7 @@ const schedule_columns = [
22415
22572
  key: "originalPriceOpen",
22416
22573
  label: "Original Entry",
22417
22574
  format: (data) => data.originalPriceOpen !== undefined
22418
- ? `${data.originalPriceOpen.toFixed(8)} USD`
22575
+ ? `${data.originalPriceOpen.toFixed(getPriceScale(data.originalPriceOpen))} USD`
22419
22576
  : "N/A",
22420
22577
  isVisible: () => true,
22421
22578
  },
@@ -22540,7 +22697,7 @@ const strategy_columns = [
22540
22697
  {
22541
22698
  key: "currentPrice",
22542
22699
  label: "Price",
22543
- format: (data) => (data.currentPrice !== undefined ? `${data.currentPrice.toFixed(8)} USD` : "N/A"),
22700
+ format: (data) => (data.currentPrice !== undefined ? `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD` : "N/A"),
22544
22701
  isVisible: () => true,
22545
22702
  },
22546
22703
  {
@@ -22660,25 +22817,25 @@ const sync_columns = [
22660
22817
  {
22661
22818
  key: "currentPrice",
22662
22819
  label: "Current Price",
22663
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
22820
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
22664
22821
  isVisible: () => true,
22665
22822
  },
22666
22823
  {
22667
22824
  key: "priceOpen",
22668
22825
  label: "Entry Price",
22669
- format: (data) => `${data.priceOpen.toFixed(8)} USD`,
22826
+ format: (data) => `${data.priceOpen.toFixed(getPriceScale(data.priceOpen))} USD`,
22670
22827
  isVisible: () => true,
22671
22828
  },
22672
22829
  {
22673
22830
  key: "priceTakeProfit",
22674
22831
  label: "Take Profit",
22675
- format: (data) => `${data.priceTakeProfit.toFixed(8)} USD`,
22832
+ format: (data) => `${data.priceTakeProfit.toFixed(getPriceScale(data.priceTakeProfit))} USD`,
22676
22833
  isVisible: () => true,
22677
22834
  },
22678
22835
  {
22679
22836
  key: "priceStopLoss",
22680
22837
  label: "Stop Loss",
22681
- format: (data) => `${data.priceStopLoss.toFixed(8)} USD`,
22838
+ format: (data) => `${data.priceStopLoss.toFixed(getPriceScale(data.priceStopLoss))} USD`,
22682
22839
  isVisible: () => true,
22683
22840
  },
22684
22841
  {
@@ -22771,25 +22928,25 @@ const highest_profit_columns = [
22771
22928
  {
22772
22929
  key: "currentPrice",
22773
22930
  label: "Peak Price",
22774
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
22931
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
22775
22932
  isVisible: () => true,
22776
22933
  },
22777
22934
  {
22778
22935
  key: "priceOpen",
22779
22936
  label: "Entry Price",
22780
- format: (data) => `${data.priceOpen.toFixed(8)} USD`,
22937
+ format: (data) => `${data.priceOpen.toFixed(getPriceScale(data.priceOpen))} USD`,
22781
22938
  isVisible: () => true,
22782
22939
  },
22783
22940
  {
22784
22941
  key: "priceTakeProfit",
22785
22942
  label: "Take Profit",
22786
- format: (data) => `${data.priceTakeProfit.toFixed(8)} USD`,
22943
+ format: (data) => `${data.priceTakeProfit.toFixed(getPriceScale(data.priceTakeProfit))} USD`,
22787
22944
  isVisible: () => true,
22788
22945
  },
22789
22946
  {
22790
22947
  key: "priceStopLoss",
22791
22948
  label: "Stop Loss",
22792
- format: (data) => `${data.priceStopLoss.toFixed(8)} USD`,
22949
+ format: (data) => `${data.priceStopLoss.toFixed(getPriceScale(data.priceStopLoss))} USD`,
22793
22950
  isVisible: () => true,
22794
22951
  },
22795
22952
  {
@@ -22858,25 +23015,25 @@ const max_drawdown_columns = [
22858
23015
  {
22859
23016
  key: "currentPrice",
22860
23017
  label: "DD Price",
22861
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
23018
+ format: (data) => `${data.currentPrice.toFixed(getPriceScale(data.currentPrice))} USD`,
22862
23019
  isVisible: () => true,
22863
23020
  },
22864
23021
  {
22865
23022
  key: "priceOpen",
22866
23023
  label: "Entry Price",
22867
- format: (data) => `${data.priceOpen.toFixed(8)} USD`,
23024
+ format: (data) => `${data.priceOpen.toFixed(getPriceScale(data.priceOpen))} USD`,
22868
23025
  isVisible: () => true,
22869
23026
  },
22870
23027
  {
22871
23028
  key: "priceTakeProfit",
22872
23029
  label: "Take Profit",
22873
- format: (data) => `${data.priceTakeProfit.toFixed(8)} USD`,
23030
+ format: (data) => `${data.priceTakeProfit.toFixed(getPriceScale(data.priceTakeProfit))} USD`,
22874
23031
  isVisible: () => true,
22875
23032
  },
22876
23033
  {
22877
23034
  key: "priceStopLoss",
22878
23035
  label: "Stop Loss",
22879
- format: (data) => `${data.priceStopLoss.toFixed(8)} USD`,
23036
+ format: (data) => `${data.priceStopLoss.toFixed(getPriceScale(data.priceStopLoss))} USD`,
22880
23037
  isVisible: () => true,
22881
23038
  },
22882
23039
  {
@@ -23758,6 +23915,184 @@ MarkdownFileBase = makeExtendable(MarkdownFileBase);
23758
23915
  ReportBase = makeExtendable(ReportBase);
23759
23916
  const ReportWriter = new ReportWriterAdapter();
23760
23917
 
23918
+ /**
23919
+ * Price-profile metrics derived purely from a series of trade closes — no
23920
+ * candles, no exchange queries. Designed for `BacktestMarkdownService`,
23921
+ * `LiveMarkdownService` and per-symbol Heat: all three already have a
23922
+ * chronological series of `(closeAt, close)` points and nothing else.
23923
+ *
23924
+ * Conventions
23925
+ * -----------
23926
+ * - Pressure: fraction of up-moves vs down-moves (frequency).
23927
+ * - Strength: fraction of upward magnitude vs total movement.
23928
+ * - A divergence between pressure and strength surfaces asymmetry — e.g.
23929
+ * frequent shallow up-moves vs rare deep down-moves is "rising on weak
23930
+ * buys, falling on strong sells".
23931
+ * - Trend: linear regression of log(close) vs days. Slope in %/day,
23932
+ * confidence in R². Classification is bivariate (slope × R²): neither
23933
+ * axis alone fires, both must agree. Slope threshold is normalised by
23934
+ * medianStepSize so the metric self-tunes to the instrument's typical
23935
+ * move size.
23936
+ */
23937
+ /** Minimum samples to surface any price-profile metric. Below this the
23938
+ * per-trade step-distribution and the regression are statistically noisy. */
23939
+ const MIN_SIGNALS = 10;
23940
+ /** R² gate for declaring any trend at all. Below this the regression is
23941
+ * too weak to claim a direction — treat the series as sideways even if
23942
+ * the slope is large. 0.30 is the conventional weak-to-moderate-fit
23943
+ * boundary in econometrics (Cohen's f² ≈ 0.43). */
23944
+ const R2_TREND_GATE = 0.30;
23945
+ /** Slope-magnitude threshold relative to medianStepSize for declaring the
23946
+ * trend strong enough to call bullish/bearish. Below this the regression
23947
+ * fits but the actual drift is weaker than the typical daily step, so we
23948
+ * downgrade to "neutral" (a real but uninteresting tilt). */
23949
+ const SLOPE_VS_STEP_GATE = 0.25;
23950
+ const isFiniteNumber = (v) => typeof v === "number" && Number.isFinite(v);
23951
+ const median = (values) => {
23952
+ const sorted = values.slice().sort((a, b) => a - b);
23953
+ const mid = sorted.length >> 1;
23954
+ return sorted.length % 2 === 0
23955
+ ? (sorted[mid - 1] + sorted[mid]) / 2
23956
+ : sorted[mid];
23957
+ };
23958
+ const emptyProfile = () => ({
23959
+ medianStepSize: null,
23960
+ buyerPressure: null,
23961
+ sellerPressure: null,
23962
+ buyerStrength: null,
23963
+ sellerStrength: null,
23964
+ pressureImbalance: null,
23965
+ trend: null,
23966
+ trendStrength: null,
23967
+ trendConfidence: null,
23968
+ });
23969
+ /**
23970
+ * Computes the price-profile bundle for a chronological series of trade
23971
+ * closes. The input is expected to be sorted by `closeAt` ascending; the
23972
+ * function does not sort defensively (the markdown services already sort).
23973
+ *
23974
+ * @param series - One point per closed trade: timestamp (ms) and close price.
23975
+ * @returns A bundle of nine metrics, each `null` when the input is too small
23976
+ * or numerically unsafe.
23977
+ */
23978
+ const getPriceProfile = (series) => {
23979
+ const valid = series.filter((p) => isFiniteNumber(p.closeAt) &&
23980
+ p.closeAt > 0 &&
23981
+ isFiniteNumber(p.close) &&
23982
+ p.close > 0);
23983
+ if (valid.length < MIN_SIGNALS)
23984
+ return emptyProfile();
23985
+ const n = valid.length;
23986
+ const closes = valid.map((p) => p.close);
23987
+ const times = valid.map((p) => p.closeAt);
23988
+ const absSteps = [];
23989
+ let upMoves = 0;
23990
+ let downMoves = 0;
23991
+ let upMagnitude = 0;
23992
+ let downMagnitude = 0;
23993
+ for (let i = 1; i < n; i++) {
23994
+ const prev = closes[i - 1];
23995
+ const cur = closes[i];
23996
+ const ret = (cur - prev) / prev;
23997
+ const abs = Math.abs(ret);
23998
+ absSteps.push(abs);
23999
+ if (ret > 0) {
24000
+ upMoves++;
24001
+ upMagnitude += abs;
24002
+ }
24003
+ else if (ret < 0) {
24004
+ downMoves++;
24005
+ downMagnitude += abs;
24006
+ }
24007
+ }
24008
+ if (absSteps.length === 0)
24009
+ return emptyProfile();
24010
+ const medianStepSize = median(absSteps) * 100; // percent
24011
+ // --- Pressure / strength ---
24012
+ const decisiveMoves = upMoves + downMoves;
24013
+ const totalMagnitude = upMagnitude + downMagnitude;
24014
+ const buyerPressure = decisiveMoves > 0 ? upMoves / decisiveMoves : null;
24015
+ const sellerPressure = decisiveMoves > 0 ? downMoves / decisiveMoves : null;
24016
+ const buyerStrength = totalMagnitude > 0 ? upMagnitude / totalMagnitude : null;
24017
+ const sellerStrength = totalMagnitude > 0 ? downMagnitude / totalMagnitude : null;
24018
+ const pressureImbalance = buyerStrength !== null && sellerStrength !== null
24019
+ ? buyerStrength - sellerStrength
24020
+ : null;
24021
+ // --- Trend: linear regression of log(close) vs days ---
24022
+ // log-price slope is scale-invariant: 1%/day means the same whether the
24023
+ // asset trades at $0.01 or $10000. Use `closeAt[0]` as time origin so x_i
24024
+ // starts at zero — keeps numerical conditioning sane on long horizons.
24025
+ const t0 = times[0];
24026
+ const xs = new Array(n);
24027
+ const ys = new Array(n);
24028
+ for (let i = 0; i < n; i++) {
24029
+ xs[i] = (times[i] - t0) / (1000 * 60 * 60 * 24); // days
24030
+ ys[i] = Math.log(closes[i]);
24031
+ }
24032
+ // Calendar span must be non-degenerate for the slope to mean anything.
24033
+ const xRange = xs[n - 1] - xs[0];
24034
+ let trend = null;
24035
+ let trendStrength = null;
24036
+ let trendConfidence = null;
24037
+ if (xRange > 0) {
24038
+ let sumX = 0;
24039
+ let sumY = 0;
24040
+ for (let i = 0; i < n; i++) {
24041
+ sumX += xs[i];
24042
+ sumY += ys[i];
24043
+ }
24044
+ const meanX = sumX / n;
24045
+ const meanY = sumY / n;
24046
+ let ssXX = 0;
24047
+ let ssXY = 0;
24048
+ let ssYY = 0;
24049
+ for (let i = 0; i < n; i++) {
24050
+ const dx = xs[i] - meanX;
24051
+ const dy = ys[i] - meanY;
24052
+ ssXX += dx * dx;
24053
+ ssXY += dx * dy;
24054
+ ssYY += dy * dy;
24055
+ }
24056
+ if (ssXX > 0) {
24057
+ const slopeLog = ssXY / ssXX; // log-return per day
24058
+ const slopePct = slopeLog * 100; // %/day (small-slope approx)
24059
+ // R² = 1 - SS_res / SS_tot. With a single explanatory variable this
24060
+ // equals (ssXY)² / (ssXX * ssYY) when ssYY > 0.
24061
+ const r2 = ssYY > 0 ? (ssXY * ssXY) / (ssXX * ssYY) : 0;
24062
+ trendStrength = slopePct;
24063
+ trendConfidence = Math.max(0, Math.min(1, r2));
24064
+ if (trendConfidence < R2_TREND_GATE) {
24065
+ trend = "sideways";
24066
+ }
24067
+ else {
24068
+ const slopeMagnitude = Math.abs(slopePct);
24069
+ const stepScale = medianStepSize * SLOPE_VS_STEP_GATE;
24070
+ if (slopeMagnitude < stepScale) {
24071
+ trend = "neutral";
24072
+ }
24073
+ else if (slopePct > 0) {
24074
+ trend = "bullish";
24075
+ }
24076
+ else {
24077
+ trend = "bearish";
24078
+ }
24079
+ }
24080
+ }
24081
+ }
24082
+ const safe = (v) => v === null || !Number.isFinite(v) ? null : v;
24083
+ return {
24084
+ medianStepSize: safe(medianStepSize),
24085
+ buyerPressure: safe(buyerPressure),
24086
+ sellerPressure: safe(sellerPressure),
24087
+ buyerStrength: safe(buyerStrength),
24088
+ sellerStrength: safe(sellerStrength),
24089
+ pressureImbalance: safe(pressureImbalance),
24090
+ trend,
24091
+ trendStrength: safe(trendStrength),
24092
+ trendConfidence: safe(trendConfidence),
24093
+ };
24094
+ };
24095
+
23761
24096
  /**
23762
24097
  * Creates a unique key for memoizing ReportStorage instances.
23763
24098
  * Key format: "symbol:strategyName:exchangeName:frameName:backtest" or "symbol:strategyName:exchangeName:live"
@@ -23890,6 +24225,15 @@ let ReportStorage$a = class ReportStorage {
23890
24225
  avgConsecutiveLossPnl: null,
23891
24226
  avgWinDuration: null,
23892
24227
  avgLossDuration: null,
24228
+ medianStepSize: null,
24229
+ buyerPressure: null,
24230
+ sellerPressure: null,
24231
+ buyerStrength: null,
24232
+ sellerStrength: null,
24233
+ pressureImbalance: null,
24234
+ trend: null,
24235
+ trendStrength: null,
24236
+ trendConfidence: null,
23893
24237
  };
23894
24238
  }
23895
24239
  // Valid signal set — those with usable pendingAt AND closeTimestamp. Single source
@@ -24186,6 +24530,13 @@ let ReportStorage$a = class ReportStorage {
24186
24530
  const recoveryFactor = !canComputeRatios || blown || equityMaxDrawdown <= 0
24187
24531
  ? null
24188
24532
  : Math.max(-MAX_CALMAR_RATIO$2, Math.min(MAX_CALMAR_RATIO$2, ((equityFinal - 1) * 100) / equityMaxDrawdown));
24533
+ // Price profile — buyer/seller pressure, trend classification. Walks the
24534
+ // chronological close series (`orderedSignals` is already sorted by
24535
+ // closeTimestamp). N-gated internally by the helper (MIN_SIGNALS = 10).
24536
+ const priceProfile = getPriceProfile(orderedSignals.map((s) => ({
24537
+ closeAt: s.closeTimestamp,
24538
+ close: s.currentPrice,
24539
+ })));
24189
24540
  return {
24190
24541
  signalList: this._signalList,
24191
24542
  totalSignals,
@@ -24211,6 +24562,15 @@ let ReportStorage$a = class ReportStorage {
24211
24562
  avgConsecutiveLossPnl: isUnsafe$4(avgConsecutiveLossPnl) ? null : avgConsecutiveLossPnl,
24212
24563
  avgWinDuration: isUnsafe$4(avgWinDuration) ? null : avgWinDuration,
24213
24564
  avgLossDuration: isUnsafe$4(avgLossDuration) ? null : avgLossDuration,
24565
+ medianStepSize: priceProfile.medianStepSize,
24566
+ buyerPressure: priceProfile.buyerPressure,
24567
+ sellerPressure: priceProfile.sellerPressure,
24568
+ buyerStrength: priceProfile.buyerStrength,
24569
+ sellerStrength: priceProfile.sellerStrength,
24570
+ pressureImbalance: priceProfile.pressureImbalance,
24571
+ trend: priceProfile.trend,
24572
+ trendStrength: priceProfile.trendStrength,
24573
+ trendConfidence: priceProfile.trendConfidence,
24214
24574
  };
24215
24575
  }
24216
24576
  /**
@@ -24267,6 +24627,15 @@ let ReportStorage$a = class ReportStorage {
24267
24627
  `**Avg Loss Duration:** ${stats.avgLossDuration === null ? "N/A" : `${stats.avgLossDuration.toFixed(1)} min`}`,
24268
24628
  `**Avg Consecutive Win PNL:** ${stats.avgConsecutiveWinPnl === null ? "N/A" : `${stats.avgConsecutiveWinPnl > 0 ? "+" : ""}${stats.avgConsecutiveWinPnl.toFixed(3)}% (higher is better)`}`,
24269
24629
  `**Avg Consecutive Loss PNL:** ${stats.avgConsecutiveLossPnl === null ? "N/A" : `${stats.avgConsecutiveLossPnl.toFixed(3)}% (closer to 0 is better)`}`,
24630
+ `**Trend:** ${stats.trend === null ? "N/A" : stats.trend}`,
24631
+ `**Trend Strength:** ${stats.trendStrength === null ? "N/A" : `${stats.trendStrength > 0 ? "+" : ""}${stats.trendStrength.toFixed(3)}%/day`}`,
24632
+ `**Trend Confidence (R²):** ${stats.trendConfidence === null ? "N/A" : stats.trendConfidence.toFixed(3)}`,
24633
+ `**Buyer Pressure:** ${stats.buyerPressure === null ? "N/A" : `${(stats.buyerPressure * 100).toFixed(1)}%`}`,
24634
+ `**Seller Pressure:** ${stats.sellerPressure === null ? "N/A" : `${(stats.sellerPressure * 100).toFixed(1)}%`}`,
24635
+ `**Buyer Strength:** ${stats.buyerStrength === null ? "N/A" : `${(stats.buyerStrength * 100).toFixed(1)}%`}`,
24636
+ `**Seller Strength:** ${stats.sellerStrength === null ? "N/A" : `${(stats.sellerStrength * 100).toFixed(1)}%`}`,
24637
+ `**Pressure Imbalance:** ${stats.pressureImbalance === null ? "N/A" : `${stats.pressureImbalance > 0 ? "+" : ""}${stats.pressureImbalance.toFixed(3)}`}`,
24638
+ `**Median Step Size:** ${stats.medianStepSize === null ? "N/A" : `${stats.medianStepSize.toFixed(3)}%`}`,
24270
24639
  "",
24271
24640
  `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
24272
24641
  `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
@@ -24280,6 +24649,11 @@ let ReportStorage$a = class ReportStorage {
24280
24649
  `*Expectancy: per-trade expected value (winProb × avgWin + lossProb × avgLoss). Positive = profitable on average per trade. Break-even trades contribute 0.*`,
24281
24650
  `*All metrics require 100+ signals to be statistically reliable. Annualized metrics assume the observed trading frequency and market conditions persist year-round.*`,
24282
24651
  `*IMPORTANT: Equity curve, Expected Yearly Returns, Calmar, Recovery and Max Drawdown all assume **100% capital allocation per position** (no portfolio fraction). These metrics ignore the position-sizing subsystem (PositionSize / Kelly / ATR): pnlPercentage is a return on the position's own invested capital, never scaled by account balance. With DCA (commitAverageBuy) the cost basis is the sum of all entries and the entry price is dollar-cost-weighted, so per-trade % is measured against the averaged position, not a fixed stake. If your strategy risks X% of capital per trade, the realized portfolio return / drawdown will be roughly X/100 of the reported figures — these metrics represent a theoretical upper bound under full allocation.*`,
24652
+ `*Trend / Trend Strength / Trend Confidence: linear regression of log(close) vs days across closed-trade prices. "Trend Strength" is the slope in %/day; "Trend Confidence" is R². Classification gates on R² ≥ 0.30 (else "sideways") AND |slope| ≥ 0.25 × medianStepSize (else "neutral" — a real but weak tilt). Self-tunes to the instrument's typical move size — no magic constants on price magnitude.*`,
24653
+ `*Buyer / Seller Pressure: fraction of up-moves (resp. down-moves) among decisive close-to-close changes. Frequency-based; flats excluded.*`,
24654
+ `*Buyer / Seller Strength: share of upward (resp. downward) absolute movement in total movement. Magnitude-based. A divergence between pressure and strength surfaces asymmetry: "frequent shallow buys, rare deep sells" is a different regime than "rare deep buys, frequent shallow sells".*`,
24655
+ `*Pressure Imbalance: buyerStrength − sellerStrength ∈ [−1, +1]. Single signed summary of magnitude bias.*`,
24656
+ `*Median Step Size: median |close[i] − close[i−1]| / close[i−1] across closed trades, in %. Robust to outliers — describes the typical close-to-close jump. Used as the scale that normalises the slope-vs-step trend gate. NOTE: this is NOT classical (candle-based) volatility — there are no candles between trade closes in the report data; it measures step-distribution at the rate trades close.*`,
24283
24657
  `*Negative values for Sharpe / Sortino / Calmar / Recovery / Expected Yearly Returns indicate a losing strategy (avgPnl < 0 or totalPnl < 0). "Higher is better" still applies — closer to zero is less bad, positive is profitable.*`,
24284
24658
  ].join("\n");
24285
24659
  }
@@ -24910,6 +25284,15 @@ let ReportStorage$9 = class ReportStorage {
24910
25284
  avgConsecutiveLossPnl: null,
24911
25285
  avgWinDuration: null,
24912
25286
  avgLossDuration: null,
25287
+ medianStepSize: null,
25288
+ buyerPressure: null,
25289
+ sellerPressure: null,
25290
+ buyerStrength: null,
25291
+ sellerStrength: null,
25292
+ pressureImbalance: null,
25293
+ trend: null,
25294
+ trendStrength: null,
25295
+ trendConfidence: null,
24913
25296
  };
24914
25297
  }
24915
25298
  const closedEvents = this._eventList.filter((e) => e.action === "closed");
@@ -25208,6 +25591,14 @@ let ReportStorage$9 = class ReportStorage {
25208
25591
  const recoveryFactor = !canComputeRatios || blown || equityMaxDrawdown <= 0
25209
25592
  ? null
25210
25593
  : Math.max(-MAX_CALMAR_RATIO$1, Math.min(MAX_CALMAR_RATIO$1, ((equityFinal - 1) * 100) / equityMaxDrawdown));
25594
+ // Price profile — buyer/seller pressure, trend classification. Built only
25595
+ // from closed events' (timestamp, currentPrice) pairs in chronological
25596
+ // close order — the same data the equity curve uses. N-gated internally
25597
+ // by the helper (MIN_SIGNALS = 10).
25598
+ const priceProfile = getPriceProfile(validClosed
25599
+ .slice()
25600
+ .sort((a, b) => a.timestamp - b.timestamp)
25601
+ .map((e) => ({ closeAt: e.timestamp, close: e.currentPrice })));
25211
25602
  return {
25212
25603
  eventList: this._eventList,
25213
25604
  totalEvents: this._eventList.length,
@@ -25234,6 +25625,15 @@ let ReportStorage$9 = class ReportStorage {
25234
25625
  avgConsecutiveLossPnl: isUnsafe$3(avgConsecutiveLossPnl) ? null : avgConsecutiveLossPnl,
25235
25626
  avgWinDuration: isUnsafe$3(avgWinDuration) ? null : avgWinDuration,
25236
25627
  avgLossDuration: isUnsafe$3(avgLossDuration) ? null : avgLossDuration,
25628
+ medianStepSize: priceProfile.medianStepSize,
25629
+ buyerPressure: priceProfile.buyerPressure,
25630
+ sellerPressure: priceProfile.sellerPressure,
25631
+ buyerStrength: priceProfile.buyerStrength,
25632
+ sellerStrength: priceProfile.sellerStrength,
25633
+ pressureImbalance: priceProfile.pressureImbalance,
25634
+ trend: priceProfile.trend,
25635
+ trendStrength: priceProfile.trendStrength,
25636
+ trendConfidence: priceProfile.trendConfidence,
25237
25637
  };
25238
25638
  }
25239
25639
  /**
@@ -25290,6 +25690,15 @@ let ReportStorage$9 = class ReportStorage {
25290
25690
  `**Avg Loss Duration:** ${stats.avgLossDuration === null ? "N/A" : `${stats.avgLossDuration.toFixed(1)} min`}`,
25291
25691
  `**Avg Consecutive Win PNL:** ${stats.avgConsecutiveWinPnl === null ? "N/A" : `${stats.avgConsecutiveWinPnl > 0 ? "+" : ""}${stats.avgConsecutiveWinPnl.toFixed(3)}% (higher is better)`}`,
25292
25692
  `**Avg Consecutive Loss PNL:** ${stats.avgConsecutiveLossPnl === null ? "N/A" : `${stats.avgConsecutiveLossPnl.toFixed(3)}% (closer to 0 is better)`}`,
25693
+ `**Trend:** ${stats.trend === null ? "N/A" : stats.trend}`,
25694
+ `**Trend Strength:** ${stats.trendStrength === null ? "N/A" : `${stats.trendStrength > 0 ? "+" : ""}${stats.trendStrength.toFixed(3)}%/day`}`,
25695
+ `**Trend Confidence (R²):** ${stats.trendConfidence === null ? "N/A" : stats.trendConfidence.toFixed(3)}`,
25696
+ `**Buyer Pressure:** ${stats.buyerPressure === null ? "N/A" : `${(stats.buyerPressure * 100).toFixed(1)}%`}`,
25697
+ `**Seller Pressure:** ${stats.sellerPressure === null ? "N/A" : `${(stats.sellerPressure * 100).toFixed(1)}%`}`,
25698
+ `**Buyer Strength:** ${stats.buyerStrength === null ? "N/A" : `${(stats.buyerStrength * 100).toFixed(1)}%`}`,
25699
+ `**Seller Strength:** ${stats.sellerStrength === null ? "N/A" : `${(stats.sellerStrength * 100).toFixed(1)}%`}`,
25700
+ `**Pressure Imbalance:** ${stats.pressureImbalance === null ? "N/A" : `${stats.pressureImbalance > 0 ? "+" : ""}${stats.pressureImbalance.toFixed(3)}`}`,
25701
+ `**Median Step Size:** ${stats.medianStepSize === null ? "N/A" : `${stats.medianStepSize.toFixed(3)}%`}`,
25293
25702
  "",
25294
25703
  `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
25295
25704
  `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
@@ -25303,6 +25712,11 @@ let ReportStorage$9 = class ReportStorage {
25303
25712
  `*Expectancy: per-trade expected value (winProb × avgWin + lossProb × avgLoss). Positive = profitable on average per trade. Break-even trades contribute 0.*`,
25304
25713
  `*All metrics require 100+ signals to be statistically reliable. Annualized metrics assume the observed trading frequency and market conditions persist year-round.*`,
25305
25714
  `*IMPORTANT: Equity curve, Expected Yearly Returns, Calmar, Recovery and Max Drawdown all assume **100% capital allocation per position** (no portfolio fraction). These metrics ignore the position-sizing subsystem (PositionSize / Kelly / ATR): pnlPercentage is a return on the position's own invested capital, never scaled by account balance. With DCA (commitAverageBuy) the cost basis is the sum of all entries and the entry price is dollar-cost-weighted, so per-trade % is measured against the averaged position, not a fixed stake. If your strategy risks X% of capital per trade, the realized portfolio return / drawdown will be roughly X/100 of the reported figures — these metrics represent a theoretical upper bound under full allocation.*`,
25715
+ `*Trend / Trend Strength / Trend Confidence: linear regression of log(close) vs days across closed-trade prices. "Trend Strength" is the slope in %/day; "Trend Confidence" is R². Classification gates on R² ≥ 0.30 (else "sideways") AND |slope| ≥ 0.25 × medianStepSize (else "neutral" — a real but weak tilt). Self-tunes to the instrument's typical move size — no magic constants on price magnitude.*`,
25716
+ `*Buyer / Seller Pressure: fraction of up-moves (resp. down-moves) among decisive close-to-close changes. Frequency-based; flats excluded.*`,
25717
+ `*Buyer / Seller Strength: share of upward (resp. downward) absolute movement in total movement. Magnitude-based. A divergence between pressure and strength surfaces asymmetry: "frequent shallow buys, rare deep sells" is a different regime than "rare deep buys, frequent shallow sells".*`,
25718
+ `*Pressure Imbalance: buyerStrength − sellerStrength ∈ [−1, +1]. Single signed summary of magnitude bias.*`,
25719
+ `*Median Step Size: median |close[i] − close[i−1]| / close[i−1] across closed trades, in %. Robust to outliers — describes the typical close-to-close jump. Used as the scale that normalises the slope-vs-step trend gate. NOTE: this is NOT classical (candle-based) volatility — there are no candles between trade closes in the report data; it measures step-distribution at the rate trades close.*`,
25306
25720
  `*Negative values for Sharpe / Sortino / Calmar / Recovery / Expected Yearly Returns indicate a losing strategy (avgPnl < 0 or totalPnl < 0). "Higher is better" still applies — closer to zero is less bad, positive is profitable.*`,
25307
25721
  ].join("\n");
25308
25722
  }
@@ -27585,6 +27999,12 @@ class HeatmapStorage {
27585
27999
  calmarRatio = null;
27586
28000
  if (isUnsafe(recoveryFactor))
27587
28001
  recoveryFactor = null;
28002
+ // Price profile — buyer/seller pressure, trend classification. Built from
28003
+ // the chronological close-price series of this symbol's closed signals.
28004
+ // N-gated internally by the helper (MIN_SIGNALS = 10).
28005
+ const priceProfile = getPriceProfile([...signals]
28006
+ .sort((a, b) => a.closeTimestamp - b.closeTimestamp)
28007
+ .map((s) => ({ closeAt: s.closeTimestamp, close: s.currentPrice })));
27588
28008
  return {
27589
28009
  symbol,
27590
28010
  totalPnl,
@@ -27619,6 +28039,15 @@ class HeatmapStorage {
27619
28039
  certaintyRatio,
27620
28040
  expectedYearlyReturns,
27621
28041
  tradesPerYear,
28042
+ medianStepSize: priceProfile.medianStepSize,
28043
+ buyerPressure: priceProfile.buyerPressure,
28044
+ sellerPressure: priceProfile.sellerPressure,
28045
+ buyerStrength: priceProfile.buyerStrength,
28046
+ sellerStrength: priceProfile.sellerStrength,
28047
+ pressureImbalance: priceProfile.pressureImbalance,
28048
+ trend: priceProfile.trend,
28049
+ trendStrength: priceProfile.trendStrength,
28050
+ trendConfidence: priceProfile.trendConfidence,
27622
28051
  };
27623
28052
  }
27624
28053
  /**
@@ -28082,6 +28511,9 @@ class HeatmapStorage {
28082
28511
  `*Max Drawdown: mark-to-market — both the per-symbol and pooled equity curves apply each trade's worst intra-trade excursion (the lowest unrealized point while the position was open) before booking its realized close, so deep round-trip dips count. It is NOT realized-only (close-to-close); a realized-only curve would understate drawdown and inflate Calmar/Recovery. The pooled curve walks trades chronologically by closeTimestamp; simultaneous cross-symbol drawdowns within the same minute are still serialised (one trade applied at a time), so genuine same-instant tail correlation is not modelled.*`,
28083
28512
  `*All metrics require 100+ signals per symbol to be statistically reliable. Annualized metrics assume the observed trading frequency persists year-round.*`,
28084
28513
  `*IMPORTANT: Per-symbol equity curve, Expected Yearly Returns, Calmar, Recovery and Max Drawdown all assume **100% capital allocation per position** (no portfolio fraction). These metrics ignore the position-sizing subsystem (PositionSize / Kelly / ATR): pnlPercentage is a return on the position's own invested capital, never scaled by account balance. With DCA (commitAverageBuy) the cost basis is the sum of all entries and the entry price is dollar-cost-weighted, so per-trade % is measured against the averaged position, not a fixed stake. If your strategy risks X% of capital per trade, the realized return / drawdown will be roughly X/100 of the reported figures — these metrics represent a theoretical upper bound under full allocation.*`,
28514
+ `*Trend / Trend %/d / Trend R² (per-symbol only): linear regression of log(close) vs days across that symbol's closed-trade prices. Classification gates on R² ≥ 0.30 AND |slope| ≥ 0.25 × medianStepSize — self-tunes to the instrument. Not aggregated portfolio-wide (price series across symbols are not comparable).*`,
28515
+ `*Buyer / Seller Pres (per-symbol only): fraction of up-moves (resp. down-moves) among decisive close-to-close changes for that symbol. Buyer / Seller Str: magnitude share. Pres Imb: buyerStrength − sellerStrength ∈ [−1, +1].*`,
28516
+ `*Median Step (per-symbol only): typical |close[i] − close[i−1]| / close[i−1] across closed trades, in %. Robust to outliers. NOT classical volatility — there are no candles between closes in the report data.*`,
28085
28517
  `*Negative values for Sharpe / Annualized Sharpe / Sortino / Calmar / Recovery / Expectancy / Expected Yearly Returns indicate a losing symbol (avgPnl < 0 or totalPnl < 0). "Higher is better" still applies — closer to zero is less bad, positive is profitable.*`,
28086
28518
  ].join("\n");
28087
28519
  }
@@ -28684,6 +29116,36 @@ class WalkerValidationService {
28684
29116
  * Injected logger service instance
28685
29117
  */
28686
29118
  this.loggerService = inject(TYPES.loggerService);
29119
+ /**
29120
+ * @private
29121
+ * @readonly
29122
+ * Injected walker schema service instance
29123
+ */
29124
+ this.walkerSchemaService = inject(TYPES.walkerSchemaService);
29125
+ /**
29126
+ * @private
29127
+ * @readonly
29128
+ * Injected strategy validation service instance
29129
+ */
29130
+ this.strategyValidationService = inject(TYPES.strategyValidationService);
29131
+ /**
29132
+ * @private
29133
+ * @readonly
29134
+ * Injected strategy schema service instance
29135
+ */
29136
+ this.strategySchemaService = inject(TYPES.strategySchemaService);
29137
+ /**
29138
+ * @private
29139
+ * @readonly
29140
+ * Injected risk validation service instance
29141
+ */
29142
+ this.riskValidationService = inject(TYPES.riskValidationService);
29143
+ /**
29144
+ * @private
29145
+ * @readonly
29146
+ * Injected action validation service instance
29147
+ */
29148
+ this.actionValidationService = inject(TYPES.actionValidationService);
28687
29149
  /**
28688
29150
  * @private
28689
29151
  * Map storing walker schemas by walker name
@@ -28705,9 +29167,12 @@ class WalkerValidationService {
28705
29167
  this._walkerMap.set(walkerName, walkerSchema);
28706
29168
  };
28707
29169
  /**
28708
- * Validates the existence of a walker
29170
+ * Validates the existence of a walker and its associated strategy configurations.
29171
+ * Each strategy referenced by the walker is validated via StrategyValidationService,
29172
+ * which in turn validates the strategy's risk profiles and actions.
28709
29173
  * @public
28710
29174
  * @throws {Error} If walkerName is not found
29175
+ * @throws {Error} If any referenced strategy (or its risk/actions) is invalid
28711
29176
  * Memoized function to cache validation results
28712
29177
  */
28713
29178
  this.validate = memoize(([walkerName]) => walkerName, (walkerName, source) => {
@@ -28719,6 +29184,14 @@ class WalkerValidationService {
28719
29184
  if (!walker) {
28720
29185
  throw new Error(`walker ${walkerName} not found source=${source}`);
28721
29186
  }
29187
+ const walkerSchema = this.walkerSchemaService.get(walkerName);
29188
+ for (const strategyName of walkerSchema.strategies) {
29189
+ const { riskName, riskList, actions } = this.strategySchemaService.get(strategyName);
29190
+ this.strategyValidationService.validate(strategyName, source);
29191
+ riskName && this.riskValidationService.validate(riskName, source);
29192
+ riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, source));
29193
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, source));
29194
+ }
28722
29195
  return true;
28723
29196
  });
28724
29197
  /**
@@ -30155,6 +30628,10 @@ class PartialGlobalService {
30155
30628
  * Frame validation service for validating frame existence.
30156
30629
  */
30157
30630
  this.frameValidationService = inject(TYPES.frameValidationService);
30631
+ /**
30632
+ * Action validation service for validating action existence.
30633
+ */
30634
+ this.actionValidationService = inject(TYPES.actionValidationService);
30158
30635
  /**
30159
30636
  * Validates strategy and associated risk configuration.
30160
30637
  * Memoized to avoid redundant validations for the same strategy-exchange-frame combination.
@@ -30170,9 +30647,10 @@ class PartialGlobalService {
30170
30647
  this.strategyValidationService.validate(context.strategyName, methodName);
30171
30648
  this.exchangeValidationService.validate(context.exchangeName, methodName);
30172
30649
  context.frameName && this.frameValidationService.validate(context.frameName, methodName);
30173
- const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
30650
+ const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
30174
30651
  riskName && this.riskValidationService.validate(riskName, methodName);
30175
30652
  riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, methodName));
30653
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, methodName));
30176
30654
  });
30177
30655
  /**
30178
30656
  * Processes profit state and emits events for newly reached profit levels.
@@ -31239,6 +31717,10 @@ class BreakevenGlobalService {
31239
31717
  * Frame validation service for validating frame existence.
31240
31718
  */
31241
31719
  this.frameValidationService = inject(TYPES.frameValidationService);
31720
+ /**
31721
+ * Action validation service for validating frame existence.
31722
+ */
31723
+ this.actionValidationService = inject(TYPES.actionValidationService);
31242
31724
  /**
31243
31725
  * Validates strategy and associated risk configuration.
31244
31726
  * Memoized to avoid redundant validations for the same strategy-exchange-frame combination.
@@ -31254,9 +31736,10 @@ class BreakevenGlobalService {
31254
31736
  this.strategyValidationService.validate(context.strategyName, methodName);
31255
31737
  this.exchangeValidationService.validate(context.exchangeName, methodName);
31256
31738
  context.frameName && this.frameValidationService.validate(context.frameName, methodName);
31257
- const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
31739
+ const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
31258
31740
  riskName && this.riskValidationService.validate(riskName, methodName);
31259
31741
  riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, methodName));
31742
+ actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, methodName));
31260
31743
  });
31261
31744
  /**
31262
31745
  * Checks if breakeven should be triggered and emits event if conditions met.
@@ -50598,6 +51081,7 @@ const GET_TIMESTAMP_METHOD_NAME = "meta.getTimestamp";
50598
51081
  const GET_MODE_METHOD_NAME = "meta.getMode";
50599
51082
  const GET_SYMBOL_METHOD_NAME = "meta.getSymbol";
50600
51083
  const GET_CONTEXT_METHOD_NAME = "meta.getContext";
51084
+ const GET_RUNTIME_INFO_METHOD_NAME = "meta.getRuntimeInfo";
50601
51085
  /**
50602
51086
  * Gets the current date from execution context.
50603
51087
  *
@@ -50714,6 +51198,45 @@ async function getContext() {
50714
51198
  }
50715
51199
  return backtest.methodContextService.context;
50716
51200
  }
51201
+ /**
51202
+ * Gets runtime information about the current execution environment.
51203
+ *
51204
+ * This includes details such as the current symbol, exchange, timeframe, strategy, and whether it's a backtest or live run.
51205
+ *
51206
+ * @returns Promise resolving to an object containing runtime information
51207
+ * @throws Error if method context or execution context is not active
51208
+ *
51209
+ * @example
51210
+ * ```typescript
51211
+ * const runtimeInfo = await getRuntimeInfo();
51212
+ * console.log(runtimeInfo);
51213
+ * // {
51214
+ * // symbol: "BTCUSDT",
51215
+ * // context: {,
51216
+ * // exchangeName: "Binance",
51217
+ * // frameName: "1m",
51218
+ * // strategyName: "MyStrategy",
51219
+ * // },
51220
+ * // backtest: false
51221
+ * // }
51222
+ * ```
51223
+ */
51224
+ async function getRuntimeInfo() {
51225
+ backtest.loggerService.info(GET_RUNTIME_INFO_METHOD_NAME);
51226
+ if (!MethodContextService.hasContext()) {
51227
+ throw new Error("getRuntimeInfo requires a method context");
51228
+ }
51229
+ if (!ExecutionContextService.hasContext()) {
51230
+ throw new Error("getRuntimeInfo requires an execution context");
51231
+ }
51232
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
51233
+ const { symbol, backtest: isBacktest } = backtest.executionContextService.context;
51234
+ return await backtest.runtimeMetaService.getRuntimeInfo(symbol, {
51235
+ exchangeName,
51236
+ frameName,
51237
+ strategyName,
51238
+ }, isBacktest);
51239
+ }
50717
51240
 
50718
51241
  const RECENT_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentPersistBacktestUtils.handleActivePing";
50719
51242
  const RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL = "RecentPersistBacktestUtils.getLatestSignal";
@@ -66610,4 +67133,4 @@ const validateSignal = (signal, currentPrice) => {
66610
67133
  return !errors.length;
66611
67134
  };
66612
67135
 
66613
- 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, 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, getRawCandles, getRiskSchema, 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, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, waitForReady, warmCandles, writeMemory };
67136
+ 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, 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, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, waitForReady, warmCandles, writeMemory };