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.
- package/LICENSE +21 -21
- package/README.md +1996 -1996
- package/build/index.cjs +661 -136
- package/build/index.mjs +660 -137
- package/package.json +86 -86
- 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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
16481
|
-
this.loggerService.log(METHOD_NAME_VALIDATE$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
16793
|
-
this.loggerService.log(METHOD_NAME_VALIDATE$
|
|
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$
|
|
16798
|
-
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$
|
|
16799
|
-
context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$
|
|
16800
|
-
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$
|
|
16801
|
-
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
18332
|
-
this.loggerService.log(METHOD_NAME_VALIDATE$
|
|
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$
|
|
18337
|
-
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$
|
|
18338
|
-
context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$
|
|
18339
|
-
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$
|
|
18340
|
-
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$
|
|
18341
|
-
actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_VALIDATE$
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 };
|