@backtest-kit/mongo 0.0.1

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.
@@ -0,0 +1,2255 @@
1
+ 'use strict';
2
+
3
+ var backtestKit = require('backtest-kit');
4
+ var functoolsKit = require('functools-kit');
5
+ var diKit = require('di-kit');
6
+ var mongoose = require('mongoose');
7
+ var ioredis = require('ioredis');
8
+ var diFactory = require('di-factory');
9
+
10
+ const GLOBAL_CONFIG = {
11
+ CC_REDIS_HOST: "",
12
+ CC_REDIS_PORT: 0,
13
+ CC_REDIS_USER: "",
14
+ CC_REDIS_PASSWORD: "",
15
+ CC_MONGO_CONNECTION_STRING: "",
16
+ };
17
+ Object.freeze({ ...GLOBAL_CONFIG });
18
+ const getConfig = () => {
19
+ const config = {
20
+ CC_REDIS_HOST: process.env.CC_REDIS_HOST || "127.0.0.1",
21
+ CC_REDIS_PORT: parseInt(process.env.CC_REDIS_PORT) || 6379,
22
+ CC_REDIS_USER: process.env.CC_REDIS_USER || "",
23
+ CC_REDIS_PASSWORD: process.env.CC_REDIS_PASSWORD || "",
24
+ CC_MONGO_CONNECTION_STRING: process.env.CC_MONGO_CONNECTION_STRING ||
25
+ "mongodb://localhost:27017/backtest-kit?wtimeoutMS=15000",
26
+ };
27
+ if (GLOBAL_CONFIG.CC_REDIS_HOST) {
28
+ config.CC_REDIS_HOST = GLOBAL_CONFIG.CC_REDIS_HOST;
29
+ }
30
+ if (GLOBAL_CONFIG.CC_REDIS_PORT) {
31
+ config.CC_REDIS_PORT = GLOBAL_CONFIG.CC_REDIS_PORT;
32
+ }
33
+ if (GLOBAL_CONFIG.CC_REDIS_USER) {
34
+ config.CC_REDIS_USER = GLOBAL_CONFIG.CC_REDIS_USER;
35
+ }
36
+ if (GLOBAL_CONFIG.CC_REDIS_PASSWORD) {
37
+ config.CC_REDIS_PASSWORD = GLOBAL_CONFIG.CC_REDIS_PASSWORD;
38
+ }
39
+ if (GLOBAL_CONFIG.CC_MONGO_CONNECTION_STRING) {
40
+ config.CC_MONGO_CONNECTION_STRING =
41
+ GLOBAL_CONFIG.CC_MONGO_CONNECTION_STRING;
42
+ }
43
+ return config;
44
+ };
45
+ const setConfig = (config) => {
46
+ Object.assign(GLOBAL_CONFIG, config);
47
+ };
48
+
49
+ const NOOP_LOGGER = {
50
+ log() {
51
+ },
52
+ debug() {
53
+ },
54
+ info() {
55
+ },
56
+ warn() {
57
+ },
58
+ };
59
+ class LoggerService {
60
+ constructor() {
61
+ this._commonLogger = NOOP_LOGGER;
62
+ this.log = async (topic, ...args) => {
63
+ await this._commonLogger.log(topic, ...args);
64
+ };
65
+ this.debug = async (topic, ...args) => {
66
+ await this._commonLogger.debug(topic, ...args);
67
+ };
68
+ this.info = async (topic, ...args) => {
69
+ await this._commonLogger.info(topic, ...args);
70
+ };
71
+ this.warn = async (topic, ...args) => {
72
+ await this._commonLogger.warn(topic, ...args);
73
+ };
74
+ this.setLogger = (logger) => {
75
+ this._commonLogger = logger;
76
+ };
77
+ }
78
+ }
79
+
80
+ const { init, inject, provide } = diKit.createActivator("mongo");
81
+
82
+ const baseServices$1 = {
83
+ loggerService: Symbol('loggerService'),
84
+ mongoService: Symbol('mongoService'),
85
+ redisService: Symbol('redisService'),
86
+ };
87
+ const cacheServices$1 = {
88
+ candleCacheService: Symbol('candleCacheService'),
89
+ signalCacheService: Symbol('signalCacheService'),
90
+ scheduleCacheService: Symbol('scheduleCacheService'),
91
+ riskCacheService: Symbol('riskCacheService'),
92
+ partialCacheService: Symbol('partialCacheService'),
93
+ breakevenCacheService: Symbol('breakevenCacheService'),
94
+ storageCacheService: Symbol('storageCacheService'),
95
+ notificationCacheService: Symbol('notificationCacheService'),
96
+ logCacheService: Symbol('logCacheService'),
97
+ measureCacheService: Symbol('measureCacheService'),
98
+ intervalCacheService: Symbol('intervalCacheService'),
99
+ memoryCacheService: Symbol('memoryCacheService'),
100
+ recentCacheService: Symbol('recentCacheService'),
101
+ stateCacheService: Symbol('stateCacheService'),
102
+ sessionCacheService: Symbol('sessionCacheService'),
103
+ };
104
+ const dbServices$1 = {
105
+ candleDbService: Symbol('candleDbService'),
106
+ signalDbService: Symbol('signalDbService'),
107
+ scheduleDbService: Symbol('scheduleDbService'),
108
+ riskDbService: Symbol('riskDbService'),
109
+ partialDbService: Symbol('partialDbService'),
110
+ breakevenDbService: Symbol('breakevenDbService'),
111
+ storageDbService: Symbol('storageDbService'),
112
+ notificationDbService: Symbol('notificationDbService'),
113
+ logDbService: Symbol('logDbService'),
114
+ measureDbService: Symbol('measureDbService'),
115
+ intervalDbService: Symbol('intervalDbService'),
116
+ memoryDbService: Symbol('memoryDbService'),
117
+ recentDbService: Symbol('recentDbService'),
118
+ stateDbService: Symbol('stateDbService'),
119
+ sessionDbService: Symbol('sessionDbService'),
120
+ };
121
+ const TYPES = {
122
+ ...baseServices$1,
123
+ ...cacheServices$1,
124
+ ...dbServices$1,
125
+ };
126
+
127
+ const getMongo = functoolsKit.singleshot(async () => {
128
+ const GLOBAL_CONFIG = getConfig();
129
+ const mongo = mongoose.connection.readyState === 0
130
+ ? await mongoose.connect(GLOBAL_CONFIG.CC_MONGO_CONNECTION_STRING)
131
+ : mongoose;
132
+ mongo.connection.once("error", (err) => {
133
+ console.error(functoolsKit.errorData(err));
134
+ });
135
+ process.once("SIGINT", async () => {
136
+ await mongo.connection.close();
137
+ });
138
+ return mongo;
139
+ });
140
+
141
+ const CONNECTED_STATE = 1;
142
+ const CONNECTION_TIMEOUT$1 = 15000;
143
+ const TIMEOUT_SYMBOL$1 = Symbol("timeout");
144
+ const waitForConnect$1 = (mongoose, self) => new Promise((resolve) => {
145
+ mongoose.connection.on("connected", () => {
146
+ self.loggerService.log("mongooseService Mongo connected to the database");
147
+ resolve();
148
+ });
149
+ });
150
+ class MongooseService {
151
+ constructor() {
152
+ this.loggerService = inject(TYPES.loggerService);
153
+ this.waitForInit = functoolsKit.singleshot(async () => {
154
+ this.loggerService.log("mongooseService waitForInit");
155
+ const mongoose = await getMongo();
156
+ if (mongoose.connection.readyState === CONNECTED_STATE) {
157
+ return mongoose;
158
+ }
159
+ const result = await Promise.race([
160
+ waitForConnect$1(mongoose, this),
161
+ functoolsKit.sleep(CONNECTION_TIMEOUT$1).then(() => TIMEOUT_SYMBOL$1),
162
+ ]);
163
+ if (result === TIMEOUT_SYMBOL$1) {
164
+ this.waitForInit.clear();
165
+ throw new Error("Mongoose connection timeout");
166
+ }
167
+ return mongoose;
168
+ });
169
+ this.init = async () => {
170
+ this.loggerService.log("mongooseService init");
171
+ const mongoose = await this.waitForInit();
172
+ mongoose.connection.on("connected", () => {
173
+ this.loggerService.log("mongooseService Mongo connected to the database");
174
+ });
175
+ mongoose.connection.on("error", (err) => {
176
+ this.loggerService.log("mongooseService Mongo error", {
177
+ error: functoolsKit.errorData(err),
178
+ });
179
+ throw new (class extends Error {
180
+ constructor() {
181
+ super("mongooseService Mongo error");
182
+ this.originalError = functoolsKit.errorData(err);
183
+ }
184
+ })();
185
+ });
186
+ mongoose.connection.on("disconnected", () => {
187
+ this.loggerService.log("mongooseService disconnected from the database.");
188
+ });
189
+ mongoose.connection.on("reconnected", () => {
190
+ this.loggerService.log("mongooseService reconnected to the database.");
191
+ });
192
+ process.on("SIGINT", async () => {
193
+ await mongoose.connection.close();
194
+ });
195
+ };
196
+ }
197
+ }
198
+
199
+ const getRedis = functoolsKit.singleshot(() => {
200
+ const GLOBAL_CONFIG = getConfig();
201
+ const redis = new ioredis.Redis({
202
+ host: GLOBAL_CONFIG.CC_REDIS_HOST,
203
+ port: GLOBAL_CONFIG.CC_REDIS_PORT,
204
+ username: GLOBAL_CONFIG.CC_REDIS_USER,
205
+ password: GLOBAL_CONFIG.CC_REDIS_PASSWORD,
206
+ });
207
+ setInterval(async () => {
208
+ await redis.ping();
209
+ }, 30000);
210
+ process.on("SIGINT", async () => {
211
+ await redis.disconnect(false);
212
+ });
213
+ return redis;
214
+ });
215
+
216
+ const CONNECTION_TIMEOUT = 15000;
217
+ const TIMEOUT_SYMBOL = Symbol('timeout');
218
+ const waitForConnect = (redis, self) => new Promise((resolve) => {
219
+ redis.on('ready', () => {
220
+ self.loggerService.log("redisService ready");
221
+ resolve();
222
+ });
223
+ });
224
+ class RedisService {
225
+ constructor() {
226
+ this.loggerService = inject(TYPES.loggerService);
227
+ this.waitForInit = functoolsKit.singleshot(async () => {
228
+ this.loggerService.log("redisService waitForInit");
229
+ const redis = await getRedis();
230
+ if (redis.status === 'ready') {
231
+ return redis;
232
+ }
233
+ const result = await Promise.race([
234
+ waitForConnect(redis, this),
235
+ functoolsKit.sleep(CONNECTION_TIMEOUT).then(() => TIMEOUT_SYMBOL),
236
+ ]);
237
+ if (result === TIMEOUT_SYMBOL) {
238
+ this.waitForInit.clear();
239
+ throw new Error("Redis connection timeout");
240
+ }
241
+ return redis;
242
+ });
243
+ this.init = async () => {
244
+ this.loggerService.log("redisService init");
245
+ await this.waitForInit();
246
+ };
247
+ }
248
+ }
249
+
250
+ const ITERATOR_BATCH_SIZE = 100;
251
+ const DEFAULT_TTL_EXPIRE_SECONDS = 5 * 60;
252
+ const BaseMap = diFactory.factory(class BaseMap {
253
+ constructor(connectionKey, ttlExpireSeconds = DEFAULT_TTL_EXPIRE_SECONDS) {
254
+ this.connectionKey = connectionKey;
255
+ this.ttlExpireSeconds = ttlExpireSeconds;
256
+ this.loggerService = inject(TYPES.loggerService);
257
+ }
258
+ _getItemKey(key) {
259
+ return `${this.connectionKey}:${key}`;
260
+ }
261
+ async set(key, value) {
262
+ if (!key)
263
+ throw new Error("Key cannot be empty");
264
+ this.loggerService.info(`BaseMap set key=${key}`, { key, value });
265
+ const redis = await getRedis();
266
+ const itemKey = this._getItemKey(key);
267
+ await redis.set(itemKey, value);
268
+ if (this.ttlExpireSeconds !== -1) {
269
+ await redis.expire(itemKey, this.ttlExpireSeconds);
270
+ }
271
+ }
272
+ async get(key) {
273
+ this.loggerService.info(`BaseMap get key=${key}`);
274
+ if (key === null) {
275
+ return null;
276
+ }
277
+ const redis = await getRedis();
278
+ const value = await redis.get(this._getItemKey(key));
279
+ return value ?? null;
280
+ }
281
+ async delete(key) {
282
+ this.loggerService.info(`BaseMap delete key=${key}`);
283
+ if (key === null) {
284
+ return null;
285
+ }
286
+ const redis = await getRedis();
287
+ await redis.del(this._getItemKey(key));
288
+ }
289
+ async has(key) {
290
+ this.loggerService.info(`BaseMap has key=${key}`);
291
+ if (key === null) {
292
+ return false;
293
+ }
294
+ const redis = await getRedis();
295
+ const exists = await redis.exists(this._getItemKey(key));
296
+ return exists === 1;
297
+ }
298
+ async clear() {
299
+ this.loggerService.info(`BaseMap clear`);
300
+ const redis = await getRedis();
301
+ let cursor = 0;
302
+ const pattern = `${this.connectionKey}:*`;
303
+ while (true) {
304
+ const [nextCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", ITERATOR_BATCH_SIZE);
305
+ cursor = nextCursor;
306
+ if (keys?.length) {
307
+ await redis.del(...keys);
308
+ }
309
+ if (cursor === "0" || cursor === 0)
310
+ break;
311
+ }
312
+ }
313
+ async toArray() {
314
+ this.loggerService.info(`BaseMap toArray`);
315
+ const redis = await getRedis();
316
+ const result = [];
317
+ let cursor = 0;
318
+ const pattern = `${this.connectionKey}:*`;
319
+ while (true) {
320
+ const [nextCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", ITERATOR_BATCH_SIZE);
321
+ cursor = nextCursor;
322
+ if (keys?.length) {
323
+ const values = await redis.mget(...keys);
324
+ for (let i = 0; i < keys.length; i++) {
325
+ if (typeof values[i] === "string") {
326
+ const key = keys[i].substring(this.connectionKey.length + 1);
327
+ result.push([key, values[i]]);
328
+ }
329
+ }
330
+ }
331
+ if (cursor === "0" || cursor === 0)
332
+ break;
333
+ }
334
+ return result;
335
+ }
336
+ async *iterate() {
337
+ this.loggerService.info(`BaseMap iterate`);
338
+ const redis = await getRedis();
339
+ let cursor = 0;
340
+ const pattern = `${this.connectionKey}:*`;
341
+ while (true) {
342
+ const [nextCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", ITERATOR_BATCH_SIZE);
343
+ cursor = nextCursor;
344
+ if (keys?.length) {
345
+ const values = await redis.mget(...keys);
346
+ for (let i = 0; i < keys.length; i++) {
347
+ if (typeof values[i] === "string") {
348
+ const key = keys[i].substring(this.connectionKey.length + 1);
349
+ yield [key, values[i]];
350
+ }
351
+ }
352
+ }
353
+ if (cursor === "0" || cursor === 0)
354
+ break;
355
+ }
356
+ }
357
+ async *keys() {
358
+ this.loggerService.info(`BaseMap iterate keys`);
359
+ const redis = await getRedis();
360
+ let cursor = 0;
361
+ const pattern = `${this.connectionKey}:*`;
362
+ while (true) {
363
+ const [nextCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", ITERATOR_BATCH_SIZE);
364
+ cursor = nextCursor;
365
+ if (keys?.length) {
366
+ for (const fullKey of keys) {
367
+ const key = fullKey.substring(this.connectionKey.length + 1);
368
+ yield key;
369
+ }
370
+ }
371
+ if (cursor === "0" || cursor === 0)
372
+ break;
373
+ }
374
+ }
375
+ async *values() {
376
+ this.loggerService.info(`BaseMap iterate values`);
377
+ const redis = await getRedis();
378
+ let cursor = 0;
379
+ const pattern = `${this.connectionKey}:*`;
380
+ while (true) {
381
+ const [nextCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", ITERATOR_BATCH_SIZE);
382
+ cursor = nextCursor;
383
+ if (keys?.length) {
384
+ const values = await redis.mget(...keys);
385
+ for (const value of values) {
386
+ if (typeof value === "string") {
387
+ yield value;
388
+ }
389
+ }
390
+ }
391
+ if (cursor === "0" || cursor === 0)
392
+ break;
393
+ }
394
+ }
395
+ async size() {
396
+ this.loggerService.info(`BaseMap size`);
397
+ const redis = await getRedis();
398
+ let cursor = 0;
399
+ const pattern = `${this.connectionKey}:*`;
400
+ let count = 0;
401
+ while (true) {
402
+ const [nextCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", ITERATOR_BATCH_SIZE);
403
+ cursor = nextCursor;
404
+ count += keys.length;
405
+ if (cursor === "0" || cursor === 0)
406
+ break;
407
+ }
408
+ return count;
409
+ }
410
+ });
411
+
412
+ const REDIS_KEY$e = "candle_cache";
413
+ class CandleCacheService extends BaseMap(REDIS_KEY$e, -1) {
414
+ constructor() {
415
+ super(...arguments);
416
+ this.loggerService = inject(TYPES.loggerService);
417
+ }
418
+ _cacheKey(symbol, interval, exchangeName, timestamp) {
419
+ return `${exchangeName}:${symbol}:${interval}:${timestamp}`;
420
+ }
421
+ async hasCandleId(symbol, interval, exchangeName, timestamp) {
422
+ this.loggerService.log("candleCacheService getCandleId", {
423
+ symbol,
424
+ interval,
425
+ exchangeName,
426
+ timestamp,
427
+ });
428
+ const key = this._cacheKey(symbol, interval, exchangeName, timestamp);
429
+ return await this.has(key);
430
+ }
431
+ async getCandleId(symbol, interval, exchangeName, timestamp) {
432
+ this.loggerService.log("candleCacheService getCandleId", {
433
+ symbol,
434
+ interval,
435
+ exchangeName,
436
+ timestamp,
437
+ });
438
+ const key = this._cacheKey(symbol, interval, exchangeName, timestamp);
439
+ const id = await super.get(key);
440
+ return id ?? null;
441
+ }
442
+ async setCandleId(row) {
443
+ this.loggerService.log(`candleCacheService setCandleId`, {
444
+ symbol: row.symbol,
445
+ interval: row.interval,
446
+ timestamp: row.timestamp
447
+ });
448
+ const key = this._cacheKey(row.symbol, row.interval, row.exchangeName, row.timestamp);
449
+ await super.set(key, row.id);
450
+ return row.id;
451
+ }
452
+ }
453
+
454
+ const REDIS_KEY$d = "signal_cache";
455
+ class SignalCacheService extends BaseMap(REDIS_KEY$d, -1) {
456
+ constructor() {
457
+ super(...arguments);
458
+ this.loggerService = inject(TYPES.loggerService);
459
+ }
460
+ _cacheKey(symbol, strategyName, exchangeName) {
461
+ return `${exchangeName}:${strategyName}:${symbol}`;
462
+ }
463
+ async hasSignalId(symbol, strategyName, exchangeName) {
464
+ this.loggerService.log("signalCacheService hasSignalId", { symbol, strategyName, exchangeName });
465
+ return await this.has(this._cacheKey(symbol, strategyName, exchangeName));
466
+ }
467
+ async getSignalId(symbol, strategyName, exchangeName) {
468
+ this.loggerService.log("signalCacheService getSignalId", { symbol, strategyName, exchangeName });
469
+ const id = await super.get(this._cacheKey(symbol, strategyName, exchangeName));
470
+ return id ?? null;
471
+ }
472
+ async setSignalId(row) {
473
+ this.loggerService.log("signalCacheService setSignalId", {
474
+ symbol: row.symbol,
475
+ strategyName: row.strategyName,
476
+ exchangeName: row.exchangeName,
477
+ });
478
+ await super.set(this._cacheKey(row.symbol, row.strategyName, row.exchangeName), row.id);
479
+ return row.id;
480
+ }
481
+ }
482
+
483
+ const REDIS_KEY$c = "schedule_cache";
484
+ class ScheduleCacheService extends BaseMap(REDIS_KEY$c, -1) {
485
+ constructor() {
486
+ super(...arguments);
487
+ this.loggerService = inject(TYPES.loggerService);
488
+ }
489
+ _cacheKey(symbol, strategyName, exchangeName) {
490
+ return `${exchangeName}:${strategyName}:${symbol}`;
491
+ }
492
+ async hasScheduleId(symbol, strategyName, exchangeName) {
493
+ this.loggerService.log("scheduleCacheService hasScheduleId", { symbol, strategyName, exchangeName });
494
+ return await this.has(this._cacheKey(symbol, strategyName, exchangeName));
495
+ }
496
+ async getScheduleId(symbol, strategyName, exchangeName) {
497
+ this.loggerService.log("scheduleCacheService getScheduleId", { symbol, strategyName, exchangeName });
498
+ const id = await super.get(this._cacheKey(symbol, strategyName, exchangeName));
499
+ return id ?? null;
500
+ }
501
+ async setScheduleId(row) {
502
+ this.loggerService.log("scheduleCacheService setScheduleId", {
503
+ symbol: row.symbol,
504
+ strategyName: row.strategyName,
505
+ exchangeName: row.exchangeName,
506
+ });
507
+ await super.set(this._cacheKey(row.symbol, row.strategyName, row.exchangeName), row.id);
508
+ return row.id;
509
+ }
510
+ }
511
+
512
+ const REDIS_KEY$b = "risk_cache";
513
+ class RiskCacheService extends BaseMap(REDIS_KEY$b, -1) {
514
+ constructor() {
515
+ super(...arguments);
516
+ this.loggerService = inject(TYPES.loggerService);
517
+ }
518
+ _cacheKey(riskName, exchangeName) {
519
+ return `${exchangeName}:${riskName}`;
520
+ }
521
+ async hasRiskId(riskName, exchangeName) {
522
+ this.loggerService.log("riskCacheService hasRiskId", { riskName, exchangeName });
523
+ return await this.has(this._cacheKey(riskName, exchangeName));
524
+ }
525
+ async getRiskId(riskName, exchangeName) {
526
+ this.loggerService.log("riskCacheService getRiskId", { riskName, exchangeName });
527
+ const id = await super.get(this._cacheKey(riskName, exchangeName));
528
+ return id ?? null;
529
+ }
530
+ async setRiskId(row) {
531
+ this.loggerService.log("riskCacheService setRiskId", {
532
+ riskName: row.riskName,
533
+ exchangeName: row.exchangeName,
534
+ });
535
+ await super.set(this._cacheKey(row.riskName, row.exchangeName), row.id);
536
+ return row.id;
537
+ }
538
+ }
539
+
540
+ const REDIS_KEY$a = "partial_cache";
541
+ class PartialCacheService extends BaseMap(REDIS_KEY$a, -1) {
542
+ constructor() {
543
+ super(...arguments);
544
+ this.loggerService = inject(TYPES.loggerService);
545
+ }
546
+ _cacheKey(symbol, strategyName, exchangeName, signalId) {
547
+ return `${exchangeName}:${strategyName}:${symbol}:${signalId}`;
548
+ }
549
+ async hasPartialId(symbol, strategyName, exchangeName, signalId) {
550
+ this.loggerService.log("partialCacheService hasPartialId", { symbol, strategyName, exchangeName, signalId });
551
+ return await this.has(this._cacheKey(symbol, strategyName, exchangeName, signalId));
552
+ }
553
+ async getPartialId(symbol, strategyName, exchangeName, signalId) {
554
+ this.loggerService.log("partialCacheService getPartialId", { symbol, strategyName, exchangeName, signalId });
555
+ const id = await super.get(this._cacheKey(symbol, strategyName, exchangeName, signalId));
556
+ return id ?? null;
557
+ }
558
+ async setPartialId(row) {
559
+ this.loggerService.log("partialCacheService setPartialId", {
560
+ symbol: row.symbol,
561
+ strategyName: row.strategyName,
562
+ exchangeName: row.exchangeName,
563
+ signalId: row.signalId,
564
+ });
565
+ await super.set(this._cacheKey(row.symbol, row.strategyName, row.exchangeName, row.signalId), row.id);
566
+ return row.id;
567
+ }
568
+ }
569
+
570
+ const REDIS_KEY$9 = "breakeven_cache";
571
+ class BreakevenCacheService extends BaseMap(REDIS_KEY$9, -1) {
572
+ constructor() {
573
+ super(...arguments);
574
+ this.loggerService = inject(TYPES.loggerService);
575
+ }
576
+ _cacheKey(symbol, strategyName, exchangeName, signalId) {
577
+ return `${exchangeName}:${strategyName}:${symbol}:${signalId}`;
578
+ }
579
+ async hasBreakevenId(symbol, strategyName, exchangeName, signalId) {
580
+ this.loggerService.log("breakevenCacheService hasBreakevenId", { symbol, strategyName, exchangeName, signalId });
581
+ return await this.has(this._cacheKey(symbol, strategyName, exchangeName, signalId));
582
+ }
583
+ async getBreakevenId(symbol, strategyName, exchangeName, signalId) {
584
+ this.loggerService.log("breakevenCacheService getBreakevenId", { symbol, strategyName, exchangeName, signalId });
585
+ const id = await super.get(this._cacheKey(symbol, strategyName, exchangeName, signalId));
586
+ return id ?? null;
587
+ }
588
+ async setBreakevenId(row) {
589
+ this.loggerService.log("breakevenCacheService setBreakevenId", {
590
+ symbol: row.symbol,
591
+ strategyName: row.strategyName,
592
+ exchangeName: row.exchangeName,
593
+ signalId: row.signalId,
594
+ });
595
+ await super.set(this._cacheKey(row.symbol, row.strategyName, row.exchangeName, row.signalId), row.id);
596
+ return row.id;
597
+ }
598
+ }
599
+
600
+ const REDIS_KEY$8 = "storage_cache";
601
+ class StorageCacheService extends BaseMap(REDIS_KEY$8, -1) {
602
+ constructor() {
603
+ super(...arguments);
604
+ this.loggerService = inject(TYPES.loggerService);
605
+ }
606
+ _cacheKey(backtest, signalId) {
607
+ return `${backtest ? "backtest" : "live"}:${signalId}`;
608
+ }
609
+ async hasStorageId(backtest, signalId) {
610
+ this.loggerService.log("storageCacheService hasStorageId", { backtest, signalId });
611
+ return await this.has(this._cacheKey(backtest, signalId));
612
+ }
613
+ async getStorageId(backtest, signalId) {
614
+ this.loggerService.log("storageCacheService getStorageId", { backtest, signalId });
615
+ const id = await super.get(this._cacheKey(backtest, signalId));
616
+ return id ?? null;
617
+ }
618
+ async setStorageId(row) {
619
+ this.loggerService.log("storageCacheService setStorageId", {
620
+ backtest: row.backtest,
621
+ signalId: row.signalId,
622
+ });
623
+ await super.set(this._cacheKey(row.backtest, row.signalId), row.id);
624
+ return row.id;
625
+ }
626
+ }
627
+
628
+ const REDIS_KEY$7 = "notification_cache";
629
+ class NotificationCacheService extends BaseMap(REDIS_KEY$7, -1) {
630
+ constructor() {
631
+ super(...arguments);
632
+ this.loggerService = inject(TYPES.loggerService);
633
+ }
634
+ _cacheKey(backtest, notificationId) {
635
+ return `${backtest ? "backtest" : "live"}:${notificationId}`;
636
+ }
637
+ async hasNotificationId(backtest, notificationId) {
638
+ this.loggerService.log("notificationCacheService hasNotificationId", { backtest, notificationId });
639
+ return await this.has(this._cacheKey(backtest, notificationId));
640
+ }
641
+ async getNotificationId(backtest, notificationId) {
642
+ this.loggerService.log("notificationCacheService getNotificationId", { backtest, notificationId });
643
+ const id = await super.get(this._cacheKey(backtest, notificationId));
644
+ return id ?? null;
645
+ }
646
+ async setNotificationId(row) {
647
+ this.loggerService.log("notificationCacheService setNotificationId", {
648
+ backtest: row.backtest,
649
+ notificationId: row.notificationId,
650
+ });
651
+ await super.set(this._cacheKey(row.backtest, row.notificationId), row.id);
652
+ return row.id;
653
+ }
654
+ }
655
+
656
+ const REDIS_KEY$6 = "log_cache";
657
+ class LogCacheService extends BaseMap(REDIS_KEY$6, -1) {
658
+ constructor() {
659
+ super(...arguments);
660
+ this.loggerService = inject(TYPES.loggerService);
661
+ }
662
+ async hasLogId(entryId) {
663
+ this.loggerService.log("logCacheService hasLogId", { entryId });
664
+ return await this.has(entryId);
665
+ }
666
+ async getLogId(entryId) {
667
+ this.loggerService.log("logCacheService getLogId", { entryId });
668
+ const id = await super.get(entryId);
669
+ return id ?? null;
670
+ }
671
+ async setLogId(row) {
672
+ this.loggerService.log("logCacheService setLogId", { entryId: row.entryId });
673
+ await super.set(row.entryId, row.id);
674
+ return row.id;
675
+ }
676
+ }
677
+
678
+ const REDIS_KEY$5 = "measure_cache";
679
+ class MeasureCacheService extends BaseMap(REDIS_KEY$5, -1) {
680
+ constructor() {
681
+ super(...arguments);
682
+ this.loggerService = inject(TYPES.loggerService);
683
+ }
684
+ _cacheKey(bucket, entryKey) {
685
+ return `${bucket}:${entryKey}`;
686
+ }
687
+ async hasMeasureId(bucket, entryKey) {
688
+ this.loggerService.log("measureCacheService hasMeasureId", { bucket, entryKey });
689
+ return await this.has(this._cacheKey(bucket, entryKey));
690
+ }
691
+ async getMeasureId(bucket, entryKey) {
692
+ this.loggerService.log("measureCacheService getMeasureId", { bucket, entryKey });
693
+ const id = await super.get(this._cacheKey(bucket, entryKey));
694
+ return id ?? null;
695
+ }
696
+ async setMeasureId(row) {
697
+ this.loggerService.log("measureCacheService setMeasureId", { bucket: row.bucket, entryKey: row.entryKey });
698
+ await super.set(this._cacheKey(row.bucket, row.entryKey), row.id);
699
+ return row.id;
700
+ }
701
+ }
702
+
703
+ const REDIS_KEY$4 = "interval_cache";
704
+ class IntervalCacheService extends BaseMap(REDIS_KEY$4, -1) {
705
+ constructor() {
706
+ super(...arguments);
707
+ this.loggerService = inject(TYPES.loggerService);
708
+ }
709
+ _cacheKey(bucket, entryKey) {
710
+ return `${bucket}:${entryKey}`;
711
+ }
712
+ async hasIntervalId(bucket, entryKey) {
713
+ this.loggerService.log("intervalCacheService hasIntervalId", { bucket, entryKey });
714
+ return await this.has(this._cacheKey(bucket, entryKey));
715
+ }
716
+ async getIntervalId(bucket, entryKey) {
717
+ this.loggerService.log("intervalCacheService getIntervalId", { bucket, entryKey });
718
+ const id = await super.get(this._cacheKey(bucket, entryKey));
719
+ return id ?? null;
720
+ }
721
+ async setIntervalId(row) {
722
+ this.loggerService.log("intervalCacheService setIntervalId", { bucket: row.bucket, entryKey: row.entryKey });
723
+ await super.set(this._cacheKey(row.bucket, row.entryKey), row.id);
724
+ return row.id;
725
+ }
726
+ async deleteIntervalId(bucket, entryKey) {
727
+ this.loggerService.log("intervalCacheService deleteIntervalId", { bucket, entryKey });
728
+ await super.delete(this._cacheKey(bucket, entryKey));
729
+ }
730
+ }
731
+
732
+ const REDIS_KEY$3 = "memory_cache";
733
+ class MemoryCacheService extends BaseMap(REDIS_KEY$3, -1) {
734
+ constructor() {
735
+ super(...arguments);
736
+ this.loggerService = inject(TYPES.loggerService);
737
+ }
738
+ _cacheKey(signalId, bucketName, memoryId) {
739
+ return `${signalId}:${bucketName}:${memoryId}`;
740
+ }
741
+ async hasMemoryEntryId(signalId, bucketName, memoryId) {
742
+ this.loggerService.log("memoryCacheService hasMemoryEntryId", { signalId, bucketName, memoryId });
743
+ return await this.has(this._cacheKey(signalId, bucketName, memoryId));
744
+ }
745
+ async getMemoryEntryId(signalId, bucketName, memoryId) {
746
+ this.loggerService.log("memoryCacheService getMemoryEntryId", { signalId, bucketName, memoryId });
747
+ const id = await super.get(this._cacheKey(signalId, bucketName, memoryId));
748
+ return id ?? null;
749
+ }
750
+ async setMemoryEntryId(row) {
751
+ this.loggerService.log("memoryCacheService setMemoryEntryId", {
752
+ signalId: row.signalId,
753
+ bucketName: row.bucketName,
754
+ memoryId: row.memoryId,
755
+ });
756
+ await super.set(this._cacheKey(row.signalId, row.bucketName, row.memoryId), row.id);
757
+ return row.id;
758
+ }
759
+ }
760
+
761
+ const REDIS_KEY$2 = "recent_cache";
762
+ class RecentCacheService extends BaseMap(REDIS_KEY$2, -1) {
763
+ constructor() {
764
+ super(...arguments);
765
+ this.loggerService = inject(TYPES.loggerService);
766
+ }
767
+ _cacheKey(symbol, strategyName, exchangeName, frameName, backtest) {
768
+ return `${backtest ? "backtest" : "live"}:${exchangeName}:${strategyName}:${frameName}:${symbol}`;
769
+ }
770
+ async hasRecentId(symbol, strategyName, exchangeName, frameName, backtest) {
771
+ this.loggerService.log("recentCacheService hasRecentId", { symbol, strategyName, exchangeName, frameName, backtest });
772
+ return await this.has(this._cacheKey(symbol, strategyName, exchangeName, frameName, backtest));
773
+ }
774
+ async getRecentId(symbol, strategyName, exchangeName, frameName, backtest) {
775
+ this.loggerService.log("recentCacheService getRecentId", { symbol, strategyName, exchangeName, frameName, backtest });
776
+ const id = await super.get(this._cacheKey(symbol, strategyName, exchangeName, frameName, backtest));
777
+ return id ?? null;
778
+ }
779
+ async setRecentId(row) {
780
+ this.loggerService.log("recentCacheService setRecentId", {
781
+ symbol: row.symbol,
782
+ strategyName: row.strategyName,
783
+ exchangeName: row.exchangeName,
784
+ frameName: row.frameName,
785
+ backtest: row.backtest,
786
+ });
787
+ await super.set(this._cacheKey(row.symbol, row.strategyName, row.exchangeName, row.frameName, row.backtest), row.id);
788
+ return row.id;
789
+ }
790
+ }
791
+
792
+ const REDIS_KEY$1 = "state_cache";
793
+ class StateCacheService extends BaseMap(REDIS_KEY$1, -1) {
794
+ constructor() {
795
+ super(...arguments);
796
+ this.loggerService = inject(TYPES.loggerService);
797
+ }
798
+ _cacheKey(signalId, bucketName) {
799
+ return `${signalId}:${bucketName}`;
800
+ }
801
+ async hasStateId(signalId, bucketName) {
802
+ this.loggerService.log("stateCacheService hasStateId", { signalId, bucketName });
803
+ return await this.has(this._cacheKey(signalId, bucketName));
804
+ }
805
+ async getStateId(signalId, bucketName) {
806
+ this.loggerService.log("stateCacheService getStateId", { signalId, bucketName });
807
+ const id = await super.get(this._cacheKey(signalId, bucketName));
808
+ return id ?? null;
809
+ }
810
+ async setStateId(row) {
811
+ this.loggerService.log("stateCacheService setStateId", {
812
+ signalId: row.signalId,
813
+ bucketName: row.bucketName,
814
+ });
815
+ await super.set(this._cacheKey(row.signalId, row.bucketName), row.id);
816
+ return row.id;
817
+ }
818
+ }
819
+
820
+ const REDIS_KEY = "session_cache";
821
+ class SessionCacheService extends BaseMap(REDIS_KEY, -1) {
822
+ constructor() {
823
+ super(...arguments);
824
+ this.loggerService = inject(TYPES.loggerService);
825
+ }
826
+ _cacheKey(strategyName, exchangeName, frameName) {
827
+ return `${exchangeName}:${strategyName}:${frameName}`;
828
+ }
829
+ async hasSessionId(strategyName, exchangeName, frameName) {
830
+ this.loggerService.log("sessionCacheService hasSessionId", { strategyName, exchangeName, frameName });
831
+ return await this.has(this._cacheKey(strategyName, exchangeName, frameName));
832
+ }
833
+ async getSessionId(strategyName, exchangeName, frameName) {
834
+ this.loggerService.log("sessionCacheService getSessionId", { strategyName, exchangeName, frameName });
835
+ const id = await super.get(this._cacheKey(strategyName, exchangeName, frameName));
836
+ return id ?? null;
837
+ }
838
+ async setSessionId(row) {
839
+ this.loggerService.log("sessionCacheService setSessionId", {
840
+ strategyName: row.strategyName,
841
+ exchangeName: row.exchangeName,
842
+ frameName: row.frameName,
843
+ });
844
+ await super.set(this._cacheKey(row.strategyName, row.exchangeName, row.frameName), row.id);
845
+ return row.id;
846
+ }
847
+ }
848
+
849
+ const readTransform = (data) => ({
850
+ ...data,
851
+ id: String(data._id),
852
+ });
853
+
854
+ function omit(obj, keys) {
855
+ const excluded = new Set(keys);
856
+ return Object.fromEntries(Object.entries(obj).filter(([key]) => !excluded.has(key)));
857
+ }
858
+
859
+ const FIND_ALL_LIMIT = 1000;
860
+ const BaseCRUD = diFactory.factory(class {
861
+ constructor(TargetModel) {
862
+ this.TargetModel = TargetModel;
863
+ this.loggerService = inject(TYPES.loggerService);
864
+ }
865
+ async create(dto) {
866
+ this.loggerService.info(`BaseCRUD create modelName=${this.TargetModel.modelName}`, {
867
+ dto,
868
+ });
869
+ const item = await this.TargetModel.create(dto);
870
+ return readTransform(item.toJSON());
871
+ }
872
+ async update(id, dto) {
873
+ this.loggerService.info(`BaseCRUD update modelName=${this.TargetModel.modelName}`, {
874
+ id,
875
+ dto,
876
+ });
877
+ const updatedDocument = await this.TargetModel.findByIdAndUpdate(id, omit(dto, "id"), {
878
+ new: true,
879
+ runValidators: true,
880
+ });
881
+ if (!updatedDocument) {
882
+ throw new Error(`${this.TargetModel.modelName} not found`);
883
+ }
884
+ return readTransform(updatedDocument.toJSON());
885
+ }
886
+ async findById(id) {
887
+ this.loggerService.info(`BaseCRUD findById modelName=${this.TargetModel.modelName}`, {
888
+ id,
889
+ });
890
+ const item = await this.TargetModel.findById(id);
891
+ if (!item) {
892
+ throw new Error(`${this.TargetModel.modelName} not found`);
893
+ }
894
+ return readTransform(item.toJSON());
895
+ }
896
+ async findByFilter(filterData, sort) {
897
+ this.loggerService.info(`BaseCRUD findByFilter modelName=${this.TargetModel.modelName}`, {
898
+ filterData,
899
+ sort,
900
+ });
901
+ const item = await this.TargetModel.findOne(filterData, null, {
902
+ sort,
903
+ });
904
+ if (item) {
905
+ return readTransform(item.toJSON());
906
+ }
907
+ return null;
908
+ }
909
+ async findAll(filterData = {}, limit = FIND_ALL_LIMIT) {
910
+ this.loggerService.info(`BaseCRUD findAll modelName=${this.TargetModel.modelName}`, {
911
+ filterData,
912
+ });
913
+ const documents = await this.TargetModel.find(filterData)
914
+ .sort({ date: -1 })
915
+ .limit(limit);
916
+ return documents.map((doc) => readTransform(doc.toJSON()));
917
+ }
918
+ async *iterate(filterData = {}, sort) {
919
+ this.loggerService.info(`BaseCRUD iterate modelName=${this.TargetModel.modelName}`, {
920
+ filterData,
921
+ sort,
922
+ });
923
+ for await (const document of this.TargetModel.find(filterData, null, {
924
+ sort,
925
+ })) {
926
+ yield readTransform(document.toJSON());
927
+ }
928
+ }
929
+ async paginate(filterData, pagination, sort) {
930
+ this.loggerService.info(`BaseCRUD paginate modelName=${this.TargetModel.modelName}`, {
931
+ filterData,
932
+ pagination,
933
+ sort,
934
+ });
935
+ const itemsRaw = await this.TargetModel.find(filterData, null, {
936
+ sort,
937
+ })
938
+ .skip(pagination.offset)
939
+ .limit(pagination.limit);
940
+ const items = itemsRaw.map((item) => item.toJSON());
941
+ const total = await this.TargetModel.countDocuments(filterData);
942
+ return {
943
+ rows: items.map(readTransform),
944
+ total: total,
945
+ };
946
+ }
947
+ });
948
+
949
+ const INTERVAL_ENUM = [
950
+ "1m",
951
+ "3m",
952
+ "5m",
953
+ "15m",
954
+ "30m",
955
+ "1h",
956
+ "2h",
957
+ "4h",
958
+ "6h",
959
+ "8h",
960
+ "1d",
961
+ ];
962
+ const CandleSchema = new mongoose.Schema({
963
+ symbol: { type: String, required: true, index: true },
964
+ interval: { type: String, required: true, enum: INTERVAL_ENUM, index: true },
965
+ timestamp: { type: Number, required: true, index: true },
966
+ exchangeName: { type: String, required: true, index: true },
967
+ open: { type: Number, required: true },
968
+ high: { type: Number, required: true },
969
+ low: { type: Number, required: true },
970
+ close: { type: Number, required: true },
971
+ volume: { type: Number, required: true },
972
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" } });
973
+ CandleSchema.index({ symbol: 1, interval: 1, timestamp: 1 }, { unique: true });
974
+ const CandleModel = mongoose.model("candle-items", CandleSchema);
975
+
976
+ const EXCHANGE_NAME = "ccxt_binance";
977
+ class CandleDbService extends BaseCRUD(CandleModel) {
978
+ constructor() {
979
+ super(...arguments);
980
+ this.loggerService = inject(TYPES.loggerService);
981
+ this.candleCacheService = inject(TYPES.candleCacheService);
982
+ this.create = async (dto) => {
983
+ this.loggerService.log("candleDbService create", { dto });
984
+ const filter = {
985
+ symbol: dto.symbol,
986
+ interval: dto.interval,
987
+ timestamp: dto.timestamp,
988
+ };
989
+ const insertOnly = {
990
+ exchangeName: EXCHANGE_NAME,
991
+ open: dto.open,
992
+ high: dto.high,
993
+ low: dto.low,
994
+ close: dto.close,
995
+ volume: dto.volume,
996
+ };
997
+ const document = await CandleModel.findOneAndUpdate(filter, { $setOnInsert: insertOnly }, { upsert: true, new: true, setDefaultsOnInsert: true });
998
+ const result = readTransform(document.toJSON());
999
+ await this.candleCacheService.setCandleId(result);
1000
+ return result;
1001
+ };
1002
+ this.hasCandle = async (symbol, interval, timestamp) => {
1003
+ this.loggerService.log("candleDbService hasCandle", {
1004
+ symbol,
1005
+ interval,
1006
+ timestamp,
1007
+ });
1008
+ const hasInCache = await this.candleCacheService.hasCandleId(symbol, interval, EXCHANGE_NAME, timestamp);
1009
+ if (hasInCache) {
1010
+ return true;
1011
+ }
1012
+ const hasInMongo = await this.findBySymbolIntervalTimestamp(symbol, interval, timestamp);
1013
+ if (hasInMongo) {
1014
+ return true;
1015
+ }
1016
+ return false;
1017
+ };
1018
+ this.findBySymbolIntervalTimestamp = async (symbol, interval, timestamp) => {
1019
+ this.loggerService.log("candleDbService findBySymbolIntervalTimestamp", { symbol, interval, timestamp });
1020
+ try {
1021
+ const cachedId = await this.candleCacheService.getCandleId(symbol, interval, EXCHANGE_NAME, timestamp);
1022
+ return await super.findById(cachedId);
1023
+ }
1024
+ catch {
1025
+ const result = await super.findByFilter({ symbol, interval, exchangeName: EXCHANGE_NAME, timestamp });
1026
+ if (result) {
1027
+ await this.candleCacheService.setCandleId(result);
1028
+ }
1029
+ return result;
1030
+ }
1031
+ };
1032
+ }
1033
+ }
1034
+
1035
+ const SignalSchema = new mongoose.Schema({
1036
+ symbol: { type: String, required: true, index: true },
1037
+ strategyName: { type: String, required: true, index: true },
1038
+ exchangeName: { type: String, required: true, index: true },
1039
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1040
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1041
+ SignalSchema.index({ symbol: 1, strategyName: 1, exchangeName: 1 }, { unique: true });
1042
+ const SignalModel = mongoose.model("signal-items", SignalSchema);
1043
+
1044
+ class SignalDbService extends BaseCRUD(SignalModel) {
1045
+ constructor() {
1046
+ super(...arguments);
1047
+ this.loggerService = inject(TYPES.loggerService);
1048
+ this.signalCacheService = inject(TYPES.signalCacheService);
1049
+ this.upsert = async (symbol, strategyName, exchangeName, payload) => {
1050
+ this.loggerService.log("signalDbService upsert", { symbol, strategyName, exchangeName });
1051
+ const filter = { symbol, strategyName, exchangeName };
1052
+ const document = await SignalModel.findOneAndUpdate(filter, { $set: { payload } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1053
+ const result = readTransform(document.toJSON());
1054
+ await this.signalCacheService.setSignalId(result);
1055
+ };
1056
+ this.findByContext = async (symbol, strategyName, exchangeName) => {
1057
+ this.loggerService.log("signalDbService findByContext", { symbol, strategyName, exchangeName });
1058
+ try {
1059
+ const cachedId = await this.signalCacheService.getSignalId(symbol, strategyName, exchangeName);
1060
+ if (cachedId) {
1061
+ return await super.findById(cachedId);
1062
+ }
1063
+ }
1064
+ catch {
1065
+ }
1066
+ const result = await super.findByFilter({ symbol, strategyName, exchangeName });
1067
+ if (result) {
1068
+ await this.signalCacheService.setSignalId(result);
1069
+ }
1070
+ return result;
1071
+ };
1072
+ }
1073
+ }
1074
+
1075
+ const ScheduleSchema = new mongoose.Schema({
1076
+ symbol: { type: String, required: true, index: true },
1077
+ strategyName: { type: String, required: true, index: true },
1078
+ exchangeName: { type: String, required: true, index: true },
1079
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1080
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1081
+ ScheduleSchema.index({ symbol: 1, strategyName: 1, exchangeName: 1 }, { unique: true });
1082
+ const ScheduleModel = mongoose.model("schedule-items", ScheduleSchema);
1083
+
1084
+ class ScheduleDbService extends BaseCRUD(ScheduleModel) {
1085
+ constructor() {
1086
+ super(...arguments);
1087
+ this.loggerService = inject(TYPES.loggerService);
1088
+ this.scheduleCacheService = inject(TYPES.scheduleCacheService);
1089
+ this.upsert = async (symbol, strategyName, exchangeName, payload) => {
1090
+ this.loggerService.log("scheduleDbService upsert", { symbol, strategyName, exchangeName });
1091
+ const filter = { symbol, strategyName, exchangeName };
1092
+ const document = await ScheduleModel.findOneAndUpdate(filter, { $set: { payload } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1093
+ const result = readTransform(document.toJSON());
1094
+ await this.scheduleCacheService.setScheduleId(result);
1095
+ };
1096
+ this.findByContext = async (symbol, strategyName, exchangeName) => {
1097
+ this.loggerService.log("scheduleDbService findByContext", { symbol, strategyName, exchangeName });
1098
+ try {
1099
+ const cachedId = await this.scheduleCacheService.getScheduleId(symbol, strategyName, exchangeName);
1100
+ if (cachedId) {
1101
+ return await super.findById(cachedId);
1102
+ }
1103
+ }
1104
+ catch {
1105
+ }
1106
+ const result = await super.findByFilter({ symbol, strategyName, exchangeName });
1107
+ if (result) {
1108
+ await this.scheduleCacheService.setScheduleId(result);
1109
+ }
1110
+ return result;
1111
+ };
1112
+ }
1113
+ }
1114
+
1115
+ const RiskSchema = new mongoose.Schema({
1116
+ riskName: { type: String, required: true, index: true },
1117
+ exchangeName: { type: String, required: true, index: true },
1118
+ positions: { type: mongoose.Schema.Types.Mixed, required: true, default: [] },
1119
+ when: { type: Number, required: true, index: true },
1120
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1121
+ RiskSchema.index({ riskName: 1, exchangeName: 1 }, { unique: true });
1122
+ const RiskModel = mongoose.model("risk-items", RiskSchema);
1123
+
1124
+ class RiskDbService extends BaseCRUD(RiskModel) {
1125
+ constructor() {
1126
+ super(...arguments);
1127
+ this.loggerService = inject(TYPES.loggerService);
1128
+ this.riskCacheService = inject(TYPES.riskCacheService);
1129
+ this.upsert = async (riskName, exchangeName, positions, when) => {
1130
+ this.loggerService.log("riskDbService upsert", { riskName, exchangeName, when });
1131
+ const filter = { riskName, exchangeName };
1132
+ const document = await RiskModel.findOneAndUpdate(filter, { $set: { positions, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1133
+ const result = readTransform(document.toJSON());
1134
+ await this.riskCacheService.setRiskId(result);
1135
+ };
1136
+ this.findByContext = async (riskName, exchangeName) => {
1137
+ this.loggerService.log("riskDbService findByContext", { riskName, exchangeName });
1138
+ try {
1139
+ const cachedId = await this.riskCacheService.getRiskId(riskName, exchangeName);
1140
+ if (cachedId) {
1141
+ return await super.findById(cachedId);
1142
+ }
1143
+ }
1144
+ catch {
1145
+ }
1146
+ const result = await super.findByFilter({ riskName, exchangeName });
1147
+ if (result) {
1148
+ await this.riskCacheService.setRiskId(result);
1149
+ }
1150
+ return result;
1151
+ };
1152
+ }
1153
+ }
1154
+
1155
+ const PartialSchema = new mongoose.Schema({
1156
+ symbol: { type: String, required: true, index: true },
1157
+ strategyName: { type: String, required: true, index: true },
1158
+ exchangeName: { type: String, required: true, index: true },
1159
+ signalId: { type: String, required: true, index: true },
1160
+ payload: { type: mongoose.Schema.Types.Mixed, required: true, default: {} },
1161
+ when: { type: Number, required: true, index: true },
1162
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1163
+ PartialSchema.index({ symbol: 1, strategyName: 1, exchangeName: 1, signalId: 1 }, { unique: true });
1164
+ const PartialModel = mongoose.model("partial-items", PartialSchema);
1165
+
1166
+ class PartialDbService extends BaseCRUD(PartialModel) {
1167
+ constructor() {
1168
+ super(...arguments);
1169
+ this.loggerService = inject(TYPES.loggerService);
1170
+ this.partialCacheService = inject(TYPES.partialCacheService);
1171
+ this.upsert = async (symbol, strategyName, exchangeName, signalId, payload, when) => {
1172
+ this.loggerService.log("partialDbService upsert", { symbol, strategyName, exchangeName, signalId, when });
1173
+ const filter = { symbol, strategyName, exchangeName, signalId };
1174
+ const document = await PartialModel.findOneAndUpdate(filter, { $set: { payload, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1175
+ const result = readTransform(document.toJSON());
1176
+ await this.partialCacheService.setPartialId(result);
1177
+ };
1178
+ this.findByContext = async (symbol, strategyName, exchangeName, signalId) => {
1179
+ this.loggerService.log("partialDbService findByContext", { symbol, strategyName, exchangeName, signalId });
1180
+ try {
1181
+ const cachedId = await this.partialCacheService.getPartialId(symbol, strategyName, exchangeName, signalId);
1182
+ if (cachedId) {
1183
+ return await super.findById(cachedId);
1184
+ }
1185
+ }
1186
+ catch {
1187
+ }
1188
+ const result = await super.findByFilter({ symbol, strategyName, exchangeName, signalId });
1189
+ if (result) {
1190
+ await this.partialCacheService.setPartialId(result);
1191
+ }
1192
+ return result;
1193
+ };
1194
+ }
1195
+ }
1196
+
1197
+ const BreakevenSchema = new mongoose.Schema({
1198
+ symbol: { type: String, required: true, index: true },
1199
+ strategyName: { type: String, required: true, index: true },
1200
+ exchangeName: { type: String, required: true, index: true },
1201
+ signalId: { type: String, required: true, index: true },
1202
+ payload: { type: mongoose.Schema.Types.Mixed, required: true, default: {} },
1203
+ when: { type: Number, required: true, index: true },
1204
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1205
+ BreakevenSchema.index({ symbol: 1, strategyName: 1, exchangeName: 1, signalId: 1 }, { unique: true });
1206
+ const BreakevenModel = mongoose.model("breakeven-items", BreakevenSchema);
1207
+
1208
+ class BreakevenDbService extends BaseCRUD(BreakevenModel) {
1209
+ constructor() {
1210
+ super(...arguments);
1211
+ this.loggerService = inject(TYPES.loggerService);
1212
+ this.breakevenCacheService = inject(TYPES.breakevenCacheService);
1213
+ this.upsert = async (symbol, strategyName, exchangeName, signalId, payload, when) => {
1214
+ this.loggerService.log("breakevenDbService upsert", { symbol, strategyName, exchangeName, signalId, when });
1215
+ const filter = { symbol, strategyName, exchangeName, signalId };
1216
+ const document = await BreakevenModel.findOneAndUpdate(filter, { $set: { payload, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1217
+ const result = readTransform(document.toJSON());
1218
+ await this.breakevenCacheService.setBreakevenId(result);
1219
+ };
1220
+ this.findByContext = async (symbol, strategyName, exchangeName, signalId) => {
1221
+ this.loggerService.log("breakevenDbService findByContext", { symbol, strategyName, exchangeName, signalId });
1222
+ try {
1223
+ const cachedId = await this.breakevenCacheService.getBreakevenId(symbol, strategyName, exchangeName, signalId);
1224
+ if (cachedId) {
1225
+ return await super.findById(cachedId);
1226
+ }
1227
+ }
1228
+ catch {
1229
+ }
1230
+ const result = await super.findByFilter({ symbol, strategyName, exchangeName, signalId });
1231
+ if (result) {
1232
+ await this.breakevenCacheService.setBreakevenId(result);
1233
+ }
1234
+ return result;
1235
+ };
1236
+ }
1237
+ }
1238
+
1239
+ const StorageSchema = new mongoose.Schema({
1240
+ backtest: { type: Boolean, required: true, index: true },
1241
+ signalId: { type: String, required: true, index: true },
1242
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1243
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1244
+ StorageSchema.index({ backtest: 1, signalId: 1 }, { unique: true });
1245
+ const StorageModel = mongoose.model("storage-items", StorageSchema);
1246
+
1247
+ class StorageDbService extends BaseCRUD(StorageModel) {
1248
+ constructor() {
1249
+ super(...arguments);
1250
+ this.loggerService = inject(TYPES.loggerService);
1251
+ this.storageCacheService = inject(TYPES.storageCacheService);
1252
+ this.upsert = async (backtest, signalId, payload) => {
1253
+ this.loggerService.log("storageDbService upsert", { backtest, signalId });
1254
+ const filter = { backtest, signalId };
1255
+ const document = await StorageModel.findOneAndUpdate(filter, { $set: { payload } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1256
+ const result = readTransform(document.toJSON());
1257
+ await this.storageCacheService.setStorageId(result);
1258
+ };
1259
+ this.findBySignalId = async (backtest, signalId) => {
1260
+ this.loggerService.log("storageDbService findBySignalId", { backtest, signalId });
1261
+ try {
1262
+ const cachedId = await this.storageCacheService.getStorageId(backtest, signalId);
1263
+ if (cachedId) {
1264
+ return await super.findById(cachedId);
1265
+ }
1266
+ }
1267
+ catch {
1268
+ }
1269
+ const result = await super.findByFilter({ backtest, signalId });
1270
+ if (result) {
1271
+ await this.storageCacheService.setStorageId(result);
1272
+ }
1273
+ return result;
1274
+ };
1275
+ this.listByMode = async (backtest) => {
1276
+ this.loggerService.log("storageDbService listByMode", { backtest });
1277
+ return await super.findAll({ backtest });
1278
+ };
1279
+ }
1280
+ }
1281
+
1282
+ const NotificationSchema = new mongoose.Schema({
1283
+ backtest: { type: Boolean, required: true, index: true },
1284
+ notificationId: { type: String, required: true, index: true },
1285
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1286
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1287
+ NotificationSchema.index({ backtest: 1, notificationId: 1 }, { unique: true });
1288
+ const NotificationModel = mongoose.model("notification-items", NotificationSchema);
1289
+
1290
+ const LIST_LIMIT$1 = 200;
1291
+ class NotificationDbService extends BaseCRUD(NotificationModel) {
1292
+ constructor() {
1293
+ super(...arguments);
1294
+ this.loggerService = inject(TYPES.loggerService);
1295
+ this.notificationCacheService = inject(TYPES.notificationCacheService);
1296
+ this.upsert = async (backtest, notificationId, payload) => {
1297
+ this.loggerService.log("notificationDbService upsert", { backtest, notificationId });
1298
+ const filter = { backtest, notificationId };
1299
+ const document = await NotificationModel.findOneAndUpdate(filter, { $set: { payload } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1300
+ const result = readTransform(document.toJSON());
1301
+ await this.notificationCacheService.setNotificationId(result);
1302
+ };
1303
+ this.findByNotificationId = async (backtest, notificationId) => {
1304
+ this.loggerService.log("notificationDbService findByNotificationId", { backtest, notificationId });
1305
+ try {
1306
+ const cachedId = await this.notificationCacheService.getNotificationId(backtest, notificationId);
1307
+ if (cachedId) {
1308
+ return await super.findById(cachedId);
1309
+ }
1310
+ }
1311
+ catch {
1312
+ }
1313
+ const result = await super.findByFilter({ backtest, notificationId });
1314
+ if (result) {
1315
+ await this.notificationCacheService.setNotificationId(result);
1316
+ }
1317
+ return result;
1318
+ };
1319
+ this.listByMode = async (backtest) => {
1320
+ this.loggerService.log("notificationDbService listByMode", { backtest });
1321
+ const documents = await NotificationModel.find({ backtest })
1322
+ .sort({ createDate: -1 })
1323
+ .limit(LIST_LIMIT$1);
1324
+ return documents.map((doc) => readTransform(doc.toJSON()));
1325
+ };
1326
+ }
1327
+ }
1328
+
1329
+ const LogSchema = new mongoose.Schema({
1330
+ entryId: { type: String, required: true, unique: true, index: true },
1331
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1332
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1333
+ const LogModel = mongoose.model("log-items", LogSchema);
1334
+
1335
+ const LIST_LIMIT = 200;
1336
+ class LogDbService extends BaseCRUD(LogModel) {
1337
+ constructor() {
1338
+ super(...arguments);
1339
+ this.loggerService = inject(TYPES.loggerService);
1340
+ this.logCacheService = inject(TYPES.logCacheService);
1341
+ this.upsert = async (entryId, payload) => {
1342
+ this.loggerService.log("logDbService upsert", { entryId });
1343
+ const filter = { entryId };
1344
+ const document = await LogModel.findOneAndUpdate(filter, { $set: { payload } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1345
+ const result = readTransform(document.toJSON());
1346
+ await this.logCacheService.setLogId(result);
1347
+ };
1348
+ this.findByEntryId = async (entryId) => {
1349
+ this.loggerService.log("logDbService findByEntryId", { entryId });
1350
+ try {
1351
+ const cachedId = await this.logCacheService.getLogId(entryId);
1352
+ if (cachedId) {
1353
+ return await super.findById(cachedId);
1354
+ }
1355
+ }
1356
+ catch {
1357
+ }
1358
+ const result = await super.findByFilter({ entryId });
1359
+ if (result) {
1360
+ await this.logCacheService.setLogId(result);
1361
+ }
1362
+ return result;
1363
+ };
1364
+ this.listAll = async () => {
1365
+ this.loggerService.log("logDbService listAll");
1366
+ const documents = await LogModel.find({})
1367
+ .sort({ createDate: -1 })
1368
+ .limit(LIST_LIMIT);
1369
+ return documents.map((doc) => readTransform(doc.toJSON()));
1370
+ };
1371
+ }
1372
+ }
1373
+
1374
+ const MeasureSchema = new mongoose.Schema({
1375
+ bucket: { type: String, required: true, index: true },
1376
+ entryKey: { type: String, required: true, index: true },
1377
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1378
+ removed: { type: Boolean, required: true, default: false, index: true },
1379
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1380
+ MeasureSchema.index({ bucket: 1, entryKey: 1 }, { unique: true });
1381
+ const MeasureModel = mongoose.model("measure-items", MeasureSchema);
1382
+
1383
+ class MeasureDbService extends BaseCRUD(MeasureModel) {
1384
+ constructor() {
1385
+ super(...arguments);
1386
+ this.loggerService = inject(TYPES.loggerService);
1387
+ this.measureCacheService = inject(TYPES.measureCacheService);
1388
+ this.upsert = async (bucket, entryKey, payload) => {
1389
+ this.loggerService.log("measureDbService upsert", { bucket, entryKey });
1390
+ const filter = { bucket, entryKey };
1391
+ const document = await MeasureModel.findOneAndUpdate(filter, { $set: { payload, removed: payload.removed } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1392
+ const result = readTransform(document.toJSON());
1393
+ await this.measureCacheService.setMeasureId(result);
1394
+ };
1395
+ this.findByKey = async (bucket, entryKey) => {
1396
+ this.loggerService.log("measureDbService findByKey", { bucket, entryKey });
1397
+ try {
1398
+ const cachedId = await this.measureCacheService.getMeasureId(bucket, entryKey);
1399
+ if (cachedId) {
1400
+ return await super.findById(cachedId);
1401
+ }
1402
+ }
1403
+ catch {
1404
+ }
1405
+ const result = await super.findByFilter({ bucket, entryKey });
1406
+ if (result) {
1407
+ await this.measureCacheService.setMeasureId(result);
1408
+ }
1409
+ return result;
1410
+ };
1411
+ this.softRemove = async (bucket, entryKey) => {
1412
+ this.loggerService.log("measureDbService softRemove", { bucket, entryKey });
1413
+ const filter = { bucket, entryKey };
1414
+ const document = await MeasureModel.findOneAndUpdate(filter, { $set: { removed: true, "payload.removed": true } }, { new: true });
1415
+ if (!document) {
1416
+ return;
1417
+ }
1418
+ const result = readTransform(document.toJSON());
1419
+ await this.measureCacheService.setMeasureId(result);
1420
+ };
1421
+ this.listKeys = async (bucket) => {
1422
+ this.loggerService.log("measureDbService listKeys", { bucket });
1423
+ const rows = await super.findAll({ bucket, removed: false });
1424
+ return rows.map((row) => row.entryKey);
1425
+ };
1426
+ }
1427
+ }
1428
+
1429
+ const IntervalSchema = new mongoose.Schema({
1430
+ bucket: { type: String, required: true, index: true },
1431
+ entryKey: { type: String, required: true, index: true },
1432
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1433
+ removed: { type: Boolean, required: true, default: false, index: true },
1434
+ when: { type: Number, required: true, index: true },
1435
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1436
+ IntervalSchema.index({ bucket: 1, entryKey: 1 }, { unique: true });
1437
+ const IntervalModel = mongoose.model("interval-items", IntervalSchema);
1438
+
1439
+ class IntervalDbService extends BaseCRUD(IntervalModel) {
1440
+ constructor() {
1441
+ super(...arguments);
1442
+ this.loggerService = inject(TYPES.loggerService);
1443
+ this.intervalCacheService = inject(TYPES.intervalCacheService);
1444
+ this.upsert = async (bucket, entryKey, payload, when) => {
1445
+ this.loggerService.log("intervalDbService upsert", { bucket, entryKey, when });
1446
+ const filter = { bucket, entryKey };
1447
+ const document = await IntervalModel.findOneAndUpdate(filter, { $set: { payload, removed: payload.removed, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1448
+ const result = readTransform(document.toJSON());
1449
+ await this.intervalCacheService.setIntervalId(result);
1450
+ };
1451
+ this.findByKey = async (bucket, entryKey) => {
1452
+ this.loggerService.log("intervalDbService findByKey", { bucket, entryKey });
1453
+ try {
1454
+ const cachedId = await this.intervalCacheService.getIntervalId(bucket, entryKey);
1455
+ if (cachedId) {
1456
+ return await super.findById(cachedId);
1457
+ }
1458
+ }
1459
+ catch {
1460
+ }
1461
+ const result = await super.findByFilter({ bucket, entryKey });
1462
+ if (result) {
1463
+ await this.intervalCacheService.setIntervalId(result);
1464
+ }
1465
+ return result;
1466
+ };
1467
+ this.softRemove = async (bucket, entryKey) => {
1468
+ this.loggerService.log("intervalDbService softRemove", { bucket, entryKey });
1469
+ const filter = { bucket, entryKey };
1470
+ const document = await IntervalModel.findOneAndUpdate(filter, { $set: { removed: true, "payload.removed": true } }, { new: true });
1471
+ if (!document) {
1472
+ return;
1473
+ }
1474
+ const result = readTransform(document.toJSON());
1475
+ await this.intervalCacheService.setIntervalId(result);
1476
+ };
1477
+ this.listKeys = async (bucket) => {
1478
+ this.loggerService.log("intervalDbService listKeys", { bucket });
1479
+ const rows = await super.findAll({ bucket, removed: false });
1480
+ return rows.map((row) => row.entryKey);
1481
+ };
1482
+ this.clearBucket = async (bucket) => {
1483
+ this.loggerService.log("intervalDbService clearBucket", { bucket });
1484
+ const rows = await super.findAll({ bucket });
1485
+ for (const row of rows) {
1486
+ await this.intervalCacheService.deleteIntervalId(bucket, row.entryKey);
1487
+ }
1488
+ await IntervalModel.deleteMany({ bucket });
1489
+ };
1490
+ }
1491
+ }
1492
+
1493
+ const MemorySchema = new mongoose.Schema({
1494
+ signalId: { type: String, required: true, index: true },
1495
+ bucketName: { type: String, required: true, index: true },
1496
+ memoryId: { type: String, required: true, index: true },
1497
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1498
+ removed: { type: Boolean, required: true, default: false, index: true },
1499
+ when: { type: Number, required: true, index: true },
1500
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1501
+ MemorySchema.index({ signalId: 1, bucketName: 1, memoryId: 1 }, { unique: true });
1502
+ const MemoryModel = mongoose.model("memory-items", MemorySchema);
1503
+
1504
+ class MemoryDbService extends BaseCRUD(MemoryModel) {
1505
+ constructor() {
1506
+ super(...arguments);
1507
+ this.loggerService = inject(TYPES.loggerService);
1508
+ this.memoryCacheService = inject(TYPES.memoryCacheService);
1509
+ this.upsert = async (signalId, bucketName, memoryId, payload, when) => {
1510
+ this.loggerService.log("memoryDbService upsert", { signalId, bucketName, memoryId, when });
1511
+ const filter = { signalId, bucketName, memoryId };
1512
+ const document = await MemoryModel.findOneAndUpdate(filter, { $set: { payload, removed: payload.removed, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1513
+ const result = readTransform(document.toJSON());
1514
+ await this.memoryCacheService.setMemoryEntryId(result);
1515
+ };
1516
+ this.findByMemoryId = async (signalId, bucketName, memoryId) => {
1517
+ this.loggerService.log("memoryDbService findByMemoryId", { signalId, bucketName, memoryId });
1518
+ try {
1519
+ const cachedId = await this.memoryCacheService.getMemoryEntryId(signalId, bucketName, memoryId);
1520
+ if (cachedId) {
1521
+ return await super.findById(cachedId);
1522
+ }
1523
+ }
1524
+ catch {
1525
+ }
1526
+ const result = await super.findByFilter({ signalId, bucketName, memoryId });
1527
+ if (result) {
1528
+ await this.memoryCacheService.setMemoryEntryId(result);
1529
+ }
1530
+ return result;
1531
+ };
1532
+ this.hasMemoryEntry = async (signalId, bucketName, memoryId) => {
1533
+ this.loggerService.log("memoryDbService hasMemoryEntry", { signalId, bucketName, memoryId });
1534
+ if (await this.memoryCacheService.hasMemoryEntryId(signalId, bucketName, memoryId)) {
1535
+ return true;
1536
+ }
1537
+ const row = await super.findByFilter({ signalId, bucketName, memoryId });
1538
+ if (row) {
1539
+ await this.memoryCacheService.setMemoryEntryId(row);
1540
+ return true;
1541
+ }
1542
+ return false;
1543
+ };
1544
+ this.softRemove = async (signalId, bucketName, memoryId) => {
1545
+ this.loggerService.log("memoryDbService softRemove", { signalId, bucketName, memoryId });
1546
+ const filter = { signalId, bucketName, memoryId };
1547
+ const document = await MemoryModel.findOneAndUpdate(filter, { $set: { removed: true, "payload.removed": true } }, { new: true });
1548
+ if (!document) {
1549
+ return;
1550
+ }
1551
+ const result = readTransform(document.toJSON());
1552
+ await this.memoryCacheService.setMemoryEntryId(result);
1553
+ };
1554
+ this.listEntries = async (signalId, bucketName) => {
1555
+ this.loggerService.log("memoryDbService listEntries", { signalId, bucketName });
1556
+ return await super.findAll({ signalId, bucketName, removed: false });
1557
+ };
1558
+ }
1559
+ }
1560
+
1561
+ const RecentSchema = new mongoose.Schema({
1562
+ symbol: { type: String, required: true, index: true },
1563
+ strategyName: { type: String, required: true, index: true },
1564
+ exchangeName: { type: String, required: true, index: true },
1565
+ frameName: { type: String, required: true, index: true },
1566
+ backtest: { type: Boolean, required: true, index: true },
1567
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1568
+ when: { type: Number, required: true, index: true },
1569
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1570
+ RecentSchema.index({ symbol: 1, strategyName: 1, exchangeName: 1, frameName: 1, backtest: 1 }, { unique: true });
1571
+ const RecentModel = mongoose.model("recent-items", RecentSchema);
1572
+
1573
+ class RecentDbService extends BaseCRUD(RecentModel) {
1574
+ constructor() {
1575
+ super(...arguments);
1576
+ this.loggerService = inject(TYPES.loggerService);
1577
+ this.recentCacheService = inject(TYPES.recentCacheService);
1578
+ this.upsert = async (symbol, strategyName, exchangeName, frameName, backtest, payload, when) => {
1579
+ this.loggerService.log("recentDbService upsert", { symbol, strategyName, exchangeName, frameName, backtest, when });
1580
+ const filter = { symbol, strategyName, exchangeName, frameName, backtest };
1581
+ const document = await RecentModel.findOneAndUpdate(filter, { $set: { payload, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1582
+ const result = readTransform(document.toJSON());
1583
+ await this.recentCacheService.setRecentId(result);
1584
+ };
1585
+ this.findByContext = async (symbol, strategyName, exchangeName, frameName, backtest) => {
1586
+ this.loggerService.log("recentDbService findByContext", { symbol, strategyName, exchangeName, frameName, backtest });
1587
+ try {
1588
+ const cachedId = await this.recentCacheService.getRecentId(symbol, strategyName, exchangeName, frameName, backtest);
1589
+ if (cachedId) {
1590
+ return await super.findById(cachedId);
1591
+ }
1592
+ }
1593
+ catch {
1594
+ }
1595
+ const result = await super.findByFilter({ symbol, strategyName, exchangeName, frameName, backtest });
1596
+ if (result) {
1597
+ await this.recentCacheService.setRecentId(result);
1598
+ }
1599
+ return result;
1600
+ };
1601
+ }
1602
+ }
1603
+
1604
+ const StateSchema = new mongoose.Schema({
1605
+ signalId: { type: String, required: true, index: true },
1606
+ bucketName: { type: String, required: true, index: true },
1607
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1608
+ when: { type: Number, required: true, index: true },
1609
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1610
+ StateSchema.index({ signalId: 1, bucketName: 1 }, { unique: true });
1611
+ const StateModel = mongoose.model("state-items", StateSchema);
1612
+
1613
+ class StateDbService extends BaseCRUD(StateModel) {
1614
+ constructor() {
1615
+ super(...arguments);
1616
+ this.loggerService = inject(TYPES.loggerService);
1617
+ this.stateCacheService = inject(TYPES.stateCacheService);
1618
+ this.upsert = async (signalId, bucketName, payload, when) => {
1619
+ this.loggerService.log("stateDbService upsert", { signalId, bucketName, when });
1620
+ const filter = { signalId, bucketName };
1621
+ const document = await StateModel.findOneAndUpdate(filter, { $set: { payload, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1622
+ const result = readTransform(document.toJSON());
1623
+ await this.stateCacheService.setStateId(result);
1624
+ };
1625
+ this.findByContext = async (signalId, bucketName) => {
1626
+ this.loggerService.log("stateDbService findByContext", { signalId, bucketName });
1627
+ try {
1628
+ const cachedId = await this.stateCacheService.getStateId(signalId, bucketName);
1629
+ if (cachedId) {
1630
+ return await super.findById(cachedId);
1631
+ }
1632
+ }
1633
+ catch {
1634
+ }
1635
+ const result = await super.findByFilter({ signalId, bucketName });
1636
+ if (result) {
1637
+ await this.stateCacheService.setStateId(result);
1638
+ }
1639
+ return result;
1640
+ };
1641
+ }
1642
+ }
1643
+
1644
+ const SessionSchema = new mongoose.Schema({
1645
+ strategyName: { type: String, required: true, index: true },
1646
+ exchangeName: { type: String, required: true, index: true },
1647
+ frameName: { type: String, required: true, index: true },
1648
+ payload: { type: mongoose.Schema.Types.Mixed, required: true },
1649
+ when: { type: Number, required: true, index: true },
1650
+ }, { timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false });
1651
+ SessionSchema.index({ strategyName: 1, exchangeName: 1, frameName: 1 }, { unique: true });
1652
+ const SessionModel = mongoose.model("session-items", SessionSchema);
1653
+
1654
+ class SessionDbService extends BaseCRUD(SessionModel) {
1655
+ constructor() {
1656
+ super(...arguments);
1657
+ this.loggerService = inject(TYPES.loggerService);
1658
+ this.sessionCacheService = inject(TYPES.sessionCacheService);
1659
+ this.upsert = async (strategyName, exchangeName, frameName, payload, when) => {
1660
+ this.loggerService.log("sessionDbService upsert", { strategyName, exchangeName, frameName, when });
1661
+ const filter = { strategyName, exchangeName, frameName };
1662
+ const document = await SessionModel.findOneAndUpdate(filter, { $set: { payload, when: when.getTime() } }, { upsert: true, new: true, setDefaultsOnInsert: true });
1663
+ const result = readTransform(document.toJSON());
1664
+ await this.sessionCacheService.setSessionId(result);
1665
+ };
1666
+ this.findByContext = async (strategyName, exchangeName, frameName) => {
1667
+ this.loggerService.log("sessionDbService findByContext", { strategyName, exchangeName, frameName });
1668
+ try {
1669
+ const cachedId = await this.sessionCacheService.getSessionId(strategyName, exchangeName, frameName);
1670
+ if (cachedId) {
1671
+ return await super.findById(cachedId);
1672
+ }
1673
+ }
1674
+ catch {
1675
+ }
1676
+ const result = await super.findByFilter({ strategyName, exchangeName, frameName });
1677
+ if (result) {
1678
+ await this.sessionCacheService.setSessionId(result);
1679
+ }
1680
+ return result;
1681
+ };
1682
+ }
1683
+ }
1684
+
1685
+ {
1686
+ provide(TYPES.loggerService, () => new LoggerService());
1687
+ provide(TYPES.mongoService, () => new MongooseService());
1688
+ provide(TYPES.redisService, () => new RedisService());
1689
+ }
1690
+ {
1691
+ provide(TYPES.candleCacheService, () => new CandleCacheService());
1692
+ provide(TYPES.signalCacheService, () => new SignalCacheService());
1693
+ provide(TYPES.scheduleCacheService, () => new ScheduleCacheService());
1694
+ provide(TYPES.riskCacheService, () => new RiskCacheService());
1695
+ provide(TYPES.partialCacheService, () => new PartialCacheService());
1696
+ provide(TYPES.breakevenCacheService, () => new BreakevenCacheService());
1697
+ provide(TYPES.storageCacheService, () => new StorageCacheService());
1698
+ provide(TYPES.notificationCacheService, () => new NotificationCacheService());
1699
+ provide(TYPES.logCacheService, () => new LogCacheService());
1700
+ provide(TYPES.measureCacheService, () => new MeasureCacheService());
1701
+ provide(TYPES.intervalCacheService, () => new IntervalCacheService());
1702
+ provide(TYPES.memoryCacheService, () => new MemoryCacheService());
1703
+ provide(TYPES.recentCacheService, () => new RecentCacheService());
1704
+ provide(TYPES.stateCacheService, () => new StateCacheService());
1705
+ provide(TYPES.sessionCacheService, () => new SessionCacheService());
1706
+ }
1707
+ {
1708
+ provide(TYPES.candleDbService, () => new CandleDbService());
1709
+ provide(TYPES.signalDbService, () => new SignalDbService());
1710
+ provide(TYPES.scheduleDbService, () => new ScheduleDbService());
1711
+ provide(TYPES.riskDbService, () => new RiskDbService());
1712
+ provide(TYPES.partialDbService, () => new PartialDbService());
1713
+ provide(TYPES.breakevenDbService, () => new BreakevenDbService());
1714
+ provide(TYPES.storageDbService, () => new StorageDbService());
1715
+ provide(TYPES.notificationDbService, () => new NotificationDbService());
1716
+ provide(TYPES.logDbService, () => new LogDbService());
1717
+ provide(TYPES.measureDbService, () => new MeasureDbService());
1718
+ provide(TYPES.intervalDbService, () => new IntervalDbService());
1719
+ provide(TYPES.memoryDbService, () => new MemoryDbService());
1720
+ provide(TYPES.recentDbService, () => new RecentDbService());
1721
+ provide(TYPES.stateDbService, () => new StateDbService());
1722
+ provide(TYPES.sessionDbService, () => new SessionDbService());
1723
+ }
1724
+
1725
+ const baseServices = {
1726
+ loggerService: inject(TYPES.loggerService),
1727
+ mongoService: inject(TYPES.mongoService),
1728
+ redisService: inject(TYPES.redisService),
1729
+ };
1730
+ const cacheServices = {
1731
+ candleCacheService: inject(TYPES.candleCacheService),
1732
+ signalCacheService: inject(TYPES.signalCacheService),
1733
+ scheduleCacheService: inject(TYPES.scheduleCacheService),
1734
+ riskCacheService: inject(TYPES.riskCacheService),
1735
+ partialCacheService: inject(TYPES.partialCacheService),
1736
+ breakevenCacheService: inject(TYPES.breakevenCacheService),
1737
+ storageCacheService: inject(TYPES.storageCacheService),
1738
+ notificationCacheService: inject(TYPES.notificationCacheService),
1739
+ logCacheService: inject(TYPES.logCacheService),
1740
+ measureCacheService: inject(TYPES.measureCacheService),
1741
+ intervalCacheService: inject(TYPES.intervalCacheService),
1742
+ memoryCacheService: inject(TYPES.memoryCacheService),
1743
+ recentCacheService: inject(TYPES.recentCacheService),
1744
+ stateCacheService: inject(TYPES.stateCacheService),
1745
+ sessionCacheService: inject(TYPES.sessionCacheService),
1746
+ };
1747
+ const dbServices = {
1748
+ candleDbService: inject(TYPES.candleDbService),
1749
+ signalDbService: inject(TYPES.signalDbService),
1750
+ scheduleDbService: inject(TYPES.scheduleDbService),
1751
+ riskDbService: inject(TYPES.riskDbService),
1752
+ partialDbService: inject(TYPES.partialDbService),
1753
+ breakevenDbService: inject(TYPES.breakevenDbService),
1754
+ storageDbService: inject(TYPES.storageDbService),
1755
+ notificationDbService: inject(TYPES.notificationDbService),
1756
+ logDbService: inject(TYPES.logDbService),
1757
+ measureDbService: inject(TYPES.measureDbService),
1758
+ intervalDbService: inject(TYPES.intervalDbService),
1759
+ memoryDbService: inject(TYPES.memoryDbService),
1760
+ recentDbService: inject(TYPES.recentDbService),
1761
+ stateDbService: inject(TYPES.stateDbService),
1762
+ sessionDbService: inject(TYPES.sessionDbService),
1763
+ };
1764
+ const ioc = {
1765
+ ...baseServices,
1766
+ ...cacheServices,
1767
+ ...dbServices,
1768
+ };
1769
+ init();
1770
+
1771
+ const waitForInfra = functoolsKit.singleshot(async () => {
1772
+ await Promise.all([
1773
+ ioc.mongoService.waitForInit(),
1774
+ ioc.redisService.waitForInit(),
1775
+ ]);
1776
+ });
1777
+
1778
+ class PersistCandleInstance {
1779
+ constructor(symbol, interval, exchangeName) {
1780
+ this.symbol = symbol;
1781
+ this.interval = interval;
1782
+ this.exchangeName = exchangeName;
1783
+ }
1784
+ async waitForInit(initial) {
1785
+ if (!initial) {
1786
+ return;
1787
+ }
1788
+ await waitForInfra();
1789
+ }
1790
+ async writeCandlesData(candles) {
1791
+ for (const candle of candles) {
1792
+ await ioc.candleDbService.create({
1793
+ symbol: this.symbol,
1794
+ interval: this.interval,
1795
+ close: candle.close,
1796
+ high: candle.high,
1797
+ low: candle.low,
1798
+ open: candle.open,
1799
+ timestamp: candle.timestamp,
1800
+ volume: candle.volume,
1801
+ });
1802
+ }
1803
+ }
1804
+ async readCandlesData(limit, sinceTimestamp) {
1805
+ const stepMs = backtestKit.intervalStepMs(this.interval);
1806
+ const result = [];
1807
+ for (let i = 0; i < limit; i++) {
1808
+ const ts = sinceTimestamp + i * stepMs;
1809
+ const row = await ioc.candleDbService.findBySymbolIntervalTimestamp(this.symbol, this.interval, ts);
1810
+ if (!row) {
1811
+ return null;
1812
+ }
1813
+ result.push({ timestamp: row.timestamp, open: row.open, high: row.high, low: row.low, close: row.close, volume: row.volume });
1814
+ }
1815
+ return result;
1816
+ }
1817
+ }
1818
+
1819
+ class PersistSignalInstance {
1820
+ constructor(symbol, strategyName, exchangeName) {
1821
+ this.symbol = symbol;
1822
+ this.strategyName = strategyName;
1823
+ this.exchangeName = exchangeName;
1824
+ }
1825
+ async waitForInit(initial) {
1826
+ if (!initial) {
1827
+ return;
1828
+ }
1829
+ await waitForInfra();
1830
+ }
1831
+ async readSignalData() {
1832
+ const row = await ioc.signalDbService.findByContext(this.symbol, this.strategyName, this.exchangeName);
1833
+ return row ? row.payload : null;
1834
+ }
1835
+ async writeSignalData(signalRow) {
1836
+ await ioc.signalDbService.upsert(this.symbol, this.strategyName, this.exchangeName, signalRow);
1837
+ }
1838
+ }
1839
+
1840
+ class PersistRiskInstance {
1841
+ constructor(riskName, exchangeName) {
1842
+ this.riskName = riskName;
1843
+ this.exchangeName = exchangeName;
1844
+ }
1845
+ async waitForInit(initial) {
1846
+ if (!initial) {
1847
+ return;
1848
+ }
1849
+ await waitForInfra();
1850
+ }
1851
+ async readPositionData(_when) {
1852
+ const row = await ioc.riskDbService.findByContext(this.riskName, this.exchangeName);
1853
+ return row ? row.positions : [];
1854
+ }
1855
+ async writePositionData(positions, when) {
1856
+ await ioc.riskDbService.upsert(this.riskName, this.exchangeName, positions, when);
1857
+ }
1858
+ }
1859
+
1860
+ class PersistScheduleInstance {
1861
+ constructor(symbol, strategyName, exchangeName) {
1862
+ this.symbol = symbol;
1863
+ this.strategyName = strategyName;
1864
+ this.exchangeName = exchangeName;
1865
+ }
1866
+ async waitForInit(initial) {
1867
+ if (!initial) {
1868
+ return;
1869
+ }
1870
+ await waitForInfra();
1871
+ }
1872
+ async readScheduleData() {
1873
+ const row = await ioc.scheduleDbService.findByContext(this.symbol, this.strategyName, this.exchangeName);
1874
+ return row ? row.payload : null;
1875
+ }
1876
+ async writeScheduleData(scheduleRow) {
1877
+ await ioc.scheduleDbService.upsert(this.symbol, this.strategyName, this.exchangeName, scheduleRow);
1878
+ }
1879
+ }
1880
+
1881
+ class PersistPartialInstance {
1882
+ constructor(symbol, strategyName, exchangeName) {
1883
+ this.symbol = symbol;
1884
+ this.strategyName = strategyName;
1885
+ this.exchangeName = exchangeName;
1886
+ }
1887
+ async waitForInit(initial) {
1888
+ if (!initial) {
1889
+ return;
1890
+ }
1891
+ await waitForInfra();
1892
+ }
1893
+ async readPartialData(signalId, _when) {
1894
+ const row = await ioc.partialDbService.findByContext(this.symbol, this.strategyName, this.exchangeName, signalId);
1895
+ return row ? row.payload : {};
1896
+ }
1897
+ async writePartialData(data, signalId, when) {
1898
+ await ioc.partialDbService.upsert(this.symbol, this.strategyName, this.exchangeName, signalId, data, when);
1899
+ }
1900
+ }
1901
+
1902
+ class PersistBreakevenInstance {
1903
+ constructor(symbol, strategyName, exchangeName) {
1904
+ this.symbol = symbol;
1905
+ this.strategyName = strategyName;
1906
+ this.exchangeName = exchangeName;
1907
+ }
1908
+ async waitForInit(initial) {
1909
+ if (!initial) {
1910
+ return;
1911
+ }
1912
+ await waitForInfra();
1913
+ }
1914
+ async readBreakevenData(signalId, _when) {
1915
+ const row = await ioc.breakevenDbService.findByContext(this.symbol, this.strategyName, this.exchangeName, signalId);
1916
+ return row ? row.payload : {};
1917
+ }
1918
+ async writeBreakevenData(data, signalId, when) {
1919
+ await ioc.breakevenDbService.upsert(this.symbol, this.strategyName, this.exchangeName, signalId, data, when);
1920
+ }
1921
+ }
1922
+
1923
+ class PersistStorageInstance {
1924
+ constructor(backtest) {
1925
+ this.backtest = backtest;
1926
+ }
1927
+ async waitForInit(initial) {
1928
+ if (!initial) {
1929
+ return;
1930
+ }
1931
+ await waitForInfra();
1932
+ }
1933
+ async readStorageData() {
1934
+ const rows = await ioc.storageDbService.listByMode(this.backtest);
1935
+ return rows.map((row) => row.payload);
1936
+ }
1937
+ async writeStorageData(signals) {
1938
+ for (const signal of signals) {
1939
+ await ioc.storageDbService.upsert(this.backtest, signal.id, signal);
1940
+ }
1941
+ }
1942
+ }
1943
+
1944
+ class PersistNotificationInstance {
1945
+ constructor(backtest) {
1946
+ this.backtest = backtest;
1947
+ }
1948
+ async waitForInit(initial) {
1949
+ if (!initial) {
1950
+ return;
1951
+ }
1952
+ await waitForInfra();
1953
+ }
1954
+ async readNotificationData() {
1955
+ const rows = await ioc.notificationDbService.listByMode(this.backtest);
1956
+ return rows.map((row) => row.payload).reverse();
1957
+ }
1958
+ async writeNotificationData(notifications) {
1959
+ for (const notification of notifications) {
1960
+ await ioc.notificationDbService.upsert(this.backtest, notification.id, notification);
1961
+ }
1962
+ }
1963
+ }
1964
+
1965
+ class PersistLogInstance {
1966
+ async waitForInit(initial) {
1967
+ if (!initial) {
1968
+ return;
1969
+ }
1970
+ await waitForInfra();
1971
+ }
1972
+ async readLogData() {
1973
+ const rows = await ioc.logDbService.listAll();
1974
+ return rows.map((row) => row.payload).reverse();
1975
+ }
1976
+ async writeLogData(entries) {
1977
+ for (const entry of entries) {
1978
+ await ioc.logDbService.upsert(entry.id, entry);
1979
+ }
1980
+ }
1981
+ }
1982
+
1983
+ class PersistMeasureInstance {
1984
+ constructor(bucket) {
1985
+ this.bucket = bucket;
1986
+ }
1987
+ async waitForInit(initial) {
1988
+ if (!initial) {
1989
+ return;
1990
+ }
1991
+ await waitForInfra();
1992
+ }
1993
+ async readMeasureData(key) {
1994
+ const row = await ioc.measureDbService.findByKey(this.bucket, key);
1995
+ if (!row || row.removed) {
1996
+ return null;
1997
+ }
1998
+ return row.payload;
1999
+ }
2000
+ async writeMeasureData(data, key, _when) {
2001
+ await ioc.measureDbService.upsert(this.bucket, key, data);
2002
+ }
2003
+ async removeMeasureData(key) {
2004
+ await ioc.measureDbService.softRemove(this.bucket, key);
2005
+ }
2006
+ async *listMeasureData() {
2007
+ const keys = await ioc.measureDbService.listKeys(this.bucket);
2008
+ for (const key of keys) {
2009
+ yield key;
2010
+ }
2011
+ }
2012
+ }
2013
+
2014
+ class PersistIntervalInstance {
2015
+ constructor(bucket) {
2016
+ this.bucket = bucket;
2017
+ }
2018
+ async waitForInit(initial) {
2019
+ if (!initial) {
2020
+ return;
2021
+ }
2022
+ await waitForInfra();
2023
+ }
2024
+ async readIntervalData(key) {
2025
+ const row = await ioc.intervalDbService.findByKey(this.bucket, key);
2026
+ if (!row || row.removed) {
2027
+ return null;
2028
+ }
2029
+ return row.payload;
2030
+ }
2031
+ async writeIntervalData(data, key, when) {
2032
+ await ioc.intervalDbService.upsert(this.bucket, key, data, when);
2033
+ }
2034
+ async removeIntervalData(key) {
2035
+ await ioc.intervalDbService.softRemove(this.bucket, key);
2036
+ }
2037
+ async *listIntervalData() {
2038
+ const keys = await ioc.intervalDbService.listKeys(this.bucket);
2039
+ for (const key of keys) {
2040
+ yield key;
2041
+ }
2042
+ }
2043
+ }
2044
+
2045
+ class PersistMemoryInstance {
2046
+ constructor(signalId, bucketName) {
2047
+ this.signalId = signalId;
2048
+ this.bucketName = bucketName;
2049
+ }
2050
+ async waitForInit(initial) {
2051
+ if (!initial) {
2052
+ return;
2053
+ }
2054
+ await waitForInfra();
2055
+ }
2056
+ async readMemoryData(memoryId) {
2057
+ const row = await ioc.memoryDbService.findByMemoryId(this.signalId, this.bucketName, memoryId);
2058
+ if (!row || row.removed) {
2059
+ return null;
2060
+ }
2061
+ return row.payload;
2062
+ }
2063
+ async hasMemoryData(memoryId) {
2064
+ return await ioc.memoryDbService.hasMemoryEntry(this.signalId, this.bucketName, memoryId);
2065
+ }
2066
+ async writeMemoryData(data, memoryId, when) {
2067
+ await ioc.memoryDbService.upsert(this.signalId, this.bucketName, memoryId, data, when);
2068
+ }
2069
+ async removeMemoryData(memoryId) {
2070
+ await ioc.memoryDbService.softRemove(this.signalId, this.bucketName, memoryId);
2071
+ }
2072
+ async *listMemoryData() {
2073
+ const rows = await ioc.memoryDbService.listEntries(this.signalId, this.bucketName);
2074
+ for (const row of rows) {
2075
+ yield { memoryId: row.memoryId, data: row.payload };
2076
+ }
2077
+ }
2078
+ dispose() { }
2079
+ }
2080
+
2081
+ class PersistRecentInstance {
2082
+ constructor(symbol, strategyName, exchangeName, frameName, backtest) {
2083
+ this.symbol = symbol;
2084
+ this.strategyName = strategyName;
2085
+ this.exchangeName = exchangeName;
2086
+ this.frameName = frameName;
2087
+ this.backtest = backtest;
2088
+ }
2089
+ async waitForInit(initial) {
2090
+ if (!initial) {
2091
+ return;
2092
+ }
2093
+ await waitForInfra();
2094
+ }
2095
+ async readRecentData() {
2096
+ const row = await ioc.recentDbService.findByContext(this.symbol, this.strategyName, this.exchangeName, this.frameName, this.backtest);
2097
+ return row ? row.payload : null;
2098
+ }
2099
+ async writeRecentData(signalRow, when) {
2100
+ await ioc.recentDbService.upsert(this.symbol, this.strategyName, this.exchangeName, this.frameName, this.backtest, signalRow, when);
2101
+ }
2102
+ }
2103
+
2104
+ class PersistStateInstance {
2105
+ constructor(signalId, bucketName) {
2106
+ this.signalId = signalId;
2107
+ this.bucketName = bucketName;
2108
+ }
2109
+ async waitForInit(initial) {
2110
+ if (!initial) {
2111
+ return;
2112
+ }
2113
+ await waitForInfra();
2114
+ }
2115
+ async readStateData() {
2116
+ const row = await ioc.stateDbService.findByContext(this.signalId, this.bucketName);
2117
+ return row ? row.payload : null;
2118
+ }
2119
+ async writeStateData(data, when) {
2120
+ await ioc.stateDbService.upsert(this.signalId, this.bucketName, data, when);
2121
+ }
2122
+ dispose() { }
2123
+ }
2124
+
2125
+ class PersistSessionInstance {
2126
+ constructor(strategyName, exchangeName, frameName) {
2127
+ this.strategyName = strategyName;
2128
+ this.exchangeName = exchangeName;
2129
+ this.frameName = frameName;
2130
+ }
2131
+ async waitForInit(initial) {
2132
+ if (!initial) {
2133
+ return;
2134
+ }
2135
+ await waitForInfra();
2136
+ }
2137
+ async readSessionData() {
2138
+ const row = await ioc.sessionDbService.findByContext(this.strategyName, this.exchangeName, this.frameName);
2139
+ return row ? row.payload : null;
2140
+ }
2141
+ async writeSessionData(data, when) {
2142
+ await ioc.sessionDbService.upsert(this.strategyName, this.exchangeName, this.frameName, data, when);
2143
+ }
2144
+ dispose() { }
2145
+ }
2146
+
2147
+ /**
2148
+ * Initializes the `@backtest-kit/mongo` package: applies user-provided configuration
2149
+ * and registers all MongoDB/Redis persistence adapters into the global `backtest-kit` registries.
2150
+ *
2151
+ * Should be called **once** at application startup — before any trading data operations.
2152
+ * Internally calls {@link setConfig} followed by {@link install}.
2153
+ *
2154
+ * @param config - Connection parameters. If omitted, {@link DEFAULT_CONFIG} is used,
2155
+ * which reads values from environment variables:
2156
+ * - `CC_MONGO_CONNECTION_STRING` — MongoDB connection string (default: `mongodb://localhost:27017/backtest-kit`)
2157
+ * - `CC_REDIS_HOST` — Redis host (default: `127.0.0.1`)
2158
+ * - `CC_REDIS_PORT` — Redis port (default: `6379`)
2159
+ * - `CC_REDIS_USER` — Redis username (default: empty string)
2160
+ * - `CC_REDIS_PASSWORD` — Redis password (default: empty string)
2161
+ *
2162
+ * @example
2163
+ * // Minimal — everything is read from env variables
2164
+ * setup();
2165
+ *
2166
+ * @example
2167
+ * // Explicit configuration
2168
+ * setup({
2169
+ * CC_MONGO_CONNECTION_STRING: "mongodb://mongo:27017/mydb",
2170
+ * CC_REDIS_HOST: "redis",
2171
+ * CC_REDIS_PORT: 6379,
2172
+ * CC_REDIS_USER: "",
2173
+ * CC_REDIS_PASSWORD: "secret",
2174
+ * });
2175
+ */
2176
+ function setup(config) {
2177
+ config && setConfig(config);
2178
+ install();
2179
+ }
2180
+ /**
2181
+ * Registers MongoDB implementations of all `backtest-kit` persistence adapters
2182
+ * without modifying the global connection configuration.
2183
+ *
2184
+ * Use directly when the configuration has already been applied via {@link setConfig}
2185
+ * or is provided through environment variables and does not need to be overridden.
2186
+ * In the typical scenario, calling {@link setup} is sufficient — it calls `install` internally.
2187
+ *
2188
+ * Registered adapters:
2189
+ * - **Candle** — OHLCV candle data (`PersistCandleAdapter`)
2190
+ * - **Signal** — strategy signals (`PersistSignalAdapter`)
2191
+ * - **Risk** — risk manager positions (`PersistRiskAdapter`)
2192
+ * - **Schedule** — deferred signals (`PersistScheduleAdapter`)
2193
+ * - **Partial** — partial close data (`PersistPartialAdapter`)
2194
+ * - **Breakeven** — breakeven data (`PersistBreakevenAdapter`)
2195
+ * - **Storage** — general signal storage (`PersistStorageAdapter`)
2196
+ * - **Notification** — notifications (`PersistNotificationAdapter`)
2197
+ * - **Log** — log entries (`PersistLogAdapter`)
2198
+ * - **Measure** — arbitrary metrics keyed by bucket/key (`PersistMeasureAdapter`)
2199
+ * - **Interval** — interval task data (`PersistIntervalAdapter`)
2200
+ * - **Memory** — long-term signal memory (`PersistMemoryAdapter`)
2201
+ * - **Recent** — latest strategy frame result (`PersistRecentAdapter`)
2202
+ * - **State** — signal state (`PersistStateAdapter`)
2203
+ * - **Session** — strategy session data (`PersistSessionAdapter`)
2204
+ *
2205
+ * @example
2206
+ * // Configuration is provided via env variables, adapters are installed manually
2207
+ * install();
2208
+ */
2209
+ function install() {
2210
+ backtestKit.PersistCandleAdapter.usePersistCandleAdapter(PersistCandleInstance);
2211
+ backtestKit.PersistSignalAdapter.usePersistSignalAdapter(PersistSignalInstance);
2212
+ backtestKit.PersistRiskAdapter.usePersistRiskAdapter(PersistRiskInstance);
2213
+ backtestKit.PersistScheduleAdapter.usePersistScheduleAdapter(PersistScheduleInstance);
2214
+ backtestKit.PersistPartialAdapter.usePersistPartialAdapter(PersistPartialInstance);
2215
+ backtestKit.PersistBreakevenAdapter.usePersistBreakevenAdapter(PersistBreakevenInstance);
2216
+ backtestKit.PersistStorageAdapter.usePersistStorageAdapter(PersistStorageInstance);
2217
+ backtestKit.PersistNotificationAdapter.usePersistNotificationAdapter(PersistNotificationInstance);
2218
+ backtestKit.PersistLogAdapter.usePersistLogAdapter(PersistLogInstance);
2219
+ backtestKit.PersistMeasureAdapter.usePersistMeasureAdapter(PersistMeasureInstance);
2220
+ backtestKit.PersistIntervalAdapter.usePersistIntervalAdapter(PersistIntervalInstance);
2221
+ backtestKit.PersistMemoryAdapter.usePersistMemoryAdapter(PersistMemoryInstance);
2222
+ backtestKit.PersistRecentAdapter.usePersistRecentAdapter(PersistRecentInstance);
2223
+ backtestKit.PersistStateAdapter.usePersistStateAdapter(PersistStateInstance);
2224
+ backtestKit.PersistSessionAdapter.usePersistSessionAdapter(PersistSessionInstance);
2225
+ }
2226
+ /**
2227
+ * Attaches a custom logger to the internal `LoggerService`.
2228
+ *
2229
+ * By default the package logs to `console`. Pass your own {@link ILogger} implementation
2230
+ * to redirect output to an external logging system (Winston, Pino, Datadog, etc.).
2231
+ *
2232
+ * @param logger - Object with `log`, `debug`, `info`, and `warn` methods.
2233
+ * Each method receives a string topic followed by arbitrary arguments.
2234
+ *
2235
+ * @example
2236
+ * import winston from "winston";
2237
+ *
2238
+ * setLogger({
2239
+ * log: (topic, ...args) => winston.verbose(topic, ...args),
2240
+ * debug: (topic, ...args) => winston.debug(topic, ...args),
2241
+ * info: (topic, ...args) => winston.info(topic, ...args),
2242
+ * warn: (topic, ...args) => winston.warn(topic, ...args),
2243
+ * });
2244
+ */
2245
+ function setLogger(logger) {
2246
+ ioc.loggerService.setLogger(logger);
2247
+ }
2248
+
2249
+ exports.getConfig = getConfig;
2250
+ exports.getMongo = getMongo;
2251
+ exports.getRedis = getRedis;
2252
+ exports.install = install;
2253
+ exports.setConfig = setConfig;
2254
+ exports.setLogger = setLogger;
2255
+ exports.setup = setup;