backtest-kit 1.5.44 → 1.5.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # 🧿 Backtest Kit
4
4
 
5
- > A TypeScript framework for backtesting and live trading strategies on crypto markets or forex, with crash-safe persistence, signal validation, and AI optimization.
5
+ > A TypeScript framework for backtesting and live trading strategies on multi-asset, crypto or forex, with crash-safe persistence, signal validation, and AI optimization.
6
6
 
7
7
  ![future](./assets/prophet.png)
8
8
 
package/build/index.cjs CHANGED
@@ -4175,16 +4175,30 @@ class SizingConnectionService {
4175
4175
  }
4176
4176
  }
4177
4177
 
4178
+ /**
4179
+ * Retrieves a value from an object using a given path.
4180
+ *
4181
+ * @param object - The object from which to retrieve the value.
4182
+ * @param path - The path to the desired value, either as an array or dot-separated string.
4183
+ * @returns - The value at the specified path, or undefined if it does not exist.
4184
+ */
4185
+ const get = (object, path) => {
4186
+ const pathArray = Array.isArray(path) ? path : path.split('.').filter((key) => key);
4187
+ const pathArrayFlat = pathArray.flatMap((part) => typeof part === 'string' ? part.split('.') : part);
4188
+ return pathArrayFlat.reduce((obj, key) => obj && obj[key], object);
4189
+ };
4190
+
4191
+ /** Symbol indicating that a signal was rejected */
4192
+ const SYMBOL_REJECTED = Symbol("rejected");
4178
4193
  /** Symbol indicating that positions need to be fetched from persistence */
4179
4194
  const POSITION_NEED_FETCH = Symbol("risk-need-fetch");
4180
4195
  /** Key generator for active position map */
4181
4196
  const GET_KEY_FN = (strategyName, symbol) => `${strategyName}:${symbol}`;
4182
4197
  /** Wrapper to execute risk validation function with error handling */
