backtest-kit 9.0.0 → 9.0.2
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 +455 -230
- package/build/index.mjs +455 -231
- package/package.json +1 -1
- package/types.d.ts +172 -65
package/build/index.mjs
CHANGED
|
@@ -918,7 +918,7 @@ const LOGGER_SERVICE$7 = new LoggerService();
|
|
|
918
918
|
/** Symbol key for the singleshot waitForInit function on PersistBase instances. */
|
|
919
919
|
const BASE_WAIT_FOR_INIT_SYMBOL = Symbol("wait-for-init");
|
|
920
920
|
// Calculate step in milliseconds for candle close time validation
|
|
921
|
-
const INTERVAL_MINUTES$
|
|
921
|
+
const INTERVAL_MINUTES$a = {
|
|
922
922
|
"1m": 1,
|
|
923
923
|
"3m": 3,
|
|
924
924
|
"5m": 5,
|
|
@@ -931,7 +931,7 @@ const INTERVAL_MINUTES$9 = {
|
|
|
931
931
|
"8h": 480,
|
|
932
932
|
"1d": 1440,
|
|
933
933
|
};
|
|
934
|
-
const MS_PER_MINUTE$
|
|
934
|
+
const MS_PER_MINUTE$8 = 60000;
|
|
935
935
|
const PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_PERSIST_SIGNAL_ADAPTER = "PersistSignalUtils.usePersistSignalAdapter";
|
|
936
936
|
const PERSIST_SIGNAL_UTILS_METHOD_NAME_READ_DATA = "PersistSignalUtils.readSignalData";
|
|
937
937
|
const PERSIST_SIGNAL_UTILS_METHOD_NAME_WRITE_DATA = "PersistSignalUtils.writeSignalData";
|
|
@@ -2256,7 +2256,7 @@ class PersistCandleInstance {
|
|
|
2256
2256
|
* @returns Promise resolving to candles in order, or null on cache miss
|
|
2257
2257
|
*/
|
|
2258
2258
|
async readCandlesData(limit, sinceTimestamp, _untilTimestamp) {
|
|
2259
|
-
const stepMs = INTERVAL_MINUTES$
|
|
2259
|
+
const stepMs = INTERVAL_MINUTES$a[this.interval] * MS_PER_MINUTE$8;
|
|
2260
2260
|
const cachedCandles = [];
|
|
2261
2261
|
for (let i = 0; i < limit; i++) {
|
|
2262
2262
|
const expectedTimestamp = sinceTimestamp + i * stepMs;
|
|
@@ -2291,7 +2291,7 @@ class PersistCandleInstance {
|
|
|
2291
2291
|
* @returns Promise that resolves when all writes are complete
|
|
2292
2292
|
*/
|
|
2293
2293
|
async writeCandlesData(candles) {
|
|
2294
|
-
const stepMs = INTERVAL_MINUTES$
|
|
2294
|
+
const stepMs = INTERVAL_MINUTES$a[this.interval] * MS_PER_MINUTE$8;
|
|
2295
2295
|
const now = Date.now();
|
|
2296
2296
|
for (const candle of candles) {
|
|
2297
2297
|
const candleCloseTime = candle.timestamp + stepMs;
|
|
@@ -3177,12 +3177,12 @@ class PersistMeasureUtils {
|
|
|
3177
3177
|
* @param key - Cache key within the bucket
|
|
3178
3178
|
* @returns Promise that resolves when write is complete
|
|
3179
3179
|
*/
|
|
3180
|
-
this.writeMeasureData = async (data, bucket, key) => {
|
|
3180
|
+
this.writeMeasureData = async (data, bucket, key, when) => {
|
|
3181
3181
|
LOGGER_SERVICE$7.info(PERSIST_MEASURE_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
|
|
3182
3182
|
const isInitial = !this.getMeasureStorage.has(bucket);
|
|
3183
3183
|
const instance = this.getMeasureStorage(bucket);
|
|
3184
3184
|
await instance.waitForInit(isInitial);
|
|
3185
|
-
return instance.writeMeasureData(data, key);
|
|
3185
|
+
return instance.writeMeasureData(data, key, when);
|
|
3186
3186
|
};
|
|
3187
3187
|
/**
|
|
3188
3188
|
* Soft-deletes a measure entry in the given bucket by setting `removed: true`.
|
|
@@ -3418,12 +3418,12 @@ class PersistIntervalUtils {
|
|
|
3418
3418
|
* @param key - Marker key within the bucket
|
|
3419
3419
|
* @returns Promise that resolves when write is complete
|
|
3420
3420
|
*/
|
|
3421
|
-
this.writeIntervalData = async (data, bucket, key) => {
|
|
3421
|
+
this.writeIntervalData = async (data, bucket, key, when) => {
|
|
3422
3422
|
LOGGER_SERVICE$7.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
|
|
3423
3423
|
const isInitial = !this.getIntervalStorage.has(bucket);
|
|
3424
3424
|
const instance = this.getIntervalStorage(bucket);
|
|
3425
3425
|
await instance.waitForInit(isInitial);
|
|
3426
|
-
return instance.writeIntervalData(data, key);
|
|
3426
|
+
return instance.writeIntervalData(data, key, when);
|
|
3427
3427
|
};
|
|
3428
3428
|
/**
|
|
3429
3429
|
* Soft-deletes a marker in the given bucket by setting `removed: true`.
|
|
@@ -3561,7 +3561,7 @@ class PersistMemoryInstance {
|
|
|
3561
3561
|
* @param memoryId - Memory entry identifier
|
|
3562
3562
|
* @returns Promise that resolves when write is complete
|
|
3563
3563
|
*/
|
|
3564
|
-
async writeMemoryData(data, memoryId) {
|
|
3564
|
+
async writeMemoryData(data, memoryId, _when) {
|
|
3565
3565
|
await this._storage.writeValue(memoryId, data);
|
|
3566
3566
|
}
|
|
3567
3567
|
/**
|
|
@@ -3626,7 +3626,7 @@ class PersistMemoryDummyInstance {
|
|
|
3626
3626
|
* No-op write (discards entry).
|
|
3627
3627
|
* @returns Promise that resolves immediately
|
|
3628
3628
|
*/
|
|
3629
|
-
async writeMemoryData(_data, _memoryId) { }
|
|
3629
|
+
async writeMemoryData(_data, _memoryId, _when) { }
|
|
3630
3630
|
/**
|
|
3631
3631
|
* No-op remove.
|
|
3632
3632
|
* @returns Promise that resolves immediately
|
|
@@ -3719,19 +3719,20 @@ class PersistMemoryUtils {
|
|
|
3719
3719
|
* Writes a memory entry for the given context.
|
|
3720
3720
|
* Lazily initializes the instance on first access.
|
|
3721
3721
|
*
|
|
3722
|
-
* @param data - Entry data to persist
|
|
3722
|
+
* @param data - Entry data to persist (already carries `data.when`)
|
|
3723
3723
|
* @param signalId - Signal identifier
|
|
3724
3724
|
* @param bucketName - Bucket name
|
|
3725
3725
|
* @param memoryId - Memory entry identifier
|
|
3726
|
+
* @param when - Logical timestamp this entry belongs to (duplicates `data.when` for API consistency)
|
|
3726
3727
|
* @returns Promise that resolves when write is complete
|
|
3727
3728
|
*/
|
|
3728
|
-
this.writeMemoryData = async (data, signalId, bucketName, memoryId) => {
|
|
3729
|
+
this.writeMemoryData = async (data, signalId, bucketName, memoryId, when) => {
|
|
3729
3730
|
LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_WRITE_DATA, { signalId, bucketName, memoryId });
|
|
3730
3731
|
const key = `${signalId}:${bucketName}`;
|
|
3731
3732
|
const isInitial = !this.getMemoryStorage.has(key);
|
|
3732
3733
|
const instance = this.getMemoryStorage(signalId, bucketName);
|
|
3733
3734
|
await instance.waitForInit(isInitial);
|
|
3734
|
-
return instance.writeMemoryData(data, memoryId);
|
|
3735
|
+
return instance.writeMemoryData(data, memoryId, when);
|
|
3735
3736
|
};
|
|
3736
3737
|
/**
|
|
3737
3738
|
* Soft-deletes a memory entry for the given context.
|
|
@@ -3895,7 +3896,7 @@ class PersistRecentInstance {
|
|
|
3895
3896
|
* @param signalRow - Recent signal data to persist
|
|
3896
3897
|
* @returns Promise that resolves when write is complete
|
|
3897
3898
|
*/
|
|
3898
|
-
async writeRecentData(signalRow) {
|
|
3899
|
+
async writeRecentData(signalRow, _when) {
|
|
3899
3900
|
await this._storage.writeValue(this.symbol, signalRow);
|
|
3900
3901
|
}
|
|
3901
3902
|
}
|
|
@@ -3923,7 +3924,7 @@ class PersistRecentDummyInstance {
|
|
|
3923
3924
|
* No-op write (discards recent signal).
|
|
3924
3925
|
* @returns Promise that resolves immediately
|
|
3925
3926
|
*/
|
|
3926
|
-
async writeRecentData(_signalRow) { }
|
|
3927
|
+
async writeRecentData(_signalRow, _when) { }
|
|
3927
3928
|
}
|
|
3928
3929
|
/**
|
|
3929
3930
|
* Utility class for managing recent signal persistence.
|
|
@@ -3970,21 +3971,22 @@ class PersistRecentUtils {
|
|
|
3970
3971
|
* Writes the latest recent signal for the given context.
|
|
3971
3972
|
* Lazily initializes the instance on first access.
|
|
3972
3973
|
*
|
|
3973
|
-
* @param signalRow - Recent signal data to persist
|
|
3974
|
+
* @param signalRow - Recent signal data to persist (already carries `signalRow.timestamp`)
|
|
3974
3975
|
* @param symbol - Trading pair symbol
|
|
3975
3976
|
* @param strategyName - Strategy identifier
|
|
3976
3977
|
* @param exchangeName - Exchange identifier
|
|
3977
3978
|
* @param frameName - Frame identifier (may be empty)
|
|
3978
3979
|
* @param backtest - True for backtest mode, false for live mode
|
|
3980
|
+
* @param when - Logical timestamp this signal belongs to (duplicates `signalRow.timestamp` for API consistency)
|
|
3979
3981
|
* @returns Promise that resolves when write is complete
|
|
3980
3982
|
*/
|
|
3981
|
-
this.writeRecentData = async (signalRow, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
3983
|
+
this.writeRecentData = async (signalRow, symbol, strategyName, exchangeName, frameName, backtest, when) => {
|
|
3982
3984
|
LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA);
|
|
3983
3985
|
const key = this.createKey(symbol, strategyName, exchangeName, frameName, backtest);
|
|
3984
3986
|
const isInitial = !this.getStorage.has(key);
|
|
3985
3987
|
const instance = this.getStorage(symbol, strategyName, exchangeName, frameName, backtest);
|
|
3986
3988
|
await instance.waitForInit(isInitial);
|
|
3987
|
-
return instance.writeRecentData(signalRow);
|
|
3989
|
+
return instance.writeRecentData(signalRow, when);
|
|
3988
3990
|
};
|
|
3989
3991
|
}
|
|
3990
3992
|
/**
|
|
@@ -4098,7 +4100,7 @@ class PersistStateInstance {
|
|
|
4098
4100
|
* @param data - State data to persist
|
|
4099
4101
|
* @returns Promise that resolves when write is complete
|
|
4100
4102
|
*/
|
|
4101
|
-
async writeStateData(data) {
|
|
4103
|
+
async writeStateData(data, _when) {
|
|
4102
4104
|
await this._storage.writeValue(this.bucketName, data);
|
|
4103
4105
|
}
|
|
4104
4106
|
/**
|
|
@@ -4131,7 +4133,7 @@ class PersistStateDummyInstance {
|
|
|
4131
4133
|
* No-op write (discards state).
|
|
4132
4134
|
* @returns Promise that resolves immediately
|
|
4133
4135
|
*/
|
|
4134
|
-
async writeStateData(_data) { }
|
|
4136
|
+
async writeStateData(_data, _when) { }
|
|
4135
4137
|
/**
|
|
4136
4138
|
* No-op dispose.
|
|
4137
4139
|
*/
|
|
@@ -4196,18 +4198,19 @@ class PersistStateUtils {
|
|
|
4196
4198
|
* Writes state for the given context.
|
|
4197
4199
|
* Lazily initializes the instance on first access.
|
|
4198
4200
|
*
|
|
4199
|
-
* @param data - State data to persist
|
|
4201
|
+
* @param data - State data to persist (already carries `data.when`)
|
|
4200
4202
|
* @param signalId - Signal identifier
|
|
4201
4203
|
* @param bucketName - Bucket name
|
|
4204
|
+
* @param when - Logical timestamp this value belongs to (duplicates `data.when` for API consistency)
|
|
4202
4205
|
* @returns Promise that resolves when write is complete
|
|
4203
4206
|
*/
|
|
4204
|
-
this.writeStateData = async (data, signalId, bucketName) => {
|
|
4207
|
+
this.writeStateData = async (data, signalId, bucketName, when) => {
|
|
4205
4208
|
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA, { signalId, bucketName });
|
|
4206
4209
|
const key = `${signalId}:${bucketName}`;
|
|
4207
4210
|
const isInitial = !this.getStateStorage.has(key);
|
|
4208
4211
|
const instance = this.getStateStorage(signalId, bucketName);
|
|
4209
4212
|
await instance.waitForInit(isInitial);
|
|
4210
|
-
return instance.writeStateData(data);
|
|
4213
|
+
return instance.writeStateData(data, when);
|
|
4211
4214
|
};
|
|
4212
4215
|
/**
|
|
4213
4216
|
* Switches to PersistStateDummyInstance (all operations are no-ops).
|
|
@@ -4317,7 +4320,7 @@ class PersistSessionInstance {
|
|
|
4317
4320
|
* @param data - Session data to persist
|
|
4318
4321
|
* @returns Promise that resolves when write is complete
|
|
4319
4322
|
*/
|
|
4320
|
-
async writeSessionData(data) {
|
|
4323
|
+
async writeSessionData(data, _when) {
|
|
4321
4324
|
await this._storage.writeValue(this.frameName, data);
|
|
4322
4325
|
}
|
|
4323
4326
|
/**
|
|
@@ -4350,7 +4353,7 @@ class PersistSessionDummyInstance {
|
|
|
4350
4353
|
* No-op write (discards session data).
|
|
4351
4354
|
* @returns Promise that resolves immediately
|
|
4352
4355
|
*/
|
|
4353
|
-
async writeSessionData(_data) { }
|
|
4356
|
+
async writeSessionData(_data, _when) { }
|
|
4354
4357
|
/**
|
|
4355
4358
|
* No-op dispose.
|
|
4356
4359
|
*/
|
|
@@ -4418,19 +4421,20 @@ class PersistSessionUtils {
|
|
|
4418
4421
|
* Writes session data for the given context.
|
|
4419
4422
|
* Lazily initializes the instance on first access.
|
|
4420
4423
|
*
|
|
4421
|
-
* @param data - Session data to persist
|
|
4424
|
+
* @param data - Session data to persist (already carries `data.when`)
|
|
4422
4425
|
* @param strategyName - Strategy identifier
|
|
4423
4426
|
* @param exchangeName - Exchange identifier
|
|
4424
4427
|
* @param frameName - Frame identifier
|
|
4428
|
+
* @param when - Logical timestamp this value belongs to (duplicates `data.when` for API consistency)
|
|
4425
4429
|
* @returns Promise that resolves when write is complete
|
|
4426
4430
|
*/
|
|
4427
|
-
this.writeSessionData = async (data, strategyName, exchangeName, frameName) => {
|
|
4431
|
+
this.writeSessionData = async (data, strategyName, exchangeName, frameName, when) => {
|
|
4428
4432
|
LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_WRITE_DATA, { strategyName, exchangeName, frameName });
|
|
4429
4433
|
const key = `${strategyName}:${exchangeName}:${frameName}`;
|
|
4430
4434
|
const isInitial = !this.getSessionStorage.has(key);
|
|
4431
4435
|
const instance = this.getSessionStorage(strategyName, exchangeName, frameName);
|
|
4432
4436
|
await instance.waitForInit(isInitial);
|
|
4433
|
-
return instance.writeSessionData(data);
|
|
4437
|
+
return instance.writeSessionData(data, when);
|
|
4434
4438
|
};
|
|
4435
4439
|
/**
|
|
4436
4440
|
* Switches to PersistSessionDummyInstance (all operations are no-ops).
|
|
@@ -4609,8 +4613,8 @@ class CandleUtils {
|
|
|
4609
4613
|
}
|
|
4610
4614
|
const Candle = new CandleUtils();
|
|
4611
4615
|
|
|
4612
|
-
const MS_PER_MINUTE$
|
|
4613
|
-
const INTERVAL_MINUTES$
|
|
4616
|
+
const MS_PER_MINUTE$7 = 60000;
|
|
4617
|
+
const INTERVAL_MINUTES$9 = {
|
|
4614
4618
|
"1m": 1,
|
|
4615
4619
|
"3m": 3,
|
|
4616
4620
|
"5m": 5,
|
|
@@ -4641,7 +4645,7 @@ const INTERVAL_MINUTES$8 = {
|
|
|
4641
4645
|
* @returns Aligned timestamp rounded down to interval boundary
|
|
4642
4646
|
*/
|
|
4643
4647
|
const ALIGN_TO_INTERVAL_FN$2 = (timestamp, intervalMinutes) => {
|
|
4644
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
4648
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$7;
|
|
4645
4649
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
4646
4650
|
};
|
|
4647
4651
|
/**
|
|
@@ -4795,9 +4799,9 @@ const WRITE_CANDLES_CACHE_FN$1 = trycatch(queued(async (candles, dto, self) => {
|
|
|
4795
4799
|
* @returns Promise resolving to array of candle data
|
|
4796
4800
|
*/
|
|
4797
4801
|
const GET_CANDLES_FN$1 = async (dto, since, self) => {
|
|
4798
|
-
const step = INTERVAL_MINUTES$
|
|
4802
|
+
const step = INTERVAL_MINUTES$9[dto.interval];
|
|
4799
4803
|
const sinceTimestamp = since.getTime();
|
|
4800
|
-
const untilTimestamp = sinceTimestamp + dto.limit * step * MS_PER_MINUTE$
|
|
4804
|
+
const untilTimestamp = sinceTimestamp + dto.limit * step * MS_PER_MINUTE$7;
|
|
4801
4805
|
await Candle.acquireLock(`ClientExchange GET_CANDLES_FN symbol=${dto.symbol} interval=${dto.interval} limit=${dto.limit}`);
|
|
4802
4806
|
try {
|
|
4803
4807
|
// Try to read from cache first
|
|
@@ -4911,11 +4915,11 @@ class ClientExchange {
|
|
|
4911
4915
|
interval,
|
|
4912
4916
|
limit,
|
|
4913
4917
|
});
|
|
4914
|
-
const step = INTERVAL_MINUTES$
|
|
4918
|
+
const step = INTERVAL_MINUTES$9[interval];
|
|
4915
4919
|
if (!step) {
|
|
4916
4920
|
throw new Error(`ClientExchange unknown interval=${interval}`);
|
|
4917
4921
|
}
|
|
4918
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
4922
|
+
const stepMs = step * MS_PER_MINUTE$7;
|
|
4919
4923
|
// Align when down to interval boundary
|
|
4920
4924
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
4921
4925
|
const alignedWhen = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, step);
|
|
@@ -4992,11 +4996,11 @@ class ClientExchange {
|
|
|
4992
4996
|
if (!this.params.execution.context.backtest) {
|
|
4993
4997
|
throw new Error(`ClientExchange getNextCandles: cannot fetch future candles in live mode`);
|
|
4994
4998
|
}
|
|
4995
|
-
const step = INTERVAL_MINUTES$
|
|
4999
|
+
const step = INTERVAL_MINUTES$9[interval];
|
|
4996
5000
|
if (!step) {
|
|
4997
5001
|
throw new Error(`ClientExchange getNextCandles: unknown interval=${interval}`);
|
|
4998
5002
|
}
|
|
4999
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
5003
|
+
const stepMs = step * MS_PER_MINUTE$7;
|
|
5000
5004
|
const now = Date.now();
|
|
5001
5005
|
// Align when down to interval boundary
|
|
5002
5006
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
@@ -5181,11 +5185,11 @@ class ClientExchange {
|
|
|
5181
5185
|
sDate,
|
|
5182
5186
|
eDate,
|
|
5183
5187
|
});
|
|
5184
|
-
const step = INTERVAL_MINUTES$
|
|
5188
|
+
const step = INTERVAL_MINUTES$9[interval];
|
|
5185
5189
|
if (!step) {
|
|
5186
5190
|
throw new Error(`ClientExchange getRawCandles: unknown interval=${interval}`);
|
|
5187
5191
|
}
|
|
5188
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
5192
|
+
const stepMs = step * MS_PER_MINUTE$7;
|
|
5189
5193
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
5190
5194
|
const alignedWhen = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, step);
|
|
5191
5195
|
let sinceTimestamp;
|
|
@@ -5314,7 +5318,7 @@ class ClientExchange {
|
|
|
5314
5318
|
const alignedTo = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES);
|
|
5315
5319
|
const to = new Date(alignedTo);
|
|
5316
5320
|
const from = new Date(alignedTo -
|
|
5317
|
-
GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$
|
|
5321
|
+
GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$7);
|
|
5318
5322
|
return await this.params.getOrderBook(symbol, depth, from, to, this.params.execution.context.backtest);
|
|
5319
5323
|
}
|
|
5320
5324
|
/**
|
|
@@ -5343,7 +5347,7 @@ class ClientExchange {
|
|
|
5343
5347
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
5344
5348
|
// Align to 1-minute boundary to prevent look-ahead bias
|
|
5345
5349
|
const alignedTo = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, 1);
|
|
5346
|
-
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$
|
|
5350
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$7 - MS_PER_MINUTE$7;
|
|
5347
5351
|
// No limit: fetch a single window and return as-is
|
|
5348
5352
|
if (limit === undefined) {
|
|
5349
5353
|
const to = new Date(alignedTo);
|
|
@@ -6297,7 +6301,7 @@ const validateScheduledSignal = (signal, currentPrice) => {
|
|
|
6297
6301
|
}
|
|
6298
6302
|
};
|
|
6299
6303
|
|
|
6300
|
-
const INTERVAL_MINUTES$
|
|
6304
|
+
const INTERVAL_MINUTES$8 = {
|
|
6301
6305
|
"1m": 1,
|
|
6302
6306
|
"3m": 3,
|
|
6303
6307
|
"5m": 5,
|
|
@@ -6718,7 +6722,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
6718
6722
|
}
|
|
6719
6723
|
const currentTime = self.params.execution.context.when.getTime();
|
|
6720
6724
|
{
|
|
6721
|
-
const intervalMinutes = INTERVAL_MINUTES$
|
|
6725
|
+
const intervalMinutes = INTERVAL_MINUTES$8[self.params.interval];
|
|
6722
6726
|
const intervalMs = intervalMinutes * 60 * 1000;
|
|
6723
6727
|
const alignedTime = Math.floor(currentTime / intervalMs) * intervalMs;
|
|
6724
6728
|
// Проверяем что наступил новый интервал (по aligned timestamp)
|
|
@@ -13713,7 +13717,7 @@ class StrategyConnectionService {
|
|
|
13713
13717
|
* Maps FrameInterval to minutes for timestamp calculation.
|
|
13714
13718
|
* Used to generate timeframe arrays with proper spacing.
|
|
13715
13719
|
*/
|
|
13716
|
-
const INTERVAL_MINUTES$
|
|
13720
|
+
const INTERVAL_MINUTES$7 = {
|
|
13717
13721
|
"1m": 1,
|
|
13718
13722
|
"3m": 3,
|
|
13719
13723
|
"5m": 5,
|
|
@@ -13767,7 +13771,7 @@ const GET_TIMEFRAME_FN = async (symbol, self) => {
|
|
|
13767
13771
|
symbol,
|
|
13768
13772
|
});
|
|
13769
13773
|
const { interval, startDate, endDate } = self.params;
|
|
13770
|
-
const intervalMinutes = INTERVAL_MINUTES$
|
|
13774
|
+
const intervalMinutes = INTERVAL_MINUTES$7[interval];
|
|
13771
13775
|
if (!intervalMinutes) {
|
|
13772
13776
|
throw new Error(`ClientFrame unknown interval: ${interval}`);
|
|
13773
13777
|
}
|
|
@@ -14132,8 +14136,8 @@ const get = (object, path) => {
|
|
|
14132
14136
|
return pathArrayFlat.reduce((obj, key) => obj && obj[key], object);
|
|
14133
14137
|
};
|
|
14134
14138
|
|
|
14135
|
-
const MS_PER_MINUTE$
|
|
14136
|
-
const INTERVAL_MINUTES$
|
|
14139
|
+
const MS_PER_MINUTE$6 = 60000;
|
|
14140
|
+
const INTERVAL_MINUTES$6 = {
|
|
14137
14141
|
"1m": 1,
|
|
14138
14142
|
"3m": 3,
|
|
14139
14143
|
"5m": 5,
|
|
@@ -14164,11 +14168,11 @@ const INTERVAL_MINUTES$5 = {
|
|
|
14164
14168
|
* @returns New Date aligned down to interval boundary
|
|
14165
14169
|
*/
|
|
14166
14170
|
const alignToInterval = (date, interval) => {
|
|
14167
|
-
const minutes = INTERVAL_MINUTES$
|
|
14171
|
+
const minutes = INTERVAL_MINUTES$6[interval];
|
|
14168
14172
|
if (minutes === undefined) {
|
|
14169
14173
|
throw new Error(`alignToInterval: unknown interval=${interval}`);
|
|
14170
14174
|
}
|
|
14171
|
-
const intervalMs = minutes * MS_PER_MINUTE$
|
|
14175
|
+
const intervalMs = minutes * MS_PER_MINUTE$6;
|
|
14172
14176
|
return new Date(Math.floor(date.getTime() / intervalMs) * intervalMs);
|
|
14173
14177
|
};
|
|
14174
14178
|
|
|
@@ -19650,8 +19654,8 @@ class BacktestLogicPrivateService {
|
|
|
19650
19654
|
}
|
|
19651
19655
|
|
|
19652
19656
|
const EMITTER_CHECK_INTERVAL = 5000;
|
|
19653
|
-
const MS_PER_MINUTE$
|
|
19654
|
-
const INTERVAL_MINUTES$
|
|
19657
|
+
const MS_PER_MINUTE$5 = 60000;
|
|
19658
|
+
const INTERVAL_MINUTES$5 = {
|
|
19655
19659
|
"1m": 1,
|
|
19656
19660
|
"3m": 3,
|
|
19657
19661
|
"5m": 5,
|
|
@@ -19666,7 +19670,7 @@ const INTERVAL_MINUTES$4 = {
|
|
|
19666
19670
|
};
|
|
19667
19671
|
const createEmitter = memoize(([interval]) => `${interval}`, (interval) => {
|
|
19668
19672
|
const tickSubject = new Subject();
|
|
19669
|
-
const intervalMs = INTERVAL_MINUTES$
|
|
19673
|
+
const intervalMs = INTERVAL_MINUTES$5[interval] * MS_PER_MINUTE$5;
|
|
19670
19674
|
{
|
|
19671
19675
|
let lastAligned = Math.floor(Date.now() / intervalMs) * intervalMs;
|
|
19672
19676
|
Source.fromInterval(EMITTER_CHECK_INTERVAL)
|
|
@@ -35172,7 +35176,7 @@ const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
|
|
|
35172
35176
|
const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
|
|
35173
35177
|
const EXCHANGE_METHOD_NAME_GET_RAW_CANDLES = "ExchangeUtils.getRawCandles";
|
|
35174
35178
|
const EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES = "ExchangeUtils.getAggregatedTrades";
|
|
35175
|
-
const MS_PER_MINUTE$
|
|
35179
|
+
const MS_PER_MINUTE$4 = 60000;
|
|
35176
35180
|
/**
|
|
35177
35181
|
* Gets current timestamp from execution context if available.
|
|
35178
35182
|
* Returns current Date() if no execution context exists (non-trading GUI).
|
|
@@ -35239,7 +35243,7 @@ const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to, _backtest)
|
|
|
35239
35243
|
const DEFAULT_GET_AGGREGATED_TRADES_FN = async (_symbol, _from, _to, _backtest) => {
|
|
35240
35244
|
throw new Error(`getAggregatedTrades is not implemented for this exchange`);
|
|
35241
35245
|
};
|
|
35242
|
-
const INTERVAL_MINUTES$
|
|
35246
|
+
const INTERVAL_MINUTES$4 = {
|
|
35243
35247
|
"1m": 1,
|
|
35244
35248
|
"3m": 3,
|
|
35245
35249
|
"5m": 5,
|
|
@@ -35270,7 +35274,7 @@ const INTERVAL_MINUTES$3 = {
|
|
|
35270
35274
|
* @returns Aligned timestamp rounded down to interval boundary
|
|
35271
35275
|
*/
|
|
35272
35276
|
const ALIGN_TO_INTERVAL_FN$1 = (timestamp, intervalMinutes) => {
|
|
35273
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
35277
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$4;
|
|
35274
35278
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
35275
35279
|
};
|
|
35276
35280
|
/**
|
|
@@ -35410,11 +35414,11 @@ class ExchangeInstance {
|
|
|
35410
35414
|
limit,
|
|
35411
35415
|
});
|
|
35412
35416
|
const getCandles = this._methods.getCandles;
|
|
35413
|
-
const step = INTERVAL_MINUTES$
|
|
35417
|
+
const step = INTERVAL_MINUTES$4[interval];
|
|
35414
35418
|
if (!step) {
|
|
35415
35419
|
throw new Error(`ExchangeInstance unknown interval=${interval}`);
|
|
35416
35420
|
}
|
|
35417
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
35421
|
+
const stepMs = step * MS_PER_MINUTE$4;
|
|
35418
35422
|
// Align when down to interval boundary
|
|
35419
35423
|
const when = await GET_TIMESTAMP_FN();
|
|
35420
35424
|
const whenTimestamp = when.getTime();
|
|
@@ -35628,7 +35632,7 @@ class ExchangeInstance {
|
|
|
35628
35632
|
const when = await GET_TIMESTAMP_FN();
|
|
35629
35633
|
const alignedTo = ALIGN_TO_INTERVAL_FN$1(when.getTime(), GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES);
|
|
35630
35634
|
const to = new Date(alignedTo);
|
|
35631
|
-
const from = new Date(alignedTo - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$
|
|
35635
|
+
const from = new Date(alignedTo - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$4);
|
|
35632
35636
|
const isBacktest = await GET_BACKTEST_FN();
|
|
35633
35637
|
return await this._methods.getOrderBook(symbol, depth, from, to, isBacktest);
|
|
35634
35638
|
};
|
|
@@ -35660,7 +35664,7 @@ class ExchangeInstance {
|
|
|
35660
35664
|
const when = await GET_TIMESTAMP_FN();
|
|
35661
35665
|
// Align to 1-minute boundary to prevent look-ahead bias
|
|
35662
35666
|
const alignedTo = ALIGN_TO_INTERVAL_FN$1(when.getTime(), 1);
|
|
35663
|
-
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$
|
|
35667
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$4 - MS_PER_MINUTE$4;
|
|
35664
35668
|
const isBacktest = await GET_BACKTEST_FN();
|
|
35665
35669
|
// No limit: fetch a single window and return as-is
|
|
35666
35670
|
if (limit === undefined) {
|
|
@@ -35723,11 +35727,11 @@ class ExchangeInstance {
|
|
|
35723
35727
|
sDate,
|
|
35724
35728
|
eDate,
|
|
35725
35729
|
});
|
|
35726
|
-
const step = INTERVAL_MINUTES$
|
|
35730
|
+
const step = INTERVAL_MINUTES$4[interval];
|
|
35727
35731
|
if (!step) {
|
|
35728
35732
|
throw new Error(`ExchangeInstance getRawCandles: unknown interval=${interval}`);
|
|
35729
35733
|
}
|
|
35730
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
35734
|
+
const stepMs = step * MS_PER_MINUTE$4;
|
|
35731
35735
|
const when = await GET_TIMESTAMP_FN();
|
|
35732
35736
|
const nowTimestamp = when.getTime();
|
|
35733
35737
|
const alignedNow = ALIGN_TO_INTERVAL_FN$1(nowTimestamp, step);
|
|
@@ -36039,8 +36043,8 @@ const Exchange = new ExchangeUtils();
|
|
|
36039
36043
|
|
|
36040
36044
|
const WARM_CANDLES_METHOD_NAME = "cache.warmCandles";
|
|
36041
36045
|
const CHECK_CANDLES_METHOD_NAME = "cache.checkCandles";
|
|
36042
|
-
const MS_PER_MINUTE$
|
|
36043
|
-
const INTERVAL_MINUTES$
|
|
36046
|
+
const MS_PER_MINUTE$3 = 60000;
|
|
36047
|
+
const INTERVAL_MINUTES$3 = {
|
|
36044
36048
|
"1m": 1,
|
|
36045
36049
|
"3m": 3,
|
|
36046
36050
|
"5m": 5,
|
|
@@ -36054,7 +36058,7 @@ const INTERVAL_MINUTES$2 = {
|
|
|
36054
36058
|
"1d": 1440,
|
|
36055
36059
|
};
|
|
36056
36060
|
const ALIGN_TO_INTERVAL_FN = (timestamp, intervalMinutes) => {
|
|
36057
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
36061
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$3;
|
|
36058
36062
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
36059
36063
|
};
|
|
36060
36064
|
const BAR_LENGTH = 30;
|
|
@@ -36079,11 +36083,11 @@ const PRINT_PROGRESS_FN = (fetched, total, symbol, interval) => {
|
|
|
36079
36083
|
async function checkCandles(params) {
|
|
36080
36084
|
const { symbol, exchangeName, interval, from, to, baseDir = "./dump/data/candle" } = params;
|
|
36081
36085
|
backtest.loggerService.info(CHECK_CANDLES_METHOD_NAME, params);
|
|
36082
|
-
const step = INTERVAL_MINUTES$
|
|
36086
|
+
const step = INTERVAL_MINUTES$3[interval];
|
|
36083
36087
|
if (!step) {
|
|
36084
36088
|
throw new Error(`checkCandles: unsupported interval=${interval}`);
|
|
36085
36089
|
}
|
|
36086
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
36090
|
+
const stepMs = step * MS_PER_MINUTE$3;
|
|
36087
36091
|
const dir = join(baseDir, exchangeName, symbol, interval);
|
|
36088
36092
|
const fromTs = ALIGN_TO_INTERVAL_FN(from.getTime(), step);
|
|
36089
36093
|
const toTs = ALIGN_TO_INTERVAL_FN(to.getTime(), step);
|
|
@@ -36153,11 +36157,11 @@ async function warmCandles(params) {
|
|
|
36153
36157
|
from,
|
|
36154
36158
|
to,
|
|
36155
36159
|
});
|
|
36156
|
-
const step = INTERVAL_MINUTES$
|
|
36160
|
+
const step = INTERVAL_MINUTES$3[interval];
|
|
36157
36161
|
if (!step) {
|
|
36158
36162
|
throw new Error(`warmCandles: unsupported interval=${interval}`);
|
|
36159
36163
|
}
|
|
36160
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
36164
|
+
const stepMs = step * MS_PER_MINUTE$3;
|
|
36161
36165
|
const instance = new ExchangeInstance(exchangeName);
|
|
36162
36166
|
const sinceTimestamp = ALIGN_TO_INTERVAL_FN(from.getTime(), step);
|
|
36163
36167
|
const untilTimestamp = ALIGN_TO_INTERVAL_FN(to.getTime(), step);
|
|
@@ -48500,18 +48504,21 @@ class RecentPersistBacktestUtils {
|
|
|
48500
48504
|
backtest.loggerService.info(RECENT_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
48501
48505
|
signalId: event.data.id,
|
|
48502
48506
|
});
|
|
48503
|
-
await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
48507
|
+
await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest, new Date(event.data.timestamp));
|
|
48504
48508
|
};
|
|
48505
48509
|
/**
|
|
48506
48510
|
* Retrieves the latest persisted signal for the given context.
|
|
48511
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48512
|
+
* (look-ahead bias protection).
|
|
48507
48513
|
* @param symbol - Trading pair symbol
|
|
48508
48514
|
* @param strategyName - Strategy identifier
|
|
48509
48515
|
* @param exchangeName - Exchange identifier
|
|
48510
48516
|
* @param frameName - Frame identifier
|
|
48511
48517
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48512
|
-
* @
|
|
48518
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48519
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48513
48520
|
*/
|
|
48514
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48521
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48515
48522
|
backtest.loggerService.info(RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48516
48523
|
symbol,
|
|
48517
48524
|
strategyName,
|
|
@@ -48519,20 +48526,26 @@ class RecentPersistBacktestUtils {
|
|
|
48519
48526
|
frameName,
|
|
48520
48527
|
backtest: backtest$1,
|
|
48521
48528
|
});
|
|
48522
|
-
|
|
48529
|
+
const signal = await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48530
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48531
|
+
return null;
|
|
48532
|
+
}
|
|
48533
|
+
return signal;
|
|
48523
48534
|
};
|
|
48524
48535
|
/**
|
|
48525
48536
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48526
|
-
*
|
|
48537
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48538
|
+
* the requested one is treated as not yet visible.
|
|
48539
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48527
48540
|
* @param symbol - Trading pair symbol
|
|
48528
48541
|
* @param strategyName - Strategy identifier
|
|
48529
48542
|
* @param exchangeName - Exchange identifier
|
|
48530
48543
|
* @param frameName - Frame identifier
|
|
48531
48544
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48532
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48545
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48533
48546
|
*/
|
|
48534
48547
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48535
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48548
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48536
48549
|
if (!signal) {
|
|
48537
48550
|
return null;
|
|
48538
48551
|
}
|
|
@@ -48568,30 +48581,39 @@ class RecentMemoryBacktestUtils {
|
|
|
48568
48581
|
};
|
|
48569
48582
|
/**
|
|
48570
48583
|
* Retrieves the latest in-memory signal for the given context.
|
|
48584
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48585
|
+
* (look-ahead bias protection).
|
|
48571
48586
|
* @param symbol - Trading pair symbol
|
|
48572
48587
|
* @param strategyName - Strategy identifier
|
|
48573
48588
|
* @param exchangeName - Exchange identifier
|
|
48574
48589
|
* @param frameName - Frame identifier
|
|
48575
48590
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48576
|
-
* @
|
|
48591
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48592
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48577
48593
|
*/
|
|
48578
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48594
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48579
48595
|
const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48580
48596
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
48581
|
-
|
|
48597
|
+
const signal = this._signals.get(key) ?? null;
|
|
48598
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48599
|
+
return null;
|
|
48600
|
+
}
|
|
48601
|
+
return signal;
|
|
48582
48602
|
};
|
|
48583
48603
|
/**
|
|
48584
48604
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48585
|
-
*
|
|
48605
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48606
|
+
* the requested one is treated as not yet visible.
|
|
48607
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48586
48608
|
* @param symbol - Trading pair symbol
|
|
48587
48609
|
* @param strategyName - Strategy identifier
|
|
48588
48610
|
* @param exchangeName - Exchange identifier
|
|
48589
48611
|
* @param frameName - Frame identifier
|
|
48590
48612
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48591
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48613
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48592
48614
|
*/
|
|
48593
48615
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48594
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48616
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48595
48617
|
if (!signal) {
|
|
48596
48618
|
return null;
|
|
48597
48619
|
}
|
|
@@ -48619,18 +48641,21 @@ class RecentPersistLiveUtils {
|
|
|
48619
48641
|
backtest.loggerService.info(RECENT_PERSIST_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
48620
48642
|
signalId: event.data.id,
|
|
48621
48643
|
});
|
|
48622
|
-
await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
48644
|
+
await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest, new Date(event.data.timestamp));
|
|
48623
48645
|
};
|
|
48624
48646
|
/**
|
|
48625
48647
|
* Retrieves the latest persisted signal for the given context.
|
|
48648
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48649
|
+
* (look-ahead bias protection).
|
|
48626
48650
|
* @param symbol - Trading pair symbol
|
|
48627
48651
|
* @param strategyName - Strategy identifier
|
|
48628
48652
|
* @param exchangeName - Exchange identifier
|
|
48629
48653
|
* @param frameName - Frame identifier
|
|
48630
48654
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48631
|
-
* @
|
|
48655
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48656
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48632
48657
|
*/
|
|
48633
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48658
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48634
48659
|
backtest.loggerService.info(RECENT_PERSIST_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48635
48660
|
symbol,
|
|
48636
48661
|
strategyName,
|
|
@@ -48638,20 +48663,26 @@ class RecentPersistLiveUtils {
|
|
|
48638
48663
|
frameName,
|
|
48639
48664
|
backtest: backtest$1,
|
|
48640
48665
|
});
|
|
48641
|
-
|
|
48666
|
+
const signal = await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48667
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48668
|
+
return null;
|
|
48669
|
+
}
|
|
48670
|
+
return signal;
|
|
48642
48671
|
};
|
|
48643
48672
|
/**
|
|
48644
48673
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48645
|
-
*
|
|
48674
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48675
|
+
* the requested one is treated as not yet visible.
|
|
48676
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48646
48677
|
* @param symbol - Trading pair symbol
|
|
48647
48678
|
* @param strategyName - Strategy identifier
|
|
48648
48679
|
* @param exchangeName - Exchange identifier
|
|
48649
48680
|
* @param frameName - Frame identifier
|
|
48650
48681
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48651
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48682
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48652
48683
|
*/
|
|
48653
48684
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48654
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48685
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48655
48686
|
if (!signal) {
|
|
48656
48687
|
return null;
|
|
48657
48688
|
}
|
|
@@ -48687,30 +48718,39 @@ class RecentMemoryLiveUtils {
|
|
|
48687
48718
|
};
|
|
48688
48719
|
/**
|
|
48689
48720
|
* Retrieves the latest in-memory signal for the given context.
|
|
48721
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48722
|
+
* (look-ahead bias protection).
|
|
48690
48723
|
* @param symbol - Trading pair symbol
|
|
48691
48724
|
* @param strategyName - Strategy identifier
|
|
48692
48725
|
* @param exchangeName - Exchange identifier
|
|
48693
48726
|
* @param frameName - Frame identifier
|
|
48694
48727
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48695
|
-
* @
|
|
48728
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48729
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48696
48730
|
*/
|
|
48697
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48731
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48698
48732
|
const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48699
48733
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
48700
|
-
|
|
48734
|
+
const signal = this._signals.get(key) ?? null;
|
|
48735
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48736
|
+
return null;
|
|
48737
|
+
}
|
|
48738
|
+
return signal;
|
|
48701
48739
|
};
|
|
48702
48740
|
/**
|
|
48703
48741
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48704
|
-
*
|
|
48742
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48743
|
+
* the requested one is treated as not yet visible.
|
|
48744
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48705
48745
|
* @param symbol - Trading pair symbol
|
|
48706
48746
|
* @param strategyName - Strategy identifier
|
|
48707
48747
|
* @param exchangeName - Exchange identifier
|
|
48708
48748
|
* @param frameName - Frame identifier
|
|
48709
48749
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48710
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48750
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48711
48751
|
*/
|
|
48712
48752
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48713
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48753
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48714
48754
|
if (!signal) {
|
|
48715
48755
|
return null;
|
|
48716
48756
|
}
|
|
@@ -48750,9 +48790,10 @@ class RecentBacktestAdapter {
|
|
|
48750
48790
|
* @param exchangeName - Exchange identifier
|
|
48751
48791
|
* @param frameName - Frame identifier
|
|
48752
48792
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48753
|
-
* @
|
|
48793
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48794
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48754
48795
|
*/
|
|
48755
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48796
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48756
48797
|
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48757
48798
|
symbol,
|
|
48758
48799
|
strategyName,
|
|
@@ -48760,18 +48801,20 @@ class RecentBacktestAdapter {
|
|
|
48760
48801
|
frameName,
|
|
48761
48802
|
backtest: backtest$1,
|
|
48762
48803
|
});
|
|
48763
|
-
return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48804
|
+
return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
|
|
48764
48805
|
};
|
|
48765
48806
|
/**
|
|
48766
48807
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48767
|
-
* Proxies call to the underlying storage adapter.
|
|
48768
|
-
*
|
|
48808
|
+
* Proxies call to the underlying storage adapter. `timestamp` doubles as the
|
|
48809
|
+
* look-ahead cutoff — a signal whose `timestamp` exceeds the requested one is
|
|
48810
|
+
* treated as not yet visible.
|
|
48811
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48769
48812
|
* @param symbol - Trading pair symbol
|
|
48770
48813
|
* @param strategyName - Strategy identifier
|
|
48771
48814
|
* @param exchangeName - Exchange identifier
|
|
48772
48815
|
* @param frameName - Frame identifier
|
|
48773
48816
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48774
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48817
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48775
48818
|
*/
|
|
48776
48819
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48777
48820
|
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
@@ -48782,7 +48825,7 @@ class RecentBacktestAdapter {
|
|
|
48782
48825
|
backtest: backtest$1,
|
|
48783
48826
|
timestamp,
|
|
48784
48827
|
});
|
|
48785
|
-
const signal = await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48828
|
+
const signal = await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
|
|
48786
48829
|
if (!signal) {
|
|
48787
48830
|
return null;
|
|
48788
48831
|
}
|
|
@@ -48854,9 +48897,10 @@ class RecentLiveAdapter {
|
|
|
48854
48897
|
* @param exchangeName - Exchange identifier
|
|
48855
48898
|
* @param frameName - Frame identifier
|
|
48856
48899
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48857
|
-
* @
|
|
48900
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48901
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48858
48902
|
*/
|
|
48859
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48903
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48860
48904
|
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48861
48905
|
symbol,
|
|
48862
48906
|
strategyName,
|
|
@@ -48864,18 +48908,20 @@ class RecentLiveAdapter {
|
|
|
48864
48908
|
frameName,
|
|
48865
48909
|
backtest: backtest$1,
|
|
48866
48910
|
});
|
|
48867
|
-
return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48911
|
+
return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
|
|
48868
48912
|
};
|
|
48869
48913
|
/**
|
|
48870
48914
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48871
|
-
* Proxies call to the underlying storage adapter.
|
|
48872
|
-
*
|
|
48915
|
+
* Proxies call to the underlying storage adapter. `timestamp` doubles as the
|
|
48916
|
+
* look-ahead cutoff — a signal whose `timestamp` exceeds the requested one is
|
|
48917
|
+
* treated as not yet visible.
|
|
48918
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48873
48919
|
* @param symbol - Trading pair symbol
|
|
48874
48920
|
* @param strategyName - Strategy identifier
|
|
48875
48921
|
* @param exchangeName - Exchange identifier
|
|
48876
48922
|
* @param frameName - Frame identifier
|
|
48877
48923
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48878
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48924
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48879
48925
|
*/
|
|
48880
48926
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48881
48927
|
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
@@ -48886,7 +48932,7 @@ class RecentLiveAdapter {
|
|
|
48886
48932
|
backtest: backtest$1,
|
|
48887
48933
|
timestamp,
|
|
48888
48934
|
});
|
|
48889
|
-
const signal = await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48935
|
+
const signal = await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
|
|
48890
48936
|
if (!signal) {
|
|
48891
48937
|
return null;
|
|
48892
48938
|
}
|
|
@@ -48976,14 +49022,16 @@ class RecentAdapter {
|
|
|
48976
49022
|
/**
|
|
48977
49023
|
* Retrieves the latest active signal for the given symbol and context.
|
|
48978
49024
|
* Searches backtest storage first, then live storage.
|
|
49025
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
49026
|
+
* (look-ahead bias protection).
|
|
48979
49027
|
*
|
|
48980
49028
|
* @param symbol - Trading pair symbol
|
|
48981
49029
|
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
48982
|
-
* @param
|
|
48983
|
-
* @returns The latest signal or null if not found
|
|
49030
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49031
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48984
49032
|
* @throws Error if RecentAdapter is not enabled
|
|
48985
49033
|
*/
|
|
48986
|
-
this.getLatestSignal = async (symbol, context) => {
|
|
49034
|
+
this.getLatestSignal = async (symbol, context, when) => {
|
|
48987
49035
|
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48988
49036
|
symbol,
|
|
48989
49037
|
context,
|
|
@@ -48992,10 +49040,10 @@ class RecentAdapter {
|
|
|
48992
49040
|
throw new Error("RecentAdapter is not enabled. Call enable() first.");
|
|
48993
49041
|
}
|
|
48994
49042
|
let result = null;
|
|
48995
|
-
if (result = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true)) {
|
|
49043
|
+
if (result = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true, when)) {
|
|
48996
49044
|
return result;
|
|
48997
49045
|
}
|
|
48998
|
-
if (result = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false)) {
|
|
49046
|
+
if (result = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false, when)) {
|
|
48999
49047
|
return result;
|
|
49000
49048
|
}
|
|
49001
49049
|
return null;
|
|
@@ -49003,13 +49051,16 @@ class RecentAdapter {
|
|
|
49003
49051
|
/**
|
|
49004
49052
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
49005
49053
|
* Searches backtest storage first, then live storage.
|
|
49006
|
-
*
|
|
49054
|
+
* `when` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
49055
|
+
* `when.getTime()` is treated as not yet visible — and as the "now" against
|
|
49056
|
+
* which elapsed minutes are computed.
|
|
49007
49057
|
* @param symbol - Trading pair symbol
|
|
49008
49058
|
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
49009
|
-
* @
|
|
49059
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead cutoff + "now")
|
|
49060
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
49010
49061
|
* @throws Error if RecentAdapter is not enabled
|
|
49011
49062
|
*/
|
|
49012
|
-
this.getMinutesSinceLatestSignalCreated = async (symbol, context) => {
|
|
49063
|
+
this.getMinutesSinceLatestSignalCreated = async (symbol, context, when) => {
|
|
49013
49064
|
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL, {
|
|
49014
49065
|
symbol,
|
|
49015
49066
|
context,
|
|
@@ -49018,13 +49069,11 @@ class RecentAdapter {
|
|
|
49018
49069
|
throw new Error("RecentAdapter is not enabled. Call enable() first.");
|
|
49019
49070
|
}
|
|
49020
49071
|
let signal = null;
|
|
49021
|
-
if (signal = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true)) {
|
|
49022
|
-
|
|
49023
|
-
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
49072
|
+
if (signal = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true, when)) {
|
|
49073
|
+
return Math.floor((when.getTime() - signal.timestamp) / (1000 * 60));
|
|
49024
49074
|
}
|
|
49025
|
-
if (signal = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false)) {
|
|
49026
|
-
|
|
49027
|
-
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
49075
|
+
if (signal = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false, when)) {
|
|
49076
|
+
return Math.floor((when.getTime() - signal.timestamp) / (1000 * 60));
|
|
49028
49077
|
}
|
|
49029
49078
|
return null;
|
|
49030
49079
|
};
|
|
@@ -49089,41 +49138,54 @@ class StateLocalInstance {
|
|
|
49089
49138
|
this.initialValue = initialValue;
|
|
49090
49139
|
this.signalId = signalId;
|
|
49091
49140
|
this.bucketName = bucketName;
|
|
49141
|
+
this._when = 0;
|
|
49092
49142
|
/**
|
|
49093
49143
|
* Initializes _value from initialValue - local state needs no async setup.
|
|
49094
49144
|
* @returns Promise that resolves immediately
|
|
49095
49145
|
*/
|
|
49096
49146
|
this.waitForInit = singleshot(async (_initial) => {
|
|
49097
49147
|
this._value = this.initialValue;
|
|
49148
|
+
this._when = 0;
|
|
49098
49149
|
});
|
|
49099
49150
|
/**
|
|
49100
49151
|
* Update the in-memory state value.
|
|
49152
|
+
* Records `when` so future reads with a smaller `when` see `initialValue`.
|
|
49153
|
+
* The dispatch updater receives the look-ahead-guarded current value.
|
|
49101
49154
|
* @param dispatch - New value or updater function receiving current value
|
|
49155
|
+
* @param when - Logical timestamp this value belongs to
|
|
49102
49156
|
* @returns Updated state value
|
|
49103
49157
|
*/
|
|
49104
|
-
this.setState = queued(async (dispatch) => {
|
|
49158
|
+
this.setState = queued(async (dispatch, when) => {
|
|
49105
49159
|
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
49106
49160
|
signalId: this.signalId,
|
|
49107
49161
|
bucketName: this.bucketName,
|
|
49108
49162
|
});
|
|
49109
49163
|
if (typeof dispatch === "function") {
|
|
49110
|
-
this.
|
|
49164
|
+
const prev = this._when > when.getTime() ? this.initialValue : this._value;
|
|
49165
|
+
this._value = await dispatch(prev);
|
|
49111
49166
|
}
|
|
49112
49167
|
else {
|
|
49113
49168
|
this._value = dispatch;
|
|
49114
49169
|
}
|
|
49170
|
+
this._when = when.getTime();
|
|
49115
49171
|
return this._value;
|
|
49116
49172
|
});
|
|
49117
49173
|
}
|
|
49118
49174
|
/**
|
|
49119
49175
|
* Read the current in-memory state value.
|
|
49176
|
+
* Returns `initialValue` when the stored `when` is greater than the requested `when`
|
|
49177
|
+
* (look-ahead bias protection).
|
|
49178
|
+
* @param when - Logical timestamp at which the read is happening
|
|
49120
49179
|
* @returns Current state value
|
|
49121
49180
|
*/
|
|
49122
|
-
async getState() {
|
|
49181
|
+
async getState(when) {
|
|
49123
49182
|
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
49124
49183
|
signalId: this.signalId,
|
|
49125
49184
|
bucketName: this.bucketName,
|
|
49126
49185
|
});
|
|
49186
|
+
if (this._when > when.getTime()) {
|
|
49187
|
+
return this.initialValue;
|
|
49188
|
+
}
|
|
49127
49189
|
return this._value;
|
|
49128
49190
|
}
|
|
49129
49191
|
/** Releases resources held by this instance. */
|
|
@@ -49157,14 +49219,14 @@ class StateDummyInstance {
|
|
|
49157
49219
|
* No-op read - always returns initialValue.
|
|
49158
49220
|
* @returns initialValue
|
|
49159
49221
|
*/
|
|
49160
|
-
async getState() {
|
|
49222
|
+
async getState(_when) {
|
|
49161
49223
|
return this.initialValue;
|
|
49162
49224
|
}
|
|
49163
49225
|
/**
|
|
49164
49226
|
* No-op write - discards the value and returns initialValue.
|
|
49165
49227
|
* @returns initialValue
|
|
49166
49228
|
*/
|
|
49167
|
-
async setState(_dispatch) {
|
|
49229
|
+
async setState(_dispatch, _when) {
|
|
49168
49230
|
return this.initialValue;
|
|
49169
49231
|
}
|
|
49170
49232
|
/** No-op. */
|
|
@@ -49190,6 +49252,7 @@ class StatePersistInstance {
|
|
|
49190
49252
|
this.initialValue = initialValue;
|
|
49191
49253
|
this.signalId = signalId;
|
|
49192
49254
|
this.bucketName = bucketName;
|
|
49255
|
+
this._when = 0;
|
|
49193
49256
|
/**
|
|
49194
49257
|
* Initialize persistence storage and restore state from disk.
|
|
49195
49258
|
* @param initial - Whether this is the first initialization
|
|
@@ -49204,40 +49267,54 @@ class StatePersistInstance {
|
|
|
49204
49267
|
const data = await PersistStateAdapter.readStateData(this.signalId, this.bucketName);
|
|
49205
49268
|
if (data) {
|
|
49206
49269
|
this._value = data.data;
|
|
49270
|
+
this._when = data.when;
|
|
49207
49271
|
return;
|
|
49208
49272
|
}
|
|
49209
49273
|
this._value = this.initialValue;
|
|
49274
|
+
this._when = 0;
|
|
49210
49275
|
});
|
|
49211
49276
|
/**
|
|
49212
49277
|
* Update state and persist to disk atomically.
|
|
49278
|
+
* A write with a smaller `when` overwrites an existing record — that lets a
|
|
49279
|
+
* restarted backtest reset live-written state without breaking live.
|
|
49280
|
+
* The dispatch updater receives the look-ahead-guarded current value.
|
|
49213
49281
|
* @param dispatch - New value or updater function receiving current value
|
|
49282
|
+
* @param when - Logical timestamp this value belongs to
|
|
49214
49283
|
* @returns Updated state value
|
|
49215
49284
|
*/
|
|
49216
|
-
this.setState = queued(async (dispatch) => {
|
|
49285
|
+
this.setState = queued(async (dispatch, when) => {
|
|
49217
49286
|
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
49218
49287
|
signalId: this.signalId,
|
|
49219
49288
|
bucketName: this.bucketName,
|
|
49220
49289
|
});
|
|
49221
49290
|
if (typeof dispatch === "function") {
|
|
49222
|
-
this.
|
|
49291
|
+
const prev = this._when > when.getTime() ? this.initialValue : this._value;
|
|
49292
|
+
this._value = await dispatch(prev);
|
|
49223
49293
|
}
|
|
49224
49294
|
else {
|
|
49225
49295
|
this._value = dispatch;
|
|
49226
49296
|
}
|
|
49297
|
+
this._when = when.getTime();
|
|
49227
49298
|
const id = CREATE_KEY_FN$6(this.signalId, this.bucketName);
|
|
49228
|
-
await PersistStateAdapter.writeStateData({ id, data: this._value }, this.signalId, this.bucketName);
|
|
49299
|
+
await PersistStateAdapter.writeStateData({ id, data: this._value, when: this._when }, this.signalId, this.bucketName, when);
|
|
49229
49300
|
return this._value;
|
|
49230
49301
|
});
|
|
49231
49302
|
}
|
|
49232
49303
|
/**
|
|
49233
49304
|
* Read the current persisted state value.
|
|
49305
|
+
* Returns `initialValue` when the stored `when` is greater than the requested `when`
|
|
49306
|
+
* (look-ahead bias protection).
|
|
49307
|
+
* @param when - Logical timestamp at which the read is happening
|
|
49234
49308
|
* @returns Current state value
|
|
49235
49309
|
*/
|
|
49236
|
-
async getState() {
|
|
49310
|
+
async getState(when) {
|
|
49237
49311
|
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
49238
49312
|
signalId: this.signalId,
|
|
49239
49313
|
bucketName: this.bucketName,
|
|
49240
49314
|
});
|
|
49315
|
+
if (this._when > when.getTime()) {
|
|
49316
|
+
return this.initialValue;
|
|
49317
|
+
}
|
|
49241
49318
|
return this._value;
|
|
49242
49319
|
}
|
|
49243
49320
|
/** Releases resources held by this instance. */
|
|
@@ -49290,6 +49367,7 @@ class StateBacktestAdapter {
|
|
|
49290
49367
|
* @param dto.signalId - Signal identifier
|
|
49291
49368
|
* @param dto.bucketName - Bucket name
|
|
49292
49369
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49370
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49293
49371
|
* @returns Current state value
|
|
49294
49372
|
*/
|
|
49295
49373
|
this.getState = async (dto) => {
|
|
@@ -49301,7 +49379,7 @@ class StateBacktestAdapter {
|
|
|
49301
49379
|
const isInitial = !this.getInstance.has(key);
|
|
49302
49380
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49303
49381
|
await instance.waitForInit(isInitial);
|
|
49304
|
-
return await instance.getState();
|
|
49382
|
+
return await instance.getState(dto.when);
|
|
49305
49383
|
};
|
|
49306
49384
|
/**
|
|
49307
49385
|
* Update the state value for a signal.
|
|
@@ -49309,6 +49387,7 @@ class StateBacktestAdapter {
|
|
|
49309
49387
|
* @param dto.signalId - Signal identifier
|
|
49310
49388
|
* @param dto.bucketName - Bucket name
|
|
49311
49389
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49390
|
+
* @param dto.when - Logical timestamp this value belongs to
|
|
49312
49391
|
* @returns Updated state value
|
|
49313
49392
|
*/
|
|
49314
49393
|
this.setState = async (dispatch, dto) => {
|
|
@@ -49320,7 +49399,7 @@ class StateBacktestAdapter {
|
|
|
49320
49399
|
const isInitial = !this.getInstance.has(key);
|
|
49321
49400
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49322
49401
|
await instance.waitForInit(isInitial);
|
|
49323
|
-
return await instance.setState(dispatch);
|
|
49402
|
+
return await instance.setState(dispatch, dto.when);
|
|
49324
49403
|
};
|
|
49325
49404
|
/**
|
|
49326
49405
|
* Switches to in-memory adapter (default).
|
|
@@ -49405,6 +49484,7 @@ class StateLiveAdapter {
|
|
|
49405
49484
|
* @param dto.signalId - Signal identifier
|
|
49406
49485
|
* @param dto.bucketName - Bucket name
|
|
49407
49486
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49487
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49408
49488
|
* @returns Current state value
|
|
49409
49489
|
*/
|
|
49410
49490
|
this.getState = async (dto) => {
|
|
@@ -49416,7 +49496,7 @@ class StateLiveAdapter {
|
|
|
49416
49496
|
const isInitial = !this.getInstance.has(key);
|
|
49417
49497
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49418
49498
|
await instance.waitForInit(isInitial);
|
|
49419
|
-
return await instance.getState();
|
|
49499
|
+
return await instance.getState(dto.when);
|
|
49420
49500
|
};
|
|
49421
49501
|
/**
|
|
49422
49502
|
* Update the state value for a signal.
|
|
@@ -49424,6 +49504,7 @@ class StateLiveAdapter {
|
|
|
49424
49504
|
* @param dto.signalId - Signal identifier
|
|
49425
49505
|
* @param dto.bucketName - Bucket name
|
|
49426
49506
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49507
|
+
* @param dto.when - Logical timestamp this value belongs to
|
|
49427
49508
|
* @returns Updated state value
|
|
49428
49509
|
*/
|
|
49429
49510
|
this.setState = async (dispatch, dto) => {
|
|
@@ -49435,7 +49516,7 @@ class StateLiveAdapter {
|
|
|
49435
49516
|
const isInitial = !this.getInstance.has(key);
|
|
49436
49517
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49437
49518
|
await instance.waitForInit(isInitial);
|
|
49438
|
-
return await instance.setState(dispatch);
|
|
49519
|
+
return await instance.setState(dispatch, dto.when);
|
|
49439
49520
|
};
|
|
49440
49521
|
/**
|
|
49441
49522
|
* Switches to in-memory adapter.
|
|
@@ -49532,6 +49613,7 @@ class StateAdapter {
|
|
|
49532
49613
|
* @param dto.bucketName - Bucket name
|
|
49533
49614
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49534
49615
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
49616
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49535
49617
|
* @returns Current state value
|
|
49536
49618
|
* @throws Error if adapter is not enabled
|
|
49537
49619
|
*/
|
|
@@ -49557,6 +49639,7 @@ class StateAdapter {
|
|
|
49557
49639
|
* @param dto.bucketName - Bucket name
|
|
49558
49640
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49559
49641
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
49642
|
+
* @param dto.when - Logical timestamp this value belongs to
|
|
49560
49643
|
* @returns Updated state value
|
|
49561
49644
|
* @throws Error if adapter is not enabled
|
|
49562
49645
|
*/
|
|
@@ -49630,8 +49713,9 @@ async function getLatestSignal(symbol) {
|
|
|
49630
49713
|
if (!MethodContextService.hasContext()) {
|
|
49631
49714
|
throw new Error("getLatestSignal requires a method context");
|
|
49632
49715
|
}
|
|
49716
|
+
const { when } = backtest.executionContextService.context;
|
|
49633
49717
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49634
|
-
return await Recent.getLatestSignal(symbol, { exchangeName, frameName, strategyName });
|
|
49718
|
+
return await Recent.getLatestSignal(symbol, { exchangeName, frameName, strategyName }, when);
|
|
49635
49719
|
}
|
|
49636
49720
|
/**
|
|
49637
49721
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
@@ -49666,8 +49750,9 @@ async function getMinutesSinceLatestSignalCreated(symbol) {
|
|
|
49666
49750
|
if (!MethodContextService.hasContext()) {
|
|
49667
49751
|
throw new Error("getMinutesSinceLatestSignalCreated requires a method context");
|
|
49668
49752
|
}
|
|
49753
|
+
const { when } = backtest.executionContextService.context;
|
|
49669
49754
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49670
|
-
return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName });
|
|
49755
|
+
return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName }, when);
|
|
49671
49756
|
}
|
|
49672
49757
|
/**
|
|
49673
49758
|
* Reads the state value scoped to the current active signal.
|
|
@@ -49712,7 +49797,7 @@ async function getSignalState(symbol, dto) {
|
|
|
49712
49797
|
if (!MethodContextService.hasContext()) {
|
|
49713
49798
|
throw new Error("getSignalState requires a method context");
|
|
49714
49799
|
}
|
|
49715
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
49800
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
49716
49801
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49717
49802
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49718
49803
|
let signal;
|
|
@@ -49722,6 +49807,7 @@ async function getSignalState(symbol, dto) {
|
|
|
49722
49807
|
bucketName,
|
|
49723
49808
|
initialValue,
|
|
49724
49809
|
backtest: isBacktest,
|
|
49810
|
+
when,
|
|
49725
49811
|
});
|
|
49726
49812
|
}
|
|
49727
49813
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -49730,6 +49816,7 @@ async function getSignalState(symbol, dto) {
|
|
|
49730
49816
|
bucketName,
|
|
49731
49817
|
initialValue,
|
|
49732
49818
|
backtest: isBacktest,
|
|
49819
|
+
when,
|
|
49733
49820
|
});
|
|
49734
49821
|
}
|
|
49735
49822
|
throw new Error(`getSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
@@ -49781,7 +49868,7 @@ async function setSignalState(symbol, dispatch, dto) {
|
|
|
49781
49868
|
if (!MethodContextService.hasContext()) {
|
|
49782
49869
|
throw new Error("setSignalState requires a method context");
|
|
49783
49870
|
}
|
|
49784
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
49871
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
49785
49872
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49786
49873
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49787
49874
|
let signal;
|
|
@@ -49791,6 +49878,7 @@ async function setSignalState(symbol, dispatch, dto) {
|
|
|
49791
49878
|
bucketName,
|
|
49792
49879
|
initialValue,
|
|
49793
49880
|
backtest: isBacktest,
|
|
49881
|
+
when,
|
|
49794
49882
|
});
|
|
49795
49883
|
}
|
|
49796
49884
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -49799,6 +49887,7 @@ async function setSignalState(symbol, dispatch, dto) {
|
|
|
49799
49887
|
bucketName,
|
|
49800
49888
|
initialValue,
|
|
49801
49889
|
backtest: isBacktest,
|
|
49890
|
+
when,
|
|
49802
49891
|
});
|
|
49803
49892
|
}
|
|
49804
49893
|
throw new Error(`setSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
@@ -49852,36 +49941,47 @@ class SessionLocalInstance {
|
|
|
49852
49941
|
this.frameName = frameName;
|
|
49853
49942
|
this.backtest = backtest$1;
|
|
49854
49943
|
this._data = null;
|
|
49944
|
+
this._when = 0;
|
|
49855
49945
|
/**
|
|
49856
49946
|
* Initializes _data to null — local session needs no async setup.
|
|
49857
49947
|
* @returns Promise that resolves immediately
|
|
49858
49948
|
*/
|
|
49859
49949
|
this.waitForInit = singleshot(async (_initial) => {
|
|
49860
49950
|
this._data = null;
|
|
49951
|
+
this._when = 0;
|
|
49861
49952
|
});
|
|
49862
49953
|
/**
|
|
49863
49954
|
* Read the current in-memory session value.
|
|
49864
|
-
*
|
|
49955
|
+
* Returns null if the stored `when` is greater than the requested `when`
|
|
49956
|
+
* (look-ahead bias protection).
|
|
49957
|
+
* @param when - Logical timestamp at which the read is happening
|
|
49958
|
+
* @returns Current session value, or null
|
|
49865
49959
|
*/
|
|
49866
|
-
this.getData = async () => {
|
|
49960
|
+
this.getData = async (when) => {
|
|
49867
49961
|
backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
49868
49962
|
strategyName: this.strategyName,
|
|
49869
49963
|
exchangeName: this.exchangeName,
|
|
49870
49964
|
frameName: this.frameName,
|
|
49871
49965
|
});
|
|
49966
|
+
if (this._when > when.getTime()) {
|
|
49967
|
+
return null;
|
|
49968
|
+
}
|
|
49872
49969
|
return this._data;
|
|
49873
49970
|
};
|
|
49874
49971
|
/**
|
|
49875
49972
|
* Update the in-memory session value.
|
|
49973
|
+
* Records `when` so future reads with a smaller `when` see no value.
|
|
49876
49974
|
* @param value - New value or null to clear
|
|
49975
|
+
* @param when - Logical timestamp this value belongs to
|
|
49877
49976
|
*/
|
|
49878
|
-
this.setData = async (value) => {
|
|
49977
|
+
this.setData = async (value, when) => {
|
|
49879
49978
|
backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
49880
49979
|
strategyName: this.strategyName,
|
|
49881
49980
|
exchangeName: this.exchangeName,
|
|
49882
49981
|
frameName: this.frameName,
|
|
49883
49982
|
});
|
|
49884
49983
|
this._data = value;
|
|
49984
|
+
this._when = when.getTime();
|
|
49885
49985
|
};
|
|
49886
49986
|
}
|
|
49887
49987
|
/** Releases resources held by this instance. */
|
|
@@ -49917,13 +50017,13 @@ class SessionDummyInstance {
|
|
|
49917
50017
|
* No-op read — always returns null.
|
|
49918
50018
|
* @returns null
|
|
49919
50019
|
*/
|
|
49920
|
-
this.getData = async () => {
|
|
50020
|
+
this.getData = async (_when) => {
|
|
49921
50021
|
return null;
|
|
49922
50022
|
};
|
|
49923
50023
|
/**
|
|
49924
50024
|
* No-op write — discards the value.
|
|
49925
50025
|
*/
|
|
49926
|
-
this.setData = async (_value) => {
|
|
50026
|
+
this.setData = async (_value, _when) => {
|
|
49927
50027
|
};
|
|
49928
50028
|
}
|
|
49929
50029
|
/** No-op. */
|
|
@@ -49949,6 +50049,7 @@ class SessionPersistInstance {
|
|
|
49949
50049
|
this.frameName = frameName;
|
|
49950
50050
|
this.backtest = backtest$1;
|
|
49951
50051
|
this._data = null;
|
|
50052
|
+
this._when = 0;
|
|
49952
50053
|
/**
|
|
49953
50054
|
* Initialize persistence storage and restore session from disk.
|
|
49954
50055
|
* @param initial - Whether this is the first initialization
|
|
@@ -49964,35 +50065,47 @@ class SessionPersistInstance {
|
|
|
49964
50065
|
const data = await PersistSessionAdapter.readSessionData(this.strategyName, this.exchangeName, this.frameName);
|
|
49965
50066
|
if (data) {
|
|
49966
50067
|
this._data = data.data;
|
|
50068
|
+
this._when = data.when;
|
|
49967
50069
|
return;
|
|
49968
50070
|
}
|
|
49969
50071
|
this._data = null;
|
|
50072
|
+
this._when = 0;
|
|
49970
50073
|
});
|
|
49971
50074
|
/**
|
|
49972
50075
|
* Read the current persisted session value.
|
|
49973
|
-
*
|
|
50076
|
+
* Returns null if the stored `when` is greater than the requested `when`
|
|
50077
|
+
* (look-ahead bias protection).
|
|
50078
|
+
* @param when - Logical timestamp at which the read is happening
|
|
50079
|
+
* @returns Current session value, or null
|
|
49974
50080
|
*/
|
|
49975
|
-
this.getData = async () => {
|
|
50081
|
+
this.getData = async (when) => {
|
|
49976
50082
|
backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
49977
50083
|
strategyName: this.strategyName,
|
|
49978
50084
|
exchangeName: this.exchangeName,
|
|
49979
50085
|
frameName: this.frameName,
|
|
49980
50086
|
});
|
|
50087
|
+
if (this._when > when.getTime()) {
|
|
50088
|
+
return null;
|
|
50089
|
+
}
|
|
49981
50090
|
return this._data;
|
|
49982
50091
|
};
|
|
49983
50092
|
/**
|
|
49984
50093
|
* Update session value and persist to disk atomically.
|
|
50094
|
+
* A write with a smaller `when` overwrites an existing record — that lets
|
|
50095
|
+
* a restarted backtest reset live-written state without breaking live.
|
|
49985
50096
|
* @param value - New value or null to clear
|
|
50097
|
+
* @param when - Logical timestamp this value belongs to
|
|
49986
50098
|
*/
|
|
49987
|
-
this.setData = async (value) => {
|
|
50099
|
+
this.setData = async (value, when) => {
|
|
49988
50100
|
backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
49989
50101
|
strategyName: this.strategyName,
|
|
49990
50102
|
exchangeName: this.exchangeName,
|
|
49991
50103
|
frameName: this.frameName,
|
|
49992
50104
|
});
|
|
49993
50105
|
this._data = value;
|
|
50106
|
+
this._when = when.getTime();
|
|
49994
50107
|
const id = CREATE_KEY_FN$5(this.symbol, this.strategyName, this.exchangeName, this.frameName, this.backtest);
|
|
49995
|
-
await PersistSessionAdapter.writeSessionData({ id, data: value }, this.strategyName, this.exchangeName, this.frameName);
|
|
50108
|
+
await PersistSessionAdapter.writeSessionData({ id, data: value, when: this._when }, this.strategyName, this.exchangeName, this.frameName, when);
|
|
49996
50109
|
};
|
|
49997
50110
|
}
|
|
49998
50111
|
/** Releases resources held by this instance. */
|
|
@@ -50025,9 +50138,10 @@ class SessionBacktestAdapter {
|
|
|
50025
50138
|
* @param context.strategyName - Strategy identifier
|
|
50026
50139
|
* @param context.exchangeName - Exchange identifier
|
|
50027
50140
|
* @param context.frameName - Frame identifier
|
|
50028
|
-
* @
|
|
50141
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50142
|
+
* @returns Current session value, or null if not set / look-ahead
|
|
50029
50143
|
*/
|
|
50030
|
-
this.getData = async (symbol, context) => {
|
|
50144
|
+
this.getData = async (symbol, context, when) => {
|
|
50031
50145
|
backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_GET, {
|
|
50032
50146
|
strategyName: context.strategyName,
|
|
50033
50147
|
exchangeName: context.exchangeName,
|
|
@@ -50037,7 +50151,7 @@ class SessionBacktestAdapter {
|
|
|
50037
50151
|
const isInitial = !this.getInstance.has(key);
|
|
50038
50152
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, true);
|
|
50039
50153
|
await instance.waitForInit(isInitial);
|
|
50040
|
-
return await instance.getData();
|
|
50154
|
+
return await instance.getData(when);
|
|
50041
50155
|
};
|
|
50042
50156
|
/**
|
|
50043
50157
|
* Update the session value for a backtest run.
|
|
@@ -50046,8 +50160,9 @@ class SessionBacktestAdapter {
|
|
|
50046
50160
|
* @param context.strategyName - Strategy identifier
|
|
50047
50161
|
* @param context.exchangeName - Exchange identifier
|
|
50048
50162
|
* @param context.frameName - Frame identifier
|
|
50163
|
+
* @param when - Logical timestamp this value belongs to
|
|
50049
50164
|
*/
|
|
50050
|
-
this.setData = async (symbol, value, context) => {
|
|
50165
|
+
this.setData = async (symbol, value, context, when) => {
|
|
50051
50166
|
backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_SET, {
|
|
50052
50167
|
strategyName: context.strategyName,
|
|
50053
50168
|
exchangeName: context.exchangeName,
|
|
@@ -50057,7 +50172,7 @@ class SessionBacktestAdapter {
|
|
|
50057
50172
|
const isInitial = !this.getInstance.has(key);
|
|
50058
50173
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, true);
|
|
50059
50174
|
await instance.waitForInit(isInitial);
|
|
50060
|
-
return await instance.setData(value);
|
|
50175
|
+
return await instance.setData(value, when);
|
|
50061
50176
|
};
|
|
50062
50177
|
/**
|
|
50063
50178
|
* Switches to in-memory adapter (default).
|
|
@@ -50121,9 +50236,10 @@ class SessionLiveAdapter {
|
|
|
50121
50236
|
* @param context.strategyName - Strategy identifier
|
|
50122
50237
|
* @param context.exchangeName - Exchange identifier
|
|
50123
50238
|
* @param context.frameName - Frame identifier
|
|
50124
|
-
* @
|
|
50239
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50240
|
+
* @returns Current session value, or null if not set / look-ahead
|
|
50125
50241
|
*/
|
|
50126
|
-
this.getData = async (symbol, context) => {
|
|
50242
|
+
this.getData = async (symbol, context, when) => {
|
|
50127
50243
|
backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_GET, {
|
|
50128
50244
|
strategyName: context.strategyName,
|
|
50129
50245
|
exchangeName: context.exchangeName,
|
|
@@ -50133,7 +50249,7 @@ class SessionLiveAdapter {
|
|
|
50133
50249
|
const isInitial = !this.getInstance.has(key);
|
|
50134
50250
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, false);
|
|
50135
50251
|
await instance.waitForInit(isInitial);
|
|
50136
|
-
return await instance.getData();
|
|
50252
|
+
return await instance.getData(when);
|
|
50137
50253
|
};
|
|
50138
50254
|
/**
|
|
50139
50255
|
* Update the session value for a live run.
|
|
@@ -50142,8 +50258,9 @@ class SessionLiveAdapter {
|
|
|
50142
50258
|
* @param context.strategyName - Strategy identifier
|
|
50143
50259
|
* @param context.exchangeName - Exchange identifier
|
|
50144
50260
|
* @param context.frameName - Frame identifier
|
|
50261
|
+
* @param when - Logical timestamp this value belongs to
|
|
50145
50262
|
*/
|
|
50146
|
-
this.setData = async (symbol, value, context) => {
|
|
50263
|
+
this.setData = async (symbol, value, context, when) => {
|
|
50147
50264
|
backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_SET, {
|
|
50148
50265
|
strategyName: context.strategyName,
|
|
50149
50266
|
exchangeName: context.exchangeName,
|
|
@@ -50153,7 +50270,7 @@ class SessionLiveAdapter {
|
|
|
50153
50270
|
const isInitial = !this.getInstance.has(key);
|
|
50154
50271
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, false);
|
|
50155
50272
|
await instance.waitForInit(isInitial);
|
|
50156
|
-
return await instance.setData(value);
|
|
50273
|
+
return await instance.setData(value, when);
|
|
50157
50274
|
};
|
|
50158
50275
|
/**
|
|
50159
50276
|
* Switches to in-memory adapter.
|
|
@@ -50213,9 +50330,10 @@ class SessionAdapter {
|
|
|
50213
50330
|
* @param context.exchangeName - Exchange identifier
|
|
50214
50331
|
* @param context.frameName - Frame identifier
|
|
50215
50332
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
50216
|
-
* @
|
|
50333
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50334
|
+
* @returns Current session value, or null if not set / look-ahead
|
|
50217
50335
|
*/
|
|
50218
|
-
this.getData = async (symbol, context, backtest$1) => {
|
|
50336
|
+
this.getData = async (symbol, context, backtest$1, when) => {
|
|
50219
50337
|
backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_GET, {
|
|
50220
50338
|
strategyName: context.strategyName,
|
|
50221
50339
|
exchangeName: context.exchangeName,
|
|
@@ -50223,9 +50341,9 @@ class SessionAdapter {
|
|
|
50223
50341
|
backtest: backtest$1,
|
|
50224
50342
|
});
|
|
50225
50343
|
if (backtest$1) {
|
|
50226
|
-
return await SessionBacktest.getData(symbol, context);
|
|
50344
|
+
return await SessionBacktest.getData(symbol, context, when);
|
|
50227
50345
|
}
|
|
50228
|
-
return await SessionLive.getData(symbol, context);
|
|
50346
|
+
return await SessionLive.getData(symbol, context, when);
|
|
50229
50347
|
};
|
|
50230
50348
|
/**
|
|
50231
50349
|
* Update the session value for a signal.
|
|
@@ -50236,8 +50354,9 @@ class SessionAdapter {
|
|
|
50236
50354
|
* @param context.exchangeName - Exchange identifier
|
|
50237
50355
|
* @param context.frameName - Frame identifier
|
|
50238
50356
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
50357
|
+
* @param when - Logical timestamp this value belongs to
|
|
50239
50358
|
*/
|
|
50240
|
-
this.setData = async (symbol, value, context, backtest$1) => {
|
|
50359
|
+
this.setData = async (symbol, value, context, backtest$1, when) => {
|
|
50241
50360
|
backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_SET, {
|
|
50242
50361
|
strategyName: context.strategyName,
|
|
50243
50362
|
exchangeName: context.exchangeName,
|
|
@@ -50245,9 +50364,9 @@ class SessionAdapter {
|
|
|
50245
50364
|
backtest: backtest$1,
|
|
50246
50365
|
});
|
|
50247
50366
|
if (backtest$1) {
|
|
50248
|
-
return await SessionBacktest.setData(symbol, value, context);
|
|
50367
|
+
return await SessionBacktest.setData(symbol, value, context, when);
|
|
50249
50368
|
}
|
|
50250
|
-
return await SessionLive.setData(symbol, value, context);
|
|
50369
|
+
return await SessionLive.setData(symbol, value, context, when);
|
|
50251
50370
|
};
|
|
50252
50371
|
}
|
|
50253
50372
|
}
|
|
@@ -50299,9 +50418,9 @@ async function getSessionData(symbol) {
|
|
|
50299
50418
|
if (!MethodContextService.hasContext()) {
|
|
50300
50419
|
throw new Error("getSession requires a method context");
|
|
50301
50420
|
}
|
|
50302
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50421
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50303
50422
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50304
|
-
return await Session.getData(symbol, { exchangeName, frameName, strategyName }, isBacktest);
|
|
50423
|
+
return await Session.getData(symbol, { exchangeName, frameName, strategyName }, isBacktest, when);
|
|
50305
50424
|
}
|
|
50306
50425
|
/**
|
|
50307
50426
|
* Writes a session value scoped to the current (symbol, strategy, exchange, frame) context.
|
|
@@ -50333,9 +50452,9 @@ async function setSessionData(symbol, value) {
|
|
|
50333
50452
|
if (!MethodContextService.hasContext()) {
|
|
50334
50453
|
throw new Error("setSession requires a method context");
|
|
50335
50454
|
}
|
|
50336
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50455
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50337
50456
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50338
|
-
await Session.setData(symbol, value, { exchangeName, frameName, strategyName }, isBacktest);
|
|
50457
|
+
await Session.setData(symbol, value, { exchangeName, frameName, strategyName }, isBacktest, when);
|
|
50339
50458
|
}
|
|
50340
50459
|
|
|
50341
50460
|
const CREATE_SIGNAL_STATE_METHOD_NAME = "state.createSignalState";
|
|
@@ -50346,7 +50465,7 @@ const CREATE_SET_STATE_FN = (params) => async (symbol, dispatch) => {
|
|
|
50346
50465
|
if (!MethodContextService.hasContext()) {
|
|
50347
50466
|
throw new Error("createSignalState requires a method context");
|
|
50348
50467
|
}
|
|
50349
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50468
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50350
50469
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50351
50470
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
50352
50471
|
let signal;
|
|
@@ -50356,6 +50475,7 @@ const CREATE_SET_STATE_FN = (params) => async (symbol, dispatch) => {
|
|
|
50356
50475
|
bucketName: params.bucketName,
|
|
50357
50476
|
initialValue: params.initialValue,
|
|
50358
50477
|
signalId: signal.id,
|
|
50478
|
+
when,
|
|
50359
50479
|
});
|
|
50360
50480
|
}
|
|
50361
50481
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -50364,6 +50484,7 @@ const CREATE_SET_STATE_FN = (params) => async (symbol, dispatch) => {
|
|
|
50364
50484
|
bucketName: params.bucketName,
|
|
50365
50485
|
initialValue: params.initialValue,
|
|
50366
50486
|
signalId: signal.id,
|
|
50487
|
+
when,
|
|
50367
50488
|
});
|
|
50368
50489
|
}
|
|
50369
50490
|
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
@@ -50375,7 +50496,7 @@ const CREATE_GET_STATE_FN = (params) => async (symbol) => {
|
|
|
50375
50496
|
if (!MethodContextService.hasContext()) {
|
|
50376
50497
|
throw new Error("createSignalState requires a method context");
|
|
50377
50498
|
}
|
|
50378
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50499
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50379
50500
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50380
50501
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
50381
50502
|
let signal;
|
|
@@ -50385,6 +50506,7 @@ const CREATE_GET_STATE_FN = (params) => async (symbol) => {
|
|
|
50385
50506
|
bucketName: params.bucketName,
|
|
50386
50507
|
initialValue: params.initialValue,
|
|
50387
50508
|
signalId: signal.id,
|
|
50509
|
+
when,
|
|
50388
50510
|
});
|
|
50389
50511
|
}
|
|
50390
50512
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -50393,6 +50515,7 @@ const CREATE_GET_STATE_FN = (params) => async (symbol) => {
|
|
|
50393
50515
|
bucketName: params.bucketName,
|
|
50394
50516
|
initialValue: params.initialValue,
|
|
50395
50517
|
signalId: signal.id,
|
|
50518
|
+
when,
|
|
50396
50519
|
});
|
|
50397
50520
|
}
|
|
50398
50521
|
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
@@ -50475,7 +50598,7 @@ const createSearchIndex = () => {
|
|
|
50475
50598
|
df.set(term, count);
|
|
50476
50599
|
});
|
|
50477
50600
|
};
|
|
50478
|
-
const upsert = ({ id, content, index = JSON.stringify(content), priority = Date.now(), }) => {
|
|
50601
|
+
const upsert = ({ id, content, when, index = JSON.stringify(content), priority = Date.now(), }) => {
|
|
50479
50602
|
const existing = docs.get(id);
|
|
50480
50603
|
{
|
|
50481
50604
|
existing && subtractDf(existing.tf);
|
|
@@ -50484,12 +50607,21 @@ const createSearchIndex = () => {
|
|
|
50484
50607
|
const tf = new Map();
|
|
50485
50608
|
for (const t of tokens)
|
|
50486
50609
|
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
50487
|
-
docs.set(id, { tf, len: tokens.length, content, priority });
|
|
50610
|
+
docs.set(id, { tf, len: tokens.length, content, priority, when });
|
|
50488
50611
|
{
|
|
50489
50612
|
addDf(tf);
|
|
50490
50613
|
}
|
|
50491
50614
|
};
|
|
50492
|
-
|
|
50615
|
+
/**
|
|
50616
|
+
* Read a document by id. Returns undefined when the document was written
|
|
50617
|
+
* with a `when` greater than the requested `when` (look-ahead guard).
|
|
50618
|
+
*/
|
|
50619
|
+
const read = (id, when) => {
|
|
50620
|
+
const doc = docs.get(id);
|
|
50621
|
+
if (!doc || doc.when > when)
|
|
50622
|
+
return undefined;
|
|
50623
|
+
return doc.content;
|
|
50624
|
+
};
|
|
50493
50625
|
const remove = (id) => {
|
|
50494
50626
|
{
|
|
50495
50627
|
const existing = docs.get(id);
|
|
@@ -50497,16 +50629,30 @@ const createSearchIndex = () => {
|
|
|
50497
50629
|
}
|
|
50498
50630
|
docs.delete(id);
|
|
50499
50631
|
};
|
|
50500
|
-
|
|
50632
|
+
/**
|
|
50633
|
+
* List documents whose `when` is less than or equal to the requested `when`
|
|
50634
|
+
* (look-ahead guard), sorted by priority.
|
|
50635
|
+
*/
|
|
50636
|
+
const list = (when) => Array.from(docs.entries())
|
|
50637
|
+
.filter(([, doc]) => doc.when <= when)
|
|
50501
50638
|
.sort(([, a], [, b]) => a.priority - b.priority)
|
|
50502
50639
|
.map(([id, { content }]) => ({ id, content }));
|
|
50503
|
-
|
|
50640
|
+
/**
|
|
50641
|
+
* BM25 search over documents whose `when` is less than or equal to the
|
|
50642
|
+
* requested `when` (look-ahead guard).
|
|
50643
|
+
*
|
|
50644
|
+
* Document frequency (df) is computed across the whole index — the time-cut
|
|
50645
|
+
* is applied only to the candidate set, so scores stay comparable across
|
|
50646
|
+
* different `when` values.
|
|
50647
|
+
*/
|
|
50648
|
+
const search = (query, when, settings = DEFAULT_SETTINGS) => {
|
|
50504
50649
|
const terms = tokenize(query);
|
|
50505
50650
|
if (!terms.length || !docs.size)
|
|
50506
50651
|
return [];
|
|
50507
50652
|
const N = docs.size;
|
|
50508
50653
|
const avgLen = [...docs.values()].reduce((s, d) => s + d.len, 0) / N;
|
|
50509
50654
|
return [...docs.entries()]
|
|
50655
|
+
.filter(([, doc]) => doc.when <= when)
|
|
50510
50656
|
.map(([id, doc]) => {
|
|
50511
50657
|
let score = 0;
|
|
50512
50658
|
for (const term of terms) {
|
|
@@ -50603,8 +50749,9 @@ class MemoryLocalInstance {
|
|
|
50603
50749
|
* @param memoryId - Unique entry identifier
|
|
50604
50750
|
* @param value - Value to store and index
|
|
50605
50751
|
* @param description - BM25 index string
|
|
50752
|
+
* @param when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
50606
50753
|
*/
|
|
50607
|
-
async writeMemory(memoryId, value, description) {
|
|
50754
|
+
async writeMemory(memoryId, value, description, when) {
|
|
50608
50755
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_WRITE, {
|
|
50609
50756
|
signalId: this.signalId,
|
|
50610
50757
|
bucketName: this.bucketName,
|
|
@@ -50615,21 +50762,24 @@ class MemoryLocalInstance {
|
|
|
50615
50762
|
content: value,
|
|
50616
50763
|
index: description,
|
|
50617
50764
|
priority: Date.now(),
|
|
50765
|
+
when: when.getTime(),
|
|
50618
50766
|
});
|
|
50619
50767
|
}
|
|
50620
50768
|
/**
|
|
50621
50769
|
* Read a single entry from the in-memory index.
|
|
50770
|
+
* Behaves as not-found if the stored `when` is greater than the requested `when`.
|
|
50622
50771
|
* @param memoryId - Unique entry identifier
|
|
50772
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50623
50773
|
* @returns Parsed entry value
|
|
50624
|
-
* @throws Error if entry not found
|
|
50774
|
+
* @throws Error if entry not found (or shadowed by look-ahead)
|
|
50625
50775
|
*/
|
|
50626
|
-
async readMemory(memoryId) {
|
|
50776
|
+
async readMemory(memoryId, when) {
|
|
50627
50777
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_READ, {
|
|
50628
50778
|
signalId: this.signalId,
|
|
50629
50779
|
bucketName: this.bucketName,
|
|
50630
50780
|
memoryId,
|
|
50631
50781
|
});
|
|
50632
|
-
const value = this._index.read(memoryId);
|
|
50782
|
+
const value = this._index.read(memoryId, when.getTime());
|
|
50633
50783
|
if (!value) {
|
|
50634
50784
|
throw new Error(`MemoryLocalInstance value not found memoryId=${memoryId}`);
|
|
50635
50785
|
}
|
|
@@ -50637,33 +50787,38 @@ class MemoryLocalInstance {
|
|
|
50637
50787
|
}
|
|
50638
50788
|
/**
|
|
50639
50789
|
* Search entries using BM25 full-text scoring.
|
|
50790
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50640
50791
|
* @param query - Search query string
|
|
50792
|
+
* @param when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
50641
50793
|
* @returns Matching entries sorted by relevance score
|
|
50642
50794
|
*/
|
|
50643
|
-
async searchMemory(query, settings) {
|
|
50795
|
+
async searchMemory(query, when, settings) {
|
|
50644
50796
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_SEARCH, {
|
|
50645
50797
|
signalId: this.signalId,
|
|
50646
50798
|
bucketName: this.bucketName,
|
|
50647
50799
|
query,
|
|
50648
50800
|
});
|
|
50649
|
-
return this._index.search(query, settings).map(SEARCH_MEMORY_FN);
|
|
50801
|
+
return this._index.search(query, when.getTime(), settings).map(SEARCH_MEMORY_FN);
|
|
50650
50802
|
}
|
|
50651
50803
|
/**
|
|
50652
50804
|
* List all entries stored in the in-memory index.
|
|
50805
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50806
|
+
* @param when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
50653
50807
|
* @returns Array of all stored entries
|
|
50654
50808
|
*/
|
|
50655
|
-
async listMemory() {
|
|
50809
|
+
async listMemory(when) {
|
|
50656
50810
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_LIST, {
|
|
50657
50811
|
signalId: this.signalId,
|
|
50658
50812
|
bucketName: this.bucketName,
|
|
50659
50813
|
});
|
|
50660
|
-
return this._index.list().map(LIST_MEMORY_FN);
|
|
50814
|
+
return this._index.list(when.getTime()).map(LIST_MEMORY_FN);
|
|
50661
50815
|
}
|
|
50662
50816
|
/**
|
|
50663
50817
|
* Remove an entry from the in-memory index.
|
|
50664
50818
|
* @param memoryId - Unique entry identifier
|
|
50819
|
+
* @param when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
50665
50820
|
*/
|
|
50666
|
-
async removeMemory(memoryId) {
|
|
50821
|
+
async removeMemory(memoryId, _when) {
|
|
50667
50822
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_REMOVE, {
|
|
50668
50823
|
signalId: this.signalId,
|
|
50669
50824
|
bucketName: this.bucketName,
|
|
@@ -50709,12 +50864,13 @@ class MemoryPersistInstance {
|
|
|
50709
50864
|
initial,
|
|
50710
50865
|
});
|
|
50711
50866
|
await PersistMemoryAdapter.waitForInit(this.signalId, this.bucketName, initial);
|
|
50712
|
-
for await (const { memoryId, data: { data, index, priority } } of PersistMemoryAdapter.listMemoryData(this.signalId, this.bucketName)) {
|
|
50867
|
+
for await (const { memoryId, data: { data, index, priority, when } } of PersistMemoryAdapter.listMemoryData(this.signalId, this.bucketName)) {
|
|
50713
50868
|
this._index.upsert({
|
|
50714
50869
|
id: memoryId,
|
|
50715
50870
|
content: data,
|
|
50716
50871
|
index,
|
|
50717
50872
|
priority,
|
|
50873
|
+
when,
|
|
50718
50874
|
});
|
|
50719
50875
|
}
|
|
50720
50876
|
}
|
|
@@ -50723,69 +50879,79 @@ class MemoryPersistInstance {
|
|
|
50723
50879
|
* @param memoryId - Unique entry identifier
|
|
50724
50880
|
* @param value - Value to persist and index
|
|
50725
50881
|
* @param index - BM25 index string; defaults to JSON.stringify(value)
|
|
50882
|
+
* @param when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
50726
50883
|
*/
|
|
50727
|
-
async writeMemory(memoryId, value, index
|
|
50884
|
+
async writeMemory(memoryId, value, index, when) {
|
|
50728
50885
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_WRITE, {
|
|
50729
50886
|
signalId: this.signalId,
|
|
50730
50887
|
bucketName: this.bucketName,
|
|
50731
50888
|
memoryId,
|
|
50732
50889
|
});
|
|
50733
50890
|
const priority = Date.now();
|
|
50734
|
-
|
|
50891
|
+
const whenMs = when.getTime();
|
|
50892
|
+
await PersistMemoryAdapter.writeMemoryData({ data: value, priority, removed: false, index, when: whenMs }, this.signalId, this.bucketName, memoryId, when);
|
|
50735
50893
|
this._index.upsert({
|
|
50736
50894
|
id: memoryId,
|
|
50737
50895
|
content: value,
|
|
50738
50896
|
index,
|
|
50739
50897
|
priority,
|
|
50898
|
+
when: whenMs,
|
|
50740
50899
|
});
|
|
50741
50900
|
}
|
|
50742
50901
|
/**
|
|
50743
50902
|
* Read a single entry from disk.
|
|
50903
|
+
* Behaves as not-found if the stored `when` is greater than the requested `when`.
|
|
50744
50904
|
* @param memoryId - Unique entry identifier
|
|
50905
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50745
50906
|
* @returns Entry value
|
|
50746
|
-
* @throws Error if entry not found
|
|
50907
|
+
* @throws Error if entry not found (or shadowed by look-ahead)
|
|
50747
50908
|
*/
|
|
50748
|
-
async readMemory(memoryId) {
|
|
50909
|
+
async readMemory(memoryId, when) {
|
|
50749
50910
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_READ, {
|
|
50750
50911
|
signalId: this.signalId,
|
|
50751
50912
|
bucketName: this.bucketName,
|
|
50752
50913
|
memoryId,
|
|
50753
50914
|
});
|
|
50754
50915
|
const data = await PersistMemoryAdapter.readMemoryData(this.signalId, this.bucketName, memoryId);
|
|
50755
|
-
if (!data) {
|
|
50916
|
+
if (!data || data.when > when.getTime()) {
|
|
50756
50917
|
throw new Error(`MemoryPersistInstance value not found memoryId=${memoryId}`);
|
|
50757
50918
|
}
|
|
50758
50919
|
return data.data;
|
|
50759
50920
|
}
|
|
50760
50921
|
/**
|
|
50761
50922
|
* Search entries using BM25 index rebuilt from disk on init.
|
|
50923
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50762
50924
|
* @param query - Search query string
|
|
50925
|
+
* @param when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
50763
50926
|
* @returns Matching entries sorted by relevance score
|
|
50764
50927
|
*/
|
|
50765
|
-
async searchMemory(query, settings) {
|
|
50928
|
+
async searchMemory(query, when, settings) {
|
|
50766
50929
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_SEARCH, {
|
|
50767
50930
|
signalId: this.signalId,
|
|
50768
50931
|
bucketName: this.bucketName,
|
|
50769
50932
|
query,
|
|
50770
50933
|
});
|
|
50771
|
-
return this._index.search(query, settings).map(SEARCH_MEMORY_FN);
|
|
50934
|
+
return this._index.search(query, when.getTime(), settings).map(SEARCH_MEMORY_FN);
|
|
50772
50935
|
}
|
|
50773
50936
|
/**
|
|
50774
50937
|
* List all entries from the in-memory index (populated from disk on init).
|
|
50938
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50939
|
+
* @param when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
50775
50940
|
* @returns Array of all stored entries
|
|
50776
50941
|
*/
|
|
50777
|
-
async listMemory() {
|
|
50942
|
+
async listMemory(when) {
|
|
50778
50943
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_LIST, {
|
|
50779
50944
|
signalId: this.signalId,
|
|
50780
50945
|
bucketName: this.bucketName,
|
|
50781
50946
|
});
|
|
50782
|
-
return this._index.list().map(LIST_MEMORY_FN);
|
|
50947
|
+
return this._index.list(when.getTime()).map(LIST_MEMORY_FN);
|
|
50783
50948
|
}
|
|
50784
50949
|
/**
|
|
50785
50950
|
* Remove an entry from disk and from the BM25 index.
|
|
50786
50951
|
* @param memoryId - Unique entry identifier
|
|
50952
|
+
* @param when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
50787
50953
|
*/
|
|
50788
|
-
async removeMemory(memoryId) {
|
|
50954
|
+
async removeMemory(memoryId, _when) {
|
|
50789
50955
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_REMOVE, {
|
|
50790
50956
|
signalId: this.signalId,
|
|
50791
50957
|
bucketName: this.bucketName,
|
|
@@ -50822,34 +50988,34 @@ class MemoryDummyInstance {
|
|
|
50822
50988
|
* No-op write - discards the value.
|
|
50823
50989
|
* @returns Promise that resolves immediately
|
|
50824
50990
|
*/
|
|
50825
|
-
async writeMemory() {
|
|
50991
|
+
async writeMemory(_memoryId, _value, _description, _when) {
|
|
50826
50992
|
}
|
|
50827
50993
|
/**
|
|
50828
50994
|
* No-op read - always throws.
|
|
50829
50995
|
* @throws Error always
|
|
50830
50996
|
*/
|
|
50831
|
-
async readMemory(_memoryId) {
|
|
50997
|
+
async readMemory(_memoryId, _when) {
|
|
50832
50998
|
throw new Error("MemoryDummyInstance: readMemory not supported");
|
|
50833
50999
|
}
|
|
50834
51000
|
/**
|
|
50835
51001
|
* No-op search - returns empty array.
|
|
50836
51002
|
* @returns Empty array
|
|
50837
51003
|
*/
|
|
50838
|
-
async searchMemory() {
|
|
51004
|
+
async searchMemory(_query, _when, _settings) {
|
|
50839
51005
|
return [];
|
|
50840
51006
|
}
|
|
50841
51007
|
/**
|
|
50842
51008
|
* No-op list - returns empty array.
|
|
50843
51009
|
* @returns Empty array
|
|
50844
51010
|
*/
|
|
50845
|
-
async listMemory() {
|
|
51011
|
+
async listMemory(_when) {
|
|
50846
51012
|
return [];
|
|
50847
51013
|
}
|
|
50848
51014
|
/**
|
|
50849
51015
|
* No-op remove.
|
|
50850
51016
|
* @returns Promise that resolves immediately
|
|
50851
51017
|
*/
|
|
50852
|
-
async removeMemory() {
|
|
51018
|
+
async removeMemory(_memoryId, _when) {
|
|
50853
51019
|
}
|
|
50854
51020
|
/** No-op. */
|
|
50855
51021
|
dispose() {
|
|
@@ -50893,6 +51059,7 @@ class MemoryBacktestAdapter {
|
|
|
50893
51059
|
* @param dto.signalId - Signal identifier
|
|
50894
51060
|
* @param dto.bucketName - Bucket name
|
|
50895
51061
|
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
51062
|
+
* @param dto.when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
50896
51063
|
*/
|
|
50897
51064
|
this.writeMemory = async (dto) => {
|
|
50898
51065
|
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE, {
|
|
@@ -50904,13 +51071,14 @@ class MemoryBacktestAdapter {
|
|
|
50904
51071
|
const isInitial = !this.getInstance.has(key);
|
|
50905
51072
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50906
51073
|
await instance.waitForInit(isInitial);
|
|
50907
|
-
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
51074
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description, dto.when);
|
|
50908
51075
|
};
|
|
50909
51076
|
/**
|
|
50910
51077
|
* Search memory using BM25 full-text scoring.
|
|
50911
51078
|
* @param dto.query - Search query string
|
|
50912
51079
|
* @param dto.signalId - Signal identifier
|
|
50913
51080
|
* @param dto.bucketName - Bucket name
|
|
51081
|
+
* @param dto.when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
50914
51082
|
* @returns Matching entries sorted by relevance score
|
|
50915
51083
|
*/
|
|
50916
51084
|
this.searchMemory = async (dto) => {
|
|
@@ -50923,12 +51091,13 @@ class MemoryBacktestAdapter {
|
|
|
50923
51091
|
const isInitial = !this.getInstance.has(key);
|
|
50924
51092
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50925
51093
|
await instance.waitForInit(isInitial);
|
|
50926
|
-
return await instance.searchMemory(dto.query, dto.settings);
|
|
51094
|
+
return await instance.searchMemory(dto.query, dto.when, dto.settings);
|
|
50927
51095
|
};
|
|
50928
51096
|
/**
|
|
50929
51097
|
* List all entries in memory.
|
|
50930
51098
|
* @param dto.signalId - Signal identifier
|
|
50931
51099
|
* @param dto.bucketName - Bucket name
|
|
51100
|
+
* @param dto.when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
50932
51101
|
* @returns Array of all stored entries
|
|
50933
51102
|
*/
|
|
50934
51103
|
this.listMemory = async (dto) => {
|
|
@@ -50940,13 +51109,14 @@ class MemoryBacktestAdapter {
|
|
|
50940
51109
|
const isInitial = !this.getInstance.has(key);
|
|
50941
51110
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50942
51111
|
await instance.waitForInit(isInitial);
|
|
50943
|
-
return await instance.listMemory();
|
|
51112
|
+
return await instance.listMemory(dto.when);
|
|
50944
51113
|
};
|
|
50945
51114
|
/**
|
|
50946
51115
|
* Remove an entry from memory.
|
|
50947
51116
|
* @param dto.memoryId - Unique entry identifier
|
|
50948
51117
|
* @param dto.signalId - Signal identifier
|
|
50949
51118
|
* @param dto.bucketName - Bucket name
|
|
51119
|
+
* @param dto.when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
50950
51120
|
*/
|
|
50951
51121
|
this.removeMemory = async (dto) => {
|
|
50952
51122
|
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE, {
|
|
@@ -50958,13 +51128,14 @@ class MemoryBacktestAdapter {
|
|
|
50958
51128
|
const isInitial = !this.getInstance.has(key);
|
|
50959
51129
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50960
51130
|
await instance.waitForInit(isInitial);
|
|
50961
|
-
return await instance.removeMemory(dto.memoryId);
|
|
51131
|
+
return await instance.removeMemory(dto.memoryId, dto.when);
|
|
50962
51132
|
};
|
|
50963
51133
|
/**
|
|
50964
51134
|
* Read a single entry from memory.
|
|
50965
51135
|
* @param dto.memoryId - Unique entry identifier
|
|
50966
51136
|
* @param dto.signalId - Signal identifier
|
|
50967
51137
|
* @param dto.bucketName - Bucket name
|
|
51138
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50968
51139
|
* @returns Entry value
|
|
50969
51140
|
* @throws Error if entry not found
|
|
50970
51141
|
*/
|
|
@@ -50978,7 +51149,7 @@ class MemoryBacktestAdapter {
|
|
|
50978
51149
|
const isInitial = !this.getInstance.has(key);
|
|
50979
51150
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50980
51151
|
await instance.waitForInit(isInitial);
|
|
50981
|
-
return await instance.readMemory(dto.memoryId);
|
|
51152
|
+
return await instance.readMemory(dto.memoryId, dto.when);
|
|
50982
51153
|
};
|
|
50983
51154
|
/**
|
|
50984
51155
|
* Switches to in-memory BM25 adapter (default).
|
|
@@ -51060,6 +51231,7 @@ class MemoryLiveAdapter {
|
|
|
51060
51231
|
* @param dto.signalId - Signal identifier
|
|
51061
51232
|
* @param dto.bucketName - Bucket name
|
|
51062
51233
|
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
51234
|
+
* @param dto.when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
51063
51235
|
*/
|
|
51064
51236
|
this.writeMemory = async (dto) => {
|
|
51065
51237
|
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE, {
|
|
@@ -51071,13 +51243,14 @@ class MemoryLiveAdapter {
|
|
|
51071
51243
|
const isInitial = !this.getInstance.has(key);
|
|
51072
51244
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51073
51245
|
await instance.waitForInit(isInitial);
|
|
51074
|
-
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
51246
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description, dto.when);
|
|
51075
51247
|
};
|
|
51076
51248
|
/**
|
|
51077
51249
|
* Search memory using BM25 full-text scoring.
|
|
51078
51250
|
* @param dto.query - Search query string
|
|
51079
51251
|
* @param dto.signalId - Signal identifier
|
|
51080
51252
|
* @param dto.bucketName - Bucket name
|
|
51253
|
+
* @param dto.when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
51081
51254
|
* @returns Matching entries sorted by relevance score
|
|
51082
51255
|
*/
|
|
51083
51256
|
this.searchMemory = async (dto) => {
|
|
@@ -51090,12 +51263,13 @@ class MemoryLiveAdapter {
|
|
|
51090
51263
|
const isInitial = !this.getInstance.has(key);
|
|
51091
51264
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51092
51265
|
await instance.waitForInit(isInitial);
|
|
51093
|
-
return await instance.searchMemory(dto.query, dto.settings);
|
|
51266
|
+
return await instance.searchMemory(dto.query, dto.when, dto.settings);
|
|
51094
51267
|
};
|
|
51095
51268
|
/**
|
|
51096
51269
|
* List all entries in memory.
|
|
51097
51270
|
* @param dto.signalId - Signal identifier
|
|
51098
51271
|
* @param dto.bucketName - Bucket name
|
|
51272
|
+
* @param dto.when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
51099
51273
|
* @returns Array of all stored entries
|
|
51100
51274
|
*/
|
|
51101
51275
|
this.listMemory = async (dto) => {
|
|
@@ -51107,13 +51281,14 @@ class MemoryLiveAdapter {
|
|
|
51107
51281
|
const isInitial = !this.getInstance.has(key);
|
|
51108
51282
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51109
51283
|
await instance.waitForInit(isInitial);
|
|
51110
|
-
return await instance.listMemory();
|
|
51284
|
+
return await instance.listMemory(dto.when);
|
|
51111
51285
|
};
|
|
51112
51286
|
/**
|
|
51113
51287
|
* Remove an entry from memory.
|
|
51114
51288
|
* @param dto.memoryId - Unique entry identifier
|
|
51115
51289
|
* @param dto.signalId - Signal identifier
|
|
51116
51290
|
* @param dto.bucketName - Bucket name
|
|
51291
|
+
* @param dto.when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
51117
51292
|
*/
|
|
51118
51293
|
this.removeMemory = async (dto) => {
|
|
51119
51294
|
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE, {
|
|
@@ -51125,13 +51300,14 @@ class MemoryLiveAdapter {
|
|
|
51125
51300
|
const isInitial = !this.getInstance.has(key);
|
|
51126
51301
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51127
51302
|
await instance.waitForInit(isInitial);
|
|
51128
|
-
return await instance.removeMemory(dto.memoryId);
|
|
51303
|
+
return await instance.removeMemory(dto.memoryId, dto.when);
|
|
51129
51304
|
};
|
|
51130
51305
|
/**
|
|
51131
51306
|
* Read a single entry from memory.
|
|
51132
51307
|
* @param dto.memoryId - Unique entry identifier
|
|
51133
51308
|
* @param dto.signalId - Signal identifier
|
|
51134
51309
|
* @param dto.bucketName - Bucket name
|
|
51310
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
51135
51311
|
* @returns Entry value
|
|
51136
51312
|
* @throws Error if entry not found
|
|
51137
51313
|
*/
|
|
@@ -51145,7 +51321,7 @@ class MemoryLiveAdapter {
|
|
|
51145
51321
|
const isInitial = !this.getInstance.has(key);
|
|
51146
51322
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51147
51323
|
await instance.waitForInit(isInitial);
|
|
51148
|
-
return await instance.readMemory(dto.memoryId);
|
|
51324
|
+
return await instance.readMemory(dto.memoryId, dto.when);
|
|
51149
51325
|
};
|
|
51150
51326
|
/**
|
|
51151
51327
|
* Switches to in-memory BM25 adapter.
|
|
@@ -51244,6 +51420,7 @@ class MemoryAdapter {
|
|
|
51244
51420
|
* @param dto.bucketName - Bucket name
|
|
51245
51421
|
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
51246
51422
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51423
|
+
* @param dto.when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
51247
51424
|
*/
|
|
51248
51425
|
this.writeMemory = async (dto) => {
|
|
51249
51426
|
if (!this.enable.hasValue()) {
|
|
@@ -51267,6 +51444,7 @@ class MemoryAdapter {
|
|
|
51267
51444
|
* @param dto.signalId - Signal identifier
|
|
51268
51445
|
* @param dto.bucketName - Bucket name
|
|
51269
51446
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51447
|
+
* @param dto.when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
51270
51448
|
* @returns Matching entries sorted by relevance score
|
|
51271
51449
|
*/
|
|
51272
51450
|
this.searchMemory = async (dto) => {
|
|
@@ -51290,6 +51468,7 @@ class MemoryAdapter {
|
|
|
51290
51468
|
* @param dto.signalId - Signal identifier
|
|
51291
51469
|
* @param dto.bucketName - Bucket name
|
|
51292
51470
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51471
|
+
* @param dto.when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
51293
51472
|
* @returns Array of all stored entries
|
|
51294
51473
|
*/
|
|
51295
51474
|
this.listMemory = async (dto) => {
|
|
@@ -51313,6 +51492,7 @@ class MemoryAdapter {
|
|
|
51313
51492
|
* @param dto.signalId - Signal identifier
|
|
51314
51493
|
* @param dto.bucketName - Bucket name
|
|
51315
51494
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51495
|
+
* @param dto.when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
51316
51496
|
*/
|
|
51317
51497
|
this.removeMemory = async (dto) => {
|
|
51318
51498
|
if (!this.enable.hasValue()) {
|
|
@@ -51336,6 +51516,7 @@ class MemoryAdapter {
|
|
|
51336
51516
|
* @param dto.signalId - Signal identifier
|
|
51337
51517
|
* @param dto.bucketName - Bucket name
|
|
51338
51518
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51519
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
51339
51520
|
* @returns Entry value
|
|
51340
51521
|
* @throws Error if entry not found
|
|
51341
51522
|
*/
|
|
@@ -51408,7 +51589,7 @@ async function writeMemory(dto) {
|
|
|
51408
51589
|
if (!MethodContextService.hasContext()) {
|
|
51409
51590
|
throw new Error("writeMemory requires a method context");
|
|
51410
51591
|
}
|
|
51411
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51592
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51412
51593
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51413
51594
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51414
51595
|
let signal;
|
|
@@ -51420,6 +51601,7 @@ async function writeMemory(dto) {
|
|
|
51420
51601
|
bucketName,
|
|
51421
51602
|
description,
|
|
51422
51603
|
backtest: isBacktest,
|
|
51604
|
+
when,
|
|
51423
51605
|
});
|
|
51424
51606
|
return;
|
|
51425
51607
|
}
|
|
@@ -51431,6 +51613,7 @@ async function writeMemory(dto) {
|
|
|
51431
51613
|
bucketName,
|
|
51432
51614
|
description,
|
|
51433
51615
|
backtest: isBacktest,
|
|
51616
|
+
when,
|
|
51434
51617
|
});
|
|
51435
51618
|
return;
|
|
51436
51619
|
}
|
|
@@ -51466,7 +51649,7 @@ async function readMemory(dto) {
|
|
|
51466
51649
|
if (!MethodContextService.hasContext()) {
|
|
51467
51650
|
throw new Error("readMemory requires a method context");
|
|
51468
51651
|
}
|
|
51469
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51652
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51470
51653
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51471
51654
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51472
51655
|
let signal;
|
|
@@ -51476,6 +51659,7 @@ async function readMemory(dto) {
|
|
|
51476
51659
|
signalId: signal.id,
|
|
51477
51660
|
bucketName,
|
|
51478
51661
|
backtest: isBacktest,
|
|
51662
|
+
when,
|
|
51479
51663
|
});
|
|
51480
51664
|
}
|
|
51481
51665
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -51484,6 +51668,7 @@ async function readMemory(dto) {
|
|
|
51484
51668
|
signalId: signal.id,
|
|
51485
51669
|
bucketName,
|
|
51486
51670
|
backtest: isBacktest,
|
|
51671
|
+
when,
|
|
51487
51672
|
});
|
|
51488
51673
|
}
|
|
51489
51674
|
throw new Error(`readMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
@@ -51518,7 +51703,7 @@ async function searchMemory(dto) {
|
|
|
51518
51703
|
if (!MethodContextService.hasContext()) {
|
|
51519
51704
|
throw new Error("searchMemory requires a method context");
|
|
51520
51705
|
}
|
|
51521
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51706
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51522
51707
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51523
51708
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51524
51709
|
let signal;
|
|
@@ -51528,6 +51713,7 @@ async function searchMemory(dto) {
|
|
|
51528
51713
|
signalId: signal.id,
|
|
51529
51714
|
bucketName,
|
|
51530
51715
|
backtest: isBacktest,
|
|
51716
|
+
when,
|
|
51531
51717
|
});
|
|
51532
51718
|
}
|
|
51533
51719
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -51536,6 +51722,7 @@ async function searchMemory(dto) {
|
|
|
51536
51722
|
signalId: signal.id,
|
|
51537
51723
|
bucketName,
|
|
51538
51724
|
backtest: isBacktest,
|
|
51725
|
+
when,
|
|
51539
51726
|
});
|
|
51540
51727
|
}
|
|
51541
51728
|
throw new Error(`searchMemory requires a pending or scheduled signal for symbol=${symbol} query=${query}`);
|
|
@@ -51568,7 +51755,7 @@ async function listMemory(dto) {
|
|
|
51568
51755
|
if (!MethodContextService.hasContext()) {
|
|
51569
51756
|
throw new Error("listMemory requires a method context");
|
|
51570
51757
|
}
|
|
51571
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51758
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51572
51759
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51573
51760
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51574
51761
|
let signal;
|
|
@@ -51577,6 +51764,7 @@ async function listMemory(dto) {
|
|
|
51577
51764
|
signalId: signal.id,
|
|
51578
51765
|
bucketName,
|
|
51579
51766
|
backtest: isBacktest,
|
|
51767
|
+
when,
|
|
51580
51768
|
});
|
|
51581
51769
|
}
|
|
51582
51770
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -51584,6 +51772,7 @@ async function listMemory(dto) {
|
|
|
51584
51772
|
signalId: signal.id,
|
|
51585
51773
|
bucketName,
|
|
51586
51774
|
backtest: isBacktest,
|
|
51775
|
+
when,
|
|
51587
51776
|
});
|
|
51588
51777
|
}
|
|
51589
51778
|
throw new Error(`listMemory requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
@@ -51618,7 +51807,7 @@ async function removeMemory(dto) {
|
|
|
51618
51807
|
if (!MethodContextService.hasContext()) {
|
|
51619
51808
|
throw new Error("removeMemory requires a method context");
|
|
51620
51809
|
}
|
|
51621
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51810
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51622
51811
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51623
51812
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51624
51813
|
let signal;
|
|
@@ -51628,6 +51817,7 @@ async function removeMemory(dto) {
|
|
|
51628
51817
|
signalId: signal.id,
|
|
51629
51818
|
bucketName,
|
|
51630
51819
|
backtest: isBacktest,
|
|
51820
|
+
when,
|
|
51631
51821
|
});
|
|
51632
51822
|
return;
|
|
51633
51823
|
}
|
|
@@ -51637,6 +51827,7 @@ async function removeMemory(dto) {
|
|
|
51637
51827
|
signalId: signal.id,
|
|
51638
51828
|
bucketName,
|
|
51639
51829
|
backtest: isBacktest,
|
|
51830
|
+
when,
|
|
51640
51831
|
});
|
|
51641
51832
|
return;
|
|
51642
51833
|
}
|
|
@@ -51902,6 +52093,9 @@ class DumpMemoryInstance {
|
|
|
51902
52093
|
value: { messages },
|
|
51903
52094
|
description,
|
|
51904
52095
|
backtest: this.backtest,
|
|
52096
|
+
// when=0: dumps are UI artifacts (agent transcripts, markdown reports);
|
|
52097
|
+
// they must stay visible regardless of the reader's logical time.
|
|
52098
|
+
when: new Date(0)
|
|
51905
52099
|
});
|
|
51906
52100
|
}
|
|
51907
52101
|
/**
|
|
@@ -51924,6 +52118,7 @@ class DumpMemoryInstance {
|
|
|
51924
52118
|
value: record,
|
|
51925
52119
|
description,
|
|
51926
52120
|
backtest: this.backtest,
|
|
52121
|
+
when: new Date(0)
|
|
51927
52122
|
});
|
|
51928
52123
|
}
|
|
51929
52124
|
/**
|
|
@@ -51947,6 +52142,7 @@ class DumpMemoryInstance {
|
|
|
51947
52142
|
value: { rows },
|
|
51948
52143
|
description,
|
|
51949
52144
|
backtest: this.backtest,
|
|
52145
|
+
when: new Date(0)
|
|
51950
52146
|
});
|
|
51951
52147
|
}
|
|
51952
52148
|
/**
|
|
@@ -51969,6 +52165,7 @@ class DumpMemoryInstance {
|
|
|
51969
52165
|
value: { content },
|
|
51970
52166
|
description,
|
|
51971
52167
|
backtest: this.backtest,
|
|
52168
|
+
when: new Date(0)
|
|
51972
52169
|
});
|
|
51973
52170
|
}
|
|
51974
52171
|
/**
|
|
@@ -51991,6 +52188,7 @@ class DumpMemoryInstance {
|
|
|
51991
52188
|
value: { content },
|
|
51992
52189
|
description,
|
|
51993
52190
|
backtest: this.backtest,
|
|
52191
|
+
when: new Date(0)
|
|
51994
52192
|
});
|
|
51995
52193
|
}
|
|
51996
52194
|
/**
|
|
@@ -52014,6 +52212,7 @@ class DumpMemoryInstance {
|
|
|
52014
52212
|
value: json,
|
|
52015
52213
|
description,
|
|
52016
52214
|
backtest: this.backtest,
|
|
52215
|
+
when: new Date(0)
|
|
52017
52216
|
});
|
|
52018
52217
|
}
|
|
52019
52218
|
/** Releases resources held by this instance. */
|
|
@@ -60975,8 +61174,8 @@ const CACHE_METHOD_NAME_CLEAR = "CacheUtils.clear";
|
|
|
60975
61174
|
const CACHE_METHOD_NAME_RESET_COUNTER = "CacheUtils.resetCounter";
|
|
60976
61175
|
const CACHE_FILE_INSTANCE_METHOD_NAME_RUN = "CacheFileInstance.run";
|
|
60977
61176
|
const CACHE_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "CacheFileInstance.hasValue";
|
|
60978
|
-
const MS_PER_MINUTE$
|
|
60979
|
-
const INTERVAL_MINUTES$
|
|
61177
|
+
const MS_PER_MINUTE$2 = 60000;
|
|
61178
|
+
const INTERVAL_MINUTES$2 = {
|
|
60980
61179
|
"1m": 1,
|
|
60981
61180
|
"3m": 3,
|
|
60982
61181
|
"5m": 5,
|
|
@@ -61010,11 +61209,11 @@ const INTERVAL_MINUTES$1 = {
|
|
|
61010
61209
|
* ```
|
|
61011
61210
|
*/
|
|
61012
61211
|
const align$1 = (timestamp, interval) => {
|
|
61013
|
-
const intervalMinutes = INTERVAL_MINUTES$
|
|
61212
|
+
const intervalMinutes = INTERVAL_MINUTES$2[interval];
|
|
61014
61213
|
if (!intervalMinutes) {
|
|
61015
61214
|
throw new Error(`align: unknown interval=${interval}`);
|
|
61016
61215
|
}
|
|
61017
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
61216
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$2;
|
|
61018
61217
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
61019
61218
|
};
|
|
61020
61219
|
/**
|
|
@@ -61103,7 +61302,7 @@ class CacheFnInstance {
|
|
|
61103
61302
|
*/
|
|
61104
61303
|
this.run = (...args) => {
|
|
61105
61304
|
backtest.loggerService.debug(CACHE_METHOD_NAME_RUN, { args });
|
|
61106
|
-
const step = INTERVAL_MINUTES$
|
|
61305
|
+
const step = INTERVAL_MINUTES$2[this.interval];
|
|
61107
61306
|
{
|
|
61108
61307
|
if (!MethodContextService.hasContext()) {
|
|
61109
61308
|
throw new Error("CacheFnInstance run requires method context");
|
|
@@ -61298,7 +61497,7 @@ class CacheFileInstance {
|
|
|
61298
61497
|
*/
|
|
61299
61498
|
this.run = async (...args) => {
|
|
61300
61499
|
backtest.loggerService.debug(CACHE_FILE_INSTANCE_METHOD_NAME_RUN, { args });
|
|
61301
|
-
const step = INTERVAL_MINUTES$
|
|
61500
|
+
const step = INTERVAL_MINUTES$2[this.interval];
|
|
61302
61501
|
{
|
|
61303
61502
|
if (!MethodContextService.hasContext()) {
|
|
61304
61503
|
throw new Error("CacheFileInstance run requires method context");
|
|
@@ -61320,7 +61519,7 @@ class CacheFileInstance {
|
|
|
61320
61519
|
return cached.data;
|
|
61321
61520
|
}
|
|
61322
61521
|
const result = await this.fn.call(null, ...args);
|
|
61323
|
-
await PersistMeasureAdapter.writeMeasureData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
|
|
61522
|
+
await PersistMeasureAdapter.writeMeasureData({ id: entityKey, data: result, removed: false }, bucket, entityKey, when);
|
|
61324
61523
|
return result;
|
|
61325
61524
|
};
|
|
61326
61525
|
/**
|
|
@@ -61609,8 +61808,8 @@ const INTERVAL_METHOD_NAME_FILE_HAS_VALUE = "IntervalUtils.file.hasValue";
|
|
|
61609
61808
|
const INTERVAL_METHOD_NAME_DISPOSE = "IntervalUtils.dispose";
|
|
61610
61809
|
const INTERVAL_METHOD_NAME_CLEAR = "IntervalUtils.clear";
|
|
61611
61810
|
const INTERVAL_METHOD_NAME_RESET_COUNTER = "IntervalUtils.resetCounter";
|
|
61612
|
-
const MS_PER_MINUTE = 60000;
|
|
61613
|
-
const INTERVAL_MINUTES = {
|
|
61811
|
+
const MS_PER_MINUTE$1 = 60000;
|
|
61812
|
+
const INTERVAL_MINUTES$1 = {
|
|
61614
61813
|
"1m": 1,
|
|
61615
61814
|
"3m": 3,
|
|
61616
61815
|
"5m": 5,
|
|
@@ -61640,11 +61839,11 @@ const INTERVAL_MINUTES = {
|
|
|
61640
61839
|
* ```
|
|
61641
61840
|
*/
|
|
61642
61841
|
const align = (timestamp, interval) => {
|
|
61643
|
-
const intervalMinutes = INTERVAL_MINUTES[interval];
|
|
61842
|
+
const intervalMinutes = INTERVAL_MINUTES$1[interval];
|
|
61644
61843
|
if (!intervalMinutes) {
|
|
61645
61844
|
throw new Error(`align: unknown interval=${interval}`);
|
|
61646
61845
|
}
|
|
61647
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE;
|
|
61846
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$1;
|
|
61648
61847
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
61649
61848
|
};
|
|
61650
61849
|
/**
|
|
@@ -61719,7 +61918,7 @@ class IntervalFnInstance {
|
|
|
61719
61918
|
*/
|
|
61720
61919
|
this.run = (...args) => {
|
|
61721
61920
|
backtest.loggerService.debug(INTERVAL_METHOD_NAME_RUN, { args });
|
|
61722
|
-
const step = INTERVAL_MINUTES[this.interval];
|
|
61921
|
+
const step = INTERVAL_MINUTES$1[this.interval];
|
|
61723
61922
|
{
|
|
61724
61923
|
if (!MethodContextService.hasContext()) {
|
|
61725
61924
|
throw new Error("IntervalFnInstance run requires method context");
|
|
@@ -61886,7 +62085,7 @@ class IntervalFileInstance {
|
|
|
61886
62085
|
*/
|
|
61887
62086
|
this.run = async (...args) => {
|
|
61888
62087
|
backtest.loggerService.debug(INTERVAL_FILE_INSTANCE_METHOD_NAME_RUN, { args });
|
|
61889
|
-
const step = INTERVAL_MINUTES[this.interval];
|
|
62088
|
+
const step = INTERVAL_MINUTES$1[this.interval];
|
|
61890
62089
|
{
|
|
61891
62090
|
if (!MethodContextService.hasContext()) {
|
|
61892
62091
|
throw new Error("IntervalFileInstance run requires method context");
|
|
@@ -61909,7 +62108,7 @@ class IntervalFileInstance {
|
|
|
61909
62108
|
}
|
|
61910
62109
|
const result = await this.fn.call(null, ...args);
|
|
61911
62110
|
if (result !== null) {
|
|
61912
|
-
await PersistIntervalAdapter.writeIntervalData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
|
|
62111
|
+
await PersistIntervalAdapter.writeIntervalData({ id: entityKey, data: result, when, removed: false }, bucket, entityKey, when);
|
|
61913
62112
|
}
|
|
61914
62113
|
return result;
|
|
61915
62114
|
};
|
|
@@ -63033,6 +63232,31 @@ class ActionBase {
|
|
|
63033
63232
|
// @ts-ignore
|
|
63034
63233
|
ActionBase = makeExtendable(ActionBase);
|
|
63035
63234
|
|
|
63235
|
+
const MS_PER_MINUTE = 60000;
|
|
63236
|
+
const INTERVAL_MINUTES = {
|
|
63237
|
+
"1m": 1,
|
|
63238
|
+
"3m": 3,
|
|
63239
|
+
"5m": 5,
|
|
63240
|
+
"15m": 15,
|
|
63241
|
+
"30m": 30,
|
|
63242
|
+
"1h": 60,
|
|
63243
|
+
"2h": 120,
|
|
63244
|
+
"4h": 240,
|
|
63245
|
+
"6h": 360,
|
|
63246
|
+
"8h": 480,
|
|
63247
|
+
"1d": 1440,
|
|
63248
|
+
};
|
|
63249
|
+
/**
|
|
63250
|
+
* Returns the step in milliseconds for a given candle interval.
|
|
63251
|
+
* For example, for "15m" interval, it returns 900000 (15 * 60 * 1000).
|
|
63252
|
+
*
|
|
63253
|
+
* @param interval - Candle interval (e.g., "1m", "15m", "1h")
|
|
63254
|
+
* @returns Step in milliseconds corresponding to the interval
|
|
63255
|
+
*/
|
|
63256
|
+
const intervalStepMs = (interval) => {
|
|
63257
|
+
return INTERVAL_MINUTES[interval] * MS_PER_MINUTE;
|
|
63258
|
+
};
|
|
63259
|
+
|
|
63036
63260
|
/**
|
|
63037
63261
|
* Rounds a price to the appropriate precision based on the tick size.
|
|
63038
63262
|
*
|
|
@@ -63299,4 +63523,4 @@ const validateSignal = (signal, currentPrice) => {
|
|
|
63299
63523
|
return !errors.length;
|
|
63300
63524
|
};
|
|
63301
63525
|
|
|
63302
|
-
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
|
|
63526
|
+
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, intervalStepMs, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
|