backtest-kit 1.5.13 → 1.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +106 -63
- package/build/index.mjs +106 -63
- package/package.json +1 -1
- package/types.d.ts +15 -10
package/build/index.cjs
CHANGED
|
@@ -167,10 +167,12 @@ const schemaServices$1 = {
|
|
|
167
167
|
riskSchemaService: Symbol('riskSchemaService'),
|
|
168
168
|
optimizerSchemaService: Symbol('optimizerSchemaService'),
|
|
169
169
|
};
|
|
170
|
+
const coreServices$1 = {
|
|
171
|
+
exchangeCoreService: Symbol('exchangeCoreService'),
|
|
172
|
+
strategyCoreService: Symbol('strategyCoreService'),
|
|
173
|
+
frameCoreService: Symbol('frameCoreService'),
|
|
174
|
+
};
|
|
170
175
|
const globalServices$1 = {
|
|
171
|
-
exchangeGlobalService: Symbol('exchangeGlobalService'),
|
|
172
|
-
strategyGlobalService: Symbol('strategyGlobalService'),
|
|
173
|
-
frameGlobalService: Symbol('frameGlobalService'),
|
|
174
176
|
sizingGlobalService: Symbol('sizingGlobalService'),
|
|
175
177
|
riskGlobalService: Symbol('riskGlobalService'),
|
|
176
178
|
optimizerGlobalService: Symbol('optimizerGlobalService'),
|
|
@@ -219,6 +221,7 @@ const TYPES = {
|
|
|
219
221
|
...contextServices$1,
|
|
220
222
|
...connectionServices$1,
|
|
221
223
|
...schemaServices$1,
|
|
224
|
+
...coreServices$1,
|
|
222
225
|
...globalServices$1,
|
|
223
226
|
...commandServices$1,
|
|
224
227
|
...logicPrivateServices$1,
|
|
@@ -2001,15 +2004,6 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
2001
2004
|
self._lastSignalTimestamp = currentTime;
|
|
2002
2005
|
}
|
|
2003
2006
|
const currentPrice = await self.params.exchange.getAveragePrice(self.params.execution.context.symbol);
|
|
2004
|
-
if (await functoolsKit.not(self.params.risk.checkSignal({
|
|
2005
|
-
symbol: self.params.execution.context.symbol,
|
|
2006
|
-
strategyName: self.params.method.context.strategyName,
|
|
2007
|
-
exchangeName: self.params.method.context.exchangeName,
|
|
2008
|
-
currentPrice,
|
|
2009
|
-
timestamp: currentTime,
|
|
2010
|
-
}))) {
|
|
2011
|
-
return null;
|
|
2012
|
-
}
|
|
2013
2007
|
const timeoutMs = GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS * 1000;
|
|
2014
2008
|
const signal = await Promise.race([
|
|
2015
2009
|
self.params.getSignal(self.params.execution.context.symbol, self.params.execution.context.when),
|
|
@@ -2024,6 +2018,16 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
2024
2018
|
if (self._isStopped) {
|
|
2025
2019
|
return null;
|
|
2026
2020
|
}
|
|
2021
|
+
if (await functoolsKit.not(self.params.risk.checkSignal({
|
|
2022
|
+
pendingSignal: signal,
|
|
2023
|
+
symbol: self.params.execution.context.symbol,
|
|
2024
|
+
strategyName: self.params.method.context.strategyName,
|
|
2025
|
+
exchangeName: self.params.method.context.exchangeName,
|
|
2026
|
+
currentPrice,
|
|
2027
|
+
timestamp: currentTime,
|
|
2028
|
+
}))) {
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
2027
2031
|
// Если priceOpen указан - проверяем нужно ли ждать активации или открыть сразу
|
|
2028
2032
|
if (signal.priceOpen !== undefined) {
|
|
2029
2033
|
// КРИТИЧЕСКАЯ ПРОВЕРКА: достигнут ли priceOpen?
|
|
@@ -2255,6 +2259,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
|
|
|
2255
2259
|
});
|
|
2256
2260
|
if (await functoolsKit.not(self.params.risk.checkSignal({
|
|
2257
2261
|
symbol: self.params.execution.context.symbol,
|
|
2262
|
+
pendingSignal: scheduled,
|
|
2258
2263
|
strategyName: self.params.method.context.strategyName,
|
|
2259
2264
|
exchangeName: self.params.method.context.exchangeName,
|
|
2260
2265
|
currentPrice: scheduled.priceOpen,
|
|
@@ -2338,6 +2343,7 @@ const OPEN_NEW_SCHEDULED_SIGNAL_FN = async (self, signal) => {
|
|
|
2338
2343
|
};
|
|
2339
2344
|
const OPEN_NEW_PENDING_SIGNAL_FN = async (self, signal) => {
|
|
2340
2345
|
if (await functoolsKit.not(self.params.risk.checkSignal({
|
|
2346
|
+
pendingSignal: signal,
|
|
2341
2347
|
symbol: self.params.execution.context.symbol,
|
|
2342
2348
|
strategyName: self.params.method.context.strategyName,
|
|
2343
2349
|
exchangeName: self.params.method.context.exchangeName,
|
|
@@ -2564,6 +2570,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, activat
|
|
|
2564
2570
|
pendingAt: activationTime,
|
|
2565
2571
|
});
|
|
2566
2572
|
if (await functoolsKit.not(self.params.risk.checkSignal({
|
|
2573
|
+
pendingSignal: scheduled,
|
|
2567
2574
|
symbol: self.params.execution.context.symbol,
|
|
2568
2575
|
strategyName: self.params.method.context.strategyName,
|
|
2569
2576
|
exchangeName: self.params.method.context.exchangeName,
|
|
@@ -4025,7 +4032,7 @@ class RiskConnectionService {
|
|
|
4025
4032
|
}
|
|
4026
4033
|
}
|
|
4027
4034
|
|
|
4028
|
-
const METHOD_NAME_VALIDATE$1 = "
|
|
4035
|
+
const METHOD_NAME_VALIDATE$1 = "exchangeCoreService validate";
|
|
4029
4036
|
/**
|
|
4030
4037
|
* Global service for exchange operations with execution context injection.
|
|
4031
4038
|
*
|
|
@@ -4034,7 +4041,7 @@ const METHOD_NAME_VALIDATE$1 = "exchangeGlobalService validate";
|
|
|
4034
4041
|
*
|
|
4035
4042
|
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
4036
4043
|
*/
|
|
4037
|
-
class
|
|
4044
|
+
class ExchangeCoreService {
|
|
4038
4045
|
constructor() {
|
|
4039
4046
|
this.loggerService = inject(TYPES.loggerService);
|
|
4040
4047
|
this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
|
|
@@ -4064,13 +4071,16 @@ class ExchangeGlobalService {
|
|
|
4064
4071
|
* @returns Promise resolving to array of candles
|
|
4065
4072
|
*/
|
|
4066
4073
|
this.getCandles = async (symbol, interval, limit, when, backtest) => {
|
|
4067
|
-
this.loggerService.log("
|
|
4074
|
+
this.loggerService.log("exchangeCoreService getCandles", {
|
|
4068
4075
|
symbol,
|
|
4069
4076
|
interval,
|
|
4070
4077
|
limit,
|
|
4071
4078
|
when,
|
|
4072
4079
|
backtest,
|
|
4073
4080
|
});
|
|
4081
|
+
if (!MethodContextService.hasContext()) {
|
|
4082
|
+
throw new Error("exchangeCoreService getCandles requires a method context");
|
|
4083
|
+
}
|
|
4074
4084
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4075
4085
|
return await ExecutionContextService.runInContext(async () => {
|
|
4076
4086
|
return await this.exchangeConnectionService.getCandles(symbol, interval, limit);
|
|
@@ -4091,13 +4101,16 @@ class ExchangeGlobalService {
|
|
|
4091
4101
|
* @returns Promise resolving to array of future candles
|
|
4092
4102
|
*/
|
|
4093
4103
|
this.getNextCandles = async (symbol, interval, limit, when, backtest) => {
|
|
4094
|
-
this.loggerService.log("
|
|
4104
|
+
this.loggerService.log("exchangeCoreService getNextCandles", {
|
|
4095
4105
|
symbol,
|
|
4096
4106
|
interval,
|
|
4097
4107
|
limit,
|
|
4098
4108
|
when,
|
|
4099
4109
|
backtest,
|
|
4100
4110
|
});
|
|
4111
|
+
if (!MethodContextService.hasContext()) {
|
|
4112
|
+
throw new Error("exchangeCoreService getNextCandles requires a method context");
|
|
4113
|
+
}
|
|
4101
4114
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4102
4115
|
return await ExecutionContextService.runInContext(async () => {
|
|
4103
4116
|
return await this.exchangeConnectionService.getNextCandles(symbol, interval, limit);
|
|
@@ -4116,11 +4129,14 @@ class ExchangeGlobalService {
|
|
|
4116
4129
|
* @returns Promise resolving to VWAP price
|
|
4117
4130
|
*/
|
|
4118
4131
|
this.getAveragePrice = async (symbol, when, backtest) => {
|
|
4119
|
-
this.loggerService.log("
|
|
4132
|
+
this.loggerService.log("exchangeCoreService getAveragePrice", {
|
|
4120
4133
|
symbol,
|
|
4121
4134
|
when,
|
|
4122
4135
|
backtest,
|
|
4123
4136
|
});
|
|
4137
|
+
if (!MethodContextService.hasContext()) {
|
|
4138
|
+
throw new Error("exchangeCoreService getAveragePrice requires a method context");
|
|
4139
|
+
}
|
|
4124
4140
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4125
4141
|
return await ExecutionContextService.runInContext(async () => {
|
|
4126
4142
|
return await this.exchangeConnectionService.getAveragePrice(symbol);
|
|
@@ -4140,12 +4156,15 @@ class ExchangeGlobalService {
|
|
|
4140
4156
|
* @returns Promise resolving to formatted price string
|
|
4141
4157
|
*/
|
|
4142
4158
|
this.formatPrice = async (symbol, price, when, backtest) => {
|
|
4143
|
-
this.loggerService.log("
|
|
4159
|
+
this.loggerService.log("exchangeCoreService formatPrice", {
|
|
4144
4160
|
symbol,
|
|
4145
4161
|
price,
|
|
4146
4162
|
when,
|
|
4147
4163
|
backtest,
|
|
4148
4164
|
});
|
|
4165
|
+
if (!MethodContextService.hasContext()) {
|
|
4166
|
+
throw new Error("exchangeCoreService formatPrice requires a method context");
|
|
4167
|
+
}
|
|
4149
4168
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4150
4169
|
return await ExecutionContextService.runInContext(async () => {
|
|
4151
4170
|
return await this.exchangeConnectionService.formatPrice(symbol, price);
|
|
@@ -4165,12 +4184,15 @@ class ExchangeGlobalService {
|
|
|
4165
4184
|
* @returns Promise resolving to formatted quantity string
|
|
4166
4185
|
*/
|
|
4167
4186
|
this.formatQuantity = async (symbol, quantity, when, backtest) => {
|
|
4168
|
-
this.loggerService.log("
|
|
4187
|
+
this.loggerService.log("exchangeCoreService formatQuantity", {
|
|
4169
4188
|
symbol,
|
|
4170
4189
|
quantity,
|
|
4171
4190
|
when,
|
|
4172
4191
|
backtest,
|
|
4173
4192
|
});
|
|
4193
|
+
if (!MethodContextService.hasContext()) {
|
|
4194
|
+
throw new Error("exchangeCoreService formatQuantity requires a method context");
|
|
4195
|
+
}
|
|
4174
4196
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4175
4197
|
return await ExecutionContextService.runInContext(async () => {
|
|
4176
4198
|
return await this.exchangeConnectionService.formatQuantity(symbol, quantity);
|
|
@@ -4183,7 +4205,7 @@ class ExchangeGlobalService {
|
|
|
4183
4205
|
}
|
|
4184
4206
|
}
|
|
4185
4207
|
|
|
4186
|
-
const METHOD_NAME_VALIDATE = "
|
|
4208
|
+
const METHOD_NAME_VALIDATE = "strategyCoreService validate";
|
|
4187
4209
|
/**
|
|
4188
4210
|
* Global service for strategy operations with execution context injection.
|
|
4189
4211
|
*
|
|
@@ -4192,7 +4214,7 @@ const METHOD_NAME_VALIDATE = "strategyGlobalService validate";
|
|
|
4192
4214
|
*
|
|
4193
4215
|
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
4194
4216
|
*/
|
|
4195
|
-
class
|
|
4217
|
+
class StrategyCoreService {
|
|
4196
4218
|
constructor() {
|
|
4197
4219
|
this.loggerService = inject(TYPES.loggerService);
|
|
4198
4220
|
this.strategyConnectionService = inject(TYPES.strategyConnectionService);
|
|
@@ -4230,10 +4252,13 @@ class StrategyGlobalService {
|
|
|
4230
4252
|
* @returns Promise resolving to pending signal or null
|
|
4231
4253
|
*/
|
|
4232
4254
|
this.getPendingSignal = async (symbol, strategyName) => {
|
|
4233
|
-
this.loggerService.log("
|
|
4255
|
+
this.loggerService.log("strategyCoreService getPendingSignal", {
|
|
4234
4256
|
symbol,
|
|
4235
4257
|
strategyName,
|
|
4236
4258
|
});
|
|
4259
|
+
if (!MethodContextService.hasContext()) {
|
|
4260
|
+
throw new Error("strategyCoreService getPendingSignal requires a method context");
|
|
4261
|
+
}
|
|
4237
4262
|
await this.validate(symbol, strategyName);
|
|
4238
4263
|
return await this.strategyConnectionService.getPendingSignal(symbol, strategyName);
|
|
4239
4264
|
};
|
|
@@ -4248,10 +4273,13 @@ class StrategyGlobalService {
|
|
|
4248
4273
|
* @returns Promise resolving to true if strategy is stopped, false otherwise
|
|
4249
4274
|
*/
|
|
4250
4275
|
this.getStopped = async (symbol, strategyName) => {
|
|
4251
|
-
this.loggerService.log("
|
|
4276
|
+
this.loggerService.log("strategyCoreService getStopped", {
|
|
4252
4277
|
symbol,
|
|
4253
4278
|
strategyName,
|
|
4254
4279
|
});
|
|
4280
|
+
if (!MethodContextService.hasContext()) {
|
|
4281
|
+
throw new Error("strategyCoreService getStopped requires a method context");
|
|
4282
|
+
}
|
|
4255
4283
|
await this.validate(symbol, strategyName);
|
|
4256
4284
|
return await this.strategyConnectionService.getStopped(symbol, strategyName);
|
|
4257
4285
|
};
|
|
@@ -4267,11 +4295,14 @@ class StrategyGlobalService {
|
|
|
4267
4295
|
* @returns Discriminated union of tick result (idle, opened, active, closed)
|
|
4268
4296
|
*/
|
|
4269
4297
|
this.tick = async (symbol, when, backtest) => {
|
|
4270
|
-
this.loggerService.log("
|
|
4298
|
+
this.loggerService.log("strategyCoreService tick", {
|
|
4271
4299
|
symbol,
|
|
4272
4300
|
when,
|
|
4273
4301
|
backtest,
|
|
4274
4302
|
});
|
|
4303
|
+
if (!MethodContextService.hasContext()) {
|
|
4304
|
+
throw new Error("strategyCoreService tick requires a method context");
|
|
4305
|
+
}
|
|
4275
4306
|
const strategyName = this.methodContextService.context.strategyName;
|
|
4276
4307
|
await this.validate(symbol, strategyName);
|
|
4277
4308
|
return await ExecutionContextService.runInContext(async () => {
|
|
@@ -4295,12 +4326,15 @@ class StrategyGlobalService {
|
|
|
4295
4326
|
* @returns Closed signal result with PNL
|
|
4296
4327
|
*/
|
|
4297
4328
|
this.backtest = async (symbol, candles, when, backtest) => {
|
|
4298
|
-
this.loggerService.log("
|
|
4329
|
+
this.loggerService.log("strategyCoreService backtest", {
|
|
4299
4330
|
symbol,
|
|
4300
4331
|
candleCount: candles.length,
|
|
4301
4332
|
when,
|
|
4302
4333
|
backtest,
|
|
4303
4334
|
});
|
|
4335
|
+
if (!MethodContextService.hasContext()) {
|
|
4336
|
+
throw new Error("strategyCoreService backtest requires a method context");
|
|
4337
|
+
}
|
|
4304
4338
|
const strategyName = this.methodContextService.context.strategyName;
|
|
4305
4339
|
await this.validate(symbol, strategyName);
|
|
4306
4340
|
return await ExecutionContextService.runInContext(async () => {
|
|
@@ -4322,7 +4356,7 @@ class StrategyGlobalService {
|
|
|
4322
4356
|
* @returns Promise that resolves when stop flag is set
|
|
4323
4357
|
*/
|
|
4324
4358
|
this.stop = async (ctx, backtest) => {
|
|
4325
|
-
this.loggerService.log("
|
|
4359
|
+
this.loggerService.log("strategyCoreService stop", {
|
|
4326
4360
|
ctx,
|
|
4327
4361
|
backtest,
|
|
4328
4362
|
});
|
|
@@ -4338,7 +4372,7 @@ class StrategyGlobalService {
|
|
|
4338
4372
|
* @param ctx - Optional context with symbol and strategyName (clears all if not provided)
|
|
4339
4373
|
*/
|
|
4340
4374
|
this.clear = async (ctx) => {
|
|
4341
|
-
this.loggerService.log("
|
|
4375
|
+
this.loggerService.log("strategyCoreService clear", {
|
|
4342
4376
|
ctx,
|
|
4343
4377
|
});
|
|
4344
4378
|
if (ctx) {
|
|
@@ -4349,14 +4383,14 @@ class StrategyGlobalService {
|
|
|
4349
4383
|
}
|
|
4350
4384
|
}
|
|
4351
4385
|
|
|
4352
|
-
const METHOD_NAME_GET_TIMEFRAME = "
|
|
4386
|
+
const METHOD_NAME_GET_TIMEFRAME = "frameCoreService getTimeframe";
|
|
4353
4387
|
/**
|
|
4354
4388
|
* Global service for frame operations.
|
|
4355
4389
|
*
|
|
4356
4390
|
* Wraps FrameConnectionService for timeframe generation.
|
|
4357
4391
|
* Used internally by BacktestLogicPrivateService.
|
|
4358
4392
|
*/
|
|
4359
|
-
class
|
|
4393
|
+
class FrameCoreService {
|
|
4360
4394
|
constructor() {
|
|
4361
4395
|
this.loggerService = inject(TYPES.loggerService);
|
|
4362
4396
|
this.frameConnectionService = inject(TYPES.frameConnectionService);
|
|
@@ -4372,6 +4406,9 @@ class FrameGlobalService {
|
|
|
4372
4406
|
frameName,
|
|
4373
4407
|
symbol,
|
|
4374
4408
|
});
|
|
4409
|
+
if (!MethodContextService.hasContext()) {
|
|
4410
|
+
throw new Error("frameCoreService getTimeframe requires a method context");
|
|
4411
|
+
}
|
|
4375
4412
|
this.frameValidationService.validate(frameName, METHOD_NAME_GET_TIMEFRAME);
|
|
4376
4413
|
return await this.frameConnectionService.getTimeframe(symbol, frameName);
|
|
4377
4414
|
};
|
|
@@ -4977,9 +5014,9 @@ class WalkerSchemaService {
|
|
|
4977
5014
|
class BacktestLogicPrivateService {
|
|
4978
5015
|
constructor() {
|
|
4979
5016
|
this.loggerService = inject(TYPES.loggerService);
|
|
4980
|
-
this.
|
|
4981
|
-
this.
|
|
4982
|
-
this.
|
|
5017
|
+
this.strategyCoreService = inject(TYPES.strategyCoreService);
|
|
5018
|
+
this.exchangeCoreService = inject(TYPES.exchangeCoreService);
|
|
5019
|
+
this.frameCoreService = inject(TYPES.frameCoreService);
|
|
4983
5020
|
this.methodContextService = inject(TYPES.methodContextService);
|
|
4984
5021
|
}
|
|
4985
5022
|
/**
|
|
@@ -5001,7 +5038,7 @@ class BacktestLogicPrivateService {
|
|
|
5001
5038
|
symbol,
|
|
5002
5039
|
});
|
|
5003
5040
|
const backtestStartTime = performance.now();
|
|
5004
|
-
const timeframes = await this.
|
|
5041
|
+
const timeframes = await this.frameCoreService.getTimeframe(symbol, this.methodContextService.context.frameName);
|
|
5005
5042
|
const totalFrames = timeframes.length;
|
|
5006
5043
|
let i = 0;
|
|
5007
5044
|
let previousEventTimestamp = null;
|
|
@@ -5020,7 +5057,7 @@ class BacktestLogicPrivateService {
|
|
|
5020
5057
|
});
|
|
5021
5058
|
}
|
|
5022
5059
|
// Check if strategy should stop before processing next frame
|
|
5023
|
-
if (await this.
|
|
5060
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5024
5061
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (before tick)", {
|
|
5025
5062
|
symbol,
|
|
5026
5063
|
when: when.toISOString(),
|
|
@@ -5031,7 +5068,7 @@ class BacktestLogicPrivateService {
|
|
|
5031
5068
|
}
|
|
5032
5069
|
let result;
|
|
5033
5070
|
try {
|
|
5034
|
-
result = await this.
|
|
5071
|
+
result = await this.strategyCoreService.tick(symbol, when, true);
|
|
5035
5072
|
}
|
|
5036
5073
|
catch (error) {
|
|
5037
5074
|
console.warn(`backtestLogicPrivateService tick failed, skipping timeframe when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5045,7 +5082,7 @@ class BacktestLogicPrivateService {
|
|
|
5045
5082
|
continue;
|
|
5046
5083
|
}
|
|
5047
5084
|
// Check if strategy should stop when idle (no active signal)
|
|
5048
|
-
if (await functoolsKit.and(Promise.resolve(result.action === "idle"), this.
|
|
5085
|
+
if (await functoolsKit.and(Promise.resolve(result.action === "idle"), this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName))) {
|
|
5049
5086
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (idle state)", {
|
|
5050
5087
|
symbol,
|
|
5051
5088
|
when: when.toISOString(),
|
|
@@ -5075,7 +5112,7 @@ class BacktestLogicPrivateService {
|
|
|
5075
5112
|
const candlesNeeded = bufferMinutes + GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES + signal.minuteEstimatedTime + 1;
|
|
5076
5113
|
let candles;
|
|
5077
5114
|
try {
|
|
5078
|
-
candles = await this.
|
|
5115
|
+
candles = await this.exchangeCoreService.getNextCandles(symbol, "1m", candlesNeeded, bufferStartTime, true);
|
|
5079
5116
|
}
|
|
5080
5117
|
catch (error) {
|
|
5081
5118
|
console.warn(`backtestLogicPrivateService getNextCandles failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5104,7 +5141,7 @@ class BacktestLogicPrivateService {
|
|
|
5104
5141
|
// и если активируется - продолжит с TP/SL мониторингом
|
|
5105
5142
|
let backtestResult;
|
|
5106
5143
|
try {
|
|
5107
|
-
backtestResult = await this.
|
|
5144
|
+
backtestResult = await this.strategyCoreService.backtest(symbol, candles, when, true);
|
|
5108
5145
|
}
|
|
5109
5146
|
catch (error) {
|
|
5110
5147
|
console.warn(`backtestLogicPrivateService backtest failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5147,7 +5184,7 @@ class BacktestLogicPrivateService {
|
|
|
5147
5184
|
}
|
|
5148
5185
|
yield backtestResult;
|
|
5149
5186
|
// Check if strategy should stop after signal is closed
|
|
5150
|
-
if (await this.
|
|
5187
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5151
5188
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (after scheduled signal closed)", {
|
|
5152
5189
|
symbol,
|
|
5153
5190
|
signalId: backtestResult.signal.id,
|
|
@@ -5174,7 +5211,7 @@ class BacktestLogicPrivateService {
|
|
|
5174
5211
|
const totalCandles = signal.minuteEstimatedTime + bufferMinutes;
|
|
5175
5212
|
let candles;
|
|
5176
5213
|
try {
|
|
5177
|
-
candles = await this.
|
|
5214
|
+
candles = await this.exchangeCoreService.getNextCandles(symbol, "1m", totalCandles, bufferStartTime, true);
|
|
5178
5215
|
}
|
|
5179
5216
|
catch (error) {
|
|
5180
5217
|
console.warn(`backtestLogicPrivateService getNextCandles failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5201,7 +5238,7 @@ class BacktestLogicPrivateService {
|
|
|
5201
5238
|
// Вызываем backtest - всегда возвращает closed
|
|
5202
5239
|
let backtestResult;
|
|
5203
5240
|
try {
|
|
5204
|
-
backtestResult = await this.
|
|
5241
|
+
backtestResult = await this.strategyCoreService.backtest(symbol, candles, when, true);
|
|
5205
5242
|
}
|
|
5206
5243
|
catch (error) {
|
|
5207
5244
|
console.warn(`backtestLogicPrivateService backtest failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5240,7 +5277,7 @@ class BacktestLogicPrivateService {
|
|
|
5240
5277
|
}
|
|
5241
5278
|
yield backtestResult;
|
|
5242
5279
|
// Check if strategy should stop after signal is closed
|
|
5243
|
-
if (await this.
|
|
5280
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5244
5281
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (after signal closed)", {
|
|
5245
5282
|
symbol,
|
|
5246
5283
|
signalId: backtestResult.signal.id,
|
|
@@ -5314,7 +5351,7 @@ const TICK_TTL = 1 * 60 * 1000 + 1;
|
|
|
5314
5351
|
class LiveLogicPrivateService {
|
|
5315
5352
|
constructor() {
|
|
5316
5353
|
this.loggerService = inject(TYPES.loggerService);
|
|
5317
|
-
this.
|
|
5354
|
+
this.strategyCoreService = inject(TYPES.strategyCoreService);
|
|
5318
5355
|
this.methodContextService = inject(TYPES.methodContextService);
|
|
5319
5356
|
}
|
|
5320
5357
|
/**
|
|
@@ -5349,7 +5386,7 @@ class LiveLogicPrivateService {
|
|
|
5349
5386
|
const when = new Date();
|
|
5350
5387
|
let result;
|
|
5351
5388
|
try {
|
|
5352
|
-
result = await this.
|
|
5389
|
+
result = await this.strategyCoreService.tick(symbol, when, false);
|
|
5353
5390
|
}
|
|
5354
5391
|
catch (error) {
|
|
5355
5392
|
console.warn(`backtestLogicPrivateService tick failed when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5383,7 +5420,7 @@ class LiveLogicPrivateService {
|
|
|
5383
5420
|
previousEventTimestamp = currentTimestamp;
|
|
5384
5421
|
// Check if strategy should stop when idle (no active signal)
|
|
5385
5422
|
if (result.action === "idle") {
|
|
5386
|
-
if (await functoolsKit.and(Promise.resolve(true), this.
|
|
5423
|
+
if (await functoolsKit.and(Promise.resolve(true), this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName))) {
|
|
5387
5424
|
this.loggerService.info("liveLogicPrivateService stopped by user request (idle state)", {
|
|
5388
5425
|
symbol,
|
|
5389
5426
|
when: when.toISOString(),
|
|
@@ -5405,7 +5442,7 @@ class LiveLogicPrivateService {
|
|
|
5405
5442
|
yield result;
|
|
5406
5443
|
// Check if strategy should stop after signal is closed
|
|
5407
5444
|
if (result.action === "closed") {
|
|
5408
|
-
if (await this.
|
|
5445
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5409
5446
|
this.loggerService.info("liveLogicPrivateService stopped by user request (after signal closed)", {
|
|
5410
5447
|
symbol,
|
|
5411
5448
|
signalId: result.signal.id,
|
|
@@ -11841,9 +11878,11 @@ class ConfigValidationService {
|
|
|
11841
11878
|
provide(TYPES.optimizerSchemaService, () => new OptimizerSchemaService());
|
|
11842
11879
|
}
|
|
11843
11880
|
{
|
|
11844
|
-
provide(TYPES.
|
|
11845
|
-
provide(TYPES.
|
|
11846
|
-
provide(TYPES.
|
|
11881
|
+
provide(TYPES.exchangeCoreService, () => new ExchangeCoreService());
|
|
11882
|
+
provide(TYPES.strategyCoreService, () => new StrategyCoreService());
|
|
11883
|
+
provide(TYPES.frameCoreService, () => new FrameCoreService());
|
|
11884
|
+
}
|
|
11885
|
+
{
|
|
11847
11886
|
provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
|
|
11848
11887
|
provide(TYPES.riskGlobalService, () => new RiskGlobalService());
|
|
11849
11888
|
provide(TYPES.optimizerGlobalService, () => new OptimizerGlobalService());
|
|
@@ -11913,10 +11952,12 @@ const schemaServices = {
|
|
|
11913
11952
|
riskSchemaService: inject(TYPES.riskSchemaService),
|
|
11914
11953
|
optimizerSchemaService: inject(TYPES.optimizerSchemaService),
|
|
11915
11954
|
};
|
|
11955
|
+
const coreServices = {
|
|
11956
|
+
exchangeCoreService: inject(TYPES.exchangeCoreService),
|
|
11957
|
+
strategyCoreService: inject(TYPES.strategyCoreService),
|
|
11958
|
+
frameCoreService: inject(TYPES.frameCoreService),
|
|
11959
|
+
};
|
|
11916
11960
|
const globalServices = {
|
|
11917
|
-
exchangeGlobalService: inject(TYPES.exchangeGlobalService),
|
|
11918
|
-
strategyGlobalService: inject(TYPES.strategyGlobalService),
|
|
11919
|
-
frameGlobalService: inject(TYPES.frameGlobalService),
|
|
11920
11961
|
sizingGlobalService: inject(TYPES.sizingGlobalService),
|
|
11921
11962
|
riskGlobalService: inject(TYPES.riskGlobalService),
|
|
11922
11963
|
optimizerGlobalService: inject(TYPES.optimizerGlobalService),
|
|
@@ -11965,6 +12006,7 @@ const backtest = {
|
|
|
11965
12006
|
...contextServices,
|
|
11966
12007
|
...connectionServices,
|
|
11967
12008
|
...schemaServices,
|
|
12009
|
+
...coreServices,
|
|
11968
12010
|
...globalServices,
|
|
11969
12011
|
...commandServices,
|
|
11970
12012
|
...logicPrivateServices,
|
|
@@ -11999,6 +12041,7 @@ function setLogger(logger) {
|
|
|
11999
12041
|
/**
|
|
12000
12042
|
* Sets global configuration parameters for the framework.
|
|
12001
12043
|
* @param config - Partial configuration object to override default settings
|
|
12044
|
+
* @param _unsafe - Skip config validations - required for testbed
|
|
12002
12045
|
*
|
|
12003
12046
|
* @example
|
|
12004
12047
|
* ```typescript
|
|
@@ -13861,7 +13904,7 @@ class BacktestInstance {
|
|
|
13861
13904
|
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
13862
13905
|
}
|
|
13863
13906
|
{
|
|
13864
|
-
backtest$1.
|
|
13907
|
+
backtest$1.strategyCoreService.clear({ symbol, strategyName: context.strategyName });
|
|
13865
13908
|
}
|
|
13866
13909
|
{
|
|
13867
13910
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
@@ -13896,8 +13939,8 @@ class BacktestInstance {
|
|
|
13896
13939
|
});
|
|
13897
13940
|
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
13898
13941
|
return () => {
|
|
13899
|
-
backtest$1.
|
|
13900
|
-
backtest$1.
|
|
13942
|
+
backtest$1.strategyCoreService.stop({ symbol, strategyName: context.strategyName }, true);
|
|
13943
|
+
backtest$1.strategyCoreService
|
|
13901
13944
|
.getPendingSignal(symbol, context.strategyName)
|
|
13902
13945
|
.then(async (pendingSignal) => {
|
|
13903
13946
|
if (pendingSignal) {
|
|
@@ -13938,7 +13981,7 @@ class BacktestInstance {
|
|
|
13938
13981
|
symbol,
|
|
13939
13982
|
strategyName,
|
|
13940
13983
|
});
|
|
13941
|
-
await backtest$1.
|
|
13984
|
+
await backtest$1.strategyCoreService.stop({ symbol, strategyName }, true);
|
|
13942
13985
|
};
|
|
13943
13986
|
/**
|
|
13944
13987
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
@@ -14353,7 +14396,7 @@ class LiveInstance {
|
|
|
14353
14396
|
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
14354
14397
|
}
|
|
14355
14398
|
{
|
|
14356
|
-
backtest$1.
|
|
14399
|
+
backtest$1.strategyCoreService.clear({ symbol, strategyName: context.strategyName });
|
|
14357
14400
|
}
|
|
14358
14401
|
{
|
|
14359
14402
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
@@ -14388,8 +14431,8 @@ class LiveInstance {
|
|
|
14388
14431
|
});
|
|
14389
14432
|
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
14390
14433
|
return () => {
|
|
14391
|
-
backtest$1.
|
|
14392
|
-
backtest$1.
|
|
14434
|
+
backtest$1.strategyCoreService.stop({ symbol, strategyName: context.strategyName }, false);
|
|
14435
|
+
backtest$1.strategyCoreService
|
|
14393
14436
|
.getPendingSignal(symbol, context.strategyName)
|
|
14394
14437
|
.then(async (pendingSignal) => {
|
|
14395
14438
|
if (pendingSignal) {
|
|
@@ -14430,7 +14473,7 @@ class LiveInstance {
|
|
|
14430
14473
|
symbol,
|
|
14431
14474
|
strategyName,
|
|
14432
14475
|
});
|
|
14433
|
-
await backtest$1.
|
|
14476
|
+
await backtest$1.strategyCoreService.stop({ symbol, strategyName }, false);
|
|
14434
14477
|
};
|
|
14435
14478
|
/**
|
|
14436
14479
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
@@ -15111,7 +15154,7 @@ class WalkerInstance {
|
|
|
15111
15154
|
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName });
|
|
15112
15155
|
}
|
|
15113
15156
|
{
|
|
15114
|
-
backtest$1.
|
|
15157
|
+
backtest$1.strategyCoreService.clear({ symbol, strategyName });
|
|
15115
15158
|
}
|
|
15116
15159
|
{
|
|
15117
15160
|
const { riskName } = backtest$1.strategySchemaService.get(strategyName);
|
|
@@ -15151,7 +15194,7 @@ class WalkerInstance {
|
|
|
15151
15194
|
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
15152
15195
|
return () => {
|
|
15153
15196
|
for (const strategyName of walkerSchema.strategies) {
|
|
15154
|
-
backtest$1.
|
|
15197
|
+
backtest$1.strategyCoreService.stop({ symbol, strategyName }, true);
|
|
15155
15198
|
walkerStopSubject.next({ symbol, strategyName, walkerName: context.walkerName });
|
|
15156
15199
|
}
|
|
15157
15200
|
if (!this._isDone) {
|
|
@@ -15197,7 +15240,7 @@ class WalkerInstance {
|
|
|
15197
15240
|
const walkerSchema = backtest$1.walkerSchemaService.get(walkerName);
|
|
15198
15241
|
for (const strategyName of walkerSchema.strategies) {
|
|
15199
15242
|
await walkerStopSubject.next({ symbol, strategyName, walkerName });
|
|
15200
|
-
await backtest$1.
|
|
15243
|
+
await backtest$1.strategyCoreService.stop({ symbol, strategyName }, true);
|
|
15201
15244
|
}
|
|
15202
15245
|
};
|
|
15203
15246
|
/**
|
package/build/index.mjs
CHANGED
|
@@ -165,10 +165,12 @@ const schemaServices$1 = {
|
|
|
165
165
|
riskSchemaService: Symbol('riskSchemaService'),
|
|
166
166
|
optimizerSchemaService: Symbol('optimizerSchemaService'),
|
|
167
167
|
};
|
|
168
|
+
const coreServices$1 = {
|
|
169
|
+
exchangeCoreService: Symbol('exchangeCoreService'),
|
|
170
|
+
strategyCoreService: Symbol('strategyCoreService'),
|
|
171
|
+
frameCoreService: Symbol('frameCoreService'),
|
|
172
|
+
};
|
|
168
173
|
const globalServices$1 = {
|
|
169
|
-
exchangeGlobalService: Symbol('exchangeGlobalService'),
|
|
170
|
-
strategyGlobalService: Symbol('strategyGlobalService'),
|
|
171
|
-
frameGlobalService: Symbol('frameGlobalService'),
|
|
172
174
|
sizingGlobalService: Symbol('sizingGlobalService'),
|
|
173
175
|
riskGlobalService: Symbol('riskGlobalService'),
|
|
174
176
|
optimizerGlobalService: Symbol('optimizerGlobalService'),
|
|
@@ -217,6 +219,7 @@ const TYPES = {
|
|
|
217
219
|
...contextServices$1,
|
|
218
220
|
...connectionServices$1,
|
|
219
221
|
...schemaServices$1,
|
|
222
|
+
...coreServices$1,
|
|
220
223
|
...globalServices$1,
|
|
221
224
|
...commandServices$1,
|
|
222
225
|
...logicPrivateServices$1,
|
|
@@ -1999,15 +2002,6 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1999
2002
|
self._lastSignalTimestamp = currentTime;
|
|
2000
2003
|
}
|
|
2001
2004
|
const currentPrice = await self.params.exchange.getAveragePrice(self.params.execution.context.symbol);
|
|
2002
|
-
if (await not(self.params.risk.checkSignal({
|
|
2003
|
-
symbol: self.params.execution.context.symbol,
|
|
2004
|
-
strategyName: self.params.method.context.strategyName,
|
|
2005
|
-
exchangeName: self.params.method.context.exchangeName,
|
|
2006
|
-
currentPrice,
|
|
2007
|
-
timestamp: currentTime,
|
|
2008
|
-
}))) {
|
|
2009
|
-
return null;
|
|
2010
|
-
}
|
|
2011
2005
|
const timeoutMs = GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS * 1000;
|
|
2012
2006
|
const signal = await Promise.race([
|
|
2013
2007
|
self.params.getSignal(self.params.execution.context.symbol, self.params.execution.context.when),
|
|
@@ -2022,6 +2016,16 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
2022
2016
|
if (self._isStopped) {
|
|
2023
2017
|
return null;
|
|
2024
2018
|
}
|
|
2019
|
+
if (await not(self.params.risk.checkSignal({
|
|
2020
|
+
pendingSignal: signal,
|
|
2021
|
+
symbol: self.params.execution.context.symbol,
|
|
2022
|
+
strategyName: self.params.method.context.strategyName,
|
|
2023
|
+
exchangeName: self.params.method.context.exchangeName,
|
|
2024
|
+
currentPrice,
|
|
2025
|
+
timestamp: currentTime,
|
|
2026
|
+
}))) {
|
|
2027
|
+
return null;
|
|
2028
|
+
}
|
|
2025
2029
|
// Если priceOpen указан - проверяем нужно ли ждать активации или открыть сразу
|
|
2026
2030
|
if (signal.priceOpen !== undefined) {
|
|
2027
2031
|
// КРИТИЧЕСКАЯ ПРОВЕРКА: достигнут ли priceOpen?
|
|
@@ -2253,6 +2257,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
|
|
|
2253
2257
|
});
|
|
2254
2258
|
if (await not(self.params.risk.checkSignal({
|
|
2255
2259
|
symbol: self.params.execution.context.symbol,
|
|
2260
|
+
pendingSignal: scheduled,
|
|
2256
2261
|
strategyName: self.params.method.context.strategyName,
|
|
2257
2262
|
exchangeName: self.params.method.context.exchangeName,
|
|
2258
2263
|
currentPrice: scheduled.priceOpen,
|
|
@@ -2336,6 +2341,7 @@ const OPEN_NEW_SCHEDULED_SIGNAL_FN = async (self, signal) => {
|
|
|
2336
2341
|
};
|
|
2337
2342
|
const OPEN_NEW_PENDING_SIGNAL_FN = async (self, signal) => {
|
|
2338
2343
|
if (await not(self.params.risk.checkSignal({
|
|
2344
|
+
pendingSignal: signal,
|
|
2339
2345
|
symbol: self.params.execution.context.symbol,
|
|
2340
2346
|
strategyName: self.params.method.context.strategyName,
|
|
2341
2347
|
exchangeName: self.params.method.context.exchangeName,
|
|
@@ -2562,6 +2568,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, activat
|
|
|
2562
2568
|
pendingAt: activationTime,
|
|
2563
2569
|
});
|
|
2564
2570
|
if (await not(self.params.risk.checkSignal({
|
|
2571
|
+
pendingSignal: scheduled,
|
|
2565
2572
|
symbol: self.params.execution.context.symbol,
|
|
2566
2573
|
strategyName: self.params.method.context.strategyName,
|
|
2567
2574
|
exchangeName: self.params.method.context.exchangeName,
|
|
@@ -4023,7 +4030,7 @@ class RiskConnectionService {
|
|
|
4023
4030
|
}
|
|
4024
4031
|
}
|
|
4025
4032
|
|
|
4026
|
-
const METHOD_NAME_VALIDATE$1 = "
|
|
4033
|
+
const METHOD_NAME_VALIDATE$1 = "exchangeCoreService validate";
|
|
4027
4034
|
/**
|
|
4028
4035
|
* Global service for exchange operations with execution context injection.
|
|
4029
4036
|
*
|
|
@@ -4032,7 +4039,7 @@ const METHOD_NAME_VALIDATE$1 = "exchangeGlobalService validate";
|
|
|
4032
4039
|
*
|
|
4033
4040
|
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
4034
4041
|
*/
|
|
4035
|
-
class
|
|
4042
|
+
class ExchangeCoreService {
|
|
4036
4043
|
constructor() {
|
|
4037
4044
|
this.loggerService = inject(TYPES.loggerService);
|
|
4038
4045
|
this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
|
|
@@ -4062,13 +4069,16 @@ class ExchangeGlobalService {
|
|
|
4062
4069
|
* @returns Promise resolving to array of candles
|
|
4063
4070
|
*/
|
|
4064
4071
|
this.getCandles = async (symbol, interval, limit, when, backtest) => {
|
|
4065
|
-
this.loggerService.log("
|
|
4072
|
+
this.loggerService.log("exchangeCoreService getCandles", {
|
|
4066
4073
|
symbol,
|
|
4067
4074
|
interval,
|
|
4068
4075
|
limit,
|
|
4069
4076
|
when,
|
|
4070
4077
|
backtest,
|
|
4071
4078
|
});
|
|
4079
|
+
if (!MethodContextService.hasContext()) {
|
|
4080
|
+
throw new Error("exchangeCoreService getCandles requires a method context");
|
|
4081
|
+
}
|
|
4072
4082
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4073
4083
|
return await ExecutionContextService.runInContext(async () => {
|
|
4074
4084
|
return await this.exchangeConnectionService.getCandles(symbol, interval, limit);
|
|
@@ -4089,13 +4099,16 @@ class ExchangeGlobalService {
|
|
|
4089
4099
|
* @returns Promise resolving to array of future candles
|
|
4090
4100
|
*/
|
|
4091
4101
|
this.getNextCandles = async (symbol, interval, limit, when, backtest) => {
|
|
4092
|
-
this.loggerService.log("
|
|
4102
|
+
this.loggerService.log("exchangeCoreService getNextCandles", {
|
|
4093
4103
|
symbol,
|
|
4094
4104
|
interval,
|
|
4095
4105
|
limit,
|
|
4096
4106
|
when,
|
|
4097
4107
|
backtest,
|
|
4098
4108
|
});
|
|
4109
|
+
if (!MethodContextService.hasContext()) {
|
|
4110
|
+
throw new Error("exchangeCoreService getNextCandles requires a method context");
|
|
4111
|
+
}
|
|
4099
4112
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4100
4113
|
return await ExecutionContextService.runInContext(async () => {
|
|
4101
4114
|
return await this.exchangeConnectionService.getNextCandles(symbol, interval, limit);
|
|
@@ -4114,11 +4127,14 @@ class ExchangeGlobalService {
|
|
|
4114
4127
|
* @returns Promise resolving to VWAP price
|
|
4115
4128
|
*/
|
|
4116
4129
|
this.getAveragePrice = async (symbol, when, backtest) => {
|
|
4117
|
-
this.loggerService.log("
|
|
4130
|
+
this.loggerService.log("exchangeCoreService getAveragePrice", {
|
|
4118
4131
|
symbol,
|
|
4119
4132
|
when,
|
|
4120
4133
|
backtest,
|
|
4121
4134
|
});
|
|
4135
|
+
if (!MethodContextService.hasContext()) {
|
|
4136
|
+
throw new Error("exchangeCoreService getAveragePrice requires a method context");
|
|
4137
|
+
}
|
|
4122
4138
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4123
4139
|
return await ExecutionContextService.runInContext(async () => {
|
|
4124
4140
|
return await this.exchangeConnectionService.getAveragePrice(symbol);
|
|
@@ -4138,12 +4154,15 @@ class ExchangeGlobalService {
|
|
|
4138
4154
|
* @returns Promise resolving to formatted price string
|
|
4139
4155
|
*/
|
|
4140
4156
|
this.formatPrice = async (symbol, price, when, backtest) => {
|
|
4141
|
-
this.loggerService.log("
|
|
4157
|
+
this.loggerService.log("exchangeCoreService formatPrice", {
|
|
4142
4158
|
symbol,
|
|
4143
4159
|
price,
|
|
4144
4160
|
when,
|
|
4145
4161
|
backtest,
|
|
4146
4162
|
});
|
|
4163
|
+
if (!MethodContextService.hasContext()) {
|
|
4164
|
+
throw new Error("exchangeCoreService formatPrice requires a method context");
|
|
4165
|
+
}
|
|
4147
4166
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4148
4167
|
return await ExecutionContextService.runInContext(async () => {
|
|
4149
4168
|
return await this.exchangeConnectionService.formatPrice(symbol, price);
|
|
@@ -4163,12 +4182,15 @@ class ExchangeGlobalService {
|
|
|
4163
4182
|
* @returns Promise resolving to formatted quantity string
|
|
4164
4183
|
*/
|
|
4165
4184
|
this.formatQuantity = async (symbol, quantity, when, backtest) => {
|
|
4166
|
-
this.loggerService.log("
|
|
4185
|
+
this.loggerService.log("exchangeCoreService formatQuantity", {
|
|
4167
4186
|
symbol,
|
|
4168
4187
|
quantity,
|
|
4169
4188
|
when,
|
|
4170
4189
|
backtest,
|
|
4171
4190
|
});
|
|
4191
|
+
if (!MethodContextService.hasContext()) {
|
|
4192
|
+
throw new Error("exchangeCoreService formatQuantity requires a method context");
|
|
4193
|
+
}
|
|
4172
4194
|
await this.validate(this.methodContextService.context.exchangeName);
|
|
4173
4195
|
return await ExecutionContextService.runInContext(async () => {
|
|
4174
4196
|
return await this.exchangeConnectionService.formatQuantity(symbol, quantity);
|
|
@@ -4181,7 +4203,7 @@ class ExchangeGlobalService {
|
|
|
4181
4203
|
}
|
|
4182
4204
|
}
|
|
4183
4205
|
|
|
4184
|
-
const METHOD_NAME_VALIDATE = "
|
|
4206
|
+
const METHOD_NAME_VALIDATE = "strategyCoreService validate";
|
|
4185
4207
|
/**
|
|
4186
4208
|
* Global service for strategy operations with execution context injection.
|
|
4187
4209
|
*
|
|
@@ -4190,7 +4212,7 @@ const METHOD_NAME_VALIDATE = "strategyGlobalService validate";
|
|
|
4190
4212
|
*
|
|
4191
4213
|
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
4192
4214
|
*/
|
|
4193
|
-
class
|
|
4215
|
+
class StrategyCoreService {
|
|
4194
4216
|
constructor() {
|
|
4195
4217
|
this.loggerService = inject(TYPES.loggerService);
|
|
4196
4218
|
this.strategyConnectionService = inject(TYPES.strategyConnectionService);
|
|
@@ -4228,10 +4250,13 @@ class StrategyGlobalService {
|
|
|
4228
4250
|
* @returns Promise resolving to pending signal or null
|
|
4229
4251
|
*/
|
|
4230
4252
|
this.getPendingSignal = async (symbol, strategyName) => {
|
|
4231
|
-
this.loggerService.log("
|
|
4253
|
+
this.loggerService.log("strategyCoreService getPendingSignal", {
|
|
4232
4254
|
symbol,
|
|
4233
4255
|
strategyName,
|
|
4234
4256
|
});
|
|
4257
|
+
if (!MethodContextService.hasContext()) {
|
|
4258
|
+
throw new Error("strategyCoreService getPendingSignal requires a method context");
|
|
4259
|
+
}
|
|
4235
4260
|
await this.validate(symbol, strategyName);
|
|
4236
4261
|
return await this.strategyConnectionService.getPendingSignal(symbol, strategyName);
|
|
4237
4262
|
};
|
|
@@ -4246,10 +4271,13 @@ class StrategyGlobalService {
|
|
|
4246
4271
|
* @returns Promise resolving to true if strategy is stopped, false otherwise
|
|
4247
4272
|
*/
|
|
4248
4273
|
this.getStopped = async (symbol, strategyName) => {
|
|
4249
|
-
this.loggerService.log("
|
|
4274
|
+
this.loggerService.log("strategyCoreService getStopped", {
|
|
4250
4275
|
symbol,
|
|
4251
4276
|
strategyName,
|
|
4252
4277
|
});
|
|
4278
|
+
if (!MethodContextService.hasContext()) {
|
|
4279
|
+
throw new Error("strategyCoreService getStopped requires a method context");
|
|
4280
|
+
}
|
|
4253
4281
|
await this.validate(symbol, strategyName);
|
|
4254
4282
|
return await this.strategyConnectionService.getStopped(symbol, strategyName);
|
|
4255
4283
|
};
|
|
@@ -4265,11 +4293,14 @@ class StrategyGlobalService {
|
|
|
4265
4293
|
* @returns Discriminated union of tick result (idle, opened, active, closed)
|
|
4266
4294
|
*/
|
|
4267
4295
|
this.tick = async (symbol, when, backtest) => {
|
|
4268
|
-
this.loggerService.log("
|
|
4296
|
+
this.loggerService.log("strategyCoreService tick", {
|
|
4269
4297
|
symbol,
|
|
4270
4298
|
when,
|
|
4271
4299
|
backtest,
|
|
4272
4300
|
});
|
|
4301
|
+
if (!MethodContextService.hasContext()) {
|
|
4302
|
+
throw new Error("strategyCoreService tick requires a method context");
|
|
4303
|
+
}
|
|
4273
4304
|
const strategyName = this.methodContextService.context.strategyName;
|
|
4274
4305
|
await this.validate(symbol, strategyName);
|
|
4275
4306
|
return await ExecutionContextService.runInContext(async () => {
|
|
@@ -4293,12 +4324,15 @@ class StrategyGlobalService {
|
|
|
4293
4324
|
* @returns Closed signal result with PNL
|
|
4294
4325
|
*/
|
|
4295
4326
|
this.backtest = async (symbol, candles, when, backtest) => {
|
|
4296
|
-
this.loggerService.log("
|
|
4327
|
+
this.loggerService.log("strategyCoreService backtest", {
|
|
4297
4328
|
symbol,
|
|
4298
4329
|
candleCount: candles.length,
|
|
4299
4330
|
when,
|
|
4300
4331
|
backtest,
|
|
4301
4332
|
});
|
|
4333
|
+
if (!MethodContextService.hasContext()) {
|
|
4334
|
+
throw new Error("strategyCoreService backtest requires a method context");
|
|
4335
|
+
}
|
|
4302
4336
|
const strategyName = this.methodContextService.context.strategyName;
|
|
4303
4337
|
await this.validate(symbol, strategyName);
|
|
4304
4338
|
return await ExecutionContextService.runInContext(async () => {
|
|
@@ -4320,7 +4354,7 @@ class StrategyGlobalService {
|
|
|
4320
4354
|
* @returns Promise that resolves when stop flag is set
|
|
4321
4355
|
*/
|
|
4322
4356
|
this.stop = async (ctx, backtest) => {
|
|
4323
|
-
this.loggerService.log("
|
|
4357
|
+
this.loggerService.log("strategyCoreService stop", {
|
|
4324
4358
|
ctx,
|
|
4325
4359
|
backtest,
|
|
4326
4360
|
});
|
|
@@ -4336,7 +4370,7 @@ class StrategyGlobalService {
|
|
|
4336
4370
|
* @param ctx - Optional context with symbol and strategyName (clears all if not provided)
|
|
4337
4371
|
*/
|
|
4338
4372
|
this.clear = async (ctx) => {
|
|
4339
|
-
this.loggerService.log("
|
|
4373
|
+
this.loggerService.log("strategyCoreService clear", {
|
|
4340
4374
|
ctx,
|
|
4341
4375
|
});
|
|
4342
4376
|
if (ctx) {
|
|
@@ -4347,14 +4381,14 @@ class StrategyGlobalService {
|
|
|
4347
4381
|
}
|
|
4348
4382
|
}
|
|
4349
4383
|
|
|
4350
|
-
const METHOD_NAME_GET_TIMEFRAME = "
|
|
4384
|
+
const METHOD_NAME_GET_TIMEFRAME = "frameCoreService getTimeframe";
|
|
4351
4385
|
/**
|
|
4352
4386
|
* Global service for frame operations.
|
|
4353
4387
|
*
|
|
4354
4388
|
* Wraps FrameConnectionService for timeframe generation.
|
|
4355
4389
|
* Used internally by BacktestLogicPrivateService.
|
|
4356
4390
|
*/
|
|
4357
|
-
class
|
|
4391
|
+
class FrameCoreService {
|
|
4358
4392
|
constructor() {
|
|
4359
4393
|
this.loggerService = inject(TYPES.loggerService);
|
|
4360
4394
|
this.frameConnectionService = inject(TYPES.frameConnectionService);
|
|
@@ -4370,6 +4404,9 @@ class FrameGlobalService {
|
|
|
4370
4404
|
frameName,
|
|
4371
4405
|
symbol,
|
|
4372
4406
|
});
|
|
4407
|
+
if (!MethodContextService.hasContext()) {
|
|
4408
|
+
throw new Error("frameCoreService getTimeframe requires a method context");
|
|
4409
|
+
}
|
|
4373
4410
|
this.frameValidationService.validate(frameName, METHOD_NAME_GET_TIMEFRAME);
|
|
4374
4411
|
return await this.frameConnectionService.getTimeframe(symbol, frameName);
|
|
4375
4412
|
};
|
|
@@ -4975,9 +5012,9 @@ class WalkerSchemaService {
|
|
|
4975
5012
|
class BacktestLogicPrivateService {
|
|
4976
5013
|
constructor() {
|
|
4977
5014
|
this.loggerService = inject(TYPES.loggerService);
|
|
4978
|
-
this.
|
|
4979
|
-
this.
|
|
4980
|
-
this.
|
|
5015
|
+
this.strategyCoreService = inject(TYPES.strategyCoreService);
|
|
5016
|
+
this.exchangeCoreService = inject(TYPES.exchangeCoreService);
|
|
5017
|
+
this.frameCoreService = inject(TYPES.frameCoreService);
|
|
4981
5018
|
this.methodContextService = inject(TYPES.methodContextService);
|
|
4982
5019
|
}
|
|
4983
5020
|
/**
|
|
@@ -4999,7 +5036,7 @@ class BacktestLogicPrivateService {
|
|
|
4999
5036
|
symbol,
|
|
5000
5037
|
});
|
|
5001
5038
|
const backtestStartTime = performance.now();
|
|
5002
|
-
const timeframes = await this.
|
|
5039
|
+
const timeframes = await this.frameCoreService.getTimeframe(symbol, this.methodContextService.context.frameName);
|
|
5003
5040
|
const totalFrames = timeframes.length;
|
|
5004
5041
|
let i = 0;
|
|
5005
5042
|
let previousEventTimestamp = null;
|
|
@@ -5018,7 +5055,7 @@ class BacktestLogicPrivateService {
|
|
|
5018
5055
|
});
|
|
5019
5056
|
}
|
|
5020
5057
|
// Check if strategy should stop before processing next frame
|
|
5021
|
-
if (await this.
|
|
5058
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5022
5059
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (before tick)", {
|
|
5023
5060
|
symbol,
|
|
5024
5061
|
when: when.toISOString(),
|
|
@@ -5029,7 +5066,7 @@ class BacktestLogicPrivateService {
|
|
|
5029
5066
|
}
|
|
5030
5067
|
let result;
|
|
5031
5068
|
try {
|
|
5032
|
-
result = await this.
|
|
5069
|
+
result = await this.strategyCoreService.tick(symbol, when, true);
|
|
5033
5070
|
}
|
|
5034
5071
|
catch (error) {
|
|
5035
5072
|
console.warn(`backtestLogicPrivateService tick failed, skipping timeframe when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5043,7 +5080,7 @@ class BacktestLogicPrivateService {
|
|
|
5043
5080
|
continue;
|
|
5044
5081
|
}
|
|
5045
5082
|
// Check if strategy should stop when idle (no active signal)
|
|
5046
|
-
if (await and(Promise.resolve(result.action === "idle"), this.
|
|
5083
|
+
if (await and(Promise.resolve(result.action === "idle"), this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName))) {
|
|
5047
5084
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (idle state)", {
|
|
5048
5085
|
symbol,
|
|
5049
5086
|
when: when.toISOString(),
|
|
@@ -5073,7 +5110,7 @@ class BacktestLogicPrivateService {
|
|
|
5073
5110
|
const candlesNeeded = bufferMinutes + GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES + signal.minuteEstimatedTime + 1;
|
|
5074
5111
|
let candles;
|
|
5075
5112
|
try {
|
|
5076
|
-
candles = await this.
|
|
5113
|
+
candles = await this.exchangeCoreService.getNextCandles(symbol, "1m", candlesNeeded, bufferStartTime, true);
|
|
5077
5114
|
}
|
|
5078
5115
|
catch (error) {
|
|
5079
5116
|
console.warn(`backtestLogicPrivateService getNextCandles failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5102,7 +5139,7 @@ class BacktestLogicPrivateService {
|
|
|
5102
5139
|
// и если активируется - продолжит с TP/SL мониторингом
|
|
5103
5140
|
let backtestResult;
|
|
5104
5141
|
try {
|
|
5105
|
-
backtestResult = await this.
|
|
5142
|
+
backtestResult = await this.strategyCoreService.backtest(symbol, candles, when, true);
|
|
5106
5143
|
}
|
|
5107
5144
|
catch (error) {
|
|
5108
5145
|
console.warn(`backtestLogicPrivateService backtest failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5145,7 +5182,7 @@ class BacktestLogicPrivateService {
|
|
|
5145
5182
|
}
|
|
5146
5183
|
yield backtestResult;
|
|
5147
5184
|
// Check if strategy should stop after signal is closed
|
|
5148
|
-
if (await this.
|
|
5185
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5149
5186
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (after scheduled signal closed)", {
|
|
5150
5187
|
symbol,
|
|
5151
5188
|
signalId: backtestResult.signal.id,
|
|
@@ -5172,7 +5209,7 @@ class BacktestLogicPrivateService {
|
|
|
5172
5209
|
const totalCandles = signal.minuteEstimatedTime + bufferMinutes;
|
|
5173
5210
|
let candles;
|
|
5174
5211
|
try {
|
|
5175
|
-
candles = await this.
|
|
5212
|
+
candles = await this.exchangeCoreService.getNextCandles(symbol, "1m", totalCandles, bufferStartTime, true);
|
|
5176
5213
|
}
|
|
5177
5214
|
catch (error) {
|
|
5178
5215
|
console.warn(`backtestLogicPrivateService getNextCandles failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5199,7 +5236,7 @@ class BacktestLogicPrivateService {
|
|
|
5199
5236
|
// Вызываем backtest - всегда возвращает closed
|
|
5200
5237
|
let backtestResult;
|
|
5201
5238
|
try {
|
|
5202
|
-
backtestResult = await this.
|
|
5239
|
+
backtestResult = await this.strategyCoreService.backtest(symbol, candles, when, true);
|
|
5203
5240
|
}
|
|
5204
5241
|
catch (error) {
|
|
5205
5242
|
console.warn(`backtestLogicPrivateService backtest failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5238,7 +5275,7 @@ class BacktestLogicPrivateService {
|
|
|
5238
5275
|
}
|
|
5239
5276
|
yield backtestResult;
|
|
5240
5277
|
// Check if strategy should stop after signal is closed
|
|
5241
|
-
if (await this.
|
|
5278
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5242
5279
|
this.loggerService.info("backtestLogicPrivateService stopped by user request (after signal closed)", {
|
|
5243
5280
|
symbol,
|
|
5244
5281
|
signalId: backtestResult.signal.id,
|
|
@@ -5312,7 +5349,7 @@ const TICK_TTL = 1 * 60 * 1000 + 1;
|
|
|
5312
5349
|
class LiveLogicPrivateService {
|
|
5313
5350
|
constructor() {
|
|
5314
5351
|
this.loggerService = inject(TYPES.loggerService);
|
|
5315
|
-
this.
|
|
5352
|
+
this.strategyCoreService = inject(TYPES.strategyCoreService);
|
|
5316
5353
|
this.methodContextService = inject(TYPES.methodContextService);
|
|
5317
5354
|
}
|
|
5318
5355
|
/**
|
|
@@ -5347,7 +5384,7 @@ class LiveLogicPrivateService {
|
|
|
5347
5384
|
const when = new Date();
|
|
5348
5385
|
let result;
|
|
5349
5386
|
try {
|
|
5350
|
-
result = await this.
|
|
5387
|
+
result = await this.strategyCoreService.tick(symbol, when, false);
|
|
5351
5388
|
}
|
|
5352
5389
|
catch (error) {
|
|
5353
5390
|
console.warn(`backtestLogicPrivateService tick failed when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
@@ -5381,7 +5418,7 @@ class LiveLogicPrivateService {
|
|
|
5381
5418
|
previousEventTimestamp = currentTimestamp;
|
|
5382
5419
|
// Check if strategy should stop when idle (no active signal)
|
|
5383
5420
|
if (result.action === "idle") {
|
|
5384
|
-
if (await and(Promise.resolve(true), this.
|
|
5421
|
+
if (await and(Promise.resolve(true), this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName))) {
|
|
5385
5422
|
this.loggerService.info("liveLogicPrivateService stopped by user request (idle state)", {
|
|
5386
5423
|
symbol,
|
|
5387
5424
|
when: when.toISOString(),
|
|
@@ -5403,7 +5440,7 @@ class LiveLogicPrivateService {
|
|
|
5403
5440
|
yield result;
|
|
5404
5441
|
// Check if strategy should stop after signal is closed
|
|
5405
5442
|
if (result.action === "closed") {
|
|
5406
|
-
if (await this.
|
|
5443
|
+
if (await this.strategyCoreService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5407
5444
|
this.loggerService.info("liveLogicPrivateService stopped by user request (after signal closed)", {
|
|
5408
5445
|
symbol,
|
|
5409
5446
|
signalId: result.signal.id,
|
|
@@ -11839,9 +11876,11 @@ class ConfigValidationService {
|
|
|
11839
11876
|
provide(TYPES.optimizerSchemaService, () => new OptimizerSchemaService());
|
|
11840
11877
|
}
|
|
11841
11878
|
{
|
|
11842
|
-
provide(TYPES.
|
|
11843
|
-
provide(TYPES.
|
|
11844
|
-
provide(TYPES.
|
|
11879
|
+
provide(TYPES.exchangeCoreService, () => new ExchangeCoreService());
|
|
11880
|
+
provide(TYPES.strategyCoreService, () => new StrategyCoreService());
|
|
11881
|
+
provide(TYPES.frameCoreService, () => new FrameCoreService());
|
|
11882
|
+
}
|
|
11883
|
+
{
|
|
11845
11884
|
provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
|
|
11846
11885
|
provide(TYPES.riskGlobalService, () => new RiskGlobalService());
|
|
11847
11886
|
provide(TYPES.optimizerGlobalService, () => new OptimizerGlobalService());
|
|
@@ -11911,10 +11950,12 @@ const schemaServices = {
|
|
|
11911
11950
|
riskSchemaService: inject(TYPES.riskSchemaService),
|
|
11912
11951
|
optimizerSchemaService: inject(TYPES.optimizerSchemaService),
|
|
11913
11952
|
};
|
|
11953
|
+
const coreServices = {
|
|
11954
|
+
exchangeCoreService: inject(TYPES.exchangeCoreService),
|
|
11955
|
+
strategyCoreService: inject(TYPES.strategyCoreService),
|
|
11956
|
+
frameCoreService: inject(TYPES.frameCoreService),
|
|
11957
|
+
};
|
|
11914
11958
|
const globalServices = {
|
|
11915
|
-
exchangeGlobalService: inject(TYPES.exchangeGlobalService),
|
|
11916
|
-
strategyGlobalService: inject(TYPES.strategyGlobalService),
|
|
11917
|
-
frameGlobalService: inject(TYPES.frameGlobalService),
|
|
11918
11959
|
sizingGlobalService: inject(TYPES.sizingGlobalService),
|
|
11919
11960
|
riskGlobalService: inject(TYPES.riskGlobalService),
|
|
11920
11961
|
optimizerGlobalService: inject(TYPES.optimizerGlobalService),
|
|
@@ -11963,6 +12004,7 @@ const backtest = {
|
|
|
11963
12004
|
...contextServices,
|
|
11964
12005
|
...connectionServices,
|
|
11965
12006
|
...schemaServices,
|
|
12007
|
+
...coreServices,
|
|
11966
12008
|
...globalServices,
|
|
11967
12009
|
...commandServices,
|
|
11968
12010
|
...logicPrivateServices,
|
|
@@ -11997,6 +12039,7 @@ function setLogger(logger) {
|
|
|
11997
12039
|
/**
|
|
11998
12040
|
* Sets global configuration parameters for the framework.
|
|
11999
12041
|
* @param config - Partial configuration object to override default settings
|
|
12042
|
+
* @param _unsafe - Skip config validations - required for testbed
|
|
12000
12043
|
*
|
|
12001
12044
|
* @example
|
|
12002
12045
|
* ```typescript
|
|
@@ -13859,7 +13902,7 @@ class BacktestInstance {
|
|
|
13859
13902
|
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
13860
13903
|
}
|
|
13861
13904
|
{
|
|
13862
|
-
backtest$1.
|
|
13905
|
+
backtest$1.strategyCoreService.clear({ symbol, strategyName: context.strategyName });
|
|
13863
13906
|
}
|
|
13864
13907
|
{
|
|
13865
13908
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
@@ -13894,8 +13937,8 @@ class BacktestInstance {
|
|
|
13894
13937
|
});
|
|
13895
13938
|
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
13896
13939
|
return () => {
|
|
13897
|
-
backtest$1.
|
|
13898
|
-
backtest$1.
|
|
13940
|
+
backtest$1.strategyCoreService.stop({ symbol, strategyName: context.strategyName }, true);
|
|
13941
|
+
backtest$1.strategyCoreService
|
|
13899
13942
|
.getPendingSignal(symbol, context.strategyName)
|
|
13900
13943
|
.then(async (pendingSignal) => {
|
|
13901
13944
|
if (pendingSignal) {
|
|
@@ -13936,7 +13979,7 @@ class BacktestInstance {
|
|
|
13936
13979
|
symbol,
|
|
13937
13980
|
strategyName,
|
|
13938
13981
|
});
|
|
13939
|
-
await backtest$1.
|
|
13982
|
+
await backtest$1.strategyCoreService.stop({ symbol, strategyName }, true);
|
|
13940
13983
|
};
|
|
13941
13984
|
/**
|
|
13942
13985
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
@@ -14351,7 +14394,7 @@ class LiveInstance {
|
|
|
14351
14394
|
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
14352
14395
|
}
|
|
14353
14396
|
{
|
|
14354
|
-
backtest$1.
|
|
14397
|
+
backtest$1.strategyCoreService.clear({ symbol, strategyName: context.strategyName });
|
|
14355
14398
|
}
|
|
14356
14399
|
{
|
|
14357
14400
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
@@ -14386,8 +14429,8 @@ class LiveInstance {
|
|
|
14386
14429
|
});
|
|
14387
14430
|
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
14388
14431
|
return () => {
|
|
14389
|
-
backtest$1.
|
|
14390
|
-
backtest$1.
|
|
14432
|
+
backtest$1.strategyCoreService.stop({ symbol, strategyName: context.strategyName }, false);
|
|
14433
|
+
backtest$1.strategyCoreService
|
|
14391
14434
|
.getPendingSignal(symbol, context.strategyName)
|
|
14392
14435
|
.then(async (pendingSignal) => {
|
|
14393
14436
|
if (pendingSignal) {
|
|
@@ -14428,7 +14471,7 @@ class LiveInstance {
|
|
|
14428
14471
|
symbol,
|
|
14429
14472
|
strategyName,
|
|
14430
14473
|
});
|
|
14431
|
-
await backtest$1.
|
|
14474
|
+
await backtest$1.strategyCoreService.stop({ symbol, strategyName }, false);
|
|
14432
14475
|
};
|
|
14433
14476
|
/**
|
|
14434
14477
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
@@ -15109,7 +15152,7 @@ class WalkerInstance {
|
|
|
15109
15152
|
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName });
|
|
15110
15153
|
}
|
|
15111
15154
|
{
|
|
15112
|
-
backtest$1.
|
|
15155
|
+
backtest$1.strategyCoreService.clear({ symbol, strategyName });
|
|
15113
15156
|
}
|
|
15114
15157
|
{
|
|
15115
15158
|
const { riskName } = backtest$1.strategySchemaService.get(strategyName);
|
|
@@ -15149,7 +15192,7 @@ class WalkerInstance {
|
|
|
15149
15192
|
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
15150
15193
|
return () => {
|
|
15151
15194
|
for (const strategyName of walkerSchema.strategies) {
|
|
15152
|
-
backtest$1.
|
|
15195
|
+
backtest$1.strategyCoreService.stop({ symbol, strategyName }, true);
|
|
15153
15196
|
walkerStopSubject.next({ symbol, strategyName, walkerName: context.walkerName });
|
|
15154
15197
|
}
|
|
15155
15198
|
if (!this._isDone) {
|
|
@@ -15195,7 +15238,7 @@ class WalkerInstance {
|
|
|
15195
15238
|
const walkerSchema = backtest$1.walkerSchemaService.get(walkerName);
|
|
15196
15239
|
for (const strategyName of walkerSchema.strategies) {
|
|
15197
15240
|
await walkerStopSubject.next({ symbol, strategyName, walkerName });
|
|
15198
|
-
await backtest$1.
|
|
15241
|
+
await backtest$1.strategyCoreService.stop({ symbol, strategyName }, true);
|
|
15199
15242
|
}
|
|
15200
15243
|
};
|
|
15201
15244
|
/**
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -159,6 +159,7 @@ declare function setLogger(logger: ILogger): void;
|
|
|
159
159
|
/**
|
|
160
160
|
* Sets global configuration parameters for the framework.
|
|
161
161
|
* @param config - Partial configuration object to override default settings
|
|
162
|
+
* @param _unsafe - Skip config validations - required for testbed
|
|
162
163
|
*
|
|
163
164
|
* @example
|
|
164
165
|
* ```typescript
|
|
@@ -540,6 +541,8 @@ declare const MethodContextService: (new () => {
|
|
|
540
541
|
interface IRiskCheckArgs {
|
|
541
542
|
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
542
543
|
symbol: string;
|
|
544
|
+
/** Pending signal to apply */
|
|
545
|
+
pendingSignal: ISignalDto;
|
|
543
546
|
/** Strategy name requesting to open a position */
|
|
544
547
|
strategyName: StrategyName;
|
|
545
548
|
/** Exchange name */
|
|
@@ -576,6 +579,8 @@ interface IRiskCallbacks {
|
|
|
576
579
|
* Extends IRiskCheckArgs with portfolio state data.
|
|
577
580
|
*/
|
|
578
581
|
interface IRiskValidationPayload extends IRiskCheckArgs {
|
|
582
|
+
/** Pending signal to apply */
|
|
583
|
+
pendingSignal: ISignalDto;
|
|
579
584
|
/** Number of currently active positions across all strategies */
|
|
580
585
|
activePositionCount: number;
|
|
581
586
|
/** List of currently active positions across all strategies */
|
|
@@ -7228,7 +7233,7 @@ declare class RiskConnectionService {
|
|
|
7228
7233
|
*
|
|
7229
7234
|
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
7230
7235
|
*/
|
|
7231
|
-
declare class
|
|
7236
|
+
declare class ExchangeCoreService {
|
|
7232
7237
|
private readonly loggerService;
|
|
7233
7238
|
private readonly exchangeConnectionService;
|
|
7234
7239
|
private readonly methodContextService;
|
|
@@ -7302,7 +7307,7 @@ declare class ExchangeGlobalService {
|
|
|
7302
7307
|
*
|
|
7303
7308
|
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
7304
7309
|
*/
|
|
7305
|
-
declare class
|
|
7310
|
+
declare class StrategyCoreService {
|
|
7306
7311
|
private readonly loggerService;
|
|
7307
7312
|
private readonly strategyConnectionService;
|
|
7308
7313
|
private readonly strategySchemaService;
|
|
@@ -7399,7 +7404,7 @@ declare class StrategyGlobalService {
|
|
|
7399
7404
|
* Wraps FrameConnectionService for timeframe generation.
|
|
7400
7405
|
* Used internally by BacktestLogicPrivateService.
|
|
7401
7406
|
*/
|
|
7402
|
-
declare class
|
|
7407
|
+
declare class FrameCoreService {
|
|
7403
7408
|
private readonly loggerService;
|
|
7404
7409
|
private readonly frameConnectionService;
|
|
7405
7410
|
private readonly frameValidationService;
|
|
@@ -7823,9 +7828,9 @@ declare class WalkerSchemaService {
|
|
|
7823
7828
|
*/
|
|
7824
7829
|
declare class BacktestLogicPrivateService {
|
|
7825
7830
|
private readonly loggerService;
|
|
7826
|
-
private readonly
|
|
7827
|
-
private readonly
|
|
7828
|
-
private readonly
|
|
7831
|
+
private readonly strategyCoreService;
|
|
7832
|
+
private readonly exchangeCoreService;
|
|
7833
|
+
private readonly frameCoreService;
|
|
7829
7834
|
private readonly methodContextService;
|
|
7830
7835
|
/**
|
|
7831
7836
|
* Runs backtest for a symbol, streaming closed signals as async generator.
|
|
@@ -7862,7 +7867,7 @@ declare class BacktestLogicPrivateService {
|
|
|
7862
7867
|
*/
|
|
7863
7868
|
declare class LiveLogicPrivateService {
|
|
7864
7869
|
private readonly loggerService;
|
|
7865
|
-
private readonly
|
|
7870
|
+
private readonly strategyCoreService;
|
|
7866
7871
|
private readonly methodContextService;
|
|
7867
7872
|
/**
|
|
7868
7873
|
* Runs live trading for a symbol, streaming results as async generator.
|
|
@@ -9293,13 +9298,13 @@ declare const backtest: {
|
|
|
9293
9298
|
liveCommandService: LiveCommandService;
|
|
9294
9299
|
backtestCommandService: BacktestCommandService;
|
|
9295
9300
|
walkerCommandService: WalkerCommandService;
|
|
9296
|
-
exchangeGlobalService: ExchangeGlobalService;
|
|
9297
|
-
strategyGlobalService: StrategyGlobalService;
|
|
9298
|
-
frameGlobalService: FrameGlobalService;
|
|
9299
9301
|
sizingGlobalService: SizingGlobalService;
|
|
9300
9302
|
riskGlobalService: RiskGlobalService;
|
|
9301
9303
|
optimizerGlobalService: OptimizerGlobalService;
|
|
9302
9304
|
partialGlobalService: PartialGlobalService;
|
|
9305
|
+
exchangeCoreService: ExchangeCoreService;
|
|
9306
|
+
strategyCoreService: StrategyCoreService;
|
|
9307
|
+
frameCoreService: FrameCoreService;
|
|
9303
9308
|
exchangeSchemaService: ExchangeSchemaService;
|
|
9304
9309
|
strategySchemaService: StrategySchemaService;
|
|
9305
9310
|
frameSchemaService: FrameSchemaService;
|