4183
4198
  const DO_VALIDATION_FN = functoolsKit.trycatch(async (validation, params) => {
4184
- await validation(params);
4185
- return true;
4199
+ return await validation(params);
4186
4200
  }, {
4187
- defaultValue: false,
4201
+ defaultValue: SYMBOL_REJECTED,
4188
4202
  fallback: (error) => {
4189
4203
  const message = "ClientRisk exception thrown";
4190
4204
  const payload = {
@@ -4204,7 +4218,9 @@ const DO_VALIDATION_FN = functoolsKit.trycatch(async (validation, params) => {
4204
4218
  * In backtest mode, initializes with empty Map. In live mode, reads from persist storage.
4205
4219
  */
4206
4220
  const WAIT_FOR_INIT_FN$1 = async (self) => {
4207
- self.params.logger.debug("ClientRisk waitForInit", { backtest: self.params.backtest });
4221
+ self.params.logger.debug("ClientRisk waitForInit", {
4222
+ backtest: self.params.backtest,
4223
+ });
4208
4224
  if (self.params.backtest) {
4209
4225
  self._activePositions = new Map();
4210
4226
  return;
@@ -4267,26 +4283,39 @@ class ClientRisk {
4267
4283
  activePositionCount: riskMap.size,
4268
4284
  activePositions: Array.from(riskMap.values()),
4269
4285
  };
4270
- // Execute custom validations
4271
- let isValid = true;
4272
- let rejectionNote = "N/A";
4286
+ let rejectionResult = null;
4273
4287
  if (this.params.validations) {
4274
4288
  for (const validation of this.params.validations) {
4275
- if (functoolsKit.not(await DO_VALIDATION_FN(typeof validation === "function"
4276
- ? validation
4277
- : validation.validate, payload))) {
4278
- isValid = false;
4279
- // Capture note from validation if available
4280
- if (typeof validation !== "function" && validation.note) {
4281
- rejectionNote = validation.note;
4282
- }
4289
+ const rejection = await DO_VALIDATION_FN(typeof validation === "function" ? validation : validation.validate, payload);
4290
+ if (!rejection) {
4291
+ continue;
4292
+ }
4293
+ if (typeof rejection === "symbol") {
4294
+ rejectionResult = {
4295
+ id: null,
4296
+ note: "note" in validation ? validation.note : "Validation failed",
4297
+ };
4298
+ break;
4299
+ }
4300
+ if (typeof rejection === "string") {
4301
+ rejectionResult = {
4302
+ id: null,
4303
+ note: rejection,
4304
+ };
4305
+ break;
4306
+ }
4307
+ if (functoolsKit.isObject(rejection)) {
4308
+ rejectionResult = {
4309
+ id: get(rejection, "id") || null,
4310
+ note: get(rejection, "note") || "Validation rejected the signal",
4311
+ };
4283
4312
  break;
4284
4313
  }
4285
4314
  }
4286
4315
  }
4287
- if (!isValid) {
4316
+ if (rejectionResult) {
4288
4317
  // Call params.onRejected for riskSubject emission
4289
- await this.params.onRejected(params.symbol, params, riskMap.size, rejectionNote, Date.now(), this.params.backtest);
4318
+ await this.params.onRejected(params.symbol, params, riskMap.size, rejectionResult, params.timestamp, this.params.backtest);
4290
4319
  // Call schema callbacks.onRejected if defined
4291
4320
  if (this.params.callbacks?.onRejected) {
4292
4321
  this.params.callbacks.onRejected(params.symbol, params);
@@ -4365,18 +4394,19 @@ class ClientRisk {
4365
4394
  * @param symbol - Trading pair symbol
4366
4395
  * @param params - Risk check arguments
4367
4396
  * @param activePositionCount - Number of active positions at rejection time
4368
- * @param comment - Rejection reason from validation note or "N/A"
4397
+ * @param rejectionResult - Rejection result with id and note
4369
4398
  * @param timestamp - Event timestamp in milliseconds
4370
4399
  * @param backtest - True if backtest mode, false if live mode
4371
4400
  */
4372
- const COMMIT_REJECTION_FN = async (symbol, params, activePositionCount, comment, timestamp, backtest) => await riskSubject.next({
4401
+ const COMMIT_REJECTION_FN = async (symbol, params, activePositionCount, rejectionResult, timestamp, backtest) => await riskSubject.next({
4373
4402
  symbol,
4374
4403
  pendingSignal: params.pendingSignal,
4375
4404
  strategyName: params.strategyName,
4376
4405
  exchangeName: params.exchangeName,
4377
4406
  currentPrice: params.currentPrice,
4378
4407
  activePositionCount,
4379
- comment,
4408
+ rejectionId: rejectionResult.id,
4409
+ rejectionNote: rejectionResult.note,
4380
4410
  timestamp,
4381
4411
  backtest,
4382
4412
  });
@@ -7019,11 +7049,13 @@ const performance_columns = [
7019
7049
  * - Signal identification (symbol, strategy name, signal ID, position)
7020
7050
  * - Exchange information (exchange name, active position count)
7021
7051
  * - Price data (open price, take profit, stop loss, current price)
7022
- * - Rejection details (rejection reason/comment, timestamp)
7052
+ * - Rejection details (rejection ID, rejection reason, timestamp)
7023
7053
  *
7024
7054
  * @remarks
7025
7055
  * This configuration helps analyze when and why the risk management system rejected signals.
7026
- * The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
7056
+ * - The "note" column (signal note) visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
7057
+ * - The "rejectionNote" column (rejection reason) is always visible as it contains critical risk information.
7058
+ * - The "rejectionId" can be used to correlate rejections with signal IDs for debugging.
7027
7059
  * Useful for tuning risk parameters and understanding risk control effectiveness.
7028
7060
  *
7029
7061
  * @example
@@ -7036,7 +7068,7 @@ const performance_columns = [
7036
7068
  *
7037
7069
  * // Or customize to focus on rejection reasons
7038
7070
  * const customColumns = risk_columns.filter(col =>
7039
- * ["symbol", "strategyName", "comment", "activePositionCount", "timestamp"].includes(col.key)
7071
+ * ["symbol", "strategyName", "rejectionId", "rejectionNote", "activePositionCount", "timestamp"].includes(col.key)
7040
7072
  * );
7041
7073
  * await service.getReport("BTCUSDT", "my-strategy", customColumns);
7042
7074
  * ```
@@ -7064,18 +7096,18 @@ const risk_columns = [
7064
7096
  format: (data) => data.pendingSignal.id || "N/A",
7065
7097
  isVisible: () => true,
7066
7098
  },
7067
- {
7068
- key: "position",
7069
- label: "Position",
7070
- format: (data) => data.pendingSignal.position.toUpperCase(),
7071
- isVisible: () => true,
7072
- },
7073
7099
  {
7074
7100
  key: "note",
7075
7101
  label: "Note",
7076
7102
  format: (data) => toPlainString(data.pendingSignal.note ?? "N/A"),
7077
7103
  isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
7078
7104
  },
7105
+ {
7106
+ key: "position",
7107
+ label: "Position",
7108
+ format: (data) => data.pendingSignal.position.toUpperCase(),
7109
+ isVisible: () => true,
7110
+ },
7079
7111
  {
7080
7112
  key: "exchangeName",
7081
7113
  label: "Exchange",
@@ -7119,9 +7151,15 @@ const risk_columns = [
7119
7151
  isVisible: () => true,
7120
7152
  },
7121
7153
  {
7122
- key: "comment",
7123
- label: "Reason",
7124
- format: (data) => data.comment,
7154
+ key: "rejectionId",
7155
+ label: "ID",
7156
+ format: (data) => data.rejectionId ?? "N/A",
7157
+ isVisible: () => true,
7158
+ },
7159
+ {
7160
+ key: "rejectionNote",
7161
+ label: "Rejection Reason",
7162
+ format: (data) => data.rejectionNote,
7125
7163
  isVisible: () => true,
7126
7164
  },
7127
7165
  {
package/build/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createActivator } from 'di-kit';
2
2
  import { scoped } from 'di-scoped';
3
- import { errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, ToolRegistry, isObject, and, resolveDocuments, str, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
3
+ import { errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, isObject, ToolRegistry, and, resolveDocuments, str, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
4
4
  import fs, { mkdir, writeFile } from 'fs/promises';
5
5
  import path, { join } from 'path';
6
6
  import crypto from 'crypto';
@@ -4173,16 +4173,30 @@ class SizingConnectionService {
4173
4173
  }
4174
4174
  }
4175
4175
 
4176
+ /**
4177
+ * Retrieves a value from an object using a given path.
4178
+ *
4179
+ * @param object - The object from which to retrieve the value.
4180
+ * @param path - The path to the desired value, either as an array or dot-separated string.
4181
+ * @returns - The value at the specified path, or undefined if it does not exist.
4182
+ */
4183
+ const get = (object, path) => {
4184
+ const pathArray = Array.isArray(path) ? path : path.split('.').filter((key) => key);
4185
+ const pathArrayFlat = pathArray.flatMap((part) => typeof part === 'string' ? part.split('.') : part);
4186
+ return pathArrayFlat.reduce((obj, key) => obj && obj[key], object);
4187
+ };
4188
+
4189
+ /** Symbol indicating that a signal was rejected */
4190
+ const SYMBOL_REJECTED = Symbol("rejected");
4176
4191
  /** Symbol indicating that positions need to be fetched from persistence */
4177
4192
  const POSITION_NEED_FETCH = Symbol("risk-need-fetch");
4178
4193
  /** Key generator for active position map */
4179
4194
  const GET_KEY_FN = (strategyName, symbol) => `${strategyName}:${symbol}`;
4180
4195
  /** Wrapper to execute risk validation function with error handling */
4181
4196
  const DO_VALIDATION_FN = trycatch(async (validation, params) => {
4182
- await validation(params);
4183
- return true;
4197
+ return await validation(params);
4184
4198
  }, {
4185
- defaultValue: false,
4199
+ defaultValue: SYMBOL_REJECTED,
4186
4200
  fallback: (error) => {
4187
4201
  const message = "ClientRisk exception thrown";
4188
4202
  const payload = {
@@ -4202,7 +4216,9 @@ const DO_VALIDATION_FN = trycatch(async (validation, params) => {
4202
4216
  * In backtest mode, initializes with empty Map. In live mode, reads from persist storage.
4203
4217
  */
4204
4218
  const WAIT_FOR_INIT_FN$1 = async (self) => {
4205
- self.params.logger.debug("ClientRisk waitForInit", { backtest: self.params.backtest });
4219
+ self.params.logger.debug("ClientRisk waitForInit", {
4220
+ backtest: self.params.backtest,
4221
+ });
4206
4222
  if (self.params.backtest) {
4207
4223
  self._activePositions = new Map();
4208
4224
  return;
@@ -4265,26 +4281,39 @@ class ClientRisk {
4265
4281
  activePositionCount: riskMap.size,
4266
4282
  activePositions: Array.from(riskMap.values()),
4267
4283
  };
4268
- // Execute custom validations
4269
- let isValid = true;
4270
- let rejectionNote = "N/A";
4284
+ let rejectionResult = null;
4271
4285
  if (this.params.validations) {
4272
4286
  for (const validation of this.params.validations) {
4273
- if (not(await DO_VALIDATION_FN(typeof validation === "function"
4274
- ? validation
4275
- : validation.validate, payload))) {
4276
- isValid = false;
4277
- // Capture note from validation if available
4278
- if (typeof validation !== "function" && validation.note) {
4279
- rejectionNote = validation.note;
4280
- }
4287
+ const rejection = await DO_VALIDATION_FN(typeof validation === "function" ? validation : validation.validate, payload);
4288
+ if (!rejection) {
4289
+ continue;
4290
+ }
4291
+ if (typeof rejection === "symbol") {
4292
+ rejectionResult = {
4293
+ id: null,
4294
+ note: "note" in validation ? validation.note : "Validation failed",
4295
+ };
4296
+ break;
4297
+ }
4298
+ if (typeof rejection === "string") {
4299
+ rejectionResult = {
4300
+ id: null,
4301
+ note: rejection,
4302
+ };
4303
+ break;
4304
+ }
4305
+ if (isObject(rejection)) {
4306
+ rejectionResult = {
4307
+ id: get(rejection, "id") || null,
4308
+ note: get(rejection, "note") || "Validation rejected the signal",
4309
+ };
4281
4310
  break;
4282
4311
  }
4283
4312
  }
4284
4313
  }
4285
- if (!isValid) {
4314
+ if (rejectionResult) {
4286
4315
  // Call params.onRejected for riskSubject emission
4287
- await this.params.onRejected(params.symbol, params, riskMap.size, rejectionNote, Date.now(), this.params.backtest);
4316
+ await this.params.onRejected(params.symbol, params, riskMap.size, rejectionResult, params.timestamp, this.params.backtest);
4288
4317
  // Call schema callbacks.onRejected if defined
4289
4318
  if (this.params.callbacks?.onRejected) {
4290
4319
  this.params.callbacks.onRejected(params.symbol, params);
@@ -4363,18 +4392,19 @@ class ClientRisk {
4363
4392
  * @param symbol - Trading pair symbol
4364
4393
  * @param params - Risk check arguments
4365
4394
  * @param activePositionCount - Number of active positions at rejection time
4366
- * @param comment - Rejection reason from validation note or "N/A"
4395
+ * @param rejectionResult - Rejection result with id and note
4367
4396
  * @param timestamp - Event timestamp in milliseconds
4368
4397
  * @param backtest - True if backtest mode, false if live mode
4369
4398
  */
4370
- const COMMIT_REJECTION_FN = async (symbol, params, activePositionCount, comment, timestamp, backtest) => await riskSubject.next({
4399
+ const COMMIT_REJECTION_FN = async (symbol, params, activePositionCount, rejectionResult, timestamp, backtest) => await riskSubject.next({
4371
4400
  symbol,
4372
4401
  pendingSignal: params.pendingSignal,
4373
4402
  strategyName: params.strategyName,
4374
4403
  exchangeName: params.exchangeName,
4375
4404
  currentPrice: params.currentPrice,
4376
4405
  activePositionCount,
4377
- comment,
4406
+ rejectionId: rejectionResult.id,
4407
+ rejectionNote: rejectionResult.note,
4378
4408
  timestamp,
4379
4409
  backtest,
4380
4410
  });
@@ -7017,11 +7047,13 @@ const performance_columns = [
7017
7047
  * - Signal identification (symbol, strategy name, signal ID, position)
7018
7048
  * - Exchange information (exchange name, active position count)
7019
7049
  * - Price data (open price, take profit, stop loss, current price)
7020
- * - Rejection details (rejection reason/comment, timestamp)
7050
+ * - Rejection details (rejection ID, rejection reason, timestamp)
7021
7051
  *
7022
7052
  * @remarks
7023
7053
  * This configuration helps analyze when and why the risk management system rejected signals.
7024
- * The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
7054
+ * - The "note" column (signal note) visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
7055
+ * - The "rejectionNote" column (rejection reason) is always visible as it contains critical risk information.
7056
+ * - The "rejectionId" can be used to correlate rejections with signal IDs for debugging.
7025
7057
  * Useful for tuning risk parameters and understanding risk control effectiveness.
7026
7058
  *
7027
7059
  * @example
@@ -7034,7 +7066,7 @@ const performance_columns = [
7034
7066
  *
7035
7067
  * // Or customize to focus on rejection reasons
7036
7068
  * const customColumns = risk_columns.filter(col =>
7037
- * ["symbol", "strategyName", "comment", "activePositionCount", "timestamp"].includes(col.key)
7069
+ * ["symbol", "strategyName", "rejectionId", "rejectionNote", "activePositionCount", "timestamp"].includes(col.key)
7038
7070
  * );
7039
7071
  * await service.getReport("BTCUSDT", "my-strategy", customColumns);
7040
7072
  * ```
@@ -7062,18 +7094,18 @@ const risk_columns = [
7062
7094
  format: (data) => data.pendingSignal.id || "N/A",
7063
7095
  isVisible: () => true,
7064
7096
  },
7065
- {
7066
- key: "position",
7067
- label: "Position",
7068
- format: (data) => data.pendingSignal.position.toUpperCase(),
7069
- isVisible: () => true,
7070
- },
7071
7097
  {
7072
7098
  key: "note",
7073
7099
  label: "Note",
7074
7100
  format: (data) => toPlainString(data.pendingSignal.note ?? "N/A"),
7075
7101
  isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
7076
7102
  },
7103
+ {
7104
+ key: "position",
7105
+ label: "Position",
7106
+ format: (data) => data.pendingSignal.position.toUpperCase(),
7107
+ isVisible: () => true,
7108
+ },
7077
7109
  {
7078
7110
  key: "exchangeName",
7079
7111
  label: "Exchange",
@@ -7117,9 +7149,15 @@ const risk_columns = [
7117
7149
  isVisible: () => true,
7118
7150
  },
7119
7151
  {
7120
- key: "comment",
7121
- label: "Reason",
7122
- format: (data) => data.comment,
7152
+ key: "rejectionId",
7153
+ label: "ID",
7154
+ format: (data) => data.rejectionId ?? "N/A",
7155
+ isVisible: () => true,
7156
+ },
7157
+ {
7158
+ key: "rejectionNote",
7159
+ label: "Rejection Reason",
7160
+ format: (data) => data.rejectionNote,
7123
7161
  isVisible: () => true,
7124
7162
  },
7125
7163
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "1.5.44",
3
+ "version": "1.5.45",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -759,6 +759,11 @@ declare const MethodContextService: (new () => {
759
759
  };
760
760
  }, "prototype"> & di_scoped.IScopedClassRun<[context: IMethodContext]>;
761
761
 
762
+ /**
763
+ * Risk rejection result type.
764
+ * Can be void, null, or an IRiskRejectionResult object.
765
+ */
766
+ type RiskRejection = void | IRiskRejectionResult | string | null;
762
767
  /**
763
768
  * Risk check arguments for evaluating whether to allow opening a new position.
764
769
  * Called BEFORE signal creation to validate if conditions allow new signals.
@@ -812,12 +817,23 @@ interface IRiskValidationPayload extends IRiskCheckArgs {
812
817
  /** List of currently active positions across all strategies */
813
818
  activePositions: IRiskActivePosition[];
814
819
  }
820
+ /**
821
+ * Risk validation rejection result.
822
+ * Returned when validation fails, contains debugging information.
823
+ */
824
+ interface IRiskRejectionResult {
825
+ /** Unique identifier for this rejection instance */
826
+ id: string | null;
827
+ /** Human-readable reason for rejection */
828
+ note: string;
829
+ }
815
830
  /**
816
831
  * Risk validation function type.
817
- * Validates risk parameters and throws error if validation fails.
832
+ * Returns null/void if validation passes, IRiskRejectionResult if validation fails.
833
+ * Can also throw error which will be caught and converted to IRiskRejectionResult.
818
834
  */
819
835
  interface IRiskValidationFn {
820
- (payload: IRiskValidationPayload): void | Promise<void>;
836
+ (payload: IRiskValidationPayload): RiskRejection | Promise<RiskRejection>;
821
837
  }
822
838
  /**
823
839
  * Risk validation configuration.
@@ -865,11 +881,11 @@ interface IRiskParams extends IRiskSchema {
865
881
  * @param symbol - Trading pair symbol
866
882
  * @param params - Risk check arguments
867
883
  * @param activePositionCount - Number of active positions at rejection time
868
- * @param comment - Rejection reason from validation note or "N/A"
884
+ * @param rejectionResult - Rejection result with id and note
869
885
  * @param timestamp - Event timestamp in milliseconds
870
886
  * @param backtest - True if backtest mode, false if live mode
871
887
  */
872
- onRejected: (symbol: string, params: IRiskCheckArgs, activePositionCount: number, comment: string, timestamp: number, backtest: boolean) => void | Promise<void>;
888
+ onRejected: (symbol: string, params: IRiskCheckArgs, activePositionCount: number, rejectionResult: IRiskRejectionResult, timestamp: number, backtest: boolean) => void | Promise<void>;
873
889
  }
874
890
  /**
875
891
  * Risk interface implemented by ClientRisk.
@@ -3124,16 +3140,22 @@ interface RiskContract {
3124
3140
  */
3125
3141
  activePositionCount: number;
3126
3142
  /**
3127
- * Comment describing why the signal was rejected.
3128
- * Captured from IRiskValidation.note or "N/A" if not provided.
3143
+ * Unique identifier for this rejection instance.
3144
+ * Generated by ClientRisk for tracking and debugging purposes.
3145
+ * Null if validation threw exception without custom ID.
3146
+ */
3147
+ rejectionId: string | null;
3148
+ /**
3149
+ * Human-readable reason why the signal was rejected.
3150
+ * Captured from IRiskValidation.note or error message.
3129
3151
  *
3130
3152
  * @example
3131
3153
  * ```typescript
3132
- * console.log(`Rejection reason: ${event.comment}`);
3154
+ * console.log(`Rejection reason: ${event.rejectionNote}`);
3133
3155
  * // Output: "Rejection reason: Max 3 positions allowed"
3134
3156
  * ```
3135
3157
  */
3136
- comment: string;
3158
+ rejectionNote: string;
3137
3159
  /**
3138
3160
  * Event timestamp in milliseconds since Unix epoch.
3139
3161
  * Represents when the signal was rejected.
@@ -4540,8 +4562,10 @@ interface RiskEvent {
4540
4562
  currentPrice: number;
4541
4563
  /** Number of active positions at rejection time */
4542
4564
  activePositionCount: number;
4565
+ /** Unique identifier for this rejection instance (null if validation threw exception without custom ID) */
4566
+ rejectionId: string | null;
4543
4567
  /** Rejection reason from validation note */
4544
- comment: string;
4568
+ rejectionNote: string;
4545
4569
  /** Whether this event is from backtest mode (true) or live mode (false) */
4546
4570
  backtest: boolean;
4547
4571
  }
@@ -5348,7 +5372,7 @@ declare class BacktestUtils {
5348
5372
  id: string;
5349
5373
  symbol: string;
5350
5374
  strategyName: string;
5351
- status: "pending" | "fulfilled" | "rejected" | "ready";
5375
+ status: "rejected" | "pending" | "fulfilled" | "ready";
5352
5376
  }[]>;
5353
5377
  }
5354
5378
  /**
@@ -5714,7 +5738,7 @@ declare class LiveUtils {
5714
5738
  id: string;
5715
5739
  symbol: string;
5716
5740
  strategyName: string;
5717
- status: "pending" | "fulfilled" | "rejected" | "ready";
5741
+ status: "rejected" | "pending" | "fulfilled" | "ready";
5718
5742
  }[]>;
5719
5743
  }
5720
5744
  /**
@@ -6597,7 +6621,7 @@ declare class WalkerUtils {
6597
6621
  id: string;
6598
6622
  symbol: string;
6599
6623
  walkerName: string;
6600
- status: "pending" | "fulfilled" | "rejected" | "ready";
6624
+ status: "rejected" | "pending" | "fulfilled" | "ready";
6601
6625
  }[]>;
6602
6626
  }
6603
6627
  /**