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.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createActivator } from 'di-kit';
|
|
2
2
|
import { scoped } from 'di-scoped';
|
|
3
|
-
import { Subject, makeExtendable, singleshot, getErrorMessage, memoize, not, errorData, trycatch, retry, queued, sleep, randomString, str, isObject, ToolRegistry, typo, and, Source, resolveDocuments, timeout, TIMEOUT_SYMBOL as TIMEOUT_SYMBOL$1, compose, singlerun } from 'functools-kit';
|
|
3
|
+
import { Subject, makeExtendable, singleshot, getErrorMessage, memoize, not, errorData, trycatch, retry, queued, sleep, randomString, str, isObject, ToolRegistry, typo, and, Source, resolveDocuments, timeout, TIMEOUT_SYMBOL as TIMEOUT_SYMBOL$1, compose, BehaviorSubject, waitForNext, singlerun } from 'functools-kit';
|
|
4
4
|
import * as fs from 'fs/promises';
|
|
5
5
|
import fs__default, { stat, opendir, readFile } from 'fs/promises';
|
|
6
6
|
import path, { join, dirname } from 'path';
|
|
@@ -69,6 +69,10 @@ const coreServices$1 = {
|
|
|
69
69
|
actionCoreService: Symbol('actionCoreService'),
|
|
70
70
|
frameCoreService: Symbol('frameCoreService'),
|
|
71
71
|
};
|
|
72
|
+
const metaServices$1 = {
|
|
73
|
+
priceMetaService: Symbol('priceMetaService'),
|
|
74
|
+
timeMetaService: Symbol('timeMetaService'),
|
|
75
|
+
};
|
|
72
76
|
const globalServices$1 = {
|
|
73
77
|
sizingGlobalService: Symbol('sizingGlobalService'),
|
|
74
78
|
riskGlobalService: Symbol('riskGlobalService'),
|
|
@@ -134,6 +138,7 @@ const TYPES = {
|
|
|
134
138
|
...connectionServices$1,
|
|
135
139
|
...schemaServices$1,
|
|
136
140
|
...coreServices$1,
|
|
141
|
+
...metaServices$1,
|
|
137
142
|
...globalServices$1,
|
|
138
143
|
...commandServices$1,
|
|
139
144
|
...logicPrivateServices$1,
|
|
@@ -3538,19 +3543,6 @@ const beginTime = (run) => (...args) => {
|
|
|
3538
3543
|
return fn();
|
|
3539
3544
|
};
|
|
3540
3545
|
|
|
3541
|
-
/**
|
|
3542
|
-
* Retrieves the current timestamp for debugging purposes.
|
|
3543
|
-
* 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.
|
|
3544
|
-
* Can be empty (undefined) if not called from strategy async context, as it's intended for debugging and not critical for logic.
|
|
3545
|
-
* @return {number | undefined} The current timestamp in milliseconds from the execution context, or undefined if not available.
|
|
3546
|
-
*/
|
|
3547
|
-
const getDebugTimestamp = () => {
|
|
3548
|
-
if (ExecutionContextService.hasContext()) {
|
|
3549
|
-
return bt.executionContextService.context.when.getTime();
|
|
3550
|
-
}
|
|
3551
|
-
return undefined;
|
|
3552
|
-
};
|
|
3553
|
-
|
|
3554
3546
|
const INTERVAL_MINUTES$6 = {
|
|
3555
3547
|
"1m": 1,
|
|
3556
3548
|
"3m": 3,
|
|
@@ -4258,7 +4250,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
4258
4250
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
4259
4251
|
timestamp: currentTime,
|
|
4260
4252
|
_isScheduled: false,
|
|
4261
|
-
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4253
|
+
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp: currentTime }],
|
|
4262
4254
|
};
|
|
4263
4255
|
// Валидируем сигнал перед возвратом
|
|
4264
4256
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -4282,7 +4274,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
4282
4274
|
pendingAt: SCHEDULED_SIGNAL_PENDING_MOCK, // Временно, обновится при активации
|
|
4283
4275
|
timestamp: currentTime,
|
|
4284
4276
|
_isScheduled: true,
|
|
4285
|
-
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4277
|
+
_entry: [{ price: signal.priceOpen, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp: currentTime }],
|
|
4286
4278
|
};
|
|
4287
4279
|
// Валидируем сигнал перед возвратом
|
|
4288
4280
|
VALIDATE_SIGNAL_FN(scheduledSignalRow, currentPrice, true);
|
|
@@ -4302,7 +4294,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
4302
4294
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
4303
4295
|
timestamp: currentTime,
|
|
4304
4296
|
_isScheduled: false,
|
|
4305
|
-
_entry: [{ price: currentPrice, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4297
|
+
_entry: [{ price: currentPrice, cost: signal.cost ?? GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp: currentTime }],
|
|
4306
4298
|
};
|
|
4307
4299
|
// Валидируем сигнал перед возвратом
|
|
4308
4300
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -4372,7 +4364,7 @@ const WAIT_FOR_DISPOSE_FN$1 = async (self) => {
|
|
|
4372
4364
|
self.params.logger.debug("ClientStrategy dispose");
|
|
4373
4365
|
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);
|
|
4374
4366
|
};
|
|
4375
|
-
const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice) => {
|
|
4367
|
+
const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice, timestamp) => {
|
|
4376
4368
|
// Initialize partial array if not present
|
|
4377
4369
|
if (!signal._partial)
|
|
4378
4370
|
signal._partial = [];
|
|
@@ -4401,7 +4393,7 @@ const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
4401
4393
|
entryCountAtClose,
|
|
4402
4394
|
currentPrice,
|
|
4403
4395
|
costBasisAtClose: remainingCostBasis,
|
|
4404
|
-
|
|
4396
|
+
timestamp,
|
|
4405
4397
|
});
|
|
4406
4398
|
self.params.logger.info("PARTIAL_PROFIT_FN executed", {
|
|
4407
4399
|
signalId: signal.id,
|
|
@@ -4411,7 +4403,7 @@ const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
4411
4403
|
});
|
|
4412
4404
|
return true;
|
|
4413
4405
|
};
|
|
4414
|
-
const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice) => {
|
|
4406
|
+
const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice, timestamp) => {
|
|
4415
4407
|
// Initialize partial array if not present
|
|
4416
4408
|
if (!signal._partial)
|
|
4417
4409
|
signal._partial = [];
|
|
@@ -4439,7 +4431,7 @@ const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
4439
4431
|
currentPrice,
|
|
4440
4432
|
entryCountAtClose,
|
|
4441
4433
|
costBasisAtClose: remainingCostBasis,
|
|
4442
|
-
|
|
4434
|
+
timestamp,
|
|
4443
4435
|
});
|
|
4444
4436
|
self.params.logger.warn("PARTIAL_LOSS_FN executed", {
|
|
4445
4437
|
signalId: signal.id,
|
|
@@ -4836,10 +4828,10 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4836
4828
|
});
|
|
4837
4829
|
return true;
|
|
4838
4830
|
};
|
|
4839
|
-
const AVERAGE_BUY_FN = (self, signal, currentPrice, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) => {
|
|
4831
|
+
const AVERAGE_BUY_FN = (self, signal, currentPrice, timestamp, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) => {
|
|
4840
4832
|
// Ensure _entry is initialized (handles signals loaded from disk without _entry)
|
|
4841
4833
|
if (!signal._entry || signal._entry.length === 0) {
|
|
4842
|
-
signal._entry = [{ price: signal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST,
|
|
4834
|
+
signal._entry = [{ price: signal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp }];
|
|
4843
4835
|
}
|
|
4844
4836
|
if (signal.position === "long") {
|
|
4845
4837
|
// LONG: new entry must beat the all-time low — strictly below every prior entry price
|
|
@@ -4869,7 +4861,7 @@ const AVERAGE_BUY_FN = (self, signal, currentPrice, cost = GLOBAL_CONFIG.CC_POSI
|
|
|
4869
4861
|
return false;
|
|
4870
4862
|
}
|
|
4871
4863
|
}
|
|
4872
|
-
signal._entry.push({ price: currentPrice, cost,
|
|
4864
|
+
signal._entry.push({ price: currentPrice, cost, timestamp });
|
|
4873
4865
|
self.params.logger.info("AVERAGE_BUY_FN executed", {
|
|
4874
4866
|
signalId: signal.id,
|
|
4875
4867
|
position: signal.position,
|
|
@@ -5052,7 +5044,7 @@ const CALL_SCHEDULE_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol,
|
|
|
5052
5044
|
await ExecutionContextService.runInContext(async () => {
|
|
5053
5045
|
const publicSignal = TO_PUBLIC_SIGNAL(scheduled, currentPrice);
|
|
5054
5046
|
// Call system onSchedulePing callback first (emits to pingSubject)
|
|
5055
|
-
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);
|
|
5047
|
+
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);
|
|
5056
5048
|
// Call user onSchedulePing callback only if signal is still active (not cancelled, not activated)
|
|
5057
5049
|
if (self.params.callbacks?.onSchedulePing) {
|
|
5058
5050
|
await self.params.callbacks.onSchedulePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
@@ -5078,7 +5070,7 @@ const CALL_ACTIVE_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, pe
|
|
|
5078
5070
|
await ExecutionContextService.runInContext(async () => {
|
|
5079
5071
|
const publicSignal = TO_PUBLIC_SIGNAL(pending, currentPrice);
|
|
5080
5072
|
// Call system onActivePing callback first (emits to activePingSubject)
|
|
5081
|
-
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);
|
|
5073
|
+
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);
|
|
5082
5074
|
// Call user onActivePing callback only if signal is still active (not closed)
|
|
5083
5075
|
if (self.params.callbacks?.onActivePing) {
|
|
5084
5076
|
await self.params.callbacks.onActivePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
@@ -5876,6 +5868,57 @@ const CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, signal, averagePrice, c
|
|
|
5876
5868
|
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, closeTimestamp, self.params.execution.context.backtest);
|
|
5877
5869
|
return result;
|
|
5878
5870
|
};
|
|
5871
|
+
const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, averagePrice, closeTimestamp) => {
|
|
5872
|
+
const syncCloseAllowed = await CALL_SIGNAL_SYNC_CLOSE_FN(closeTimestamp, averagePrice, "closed", closedSignal, self);
|
|
5873
|
+
if (!syncCloseAllowed) {
|
|
5874
|
+
self.params.logger.info("ClientStrategy backtest: user-closed signal rejected by sync, will retry", {
|
|
5875
|
+
symbol: self.params.execution.context.symbol,
|
|
5876
|
+
signalId: closedSignal.id,
|
|
5877
|
+
});
|
|
5878
|
+
self._closedSignal = null;
|
|
5879
|
+
self._pendingSignal = closedSignal;
|
|
5880
|
+
throw new Error(`ClientStrategy backtest: signal close rejected by sync (signalId=${closedSignal.id}). ` +
|
|
5881
|
+
`Retry backtest() with new candle data.`);
|
|
5882
|
+
}
|
|
5883
|
+
self._closedSignal = null;
|
|
5884
|
+
await CALL_COMMIT_FN(self, {
|
|
5885
|
+
action: "close-pending",
|
|
5886
|
+
symbol: self.params.execution.context.symbol,
|
|
5887
|
+
strategyName: self.params.strategyName,
|
|
5888
|
+
exchangeName: self.params.exchangeName,
|
|
5889
|
+
frameName: self.params.frameName,
|
|
5890
|
+
signalId: closedSignal.id,
|
|
5891
|
+
backtest: true,
|
|
5892
|
+
closeId: closedSignal.closeId,
|
|
5893
|
+
timestamp: closeTimestamp,
|
|
5894
|
+
totalEntries: closedSignal._entry?.length ?? 1,
|
|
5895
|
+
totalPartials: closedSignal._partial?.length ?? 0,
|
|
5896
|
+
originalPriceOpen: closedSignal.priceOpen,
|
|
5897
|
+
pnl: toProfitLossDto(closedSignal, averagePrice),
|
|
5898
|
+
});
|
|
5899
|
+
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
5900
|
+
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
5901
|
+
await CALL_BREAKEVEN_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
5902
|
+
await CALL_RISK_REMOVE_SIGNAL_FN(self, self.params.execution.context.symbol, closeTimestamp, self.params.execution.context.backtest);
|
|
5903
|
+
const pnl = toProfitLossDto(closedSignal, averagePrice);
|
|
5904
|
+
const result = {
|
|
5905
|
+
action: "closed",
|
|
5906
|
+
signal: TO_PUBLIC_SIGNAL(closedSignal, averagePrice),
|
|
5907
|
+
currentPrice: averagePrice,
|
|
5908
|
+
closeReason: "closed",
|
|
5909
|
+
closeTimestamp,
|
|
5910
|
+
pnl,
|
|
5911
|
+
strategyName: self.params.method.context.strategyName,
|
|
5912
|
+
exchangeName: self.params.method.context.exchangeName,
|
|
5913
|
+
frameName: self.params.method.context.frameName,
|
|
5914
|
+
symbol: self.params.execution.context.symbol,
|
|
5915
|
+
backtest: self.params.execution.context.backtest,
|
|
5916
|
+
closeId: closedSignal.closeId,
|
|
5917
|
+
createdAt: closeTimestamp,
|
|
5918
|
+
};
|
|
5919
|
+
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, closeTimestamp, self.params.execution.context.backtest);
|
|
5920
|
+
return result;
|
|
5921
|
+
};
|
|
5879
5922
|
const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) => {
|
|
5880
5923
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
5881
5924
|
const maxTimeToWait = GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES * 60 * 1000;
|
|
@@ -5893,7 +5936,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5893
5936
|
if (self._cancelledSignal) {
|
|
5894
5937
|
// Сигнал был отменен через cancel() в onSchedulePing
|
|
5895
5938
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user");
|
|
5896
|
-
return {
|
|
5939
|
+
return { outcome: "cancelled", result };
|
|
5897
5940
|
}
|
|
5898
5941
|
// КРИТИЧНО: Проверяем был ли сигнал активирован пользователем через activateScheduled()
|
|
5899
5942
|
// Обрабатываем inline (как в tick()) с риск-проверкой по averagePrice
|
|
@@ -5907,7 +5950,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5907
5950
|
signalId: activatedSignal.id,
|
|
5908
5951
|
});
|
|
5909
5952
|
await self.setScheduledSignal(null);
|
|
5910
|
-
return {
|
|
5953
|
+
return { outcome: "pending" };
|
|
5911
5954
|
}
|
|
5912
5955
|
// Риск-проверка по averagePrice (симметрия с LIVE tick())
|
|
5913
5956
|
if (await not(CALL_RISK_CHECK_SIGNAL_FN(self, self.params.execution.context.symbol, activatedSignal, averagePrice, candle.timestamp, self.params.execution.context.backtest))) {
|
|
@@ -5916,7 +5959,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5916
5959
|
signalId: activatedSignal.id,
|
|
5917
5960
|
});
|
|
5918
5961
|
await self.setScheduledSignal(null);
|
|
5919
|
-
return {
|
|
5962
|
+
return { outcome: "pending" };
|
|
5920
5963
|
}
|
|
5921
5964
|
const pendingSignal = {
|
|
5922
5965
|
...activatedSignal,
|
|
@@ -5945,7 +5988,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5945
5988
|
originalPriceOpen: activatedSignal.priceOpen,
|
|
5946
5989
|
pnl: toProfitLossDto(activatedSignal, averagePrice),
|
|
5947
5990
|
});
|
|
5948
|
-
return {
|
|
5991
|
+
return { outcome: "pending" };
|
|
5949
5992
|
}
|
|
5950
5993
|
await self.setScheduledSignal(null);
|
|
5951
5994
|
await self.setPendingSignal(pendingSignal);
|
|
@@ -5978,18 +6021,13 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5978
6021
|
});
|
|
5979
6022
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
|
|
5980
6023
|
await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
|
|
5981
|
-
return {
|
|
5982
|
-
activated: true,
|
|
5983
|
-
cancelled: false,
|
|
5984
|
-
activationIndex: i,
|
|
5985
|
-
result: null,
|
|
5986
|
-
};
|
|
6024
|
+
return { outcome: "activated", activationIndex: i };
|
|
5987
6025
|
}
|
|
5988
6026
|
// КРИТИЧНО: Проверяем timeout ПЕРЕД проверкой цены
|
|
5989
6027
|
const elapsedTime = candle.timestamp - scheduled.scheduledAt;
|
|
5990
6028
|
if (elapsedTime >= maxTimeToWait) {
|
|
5991
6029
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "timeout");
|
|
5992
|
-
return {
|
|
6030
|
+
return { outcome: "cancelled", result };
|
|
5993
6031
|
}
|
|
5994
6032
|
let shouldActivate = false;
|
|
5995
6033
|
let shouldCancel = false;
|
|
@@ -6027,27 +6065,17 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
6027
6065
|
}
|
|
6028
6066
|
if (shouldCancel) {
|
|
6029
6067
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "price_reject");
|
|
6030
|
-
return {
|
|
6068
|
+
return { outcome: "cancelled", result };
|
|
6031
6069
|
}
|
|
6032
6070
|
if (shouldActivate) {
|
|
6033
6071
|
await ACTIVATE_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, candle.timestamp);
|
|
6034
|
-
return {
|
|
6035
|
-
activated: true,
|
|
6036
|
-
cancelled: false,
|
|
6037
|
-
activationIndex: i,
|
|
6038
|
-
result: null,
|
|
6039
|
-
};
|
|
6072
|
+
return { outcome: "activated", activationIndex: i };
|
|
6040
6073
|
}
|
|
6041
6074
|
await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true, averagePrice);
|
|
6042
6075
|
// Process queued commit events with candle timestamp
|
|
6043
6076
|
await PROCESS_COMMIT_QUEUE_FN(self, averagePrice, candle.timestamp);
|
|
6044
6077
|
}
|
|
6045
|
-
return {
|
|
6046
|
-
activated: false,
|
|
6047
|
-
cancelled: false,
|
|
6048
|
-
activationIndex: -1,
|
|
6049
|
-
result: null,
|
|
6050
|
-
};
|
|
6078
|
+
return { outcome: "pending" };
|
|
6051
6079
|
};
|
|
6052
6080
|
const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
6053
6081
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
@@ -6066,6 +6094,10 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
6066
6094
|
const startIndex = Math.max(0, i - (candlesCount - 1));
|
|
6067
6095
|
const recentCandles = candles.slice(startIndex, i + 1);
|
|
6068
6096
|
const averagePrice = GET_AVG_PRICE_FN(recentCandles);
|
|
6097
|
+
// КРИТИЧНО: Проверяем был ли сигнал закрыт пользователем через closePending()
|
|
6098
|
+
if (self._closedSignal) {
|
|
6099
|
+
return await CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN(self, self._closedSignal, averagePrice, currentCandleTimestamp);
|
|
6100
|
+
}
|
|
6069
6101
|
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentCandleTimestamp, true, averagePrice);
|
|
6070
6102
|
let shouldClose = false;
|
|
6071
6103
|
let closeReason;
|
|
@@ -6170,7 +6202,32 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
6170
6202
|
// Process queued commit events with candle timestamp
|
|
6171
6203
|
await PROCESS_COMMIT_QUEUE_FN(self, averagePrice, currentCandleTimestamp);
|
|
6172
6204
|
}
|
|
6173
|
-
|
|
6205
|
+
// Loop exhausted without closing — check if we have enough data
|
|
6206
|
+
const lastCandles = candles.slice(-GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT);
|
|
6207
|
+
const lastPrice = GET_AVG_PRICE_FN(lastCandles);
|
|
6208
|
+
const closeTimestamp = lastCandles[lastCandles.length - 1].timestamp;
|
|
6209
|
+
const signalTime = signal.pendingAt;
|
|
6210
|
+
const maxTimeToWait = signal.minuteEstimatedTime * 60 * 1000;
|
|
6211
|
+
const elapsedTime = closeTimestamp - signalTime;
|
|
6212
|
+
if (elapsedTime < maxTimeToWait) {
|
|
6213
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
6214
|
+
const requiredCandlesCount = signal.minuteEstimatedTime + bufferCandlesCount + 1;
|
|
6215
|
+
throw new Error(str.newline(`ClientStrategy backtest: Insufficient candle data for pending signal. ` +
|
|
6216
|
+
`Signal opened at ${new Date(signal.pendingAt).toISOString()}, ` +
|
|
6217
|
+
`last candle at ${new Date(closeTimestamp).toISOString()}. ` +
|
|
6218
|
+
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${signal.minuteEstimatedTime}min required. ` +
|
|
6219
|
+
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
6220
|
+
`\nBreakdown: ${signal.minuteEstimatedTime} candles for signal lifetime + ${bufferCandlesCount} buffer candles. ` +
|
|
6221
|
+
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
6222
|
+
`so first ${bufferCandlesCount} candles are skipped to ensure accurate price averaging. ` +
|
|
6223
|
+
`Provide complete candle range: [pendingAt - ${bufferCandlesCount}min, pendingAt + ${signal.minuteEstimatedTime}min].`));
|
|
6224
|
+
}
|
|
6225
|
+
const timeExpiredResult = await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(self, signal, lastPrice, "time_expired", closeTimestamp);
|
|
6226
|
+
if (!timeExpiredResult) {
|
|
6227
|
+
throw new Error(`ClientStrategy backtest: time_expired close rejected by sync (signalId=${signal.id}). ` +
|
|
6228
|
+
`Retry backtest() with new candle data.`);
|
|
6229
|
+
}
|
|
6230
|
+
return timeExpiredResult;
|
|
6174
6231
|
};
|
|
6175
6232
|
/**
|
|
6176
6233
|
* Client implementation for trading strategy lifecycle management.
|
|
@@ -6654,16 +6711,16 @@ class ClientStrategy {
|
|
|
6654
6711
|
* // No DCA: [{ price: 43000, cost: 100 }]
|
|
6655
6712
|
* // One DCA: [{ price: 43000, cost: 100 }, { price: 42000, cost: 100 }]
|
|
6656
6713
|
*/
|
|
6657
|
-
async getPositionEntries(symbol) {
|
|
6714
|
+
async getPositionEntries(symbol, timestamp) {
|
|
6658
6715
|
this.params.logger.debug("ClientStrategy getPositionEntries", { symbol });
|
|
6659
6716
|
if (!this._pendingSignal) {
|
|
6660
6717
|
return null;
|
|
6661
6718
|
}
|
|
6662
6719
|
const entries = this._pendingSignal._entry;
|
|
6663
6720
|
if (!entries || entries.length === 0) {
|
|
6664
|
-
return [{ price: this._pendingSignal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST }];
|
|
6721
|
+
return [{ price: this._pendingSignal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST, timestamp }];
|
|
6665
6722
|
}
|
|
6666
|
-
return entries.map(({ price, cost }) => ({ price, cost }));
|
|
6723
|
+
return entries.map(({ price, cost, timestamp }) => ({ price, cost, timestamp }));
|
|
6667
6724
|
}
|
|
6668
6725
|
/**
|
|
6669
6726
|
* Performs a single tick of strategy execution.
|
|
@@ -7117,11 +7174,12 @@ class ClientStrategy {
|
|
|
7117
7174
|
priceOpen: scheduled.priceOpen,
|
|
7118
7175
|
position: scheduled.position,
|
|
7119
7176
|
});
|
|
7120
|
-
const
|
|
7121
|
-
if (
|
|
7122
|
-
return result;
|
|
7177
|
+
const scheduledResult = await PROCESS_SCHEDULED_SIGNAL_CANDLES_FN(this, scheduled, candles);
|
|
7178
|
+
if (scheduledResult.outcome === "cancelled") {
|
|
7179
|
+
return scheduledResult.result;
|
|
7123
7180
|
}
|
|
7124
|
-
if (activated) {
|
|
7181
|
+
if (scheduledResult.outcome === "activated") {
|
|
7182
|
+
const { activationIndex } = scheduledResult;
|
|
7125
7183
|
// КРИТИЧНО: activationIndex - индекс свечи активации в массиве candles
|
|
7126
7184
|
// BacktestLogicPrivateService включил буфер в начало массива, поэтому перед activationIndex достаточно свечей
|
|
7127
7185
|
// PROCESS_PENDING_SIGNAL_CANDLES_FN пропустит первые bufferCandlesCount свечей для VWAP
|
|
@@ -7193,40 +7251,7 @@ class ClientStrategy {
|
|
|
7193
7251
|
if (candles.length < candlesCount) {
|
|
7194
7252
|
this.params.logger.warn(`ClientStrategy backtest: Expected at least ${candlesCount} candles for VWAP, got ${candles.length}`);
|
|
7195
7253
|
}
|
|
7196
|
-
|
|
7197
|
-
if (closedResult) {
|
|
7198
|
-
return closedResult;
|
|
7199
|
-
}
|
|
7200
|
-
// Signal didn't close during candle processing - check if we have enough data
|
|
7201
|
-
const lastCandles = candles.slice(-GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT);
|
|
7202
|
-
const lastPrice = GET_AVG_PRICE_FN(lastCandles);
|
|
7203
|
-
const closeTimestamp = lastCandles[lastCandles.length - 1].timestamp;
|
|
7204
|
-
const signalTime = signal.pendingAt;
|
|
7205
|
-
const maxTimeToWait = signal.minuteEstimatedTime * 60 * 1000;
|
|
7206
|
-
const elapsedTime = closeTimestamp - signalTime;
|
|
7207
|
-
// Check if we actually reached time expiration or just ran out of candles
|
|
7208
|
-
if (elapsedTime < maxTimeToWait) {
|
|
7209
|
-
// EDGE CASE: backtest() called with insufficient candle data
|
|
7210
|
-
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
7211
|
-
const requiredCandlesCount = signal.minuteEstimatedTime + bufferCandlesCount + 1;
|
|
7212
|
-
throw new Error(str.newline(`ClientStrategy backtest: Insufficient candle data for pending signal. ` +
|
|
7213
|
-
`Signal opened at ${new Date(signal.pendingAt).toISOString()}, ` +
|
|
7214
|
-
`last candle at ${new Date(closeTimestamp).toISOString()}. ` +
|
|
7215
|
-
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${signal.minuteEstimatedTime}min required. ` +
|
|
7216
|
-
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
7217
|
-
`\nBreakdown: ${signal.minuteEstimatedTime} candles for signal lifetime + ${bufferCandlesCount} buffer candles. ` +
|
|
7218
|
-
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
7219
|
-
`so first ${bufferCandlesCount} candles are skipped to ensure accurate price averaging. ` +
|
|
7220
|
-
`Provide complete candle range: [pendingAt - ${bufferCandlesCount}min, pendingAt + ${signal.minuteEstimatedTime}min].`));
|
|
7221
|
-
}
|
|
7222
|
-
// Time actually expired - close with time_expired
|
|
7223
|
-
const timeExpiredResult = await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(this, signal, lastPrice, "time_expired", closeTimestamp);
|
|
7224
|
-
if (!timeExpiredResult) {
|
|
7225
|
-
// Sync rejected the close — signal remains in _pendingSignal, caller must retry
|
|
7226
|
-
throw new Error(`ClientStrategy backtest: time_expired close rejected by sync (signalId=${signal.id}). ` +
|
|
7227
|
-
`Retry backtest() with new candle data.`);
|
|
7228
|
-
}
|
|
7229
|
-
return timeExpiredResult;
|
|
7254
|
+
return await PROCESS_PENDING_SIGNAL_CANDLES_FN(this, signal, candles);
|
|
7230
7255
|
}
|
|
7231
7256
|
/**
|
|
7232
7257
|
* Stops the strategy from generating new signals.
|
|
@@ -7499,7 +7524,7 @@ class ClientStrategy {
|
|
|
7499
7524
|
* // success3 = false (skipped, would exceed 100%)
|
|
7500
7525
|
* ```
|
|
7501
7526
|
*/
|
|
7502
|
-
async partialProfit(symbol, percentToClose, currentPrice, backtest) {
|
|
7527
|
+
async partialProfit(symbol, percentToClose, currentPrice, backtest, timestamp) {
|
|
7503
7528
|
this.params.logger.debug("ClientStrategy partialProfit", {
|
|
7504
7529
|
symbol,
|
|
7505
7530
|
percentToClose,
|
|
@@ -7563,7 +7588,7 @@ class ClientStrategy {
|
|
|
7563
7588
|
return false;
|
|
7564
7589
|
}
|
|
7565
7590
|
// Execute partial close logic
|
|
7566
|
-
const wasExecuted = PARTIAL_PROFIT_FN(this, this._pendingSignal, percentToClose, currentPrice);
|
|
7591
|
+
const wasExecuted = PARTIAL_PROFIT_FN(this, this._pendingSignal, percentToClose, currentPrice, timestamp);
|
|
7567
7592
|
// If partial was not executed (exceeded 100%), return false without persistence
|
|
7568
7593
|
if (!wasExecuted) {
|
|
7569
7594
|
return false;
|
|
@@ -7682,7 +7707,7 @@ class ClientStrategy {
|
|
|
7682
7707
|
* // success3 = false (skipped, would exceed 100%)
|
|
7683
7708
|
* ```
|
|
7684
7709
|
*/
|
|
7685
|
-
async partialLoss(symbol, percentToClose, currentPrice, backtest) {
|
|
7710
|
+
async partialLoss(symbol, percentToClose, currentPrice, backtest, timestamp) {
|
|
7686
7711
|
this.params.logger.debug("ClientStrategy partialLoss", {
|
|
7687
7712
|
symbol,
|
|
7688
7713
|
percentToClose,
|
|
@@ -7746,7 +7771,7 @@ class ClientStrategy {
|
|
|
7746
7771
|
return false;
|
|
7747
7772
|
}
|
|
7748
7773
|
// Execute partial close logic
|
|
7749
|
-
const wasExecuted = PARTIAL_LOSS_FN(this, this._pendingSignal, percentToClose, currentPrice);
|
|
7774
|
+
const wasExecuted = PARTIAL_LOSS_FN(this, this._pendingSignal, percentToClose, currentPrice, timestamp);
|
|
7750
7775
|
// If partial was not executed (exceeded 100%), return false without persistence
|
|
7751
7776
|
if (!wasExecuted) {
|
|
7752
7777
|
return false;
|
|
@@ -8502,7 +8527,7 @@ class ClientStrategy {
|
|
|
8502
8527
|
* @param backtest - Whether running in backtest mode
|
|
8503
8528
|
* @returns Promise<boolean> - true if entry added, false if rejected by direction check
|
|
8504
8529
|
*/
|
|
8505
|
-
async averageBuy(symbol, currentPrice, backtest, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) {
|
|
8530
|
+
async averageBuy(symbol, currentPrice, backtest, timestamp, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_COST) {
|
|
8506
8531
|
this.params.logger.debug("ClientStrategy averageBuy", {
|
|
8507
8532
|
symbol,
|
|
8508
8533
|
currentPrice,
|
|
@@ -8517,7 +8542,7 @@ class ClientStrategy {
|
|
|
8517
8542
|
throw new Error(`ClientStrategy averageBuy: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
8518
8543
|
}
|
|
8519
8544
|
// Execute averaging logic
|
|
8520
|
-
const result = AVERAGE_BUY_FN(this, this._pendingSignal, currentPrice, cost);
|
|
8545
|
+
const result = AVERAGE_BUY_FN(this, this._pendingSignal, currentPrice, timestamp, cost);
|
|
8521
8546
|
if (!result) {
|
|
8522
8547
|
return false;
|
|
8523
8548
|
}
|
|
@@ -8981,7 +9006,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
|
|
|
8981
9006
|
* @param backtest - Whether running in backtest mode
|
|
8982
9007
|
* @returns Unique string key for memoization
|
|
8983
9008
|
*/
|
|
8984
|
-
const CREATE_KEY_FN$
|
|
9009
|
+
const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
8985
9010
|
const parts = [symbol, strategyName, exchangeName];
|
|
8986
9011
|
if (frameName)
|
|
8987
9012
|
parts.push(frameName);
|
|
@@ -8997,11 +9022,12 @@ const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest
|
|
|
8997
9022
|
* @param self - Reference to StrategyConnectionService instance
|
|
8998
9023
|
* @returns Callback function for schedule ping events
|
|
8999
9024
|
*/
|
|
9000
|
-
const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => trycatch(async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
9025
|
+
const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => trycatch(async (symbol, strategyName, exchangeName, data, currentPrice, backtest, timestamp) => {
|
|
9001
9026
|
const event = {
|
|
9002
9027
|
symbol,
|
|
9003
9028
|
strategyName,
|
|
9004
9029
|
exchangeName,
|
|
9030
|
+
currentPrice,
|
|
9005
9031
|
data,
|
|
9006
9032
|
backtest,
|
|
9007
9033
|
timestamp,
|
|
@@ -9030,11 +9056,12 @@ const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => trycatch(async (symbol, strateg
|
|
|
9030
9056
|
* @param self - Reference to StrategyConnectionService instance
|
|
9031
9057
|
* @returns Callback function for active ping events
|
|
9032
9058
|
*/
|
|
9033
|
-
const CREATE_COMMIT_ACTIVE_PING_FN = (self) => trycatch(async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
9059
|
+
const CREATE_COMMIT_ACTIVE_PING_FN = (self) => trycatch(async (symbol, strategyName, exchangeName, data, currentPrice, backtest, timestamp) => {
|
|
9034
9060
|
const event = {
|
|
9035
9061
|
symbol,
|
|
9036
9062
|
strategyName,
|
|
9037
9063
|
exchangeName,
|
|
9064
|
+
currentPrice,
|
|
9038
9065
|
data,
|
|
9039
9066
|
backtest,
|
|
9040
9067
|
timestamp,
|
|
@@ -9158,6 +9185,8 @@ class StrategyConnectionService {
|
|
|
9158
9185
|
this.partialConnectionService = inject(TYPES.partialConnectionService);
|
|
9159
9186
|
this.breakevenConnectionService = inject(TYPES.breakevenConnectionService);
|
|
9160
9187
|
this.actionCoreService = inject(TYPES.actionCoreService);
|
|
9188
|
+
this.timeMetaService = inject(TYPES.timeMetaService);
|
|
9189
|
+
this.priceMetaService = inject(TYPES.priceMetaService);
|
|
9161
9190
|
/**
|
|
9162
9191
|
* Retrieves memoized ClientStrategy instance for given symbol-strategy pair with exchange and frame isolation.
|
|
9163
9192
|
*
|
|
@@ -9171,7 +9200,7 @@ class StrategyConnectionService {
|
|
|
9171
9200
|
* @param backtest - Whether running in backtest mode
|
|
9172
9201
|
* @returns Configured ClientStrategy instance
|
|
9173
9202
|
*/
|
|
9174
|
-
this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
9203
|
+
this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$o(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
9175
9204
|
const { riskName = "", riskList = [], getSignal, interval, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
9176
9205
|
return new ClientStrategy({
|
|
9177
9206
|
symbol,
|
|
@@ -9258,6 +9287,20 @@ class StrategyConnectionService {
|
|
|
9258
9287
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9259
9288
|
return await strategy.getTotalCostClosed(symbol);
|
|
9260
9289
|
};
|
|
9290
|
+
/**
|
|
9291
|
+
* Returns the effective (DCA-averaged) entry price for the current pending signal.
|
|
9292
|
+
*
|
|
9293
|
+
* This is the harmonic mean of all _entry prices, which is the correct
|
|
9294
|
+
* cost-basis price used in all PNL calculations.
|
|
9295
|
+
* With no DCA entries, equals the original priceOpen.
|
|
9296
|
+
*
|
|
9297
|
+
* Returns null if no pending signal exists.
|
|
9298
|
+
*
|
|
9299
|
+
* @param backtest - Whether running in backtest mode
|
|
9300
|
+
* @param symbol - Trading pair symbol
|
|
9301
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9302
|
+
* @returns Promise resolving to effective entry price or null
|
|
9303
|
+
*/
|
|
9261
9304
|
this.getPositionAveragePrice = async (backtest, symbol, context) => {
|
|
9262
9305
|
this.loggerService.log("strategyConnectionService getPositionAveragePrice", {
|
|
9263
9306
|
symbol,
|
|
@@ -9267,6 +9310,19 @@ class StrategyConnectionService {
|
|
|
9267
9310
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9268
9311
|
return await strategy.getPositionAveragePrice(symbol);
|
|
9269
9312
|
};
|
|
9313
|
+
/**
|
|
9314
|
+
* Returns the number of DCA entries made for the current pending signal.
|
|
9315
|
+
*
|
|
9316
|
+
* 1 = original entry only (no DCA).
|
|
9317
|
+
* Increases by 1 with each successful commitAverageBuy().
|
|
9318
|
+
*
|
|
9319
|
+
* Returns null if no pending signal exists.
|
|
9320
|
+
*
|
|
9321
|
+
* @param backtest - Whether running in backtest mode
|
|
9322
|
+
* @param symbol - Trading pair symbol
|
|
9323
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9324
|
+
* @returns Promise resolving to entry count or null
|
|
9325
|
+
*/
|
|
9270
9326
|
this.getPositionInvestedCount = async (backtest, symbol, context) => {
|
|
9271
9327
|
this.loggerService.log("strategyConnectionService getPositionInvestedCount", {
|
|
9272
9328
|
symbol,
|
|
@@ -9276,6 +9332,19 @@ class StrategyConnectionService {
|
|
|
9276
9332
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9277
9333
|
return await strategy.getPositionInvestedCount(symbol);
|
|
9278
9334
|
};
|
|
9335
|
+
/**
|
|
9336
|
+
* Returns the total invested cost basis in dollars for the current pending signal.
|
|
9337
|
+
*
|
|
9338
|
+
* Equal to entryCount × $100 (COST_BASIS_PER_ENTRY).
|
|
9339
|
+
* 1 entry = $100, 2 entries = $200, etc.
|
|
9340
|
+
*
|
|
9341
|
+
* Returns null if no pending signal exists.
|
|
9342
|
+
*
|
|
9343
|
+
* @param backtest - Whether running in backtest mode
|
|
9344
|
+
* @param symbol - Trading pair symbol
|
|
9345
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9346
|
+
* @returns Promise resolving to total invested cost in dollars or null
|
|
9347
|
+
*/
|
|
9279
9348
|
this.getPositionInvestedCost = async (backtest, symbol, context) => {
|
|
9280
9349
|
this.loggerService.log("strategyConnectionService getPositionInvestedCost", {
|
|
9281
9350
|
symbol,
|
|
@@ -9285,6 +9354,20 @@ class StrategyConnectionService {
|
|
|
9285
9354
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9286
9355
|
return await strategy.getPositionInvestedCost(symbol);
|
|
9287
9356
|
};
|
|
9357
|
+
/**
|
|
9358
|
+
* Returns the unrealized PNL percentage for the current pending signal at currentPrice.
|
|
9359
|
+
*
|
|
9360
|
+
* Accounts for partial closes, DCA entries, slippage and fees
|
|
9361
|
+
* (delegates to toProfitLossDto).
|
|
9362
|
+
*
|
|
9363
|
+
* Returns null if no pending signal exists.
|
|
9364
|
+
*
|
|
9365
|
+
* @param backtest - Whether running in backtest mode
|
|
9366
|
+
* @param symbol - Trading pair symbol
|
|
9367
|
+
* @param currentPrice - Current market price
|
|
9368
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9369
|
+
* @returns Promise resolving to pnlPercentage or null
|
|
9370
|
+
*/
|
|
9288
9371
|
this.getPositionPnlPercent = async (backtest, symbol, currentPrice, context) => {
|
|
9289
9372
|
this.loggerService.log("strategyConnectionService getPositionPnlPercent", {
|
|
9290
9373
|
symbol,
|
|
@@ -9295,6 +9378,20 @@ class StrategyConnectionService {
|
|
|
9295
9378
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9296
9379
|
return await strategy.getPositionPnlPercent(symbol, currentPrice);
|
|
9297
9380
|
};
|
|
9381
|
+
/**
|
|
9382
|
+
* Returns the unrealized PNL in dollars for the current pending signal at currentPrice.
|
|
9383
|
+
*
|
|
9384
|
+
* Calculated as: pnlPercentage / 100 × totalInvestedCost
|
|
9385
|
+
* Accounts for partial closes, DCA entries, slippage and fees.
|
|
9386
|
+
*
|
|
9387
|
+
* Returns null if no pending signal exists.
|
|
9388
|
+
*
|
|
9389
|
+
* @param backtest - Whether running in backtest mode
|
|
9390
|
+
* @param symbol - Trading pair symbol
|
|
9391
|
+
* @param currentPrice - Current market price
|
|
9392
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9393
|
+
* @returns Promise resolving to pnl in dollars or null
|
|
9394
|
+
*/
|
|
9298
9395
|
this.getPositionPnlCost = async (backtest, symbol, currentPrice, context) => {
|
|
9299
9396
|
this.loggerService.log("strategyConnectionService getPositionPnlCost", {
|
|
9300
9397
|
symbol,
|
|
@@ -9305,6 +9402,27 @@ class StrategyConnectionService {
|
|
|
9305
9402
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9306
9403
|
return await strategy.getPositionPnlCost(symbol, currentPrice);
|
|
9307
9404
|
};
|
|
9405
|
+
/**
|
|
9406
|
+
* Returns the list of DCA entry prices for the current pending signal.
|
|
9407
|
+
*
|
|
9408
|
+
* The first element is always the original priceOpen (initial entry).
|
|
9409
|
+
* Each subsequent element is a price added by commitAverageBuy().
|
|
9410
|
+
*
|
|
9411
|
+
* Returns null if no pending signal exists.
|
|
9412
|
+
* Returns a single-element array [priceOpen] if no DCA entries were made.
|
|
9413
|
+
*
|
|
9414
|
+
* @param backtest - Whether running in backtest mode
|
|
9415
|
+
* @param symbol - Trading pair symbol
|
|
9416
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9417
|
+
* @returns Promise resolving to array of entry prices or null
|
|
9418
|
+
*
|
|
9419
|
+
* @example
|
|
9420
|
+
* ```typescript
|
|
9421
|
+
* // No DCA: [43000]
|
|
9422
|
+
* // One DCA: [43000, 42000]
|
|
9423
|
+
* // Two DCA: [43000, 42000, 41500]
|
|
9424
|
+
* ```
|
|
9425
|
+
*/
|
|
9308
9426
|
this.getPositionLevels = async (backtest, symbol, context) => {
|
|
9309
9427
|
this.loggerService.log("strategyConnectionService getPositionLevels", {
|
|
9310
9428
|
symbol,
|
|
@@ -9314,6 +9432,20 @@ class StrategyConnectionService {
|
|
|
9314
9432
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9315
9433
|
return await strategy.getPositionLevels(symbol);
|
|
9316
9434
|
};
|
|
9435
|
+
/**
|
|
9436
|
+
* Returns the list of partial closes for the current pending signal.
|
|
9437
|
+
*
|
|
9438
|
+
* Each entry records a partial profit or loss close event with its type,
|
|
9439
|
+
* percent closed, price at close, cost basis snapshot, and entry count at close.
|
|
9440
|
+
*
|
|
9441
|
+
* Returns null if no pending signal exists.
|
|
9442
|
+
* Returns an empty array if no partial closes have been executed.
|
|
9443
|
+
*
|
|
9444
|
+
* @param backtest - Whether running in backtest mode
|
|
9445
|
+
* @param symbol - Trading pair symbol
|
|
9446
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9447
|
+
* @returns Promise resolving to array of partial close records or null
|
|
9448
|
+
*/
|
|
9317
9449
|
this.getPositionPartials = async (backtest, symbol, context) => {
|
|
9318
9450
|
this.loggerService.log("strategyConnectionService getPositionPartials", {
|
|
9319
9451
|
symbol,
|
|
@@ -9323,6 +9455,27 @@ class StrategyConnectionService {
|
|
|
9323
9455
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9324
9456
|
return await strategy.getPositionPartials(symbol);
|
|
9325
9457
|
};
|
|
9458
|
+
/**
|
|
9459
|
+
* Returns the list of DCA entry prices and costs for the current pending signal.
|
|
9460
|
+
*
|
|
9461
|
+
* Each entry records the price and cost of a single position entry.
|
|
9462
|
+
* The first element is always the original priceOpen (initial entry).
|
|
9463
|
+
* Each subsequent element is an entry added by averageBuy().
|
|
9464
|
+
*
|
|
9465
|
+
* Returns null if no pending signal exists.
|
|
9466
|
+
* Returns a single-element array [{ price: priceOpen, cost }] if no DCA entries were made.
|
|
9467
|
+
*
|
|
9468
|
+
* @param backtest - Whether running in backtest mode
|
|
9469
|
+
* @param symbol - Trading pair symbol
|
|
9470
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9471
|
+
* @returns Promise resolving to array of entry records or null
|
|
9472
|
+
*
|
|
9473
|
+
* @example
|
|
9474
|
+
* ```typescript
|
|
9475
|
+
* // No DCA: [{ price: 43000, cost: 100 }]
|
|
9476
|
+
* // One DCA: [{ price: 43000, cost: 100 }, { price: 42000, cost: 100 }]
|
|
9477
|
+
* ```
|
|
9478
|
+
*/
|
|
9326
9479
|
this.getPositionEntries = async (backtest, symbol, context) => {
|
|
9327
9480
|
this.loggerService.log("strategyConnectionService getPositionEntries", {
|
|
9328
9481
|
symbol,
|
|
@@ -9330,7 +9483,8 @@ class StrategyConnectionService {
|
|
|
9330
9483
|
backtest,
|
|
9331
9484
|
});
|
|
9332
9485
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9333
|
-
|
|
9486
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
9487
|
+
return await strategy.getPositionEntries(symbol, timestamp);
|
|
9334
9488
|
};
|
|
9335
9489
|
/**
|
|
9336
9490
|
* Retrieves the currently active scheduled signal for the strategy.
|
|
@@ -9432,6 +9586,10 @@ class StrategyConnectionService {
|
|
|
9432
9586
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9433
9587
|
await strategy.waitForInit();
|
|
9434
9588
|
const tick = await strategy.tick(symbol, context.strategyName);
|
|
9589
|
+
{
|
|
9590
|
+
this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
|
|
9591
|
+
this.timeMetaService.next(symbol, tick.createdAt, context, backtest);
|
|
9592
|
+
}
|
|
9435
9593
|
{
|
|
9436
9594
|
await CALL_SIGNAL_EMIT_FN(this, tick, context, backtest, symbol);
|
|
9437
9595
|
}
|
|
@@ -9538,7 +9696,7 @@ class StrategyConnectionService {
|
|
|
9538
9696
|
}
|
|
9539
9697
|
return;
|
|
9540
9698
|
}
|
|
9541
|
-
const key = CREATE_KEY_FN$
|
|
9699
|
+
const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
9542
9700
|
if (!this.getStrategy.has(key)) {
|
|
9543
9701
|
return;
|
|
9544
9702
|
}
|
|
@@ -9607,9 +9765,9 @@ class StrategyConnectionService {
|
|
|
9607
9765
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9608
9766
|
* @returns Promise<boolean> - true if `partialProfit` would execute, false otherwise
|
|
9609
9767
|
*/
|
|
9610
|
-
this.validatePartialProfit = (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9768
|
+
this.validatePartialProfit = async (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9611
9769
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9612
|
-
return
|
|
9770
|
+
return await strategy.validatePartialProfit(symbol, percentToClose, currentPrice);
|
|
9613
9771
|
};
|
|
9614
9772
|
/**
|
|
9615
9773
|
* Executes partial close at profit level (moving toward TP).
|
|
@@ -9650,7 +9808,8 @@ class StrategyConnectionService {
|
|
|
9650
9808
|
backtest,
|
|
9651
9809
|
});
|
|
9652
9810
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9653
|
-
|
|
9811
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
9812
|
+
return await strategy.partialProfit(symbol, percentToClose, currentPrice, backtest, timestamp);
|
|
9654
9813
|
};
|
|
9655
9814
|
/**
|
|
9656
9815
|
* Checks whether `partialLoss` would succeed without executing it.
|
|
@@ -9663,9 +9822,9 @@ class StrategyConnectionService {
|
|
|
9663
9822
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9664
9823
|
* @returns Promise<boolean> - true if `partialLoss` would execute, false otherwise
|
|
9665
9824
|
*/
|
|
9666
|
-
this.validatePartialLoss = (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9825
|
+
this.validatePartialLoss = async (backtest, symbol, percentToClose, currentPrice, context) => {
|
|
9667
9826
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9668
|
-
return
|
|
9827
|
+
return await strategy.validatePartialLoss(symbol, percentToClose, currentPrice);
|
|
9669
9828
|
};
|
|
9670
9829
|
/**
|
|
9671
9830
|
* Executes partial close at loss level (moving toward SL).
|
|
@@ -9706,7 +9865,8 @@ class StrategyConnectionService {
|
|
|
9706
9865
|
backtest,
|
|
9707
9866
|
});
|
|
9708
9867
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9709
|
-
|
|
9868
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
9869
|
+
return await strategy.partialLoss(symbol, percentToClose, currentPrice, backtest, timestamp);
|
|
9710
9870
|
};
|
|
9711
9871
|
/**
|
|
9712
9872
|
* Checks whether `trailingStop` would succeed without executing it.
|
|
@@ -9719,9 +9879,9 @@ class StrategyConnectionService {
|
|
|
9719
9879
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9720
9880
|
* @returns Promise<boolean> - true if `trailingStop` would execute, false otherwise
|
|
9721
9881
|
*/
|
|
9722
|
-
this.validateTrailingStop = (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9882
|
+
this.validateTrailingStop = async (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9723
9883
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9724
|
-
return
|
|
9884
|
+
return await strategy.validateTrailingStop(symbol, percentShift, currentPrice);
|
|
9725
9885
|
};
|
|
9726
9886
|
/**
|
|
9727
9887
|
* Adjusts the trailing stop-loss distance for an active pending signal.
|
|
@@ -9773,9 +9933,9 @@ class StrategyConnectionService {
|
|
|
9773
9933
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9774
9934
|
* @returns Promise<boolean> - true if `trailingTake` would execute, false otherwise
|
|
9775
9935
|
*/
|
|
9776
|
-
this.validateTrailingTake = (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9936
|
+
this.validateTrailingTake = async (backtest, symbol, percentShift, currentPrice, context) => {
|
|
9777
9937
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9778
|
-
return
|
|
9938
|
+
return await strategy.validateTrailingTake(symbol, percentShift, currentPrice);
|
|
9779
9939
|
};
|
|
9780
9940
|
/**
|
|
9781
9941
|
* Adjusts the trailing take-profit distance for an active pending signal.
|
|
@@ -9826,9 +9986,9 @@ class StrategyConnectionService {
|
|
|
9826
9986
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9827
9987
|
* @returns Promise<boolean> - true if `breakeven` would execute, false otherwise
|
|
9828
9988
|
*/
|
|
9829
|
-
this.validateBreakeven = (backtest, symbol, currentPrice, context) => {
|
|
9989
|
+
this.validateBreakeven = async (backtest, symbol, currentPrice, context) => {
|
|
9830
9990
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9831
|
-
return
|
|
9991
|
+
return await strategy.validateBreakeven(symbol, currentPrice);
|
|
9832
9992
|
};
|
|
9833
9993
|
/**
|
|
9834
9994
|
* Delegates to ClientStrategy.breakeven() with current execution context.
|
|
@@ -9905,9 +10065,9 @@ class StrategyConnectionService {
|
|
|
9905
10065
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
9906
10066
|
* @returns Promise<boolean> - true if `averageBuy` would execute, false otherwise
|
|
9907
10067
|
*/
|
|
9908
|
-
this.validateAverageBuy = (backtest, symbol, currentPrice, context) => {
|
|
10068
|
+
this.validateAverageBuy = async (backtest, symbol, currentPrice, context) => {
|
|
9909
10069
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9910
|
-
return
|
|
10070
|
+
return await strategy.validateAverageBuy(symbol, currentPrice);
|
|
9911
10071
|
};
|
|
9912
10072
|
/**
|
|
9913
10073
|
* Adds a new DCA entry to the active pending signal.
|
|
@@ -9928,7 +10088,8 @@ class StrategyConnectionService {
|
|
|
9928
10088
|
backtest,
|
|
9929
10089
|
});
|
|
9930
10090
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
9931
|
-
|
|
10091
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
10092
|
+
return await strategy.averageBuy(symbol, currentPrice, backtest, timestamp, cost);
|
|
9932
10093
|
};
|
|
9933
10094
|
}
|
|
9934
10095
|
}
|
|
@@ -10669,7 +10830,7 @@ class ClientRisk {
|
|
|
10669
10830
|
* @param backtest - Whether running in backtest mode
|
|
10670
10831
|
* @returns Unique string key for memoization
|
|
10671
10832
|
*/
|
|
10672
|
-
const CREATE_KEY_FN$
|
|
10833
|
+
const CREATE_KEY_FN$n = (riskName, exchangeName, frameName, backtest) => {
|
|
10673
10834
|
const parts = [riskName, exchangeName];
|
|
10674
10835
|
if (frameName)
|
|
10675
10836
|
parts.push(frameName);
|
|
@@ -10768,7 +10929,7 @@ class RiskConnectionService {
|
|
|
10768
10929
|
* @param backtest - True if backtest mode, false if live mode
|
|
10769
10930
|
* @returns Configured ClientRisk instance
|
|
10770
10931
|
*/
|
|
10771
|
-
this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
10932
|
+
this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$n(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
|
|
10772
10933
|
const schema = this.riskSchemaService.get(riskName);
|
|
10773
10934
|
return new ClientRisk({
|
|
10774
10935
|
...schema,
|
|
@@ -10836,7 +10997,7 @@ class RiskConnectionService {
|
|
|
10836
10997
|
payload,
|
|
10837
10998
|
});
|
|
10838
10999
|
if (payload) {
|
|
10839
|
-
const key = CREATE_KEY_FN$
|
|
11000
|
+
const key = CREATE_KEY_FN$n(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
10840
11001
|
this.getRisk.clear(key);
|
|
10841
11002
|
}
|
|
10842
11003
|
else {
|
|
@@ -12303,7 +12464,7 @@ class ClientAction {
|
|
|
12303
12464
|
* @param backtest - Whether running in backtest mode
|
|
12304
12465
|
* @returns Unique string key for memoization
|
|
12305
12466
|
*/
|
|
12306
|
-
const CREATE_KEY_FN$
|
|
12467
|
+
const CREATE_KEY_FN$m = (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
12307
12468
|
const parts = [actionName, strategyName, exchangeName];
|
|
12308
12469
|
if (frameName)
|
|
12309
12470
|
parts.push(frameName);
|
|
@@ -12354,7 +12515,7 @@ class ActionConnectionService {
|
|
|
12354
12515
|
* @param backtest - True if backtest mode, false if live mode
|
|
12355
12516
|
* @returns Configured ClientAction instance
|
|
12356
12517
|
*/
|
|
12357
|
-
this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
12518
|
+
this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
12358
12519
|
const schema = this.actionSchemaService.get(actionName);
|
|
12359
12520
|
return new ClientAction({
|
|
12360
12521
|
...schema,
|
|
@@ -12564,7 +12725,7 @@ class ActionConnectionService {
|
|
|
12564
12725
|
await Promise.all(actions.map(async (action) => await action.dispose()));
|
|
12565
12726
|
return;
|
|
12566
12727
|
}
|
|
12567
|
-
const key = CREATE_KEY_FN$
|
|
12728
|
+
const key = CREATE_KEY_FN$m(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
12568
12729
|
if (!this.getAction.has(key)) {
|
|
12569
12730
|
return;
|
|
12570
12731
|
}
|
|
@@ -12582,7 +12743,7 @@ const METHOD_NAME_VALIDATE$2 = "exchangeCoreService validate";
|
|
|
12582
12743
|
* @param exchangeName - Exchange name
|
|
12583
12744
|
* @returns Unique string key for memoization
|
|
12584
12745
|
*/
|
|
12585
|
-
const CREATE_KEY_FN$
|
|
12746
|
+
const CREATE_KEY_FN$l = (exchangeName) => {
|
|
12586
12747
|
return exchangeName;
|
|
12587
12748
|
};
|
|
12588
12749
|
/**
|
|
@@ -12606,7 +12767,7 @@ class ExchangeCoreService {
|
|
|
12606
12767
|
* @param exchangeName - Name of the exchange to validate
|
|
12607
12768
|
* @returns Promise that resolves when validation is complete
|
|
12608
12769
|
*/
|
|
12609
|
-
this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$
|
|
12770
|
+
this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$l(exchangeName), async (exchangeName) => {
|
|
12610
12771
|
this.loggerService.log(METHOD_NAME_VALIDATE$2, {
|
|
12611
12772
|
exchangeName,
|
|
12612
12773
|
});
|
|
@@ -12858,7 +13019,7 @@ const METHOD_NAME_VALIDATE$1 = "strategyCoreService validate";
|
|
|
12858
13019
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
12859
13020
|
* @returns Unique string key for memoization
|
|
12860
13021
|
*/
|
|
12861
|
-
const CREATE_KEY_FN$
|
|
13022
|
+
const CREATE_KEY_FN$k = (context) => {
|
|
12862
13023
|
const parts = [context.strategyName, context.exchangeName];
|
|
12863
13024
|
if (context.frameName)
|
|
12864
13025
|
parts.push(context.frameName);
|
|
@@ -12890,7 +13051,7 @@ class StrategyCoreService {
|
|
|
12890
13051
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
12891
13052
|
* @returns Promise that resolves when validation is complete
|
|
12892
13053
|
*/
|
|
12893
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
13054
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$k(context), async (context) => {
|
|
12894
13055
|
this.loggerService.log(METHOD_NAME_VALIDATE$1, {
|
|
12895
13056
|
context,
|
|
12896
13057
|
});
|
|
@@ -13725,7 +13886,7 @@ class SizingGlobalService {
|
|
|
13725
13886
|
* @param context - Context with riskName, exchangeName, frameName
|
|
13726
13887
|
* @returns Unique string key for memoization
|
|
13727
13888
|
*/
|
|
13728
|
-
const CREATE_KEY_FN$
|
|
13889
|
+
const CREATE_KEY_FN$j = (context) => {
|
|
13729
13890
|
const parts = [context.riskName, context.exchangeName];
|
|
13730
13891
|
if (context.frameName)
|
|
13731
13892
|
parts.push(context.frameName);
|
|
@@ -13751,7 +13912,7 @@ class RiskGlobalService {
|
|
|
13751
13912
|
* @param payload - Payload with riskName, exchangeName and frameName
|
|
13752
13913
|
* @returns Promise that resolves when validation is complete
|
|
13753
13914
|
*/
|
|
13754
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
13915
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$j(context), async (context) => {
|
|
13755
13916
|
this.loggerService.log("riskGlobalService validate", {
|
|
13756
13917
|
context,
|
|
13757
13918
|
});
|
|
@@ -13829,7 +13990,7 @@ const METHOD_NAME_VALIDATE = "actionCoreService validate";
|
|
|
13829
13990
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
13830
13991
|
* @returns Unique string key for memoization
|
|
13831
13992
|
*/
|
|
13832
|
-
const CREATE_KEY_FN$
|
|
13993
|
+
const CREATE_KEY_FN$i = (context) => {
|
|
13833
13994
|
const parts = [context.strategyName, context.exchangeName];
|
|
13834
13995
|
if (context.frameName)
|
|
13835
13996
|
parts.push(context.frameName);
|
|
@@ -13873,7 +14034,7 @@ class ActionCoreService {
|
|
|
13873
14034
|
* @param context - Strategy execution context with strategyName, exchangeName and frameName
|
|
13874
14035
|
* @returns Promise that resolves when all validations complete
|
|
13875
14036
|
*/
|
|
13876
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
14037
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$i(context), async (context) => {
|
|
13877
14038
|
this.loggerService.log(METHOD_NAME_VALIDATE, {
|
|
13878
14039
|
context,
|
|
13879
14040
|
});
|
|
@@ -18408,7 +18569,7 @@ const Markdown = new MarkdownAdapter();
|
|
|
18408
18569
|
* @param backtest - Whether running in backtest mode
|
|
18409
18570
|
* @returns Unique string key for memoization
|
|
18410
18571
|
*/
|
|
18411
|
-
const CREATE_KEY_FN$
|
|
18572
|
+
const CREATE_KEY_FN$h = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
18412
18573
|
const parts = [symbol, strategyName, exchangeName];
|
|
18413
18574
|
if (frameName)
|
|
18414
18575
|
parts.push(frameName);
|
|
@@ -18647,7 +18808,7 @@ class BacktestMarkdownService {
|
|
|
18647
18808
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
18648
18809
|
* Each combination gets its own isolated storage instance.
|
|
18649
18810
|
*/
|
|
18650
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
18811
|
+
this.getStorage = 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));
|
|
18651
18812
|
/**
|
|
18652
18813
|
* Processes tick events and accumulates closed signals.
|
|
18653
18814
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -18804,7 +18965,7 @@ class BacktestMarkdownService {
|
|
|
18804
18965
|
payload,
|
|
18805
18966
|
});
|
|
18806
18967
|
if (payload) {
|
|
18807
|
-
const key = CREATE_KEY_FN$
|
|
18968
|
+
const key = CREATE_KEY_FN$h(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
18808
18969
|
this.getStorage.clear(key);
|
|
18809
18970
|
}
|
|
18810
18971
|
else {
|
|
@@ -18866,7 +19027,7 @@ class BacktestMarkdownService {
|
|
|
18866
19027
|
* @param backtest - Whether running in backtest mode
|
|
18867
19028
|
* @returns Unique string key for memoization
|
|
18868
19029
|
*/
|
|
18869
|
-
const CREATE_KEY_FN$
|
|
19030
|
+
const CREATE_KEY_FN$g = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
18870
19031
|
const parts = [symbol, strategyName, exchangeName];
|
|
18871
19032
|
if (frameName)
|
|
18872
19033
|
parts.push(frameName);
|
|
@@ -19349,7 +19510,7 @@ class LiveMarkdownService {
|
|
|
19349
19510
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
19350
19511
|
* Each combination gets its own isolated storage instance.
|
|
19351
19512
|
*/
|
|
19352
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
19513
|
+
this.getStorage = 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));
|
|
19353
19514
|
/**
|
|
19354
19515
|
* Subscribes to live signal emitter to receive tick events.
|
|
19355
19516
|
* Protected against multiple subscriptions.
|
|
@@ -19567,7 +19728,7 @@ class LiveMarkdownService {
|
|
|
19567
19728
|
payload,
|
|
19568
19729
|
});
|
|
19569
19730
|
if (payload) {
|
|
19570
|
-
const key = CREATE_KEY_FN$
|
|
19731
|
+
const key = CREATE_KEY_FN$g(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
19571
19732
|
this.getStorage.clear(key);
|
|
19572
19733
|
}
|
|
19573
19734
|
else {
|
|
@@ -19587,7 +19748,7 @@ class LiveMarkdownService {
|
|
|
19587
19748
|
* @param backtest - Whether running in backtest mode
|
|
19588
19749
|
* @returns Unique string key for memoization
|
|
19589
19750
|
*/
|
|
19590
|
-
const CREATE_KEY_FN$
|
|
19751
|
+
const CREATE_KEY_FN$f = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
19591
19752
|
const parts = [symbol, strategyName, exchangeName];
|
|
19592
19753
|
if (frameName)
|
|
19593
19754
|
parts.push(frameName);
|
|
@@ -19878,7 +20039,7 @@ class ScheduleMarkdownService {
|
|
|
19878
20039
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
19879
20040
|
* Each combination gets its own isolated storage instance.
|
|
19880
20041
|
*/
|
|
19881
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
20042
|
+
this.getStorage = 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));
|
|
19882
20043
|
/**
|
|
19883
20044
|
* Subscribes to signal emitter to receive scheduled signal events.
|
|
19884
20045
|
* Protected against multiple subscriptions.
|
|
@@ -20081,7 +20242,7 @@ class ScheduleMarkdownService {
|
|
|
20081
20242
|
payload,
|
|
20082
20243
|
});
|
|
20083
20244
|
if (payload) {
|
|
20084
|
-
const key = CREATE_KEY_FN$
|
|
20245
|
+
const key = CREATE_KEY_FN$f(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
20085
20246
|
this.getStorage.clear(key);
|
|
20086
20247
|
}
|
|
20087
20248
|
else {
|
|
@@ -20101,7 +20262,7 @@ class ScheduleMarkdownService {
|
|
|
20101
20262
|
* @param backtest - Whether running in backtest mode
|
|
20102
20263
|
* @returns Unique string key for memoization
|
|
20103
20264
|
*/
|
|
20104
|
-
const CREATE_KEY_FN$
|
|
20265
|
+
const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
20105
20266
|
const parts = [symbol, strategyName, exchangeName];
|
|
20106
20267
|
if (frameName)
|
|
20107
20268
|
parts.push(frameName);
|
|
@@ -20349,7 +20510,7 @@ class PerformanceMarkdownService {
|
|
|
20349
20510
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
20350
20511
|
* Each combination gets its own isolated storage instance.
|
|
20351
20512
|
*/
|
|
20352
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
20513
|
+
this.getStorage = 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));
|
|
20353
20514
|
/**
|
|
20354
20515
|
* Subscribes to performance emitter to receive performance events.
|
|
20355
20516
|
* Protected against multiple subscriptions.
|
|
@@ -20516,7 +20677,7 @@ class PerformanceMarkdownService {
|
|
|
20516
20677
|
payload,
|
|
20517
20678
|
});
|
|
20518
20679
|
if (payload) {
|
|
20519
|
-
const key = CREATE_KEY_FN$
|
|
20680
|
+
const key = CREATE_KEY_FN$e(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
20520
20681
|
this.getStorage.clear(key);
|
|
20521
20682
|
}
|
|
20522
20683
|
else {
|
|
@@ -20986,7 +21147,7 @@ class WalkerMarkdownService {
|
|
|
20986
21147
|
* @param backtest - Whether running in backtest mode
|
|
20987
21148
|
* @returns Unique string key for memoization
|
|
20988
21149
|
*/
|
|
20989
|
-
const CREATE_KEY_FN$
|
|
21150
|
+
const CREATE_KEY_FN$d = (exchangeName, frameName, backtest) => {
|
|
20990
21151
|
const parts = [exchangeName];
|
|
20991
21152
|
if (frameName)
|
|
20992
21153
|
parts.push(frameName);
|
|
@@ -21353,7 +21514,7 @@ class HeatMarkdownService {
|
|
|
21353
21514
|
* Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
|
|
21354
21515
|
* Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
|
|
21355
21516
|
*/
|
|
21356
|
-
this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
21517
|
+
this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
|
|
21357
21518
|
/**
|
|
21358
21519
|
* Subscribes to signal emitter to receive tick events.
|
|
21359
21520
|
* Protected against multiple subscriptions.
|
|
@@ -21548,7 +21709,7 @@ class HeatMarkdownService {
|
|
|
21548
21709
|
payload,
|
|
21549
21710
|
});
|
|
21550
21711
|
if (payload) {
|
|
21551
|
-
const key = CREATE_KEY_FN$
|
|
21712
|
+
const key = CREATE_KEY_FN$d(payload.exchangeName, payload.frameName, payload.backtest);
|
|
21552
21713
|
this.getStorage.clear(key);
|
|
21553
21714
|
}
|
|
21554
21715
|
else {
|
|
@@ -22579,7 +22740,7 @@ class ClientPartial {
|
|
|
22579
22740
|
* @param backtest - Whether running in backtest mode
|
|
22580
22741
|
* @returns Unique string key for memoization
|
|
22581
22742
|
*/
|
|
22582
|
-
const CREATE_KEY_FN$
|
|
22743
|
+
const CREATE_KEY_FN$c = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
22583
22744
|
/**
|
|
22584
22745
|
* Creates a callback function for emitting profit events to partialProfitSubject.
|
|
22585
22746
|
*
|
|
@@ -22701,7 +22862,7 @@ class PartialConnectionService {
|
|
|
22701
22862
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
22702
22863
|
* Value: ClientPartial instance with logger and event emitters
|
|
22703
22864
|
*/
|
|
22704
|
-
this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
22865
|
+
this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$c(signalId, backtest), (signalId, backtest) => {
|
|
22705
22866
|
return new ClientPartial({
|
|
22706
22867
|
signalId,
|
|
22707
22868
|
logger: this.loggerService,
|
|
@@ -22791,7 +22952,7 @@ class PartialConnectionService {
|
|
|
22791
22952
|
const partial = this.getPartial(data.id, backtest);
|
|
22792
22953
|
await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
22793
22954
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
22794
|
-
const key = CREATE_KEY_FN$
|
|
22955
|
+
const key = CREATE_KEY_FN$c(data.id, backtest);
|
|
22795
22956
|
this.getPartial.clear(key);
|
|
22796
22957
|
};
|
|
22797
22958
|
}
|
|
@@ -22807,7 +22968,7 @@ class PartialConnectionService {
|
|
|
22807
22968
|
* @param backtest - Whether running in backtest mode
|
|
22808
22969
|
* @returns Unique string key for memoization
|
|
22809
22970
|
*/
|
|
22810
|
-
const CREATE_KEY_FN$
|
|
22971
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
22811
22972
|
const parts = [symbol, strategyName, exchangeName];
|
|
22812
22973
|
if (frameName)
|
|
22813
22974
|
parts.push(frameName);
|
|
@@ -23032,7 +23193,7 @@ class PartialMarkdownService {
|
|
|
23032
23193
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
23033
23194
|
* Each combination gets its own isolated storage instance.
|
|
23034
23195
|
*/
|
|
23035
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
23196
|
+
this.getStorage = 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));
|
|
23036
23197
|
/**
|
|
23037
23198
|
* Subscribes to partial profit/loss signal emitters to receive events.
|
|
23038
23199
|
* Protected against multiple subscriptions.
|
|
@@ -23242,7 +23403,7 @@ class PartialMarkdownService {
|
|
|
23242
23403
|
payload,
|
|
23243
23404
|
});
|
|
23244
23405
|
if (payload) {
|
|
23245
|
-
const key = CREATE_KEY_FN$
|
|
23406
|
+
const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23246
23407
|
this.getStorage.clear(key);
|
|
23247
23408
|
}
|
|
23248
23409
|
else {
|
|
@@ -23258,7 +23419,7 @@ class PartialMarkdownService {
|
|
|
23258
23419
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
23259
23420
|
* @returns Unique string key for memoization
|
|
23260
23421
|
*/
|
|
23261
|
-
const CREATE_KEY_FN$
|
|
23422
|
+
const CREATE_KEY_FN$a = (context) => {
|
|
23262
23423
|
const parts = [context.strategyName, context.exchangeName];
|
|
23263
23424
|
if (context.frameName)
|
|
23264
23425
|
parts.push(context.frameName);
|
|
@@ -23332,7 +23493,7 @@ class PartialGlobalService {
|
|
|
23332
23493
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
23333
23494
|
* @param methodName - Name of the calling method for error tracking
|
|
23334
23495
|
*/
|
|
23335
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
23496
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$a(context), (context, methodName) => {
|
|
23336
23497
|
this.loggerService.log("partialGlobalService validate", {
|
|
23337
23498
|
context,
|
|
23338
23499
|
methodName,
|
|
@@ -23787,7 +23948,7 @@ class ClientBreakeven {
|
|
|
23787
23948
|
* @param backtest - Whether running in backtest mode
|
|
23788
23949
|
* @returns Unique string key for memoization
|
|
23789
23950
|
*/
|
|
23790
|
-
const CREATE_KEY_FN$
|
|
23951
|
+
const CREATE_KEY_FN$9 = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
23791
23952
|
/**
|
|
23792
23953
|
* Creates a callback function for emitting breakeven events to breakevenSubject.
|
|
23793
23954
|
*
|
|
@@ -23873,7 +24034,7 @@ class BreakevenConnectionService {
|
|
|
23873
24034
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
23874
24035
|
* Value: ClientBreakeven instance with logger and event emitter
|
|
23875
24036
|
*/
|
|
23876
|
-
this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
24037
|
+
this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$9(signalId, backtest), (signalId, backtest) => {
|
|
23877
24038
|
return new ClientBreakeven({
|
|
23878
24039
|
signalId,
|
|
23879
24040
|
logger: this.loggerService,
|
|
@@ -23934,7 +24095,7 @@ class BreakevenConnectionService {
|
|
|
23934
24095
|
const breakeven = this.getBreakeven(data.id, backtest);
|
|
23935
24096
|
await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
23936
24097
|
await breakeven.clear(symbol, data, priceClose, backtest);
|
|
23937
|
-
const key = CREATE_KEY_FN$
|
|
24098
|
+
const key = CREATE_KEY_FN$9(data.id, backtest);
|
|
23938
24099
|
this.getBreakeven.clear(key);
|
|
23939
24100
|
};
|
|
23940
24101
|
}
|
|
@@ -23950,7 +24111,7 @@ class BreakevenConnectionService {
|
|
|
23950
24111
|
* @param backtest - Whether running in backtest mode
|
|
23951
24112
|
* @returns Unique string key for memoization
|
|
23952
24113
|
*/
|
|
23953
|
-
const CREATE_KEY_FN$
|
|
24114
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
23954
24115
|
const parts = [symbol, strategyName, exchangeName];
|
|
23955
24116
|
if (frameName)
|
|
23956
24117
|
parts.push(frameName);
|
|
@@ -24127,7 +24288,7 @@ class BreakevenMarkdownService {
|
|
|
24127
24288
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
24128
24289
|
* Each combination gets its own isolated storage instance.
|
|
24129
24290
|
*/
|
|
24130
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24291
|
+
this.getStorage = 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));
|
|
24131
24292
|
/**
|
|
24132
24293
|
* Subscribes to breakeven signal emitter to receive events.
|
|
24133
24294
|
* Protected against multiple subscriptions.
|
|
@@ -24316,7 +24477,7 @@ class BreakevenMarkdownService {
|
|
|
24316
24477
|
payload,
|
|
24317
24478
|
});
|
|
24318
24479
|
if (payload) {
|
|
24319
|
-
const key = CREATE_KEY_FN$
|
|
24480
|
+
const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
24320
24481
|
this.getStorage.clear(key);
|
|
24321
24482
|
}
|
|
24322
24483
|
else {
|
|
@@ -24332,7 +24493,7 @@ class BreakevenMarkdownService {
|
|
|
24332
24493
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
24333
24494
|
* @returns Unique string key for memoization
|
|
24334
24495
|
*/
|
|
24335
|
-
const CREATE_KEY_FN$
|
|
24496
|
+
const CREATE_KEY_FN$7 = (context) => {
|
|
24336
24497
|
const parts = [context.strategyName, context.exchangeName];
|
|
24337
24498
|
if (context.frameName)
|
|
24338
24499
|
parts.push(context.frameName);
|
|
@@ -24406,7 +24567,7 @@ class BreakevenGlobalService {
|
|
|
24406
24567
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
24407
24568
|
* @param methodName - Name of the calling method for error tracking
|
|
24408
24569
|
*/
|
|
24409
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
24570
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$7(context), (context, methodName) => {
|
|
24410
24571
|
this.loggerService.log("breakevenGlobalService validate", {
|
|
24411
24572
|
context,
|
|
24412
24573
|
methodName,
|
|
@@ -24626,7 +24787,7 @@ class ConfigValidationService {
|
|
|
24626
24787
|
* @param backtest - Whether running in backtest mode
|
|
24627
24788
|
* @returns Unique string key for memoization
|
|
24628
24789
|
*/
|
|
24629
|
-
const CREATE_KEY_FN$
|
|
24790
|
+
const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
24630
24791
|
const parts = [symbol, strategyName, exchangeName];
|
|
24631
24792
|
if (frameName)
|
|
24632
24793
|
parts.push(frameName);
|
|
@@ -24795,7 +24956,7 @@ class RiskMarkdownService {
|
|
|
24795
24956
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
24796
24957
|
* Each combination gets its own isolated storage instance.
|
|
24797
24958
|
*/
|
|
24798
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24959
|
+
this.getStorage = 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));
|
|
24799
24960
|
/**
|
|
24800
24961
|
* Subscribes to risk rejection emitter to receive rejection events.
|
|
24801
24962
|
* Protected against multiple subscriptions.
|
|
@@ -24984,7 +25145,7 @@ class RiskMarkdownService {
|
|
|
24984
25145
|
payload,
|
|
24985
25146
|
});
|
|
24986
25147
|
if (payload) {
|
|
24987
|
-
const key = CREATE_KEY_FN$
|
|
25148
|
+
const key = CREATE_KEY_FN$6(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
24988
25149
|
this.getStorage.clear(key);
|
|
24989
25150
|
}
|
|
24990
25151
|
else {
|
|
@@ -27672,7 +27833,7 @@ class SyncReportService {
|
|
|
27672
27833
|
* @returns Colon-separated key string for memoization
|
|
27673
27834
|
* @internal
|
|
27674
27835
|
*/
|
|
27675
|
-
const CREATE_KEY_FN$
|
|
27836
|
+
const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27676
27837
|
const parts = [symbol, strategyName, exchangeName];
|
|
27677
27838
|
if (frameName)
|
|
27678
27839
|
parts.push(frameName);
|
|
@@ -27920,7 +28081,7 @@ class StrategyMarkdownService {
|
|
|
27920
28081
|
*
|
|
27921
28082
|
* @internal
|
|
27922
28083
|
*/
|
|
27923
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
28084
|
+
this.getStorage = 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));
|
|
27924
28085
|
/**
|
|
27925
28086
|
* Records a cancel-scheduled event when a scheduled signal is cancelled.
|
|
27926
28087
|
*
|
|
@@ -28488,7 +28649,7 @@ class StrategyMarkdownService {
|
|
|
28488
28649
|
this.clear = async (payload) => {
|
|
28489
28650
|
this.loggerService.log("strategyMarkdownService clear", { payload });
|
|
28490
28651
|
if (payload) {
|
|
28491
|
-
const key = CREATE_KEY_FN$
|
|
28652
|
+
const key = CREATE_KEY_FN$5(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
28492
28653
|
this.getStorage.clear(key);
|
|
28493
28654
|
}
|
|
28494
28655
|
else {
|
|
@@ -28596,7 +28757,7 @@ class StrategyMarkdownService {
|
|
|
28596
28757
|
* Creates a unique key for memoizing ReportStorage instances.
|
|
28597
28758
|
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
28598
28759
|
*/
|
|
28599
|
-
const CREATE_KEY_FN$
|
|
28760
|
+
const CREATE_KEY_FN$4 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
28600
28761
|
const parts = [symbol, strategyName, exchangeName];
|
|
28601
28762
|
if (frameName)
|
|
28602
28763
|
parts.push(frameName);
|
|
@@ -28728,7 +28889,7 @@ class ReportStorage {
|
|
|
28728
28889
|
class SyncMarkdownService {
|
|
28729
28890
|
constructor() {
|
|
28730
28891
|
this.loggerService = inject(TYPES.loggerService);
|
|
28731
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
28892
|
+
this.getStorage = 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));
|
|
28732
28893
|
this.subscribe = singleshot(() => {
|
|
28733
28894
|
this.loggerService.log("syncMarkdownService init");
|
|
28734
28895
|
const unsubscribe = syncSubject.subscribe(this.tick);
|
|
@@ -28803,7 +28964,7 @@ class SyncMarkdownService {
|
|
|
28803
28964
|
this.clear = async (payload) => {
|
|
28804
28965
|
this.loggerService.log("syncMarkdownService clear", { payload });
|
|
28805
28966
|
if (payload) {
|
|
28806
|
-
const key = CREATE_KEY_FN$
|
|
28967
|
+
const key = CREATE_KEY_FN$4(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
28807
28968
|
this.getStorage.clear(key);
|
|
28808
28969
|
}
|
|
28809
28970
|
else {
|
|
@@ -28813,6 +28974,275 @@ class SyncMarkdownService {
|
|
|
28813
28974
|
}
|
|
28814
28975
|
}
|
|
28815
28976
|
|
|
28977
|
+
const LISTEN_TIMEOUT$1 = 120000;
|
|
28978
|
+
/**
|
|
28979
|
+
* Creates a unique memoization key for a price stream.
|
|
28980
|
+
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
28981
|
+
*
|
|
28982
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
28983
|
+
* @param strategyName - Strategy identifier
|
|
28984
|
+
* @param exchangeName - Exchange identifier
|
|
28985
|
+
* @param frameName - Frame identifier (omitted when empty)
|
|
28986
|
+
* @param backtest - Whether running in backtest mode
|
|
28987
|
+
* @returns Unique string key for memoization
|
|
28988
|
+
*/
|
|
28989
|
+
const CREATE_KEY_FN$3 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
28990
|
+
const parts = [symbol, strategyName, exchangeName];
|
|
28991
|
+
if (frameName)
|
|
28992
|
+
parts.push(frameName);
|
|
28993
|
+
parts.push(backtest ? "backtest" : "live");
|
|
28994
|
+
return parts.join(":");
|
|
28995
|
+
};
|
|
28996
|
+
/**
|
|
28997
|
+
* Service for tracking the latest market price per symbol-strategy-exchange-frame combination.
|
|
28998
|
+
*
|
|
28999
|
+
* Maintains a memoized BehaviorSubject per unique key that is updated on every strategy tick
|
|
29000
|
+
* by StrategyConnectionService. Consumers can synchronously read the last known price or
|
|
29001
|
+
* await the first value if none has arrived yet.
|
|
29002
|
+
*
|
|
29003
|
+
* Primary use case: providing the current price outside of a tick execution context,
|
|
29004
|
+
* e.g., when a command is triggered between ticks.
|
|
29005
|
+
*
|
|
29006
|
+
* Features:
|
|
29007
|
+
* - One BehaviorSubject per (symbol, strategyName, exchangeName, frameName, backtest) key
|
|
29008
|
+
* - Falls back to ExchangeConnectionService.getAveragePrice when called inside an execution context
|
|
29009
|
+
* - Waits up to LISTEN_TIMEOUT ms for the first price if none is cached yet
|
|
29010
|
+
* - clear() disposes the BehaviorSubject for a single key or all keys
|
|
29011
|
+
*
|
|
29012
|
+
* Architecture:
|
|
29013
|
+
* - Registered as singleton in DI container
|
|
29014
|
+
* - Updated by StrategyConnectionService after each tick
|
|
29015
|
+
* - Cleared by Backtest/Live/Walker at strategy start to prevent stale data
|
|
29016
|
+
*
|
|
29017
|
+
* @example
|
|
29018
|
+
* ```typescript
|
|
29019
|
+
* const price = await backtest.priceMetaService.getCurrentPrice("BTCUSDT", context, false);
|
|
29020
|
+
* ```
|
|
29021
|
+
*/
|
|
29022
|
+
class PriceMetaService {
|
|
29023
|
+
constructor() {
|
|
29024
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
29025
|
+
this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
|
|
29026
|
+
/**
|
|
29027
|
+
* Memoized factory for BehaviorSubject streams keyed by (symbol, strategyName, exchangeName, frameName, backtest).
|
|
29028
|
+
*
|
|
29029
|
+
* Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
|
|
29030
|
+
* Instances are cached until clear() is called.
|
|
29031
|
+
*/
|
|
29032
|
+
this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$3(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
|
|
29033
|
+
/**
|
|
29034
|
+
* Returns the current market price for the given symbol and context.
|
|
29035
|
+
*
|
|
29036
|
+
* When called inside an execution context (i.e., during a signal handler or action),
|
|
29037
|
+
* delegates to ExchangeConnectionService.getAveragePrice for the live exchange price.
|
|
29038
|
+
* Otherwise, reads the last value from the cached BehaviorSubject. If no value has
|
|
29039
|
+
* been emitted yet, waits up to LISTEN_TIMEOUT ms for the first tick before throwing.
|
|
29040
|
+
*
|
|
29041
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29042
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29043
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29044
|
+
* @returns Current market price in quote currency
|
|
29045
|
+
* @throws When no price arrives within LISTEN_TIMEOUT ms
|
|
29046
|
+
*/
|
|
29047
|
+
this.getCurrentPrice = async (symbol, context, backtest) => {
|
|
29048
|
+
this.loggerService.log("priceMetaService getCurrentPrice", {
|
|
29049
|
+
symbol,
|
|
29050
|
+
context,
|
|
29051
|
+
backtest,
|
|
29052
|
+
});
|
|
29053
|
+
if (ExecutionContextService.hasContext() &&
|
|
29054
|
+
MethodContextService.hasContext()) {
|
|
29055
|
+
return await this.exchangeConnectionService.getAveragePrice(symbol);
|
|
29056
|
+
}
|
|
29057
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29058
|
+
if (source.data) {
|
|
29059
|
+
return source.data;
|
|
29060
|
+
}
|
|
29061
|
+
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...`);
|
|
29062
|
+
const currentPrice = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
|
|
29063
|
+
if (typeof currentPrice === "symbol") {
|
|
29064
|
+
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$3(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
29065
|
+
}
|
|
29066
|
+
return currentPrice;
|
|
29067
|
+
};
|
|
29068
|
+
/**
|
|
29069
|
+
* Pushes a new price value into the BehaviorSubject for the given key.
|
|
29070
|
+
*
|
|
29071
|
+
* Called by StrategyConnectionService after each strategy tick to keep
|
|
29072
|
+
* the cached price up to date.
|
|
29073
|
+
*
|
|
29074
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29075
|
+
* @param currentPrice - The latest price from the tick
|
|
29076
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29077
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29078
|
+
*/
|
|
29079
|
+
this.next = async (symbol, currentPrice, context, backtest) => {
|
|
29080
|
+
this.loggerService.log("priceMetaService next", {
|
|
29081
|
+
symbol,
|
|
29082
|
+
currentPrice,
|
|
29083
|
+
context,
|
|
29084
|
+
backtest,
|
|
29085
|
+
});
|
|
29086
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29087
|
+
source.next(currentPrice);
|
|
29088
|
+
};
|
|
29089
|
+
/**
|
|
29090
|
+
* Disposes cached BehaviorSubject(s) to free memory and prevent stale data.
|
|
29091
|
+
*
|
|
29092
|
+
* When called without arguments, clears all memoized price streams.
|
|
29093
|
+
* When called with a payload, clears only the stream for the specified key.
|
|
29094
|
+
* Should be called at strategy start (Backtest/Live/Walker) to reset state.
|
|
29095
|
+
*
|
|
29096
|
+
* @param payload - Optional key to clear a single stream; omit to clear all
|
|
29097
|
+
*/
|
|
29098
|
+
this.clear = (payload) => {
|
|
29099
|
+
this.loggerService.log("priceMetaService clear", {
|
|
29100
|
+
payload
|
|
29101
|
+
});
|
|
29102
|
+
if (!payload) {
|
|
29103
|
+
this.getSource.clear();
|
|
29104
|
+
return;
|
|
29105
|
+
}
|
|
29106
|
+
const key = CREATE_KEY_FN$3(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
29107
|
+
this.getSource.clear(key);
|
|
29108
|
+
};
|
|
29109
|
+
}
|
|
29110
|
+
}
|
|
29111
|
+
|
|
29112
|
+
const LISTEN_TIMEOUT = 120000;
|
|
29113
|
+
/**
|
|
29114
|
+
* Creates a unique memoization key for a timestamp stream.
|
|
29115
|
+
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
29116
|
+
*
|
|
29117
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29118
|
+
* @param strategyName - Strategy identifier
|
|
29119
|
+
* @param exchangeName - Exchange identifier
|
|
29120
|
+
* @param frameName - Frame identifier (omitted when empty)
|
|
29121
|
+
* @param backtest - Whether running in backtest mode
|
|
29122
|
+
* @returns Unique string key for memoization
|
|
29123
|
+
*/
|
|
29124
|
+
const CREATE_KEY_FN$2 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
29125
|
+
const parts = [symbol, strategyName, exchangeName];
|
|
29126
|
+
if (frameName)
|
|
29127
|
+
parts.push(frameName);
|
|
29128
|
+
parts.push(backtest ? "backtest" : "live");
|
|
29129
|
+
return parts.join(":");
|
|
29130
|
+
};
|
|
29131
|
+
/**
|
|
29132
|
+
* Service for tracking the latest candle timestamp per symbol-strategy-exchange-frame combination.
|
|
29133
|
+
*
|
|
29134
|
+
* Maintains a memoized BehaviorSubject per unique key that is updated on every strategy tick
|
|
29135
|
+
* by StrategyConnectionService. Consumers can synchronously read the last known timestamp or
|
|
29136
|
+
* await the first value if none has arrived yet.
|
|
29137
|
+
*
|
|
29138
|
+
* Primary use case: providing the current candle time outside of a tick execution context,
|
|
29139
|
+
* e.g., when a command is triggered between ticks.
|
|
29140
|
+
*
|
|
29141
|
+
* Features:
|
|
29142
|
+
* - One BehaviorSubject per (symbol, strategyName, exchangeName, frameName, backtest) key
|
|
29143
|
+
* - Falls back to ExecutionContextService.context.when when called inside an execution context
|
|
29144
|
+
* - Waits up to LISTEN_TIMEOUT ms for the first timestamp if none is cached yet
|
|
29145
|
+
* - clear() disposes the BehaviorSubject for a single key or all keys
|
|
29146
|
+
*
|
|
29147
|
+
* Architecture:
|
|
29148
|
+
* - Registered as singleton in DI container
|
|
29149
|
+
* - Updated by StrategyConnectionService after each tick
|
|
29150
|
+
* - Cleared by Backtest/Live/Walker at strategy start to prevent stale data
|
|
29151
|
+
*
|
|
29152
|
+
* @example
|
|
29153
|
+
* ```typescript
|
|
29154
|
+
* const ts = await backtest.timeMetaService.getTimestamp("BTCUSDT", context, false);
|
|
29155
|
+
* ```
|
|
29156
|
+
*/
|
|
29157
|
+
class TimeMetaService {
|
|
29158
|
+
constructor() {
|
|
29159
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
29160
|
+
this.executionContextService = inject(TYPES.executionContextService);
|
|
29161
|
+
/**
|
|
29162
|
+
* Memoized factory for BehaviorSubject streams keyed by (symbol, strategyName, exchangeName, frameName, backtest).
|
|
29163
|
+
*
|
|
29164
|
+
* Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
|
|
29165
|
+
* Instances are cached until clear() is called.
|
|
29166
|
+
*/
|
|
29167
|
+
this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$2(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
|
|
29168
|
+
/**
|
|
29169
|
+
* Returns the current candle timestamp (in milliseconds) for the given symbol and context.
|
|
29170
|
+
*
|
|
29171
|
+
* When called inside an execution context (i.e., during a signal handler or action),
|
|
29172
|
+
* reads the timestamp directly from ExecutionContextService.context.when.
|
|
29173
|
+
* Otherwise, reads the last value from the cached BehaviorSubject. If no value has
|
|
29174
|
+
* been emitted yet, waits up to LISTEN_TIMEOUT ms for the first tick before throwing.
|
|
29175
|
+
*
|
|
29176
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29177
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29178
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29179
|
+
* @returns Unix timestamp in milliseconds of the latest processed candle
|
|
29180
|
+
* @throws When no timestamp arrives within LISTEN_TIMEOUT ms
|
|
29181
|
+
*/
|
|
29182
|
+
this.getTimestamp = async (symbol, context, backtest) => {
|
|
29183
|
+
this.loggerService.log("timeMetaService getTimestamp", {
|
|
29184
|
+
symbol,
|
|
29185
|
+
context,
|
|
29186
|
+
backtest,
|
|
29187
|
+
});
|
|
29188
|
+
if (ExecutionContextService.hasContext()) {
|
|
29189
|
+
return this.executionContextService.context.when.getTime();
|
|
29190
|
+
}
|
|
29191
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29192
|
+
if (source.data) {
|
|
29193
|
+
return source.data;
|
|
29194
|
+
}
|
|
29195
|
+
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...`);
|
|
29196
|
+
const timestamp = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
|
|
29197
|
+
if (typeof timestamp === "symbol") {
|
|
29198
|
+
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$2(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
29199
|
+
}
|
|
29200
|
+
return timestamp;
|
|
29201
|
+
};
|
|
29202
|
+
/**
|
|
29203
|
+
* Pushes a new timestamp value into the BehaviorSubject for the given key.
|
|
29204
|
+
*
|
|
29205
|
+
* Called by StrategyConnectionService after each strategy tick to keep
|
|
29206
|
+
* the cached timestamp up to date.
|
|
29207
|
+
*
|
|
29208
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
29209
|
+
* @param timestamp - The createdAt timestamp from the tick (milliseconds)
|
|
29210
|
+
* @param context - Strategy, exchange, and frame identifiers
|
|
29211
|
+
* @param backtest - True if backtest mode, false if live mode
|
|
29212
|
+
*/
|
|
29213
|
+
this.next = async (symbol, timestamp, context, backtest) => {
|
|
29214
|
+
this.loggerService.log("timeMetaService next", {
|
|
29215
|
+
symbol,
|
|
29216
|
+
timestamp,
|
|
29217
|
+
context,
|
|
29218
|
+
backtest,
|
|
29219
|
+
});
|
|
29220
|
+
const source = this.getSource(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
29221
|
+
source.next(timestamp);
|
|
29222
|
+
};
|
|
29223
|
+
/**
|
|
29224
|
+
* Disposes cached BehaviorSubject(s) to free memory and prevent stale data.
|
|
29225
|
+
*
|
|
29226
|
+
* When called without arguments, clears all memoized timestamp streams.
|
|
29227
|
+
* When called with a payload, clears only the stream for the specified key.
|
|
29228
|
+
* Should be called at strategy start (Backtest/Live/Walker) to reset state.
|
|
29229
|
+
*
|
|
29230
|
+
* @param payload - Optional key to clear a single stream; omit to clear all
|
|
29231
|
+
*/
|
|
29232
|
+
this.clear = (payload) => {
|
|
29233
|
+
this.loggerService.log("timeMetaService clear", {
|
|
29234
|
+
payload,
|
|
29235
|
+
});
|
|
29236
|
+
if (!payload) {
|
|
29237
|
+
this.getSource.clear();
|
|
29238
|
+
return;
|
|
29239
|
+
}
|
|
29240
|
+
const key = CREATE_KEY_FN$2(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
29241
|
+
this.getSource.clear(key);
|
|
29242
|
+
};
|
|
29243
|
+
}
|
|
29244
|
+
}
|
|
29245
|
+
|
|
28816
29246
|
{
|
|
28817
29247
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
28818
29248
|
}
|
|
@@ -28845,6 +29275,10 @@ class SyncMarkdownService {
|
|
|
28845
29275
|
provide(TYPES.actionCoreService, () => new ActionCoreService());
|
|
28846
29276
|
provide(TYPES.frameCoreService, () => new FrameCoreService());
|
|
28847
29277
|
}
|
|
29278
|
+
{
|
|
29279
|
+
provide(TYPES.priceMetaService, () => new PriceMetaService());
|
|
29280
|
+
provide(TYPES.timeMetaService, () => new TimeMetaService());
|
|
29281
|
+
}
|
|
28848
29282
|
{
|
|
28849
29283
|
provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
|
|
28850
29284
|
provide(TYPES.riskGlobalService, () => new RiskGlobalService());
|
|
@@ -28936,6 +29370,10 @@ const coreServices = {
|
|
|
28936
29370
|
actionCoreService: inject(TYPES.actionCoreService),
|
|
28937
29371
|
frameCoreService: inject(TYPES.frameCoreService),
|
|
28938
29372
|
};
|
|
29373
|
+
const metaServices = {
|
|
29374
|
+
timeMetaService: inject(TYPES.timeMetaService),
|
|
29375
|
+
priceMetaService: inject(TYPES.priceMetaService),
|
|
29376
|
+
};
|
|
28939
29377
|
const globalServices = {
|
|
28940
29378
|
sizingGlobalService: inject(TYPES.sizingGlobalService),
|
|
28941
29379
|
riskGlobalService: inject(TYPES.riskGlobalService),
|
|
@@ -29000,6 +29438,7 @@ const backtest = {
|
|
|
29000
29438
|
...connectionServices,
|
|
29001
29439
|
...schemaServices,
|
|
29002
29440
|
...coreServices,
|
|
29441
|
+
...metaServices,
|
|
29003
29442
|
...globalServices,
|
|
29004
29443
|
...commandServices,
|
|
29005
29444
|
...logicPrivateServices,
|
|
@@ -34486,6 +34925,20 @@ class BacktestInstance {
|
|
|
34486
34925
|
frameName: context.frameName,
|
|
34487
34926
|
backtest: true,
|
|
34488
34927
|
});
|
|
34928
|
+
bt.timeMetaService.clear({
|
|
34929
|
+
symbol,
|
|
34930
|
+
strategyName: context.strategyName,
|
|
34931
|
+
exchangeName: context.exchangeName,
|
|
34932
|
+
frameName: context.frameName,
|
|
34933
|
+
backtest: true,
|
|
34934
|
+
});
|
|
34935
|
+
bt.priceMetaService.clear({
|
|
34936
|
+
symbol,
|
|
34937
|
+
strategyName: context.strategyName,
|
|
34938
|
+
exchangeName: context.exchangeName,
|
|
34939
|
+
frameName: context.frameName,
|
|
34940
|
+
backtest: true,
|
|
34941
|
+
});
|
|
34489
34942
|
}
|
|
34490
34943
|
{
|
|
34491
34944
|
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
@@ -36390,6 +36843,20 @@ class LiveInstance {
|
|
|
36390
36843
|
frameName: "",
|
|
36391
36844
|
backtest: false,
|
|
36392
36845
|
});
|
|
36846
|
+
bt.timeMetaService.clear({
|
|
36847
|
+
symbol,
|
|
36848
|
+
strategyName: context.strategyName,
|
|
36849
|
+
exchangeName: context.exchangeName,
|
|
36850
|
+
frameName: "",
|
|
36851
|
+
backtest: false,
|
|
36852
|
+
});
|
|
36853
|
+
bt.priceMetaService.clear({
|
|
36854
|
+
symbol,
|
|
36855
|
+
strategyName: context.strategyName,
|
|
36856
|
+
exchangeName: context.exchangeName,
|
|
36857
|
+
frameName: "",
|
|
36858
|
+
backtest: false,
|
|
36859
|
+
});
|
|
36393
36860
|
}
|
|
36394
36861
|
{
|
|
36395
36862
|
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
@@ -40538,6 +41005,20 @@ class WalkerInstance {
|
|
|
40538
41005
|
frameName: walkerSchema.frameName,
|
|
40539
41006
|
backtest: true,
|
|
40540
41007
|
});
|
|
41008
|
+
bt.timeMetaService.clear({
|
|
41009
|
+
symbol,
|
|
41010
|
+
strategyName,
|
|
41011
|
+
exchangeName: walkerSchema.exchangeName,
|
|
41012
|
+
frameName: walkerSchema.frameName,
|
|
41013
|
+
backtest: true,
|
|
41014
|
+
});
|
|
41015
|
+
bt.priceMetaService.clear({
|
|
41016
|
+
symbol,
|
|
41017
|
+
strategyName,
|
|
41018
|
+
exchangeName: walkerSchema.exchangeName,
|
|
41019
|
+
frameName: walkerSchema.frameName,
|
|
41020
|
+
backtest: true,
|
|
41021
|
+
});
|
|
40541
41022
|
}
|
|
40542
41023
|
{
|
|
40543
41024
|
const { riskName, riskList, actions } = bt.strategySchemaService.get(strategyName);
|
|
@@ -43505,13 +43986,15 @@ class NotificationMemoryBacktestUtils {
|
|
|
43505
43986
|
* Handles signal sync events (signal-open, signal-close).
|
|
43506
43987
|
* @param data - The signal sync contract data
|
|
43507
43988
|
*/
|
|
43508
|
-
this.handleSync = async (data) => {
|
|
43989
|
+
this.handleSync = trycatch(async (data) => {
|
|
43509
43990
|
bt.loggerService.info(NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_SYNC, {
|
|
43510
43991
|
signalId: data.signalId,
|
|
43511
43992
|
action: data.action,
|
|
43512
43993
|
});
|
|
43513
43994
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
43514
|
-
}
|
|
43995
|
+
}, {
|
|
43996
|
+
defaultValue: null,
|
|
43997
|
+
});
|
|
43515
43998
|
/**
|
|
43516
43999
|
* Handles risk rejection event.
|
|
43517
44000
|
* @param data - The risk contract data
|
|
@@ -43620,8 +44103,10 @@ class NotificationDummyBacktestUtils {
|
|
|
43620
44103
|
/**
|
|
43621
44104
|
* No-op handler for signal sync event.
|
|
43622
44105
|
*/
|
|
43623
|
-
this.handleSync = async () => {
|
|
43624
|
-
}
|
|
44106
|
+
this.handleSync = trycatch(async () => {
|
|
44107
|
+
}, {
|
|
44108
|
+
defaultValue: null,
|
|
44109
|
+
});
|
|
43625
44110
|
/**
|
|
43626
44111
|
* No-op handler for risk rejection event.
|
|
43627
44112
|
*/
|
|
@@ -43760,7 +44245,7 @@ class NotificationPersistBacktestUtils {
|
|
|
43760
44245
|
* Handles signal sync events (signal-open, signal-close).
|
|
43761
44246
|
* @param data - The signal sync contract data
|
|
43762
44247
|
*/
|
|
43763
|
-
this.handleSync = async (data) => {
|
|
44248
|
+
this.handleSync = trycatch(async (data) => {
|
|
43764
44249
|
bt.loggerService.info(NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_SYNC, {
|
|
43765
44250
|
signalId: data.signalId,
|
|
43766
44251
|
action: data.action,
|
|
@@ -43768,7 +44253,9 @@ class NotificationPersistBacktestUtils {
|
|
|
43768
44253
|
await this.waitForInit();
|
|
43769
44254
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
43770
44255
|
await this._updateNotifications();
|
|
43771
|
-
}
|
|
44256
|
+
}, {
|
|
44257
|
+
defaultValue: null,
|
|
44258
|
+
});
|
|
43772
44259
|
/**
|
|
43773
44260
|
* Handles risk rejection event.
|
|
43774
44261
|
* @param data - The risk contract data
|
|
@@ -43951,13 +44438,15 @@ class NotificationMemoryLiveUtils {
|
|
|
43951
44438
|
* Handles signal sync events (signal-open, signal-close).
|
|
43952
44439
|
* @param data - The signal sync contract data
|
|
43953
44440
|
*/
|
|
43954
|
-
this.handleSync = async (data) => {
|
|
44441
|
+
this.handleSync = trycatch(async (data) => {
|
|
43955
44442
|
bt.loggerService.info(NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_SYNC, {
|
|
43956
44443
|
signalId: data.signalId,
|
|
43957
44444
|
action: data.action,
|
|
43958
44445
|
});
|
|
43959
44446
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
43960
|
-
}
|
|
44447
|
+
}, {
|
|
44448
|
+
defaultValue: null,
|
|
44449
|
+
});
|
|
43961
44450
|
/**
|
|
43962
44451
|
* Handles risk rejection event.
|
|
43963
44452
|
* @param data - The risk contract data
|
|
@@ -44066,8 +44555,10 @@ class NotificationDummyLiveUtils {
|
|
|
44066
44555
|
/**
|
|
44067
44556
|
* No-op handler for signal sync event.
|
|
44068
44557
|
*/
|
|
44069
|
-
this.handleSync = async () => {
|
|
44070
|
-
}
|
|
44558
|
+
this.handleSync = trycatch(async () => {
|
|
44559
|
+
}, {
|
|
44560
|
+
defaultValue: null,
|
|
44561
|
+
});
|
|
44071
44562
|
/**
|
|
44072
44563
|
* No-op handler for risk rejection event.
|
|
44073
44564
|
*/
|
|
@@ -44207,7 +44698,7 @@ class NotificationPersistLiveUtils {
|
|
|
44207
44698
|
* Handles signal sync events (signal-open, signal-close).
|
|
44208
44699
|
* @param data - The signal sync contract data
|
|
44209
44700
|
*/
|
|
44210
|
-
this.handleSync = async (data) => {
|
|
44701
|
+
this.handleSync = trycatch(async (data) => {
|
|
44211
44702
|
bt.loggerService.info(NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_SYNC, {
|
|
44212
44703
|
signalId: data.signalId,
|
|
44213
44704
|
action: data.action,
|
|
@@ -44215,7 +44706,9 @@ class NotificationPersistLiveUtils {
|
|
|
44215
44706
|
await this.waitForInit();
|
|
44216
44707
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
44217
44708
|
await this._updateNotifications();
|
|
44218
|
-
}
|
|
44709
|
+
}, {
|
|
44710
|
+
defaultValue: null,
|
|
44711
|
+
});
|
|
44219
44712
|
/**
|
|
44220
44713
|
* Handles risk rejection event.
|
|
44221
44714
|
* @param data - The risk contract data
|
|
@@ -44377,9 +44870,11 @@ class NotificationBacktestAdapter {
|
|
|
44377
44870
|
* Proxies call to the underlying notification adapter.
|
|
44378
44871
|
* @param data - The signal sync contract data
|
|
44379
44872
|
*/
|
|
44380
|
-
this.handleSync = async (data) => {
|
|
44873
|
+
this.handleSync = trycatch(async (data) => {
|
|
44381
44874
|
return await this._notificationBacktestUtils.handleSync(data);
|
|
44382
|
-
}
|
|
44875
|
+
}, {
|
|
44876
|
+
defaultValue: null,
|
|
44877
|
+
});
|
|
44383
44878
|
/**
|
|
44384
44879
|
* Handles risk rejection event.
|
|
44385
44880
|
* Proxies call to the underlying notification adapter.
|
|
@@ -44521,9 +45016,11 @@ class NotificationLiveAdapter {
|
|
|
44521
45016
|
* Proxies call to the underlying notification adapter.
|
|
44522
45017
|
* @param data - The signal sync contract data
|
|
44523
45018
|
*/
|
|
44524
|
-
this.handleSync = async (data) => {
|
|
45019
|
+
this.handleSync = trycatch(async (data) => {
|
|
44525
45020
|
return await this._notificationLiveUtils.handleSync(data);
|
|
44526
|
-
}
|
|
45021
|
+
}, {
|
|
45022
|
+
defaultValue: null,
|
|
45023
|
+
});
|
|
44527
45024
|
/**
|
|
44528
45025
|
* Handles risk rejection event.
|
|
44529
45026
|
* Proxies call to the underlying notification adapter.
|