backtest-kit 5.0.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +685 -188
- package/build/index.mjs +686 -189
- package/package.json +1 -1
- package/types.d.ts +367 -19
package/build/index.cjs
CHANGED
|
@@ -89,6 +89,10 @@ const coreServices$1 = {
|
|
|
89
89
|
actionCoreService: Symbol('actionCoreService'),
|
|
90
90
|
frameCoreService: Symbol('frameCoreService'),
|
|
91
91
|
};
|
|
92
|
+
const metaServices$1 = {
|
|
93
|
+
priceMetaService: Symbol('priceMetaService'),
|
|
94
|
+
timeMetaService: Symbol('timeMetaService'),
|
|
95
|
+
};
|
|
92
96
|
const globalServices$1 = {
|
|
93
97
|
sizingGlobalService: Symbol('sizingGlobalService'),
|
|
94
98
|
riskGlobalService: Symbol('riskGlobalService'),
|
|
@@ -154,6 +158,7 @@ const TYPES = {
|
|
|
154
158
|
...connectionServices$1,
|
|
155
159
|
...schemaServices$1,
|
|
156
160
|
...coreServices$1,
|
|
161
|
+
...metaServices$1,
|
|
157
162
|
...globalServices$1,
|
|
158
163
|
...commandServices$1,
|
|
159
164
|
...logicPrivateServices$1,
|
|
@@ -3558,19 +3563,6 @@ const beginTime = (run) => (...args) => {
|
|
|
3558
3563
|
return fn();
|
|
3559
3564
|
};
|
|
3560
3565
|
|
|
3561
|
-
/**
|
|
3562
|
-
* Retrieves the current timestamp for debugging purposes.
|
|
3563
|
-
* If an execution context is active (e.g., during a backtest), it returns the timestamp from the context to ensure consistency with the simulated time.
|
|
3564
|
-
* Can be empty (undefined) if not called from strategy async context, as it's intended for debugging and not critical for logic.
|
|
3565
|
-
* @return {number | undefined} The current timestamp in milliseconds from the execution context, or undefined if not available.
|
|
3566
|
-
*/
|
|
3567
|
-
const getDebugTimestamp = () => {
|
|
3568
|
-
if (ExecutionContextService.hasContext()) {
|
|
3569
|
-
return bt.executionContextService.context.when.getTime();
|
|
3570
|
-
}
|
|
3571
|
-
return undefined;
|
|
3572
|
-
};
|
|
3573
|
-
|
|
3574
3566
|
const INTERVAL_MINUTES$6 = {
|
|
3575
3567
|
"1m": 1,
|
|
3576
3568
|
"3m": 3,
|
|
@@ -4278,7 +4270,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
4278
4270
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
4279
4271
|
timestamp: currentTime,
|
|
4280
4272
|
_isScheduled: false,
|
|
4281
|
-
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4273
|
+
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp: currentTime }],
|
|
4282
4274
|
};
|
|
4283
4275
|
// Валидируем сигнал перед возвратом
|
|
4284
4276
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -4302,7 +4294,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
4302
4294
|
pendingAt: SCHEDULED_SIGNAL_PENDING_MOCK, // Временно, обновится при активации
|
|
4303
4295
|
timestamp: currentTime,
|
|
4304
4296
|
_isScheduled: true,
|
|
4305
|
-
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4297
|
+
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp: currentTime }],
|
|
4306
4298
|
};
|
|
4307
4299
|
// Валидируем сигнал перед возвратом
|
|
4308
4300
|
VALIDATE_SIGNAL_FN(scheduledSignalRow, currentPrice, true);
|
|
@@ -4322,7 +4314,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
4322
4314
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
4323
4315
|
timestamp: currentTime,
|
|
4324
4316
|
_isScheduled: false,
|
|
4325
|
-
_entry: [{ price: currentPrice, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4317
|
+
_entry: [{ price: currentPrice, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp: currentTime }],
|
|
4326
4318
|
};
|
|
4327
4319
|
// Валидируем сигнал перед возвратом
|
|
4328
4320
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -4392,7 +4384,7 @@ const WAIT_FOR_DISPOSE_FN$1 = async (self) => {
|
|
|
4392
4384
|
self.params.logger.debug("ClientStrategy dispose");
|
|
4393
4385
|
await self.params.onDispose(self.params.execution.context.symbol, self.params.strategyName, self.params.exchangeName, self.params.method.context.frameName, self.params.execution.context.backtest);
|
|
4394
4386
|
};
|
|
4395
|
-
const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice) => {
|
|
4387
|
+
const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice, timestamp) => {
|
|
4396
4388
|
// Initialize partial array if not present
|
|
4397
4389
|
if (!signal._partial)
|
|
4398
4390
|
signal._partial = [];
|
|
@@ -4421,7 +4413,7 @@ const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
4421
4413
|
entryCountAtClose,
|
|
4422
4414
|
currentPrice,
|
|
4423
4415
|
costBasisAtClose: remainingCostBasis,
|
|
4424
|
-
|
|
4416
|
+
timestamp,
|
|
4425
4417
|
});
|
|
4426
4418
|
self.params.logger.info("PARTIAL_PROFIT_FN executed", {
|
|
4427
4419
|
signalId: signal.id,
|
|
@@ -4431,7 +4423,7 @@ const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
4431
4423
|
});
|
|
4432
4424
|
return true;
|
|
4433
4425
|
};
|
|
4434
|
-
const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice) => {
|
|
4426
|
+
const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice, timestamp) => {
|
|
4435
4427
|
// Initialize partial array if not present
|
|
4436
4428
|
if (!signal._partial)
|
|
4437
4429
|
signal._partial = [];
|
|
@@ -4459,7 +4451,7 @@ const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
4459
4451
|
currentPrice,
|
|
4460
4452
|
entryCountAtClose,
|
|
4461
4453
|
costBasisAtClose: remainingCostBasis,
|
|
4462
|
-
|
|
4454
|
+
timestamp,
|
|
4463
4455
|
});
|
|
4464
4456
|
self.params.logger.warn("PARTIAL_LOSS_FN executed", {
|
|
4465
4457
|
signalId: signal.id,
|
|
@@ -4856,10 +4848,10 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4856
4848
|
});
|
|
4857
4849
|
return true;
|
|
4858
4850
|
};
|
|
4859
|
-
const AVERAGE_BUY_FN = (self, signal, currentPrice, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) => {
|
|
4851
|
+
const AVERAGE_BUY_FN = (self, signal, currentPrice, timestamp, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) => {
|
|
4860
4852
|
// Ensure _entry is initialized (handles signals loaded from disk without _entry)
|
|
4861
4853
|
if (!signal._entry || signal._entry.length === 0) {
|
|
4862
|
-
signal._entry = [{ price: signal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4854
|
+
signal._entry = [{ price: signal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp }];
|
|
4863
4855
|
}
|
|
4864
4856
|
if (signal.position === "long") {
|
|
4865
4857
|
// LONG: new entry must beat the all-time low — strictly below every prior entry price
|
|
@@ -4889,7 +4881,7 @@ const AVERAGE_BUY_FN = (self, signal, currentPrice, cost = GLOBAL_CONFIG.CC_POSI
|
|
|
4889
4881
|
return false;
|
|
4890
4882
|
}
|
|
4891
4883
|
}
|
|
4892
|
-
signal._entry.push({ price: currentPrice, cost,
|
|
4884
|
+
signal._entry.push({ price: currentPrice, cost, timestamp });
|
|
4893
4885
|
self.params.logger.info("AVERAGE_BUY_FN executed", {
|
|
4894
4886
|
signalId: signal.id,
|
|
4895
4887
|
position: signal.position,
|
|
@@ -5072,7 +5064,7 @@ const CALL_SCHEDULE_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (s
|
|
|
5072
5064
|
await ExecutionContextService.runInContext(async () => {
|
|
5073
5065
|
const publicSignal = TO_PUBLIC_SIGNAL(scheduled, currentPrice);
|
|
5074
5066
|
// Call system onSchedulePing callback first (emits to pingSubject)
|
|
5075
|
-
await self.params.onSchedulePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, self.params.execution.context.backtest, timestamp);
|
|
5067
|
+
await self.params.onSchedulePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, currentPrice, self.params.execution.context.backtest, timestamp);
|
|
5076
5068
|
// Call user onSchedulePing callback only if signal is still active (not cancelled, not activated)
|
|
5077
5069
|
if (self.params.callbacks?.onSchedulePing) {
|
|
5078
5070
|
await self.params.callbacks.onSchedulePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
@@ -5098,7 +5090,7 @@ const CALL_ACTIVE_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (sel
|
|
|
5098
5090
|
await ExecutionContextService.runInContext(async () => {
|
|
5099
5091
|
const publicSignal = TO_PUBLIC_SIGNAL(pending, currentPrice);
|
|
5100
5092
|
// Call system onActivePing callback first (emits to activePingSubject)
|
|
5101
|
-
await self.params.onActivePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, self.params.execution.context.backtest, timestamp);
|
|
5093
|
+
await self.params.onActivePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, currentPrice, self.params.execution.context.backtest, timestamp);
|
|
5102
5094
|
// Call user onActivePing callback only if signal is still active (not closed)
|
|
5103
5095
|
if (self.params.callbacks?.onActivePing) {
|
|
5104
5096
|
await self.params.callbacks.onActivePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
@@ -5896,6 +5888,57 @@ const CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, signal, averagePrice, c
|
|
|
5896
5888
|
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, closeTimestamp, self.params.execution.context.backtest);
|
|
5897
5889
|
return result;
|
|
5898
5890
|
};
|
|
5891
|
+
const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, averagePrice, closeTimestamp) => {
|
|
5892
|
+
const syncCloseAllowed = await CALL_SIGNAL_SYNC_CLOSE_FN(closeTimestamp, averagePrice, "closed", closedSignal, self);
|
|
5893
|
+
if (!syncCloseAllowed) {
|
|
5894
|
+
self.params.logger.info("ClientStrategy backtest: user-closed signal rejected by sync, will retry", {
|
|
5895
|
+
symbol: self.params.execution.context.symbol,
|
|
5896
|
+
signalId: closedSignal.id,
|
|
5897
|
+
});
|
|
5898
|
+
self._closedSignal = null;
|
|
5899
|
+
self._pendingSignal = closedSignal;
|
|
5900
|
+
throw new Error(`ClientStrategy backtest: signal close rejected by sync (signalId=${closedSignal.id}). ` +
|
|
5901
|
+
`Retry backtest() with new candle data.`);
|
|
5902
|
+
}
|
|
5903
|
+
self._closedSignal = null;
|
|
5904
|
+
await CALL_COMMIT_FN(self, {
|
|
5905
|
+
action: "close-pending",
|
|
5906
|
+
symbol: self.params.execution.context.symbol,
|
|
5907
|
+
strategyName: self.params.strategyName,
|
|
5908
|
+
exchangeName: self.params.exchangeName,
|
|
5909
|
+
frameName: self.params.frameName,
|
|
5910
|
+
signalId: closedSignal.id,
|
|
5911
|
+
backtest: true,
|
|
5912
|
+
closeId: closedSignal.closeId,
|
|
5913
|
+
timestamp: closeTimestamp,
|
|
5914
|
+
totalEntries: closedSignal._entry?.length ?? 1,
|
|
5915
|
+
totalPartials: closedSignal._partial?.length ?? 0,
|
|
5916
|
+
originalPriceOpen: closedSignal.priceOpen,
|
|
5917
|
+
pnl: toProfitLossDto(closedSignal, averagePrice),
|
|
5918
|
+
});
|
|
5919
|
+
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
5920
|
+
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
5921
|
+
await CALL_BREAKEVEN_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
5922
|
+
await CALL_RISK_REMOVE_SIGNAL_FN(self, self.params.execution.context.symbol, closeTimestamp, self.params.execution.context.backtest);
|
|
5923
|
+
const pnl = toProfitLossDto(closedSignal, averagePrice);
|
|
5924
|
+
const result = {
|
|
5925
|
+
action: "closed",
|
|
5926
|
+
signal: TO_PUBLIC_SIGNAL(closedSignal, averagePrice),
|
|
5927
|
+
currentPrice: averagePrice,
|
|
5928
|
+
closeReason: "closed",
|
|
5929
|
+
closeTimestamp,
|
|
5930
|
+
pnl,
|
|
5931
|
+
strategyName: self.params.method.context.strategyName,
|
|
5932
|
+
exchangeName: self.params.method.context.exchangeName,
|
|
5933
|
+
frameName: self.params.method.context.frameName,
|
|
5934
|
+
symbol: self.params.execution.context.symbol,
|
|
5935
|
+
backtest: self.params.execution.context.backtest,
|
|
5936
|
+
closeId: closedSignal.closeId,
|
|
5937
|
+
createdAt: closeTimestamp,
|
|
5938
|
+
};
|
|
5939
|
+
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, closeTimestamp, self.params.execution.context.backtest);
|
|
5940
|
+
return result;
|
|
5941
|
+
};
|
|
5899
5942
|
const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) => {
|
|
5900
5943
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
5901
5944
|
const maxTimeToWait = GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES * 60 * 1000;
|
|
@@ -5913,7 +5956,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5913
5956
|
if (self._cancelledSignal) {
|
|
5914
5957
|
// Сигнал был отменен через cancel() в onSchedulePing
|
|
5915
5958
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user");
|
|
5916
|
-
return {
|
|
5959
|
+
return { outcome: "cancelled", result };
|
|
5917
5960
|
}
|
|
5918
5961
|
// КРИТИЧНО: Проверяем был ли сигнал активирован пользователем через activateScheduled()
|
|
5919
5962
|
// Обрабатываем inline (как в tick()) с риск-проверкой по averagePrice
|
|
@@ -5927,7 +5970,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5927
5970
|
signalId: activatedSignal.id,
|
|
5928
5971
|
});
|
|
5929
5972
|
await self.setScheduledSignal(null);
|
|
5930
|
-
return {
|
|
5973
|
+
return { outcome: "pending" };
|
|
5931
5974
|
}
|
|
5932
5975
|
// Риск-проверка по averagePrice (симметрия с LIVE tick())
|
|
5933
5976
|
if (await functoolsKit.not(CALL_RISK_CHECK_SIGNAL_FN(self, self.params.execution.context.symbol, activatedSignal, averagePrice, candle.timestamp, self.params.execution.context.backtest))) {
|
|
@@ -5936,7 +5979,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5936
5979
|
signalId: activatedSignal.id,
|
|
5937
5980
|
});
|
|
5938
5981
|
await self.setScheduledSignal(null);
|
|
5939
|
-
return {
|
|
5982
|
+
return { outcome: "pending" };
|
|
5940
5983
|
}
|
|
5941
5984
|
const pendingSignal = {
|
|
5942
5985
|
...activatedSignal,
|
|
@@ -5965,7 +6008,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5965
6008
|
originalPriceOpen: activatedSignal.priceOpen,
|
|
5966
6009
|
pnl: toProfitLossDto(activatedSignal, averagePrice),
|
|
5967
6010
|
});
|
|
5968
|
-
return {
|
|
6011
|
+
return { outcome: "pending" };
|
|
5969
6012
|
}
|
|
5970
6013
|
await self.setScheduledSignal(null);
|
|
5971
6014
|
await self.setPendingSignal(pendingSignal);
|
|
@@ -5998,18 +6041,13 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5998
6041
|
});
|
|
5999
6042
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
|
|
6000
6043
|
await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
|
|
6001
|
-
return {
|
|
6002
|
-
activated: true,
|
|
6003
|
-
cancelled: false,
|
|
6004
|
-
activationIndex: i,
|
|
6005
|
-
result: null,
|
|
6006
|
-
};
|
|
6044
|
+
return { outcome: "activated", activationIndex: i };
|
|
6007
6045
|
}
|
|
6008
6046
|
// КРИТИЧНО: Проверяем timeout ПЕРЕД проверкой цены
|
|
6009
6047
|
const elapsedTime = candle.timestamp - scheduled.scheduledAt;
|
|
6010
6048
|
if (elapsedTime >= maxTimeToWait) {
|
|
6011
6049
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "timeout");
|
|
6012
|
-
return {
|
|
6050
|
+
return { outcome: "cancelled", result };
|
|
6013
6051
|
}
|
|
6014
6052
|
let shouldActivate = false;
|
|
6015
6053
|
let shouldCancel = false;
|
|
@@ -6047,27 +6085,17 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
6047
6085
|
}
|
|
6048
6086
|
if (shouldCancel) {
|
|
6049
6087
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "price_reject");
|
|
6050
|
-
return {
|
|
6088
|
+
return { outcome: "cancelled", result };
|
|
6051
6089
|
}
|
|
6052
6090
|
if (shouldActivate) {
|
|
6053
6091
|
await ACTIVATE_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, candle.timestamp);
|
|
6054
|
-
return {
|
|
6055
|
-
activated: true,
|
|
6056
|
-
cancelled: false,
|
|
6057
|
-
activationIndex: i,
|
|
6058
|
-
result: null,
|
|
6059
|
-
};
|
|
6092
|
+
return { outcome: "activated", activationIndex: i };
|
|
6060
6093
|
}
|
|
6061
6094
|
await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true, averagePrice);
|
|
6062
6095
|
// Process queued commit events with candle timestamp
|
|
6063
6096
|
await PROCESS_COMMIT_QUEUE_FN(self, averagePrice, candle.timestamp);
|
|
6064
6097
|
}
|
|
6065
|
-
return {
|
|
6066
|
-
activated: false,
|
|
6067
|
-
cancelled: false,
|
|
6068
|
-
activationIndex: -1,
|
|
6069
|
-
result: null,
|
|
6070
|
-
};
|
|
6098
|
+
return { outcome: "pending" };
|
|
6071
6099
|
};
|
|
6072
6100
|
const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
6073
6101
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
@@ -6086,6 +6114,10 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
6086
6114
|
const startIndex = Math.max(0, i - (candlesCount - 1));
|
|
6087
6115
|
const recentCandles = candles.slice(startIndex, i + 1);
|
|
6088
6116
|
const averagePrice = GET_AVG_PRICE_FN(recentCandles);
|
|
6117
|
+
// КРИТИЧНО: Проверяем был ли сигнал закрыт пользователем через closePending()
|
|
6118
|
+
if (self._closedSignal) {
|
|
6119
|
+
return await CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN(self, self._closedSignal, averagePrice, currentCandleTimestamp);
|
|
6120
|
+
}
|
|
6089
6121
|
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentCandleTimestamp, true, averagePrice);
|
|
6090
6122
|
let shouldClose = false;
|
|
6091
6123
|
let closeReason;
|
|
@@ -6190,7 +6222,32 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
6190
6222
|
// Process queued commit events with candle timestamp
|
|
6191
6223
|
await PROCESS_COMMIT_QUEUE_FN(self, averagePrice, currentCandleTimestamp);
|
|
6192
6224
|
}
|
|
6193
|
-
|
|
6225
|
+
// Loop exhausted without closing — check if we have enough data
|
|
6226
|
+
const lastCandles = candles.slice(-GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT);
|
|
6227
|
+
const lastPrice = GET_AVG_PRICE_FN(lastCandles);
|
|
6228
|
+
const closeTimestamp = lastCandles[lastCandles.length - 1].timestamp;
|
|
6229
|
+
const signalTime = signal.pendingAt;
|
|
6230
|
+
const maxTimeToWait = signal.minuteEstimatedTime * 60 * 1000;
|
|
6231
|
+
const elapsedTime = closeTimestamp - signalTime;
|
|
6232
|
+
if (elapsedTime < maxTimeToWait) {
|
|
6233
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
6234
|
+
const requiredCandlesCount = signal.minuteEstimatedTime + bufferCandlesCount + 1;
|
|
6235
|
+
throw new Error(functoolsKit.str.newline(`ClientStrategy backtest: Insufficient candle data for pending signal. ` +
|
|
6236
|
+
`Signal opened at ${new Date(signal.pendingAt).toISOString()}, ` +
|
|
6237
|
+
`last candle at ${new Date(closeTimestamp).toISOString()}. ` +
|
|
6238
|
+
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${signal.minuteEstimatedTime}min required. ` +
|
|
6239
|
+
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
6240
|
+
`\nBreakdown: ${signal.minuteEstimatedTime} candles for signal lifetime + ${bufferCandlesCount} buffer candles. ` +
|
|
6241
|
+
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
6242
|
+
`so first ${bufferCandlesCount} candles are skipped to ensure accurate price averaging. ` +
|
|
6243
|
+
`Provide complete candle range: [pendingAt - ${bufferCandlesCount}min, pendingAt + ${signal.minuteEstimatedTime}min].`));
|
|
6244
|
+
}
|
|
6245
|
+
const timeExpiredResult = await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(self, signal, lastPrice, "time_expired", closeTimestamp);
|
|
6246
|
+
if (!timeExpiredResult) {
|
|
6247
|
+
throw new Error(`ClientStrategy backtest: time_expired close rejected by sync (signalId=${signal.id}). ` +
|
|
6248
|
+
`Retry backtest() with new candle data.`);
|
|
6249
|
+
}
|
|
6250
|
+
return timeExpiredResult;
|
|
6194
6251
|
};
|
|
6195
6252
|
/**
|
|
6196
6253
|
* Client implementation for trading strategy lifecycle management.
|
|
@@ -6674,16 +6731,16 @@ class ClientStrategy {
|
|
|
6674
6731
|
* // No DCA: [{ price: 43000, cost: 100 }]
|
|
6675
6732
|
* // One DCA: [{ price: 43000, cost: 100 }, { price: 42000, cost: 100 }]
|
|
6676
6733
|
*/
|
|
6677
|
-
async getPositionEntries(symbol) {
|
|
6734
|
+
async getPositionEntries(symbol, timestamp) {
|
|
6678
6735
|
this.params.logger.debug("ClientStrategy getPositionEntries", { symbol });
|
|
6679
6736
|
if (!this._pendingSignal) {
|
|
6680
6737
|
return null;
|
|
6681
6738
|
}
|
|
6682
6739
|
const entries = this._pendingSignal._entry;
|
|
6683
6740
|
if (!entries || entries.length === 0) {
|
|
6684
|
-
return [{ price: this._pendingSignal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST }];
|
|
6741
|
+
return [{ price: this._pendingSignal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp }];
|
|
6685
6742
|
}
|
|
6686
|
-
return entries.map(({ price, cost }) => ({ price, cost }));
|
|
6743
|
+
return entries.map(({ price, cost, timestamp }) => ({ price, cost, timestamp }));
|
|
6687
6744
|
}
|
|
6688
6745
|
/**
|
|
6689
6746
|
* Performs a single tick of strategy execution.
|
|
@@ -7137,11 +7194,12 @@ class ClientStrategy {
|
|
|
7137
7194
|
priceOpen: scheduled.priceOpen,
|
|
7138
7195
|
position: scheduled.position,
|
|
7139
7196
|
});
|
|
7140
|
-
const
|
|
7141
|
-
if (
|
|
7142
|
-
return result;
|
|
7197
|
+
const scheduledResult = await PROCESS_SCHEDULED_SIGNAL_CANDLES_FN(this, scheduled, candles);
|
|
7198
|
+
if (scheduledResult.outcome === "cancelled") {
|
|
7199
|
+
return scheduledResult.result;
|
|
7143
7200
|
}
|
|
7144
|
-
if (activated) {
|
|
7201
|
+
if (scheduledResult.outcome === "activated") {
|
|
7202
|
+
const { activationIndex } = scheduledResult;
|
|
7145
7203
|
// КРИТИЧНО: activationIndex - индекс свечи активации в массиве candles
|
|
7146
7204
|
// BacktestLogicPrivateService включил буфер в начало массива, поэтому перед activationIndex достаточно свечей
|
|
7147
7205
|
// PROCESS_PENDING_SIGNAL_CANDLES_FN пропустит первые bufferCandlesCount свечей для VWAP
|
|
@@ -7213,40 +7271,7 @@ class ClientStrategy {
|
|
|
7213
7271
|
if (candles.length < candlesCount) {
|
|
7214
7272
|
this.params.logger.warn(`ClientStrategy backtest: Expected at least ${candlesCount} candles for VWAP, got ${candles.length}`);
|
|
7215
7273
|
}
|
|
7216
|
-
|
|
7217
|
-
if (closedResult) {
|
|
7218
|
-
return closedResult;
|
|
7219
|
-
}
|
|
7220
|
-
// Signal didn't close during candle processing - check if we have enough data
|
|
7221
|
-
const lastCandles = candles.slice(-GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT);
|
|
7222
|
-
const lastPrice = GET_AVG_PRICE_FN(lastCandles);
|
|
7223
|
-
const closeTimestamp = lastCandles[lastCandles.length - 1].timestamp;
|
|
7224
|
-
const signalTime = signal.pendingAt;
|
|
7225
|
-
const maxTimeToWait = signal.minuteEstimatedTime * 60 * 1000;
|
|
7226
|
-
const elapsedTime = closeTimestamp - signalTime;
|
|
7227
|
-
// Check if we actually reached time expiration or just ran out of candles
|
|
7228
|
-
if (elapsedTime < maxTimeToWait) {
|
|
7229
|
-
// EDGE CASE: backtest() called with insufficient candle data
|
|
7230
|
-
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
7231
|
-
const requiredCandlesCount = signal.minuteEstimatedTime + bufferCandlesCount + 1;
|
|
7232
|
-
throw new Error(functoolsKit.str.newline(`ClientStrategy backtest: Insufficient candle data for pending signal. ` +
|
|
7233
|
-
`Signal opened at ${new Date(signal.pendingAt).toISOString()}, ` +
|
|
7234
|
-
`last candle at ${new Date(closeTimestamp).toISOString()}. ` +
|
|
7235
|
-
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${signal.minuteEstimatedTime}min required. ` +
|
|
7236
|
-
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
7237
|
-
`\nBreakdown: ${signal.minuteEstimatedTime} candles for signal lifetime + ${bufferCandlesCount} buffer candles. ` +
|
|
7238
|
-
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
7239
|
-
`so first ${bufferCandlesCount} candles are skipped to ensure accurate price averaging. ` +
|
|
7240
|
-
`Provide complete candle range: [pendingAt - ${bufferCandlesCount}min, pendingAt + ${signal.minuteEstimatedTime}min].`));
|
|
7241
|
-
}
|
|
7242
|
-
// Time actually expired - close with time_expired
|
|
7243
|
-
const timeExpiredResult = await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(this, signal, lastPrice, "time_expired", closeTimestamp);
|
|
7244
|
-
if (!timeExpiredResult) {
|
|
7245
|
-
// Sync rejected the close — signal remains in _pendingSignal, caller must retry
|
|
7246
|
-
throw new Error(`ClientStrategy backtest: time_expired close rejected by sync (signalId=${signal.id}). ` +
|
|
7247
|
-
`Retry backtest() with new candle data.`);
|
|
7248
|
-
}
|
|
7249
|
-
return timeExpiredResult;
|
|
7274
|
+
return await PROCESS_PENDING_SIGNAL_CANDLES_FN(this, signal, candles);
|
|
7250
7275
|
}
|
|
7251
7276
|
/**
|
|
7252
7277
|
* Stops the strategy from generating new signals.
|
|
@@ -7519,7 +7544,7 @@ class ClientStrategy {
|
|
|
7519
7544
|
* // success3 = false (skipped, would exceed 100%)
|
|
7520
7545
|
* ```
|
|
7521
7546
|
*/
|
|
7522
|
-
async partialProfit(symbol, percentToClose, currentPrice, backtest) {
|
|
7547
|
+
async partialProfit(symbol, percentToClose, currentPrice, backtest, timestamp) {
|
|
7523
7548
|
this.params.logger.debug("ClientStrategy partialProfit", {
|
|
7524
7549
|
symbol,
|
|
7525
7550
|
percentToClose,
|
|
@@ -7583,7 +7608,7 @@ class ClientStrategy {
|
|
|
7583
7608
|
return false;
|
|
7584
7609
|
}
|
|
7585
7610
|
// Execute partial close logic
|
|
7586
|
-
const wasExecuted = PARTIAL_PROFIT_FN(this, this._pendingSignal, percentToClose, currentPrice);
|
|
7611
|
+
const wasExecuted = PARTIAL_PROFIT_FN(this, this._pendingSignal, percentToClose, currentPrice, timestamp);
|
|
7587
7612
|
// If partial was not executed (exceeded 100%), return false without persistence
|
|
7588
7613
|
if (!wasExecuted) {
|
|
7589
7614
|
return false;
|
|
@@ -7702,7 +7727,7 @@ class ClientStrategy {
|
|
|
7702
7727
|
* // success3 = false (skipped, would exceed 100%)
|
|
7703
7728
|
* ```
|
|
7704
7729
|
*/
|
|
7705
|
-
async partialLoss(symbol, percentToClose, currentPrice, backtest) {
|
|
7730
|
+
async partialLoss(symbol, percentToClose, currentPrice, backtest, timestamp) {
|
|
7706
7731
|
this.params.logger.debug("ClientStrategy partialLoss", {
|
|
7707
7732
|
symbol,
|
|
7708
7733
|
percentToClose,
|
|
@@ -7766,7 +7791,7 @@ class ClientStrategy {
|
|
|
7766
7791
|
return false;
|
|
7767
7792
|
}
|
|
7768
7793
|
// Execute partial close logic
|
|
7769
|
-
const wasExecuted = PARTIAL_LOSS_FN(this, this._pendingSignal, percentToClose, currentPrice);
|
|
7794
|
+
const wasExecuted = PARTIAL_LOSS_FN(this, this._pendingSignal, percentToClose, currentPrice, timestamp);
|
|
7770
7795
|
// If partial was not executed (exceeded 100%), return false without persistence
|
|
7771
7796
|
if (!wasExecuted) {
|
|
7772
7797
|
return false;
|
|
@@ -8522,7 +8547,7 @@ class ClientStrategy {
|
|
|
8522
8547
|
* @param backtest - Whether running in backtest mode
|
|
8523
8548
|
* @returns Promise<boolean> - true if entry added, false if rejected by direction check
|
|
8524
8549
|
*/
|
|
8525
|
-
async averageBuy(symbol, currentPrice, backtest, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) {
|
|
8550
|
+
async averageBuy(symbol, currentPrice, backtest, timestamp, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) {
|
|
8526
8551
|
this.params.logger.debug("ClientStrategy averageBuy", {
|
|
8527
8552
|
symbol,
|
|
8528
8553
|
currentPrice,
|
|
@@ -8537,7 +8562,7 @@ class ClientStrategy {
|
|
|
8537
8562
|
throw new Error(`ClientStrategy averageBuy: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
8538
8563
|
}
|
|
8539
8564
|
// Execute averaging logic
|
|
8540
|
-
const result = AVERAGE_BUY_FN(this, this._pendingSignal, currentPrice, cost);
|
|
8565
|
+
const result = AVERAGE_BUY_FN(this, this._pendingSignal, currentPrice, timestamp, cost);
|
|
8541
8566
|
if (!result) {
|
|
8542
8567
|
return false;
|
|
8543
8568
|
}
|
|
@@ -9001,7 +9026,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
|
|
|
9001
9026
|
* @param backtest - Whether running in backtest mode
|
|
9002
9027
|
* @returns Unique string key for memoization
|
|
9003
9028
|
*/
|
|
9004
|
-
const CREATE_KEY_FN$
|
|
9029
|
+
const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
9005
9030
|
const parts = [symbol, strategyName, exchangeName];
|
|
9006
9031
|
if (frameName)
|
|
9007
9032
|
parts.push(frameName);
|
|
@@ -9017,11 +9042,12 @@ const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest
|
|
|
9017
9042
|
* @param self - Reference to StrategyConnectionService instance
|
|
9018
9043
|
* @returns Callback function for schedule ping events
|
|
9019
9044
|
*/
|
|
9020
|
-
const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => functoolsKit.trycatch(async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
9045
|
+
const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => functoolsKit.trycatch(async (symbol, strategyName, exchangeName, data, currentPrice, backtest, timestamp) => {
|
|
9021
9046
|
const event = {
|
|
9022
9047
|
symbol,
|
|
9023
9048
|
strategyName,
|
|
9024
9049
|
exchangeName,
|
|
9050
|
+
currentPrice,
|
|
9025
9051
|
data,
|
|
9026
9052
|
backtest,
|
|
9027
9053
|
timestamp,
|
|
@@ -9050,11 +9076,12 @@ const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => functoolsKit.trycatch(async (sy
|
|
|
9050
9076
|
* @param self - Reference to StrategyConnectionService instance
|
|
9051
9077
|
* @returns Callback function for active ping events
|
|
9052
9078
|
*/
|
|
9053
|
-
const CREATE_COMMIT_ACTIVE_PING_FN = (self) => functoolsKit.trycatch(async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
9079
|
+
const CREATE_COMMIT_ACTIVE_PING_FN = (self) => functoolsKit.trycatch(async (symbol, strategyName, exchangeName, data, currentPrice, backtest, timestamp) => {
|
|
9054
9080
|
const event = {
|
|
9055
9081
|
symbol,
|
|
9056
9082
|
strategyName,
|
|
9057
9083
|
exchangeName,
|
|
9084
|
+
currentPrice,
|
|
9058
9085
|
data,
|
|
9059
9086
|
backtest,
|
|
9060
9087
|
timestamp,
|
|
@@ -9178,6 +9205,8 @@ class StrategyConnectionService {
|
|
|
9178
9205
|
this.partialConnectionService = inject(TYPES.partialConnectionService);
|
|
9179
9206
|
this.breakevenConnectionService = inject(TYPES.breakevenConnectionService);
|
|
9180
9207
|
this.actionCoreService = inject(TYPES.actionCoreService);
|
|
9208
|
+
this.timeMetaService = inject(TYPES.timeMetaService);
|
|
9209
|
+
this.priceMetaService = inject(TYPES.priceMetaService);
|
|
9181
9210
|
/**
|
|
9182
9211
|
* Retrieves memoized ClientStrategy instance for given symbol-strategy pair with exchange and frame isolation.
|
|
9183
9212
|
*
|
|
@@ -9191,7 +9220,7 @@ class StrategyConnectionService {
|
|
|
9191
9220
|
* @param backtest - Whether running in backtest mode
|
|
9192
9221
|
* @returns Configured ClientStrategy instance
|
|
9193
9222
|
*/
|
|
9194
|
-
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
9223
|
+
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$o(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
9195
9224
|
const { riskName = "", riskList = [], getSignal, interval, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
9196
9225
|
return new ClientStrategy({
|
|
9197
9226
|
symbol,
|
|
@@ -9278,6 +9307,20 @@ class StrategyConnectionService {
|
|
|
9278
9307
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9279
9308
|
return await strategy.getTotalCostClosed(symbol);
|
|
9280
9309
|
};
|
|
9310
|
+
/**
|
|
9311
|
+
* Returns the effective (DCA-averaged) entry price for the current pending signal.
|
|
9312
|
+
*
|
|
9313
|
+
* This is the harmonic mean of all _entry prices, which is the correct
|
|
9314
|
+
* cost-basis price used in all PNL calculations.
|
|
9315
|
+
* With no DCA entries, equals the original priceOpen.
|
|
9316
|
+
*
|
|
9317
|
+
* Returns null if no pending signal exists.
|
|
9318
|
+
*
|
|
9319
|
+
* @param backtest - Whether running in backtest mode
|
|
9320
|
+
* @param symbol - Trading pair symbol
|
|
9321
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9322
|
+
* @returns Promise resolving to effective entry price or null
|
|
9323
|
+
*/
|
|
9281
9324
|
this.getPositionAveragePrice = async (backtest, symbol, context) => {
|
|
9282
9325
|
this.loggerService.log("strategyConnectionService getPositionAveragePrice", {
|
|
9283
9326
|
symbol,
|
|
@@ -9287,6 +9330,19 @@ class StrategyConnectionService {
|
|
|
9287
9330
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9288
9331
|
return await strategy.getPositionAveragePrice(symbol);
|
|
9289
9332
|
};
|
|
9333
|
+
/**
|
|
9334
|
+
* Returns the number of DCA entries made for the current pending signal.
|
|
9335
|
+
*
|
|
9336
|
+
* 1 = original entry only (no DCA).
|
|
9337
|
+
* Increases by 1 with each successful commitAverageBuy().
|
|
9338
|
+
*
|
|
9339
|
+
* Returns null if no pending signal exists.
|
|
9340
|
+
*
|
|
9341
|
+
* @param backtest - Whether running in backtest mode
|
|
9342
|
+
* @param symbol - Trading pair symbol
|
|
9343
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9344
|
+
* @returns Promise resolving to entry count or null
|
|
9345
|
+
*/
|
|
9290
9346
|
this.getPositionInvestedCount = async (backtest, symbol, context) => {
|
|
9291
9347
|
this.loggerService.log("strategyConnectionService getPositionInvestedCount", {
|
|
9292
9348
|
symbol,
|
|
@@ -9296,6 +9352,19 @@ class StrategyConnectionService {
|
|
|
9296
9352
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9297
9353
|
return await strategy.getPositionInvestedCount(symbol);
|
|
9298
9354
|
};
|
|
9355
|
+
/**
|
|
9356
|
+
* Returns the total invested cost basis in dollars for the current pending signal.
|
|
9357
|
+
*
|
|
9358
|
+
* Equal to entryCount × $100 (COST_BASIS_PER_ENTRY).
|
|
9359
|
+
* 1 entry = $100, 2 entries = $200, etc.
|
|
9360
|
+
*
|
|
9361
|
+
* Returns null if no pending signal exists.
|
|
9362
|
+
*
|
|
9363
|
+
* @param backtest - Whether running in backtest mode
|
|
9364
|
+
* @param symbol - Trading pair symbol
|
|
9365
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9366
|
+
* @returns Promise resolving to total invested cost in dollars or null
|
|
9367
|
+
*/
|
|
9299
9368
|
this.getPositionInvestedCost = async (backtest, symbol, context) => {
|
|
9300
9369
|
this.loggerService.log("strategyConnectionService getPositionInvestedCost", {
|
|
9301
9370
|
symbol,
|
|
@@ -9305,6 +9374,20 @@ class StrategyConnectionService {
|
|
|
9305
9374
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9306
9375
|
return await strategy.getPositionInvestedCost(symbol);
|
|
9307
9376
|
};
|
|
9377
|
+
/**
|
|
9378
|
+
* Returns the unrealized PNL percentage for the current pending signal at currentPrice.
|
|
9379
|
+
*
|
|
9380
|
+
* Accounts for partial closes, DCA entries, slippage and fees
|
|
9381
|
+
* (delegates to toProfitLossDto).
|
|
9382
|
+
*
|
|
9383
|
+
* Returns null if no pending signal exists.
|
|
9384
|
+
*
|
|
9385
|
+
* @param backtest - Whether running in backtest mode
|
|
9386
|
+
* @param symbol - Trading pair symbol
|
|
9387
|
+
* @param currentPrice - Current market price
|
|
9388
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9389
|
+
* @returns Promise resolving to pnlPercentage or null
|
|
9390
|
+
*/
|
|
9308
9391
|
this.getPositionPnlPercent = async (backtest, symbol, currentPrice, context) => {
|
|
9309
9392
|
this.loggerService.log("strategyConnectionService getPositionPnlPercent", {
|
|
9310
9393
|
symbol,
|
|
@@ -9315,6 +9398,20 @@ class StrategyConnectionService {
|
|
|
9315
9398
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9316
9399
|
return await strategy.getPositionPnlPercent(symbol, currentPrice);
|
|
9317
9400
|
};
|
|
9401
|
+
/**
|
|
9402
|
+
* Returns the unrealized PNL in dollars for the current pending signal at currentPrice.
|
|
9403
|
+
*
|
|
9404
|
+
* Calculated as: pnlPercentage / 100 × totalInvestedCost
|
|
9405
|
+
* Accounts for partial closes, DCA entries, slippage and fees.
|
|
9406
|
+
*
|
|
9407
|
+
* Returns null if no pending signal exists.
|
|
9408
|
+
*
|
|
9409
|
+
* @param backtest - Whether running in backtest mode
|
|
9410
|
+
* @param symbol - Trading pair symbol
|
|
9411
|
+
* @param currentPrice - Current market price
|
|
9412
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9413
|
+
* @returns Promise resolving to pnl in dollars or null
|
|
9414
|
+
*/
|
|
9318
9415
|
this.getPositionPnlCost = async (backtest, symbol, currentPrice, context) => {
|
|
9319
9416
|
this.loggerService.log("strategyConnectionService getPositionPnlCost", {
|
|
9320
9417
|
symbol,
|
|
@@ -9325,6 +9422,27 @@ class StrategyConnectionService {
|
|
|
9325
9422
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9326
9423
|
return await strategy.getPositionPnlCost(symbol, currentPrice);
|
|
9327
9424
|
};
|
|
9425
|
+
/**
|
|
9426
|
+
* Returns the list of DCA entry prices for the current pending signal.
|
|
9427
|
+
*
|
|
9428
|
+
* The first element is always the original priceOpen (initial entry).
|
|
9429
|
+
* Each subsequent element is a price added by commitAverageBuy().
|
|
9430
|
+
*
|
|
9431
|
+
* Returns null if no pending signal exists.
|
|
9432
|
+
* Returns a single-element array [priceOpen] if no DCA entries were made.
|
|
9433
|
+
*
|
|
9434
|
+
* @param backtest - Whether running in backtest mode
|
|
9435
|
+
* @param symbol - Trading pair symbol
|
|
9436
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9437
|
+
* @returns Promise resolving to array of entry prices or null
|
|
9438
|
+
*
|
|
9439
|
+
* @example
|
|
9440
|
+
* ```typescript
|
|
9441
|
+
* // No DCA: [43000]
|
|
9442
|
+
* // One DCA: [43000, 42000]
|
|
9443
|
+
* // Two DCA: [43000, 42000, 41500]
|
|
9444
|
+
* ```
|
|
9445
|
+
*/
|
|
9328
9446
|
this.getPositionLevels = async (backtest, symbol, context) => {
|
|
9329
9447
|
this.loggerService.log("strategyConnectionService getPositionLevels", {
|
|
9330
9448
|
symbol,
|
|
@@ -9334,6 +9452,20 @@ class StrategyConnectionService {
|
|
|
9334
9452
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9335
9453
|
return await strategy.getPositionLevels(symbol);
|
|
9336
9454
|
};
|
|
9455
|
+
/**
|
|
9456
|
+
* Returns the list of partial closes for the current pending signal.
|
|
9457
|
+
*
|
|
9458
|
+
* Each entry records a partial profit or loss close event with its type,
|
|
9459
|
+
* percent closed, price at close, cost basis snapshot, and entry count at close.
|
|
9460
|
+
*
|
|
9461
|
+
* Returns null if no pending signal exists.
|
|
9462
|
+
* Returns an empty array if no partial closes have been executed.
|
|
9463
|
+
*
|
|
9464
|
+
* @param backtest - Whether running in backtest mode
|
|
9465
|
+
* @param symbol - Trading pair symbol
|
|
9466
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9467
|
+
* @returns Promise resolving to array of partial close records or null
|
|
9468
|
+
*/
|
|
9337
9469
|
this.getPositionPartials = async (backtest, symbol, context) => {
|
|
9338
9470
|
this.loggerService.log("strategyConnectionService getPositionPartials", {
|
|
9339
9471
|
symbol,
|
|
@@ -9343,6 +9475,27 @@ class StrategyConnectionService {
|
|
|
9343
9475
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9344
9476
|
return await strategy.getPositionPartials(symbol);
|
|
9345
9477
|
};
|
|
9478
|
+
/**
|
|
9479
|
+
* Returns the list of DCA entry prices and costs for the current pending signal.
|
|
9480
|
+
*
|
|
9481
|
+
* Each entry records the price and cost of a single position entry.
|
|
9482
|
+
* The first element is always the original priceOpen (initial entry).
|
|
9483
|
+
* Each subsequent element is an entry added by averageBuy().
|
|
9484
|
+
*
|
|
9485
|
+
* Returns null if no pending signal exists.
|
|
9486
|
+
* Returns a single-element array [{ price: priceOpen, cost }] if no DCA entries were made.
|
|
9487
|
+
*
|
|
9488
|
+
* @param backtest - Whether running in backtest mode
|
|
9489
|
+
* @param symbol - Trading pair symbol
|
|
9490
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9491
|
+
* @returns Promise resolving to array of entry records or null
|
|
9492
|
+
*
|
|
9493
|
+
* @example
|
|
9494
|
+
* ```typescript
|
|
9495
|
+
* // No DCA: [{ price: 43000, cost: 100 }]
|
|
9496
|
+
* // One DCA: [{ price: 43000, cost: 100 }, { price: 42000, cost: 100 }]
|
|
9497
|
+
* ```
|
|
9498
|
+
*/
|
|
9346
9499
|
this.getPositionEntries = async (backtest, symbol, context) => {
|
|
9347
9500
|
this.loggerService.log("strategyConnectionService getPositionEntries", {
|
|
9348
9501
|
symbol,
|
|
@@ -9350,7 +9503,8 @@ class StrategyConnectionService {
|
|
|
9350
9503
|
backtest,
|
|
9351
9504
|
});
|
|
9352
9505
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9353
|
-
|
|
9506
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
9507
|
+
return await strategy.getPositionEntries(symbol, timestamp);
|
|
9354
9508
|
};
|
|
9355
9509
|
/**
|
|
9356
9510
|
* Retrieves the currently active scheduled signal for the strategy.
|
|
@@ -9452,6 +9606,10 @@ class StrategyConnectionService {
|
|
|
9452
9606
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9453
9607
|
await strategy.waitForInit();
|
|
9454
9608
|
const tick = await strategy.tick(symbol, context.strategyName);
|
|
9609
|
+
{
|
|
9610
|
+
this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
|
|
9611
|
+
this.timeMetaService.next(symbol, tick.createdAt, context, backtest);
|
|
9612
|
+
}
|
|
9455
9613
|
{
|
|
9456
9614
|
await CALL_SIGNAL_EMIT_FN(this, tick, context, backtest, symbol);
|
|
9457
9615
|
}
|
|
@@ -9558,7 +9716,7 @@ class StrategyConnectionService {
|
|
|
9558
9716
|
}
|
|
9559
9717
|
return;
|
|
9560
9718
|
}
|
|
9561
|
-
const key = CREATE_KEY_FN$
|
|
9719
|
+
const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
9562
9720
|
if (!this.getStrategy.has(key)) {
|
|
9563
9721
|
return;
|
|
9564
9722
|
}
|
|
@@ -9627,9 +9785,9 @@ class StrategyConnectionService {
|
|
|
9627
9785
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9628
9786
|
* @returns Promise<boolean> - true if `partialProfit` would execute, false otherwise
|
|
9629
9787
|
*/
|
|
9630
|
-
this.validatePartialProfit = (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9788
|
+
this.validatePartialProfit = async (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9631
9789
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9632
|
-
return
|
|
9790
|
+
return await strategy.validatePartialProfit(symbol, percentToClose, currentPrice);
|
|
9633
9791
|
};
|
|
9634
9792
|
/**
|
|
9635
9793
|
* Executes partial close at profit level (moving toward TP).
|
|
@@ -9670,7 +9828,8 @@ class StrategyConnectionService {
|
|
|
9670
9828
|
backtest,
|
|
9671
9829
|
});
|
|
9672
9830
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9673
|
-
|
|
9831
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
9832
|
+
return await strategy.partialProfit(symbol, percentToClose, currentPrice, backtest, timestamp);
|
|
9674
9833
|
};
|
|
9675
9834
|
/**
|
|
9676
9835
|
* Checks whether `partialLoss` would succeed without executing it.
|
|
@@ -9683,9 +9842,9 @@ class StrategyConnectionService {
|
|
|
9683
9842
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9684
9843
|
* @returns Promise<boolean> - true if `partialLoss` would execute, false otherwise
|
|
9685
9844
|
*/
|
|
9686
|
-
this.validatePartialLoss = (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9845
|
+
this.validatePartialLoss = async (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9687
9846
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9688
|
-
return
|
|
9847
|
+
return await strategy.validatePartialLoss(symbol, percentToClose, currentPrice);
|
|
9689
9848
|
};
|
|
9690
9849
|
/**
|
|
9691
9850
|
* Executes partial close at loss level (moving toward SL).
|
|
@@ -9726,7 +9885,8 @@ class StrategyConnectionService {
|
|
|
9726
9885
|
backtest,
|
|
9727
9886
|
});
|
|
9728
9887
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9729
|
-
|
|
9888
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
9889
|
+
return await strategy.partialLoss(symbol, percentToClose, currentPrice, backtest, timestamp);
|
|
9730
9890
|
};
|
|
9731
9891
|
/**
|
|
9732
9892
|
* Checks whether `trailingStop` would succeed without executing it.
|
|
@@ -9739,9 +9899,9 @@ class StrategyConnectionService {
|
|
|
9739
9899
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9740
9900
|
* @returns Promise<boolean> - true if `trailingStop` would execute, false otherwise
|
|
9741
9901
|
*/
|
|
9742
|
-
this.validateTrailingStop = (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9902
|
+
this.validateTrailingStop = async (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9743
9903
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9744
|
-
return
|
|
9904
|
+
return await strategy.validateTrailingStop(symbol, percentShift, currentPrice);
|
|
9745
9905
|
};
|
|
9746
9906
|
/**
|
|
9747
9907
|
* Adjusts the trailing stop-loss distance for an active pending signal.
|
|
@@ -9793,9 +9953,9 @@ class StrategyConnectionService {
|
|
|
9793
9953
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9794
9954
|
* @returns Promise<boolean> - true if `trailingTake` would execute, false otherwise
|
|
9795
9955
|
*/
|
|
9796
|
-
this.validateTrailingTake = (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9956
|
+
this.validateTrailingTake = async (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9797
9957
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9798
|
-
return
|
|
9958
|
+
return await strategy.validateTrailingTake(symbol, percentShift, currentPrice);
|
|
9799
9959
|
};
|
|
9800
9960
|
/**
|
|
9801
9961
|
* Adjusts the trailing take-profit distance for an active pending signal.
|
|
@@ -9846,9 +10006,9 @@ class StrategyConnectionService {
|
|
|
9846
10006
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9847
10007
|
* @returns Promise<boolean> - true if `breakeven` would execute, false otherwise
|
|
9848
10008
|
*/
|
|
9849
|
-
this.validateBreakeven = (backtest, symbol, currentPrice, context) => {
|
|
10009
|
+
this.validateBreakeven = async (backtest, symbol, currentPrice, context) => {
|
|
9850
10010
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9851
|
-
return
|
|
10011
|
+
return await strategy.validateBreakeven(symbol, currentPrice);
|
|
9852
10012
|
};
|
|
9853
10013
|
/**
|
|
9854
10014
|
* Delegates to ClientStrategy.breakeven() with current execution context.
|
|
@@ -9925,9 +10085,9 @@ class StrategyConnectionService {
|
|
|
9925
10085
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9926
10086
|
* @returns Promise<boolean> - true if `averageBuy` would execute, false otherwise
|
|
9927
10087
|
*/
|
|
9928
|
-
this.validateAverageBuy = (backtest, symbol, currentPrice, context) => {
|
|
10088
|
+
this.validateAverageBuy = async (backtest, symbol, currentPrice, context) => {
|
|
9929
10089
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9930
|
-
return
|
|
10090
|
+
return await strategy.validateAverageBuy(symbol, currentPrice);
|
|
9931
10091
|
};
|
|
9932
10092
|
/**
|
|
9933
10093
|
* Adds a new DCA entry to the active pending signal.
|
|
@@ -9948,7 +10108,8 @@ class StrategyConnectionService {
|
|
|
9948
10108
|
backtest,
|
|
9949
10109
|
});
|
|
9950
10110
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9951
|
-
|
|
10111
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
10112
|
+
return await strategy.averageBuy(symbol, currentPrice, backtest, timestamp, cost);
|
|
9952
10113
|
};
|
|
9953
10114
|
}
|
|
9954
10115
|
}
|
|
@@ -10689,7 +10850,7 @@ class ClientRisk {
|
|
|
10689
10850
|
* @param backtest - Whether running in backtest mode
|
|
10690
10851
|
* @returns Unique string key for memoization
|
|
10691
10852
|
*/
|
|
10692
|
-
const CREATE_KEY_FN$
|
|
10853
|
+
const CREATE_KEY_FN$n = (riskName, exchangeName, frameName, backtest) => {
|
|
10693
10854
|
const parts = [riskName, exchangeName];
|
|
10694
10855
|
if (frameName)
|
|
10695
10856
|
parts.push(frameName);
|
|
@@ -10788,7 +10949,7 @@ class RiskConnectionService {
|
|
|
10788
10949
|
* @param backtest - True if backtest mode, false if live mode
|
|
10789
10950
|
* @returns Configured ClientRisk instance
|
|
10790
10951
|
*/
|
|
10791
|
-
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
10952
|
+
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$n(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
|
|
10792
10953
|
const schema = this.riskSchemaService.get(riskName);
|
|
10793
10954
|
return new ClientRisk({
|
|
10794
10955
|
...schema,
|
|
@@ -10856,7 +11017,7 @@ class RiskConnectionService {
|
|
|
10856
11017
|
payload,
|
|
10857
11018
|
});
|
|
10858
11019
|
if (payload) {
|
|
10859
|
-
const key = CREATE_KEY_FN$
|
|
11020
|
+
const key = CREATE_KEY_FN$n(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
10860
11021
|
this.getRisk.clear(key);
|
|
10861
11022
|
}
|
|
10862
11023
|
else {
|
|
@@ -12323,7 +12484,7 @@ class ClientAction {
|
|
|
12323
12484
|
* @param backtest - Whether running in backtest mode
|
|
12324
12485
|
* @returns Unique string key for memoization
|
|
12325
12486
|
*/
|
|
12326
|
-
const CREATE_KEY_FN$
|
|
12487
|
+
const CREATE_KEY_FN$m = (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
12327
12488
|
const parts = [actionName, strategyName, exchangeName];
|
|
12328
12489
|
if (frameName)
|
|
12329
12490
|
parts.push(frameName);
|
|
@@ -12374,7 +12535,7 @@ class ActionConnectionService {
|
|
|
12374
12535
|
* @param backtest - True if backtest mode, false if live mode
|
|
12375
12536
|
* @returns Configured ClientAction instance
|
|
12376
12537
|
*/
|
|
12377
|
-
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
12538
|
+
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
12378
12539
|
const schema = this.actionSchemaService.get(actionName);
|
|
12379
12540
|
return new ClientAction({
|
|
12380
12541
|
...schema,
|
|
@@ -12584,7 +12745,7 @@ class ActionConnectionService {
|
|
|
12584
12745
|
await Promise.all(actions.map(async (action) => await action.dispose()));
|
|
12585
12746
|
return;
|
|
12586
12747
|
}
|
|
12587
|
-
const key = CREATE_KEY_FN$
|
|
12748
|
+
const key = CREATE_KEY_FN$m(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
12588
12749
|
if (!this.getAction.has(key)) {
|
|
12589
12750
|
return;
|
|
12590
12751
|
}
|
|
@@ -12602,7 +12763,7 @@ const METHOD_NAME_VALIDATE$2 = "exchangeCoreService validate";
|
|
|
12602
12763
|
* @param exchangeName - Exchange name
|
|
12603
12764
|
* @returns Unique string key for memoization
|
|
12604
12765
|
*/
|
|
12605
|
-
const CREATE_KEY_FN$
|
|
12766
|
+
const CREATE_KEY_FN$l = (exchangeName) => {
|
|
12606
12767
|
return exchangeName;
|
|
12607
12768
|
};
|
|
12608
12769
|
/**
|
|
@@ -12626,7 +12787,7 @@ class ExchangeCoreService {
|
|
|
12626
12787
|
* @param exchangeName - Name of the exchange to validate
|
|
12627
12788
|
* @returns Promise that resolves when validation is complete
|
|
12628
12789
|
*/
|
|
12629
|
-
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$
|
|
12790
|
+
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$l(exchangeName), async (exchangeName) => {
|
|
12630
12791
|
this.loggerService.log(METHOD_NAME_VALIDATE$2, {
|
|
12631
12792
|
exchangeName,
|
|
12632
12793
|
});
|
|
@@ -12878,7 +13039,7 @@ const METHOD_NAME_VALIDATE$1 = "strategyCoreService validate";
|
|
|
12878
13039
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
12879
13040
|
* @returns Unique string key for memoization
|
|
12880
13041
|
*/
|
|
12881
|
-
const CREATE_KEY_FN$
|
|
13042
|
+
const CREATE_KEY_FN$k = (context) => {
|
|
12882
13043
|
const parts = [context.strategyName, context.exchangeName];
|
|
12883
13044
|
if (context.frameName)
|
|
12884
13045
|
parts.push(context.frameName);
|
|
@@ -12910,7 +13071,7 @@ class StrategyCoreService {
|
|
|
12910
13071
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
12911
13072
|
* @returns Promise that resolves when validation is complete
|
|
12912
13073
|
*/
|
|
12913
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
13074
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$k(context), async (context) => {
|
|
12914
13075
|
this.loggerService.log(METHOD_NAME_VALIDATE$1, {
|
|
12915
13076
|
context,
|
|
12916
13077
|
});
|
|
@@ -13745,7 +13906,7 @@ class SizingGlobalService {
|
|
|
13745
13906
|
* @param context - Context with riskName, exchangeName, frameName
|
|
13746
13907
|
* @returns Unique string key for memoization
|
|
13747
13908
|
*/
|
|
13748
|
-
const CREATE_KEY_FN$
|
|
13909
|
+
const CREATE_KEY_FN$j = (context) => {
|
|
13749
13910
|
const parts = [context.riskName, context.exchangeName];
|
|
13750
13911
|
if (context.frameName)
|
|
13751
13912
|
parts.push(context.frameName);
|
|
@@ -13771,7 +13932,7 @@ class RiskGlobalService {
|
|
|
13771
13932
|
* @param payload - Payload with riskName, exchangeName and frameName
|
|
13772
13933
|
* @returns Promise that resolves when validation is complete
|
|
13773
13934
|
*/
|
|
13774
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
13935
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$j(context), async (context) => {
|
|
13775
13936
|
this.loggerService.log("riskGlobalService validate", {
|
|
13776
13937
|
context,
|
|
13777
13938
|
});
|
|
@@ -13849,7 +14010,7 @@ const METHOD_NAME_VALIDATE = "actionCoreService validate";
|
|
|
13849
14010
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
13850
14011
|
* @returns Unique string key for memoization
|
|
13851
14012
|
*/
|
|
13852
|
-
const CREATE_KEY_FN$
|
|
14013
|
+
const CREATE_KEY_FN$i = (context) => {
|
|
13853
14014
|
const parts = [context.strategyName, context.exchangeName];
|
|
13854
14015
|
if (context.frameName)
|
|
13855
14016
|
parts.push(context.frameName);
|
|
@@ -13893,7 +14054,7 @@ class ActionCoreService {
|
|
|
13893
14054
|
* @param context - Strategy execution context with strategyName, exchangeName and frameName
|
|
13894
14055
|
* @returns Promise that resolves when all validations complete
|
|
13895
14056
|
*/
|
|
13896
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
14057
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$i(context), async (context) => {
|
|
13897
14058
|
this.loggerService.log(METHOD_NAME_VALIDATE, {
|
|
13898
14059
|
context,
|
|
13899
14060
|
});
|
|
@@ -18428,7 +18589,7 @@ const Markdown = new MarkdownAdapter();
|
|
|
18428
18589
|
* @param backtest - Whether running in backtest mode
|
|
18429
18590
|
* @returns Unique string key for memoization
|
|
18430
18591
|
*/
|
|
18431
|
-
const CREATE_KEY_FN$
|
|
18592
|
+
const CREATE_KEY_FN$h = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
18432
18593
|
const parts = [symbol, strategyName, exchangeName];
|
|
18433
18594
|
if (frameName)
|
|
18434
18595
|
parts.push(frameName);
|
|
@@ -18667,7 +18828,7 @@ class BacktestMarkdownService {
|
|
|
18667
18828
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
18668
18829
|
* Each combination gets its own isolated storage instance.
|
|
18669
18830
|
*/
|
|
18670
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
18831
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$h(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$8(symbol, strategyName, exchangeName, frameName));
|
|
18671
18832
|
/**
|
|
18672
18833
|
* Processes tick events and accumulates closed signals.
|
|
18673
18834
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -18824,7 +18985,7 @@ class BacktestMarkdownService {
|
|
|
18824
18985
|
payload,
|
|
18825
18986
|
});
|
|
18826
18987
|
if (payload) {
|
|
18827
|
-
const key = CREATE_KEY_FN$
|
|
18988
|
+
const key = CREATE_KEY_FN$h(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
18828
18989
|
this.getStorage.clear(key);
|
|
18829
18990
|
}
|
|
18830
18991
|
else {
|
|
@@ -18886,7 +19047,7 @@ class BacktestMarkdownService {
|
|
|
18886
19047
|
* @param backtest - Whether running in backtest mode
|
|
18887
19048
|
* @returns Unique string key for memoization
|
|
18888
19049
|
*/
|
|
18889
|
-
const CREATE_KEY_FN$
|
|
19050
|
+
const CREATE_KEY_FN$g = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
18890
19051
|
const parts = [symbol, strategyName, exchangeName];
|
|
18891
19052
|
if (frameName)
|
|
18892
19053
|
parts.push(frameName);
|
|
@@ -19369,7 +19530,7 @@ class LiveMarkdownService {
|
|
|
19369
19530
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
19370
19531
|
* Each combination gets its own isolated storage instance.
|
|
19371
19532
|
*/
|
|
19372
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
19533
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$g(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$7(symbol, strategyName, exchangeName, frameName));
|
|
19373
19534
|
/**
|
|
19374
19535
|
* Subscribes to live signal emitter to receive tick events.
|
|
19375
19536
|
* Protected against multiple subscriptions.
|
|
@@ -19587,7 +19748,7 @@ class LiveMarkdownService {
|
|
|
19587
19748
|
payload,
|
|
19588
19749
|
});
|
|
19589
19750
|
if (payload) {
|
|
19590
|
-
const key = CREATE_KEY_FN$
|
|
19751
|
+
const key = CREATE_KEY_FN$g(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
19591
19752
|
this.getStorage.clear(key);
|
|
19592
19753
|
}
|
|
19593
19754
|
else {
|
|
@@ -19607,7 +19768,7 @@ class LiveMarkdownService {
|
|
|
19607
19768
|
* @param backtest - Whether running in backtest mode
|
|
19608
19769
|
* @returns Unique string key for memoization
|
|
19609
19770
|
*/
|
|
19610
|
-
const CREATE_KEY_FN$
|
|
19771
|
+
const CREATE_KEY_FN$f = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
19611
19772
|
const parts = [symbol, strategyName, exchangeName];
|
|
19612
19773
|
if (frameName)
|
|
19613
19774
|
parts.push(frameName);
|
|
@@ -19898,7 +20059,7 @@ class ScheduleMarkdownService {
|
|
|
19898
20059
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
19899
20060
|
* Each combination gets its own isolated storage instance.
|
|
19900
20061
|
*/
|
|
19901
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
20062
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$f(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$6(symbol, strategyName, exchangeName, frameName));
|
|
19902
20063
|
/**
|
|
19903
20064
|
* Subscribes to signal emitter to receive scheduled signal events.
|
|
19904
20065
|
* Protected against multiple subscriptions.
|
|
@@ -20101,7 +20262,7 @@ class ScheduleMarkdownService {
|
|
|
20101
20262
|
payload,
|
|
20102
20263
|
});
|
|
20103
20264
|
if (payload) {
|
|
20104
|
-
const key = CREATE_KEY_FN$
|
|
20265
|
+
const key = CREATE_KEY_FN$f(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
20105
20266
|
this.getStorage.clear(key);
|
|
20106
20267
|
}
|
|
20107
20268
|
else {
|
|
@@ -20121,7 +20282,7 @@ class ScheduleMarkdownService {
|
|
|
20121
20282
|
* @param backtest - Whether running in backtest mode
|
|
20122
20283
|
* @returns Unique string key for memoization
|
|
20123
20284
|
*/
|
|
20124
|
-
const CREATE_KEY_FN$
|
|
20285
|
+
const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
20125
20286
|
const parts = [symbol, strategyName, exchangeName];
|
|
20126
20287
|
if (frameName)
|
|
20127
20288
|
parts.push(frameName);
|
|
@@ -20369,7 +20530,7 @@ class PerformanceMarkdownService {
|
|
|
20369
20530
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
20370
20531
|
* Each combination gets its own isolated storage instance.
|
|
20371
20532
|
*/
|
|
20372
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
20533
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$e(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new PerformanceStorage(symbol, strategyName, exchangeName, frameName));
|
|
20373
20534
|
/**
|
|
20374
20535
|
* Subscribes to performance emitter to receive performance events.
|
|
20375
20536
|
* Protected against multiple subscriptions.
|
|
@@ -20536,7 +20697,7 @@ class PerformanceMarkdownService {
|
|
|
20536
20697
|
payload,
|
|
20537
20698
|
});
|
|
20538
20699
|
if (payload) {
|
|
20539
|
-
const key = CREATE_KEY_FN$
|
|
20700
|
+
const key = CREATE_KEY_FN$e(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
20540
20701
|
this.getStorage.clear(key);
|
|
20541
20702
|
}
|
|
20542
20703
|
else {
|
|
@@ -21006,7 +21167,7 @@ class WalkerMarkdownService {
|
|
|
21006
21167
|
* @param backtest - Whether running in backtest mode
|
|
21007
21168
|
* @returns Unique string key for memoization
|
|
21008
21169
|
*/
|
|
21009
|
-
const CREATE_KEY_FN$
|
|
21170
|
+
const CREATE_KEY_FN$d = (exchangeName, frameName, backtest) => {
|
|
21010
21171
|
const parts = [exchangeName];
|
|
21011
21172
|
if (frameName)
|
|
21012
21173
|
parts.push(frameName);
|
|
@@ -21373,7 +21534,7 @@ class HeatMarkdownService {
|
|
|
21373
21534
|
* Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
|
|
21374
21535
|
* Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
|
|
21375
21536
|
*/
|
|
21376
|
-
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
21537
|
+
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
|
|
21377
21538
|
/**
|
|
21378
21539
|
* Subscribes to signal emitter to receive tick events.
|
|
21379
21540
|
* Protected against multiple subscriptions.
|
|
@@ -21568,7 +21729,7 @@ class HeatMarkdownService {
|
|
|
21568
21729
|
payload,
|
|
21569
21730
|
});
|
|
21570
21731
|
if (payload) {
|
|
21571
|
-
const key = CREATE_KEY_FN$
|
|
21732
|
+
const key = CREATE_KEY_FN$d(payload.exchangeName, payload.frameName, payload.backtest);
|
|
21572
21733
|
this.getStorage.clear(key);
|
|
21573
21734
|
}
|
|
21574
21735
|
else {
|
|
@@ -22599,7 +22760,7 @@ class ClientPartial {
|
|
|
22599
22760
|
* @param backtest - Whether running in backtest mode
|
|
22600
22761
|
* @returns Unique string key for memoization
|
|
22601
22762
|
*/
|
|
22602
|
-
const CREATE_KEY_FN$
|
|
22763
|
+
const CREATE_KEY_FN$c = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
22603
22764
|
/**
|
|
22604
22765
|
* Creates a callback function for emitting profit events to partialProfitSubject.
|
|
22605
22766
|
*
|
|
@@ -22721,7 +22882,7 @@ class PartialConnectionService {
|
|
|
22721
22882
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
22722
22883
|
* Value: ClientPartial instance with logger and event emitters
|
|
22723
22884
|
*/
|
|
22724
|
-
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
22885
|
+
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$c(signalId, backtest), (signalId, backtest) => {
|
|
22725
22886
|
return new ClientPartial({
|
|
22726
22887
|
signalId,
|
|
22727
22888
|
logger: this.loggerService,
|
|
@@ -22811,7 +22972,7 @@ class PartialConnectionService {
|
|
|
22811
22972
|
const partial = this.getPartial(data.id, backtest);
|
|
22812
22973
|
await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
22813
22974
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
22814
|
-
const key = CREATE_KEY_FN$
|
|
22975
|
+
const key = CREATE_KEY_FN$c(data.id, backtest);
|
|
22815
22976
|
this.getPartial.clear(key);
|
|
22816
22977
|
};
|
|
22817
22978
|
}
|
|
@@ -22827,7 +22988,7 @@ class PartialConnectionService {
|
|
|
22827
22988
|
* @param backtest - Whether running in backtest mode
|
|
22828
22989
|
* @returns Unique string key for memoization
|
|
22829
22990
|
*/
|
|
22830
|
-
const CREATE_KEY_FN$
|
|
22991
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
22831
22992
|
const parts = [symbol, strategyName, exchangeName];
|
|
22832
22993
|
if (frameName)
|
|
22833
22994
|
parts.push(frameName);
|
|
@@ -23052,7 +23213,7 @@ class PartialMarkdownService {
|
|
|
23052
23213
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
23053
23214
|
* Each combination gets its own isolated storage instance.
|
|
23054
23215
|
*/
|
|
23055
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
23216
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
|
|
23056
23217
|
/**
|
|
23057
23218
|
* Subscribes to partial profit/loss signal emitters to receive events.
|
|
23058
23219
|
* Protected against multiple subscriptions.
|
|
@@ -23262,7 +23423,7 @@ class PartialMarkdownService {
|
|
|
23262
23423
|
payload,
|
|
23263
23424
|
});
|
|
23264
23425
|
if (payload) {
|
|
23265
|
-
const key = CREATE_KEY_FN$
|
|
23426
|
+
const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23266
23427
|
this.getStorage.clear(key);
|
|
23267
23428
|
}
|
|
23268
23429
|
else {
|
|
@@ -23278,7 +23439,7 @@ class PartialMarkdownService {
|
|
|
23278
23439
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
23279
23440
|
* @returns Unique string key for memoization
|
|
23280
23441
|
*/
|
|
23281
|
-
const CREATE_KEY_FN$
|
|
23442
|
+
const CREATE_KEY_FN$a = (context) => {
|
|
23282
23443
|
const parts = [context.strategyName, context.exchangeName];
|
|
23283
23444
|
if (context.frameName)
|
|
23284
23445
|
parts.push(context.frameName);
|
|
@@ -23352,7 +23513,7 @@ class PartialGlobalService {
|
|
|
23352
23513
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
23353
23514
|
* @param methodName - Name of the calling method for error tracking
|
|
23354
23515
|
*/
|
|
23355
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
23516
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$a(context), (context, methodName) => {
|
|
23356
23517
|
this.loggerService.log("partialGlobalService validate", {
|
|
23357
23518
|
context,
|
|
23358
23519
|
methodName,
|
|
@@ -23807,7 +23968,7 @@ class ClientBreakeven {
|
|
|
23807
23968
|
* @param backtest - Whether running in backtest mode
|
|
23808
23969
|
* @returns Unique string key for memoization
|
|
23809
23970
|
*/
|
|
23810
|
-
const CREATE_KEY_FN$
|
|
23971
|
+
const CREATE_KEY_FN$9 = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
23811
23972
|
/**
|
|
23812
23973
|
* Creates a callback function for emitting breakeven events to breakevenSubject.
|
|
23813
23974
|
*
|
|
@@ -23893,7 +24054,7 @@ class BreakevenConnectionService {
|
|
|
23893
24054
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
23894
24055
|
* Value: ClientBreakeven instance with logger and event emitter
|
|
23895
24056
|
*/
|
|
23896
|
-
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
24057
|
+
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$9(signalId, backtest), (signalId, backtest) => {
|
|
23897
24058
|
return new ClientBreakeven({
|
|
23898
24059
|
signalId,
|
|
23899
24060
|
logger: this.loggerService,
|
|
@@ -23954,7 +24115,7 @@ class BreakevenConnectionService {
|
|
|
23954
24115
|
const breakeven = this.getBreakeven(data.id, backtest);
|
|
23955
24116
|
await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
23956
24117
|
await breakeven.clear(symbol, data, priceClose, backtest);
|
|
23957
|
-
const key = CREATE_KEY_FN$
|
|
24118
|
+
const key = CREATE_KEY_FN$9(data.id, backtest);
|
|
23958
24119
|
this.getBreakeven.clear(key);
|
|
23959
24120
|
};
|
|
23960
24121
|
}
|
|
@@ -23970,7 +24131,7 @@ class BreakevenConnectionService {
|
|
|
23970
24131
|
* @param backtest - Whether running in backtest mode
|
|
23971
24132
|
* @returns Unique string key for memoization
|
|
23972
24133
|
*/
|
|
23973
|
-
const CREATE_KEY_FN$
|
|
24134
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
23974
24135
|
const parts = [symbol, strategyName, exchangeName];
|
|
23975
24136
|
if (frameName)
|
|
23976
24137
|
parts.push(frameName);
|
|
@@ -24147,7 +24308,7 @@ class BreakevenMarkdownService {
|
|
|
24147
24308
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
24148
24309
|
* Each combination gets its own isolated storage instance.
|
|
24149
24310
|
*/
|
|
24150
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24311
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$3(symbol, strategyName, exchangeName, frameName));
|
|
24151
24312
|
/**
|
|
24152
24313
|
* Subscribes to breakeven signal emitter to receive events.
|
|
24153
24314
|
* Protected against multiple subscriptions.
|
|
@@ -24336,7 +24497,7 @@ class BreakevenMarkdownService {
|
|
|
24336
24497
|
payload,
|
|
24337
24498
|
});
|
|
24338
24499
|
if (payload) {
|
|
24339
|
-
const key = CREATE_KEY_FN$
|
|
24500
|
+
const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
24340
24501
|
this.getStorage.clear(key);
|
|
24341
24502
|
}
|
|
24342
24503
|
else {
|
|
@@ -24352,7 +24513,7 @@ class BreakevenMarkdownService {
|
|
|
24352
24513
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
24353
24514
|
* @returns Unique string key for memoization
|
|
24354
24515
|
*/
|
|
24355
|
-
const CREATE_KEY_FN$
|
|
24516
|
+
const CREATE_KEY_FN$7 = (context) => {
|
|
24356
24517
|
const parts = [context.strategyName, context.exchangeName];
|
|
24357
24518
|
if (context.frameName)
|
|
24358
24519
|
parts.push(context.frameName);
|
|
@@ -24426,7 +24587,7 @@ class BreakevenGlobalService {
|
|
|
24426
24587
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
24427
24588
|
* @param methodName - Name of the calling method for error tracking
|
|
24428
24589
|
*/
|
|
24429
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
24590
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$7(context), (context, methodName) => {
|
|
24430
24591
|
this.loggerService.log("breakevenGlobalService validate", {
|
|
24431
24592
|
context,
|
|
24432
24593
|
methodName,
|
|
@@ -24646,7 +24807,7 @@ class ConfigValidationService {
|
|
|
24646
24807
|
* @param backtest - Whether running in backtest mode
|
|
24647
24808
|
* @returns Unique string key for memoization
|
|
24648
24809
|
*/
|
|
24649
|
-
const CREATE_KEY_FN$
|
|
24810
|
+
const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
24650
24811
|
const parts = [symbol, strategyName, exchangeName];
|
|
24651
24812
|
if (frameName)
|
|
24652
24813
|
parts.push(frameName);
|
|
@@ -24815,7 +24976,7 @@ class RiskMarkdownService {
|
|
|
24815
24976
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
24816
24977
|
* Each combination gets its own isolated storage instance.
|
|
24817
24978
|
*/
|
|
24818
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24979
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$6(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName));
|
|
24819
24980
|
/**
|
|
24820
24981
|
* Subscribes to risk rejection emitter to receive rejection events.
|
|
24821
24982
|
* Protected against multiple subscriptions.
|
|
@@ -25004,7 +25165,7 @@ class RiskMarkdownService {
|
|
|
25004
25165
|
payload,
|
|
25005
25166
|
});
|
|
25006
25167
|
if (payload) {
|
|
25007
|
-
const key = CREATE_KEY_FN$
|
|
25168
|
+
const key = CREATE_KEY_FN$6(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
25008
25169
|
this.getStorage.clear(key);
|
|
25009
25170
|
}
|
|
25010
25171
|
else {
|
|
@@ -27692,7 +27853,7 @@ class SyncReportService {
|
|
|
27692
27853
|
* @returns Colon-separated key string for memoization
|
|
27693
27854
|
* @internal
|
|
27694
27855
|
*/
|
|
27695
|
-
const CREATE_KEY_FN$
|
|
27856
|
+
const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27696
27857
|
const parts = [symbol, strategyName, exchangeName];
|
|
27697
27858
|
if (frameName)
|
|
27698
27859
|
parts.push(frameName);
|
|
@@ -27940,7 +28101,7 @@ class StrategyMarkdownService {
|
|
|
27940
28101
|
*
|
|
27941
28102
|
* @internal
|
|
27942
28103
|
*/
|
|
27943
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
28104
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$1(symbol, strategyName, exchangeName, frameName));
|
|
27944
28105
|
/**
|
|
27945
28106
|
* Records a cancel-scheduled event when a scheduled signal is cancelled.
|
|
27946
28107
|
*
|
|
@@ -28508,7 +28669,7 @@ class StrategyMarkdownService {
|
|
|
28508
28669
|
this.clear = async (payload) => {
|
|
28509
28670
|
this.loggerService.log("strategyMarkdownService clear", { payload });
|
|
28510
28671
|
if (payload) {
|
|
28511
|
-
const key = CREATE_KEY_FN$
|
|
28672
|
+
const key = CREATE_KEY_FN$5(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
28512
28673
|
this.getStorage.clear(key);
|
|
28513
28674
|
}
|
|
28514
28675
|
else {
|
|
@@ -28616,7 +28777,7 @@ class StrategyMarkdownService {
|
|
|
28616
28777
|
* Creates a unique key for memoizing ReportStorage instances.
|
|
28617
28778
|
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
28618
28779
|
*/
|
|
28619
|
-
const CREATE_KEY_FN$
|
|
28780
|
+
const CREATE_KEY_FN$4 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
28620
28781
|
const parts = [symbol, strategyName, exchangeName];
|
|
28621
28782
|
if (frameName)
|
|
28622
28783
|
parts.push(frameName);
|
|
@@ -28748,7 +28909,7 @@ class ReportStorage {
|
|
|
28748
28909
|
class SyncMarkdownService {
|
|
28749
28910
|
constructor() {
|
|
28750
28911
|
this.loggerService = inject(TYPES.loggerService);
|
|
28751
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
28912
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$4(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage(symbol, strategyName, exchangeName, frameName));
|
|
28752
28913
|
this.subscribe = functoolsKit.singleshot(() => {
|
|
28753
28914
|
this.loggerService.log("syncMarkdownService init");
|
|
28754
28915
|
const unsubscribe = syncSubject.subscribe(this.tick);
|
|
@@ -28823,7 +28984,7 @@ class SyncMarkdownService {
|
|
|
28823
28984
|
this.clear = async (payload) => {
|
|
28824
28985
|
this.loggerService.log("syncMarkdownService clear", { payload });
|
|
28825
28986
|
if (payload) {
|
|
28826
|
-
const key = CREATE_KEY_FN$
|
|
28987
|
+
const key = CREATE_KEY_FN$4(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
28827
28988
|
this.getStorage.clear(key);
|
|
28828
28989
|
}
|
|
28829
28990
|
else {
|
|
@@ -28833,6 +28994,275 @@ class SyncMarkdownService {
|
|
|
28833
28994
|
}
|
|
28834
28995
|
}
|
|
28835
28996
|
|
|
28997
|
+
const LISTEN_TIMEOUT$1 = 120000;
|
|
28998
|
+
/**
|
|
28999
|
+
* Creates a unique memoization key for a price stream.
|
|
29000
|
+
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
29001
|
+
*
|
|
29002
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29003
|
+
* @param strategyName - Strategy identifier
|
|
29004
|
+
* @param exchangeName - Exchange identifier
|
|
29005
|
+
* @param frameName - Frame identifier (omitted when empty)
|
|
29006
|
+
* @param backtest - Whether running in backtest mode
|
|
29007
|
+
* @returns Unique string key for memoization
|
|
29008
|
+
*/
|
|
29009
|
+
const CREATE_KEY_FN$3 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
29010
|
+
const parts = [symbol, strategyName, exchangeName];
|
|
29011
|
+
if (frameName)
|
|
29012
|
+
parts.push(frameName);
|
|
29013
|
+
parts.push(backtest ? "backtest" : "live");
|
|
29014
|
+
return parts.join(":");
|
|
29015
|
+
};
|
|
29016
|
+
/**
|
|
29017
|
+
* Service for tracking the latest market price per symbol-strategy-exchange-frame combination.
|
|
29018
|
+
*
|
|
29019
|
+
* Maintains a memoized BehaviorSubject per unique key that is updated on every strategy tick
|
|
29020
|
+
* by StrategyConnectionService. Consumers can synchronously read the last known price or
|
|
29021
|
+
* await the first value if none has arrived yet.
|
|
29022
|
+
*
|
|
29023
|
+
* Primary use case: providing the current price outside of a tick execution context,
|
|
29024
|
+
* e.g., when a command is triggered between ticks.
|
|
29025
|
+
*
|
|
29026
|
+
* Features:
|
|
29027
|
+
* - One BehaviorSubject per (symbol, strategyName, exchangeName, frameName, backtest) key
|
|
29028
|
+
* - Falls back to ExchangeConnectionService.getAveragePrice when called inside an execution context
|
|
29029
|
+
* - Waits up to LISTEN_TIMEOUT ms for the first price if none is cached yet
|
|
29030
|
+
* - clear() disposes the BehaviorSubject for a single key or all keys
|
|
29031
|
+
*
|
|
29032
|
+
* Architecture:
|
|
29033
|
+
* - Registered as singleton in DI container
|
|
29034
|
+
* - Updated by StrategyConnectionService after each tick
|
|
29035
|
+
* - Cleared by Backtest/Live/Walker at strategy start to prevent stale data
|
|
29036
|
+
*
|
|
29037
|
+
* @example
|
|
29038
|
+
* ```typescript
|
|
29039
|
+
* const price = await backtest.priceMetaService.getCurrentPrice("BTCUSDT", context, false);
|
|
29040
|
+
* ```
|
|
29041
|
+
*/
|
|
29042
|
+
class PriceMetaService {
|
|
29043
|
+
constructor() {
|
|
29044
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
29045
|
+
this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
|
|
29046
|
+
/**
|
|
29047
|
+
* Memoized factory for BehaviorSubject streams keyed by (symbol, strategyName, exchangeName, frameName, backtest).
|
|
29048
|
+
*
|
|
29049
|
+
* Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
|
|
29050
|
+
* Instances are cached until clear() is called.
|
|
29051
|
+
*/
|
|
29052
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$3(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
29053
|
+
/**
|
|
29054
|
+
* Returns the current market price for the given symbol and context.
|
|
29055
|
+
*
|
|
29056
|
+
* When called inside an execution context (i.e., during a signal handler or action),
|
|
29057
|
+
* delegates to ExchangeConnectionService.getAveragePrice for the live exchange price.
|
|
29058
|
+
* Otherwise, reads the last value from the cached BehaviorSubject. If no value has
|
|
29059
|
+
* been emitted yet, waits up to LISTEN_TIMEOUT ms for the first tick before throwing.
|
|
29060
|
+
*
|
|
29061
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29062
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29063
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29064
|
+
* @returns Current market price in quote currency
|
|
29065
|
+
* @throws When no price arrives within LISTEN_TIMEOUT ms
|
|
29066
|
+
*/
|
|
29067
|
+
this.getCurrentPrice = async (symbol, context, backtest) => {
|
|
29068
|
+
this.loggerService.log("priceMetaService getCurrentPrice", {
|
|
29069
|
+
symbol,
|
|
29070
|
+
context,
|
|
29071
|
+
backtest,
|
|
29072
|
+
});
|
|
29073
|
+
if (ExecutionContextService.hasContext() &&
|
|
29074
|
+
MethodContextService.hasContext()) {
|
|
29075
|
+
return await this.exchangeConnectionService.getAveragePrice(symbol);
|
|
29076
|
+
}
|
|
29077
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29078
|
+
if (source.data) {
|
|
29079
|
+
return source.data;
|
|
29080
|
+
}
|
|
29081
|
+
console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$3(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
|
|
29082
|
+
const currentPrice = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
|
|
29083
|
+
if (typeof currentPrice === "symbol") {
|
|
29084
|
+
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$3(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
29085
|
+
}
|
|
29086
|
+
return currentPrice;
|
|
29087
|
+
};
|
|
29088
|
+
/**
|
|
29089
|
+
* Pushes a new price value into the BehaviorSubject for the given key.
|
|
29090
|
+
*
|
|
29091
|
+
* Called by StrategyConnectionService after each strategy tick to keep
|
|
29092
|
+
* the cached price up to date.
|
|
29093
|
+
*
|
|
29094
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29095
|
+
* @param currentPrice - The latest price from the tick
|
|
29096
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29097
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29098
|
+
*/
|
|
29099
|
+
this.next = async (symbol, currentPrice, context, backtest) => {
|
|
29100
|
+
this.loggerService.log("priceMetaService next", {
|
|
29101
|
+
symbol,
|
|
29102
|
+
currentPrice,
|
|
29103
|
+
context,
|
|
29104
|
+
backtest,
|
|
29105
|
+
});
|
|
29106
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29107
|
+
source.next(currentPrice);
|
|
29108
|
+
};
|
|
29109
|
+
/**
|
|
29110
|
+
* Disposes cached BehaviorSubject(s) to free memory and prevent stale data.
|
|
29111
|
+
*
|
|
29112
|
+
* When called without arguments, clears all memoized price streams.
|
|
29113
|
+
* When called with a payload, clears only the stream for the specified key.
|
|
29114
|
+
* Should be called at strategy start (Backtest/Live/Walker) to reset state.
|
|
29115
|
+
*
|
|
29116
|
+
* @param payload - Optional key to clear a single stream; omit to clear all
|
|
29117
|
+
*/
|
|
29118
|
+
this.clear = (payload) => {
|
|
29119
|
+
this.loggerService.log("priceMetaService clear", {
|
|
29120
|
+
payload
|
|
29121
|
+
});
|
|
29122
|
+
if (!payload) {
|
|
29123
|
+
this.getSource.clear();
|
|
29124
|
+
return;
|
|
29125
|
+
}
|
|
29126
|
+
const key = CREATE_KEY_FN$3(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
29127
|
+
this.getSource.clear(key);
|
|
29128
|
+
};
|
|
29129
|
+
}
|
|
29130
|
+
}
|
|
29131
|
+
|
|
29132
|
+
const LISTEN_TIMEOUT = 120000;
|
|
29133
|
+
/**
|
|
29134
|
+
* Creates a unique memoization key for a timestamp stream.
|
|
29135
|
+
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
29136
|
+
*
|
|
29137
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29138
|
+
* @param strategyName - Strategy identifier
|
|
29139
|
+
* @param exchangeName - Exchange identifier
|
|
29140
|
+
* @param frameName - Frame identifier (omitted when empty)
|
|
29141
|
+
* @param backtest - Whether running in backtest mode
|
|
29142
|
+
* @returns Unique string key for memoization
|
|
29143
|
+
*/
|
|
29144
|
+
const CREATE_KEY_FN$2 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
29145
|
+
const parts = [symbol, strategyName, exchangeName];
|
|
29146
|
+
if (frameName)
|
|
29147
|
+
parts.push(frameName);
|
|
29148
|
+
parts.push(backtest ? "backtest" : "live");
|
|
29149
|
+
return parts.join(":");
|
|
29150
|
+
};
|
|
29151
|
+
/**
|
|
29152
|
+
* Service for tracking the latest candle timestamp per symbol-strategy-exchange-frame combination.
|
|
29153
|
+
*
|
|
29154
|
+
* Maintains a memoized BehaviorSubject per unique key that is updated on every strategy tick
|
|
29155
|
+
* by StrategyConnectionService. Consumers can synchronously read the last known timestamp or
|
|
29156
|
+
* await the first value if none has arrived yet.
|
|
29157
|
+
*
|
|
29158
|
+
* Primary use case: providing the current candle time outside of a tick execution context,
|
|
29159
|
+
* e.g., when a command is triggered between ticks.
|
|
29160
|
+
*
|
|
29161
|
+
* Features:
|
|
29162
|
+
* - One BehaviorSubject per (symbol, strategyName, exchangeName, frameName, backtest) key
|
|
29163
|
+
* - Falls back to ExecutionContextService.context.when when called inside an execution context
|
|
29164
|
+
* - Waits up to LISTEN_TIMEOUT ms for the first timestamp if none is cached yet
|
|
29165
|
+
* - clear() disposes the BehaviorSubject for a single key or all keys
|
|
29166
|
+
*
|
|
29167
|
+
* Architecture:
|
|
29168
|
+
* - Registered as singleton in DI container
|
|
29169
|
+
* - Updated by StrategyConnectionService after each tick
|
|
29170
|
+
* - Cleared by Backtest/Live/Walker at strategy start to prevent stale data
|
|
29171
|
+
*
|
|
29172
|
+
* @example
|
|
29173
|
+
* ```typescript
|
|
29174
|
+
* const ts = await backtest.timeMetaService.getTimestamp("BTCUSDT", context, false);
|
|
29175
|
+
* ```
|
|
29176
|
+
*/
|
|
29177
|
+
class TimeMetaService {
|
|
29178
|
+
constructor() {
|
|
29179
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
29180
|
+
this.executionContextService = inject(TYPES.executionContextService);
|
|
29181
|
+
/**
|
|
29182
|
+
* Memoized factory for BehaviorSubject streams keyed by (symbol, strategyName, exchangeName, frameName, backtest).
|
|
29183
|
+
*
|
|
29184
|
+
* Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
|
|
29185
|
+
* Instances are cached until clear() is called.
|
|
29186
|
+
*/
|
|
29187
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$2(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
29188
|
+
/**
|
|
29189
|
+
* Returns the current candle timestamp (in milliseconds) for the given symbol and context.
|
|
29190
|
+
*
|
|
29191
|
+
* When called inside an execution context (i.e., during a signal handler or action),
|
|
29192
|
+
* reads the timestamp directly from ExecutionContextService.context.when.
|
|
29193
|
+
* Otherwise, reads the last value from the cached BehaviorSubject. If no value has
|
|
29194
|
+
* been emitted yet, waits up to LISTEN_TIMEOUT ms for the first tick before throwing.
|
|
29195
|
+
*
|
|
29196
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29197
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29198
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29199
|
+
* @returns Unix timestamp in milliseconds of the latest processed candle
|
|
29200
|
+
* @throws When no timestamp arrives within LISTEN_TIMEOUT ms
|
|
29201
|
+
*/
|
|
29202
|
+
this.getTimestamp = async (symbol, context, backtest) => {
|
|
29203
|
+
this.loggerService.log("timeMetaService getTimestamp", {
|
|
29204
|
+
symbol,
|
|
29205
|
+
context,
|
|
29206
|
+
backtest,
|
|
29207
|
+
});
|
|
29208
|
+
if (ExecutionContextService.hasContext()) {
|
|
29209
|
+
return this.executionContextService.context.when.getTime();
|
|
29210
|
+
}
|
|
29211
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29212
|
+
if (source.data) {
|
|
29213
|
+
return source.data;
|
|
29214
|
+
}
|
|
29215
|
+
console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$2(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
|
|
29216
|
+
const timestamp = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
|
|
29217
|
+
if (typeof timestamp === "symbol") {
|
|
29218
|
+
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$2(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
29219
|
+
}
|
|
29220
|
+
return timestamp;
|
|
29221
|
+
};
|
|
29222
|
+
/**
|
|
29223
|
+
* Pushes a new timestamp value into the BehaviorSubject for the given key.
|
|
29224
|
+
*
|
|
29225
|
+
* Called by StrategyConnectionService after each strategy tick to keep
|
|
29226
|
+
* the cached timestamp up to date.
|
|
29227
|
+
*
|
|
29228
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29229
|
+
* @param timestamp - The createdAt timestamp from the tick (milliseconds)
|
|
29230
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29231
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29232
|
+
*/
|
|
29233
|
+
this.next = async (symbol, timestamp, context, backtest) => {
|
|
29234
|
+
this.loggerService.log("timeMetaService next", {
|
|
29235
|
+
symbol,
|
|
29236
|
+
timestamp,
|
|
29237
|
+
context,
|
|
29238
|
+
backtest,
|
|
29239
|
+
});
|
|
29240
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29241
|
+
source.next(timestamp);
|
|
29242
|
+
};
|
|
29243
|
+
/**
|
|
29244
|
+
* Disposes cached BehaviorSubject(s) to free memory and prevent stale data.
|
|
29245
|
+
*
|
|
29246
|
+
* When called without arguments, clears all memoized timestamp streams.
|
|
29247
|
+
* When called with a payload, clears only the stream for the specified key.
|
|
29248
|
+
* Should be called at strategy start (Backtest/Live/Walker) to reset state.
|
|
29249
|
+
*
|
|
29250
|
+
* @param payload - Optional key to clear a single stream; omit to clear all
|
|
29251
|
+
*/
|
|
29252
|
+
this.clear = (payload) => {
|
|
29253
|
+
this.loggerService.log("timeMetaService clear", {
|
|
29254
|
+
payload,
|
|
29255
|
+
});
|
|
29256
|
+
if (!payload) {
|
|
29257
|
+
this.getSource.clear();
|
|
29258
|
+
return;
|
|
29259
|
+
}
|
|
29260
|
+
const key = CREATE_KEY_FN$2(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
29261
|
+
this.getSource.clear(key);
|
|
29262
|
+
};
|
|
29263
|
+
}
|
|
29264
|
+
}
|
|
29265
|
+
|
|
28836
29266
|
{
|
|
28837
29267
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
28838
29268
|
}
|
|
@@ -28865,6 +29295,10 @@ class SyncMarkdownService {
|
|
|
28865
29295
|
provide(TYPES.actionCoreService, () => new ActionCoreService());
|
|
28866
29296
|
provide(TYPES.frameCoreService, () => new FrameCoreService());
|
|
28867
29297
|
}
|
|
29298
|
+
{
|
|
29299
|
+
provide(TYPES.priceMetaService, () => new PriceMetaService());
|
|
29300
|
+
provide(TYPES.timeMetaService, () => new TimeMetaService());
|
|
29301
|
+
}
|
|
28868
29302
|
{
|
|
28869
29303
|
provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
|
|
28870
29304
|
provide(TYPES.riskGlobalService, () => new RiskGlobalService());
|
|
@@ -28956,6 +29390,10 @@ const coreServices = {
|
|
|
28956
29390
|
actionCoreService: inject(TYPES.actionCoreService),
|
|
28957
29391
|
frameCoreService: inject(TYPES.frameCoreService),
|
|
28958
29392
|
};
|
|
29393
|
+
const metaServices = {
|
|
29394
|
+
timeMetaService: inject(TYPES.timeMetaService),
|
|
29395
|
+
priceMetaService: inject(TYPES.priceMetaService),
|
|
29396
|
+
};
|
|
28959
29397
|
const globalServices = {
|
|
28960
29398
|
sizingGlobalService: inject(TYPES.sizingGlobalService),
|
|
28961
29399
|
riskGlobalService: inject(TYPES.riskGlobalService),
|
|
@@ -29020,6 +29458,7 @@ const backtest = {
|
|
|
29020
29458
|
...connectionServices,
|
|
29021
29459
|
...schemaServices,
|
|
29022
29460
|
...coreServices,
|
|
29461
|
+
...metaServices,
|
|
29023
29462
|
...globalServices,
|
|
29024
29463
|
...commandServices,
|
|
29025
29464
|
...logicPrivateServices,
|
|
@@ -34506,6 +34945,20 @@ class BacktestInstance {
|
|
|
34506
34945
|
frameName: context.frameName,
|
|
34507
34946
|
backtest: true,
|
|
34508
34947
|
});
|
|
34948
|
+
bt.timeMetaService.clear({
|
|
34949
|
+
symbol,
|
|
34950
|
+
strategyName: context.strategyName,
|
|
34951
|
+
exchangeName: context.exchangeName,
|
|
34952
|
+
frameName: context.frameName,
|
|
34953
|
+
backtest: true,
|
|
34954
|
+
});
|
|
34955
|
+
bt.priceMetaService.clear({
|
|
34956
|
+
symbol,
|
|
34957
|
+
strategyName: context.strategyName,
|
|
34958
|
+
exchangeName: context.exchangeName,
|
|
34959
|
+
frameName: context.frameName,
|
|
34960
|
+
backtest: true,
|
|
34961
|
+
});
|
|
34509
34962
|
}
|
|
34510
34963
|
{
|
|
34511
34964
|
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
@@ -36410,6 +36863,20 @@ class LiveInstance {
|
|
|
36410
36863
|
frameName: "",
|
|
36411
36864
|
backtest: false,
|
|
36412
36865
|
});
|
|
36866
|
+
bt.timeMetaService.clear({
|
|
36867
|
+
symbol,
|
|
36868
|
+
strategyName: context.strategyName,
|
|
36869
|
+
exchangeName: context.exchangeName,
|
|
36870
|
+
frameName: "",
|
|
36871
|
+
backtest: false,
|
|
36872
|
+
});
|
|
36873
|
+
bt.priceMetaService.clear({
|
|
36874
|
+
symbol,
|
|
36875
|
+
strategyName: context.strategyName,
|
|
36876
|
+
exchangeName: context.exchangeName,
|
|
36877
|
+
frameName: "",
|
|
36878
|
+
backtest: false,
|
|
36879
|
+
});
|
|
36413
36880
|
}
|
|
36414
36881
|
{
|
|
36415
36882
|
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
@@ -40558,6 +41025,20 @@ class WalkerInstance {
|
|
|
40558
41025
|
frameName: walkerSchema.frameName,
|
|
40559
41026
|
backtest: true,
|
|
40560
41027
|
});
|
|
41028
|
+
bt.timeMetaService.clear({
|
|
41029
|
+
symbol,
|
|
41030
|
+
strategyName,
|
|
41031
|
+
exchangeName: walkerSchema.exchangeName,
|
|
41032
|
+
frameName: walkerSchema.frameName,
|
|
41033
|
+
backtest: true,
|
|
41034
|
+
});
|
|
41035
|
+
bt.priceMetaService.clear({
|
|
41036
|
+
symbol,
|
|
41037
|
+
strategyName,
|
|
41038
|
+
exchangeName: walkerSchema.exchangeName,
|
|
41039
|
+
frameName: walkerSchema.frameName,
|
|
41040
|
+
backtest: true,
|
|
41041
|
+
});
|
|
40561
41042
|
}
|
|
40562
41043
|
{
|
|
40563
41044
|
const { riskName, riskList, actions } = bt.strategySchemaService.get(strategyName);
|
|
@@ -43525,13 +44006,15 @@ class NotificationMemoryBacktestUtils {
|
|
|
43525
44006
|
* Handles signal sync events (signal-open, signal-close).
|
|
43526
44007
|
* @param data - The signal sync contract data
|
|
43527
44008
|
*/
|
|
43528
|
-
this.handleSync = async (data) => {
|
|
44009
|
+
this.handleSync = functoolsKit.trycatch(async (data) => {
|
|
43529
44010
|
bt.loggerService.info(NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_SYNC, {
|
|
43530
44011
|
signalId: data.signalId,
|
|
43531
44012
|
action: data.action,
|
|
43532
44013
|
});
|
|
43533
44014
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
43534
|
-
}
|
|
44015
|
+
}, {
|
|
44016
|
+
defaultValue: null,
|
|
44017
|
+
});
|
|
43535
44018
|
/**
|
|
43536
44019
|
* Handles risk rejection event.
|
|
43537
44020
|
* @param data - The risk contract data
|
|
@@ -43640,8 +44123,10 @@ class NotificationDummyBacktestUtils {
|
|
|
43640
44123
|
/**
|
|
43641
44124
|
* No-op handler for signal sync event.
|
|
43642
44125
|
*/
|
|
43643
|
-
this.handleSync = async () => {
|
|
43644
|
-
}
|
|
44126
|
+
this.handleSync = functoolsKit.trycatch(async () => {
|
|
44127
|
+
}, {
|
|
44128
|
+
defaultValue: null,
|
|
44129
|
+
});
|
|
43645
44130
|
/**
|
|
43646
44131
|
* No-op handler for risk rejection event.
|
|
43647
44132
|
*/
|
|
@@ -43780,7 +44265,7 @@ class NotificationPersistBacktestUtils {
|
|
|
43780
44265
|
* Handles signal sync events (signal-open, signal-close).
|
|
43781
44266
|
* @param data - The signal sync contract data
|
|
43782
44267
|
*/
|
|
43783
|
-
this.handleSync = async (data) => {
|
|
44268
|
+
this.handleSync = functoolsKit.trycatch(async (data) => {
|
|
43784
44269
|
bt.loggerService.info(NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_SYNC, {
|
|
43785
44270
|
signalId: data.signalId,
|
|
43786
44271
|
action: data.action,
|
|
@@ -43788,7 +44273,9 @@ class NotificationPersistBacktestUtils {
|
|
|
43788
44273
|
await this.waitForInit();
|
|
43789
44274
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
43790
44275
|
await this._updateNotifications();
|
|
43791
|
-
}
|
|
44276
|
+
}, {
|
|
44277
|
+
defaultValue: null,
|
|
44278
|
+
});
|
|
43792
44279
|
/**
|
|
43793
44280
|
* Handles risk rejection event.
|
|
43794
44281
|
* @param data - The risk contract data
|
|
@@ -43971,13 +44458,15 @@ class NotificationMemoryLiveUtils {
|
|
|
43971
44458
|
* Handles signal sync events (signal-open, signal-close).
|
|
43972
44459
|
* @param data - The signal sync contract data
|
|
43973
44460
|
*/
|
|
43974
|
-
this.handleSync = async (data) => {
|
|
44461
|
+
this.handleSync = functoolsKit.trycatch(async (data) => {
|
|
43975
44462
|
bt.loggerService.info(NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_SYNC, {
|
|
43976
44463
|
signalId: data.signalId,
|
|
43977
44464
|
action: data.action,
|
|
43978
44465
|
});
|
|
43979
44466
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
43980
|
-
}
|
|
44467
|
+
}, {
|
|
44468
|
+
defaultValue: null,
|
|
44469
|
+
});
|
|
43981
44470
|
/**
|
|
43982
44471
|
* Handles risk rejection event.
|
|
43983
44472
|
* @param data - The risk contract data
|
|
@@ -44086,8 +44575,10 @@ class NotificationDummyLiveUtils {
|
|
|
44086
44575
|
/**
|
|
44087
44576
|
* No-op handler for signal sync event.
|
|
44088
44577
|
*/
|
|
44089
|
-
this.handleSync = async () => {
|
|
44090
|
-
}
|
|
44578
|
+
this.handleSync = functoolsKit.trycatch(async () => {
|
|
44579
|
+
}, {
|
|
44580
|
+
defaultValue: null,
|
|
44581
|
+
});
|
|
44091
44582
|
/**
|
|
44092
44583
|
* No-op handler for risk rejection event.
|
|
44093
44584
|
*/
|
|
@@ -44227,7 +44718,7 @@ class NotificationPersistLiveUtils {
|
|
|
44227
44718
|
* Handles signal sync events (signal-open, signal-close).
|
|
44228
44719
|
* @param data - The signal sync contract data
|
|
44229
44720
|
*/
|
|
44230
|
-
this.handleSync = async (data) => {
|
|
44721
|
+
this.handleSync = functoolsKit.trycatch(async (data) => {
|
|
44231
44722
|
bt.loggerService.info(NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_SYNC, {
|
|
44232
44723
|
signalId: data.signalId,
|
|
44233
44724
|
action: data.action,
|
|
@@ -44235,7 +44726,9 @@ class NotificationPersistLiveUtils {
|
|
|
44235
44726
|
await this.waitForInit();
|
|
44236
44727
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
44237
44728
|
await this._updateNotifications();
|
|
44238
|
-
}
|
|
44729
|
+
}, {
|
|
44730
|
+
defaultValue: null,
|
|
44731
|
+
});
|
|
44239
44732
|
/**
|
|
44240
44733
|
* Handles risk rejection event.
|
|
44241
44734
|
* @param data - The risk contract data
|
|
@@ -44397,9 +44890,11 @@ class NotificationBacktestAdapter {
|
|
|
44397
44890
|
* Proxies call to the underlying notification adapter.
|
|
44398
44891
|
* @param data - The signal sync contract data
|
|
44399
44892
|
*/
|
|
44400
|
-
this.handleSync = async (data) => {
|
|
44893
|
+
this.handleSync = functoolsKit.trycatch(async (data) => {
|
|
44401
44894
|
return await this._notificationBacktestUtils.handleSync(data);
|
|
44402
|
-
}
|
|
44895
|
+
}, {
|
|
44896
|
+
defaultValue: null,
|
|
44897
|
+
});
|
|
44403
44898
|
/**
|
|
44404
44899
|
* Handles risk rejection event.
|
|
44405
44900
|
* Proxies call to the underlying notification adapter.
|
|
@@ -44541,9 +45036,11 @@ class NotificationLiveAdapter {
|
|
|
44541
45036
|
* Proxies call to the underlying notification adapter.
|
|
44542
45037
|
* @param data - The signal sync contract data
|
|
44543
45038
|
*/
|
|
44544
|
-
this.handleSync = async (data) => {
|
|
45039
|
+
this.handleSync = functoolsKit.trycatch(async (data) => {
|
|
44545
45040
|
return await this._notificationLiveUtils.handleSync(data);
|
|
44546
|
-
}
|
|
45041
|
+
}, {
|
|
45042
|
+
defaultValue: null,
|
|
45043
|
+
});
|
|
44547
45044
|
/**
|
|
44548
45045
|
* Handles risk rejection event.
|
|
44549
45046
|
* Proxies call to the underlying notification adapter.
|