backtest-kit 2.2.10 → 2.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +581 -5
- package/build/index.mjs +580 -6
- package/package.json +1 -1
- package/types.d.ts +293 -2
package/build/index.cjs
CHANGED
|
@@ -731,6 +731,11 @@ const PERSIST_BASE_METHOD_NAME_READ_VALUE = "PersistBase.readValue";
|
|
|
731
731
|
const PERSIST_BASE_METHOD_NAME_WRITE_VALUE = "PersistBase.writeValue";
|
|
732
732
|
const PERSIST_BASE_METHOD_NAME_HAS_VALUE = "PersistBase.hasValue";
|
|
733
733
|
const PERSIST_BASE_METHOD_NAME_KEYS = "PersistBase.keys";
|
|
734
|
+
const PERSIST_STORAGE_UTILS_METHOD_NAME_READ_DATA = "PersistStorageUtils.readStorageData";
|
|
735
|
+
const PERSIST_STORAGE_UTILS_METHOD_NAME_WRITE_DATA = "PersistStorageUtils.writeStorageData";
|
|
736
|
+
const PERSIST_STORAGE_UTILS_METHOD_NAME_USE_JSON = "PersistStorageUtils.useJson";
|
|
737
|
+
const PERSIST_STORAGE_UTILS_METHOD_NAME_USE_DUMMY = "PersistStorageUtils.useDummy";
|
|
738
|
+
const PERSIST_STORAGE_UTILS_METHOD_NAME_USE_PERSIST_STORAGE_ADAPTER = "PersistStorageUtils.usePersistStorageAdapter";
|
|
734
739
|
const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
|
|
735
740
|
const BASE_UNLINK_RETRY_COUNT = 5;
|
|
736
741
|
const BASE_UNLINK_RETRY_DELAY = 1000;
|
|
@@ -949,7 +954,7 @@ class PersistDummy {
|
|
|
949
954
|
class PersistSignalUtils {
|
|
950
955
|
constructor() {
|
|
951
956
|
this.PersistSignalFactory = PersistBase;
|
|
952
|
-
this.
|
|
957
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName]) => `${symbol}:${strategyName}:${exchangeName}`, (symbol, strategyName, exchangeName) => Reflect.construct(this.PersistSignalFactory, [
|
|
953
958
|
`${symbol}_${strategyName}_${exchangeName}`,
|
|
954
959
|
`./dump/data/signal/`,
|
|
955
960
|
]));
|
|
@@ -967,8 +972,8 @@ class PersistSignalUtils {
|
|
|
967
972
|
this.readSignalData = async (symbol, strategyName, exchangeName) => {
|
|
968
973
|
bt.loggerService.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_READ_DATA);
|
|
969
974
|
const key = `${symbol}:${strategyName}:${exchangeName}`;
|
|
970
|
-
const isInitial = !this.
|
|
971
|
-
const stateStorage = this.
|
|
975
|
+
const isInitial = !this.getStorage.has(key);
|
|
976
|
+
const stateStorage = this.getStorage(symbol, strategyName, exchangeName);
|
|
972
977
|
await stateStorage.waitForInit(isInitial);
|
|
973
978
|
if (await stateStorage.hasValue(symbol)) {
|
|
974
979
|
return await stateStorage.readValue(symbol);
|
|
@@ -990,8 +995,8 @@ class PersistSignalUtils {
|
|
|
990
995
|
this.writeSignalData = async (signalRow, symbol, strategyName, exchangeName) => {
|
|
991
996
|
bt.loggerService.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_WRITE_DATA);
|
|
992
997
|
const key = `${symbol}:${strategyName}:${exchangeName}`;
|
|
993
|
-
const isInitial = !this.
|
|
994
|
-
const stateStorage = this.
|
|
998
|
+
const isInitial = !this.getStorage.has(key);
|
|
999
|
+
const stateStorage = this.getStorage(symbol, strategyName, exchangeName);
|
|
995
1000
|
await stateStorage.waitForInit(isInitial);
|
|
996
1001
|
await stateStorage.writeValue(symbol, signalRow);
|
|
997
1002
|
};
|
|
@@ -1679,6 +1684,101 @@ class PersistCandleUtils {
|
|
|
1679
1684
|
* ```
|
|
1680
1685
|
*/
|
|
1681
1686
|
const PersistCandleAdapter = new PersistCandleUtils();
|
|
1687
|
+
/**
|
|
1688
|
+
* Utility class for managing signal storage persistence.
|
|
1689
|
+
*
|
|
1690
|
+
* Features:
|
|
1691
|
+
* - Memoized storage instances
|
|
1692
|
+
* - Custom adapter support
|
|
1693
|
+
* - Atomic read/write operations for StorageData
|
|
1694
|
+
* - Each signal stored as separate file keyed by id
|
|
1695
|
+
* - Crash-safe signal state management
|
|
1696
|
+
*
|
|
1697
|
+
* Used by SignalLiveUtils for live mode persistence of signals.
|
|
1698
|
+
*/
|
|
1699
|
+
class PersistStorageUtils {
|
|
1700
|
+
constructor() {
|
|
1701
|
+
this.PersistStorageFactory = PersistBase;
|
|
1702
|
+
this.getStorageStorage = functoolsKit.memoize(([backtest]) => backtest ? `backtest` : `live`, (backtest) => Reflect.construct(this.PersistStorageFactory, [
|
|
1703
|
+
backtest ? `backtest` : `live`,
|
|
1704
|
+
`./dump/data/signal-storage/`,
|
|
1705
|
+
]));
|
|
1706
|
+
/**
|
|
1707
|
+
* Reads persisted signals data.
|
|
1708
|
+
*
|
|
1709
|
+
* Called by StorageLiveUtils/StorageBacktestUtils.waitForInit() to restore state.
|
|
1710
|
+
* Uses keys() from PersistBase to iterate over all stored signals.
|
|
1711
|
+
* Returns empty array if no signals exist.
|
|
1712
|
+
*
|
|
1713
|
+
* @param backtest - If true, reads from backtest storage; otherwise from live storage
|
|
1714
|
+
* @returns Promise resolving to array of signal entries
|
|
1715
|
+
*/
|
|
1716
|
+
this.readStorageData = async (backtest) => {
|
|
1717
|
+
bt.loggerService.info(PERSIST_STORAGE_UTILS_METHOD_NAME_READ_DATA);
|
|
1718
|
+
const key = backtest ? `backtest` : `live`;
|
|
1719
|
+
const isInitial = !this.getStorageStorage.has(key);
|
|
1720
|
+
const stateStorage = this.getStorageStorage(backtest);
|
|
1721
|
+
await stateStorage.waitForInit(isInitial);
|
|
1722
|
+
const signals = [];
|
|
1723
|
+
for await (const signalId of stateStorage.keys()) {
|
|
1724
|
+
const signal = await stateStorage.readValue(signalId);
|
|
1725
|
+
signals.push(signal);
|
|
1726
|
+
}
|
|
1727
|
+
return signals;
|
|
1728
|
+
};
|
|
1729
|
+
/**
|
|
1730
|
+
* Writes signal data to disk with atomic file writes.
|
|
1731
|
+
*
|
|
1732
|
+
* Called by StorageLiveUtils/StorageBacktestUtils after signal changes to persist state.
|
|
1733
|
+
* Uses signal.id as the storage key for individual file storage.
|
|
1734
|
+
* Uses atomic writes to prevent corruption on crashes.
|
|
1735
|
+
*
|
|
1736
|
+
* @param signalData - Signal entries to persist
|
|
1737
|
+
* @param backtest - If true, writes to backtest storage; otherwise to live storage
|
|
1738
|
+
* @returns Promise that resolves when write is complete
|
|
1739
|
+
*/
|
|
1740
|
+
this.writeStorageData = async (signalData, backtest) => {
|
|
1741
|
+
bt.loggerService.info(PERSIST_STORAGE_UTILS_METHOD_NAME_WRITE_DATA);
|
|
1742
|
+
const key = backtest ? `backtest` : `live`;
|
|
1743
|
+
const isInitial = !this.getStorageStorage.has(key);
|
|
1744
|
+
const stateStorage = this.getStorageStorage(backtest);
|
|
1745
|
+
await stateStorage.waitForInit(isInitial);
|
|
1746
|
+
for (const signal of signalData) {
|
|
1747
|
+
await stateStorage.writeValue(signal.id, signal);
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Registers a custom persistence adapter.
|
|
1753
|
+
*
|
|
1754
|
+
* @param Ctor - Custom PersistBase constructor
|
|
1755
|
+
*/
|
|
1756
|
+
usePersistStorageAdapter(Ctor) {
|
|
1757
|
+
bt.loggerService.info(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_PERSIST_STORAGE_ADAPTER);
|
|
1758
|
+
this.PersistStorageFactory = Ctor;
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Switches to the default JSON persist adapter.
|
|
1762
|
+
* All future persistence writes will use JSON storage.
|
|
1763
|
+
*/
|
|
1764
|
+
useJson() {
|
|
1765
|
+
bt.loggerService.log(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_JSON);
|
|
1766
|
+
this.usePersistStorageAdapter(PersistBase);
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
1770
|
+
* All future persistence writes will be no-ops.
|
|
1771
|
+
*/
|
|
1772
|
+
useDummy() {
|
|
1773
|
+
bt.loggerService.log(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_DUMMY);
|
|
1774
|
+
this.usePersistStorageAdapter(PersistDummy);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Global singleton instance of PersistStorageUtils.
|
|
1779
|
+
* Used by SignalLiveUtils for signal storage persistence.
|
|
1780
|
+
*/
|
|
1781
|
+
const PersistStorageAdapter = new PersistStorageUtils();
|
|
1682
1782
|
|
|
1683
1783
|
const MS_PER_MINUTE$1 = 60000;
|
|
1684
1784
|
const INTERVAL_MINUTES$4 = {
|
|
@@ -31449,6 +31549,480 @@ class ConstantUtils {
|
|
|
31449
31549
|
*/
|
|
31450
31550
|
const Constant = new ConstantUtils();
|
|
31451
31551
|
|
|
31552
|
+
const MAX_SIGNALS = 25;
|
|
31553
|
+
const STORAGE_BACKTEST_METHOD_NAME_WAIT_FOR_INIT = "StorageBacktestUtils.waitForInit";
|
|
31554
|
+
const STORAGE_BACKTEST_METHOD_NAME_UPDATE_STORAGE = "StorageBacktestUtils._updateStorage";
|
|
31555
|
+
const STORAGE_BACKTEST_METHOD_NAME_HANDLE_OPENED = "StorageBacktestUtils.handleOpened";
|
|
31556
|
+
const STORAGE_BACKTEST_METHOD_NAME_HANDLE_CLOSED = "StorageBacktestUtils.handleClosed";
|
|
31557
|
+
const STORAGE_BACKTEST_METHOD_NAME_HANDLE_SCHEDULED = "StorageBacktestUtils.handleScheduled";
|
|
31558
|
+
const STORAGE_BACKTEST_METHOD_NAME_HANDLE_CANCELLED = "StorageBacktestUtils.handleCancelled";
|
|
31559
|
+
const STORAGE_BACKTEST_METHOD_NAME_FIND_BY_ID = "StorageBacktestUtils.findById";
|
|
31560
|
+
const STORAGE_BACKTEST_METHOD_NAME_LIST = "StorageBacktestUtils.list";
|
|
31561
|
+
const STORAGE_LIVE_METHOD_NAME_WAIT_FOR_INIT = "StorageLiveUtils.waitForInit";
|
|
31562
|
+
const STORAGE_LIVE_METHOD_NAME_UPDATE_STORAGE = "StorageLiveUtils._updateStorage";
|
|
31563
|
+
const STORAGE_LIVE_METHOD_NAME_HANDLE_OPENED = "StorageLiveUtils.handleOpened";
|
|
31564
|
+
const STORAGE_LIVE_METHOD_NAME_HANDLE_CLOSED = "StorageLiveUtils.handleClosed";
|
|
31565
|
+
const STORAGE_LIVE_METHOD_NAME_HANDLE_SCHEDULED = "StorageLiveUtils.handleScheduled";
|
|
31566
|
+
const STORAGE_LIVE_METHOD_NAME_HANDLE_CANCELLED = "StorageLiveUtils.handleCancelled";
|
|
31567
|
+
const STORAGE_LIVE_METHOD_NAME_FIND_BY_ID = "StorageLiveUtils.findById";
|
|
31568
|
+
const STORAGE_LIVE_METHOD_NAME_LIST = "StorageLiveUtils.list";
|
|
31569
|
+
const STORAGE_ADAPTER_METHOD_NAME_ENABLE = "StorageAdapter.enable";
|
|
31570
|
+
const STORAGE_ADAPTER_METHOD_NAME_DISABLE = "StorageAdapter.disable";
|
|
31571
|
+
const STORAGE_ADAPTER_METHOD_NAME_FIND_SIGNAL_BY_ID = "StorageAdapter.findSignalById";
|
|
31572
|
+
const STORAGE_ADAPTER_METHOD_NAME_LIST_SIGNAL_BACKTEST = "StorageAdapter.listSignalBacktest";
|
|
31573
|
+
const STORAGE_ADAPTER_METHOD_NAME_LIST_SIGNAL_LIVE = "StorageAdapter.listSignalLive";
|
|
31574
|
+
/**
|
|
31575
|
+
* Utility class for managing backtest signal history.
|
|
31576
|
+
*
|
|
31577
|
+
* Stores trading signal history for admin dashboard display during backtesting
|
|
31578
|
+
* with automatic initialization, deduplication, and storage limits.
|
|
31579
|
+
*
|
|
31580
|
+
* @example
|
|
31581
|
+
* ```typescript
|
|
31582
|
+
* import { StorageBacktestUtils } from "./classes/Storage";
|
|
31583
|
+
*
|
|
31584
|
+
* const storage = new StorageBacktestUtils();
|
|
31585
|
+
*
|
|
31586
|
+
* // Handle signal events
|
|
31587
|
+
* await storage.handleOpened(tickResult);
|
|
31588
|
+
* await storage.handleClosed(tickResult);
|
|
31589
|
+
*
|
|
31590
|
+
* // Query signals
|
|
31591
|
+
* const signal = await storage.findById("signal-123");
|
|
31592
|
+
* const allSignals = await storage.list();
|
|
31593
|
+
* ```
|
|
31594
|
+
*/
|
|
31595
|
+
class StorageBacktestUtils {
|
|
31596
|
+
constructor() {
|
|
31597
|
+
/**
|
|
31598
|
+
* Initializes storage by loading existing signal history from persist layer.
|
|
31599
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
31600
|
+
*/
|
|
31601
|
+
this.waitForInit = functoolsKit.singleshot(async () => {
|
|
31602
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_WAIT_FOR_INIT);
|
|
31603
|
+
const signalList = await PersistStorageAdapter.readStorageData(true);
|
|
31604
|
+
signalList.sort((a, b) => a.priority - b.priority);
|
|
31605
|
+
this._signals = new Map(signalList
|
|
31606
|
+
.slice(-MAX_SIGNALS)
|
|
31607
|
+
.map((signal) => [signal.id, signal]));
|
|
31608
|
+
});
|
|
31609
|
+
/**
|
|
31610
|
+
* Handles signal opened event.
|
|
31611
|
+
*
|
|
31612
|
+
* @param tick - Tick result containing opened signal data
|
|
31613
|
+
* @returns Promise resolving when storage is updated
|
|
31614
|
+
*/
|
|
31615
|
+
this.handleOpened = async (tick) => {
|
|
31616
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_HANDLE_OPENED, {
|
|
31617
|
+
signalId: tick.signal.id,
|
|
31618
|
+
});
|
|
31619
|
+
await this.waitForInit();
|
|
31620
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31621
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31622
|
+
return;
|
|
31623
|
+
}
|
|
31624
|
+
this._signals.set(tick.signal.id, {
|
|
31625
|
+
...tick.signal,
|
|
31626
|
+
status: "opened",
|
|
31627
|
+
priority: Date.now(),
|
|
31628
|
+
updatedAt: tick.createdAt,
|
|
31629
|
+
});
|
|
31630
|
+
await this._updateStorage();
|
|
31631
|
+
};
|
|
31632
|
+
/**
|
|
31633
|
+
* Handles signal closed event.
|
|
31634
|
+
*
|
|
31635
|
+
* @param tick - Tick result containing closed signal data
|
|
31636
|
+
* @returns Promise resolving when storage is updated
|
|
31637
|
+
*/
|
|
31638
|
+
this.handleClosed = async (tick) => {
|
|
31639
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_HANDLE_CLOSED, {
|
|
31640
|
+
signalId: tick.signal.id,
|
|
31641
|
+
});
|
|
31642
|
+
await this.waitForInit();
|
|
31643
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31644
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31645
|
+
return;
|
|
31646
|
+
}
|
|
31647
|
+
this._signals.set(tick.signal.id, {
|
|
31648
|
+
...tick.signal,
|
|
31649
|
+
status: "closed",
|
|
31650
|
+
priority: Date.now(),
|
|
31651
|
+
updatedAt: tick.createdAt,
|
|
31652
|
+
});
|
|
31653
|
+
await this._updateStorage();
|
|
31654
|
+
};
|
|
31655
|
+
/**
|
|
31656
|
+
* Handles signal scheduled event.
|
|
31657
|
+
*
|
|
31658
|
+
* @param tick - Tick result containing scheduled signal data
|
|
31659
|
+
* @returns Promise resolving when storage is updated
|
|
31660
|
+
*/
|
|
31661
|
+
this.handleScheduled = async (tick) => {
|
|
31662
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_HANDLE_SCHEDULED, {
|
|
31663
|
+
signalId: tick.signal.id,
|
|
31664
|
+
});
|
|
31665
|
+
await this.waitForInit();
|
|
31666
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31667
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31668
|
+
return;
|
|
31669
|
+
}
|
|
31670
|
+
this._signals.set(tick.signal.id, {
|
|
31671
|
+
...tick.signal,
|
|
31672
|
+
status: "scheduled",
|
|
31673
|
+
priority: Date.now(),
|
|
31674
|
+
updatedAt: tick.createdAt,
|
|
31675
|
+
});
|
|
31676
|
+
await this._updateStorage();
|
|
31677
|
+
};
|
|
31678
|
+
/**
|
|
31679
|
+
* Handles signal cancelled event.
|
|
31680
|
+
*
|
|
31681
|
+
* @param tick - Tick result containing cancelled signal data
|
|
31682
|
+
* @returns Promise resolving when storage is updated
|
|
31683
|
+
*/
|
|
31684
|
+
this.handleCancelled = async (tick) => {
|
|
31685
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_HANDLE_CANCELLED, {
|
|
31686
|
+
signalId: tick.signal.id,
|
|
31687
|
+
});
|
|
31688
|
+
await this.waitForInit();
|
|
31689
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31690
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31691
|
+
return;
|
|
31692
|
+
}
|
|
31693
|
+
this._signals.set(tick.signal.id, {
|
|
31694
|
+
...tick.signal,
|
|
31695
|
+
status: "cancelled",
|
|
31696
|
+
priority: Date.now(),
|
|
31697
|
+
updatedAt: tick.createdAt,
|
|
31698
|
+
});
|
|
31699
|
+
await this._updateStorage();
|
|
31700
|
+
};
|
|
31701
|
+
/**
|
|
31702
|
+
* Finds a signal by its unique identifier.
|
|
31703
|
+
*
|
|
31704
|
+
* @param id - Signal identifier
|
|
31705
|
+
* @returns Promise resolving to signal row or null if not found
|
|
31706
|
+
*/
|
|
31707
|
+
this.findById = async (id) => {
|
|
31708
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_FIND_BY_ID, { id });
|
|
31709
|
+
await this.waitForInit();
|
|
31710
|
+
return this._signals.get(id) ?? null;
|
|
31711
|
+
};
|
|
31712
|
+
/**
|
|
31713
|
+
* Lists all stored backtest signals.
|
|
31714
|
+
*
|
|
31715
|
+
* @returns Promise resolving to array of signal rows
|
|
31716
|
+
*/
|
|
31717
|
+
this.list = async () => {
|
|
31718
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_LIST);
|
|
31719
|
+
await this.waitForInit();
|
|
31720
|
+
return Array.from(this._signals.values());
|
|
31721
|
+
};
|
|
31722
|
+
}
|
|
31723
|
+
/**
|
|
31724
|
+
* Persists current signal history to storage.
|
|
31725
|
+
* Sorts by priority and limits to MAX_SIGNALS entries.
|
|
31726
|
+
*
|
|
31727
|
+
* @throws Error if storage not initialized
|
|
31728
|
+
*/
|
|
31729
|
+
async _updateStorage() {
|
|
31730
|
+
bt.loggerService.info(STORAGE_BACKTEST_METHOD_NAME_UPDATE_STORAGE);
|
|
31731
|
+
if (!this._signals) {
|
|
31732
|
+
throw new Error("StorageBacktestUtils not initialized. Call waitForInit first.");
|
|
31733
|
+
}
|
|
31734
|
+
const signalList = Array.from(this._signals.values());
|
|
31735
|
+
signalList.sort((a, b) => a.priority - b.priority);
|
|
31736
|
+
await PersistStorageAdapter.writeStorageData(signalList.slice(-MAX_SIGNALS), true);
|
|
31737
|
+
}
|
|
31738
|
+
}
|
|
31739
|
+
/**
|
|
31740
|
+
* Utility class for managing live trading signal history.
|
|
31741
|
+
*
|
|
31742
|
+
* Stores trading signal history for admin dashboard display during live trading
|
|
31743
|
+
* with automatic initialization, deduplication, and storage limits.
|
|
31744
|
+
*
|
|
31745
|
+
* @example
|
|
31746
|
+
* ```typescript
|
|
31747
|
+
* import { StorageLiveUtils } from "./classes/Storage";
|
|
31748
|
+
*
|
|
31749
|
+
* const storage = new StorageLiveUtils();
|
|
31750
|
+
*
|
|
31751
|
+
* // Handle signal events
|
|
31752
|
+
* await storage.handleOpened(tickResult);
|
|
31753
|
+
* await storage.handleClosed(tickResult);
|
|
31754
|
+
*
|
|
31755
|
+
* // Query signals
|
|
31756
|
+
* const signal = await storage.findById("signal-123");
|
|
31757
|
+
* const allSignals = await storage.list();
|
|
31758
|
+
* ```
|
|
31759
|
+
*/
|
|
31760
|
+
class StorageLiveUtils {
|
|
31761
|
+
constructor() {
|
|
31762
|
+
/**
|
|
31763
|
+
* Initializes storage by loading existing signal history from persist layer.
|
|
31764
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
31765
|
+
*/
|
|
31766
|
+
this.waitForInit = functoolsKit.singleshot(async () => {
|
|
31767
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_WAIT_FOR_INIT);
|
|
31768
|
+
const signalList = await PersistStorageAdapter.readStorageData(false);
|
|
31769
|
+
signalList.sort((a, b) => a.priority - b.priority);
|
|
31770
|
+
this._signals = new Map(signalList
|
|
31771
|
+
.slice(-MAX_SIGNALS)
|
|
31772
|
+
.map((signal) => [signal.id, signal]));
|
|
31773
|
+
});
|
|
31774
|
+
/**
|
|
31775
|
+
* Handles signal opened event.
|
|
31776
|
+
*
|
|
31777
|
+
* @param tick - Tick result containing opened signal data
|
|
31778
|
+
* @returns Promise resolving when history is updated
|
|
31779
|
+
*/
|
|
31780
|
+
this.handleOpened = async (tick) => {
|
|
31781
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_HANDLE_OPENED, {
|
|
31782
|
+
signalId: tick.signal.id,
|
|
31783
|
+
});
|
|
31784
|
+
await this.waitForInit();
|
|
31785
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31786
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31787
|
+
return;
|
|
31788
|
+
}
|
|
31789
|
+
this._signals.set(tick.signal.id, {
|
|
31790
|
+
...tick.signal,
|
|
31791
|
+
status: "opened",
|
|
31792
|
+
priority: Date.now(),
|
|
31793
|
+
updatedAt: tick.createdAt,
|
|
31794
|
+
});
|
|
31795
|
+
await this._updateStorage();
|
|
31796
|
+
};
|
|
31797
|
+
/**
|
|
31798
|
+
* Handles signal closed event.
|
|
31799
|
+
*
|
|
31800
|
+
* @param tick - Tick result containing closed signal data
|
|
31801
|
+
* @returns Promise resolving when history is updated
|
|
31802
|
+
*/
|
|
31803
|
+
this.handleClosed = async (tick) => {
|
|
31804
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_HANDLE_CLOSED, {
|
|
31805
|
+
signalId: tick.signal.id,
|
|
31806
|
+
});
|
|
31807
|
+
await this.waitForInit();
|
|
31808
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31809
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31810
|
+
return;
|
|
31811
|
+
}
|
|
31812
|
+
this._signals.set(tick.signal.id, {
|
|
31813
|
+
...tick.signal,
|
|
31814
|
+
status: "closed",
|
|
31815
|
+
priority: Date.now(),
|
|
31816
|
+
updatedAt: tick.createdAt,
|
|
31817
|
+
});
|
|
31818
|
+
await this._updateStorage();
|
|
31819
|
+
};
|
|
31820
|
+
/**
|
|
31821
|
+
* Handles signal scheduled event.
|
|
31822
|
+
*
|
|
31823
|
+
* @param tick - Tick result containing scheduled signal data
|
|
31824
|
+
* @returns Promise resolving when history is updated
|
|
31825
|
+
*/
|
|
31826
|
+
this.handleScheduled = async (tick) => {
|
|
31827
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_HANDLE_SCHEDULED, {
|
|
31828
|
+
signalId: tick.signal.id,
|
|
31829
|
+
});
|
|
31830
|
+
await this.waitForInit();
|
|
31831
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31832
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31833
|
+
return;
|
|
31834
|
+
}
|
|
31835
|
+
this._signals.set(tick.signal.id, {
|
|
31836
|
+
...tick.signal,
|
|
31837
|
+
status: "scheduled",
|
|
31838
|
+
priority: Date.now(),
|
|
31839
|
+
updatedAt: tick.createdAt,
|
|
31840
|
+
});
|
|
31841
|
+
await this._updateStorage();
|
|
31842
|
+
};
|
|
31843
|
+
/**
|
|
31844
|
+
* Handles signal cancelled event.
|
|
31845
|
+
*
|
|
31846
|
+
* @param tick - Tick result containing cancelled signal data
|
|
31847
|
+
* @returns Promise resolving when history is updated
|
|
31848
|
+
*/
|
|
31849
|
+
this.handleCancelled = async (tick) => {
|
|
31850
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_HANDLE_CANCELLED, {
|
|
31851
|
+
signalId: tick.signal.id,
|
|
31852
|
+
});
|
|
31853
|
+
await this.waitForInit();
|
|
31854
|
+
const lastStorage = this._signals.get(tick.signal.id);
|
|
31855
|
+
if (lastStorage && lastStorage.updatedAt > tick.createdAt) {
|
|
31856
|
+
return;
|
|
31857
|
+
}
|
|
31858
|
+
this._signals.set(tick.signal.id, {
|
|
31859
|
+
...tick.signal,
|
|
31860
|
+
status: "cancelled",
|
|
31861
|
+
priority: Date.now(),
|
|
31862
|
+
updatedAt: tick.createdAt,
|
|
31863
|
+
});
|
|
31864
|
+
await this._updateStorage();
|
|
31865
|
+
};
|
|
31866
|
+
/**
|
|
31867
|
+
* Finds a signal by its unique identifier.
|
|
31868
|
+
*
|
|
31869
|
+
* @param id - Signal identifier
|
|
31870
|
+
* @returns Promise resolving to signal row or null if not found
|
|
31871
|
+
*/
|
|
31872
|
+
this.findById = async (id) => {
|
|
31873
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_FIND_BY_ID, { id });
|
|
31874
|
+
await this.waitForInit();
|
|
31875
|
+
return this._signals.get(id) ?? null;
|
|
31876
|
+
};
|
|
31877
|
+
/**
|
|
31878
|
+
* Lists all stored live signals.
|
|
31879
|
+
*
|
|
31880
|
+
* @returns Promise resolving to array of signal rows
|
|
31881
|
+
*/
|
|
31882
|
+
this.list = async () => {
|
|
31883
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_LIST);
|
|
31884
|
+
await this.waitForInit();
|
|
31885
|
+
return Array.from(this._signals.values());
|
|
31886
|
+
};
|
|
31887
|
+
}
|
|
31888
|
+
/**
|
|
31889
|
+
* Persists current signal history to storage.
|
|
31890
|
+
* Sorts by priority and limits to MAX_SIGNALS entries.
|
|
31891
|
+
*
|
|
31892
|
+
* @throws Error if storage not initialized
|
|
31893
|
+
*/
|
|
31894
|
+
async _updateStorage() {
|
|
31895
|
+
bt.loggerService.info(STORAGE_LIVE_METHOD_NAME_UPDATE_STORAGE);
|
|
31896
|
+
if (!this._signals) {
|
|
31897
|
+
throw new Error("StorageLiveUtils not initialized. Call waitForInit first.");
|
|
31898
|
+
}
|
|
31899
|
+
const signalList = Array.from(this._signals.values());
|
|
31900
|
+
signalList.sort((a, b) => a.priority - b.priority);
|
|
31901
|
+
await PersistStorageAdapter.writeStorageData(signalList.slice(-MAX_SIGNALS), false);
|
|
31902
|
+
}
|
|
31903
|
+
}
|
|
31904
|
+
/**
|
|
31905
|
+
* Main storage adapter for signal history management.
|
|
31906
|
+
*
|
|
31907
|
+
* Provides unified interface for accessing backtest and live signal history
|
|
31908
|
+
* for admin dashboard. Subscribes to signal emitters and automatically
|
|
31909
|
+
* updates history on signal events.
|
|
31910
|
+
*
|
|
31911
|
+
* @example
|
|
31912
|
+
* ```typescript
|
|
31913
|
+
* import { Storage } from "./classes/Storage";
|
|
31914
|
+
*
|
|
31915
|
+
* // Enable signal history tracking
|
|
31916
|
+
* const unsubscribe = Storage.enable();
|
|
31917
|
+
*
|
|
31918
|
+
* // Query signals
|
|
31919
|
+
* const backtestSignals = await Storage.listSignalBacktest();
|
|
31920
|
+
* const liveSignals = await Storage.listSignalLive();
|
|
31921
|
+
* const signal = await Storage.findSignalById("signal-123");
|
|
31922
|
+
*
|
|
31923
|
+
* // Disable tracking
|
|
31924
|
+
* Storage.disable();
|
|
31925
|
+
* ```
|
|
31926
|
+
*/
|
|
31927
|
+
class StorageAdapter {
|
|
31928
|
+
constructor() {
|
|
31929
|
+
this._signalLiveUtils = new StorageLiveUtils();
|
|
31930
|
+
this._signalBacktestUtils = new StorageBacktestUtils();
|
|
31931
|
+
/**
|
|
31932
|
+
* Enables signal history tracking by subscribing to emitters.
|
|
31933
|
+
*
|
|
31934
|
+
* @returns Cleanup function to unsubscribe from all emitters
|
|
31935
|
+
*/
|
|
31936
|
+
this.enable = functoolsKit.singleshot(() => {
|
|
31937
|
+
bt.loggerService.info(STORAGE_ADAPTER_METHOD_NAME_ENABLE);
|
|
31938
|
+
let unLive;
|
|
31939
|
+
let unBacktest;
|
|
31940
|
+
{
|
|
31941
|
+
const unBacktestOpen = signalBacktestEmitter
|
|
31942
|
+
.filter(({ action }) => action === "opened")
|
|
31943
|
+
.connect((tick) => this._signalBacktestUtils.handleOpened(tick));
|
|
31944
|
+
const unBacktestClose = signalBacktestEmitter
|
|
31945
|
+
.filter(({ action }) => action === "closed")
|
|
31946
|
+
.connect((tick) => this._signalBacktestUtils.handleClosed(tick));
|
|
31947
|
+
const unBacktestScheduled = signalBacktestEmitter
|
|
31948
|
+
.filter(({ action }) => action === "scheduled")
|
|
31949
|
+
.connect((tick) => this._signalBacktestUtils.handleScheduled(tick));
|
|
31950
|
+
const unBacktestCancelled = signalBacktestEmitter
|
|
31951
|
+
.filter(({ action }) => action === "cancelled")
|
|
31952
|
+
.connect((tick) => this._signalBacktestUtils.handleCancelled(tick));
|
|
31953
|
+
unBacktest = functoolsKit.compose(() => unBacktestOpen(), () => unBacktestClose(), () => unBacktestScheduled(), () => unBacktestCancelled());
|
|
31954
|
+
}
|
|
31955
|
+
{
|
|
31956
|
+
const unLiveOpen = signalLiveEmitter
|
|
31957
|
+
.filter(({ action }) => action === "opened")
|
|
31958
|
+
.connect((tick) => this._signalLiveUtils.handleOpened(tick));
|
|
31959
|
+
const unLiveClose = signalLiveEmitter
|
|
31960
|
+
.filter(({ action }) => action === "closed")
|
|
31961
|
+
.connect((tick) => this._signalLiveUtils.handleClosed(tick));
|
|
31962
|
+
const unLiveScheduled = signalLiveEmitter
|
|
31963
|
+
.filter(({ action }) => action === "scheduled")
|
|
31964
|
+
.connect((tick) => this._signalLiveUtils.handleScheduled(tick));
|
|
31965
|
+
const unLiveCancelled = signalLiveEmitter
|
|
31966
|
+
.filter(({ action }) => action === "cancelled")
|
|
31967
|
+
.connect((tick) => this._signalLiveUtils.handleCancelled(tick));
|
|
31968
|
+
unLive = functoolsKit.compose(() => unLiveOpen(), () => unLiveClose(), () => unLiveScheduled(), () => unLiveCancelled());
|
|
31969
|
+
}
|
|
31970
|
+
return () => {
|
|
31971
|
+
unLive();
|
|
31972
|
+
unBacktest();
|
|
31973
|
+
this.enable.clear();
|
|
31974
|
+
};
|
|
31975
|
+
});
|
|
31976
|
+
/**
|
|
31977
|
+
* Disables signal history tracking by unsubscribing from emitters.
|
|
31978
|
+
*/
|
|
31979
|
+
this.disable = () => {
|
|
31980
|
+
bt.loggerService.info(STORAGE_ADAPTER_METHOD_NAME_DISABLE);
|
|
31981
|
+
if (this.enable.hasValue()) {
|
|
31982
|
+
const lastSubscription = this.enable();
|
|
31983
|
+
lastSubscription();
|
|
31984
|
+
}
|
|
31985
|
+
};
|
|
31986
|
+
/**
|
|
31987
|
+
* Finds a signal by ID across both backtest and live history.
|
|
31988
|
+
*
|
|
31989
|
+
* @param id - Signal identifier
|
|
31990
|
+
* @returns Promise resolving to signal row
|
|
31991
|
+
* @throws Error if signal not found in either storage
|
|
31992
|
+
*/
|
|
31993
|
+
this.findSignalById = async (id) => {
|
|
31994
|
+
bt.loggerService.info(STORAGE_ADAPTER_METHOD_NAME_FIND_SIGNAL_BY_ID, { id });
|
|
31995
|
+
let result = null;
|
|
31996
|
+
if ((result = await this._signalBacktestUtils.findById(id))) {
|
|
31997
|
+
return result;
|
|
31998
|
+
}
|
|
31999
|
+
if ((result = await this._signalLiveUtils.findById(id))) {
|
|
32000
|
+
return result;
|
|
32001
|
+
}
|
|
32002
|
+
throw new Error(`Storage signal with id ${id} not found`);
|
|
32003
|
+
};
|
|
32004
|
+
/**
|
|
32005
|
+
* Lists all backtest signal history.
|
|
32006
|
+
*
|
|
32007
|
+
* @returns Promise resolving to array of backtest signal rows
|
|
32008
|
+
*/
|
|
32009
|
+
this.listSignalBacktest = async () => {
|
|
32010
|
+
bt.loggerService.info(STORAGE_ADAPTER_METHOD_NAME_LIST_SIGNAL_BACKTEST);
|
|
32011
|
+
return await this._signalBacktestUtils.list();
|
|
32012
|
+
};
|
|
32013
|
+
/**
|
|
32014
|
+
* Lists all live signal history.
|
|
32015
|
+
*
|
|
32016
|
+
* @returns Promise resolving to array of live signal rows
|
|
32017
|
+
*/
|
|
32018
|
+
this.listSignalLive = async () => {
|
|
32019
|
+
bt.loggerService.info(STORAGE_ADAPTER_METHOD_NAME_LIST_SIGNAL_LIVE);
|
|
32020
|
+
return await this._signalLiveUtils.list();
|
|
32021
|
+
};
|
|
32022
|
+
}
|
|
32023
|
+
}
|
|
32024
|
+
const Storage = new StorageAdapter();
|
|
32025
|
+
|
|
31452
32026
|
const EXCHANGE_METHOD_NAME_GET_CANDLES = "ExchangeUtils.getCandles";
|
|
31453
32027
|
const EXCHANGE_METHOD_NAME_GET_AVERAGE_PRICE = "ExchangeUtils.getAveragePrice";
|
|
31454
32028
|
const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
|
|
@@ -33477,11 +34051,13 @@ exports.PersistPartialAdapter = PersistPartialAdapter;
|
|
|
33477
34051
|
exports.PersistRiskAdapter = PersistRiskAdapter;
|
|
33478
34052
|
exports.PersistScheduleAdapter = PersistScheduleAdapter;
|
|
33479
34053
|
exports.PersistSignalAdapter = PersistSignalAdapter;
|
|
34054
|
+
exports.PersistStorageAdapter = PersistStorageAdapter;
|
|
33480
34055
|
exports.PositionSize = PositionSize;
|
|
33481
34056
|
exports.Report = Report;
|
|
33482
34057
|
exports.ReportBase = ReportBase;
|
|
33483
34058
|
exports.Risk = Risk;
|
|
33484
34059
|
exports.Schedule = Schedule;
|
|
34060
|
+
exports.Storage = Storage;
|
|
33485
34061
|
exports.Strategy = Strategy;
|
|
33486
34062
|
exports.Walker = Walker;
|
|
33487
34063
|
exports.addActionSchema = addActionSchema;
|