backtest-kit 2.2.10 → 2.2.12

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 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.getSignalStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName]) => `${symbol}:${strategyName}:${exchangeName}`, (symbol, strategyName, exchangeName) => Reflect.construct(this.PersistSignalFactory, [
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.getSignalStorage.has(key);
971
- const stateStorage = this.getSignalStorage(symbol, strategyName, exchangeName);
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.getSignalStorage.has(key);
994
- const stateStorage = this.getSignalStorage(symbol, strategyName, exchangeName);
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,489 @@ 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
+ if (!this.enable.hasValue()) {
31996
+ throw new Error("StorageAdapter is not enabled. Call enable() first.");
31997
+ }
31998
+ let result = null;
31999
+ if ((result = await this._signalBacktestUtils.findById(id))) {
32000
+ return result;
32001
+ }
32002
+ if ((result = await this._signalLiveUtils.findById(id))) {
32003
+ return result;
32004
+ }
32005
+ throw new Error(`Storage signal with id ${id} not found`);
32006
+ };
32007
+ /**
32008
+ * Lists all backtest signal history.
32009
+ *
32010
+ * @returns Promise resolving to array of backtest signal rows
32011
+ */
32012
+ this.listSignalBacktest = async () => {
32013
+ bt.loggerService.info(STORAGE_ADAPTER_METHOD_NAME_LIST_SIGNAL_BACKTEST);
32014
+ if (!this.enable.hasValue()) {
32015
+ throw new Error("StorageAdapter is not enabled. Call enable() first.");
32016
+ }
32017
+ return await this._signalBacktestUtils.list();
32018
+ };
32019
+ /**
32020
+ * Lists all live signal history.
32021
+ *
32022
+ * @returns Promise resolving to array of live signal rows
32023
+ */
32024
+ this.listSignalLive = async () => {
32025
+ bt.loggerService.info(STORAGE_ADAPTER_METHOD_NAME_LIST_SIGNAL_LIVE);
32026
+ if (!this.enable.hasValue()) {
32027
+ throw new Error("StorageAdapter is not enabled. Call enable() first.");
32028
+ }
32029
+ return await this._signalLiveUtils.list();
32030
+ };
32031
+ }
32032
+ }
32033
+ const Storage = new StorageAdapter();
32034
+
31452
32035
  const EXCHANGE_METHOD_NAME_GET_CANDLES = "ExchangeUtils.getCandles";
31453
32036
  const EXCHANGE_METHOD_NAME_GET_AVERAGE_PRICE = "ExchangeUtils.getAveragePrice";
31454
32037
  const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
@@ -33477,11 +34060,13 @@ exports.PersistPartialAdapter = PersistPartialAdapter;
33477
34060
  exports.PersistRiskAdapter = PersistRiskAdapter;
33478
34061
  exports.PersistScheduleAdapter = PersistScheduleAdapter;
33479
34062
  exports.PersistSignalAdapter = PersistSignalAdapter;
34063
+ exports.PersistStorageAdapter = PersistStorageAdapter;
33480
34064
  exports.PositionSize = PositionSize;
33481
34065
  exports.Report = Report;
33482
34066
  exports.ReportBase = ReportBase;
33483
34067
  exports.Risk = Risk;
33484
34068
  exports.Schedule = Schedule;
34069
+ exports.Storage = Storage;
33485
34070
  exports.Strategy = Strategy;
33486
34071
  exports.Walker = Walker;
33487
34072
  exports.addActionSchema = addActionSchema;