backtest-kit 3.3.2 → 3.4.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.
- package/README.md +22 -22
- package/build/index.cjs +525 -0
- package/build/index.mjs +524 -1
- package/package.json +3 -3
- package/types.d.ts +199 -2
package/README.md
CHANGED
|
@@ -487,6 +487,28 @@ npm install @backtest-kit/pinets pinets backtest-kit
|
|
|
487
487
|
```
|
|
488
488
|
|
|
489
489
|
|
|
490
|
+
### @backtest-kit/graph
|
|
491
|
+
|
|
492
|
+
> **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/graph)** 🔗
|
|
493
|
+
|
|
494
|
+
The **@backtest-kit/graph** package lets you compose backtest-kit computations as a typed directed acyclic graph (DAG). Define source nodes that fetch market data and output nodes that compute derived values — then resolve the whole graph in topological order with automatic parallelism.
|
|
495
|
+
|
|
496
|
+
#### Key Features
|
|
497
|
+
- 🔌 **DAG Execution**: Nodes are resolved bottom-up in topological order with `Promise.all` parallelism
|
|
498
|
+
- 🔒 **Type-Safe Values**: TypeScript infers the return type of every node through the graph via generics
|
|
499
|
+
- 🧱 **Two APIs**: Low-level `INode` for runtime/storage, high-level `sourceNode` + `outputNode` builders for authoring
|
|
500
|
+
- 💾 **DB-Ready Serialization**: `serialize` / `deserialize` convert the graph to a flat `IFlatNode[]` list with `id` / `nodeIds`
|
|
501
|
+
- 🌐 **Context-Aware Fetch**: `sourceNode` receives `(symbol, when, exchangeName)` from the execution context automatically
|
|
502
|
+
|
|
503
|
+
#### Use Case
|
|
504
|
+
Perfect for multi-timeframe strategies where multiple Pine Script or indicator computations must be combined. Instead of manually chaining async calls, define each computation as a node and let the graph resolve dependencies in parallel. Adding a new filter or timeframe requires no changes to the existing wiring.
|
|
505
|
+
|
|
506
|
+
#### Get Started
|
|
507
|
+
```bash
|
|
508
|
+
npm install @backtest-kit/graph backtest-kit
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
|
|
490
512
|
### @backtest-kit/ui
|
|
491
513
|
|
|
492
514
|
> **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/ui)** 📊
|
|
@@ -556,28 +578,6 @@ npm install @backtest-kit/signals backtest-kit
|
|
|
556
578
|
|
|
557
579
|
|
|
558
580
|
|
|
559
|
-
### @backtest-kit/graph
|
|
560
|
-
|
|
561
|
-
> **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/graph)** 🔗
|
|
562
|
-
|
|
563
|
-
The **@backtest-kit/graph** package lets you compose backtest-kit computations as a typed directed acyclic graph (DAG). Define source nodes that fetch market data and output nodes that compute derived values — then resolve the whole graph in topological order with automatic parallelism.
|
|
564
|
-
|
|
565
|
-
#### Key Features
|
|
566
|
-
- 🔌 **DAG Execution**: Nodes are resolved bottom-up in topological order with `Promise.all` parallelism
|
|
567
|
-
- 🔒 **Type-Safe Values**: TypeScript infers the return type of every node through the graph via generics
|
|
568
|
-
- 🧱 **Two APIs**: Low-level `INode` for runtime/storage, high-level `sourceNode` + `outputNode` builders for authoring
|
|
569
|
-
- 💾 **DB-Ready Serialization**: `serialize` / `deserialize` convert the graph to a flat `IFlatNode[]` list with `id` / `nodeIds`
|
|
570
|
-
- 🌐 **Context-Aware Fetch**: `sourceNode` receives `(symbol, when, exchangeName)` from the execution context automatically
|
|
571
|
-
|
|
572
|
-
#### Use Case
|
|
573
|
-
Perfect for multi-timeframe strategies where multiple Pine Script or indicator computations must be combined. Instead of manually chaining async calls, define each computation as a node and let the graph resolve dependencies in parallel. Adding a new filter or timeframe requires no changes to the existing wiring.
|
|
574
|
-
|
|
575
|
-
#### Get Started
|
|
576
|
-
```bash
|
|
577
|
-
npm install @backtest-kit/graph backtest-kit
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
|
|
581
581
|
### @backtest-kit/sidekick
|
|
582
582
|
|
|
583
583
|
> **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/sidekick)** 🚀
|
package/build/index.cjs
CHANGED
|
@@ -442,6 +442,14 @@ const GLOBAL_CONFIG = {
|
|
|
442
442
|
* Default: 50 signals
|
|
443
443
|
*/
|
|
444
444
|
CC_MAX_SIGNALS: 50,
|
|
445
|
+
/**
|
|
446
|
+
* Maximum number of log lines to keep in storage.
|
|
447
|
+
* Older log lines are removed when this limit is exceeded.
|
|
448
|
+
* This helps prevent unbounded log growth which can consume memory and degrade performance over time.
|
|
449
|
+
*
|
|
450
|
+
* Default: 1000 log lines
|
|
451
|
+
*/
|
|
452
|
+
CC_MAX_LOG_LINES: 1000,
|
|
445
453
|
/**
|
|
446
454
|
* Enables mutex locking for candle fetching to prevent concurrent fetches of the same candles.
|
|
447
455
|
* This can help avoid redundant API calls and ensure data consistency when multiple processes/threads attempt to fetch candles simultaneously.
|
|
@@ -776,6 +784,11 @@ const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_WRITE_DATA = "PersistNotificationUt
|
|
|
776
784
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_JSON = "PersistNotificationUtils.useJson";
|
|
777
785
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_DUMMY = "PersistNotificationUtils.useDummy";
|
|
778
786
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_PERSIST_NOTIFICATION_ADAPTER = "PersistNotificationUtils.usePersistNotificationAdapter";
|
|
787
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA = "PersistLogUtils.readLogData";
|
|
788
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA = "PersistLogUtils.writeLogData";
|
|
789
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON = "PersistLogUtils.useJson";
|
|
790
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY = "PersistLogUtils.useDummy";
|
|
791
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER = "PersistLogUtils.usePersistLogAdapter";
|
|
779
792
|
const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
|
|
780
793
|
const BASE_UNLINK_RETRY_COUNT = 5;
|
|
781
794
|
const BASE_UNLINK_RETRY_DELAY = 1000;
|
|
@@ -1946,6 +1959,106 @@ class PersistNotificationUtils {
|
|
|
1946
1959
|
* Used by NotificationPersistLiveUtils/NotificationPersistBacktestUtils for notification persistence.
|
|
1947
1960
|
*/
|
|
1948
1961
|
const PersistNotificationAdapter = new PersistNotificationUtils();
|
|
1962
|
+
/**
|
|
1963
|
+
* Utility class for managing log entry persistence.
|
|
1964
|
+
*
|
|
1965
|
+
* Features:
|
|
1966
|
+
* - Memoized storage instance
|
|
1967
|
+
* - Custom adapter support
|
|
1968
|
+
* - Atomic read/write operations for LogData
|
|
1969
|
+
* - Each log entry stored as separate file keyed by id
|
|
1970
|
+
* - Crash-safe log state management
|
|
1971
|
+
*
|
|
1972
|
+
* Used by LogPersistUtils for log entry persistence.
|
|
1973
|
+
*/
|
|
1974
|
+
class PersistLogUtils {
|
|
1975
|
+
constructor() {
|
|
1976
|
+
this.PersistLogFactory = PersistBase;
|
|
1977
|
+
this._logStorage = null;
|
|
1978
|
+
/**
|
|
1979
|
+
* Reads persisted log entries.
|
|
1980
|
+
*
|
|
1981
|
+
* Called by LogPersistUtils.waitForInit() to restore state.
|
|
1982
|
+
* Uses keys() from PersistBase to iterate over all stored entries.
|
|
1983
|
+
* Returns empty array if no entries exist.
|
|
1984
|
+
*
|
|
1985
|
+
* @returns Promise resolving to array of log entries
|
|
1986
|
+
*/
|
|
1987
|
+
this.readLogData = async () => {
|
|
1988
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA);
|
|
1989
|
+
const isInitial = !this._logStorage;
|
|
1990
|
+
const stateStorage = this.getLogStorage();
|
|
1991
|
+
await stateStorage.waitForInit(isInitial);
|
|
1992
|
+
const entries = [];
|
|
1993
|
+
for await (const entryId of stateStorage.keys()) {
|
|
1994
|
+
const entry = await stateStorage.readValue(entryId);
|
|
1995
|
+
entries.push(entry);
|
|
1996
|
+
}
|
|
1997
|
+
return entries;
|
|
1998
|
+
};
|
|
1999
|
+
/**
|
|
2000
|
+
* Writes log entries to disk with atomic file writes.
|
|
2001
|
+
*
|
|
2002
|
+
* Called by LogPersistUtils after each log call to persist state.
|
|
2003
|
+
* Uses entry.id as the storage key for individual file storage.
|
|
2004
|
+
* Uses atomic writes to prevent corruption on crashes.
|
|
2005
|
+
*
|
|
2006
|
+
* @param logData - Log entries to persist
|
|
2007
|
+
* @returns Promise that resolves when write is complete
|
|
2008
|
+
*/
|
|
2009
|
+
this.writeLogData = async (logData) => {
|
|
2010
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA);
|
|
2011
|
+
const isInitial = !this._logStorage;
|
|
2012
|
+
const stateStorage = this.getLogStorage();
|
|
2013
|
+
await stateStorage.waitForInit(isInitial);
|
|
2014
|
+
for (const entry of logData) {
|
|
2015
|
+
if (await stateStorage.hasValue(entry.id)) {
|
|
2016
|
+
continue;
|
|
2017
|
+
}
|
|
2018
|
+
await stateStorage.writeValue(entry.id, entry);
|
|
2019
|
+
}
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
getLogStorage() {
|
|
2023
|
+
if (!this._logStorage) {
|
|
2024
|
+
this._logStorage = Reflect.construct(this.PersistLogFactory, [
|
|
2025
|
+
`log`,
|
|
2026
|
+
`./dump/data/log/`,
|
|
2027
|
+
]);
|
|
2028
|
+
}
|
|
2029
|
+
return this._logStorage;
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Registers a custom persistence adapter.
|
|
2033
|
+
*
|
|
2034
|
+
* @param Ctor - Custom PersistBase constructor
|
|
2035
|
+
*/
|
|
2036
|
+
usePersistLogAdapter(Ctor) {
|
|
2037
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER);
|
|
2038
|
+
this.PersistLogFactory = Ctor;
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Switches to the default JSON persist adapter.
|
|
2042
|
+
* All future persistence writes will use JSON storage.
|
|
2043
|
+
*/
|
|
2044
|
+
useJson() {
|
|
2045
|
+
bt.loggerService.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON);
|
|
2046
|
+
this.usePersistLogAdapter(PersistBase);
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
2050
|
+
* All future persistence writes will be no-ops.
|
|
2051
|
+
*/
|
|
2052
|
+
useDummy() {
|
|
2053
|
+
bt.loggerService.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY);
|
|
2054
|
+
this.usePersistLogAdapter(PersistDummy);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Global singleton instance of PersistLogUtils.
|
|
2059
|
+
* Used by LogPersistUtils for log entry persistence.
|
|
2060
|
+
*/
|
|
2061
|
+
const PersistLogAdapter = new PersistLogUtils();
|
|
1949
2062
|
|
|
1950
2063
|
var _a$2, _b$2;
|
|
1951
2064
|
const BUSY_DELAY = 100;
|
|
@@ -30882,6 +30995,416 @@ async function dumpMessages(resultId, history, result, outputDir = "./dump/strat
|
|
|
30882
30995
|
}
|
|
30883
30996
|
}
|
|
30884
30997
|
|
|
30998
|
+
const LOG_PERSIST_METHOD_NAME_WAIT_FOR_INIT = "LogPersistUtils.waitForInit";
|
|
30999
|
+
const LOG_PERSIST_METHOD_NAME_LOG = "LogPersistUtils.log";
|
|
31000
|
+
const LOG_PERSIST_METHOD_NAME_DEBUG = "LogPersistUtils.debug";
|
|
31001
|
+
const LOG_PERSIST_METHOD_NAME_INFO = "LogPersistUtils.info";
|
|
31002
|
+
const LOG_PERSIST_METHOD_NAME_WARN = "LogPersistUtils.warn";
|
|
31003
|
+
const LOG_PERSIST_METHOD_NAME_GET_LIST = "LogPersistUtils.getList";
|
|
31004
|
+
const LOG_MEMORY_METHOD_NAME_LOG = "LogMemoryUtils.log";
|
|
31005
|
+
const LOG_MEMORY_METHOD_NAME_DEBUG = "LogMemoryUtils.debug";
|
|
31006
|
+
const LOG_MEMORY_METHOD_NAME_INFO = "LogMemoryUtils.info";
|
|
31007
|
+
const LOG_MEMORY_METHOD_NAME_WARN = "LogMemoryUtils.warn";
|
|
31008
|
+
const LOG_MEMORY_METHOD_NAME_GET_LIST = "LogMemoryUtils.getList";
|
|
31009
|
+
const LOG_ADAPTER_METHOD_NAME_USE_LOGGER = "LogAdapter.useLogger";
|
|
31010
|
+
const LOG_ADAPTER_METHOD_NAME_USE_PERSIST = "LogAdapter.usePersist";
|
|
31011
|
+
const LOG_ADAPTER_METHOD_NAME_USE_MEMORY = "LogAdapter.useMemory";
|
|
31012
|
+
const LOG_ADAPTER_METHOD_NAME_USE_DUMMY = "LogAdapter.useDummy";
|
|
31013
|
+
/**
|
|
31014
|
+
* Backtest execution time retrieval function.
|
|
31015
|
+
* Returns the 'when' timestamp from the execution context if available, otherwise returns the current time.
|
|
31016
|
+
* This allows log entries to be timestamped according to the backtest timeline rather than real-world time, improving log relevance and user experience during backtest analysis.
|
|
31017
|
+
*/
|
|
31018
|
+
const GET_DATE_FN = async () => {
|
|
31019
|
+
if (ExecutionContextService.hasContext()) {
|
|
31020
|
+
return new Date(bt.executionContextService.context.when);
|
|
31021
|
+
}
|
|
31022
|
+
return new Date();
|
|
31023
|
+
};
|
|
31024
|
+
/**
|
|
31025
|
+
* Persistent log adapter.
|
|
31026
|
+
*
|
|
31027
|
+
* Features:
|
|
31028
|
+
* - Persists log entries to disk using PersistLogAdapter
|
|
31029
|
+
* - Lazy initialization with singleshot pattern
|
|
31030
|
+
* - Maintains up to CC_MAX_LOG_LINES most recent entries
|
|
31031
|
+
* - Each entry stored individually keyed by its id
|
|
31032
|
+
*
|
|
31033
|
+
* Use this adapter (default) for log persistence across sessions.
|
|
31034
|
+
*/
|
|
31035
|
+
class LogPersistUtils {
|
|
31036
|
+
constructor() {
|
|
31037
|
+
/** Array of log entries */
|
|
31038
|
+
this._entries = [];
|
|
31039
|
+
/**
|
|
31040
|
+
* Singleshot initialization function that loads entries from disk.
|
|
31041
|
+
* Protected by singleshot to ensure one-time execution.
|
|
31042
|
+
*/
|
|
31043
|
+
this.waitForInit = functoolsKit.singleshot(async () => {
|
|
31044
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_WAIT_FOR_INIT);
|
|
31045
|
+
const list = await PersistLogAdapter.readLogData();
|
|
31046
|
+
list.sort((a, b) => a.timestamp - b.timestamp);
|
|
31047
|
+
this._entries = list.slice(-GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31048
|
+
});
|
|
31049
|
+
/**
|
|
31050
|
+
* Logs a general-purpose message.
|
|
31051
|
+
* Persists entry to disk after appending.
|
|
31052
|
+
* @param topic - The log topic / method name
|
|
31053
|
+
* @param args - Additional arguments
|
|
31054
|
+
*/
|
|
31055
|
+
this.log = async (topic, ...args) => {
|
|
31056
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_LOG, { topic });
|
|
31057
|
+
await this.waitForInit();
|
|
31058
|
+
const date = await GET_DATE_FN();
|
|
31059
|
+
this._entries.push({
|
|
31060
|
+
id: functoolsKit.randomString(),
|
|
31061
|
+
type: "log",
|
|
31062
|
+
timestamp: Date.now(),
|
|
31063
|
+
createdAt: date.toISOString(),
|
|
31064
|
+
topic,
|
|
31065
|
+
args,
|
|
31066
|
+
});
|
|
31067
|
+
this._enforceLimit();
|
|
31068
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31069
|
+
};
|
|
31070
|
+
/**
|
|
31071
|
+
* Logs a debug-level message.
|
|
31072
|
+
* Persists entry to disk after appending.
|
|
31073
|
+
* @param topic - The log topic / method name
|
|
31074
|
+
* @param args - Additional arguments
|
|
31075
|
+
*/
|
|
31076
|
+
this.debug = async (topic, ...args) => {
|
|
31077
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_DEBUG, { topic });
|
|
31078
|
+
await this.waitForInit();
|
|
31079
|
+
const date = await GET_DATE_FN();
|
|
31080
|
+
this._entries.push({
|
|
31081
|
+
id: functoolsKit.randomString(),
|
|
31082
|
+
type: "debug",
|
|
31083
|
+
timestamp: Date.now(),
|
|
31084
|
+
createdAt: date.toISOString(),
|
|
31085
|
+
topic,
|
|
31086
|
+
args,
|
|
31087
|
+
});
|
|
31088
|
+
this._enforceLimit();
|
|
31089
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31090
|
+
};
|
|
31091
|
+
/**
|
|
31092
|
+
* Logs an info-level message.
|
|
31093
|
+
* Persists entry to disk after appending.
|
|
31094
|
+
* @param topic - The log topic / method name
|
|
31095
|
+
* @param args - Additional arguments
|
|
31096
|
+
*/
|
|
31097
|
+
this.info = async (topic, ...args) => {
|
|
31098
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_INFO, { topic });
|
|
31099
|
+
await this.waitForInit();
|
|
31100
|
+
const date = await GET_DATE_FN();
|
|
31101
|
+
this._entries.push({
|
|
31102
|
+
id: functoolsKit.randomString(),
|
|
31103
|
+
type: "info",
|
|
31104
|
+
timestamp: Date.now(),
|
|
31105
|
+
createdAt: date.toISOString(),
|
|
31106
|
+
topic,
|
|
31107
|
+
args,
|
|
31108
|
+
});
|
|
31109
|
+
this._enforceLimit();
|
|
31110
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31111
|
+
};
|
|
31112
|
+
/**
|
|
31113
|
+
* Logs a warning-level message.
|
|
31114
|
+
* Persists entry to disk after appending.
|
|
31115
|
+
* @param topic - The log topic / method name
|
|
31116
|
+
* @param args - Additional arguments
|
|
31117
|
+
*/
|
|
31118
|
+
this.warn = async (topic, ...args) => {
|
|
31119
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_WARN, { topic });
|
|
31120
|
+
await this.waitForInit();
|
|
31121
|
+
const date = await GET_DATE_FN();
|
|
31122
|
+
this._entries.push({
|
|
31123
|
+
id: functoolsKit.randomString(),
|
|
31124
|
+
type: "warn",
|
|
31125
|
+
timestamp: Date.now(),
|
|
31126
|
+
createdAt: date.toISOString(),
|
|
31127
|
+
topic,
|
|
31128
|
+
args,
|
|
31129
|
+
});
|
|
31130
|
+
this._enforceLimit();
|
|
31131
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31132
|
+
};
|
|
31133
|
+
/**
|
|
31134
|
+
* Lists all stored log entries.
|
|
31135
|
+
* @returns Array of all log entries
|
|
31136
|
+
*/
|
|
31137
|
+
this.getList = async () => {
|
|
31138
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_GET_LIST);
|
|
31139
|
+
await this.waitForInit();
|
|
31140
|
+
return [...this._entries];
|
|
31141
|
+
};
|
|
31142
|
+
}
|
|
31143
|
+
/**
|
|
31144
|
+
* Removes oldest entries if limit is exceeded.
|
|
31145
|
+
*/
|
|
31146
|
+
_enforceLimit() {
|
|
31147
|
+
if (this._entries.length > GLOBAL_CONFIG.CC_MAX_LOG_LINES) {
|
|
31148
|
+
this._entries.splice(0, this._entries.length - GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31149
|
+
}
|
|
31150
|
+
}
|
|
31151
|
+
}
|
|
31152
|
+
/**
|
|
31153
|
+
* In-memory log adapter.
|
|
31154
|
+
*
|
|
31155
|
+
* Features:
|
|
31156
|
+
* - Stores log entries in memory only (no persistence)
|
|
31157
|
+
* - Maintains up to CC_MAX_LOG_LINES most recent entries
|
|
31158
|
+
* - Data is lost when application restarts
|
|
31159
|
+
* - Handles all log levels (log, debug, info, warn)
|
|
31160
|
+
*
|
|
31161
|
+
* Use this adapter for testing or when persistence is not required.
|
|
31162
|
+
*/
|
|
31163
|
+
class LogMemoryUtils {
|
|
31164
|
+
constructor() {
|
|
31165
|
+
/** Array of log entries */
|
|
31166
|
+
this._entries = [];
|
|
31167
|
+
/**
|
|
31168
|
+
* Logs a general-purpose message.
|
|
31169
|
+
* Appends entry to in-memory array.
|
|
31170
|
+
* @param topic - The log topic / method name
|
|
31171
|
+
* @param args - Additional arguments
|
|
31172
|
+
*/
|
|
31173
|
+
this.log = async (topic, ...args) => {
|
|
31174
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_LOG, { topic });
|
|
31175
|
+
const date = await GET_DATE_FN();
|
|
31176
|
+
this._entries.push({
|
|
31177
|
+
id: functoolsKit.randomString(),
|
|
31178
|
+
type: "log",
|
|
31179
|
+
timestamp: Date.now(),
|
|
31180
|
+
createdAt: date.toISOString(),
|
|
31181
|
+
topic,
|
|
31182
|
+
args,
|
|
31183
|
+
});
|
|
31184
|
+
this._enforceLimit();
|
|
31185
|
+
};
|
|
31186
|
+
/**
|
|
31187
|
+
* Logs a debug-level message.
|
|
31188
|
+
* Appends entry to in-memory array.
|
|
31189
|
+
* @param topic - The log topic / method name
|
|
31190
|
+
* @param args - Additional arguments
|
|
31191
|
+
*/
|
|
31192
|
+
this.debug = async (topic, ...args) => {
|
|
31193
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_DEBUG, { topic });
|
|
31194
|
+
const date = await GET_DATE_FN();
|
|
31195
|
+
this._entries.push({
|
|
31196
|
+
id: functoolsKit.randomString(),
|
|
31197
|
+
type: "debug",
|
|
31198
|
+
timestamp: Date.now(),
|
|
31199
|
+
createdAt: date.toISOString(),
|
|
31200
|
+
topic,
|
|
31201
|
+
args,
|
|
31202
|
+
});
|
|
31203
|
+
this._enforceLimit();
|
|
31204
|
+
};
|
|
31205
|
+
/**
|
|
31206
|
+
* Logs an info-level message.
|
|
31207
|
+
* Appends entry to in-memory array.
|
|
31208
|
+
* @param topic - The log topic / method name
|
|
31209
|
+
* @param args - Additional arguments
|
|
31210
|
+
*/
|
|
31211
|
+
this.info = async (topic, ...args) => {
|
|
31212
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_INFO, { topic });
|
|
31213
|
+
const date = await GET_DATE_FN();
|
|
31214
|
+
this._entries.push({
|
|
31215
|
+
id: functoolsKit.randomString(),
|
|
31216
|
+
type: "info",
|
|
31217
|
+
timestamp: Date.now(),
|
|
31218
|
+
createdAt: date.toISOString(),
|
|
31219
|
+
topic,
|
|
31220
|
+
args,
|
|
31221
|
+
});
|
|
31222
|
+
this._enforceLimit();
|
|
31223
|
+
};
|
|
31224
|
+
/**
|
|
31225
|
+
* Logs a warning-level message.
|
|
31226
|
+
* Appends entry to in-memory array.
|
|
31227
|
+
* @param topic - The log topic / method name
|
|
31228
|
+
* @param args - Additional arguments
|
|
31229
|
+
*/
|
|
31230
|
+
this.warn = async (topic, ...args) => {
|
|
31231
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_WARN, { topic });
|
|
31232
|
+
const date = await GET_DATE_FN();
|
|
31233
|
+
this._entries.push({
|
|
31234
|
+
id: functoolsKit.randomString(),
|
|
31235
|
+
type: "warn",
|
|
31236
|
+
timestamp: Date.now(),
|
|
31237
|
+
createdAt: date.toISOString(),
|
|
31238
|
+
topic,
|
|
31239
|
+
args,
|
|
31240
|
+
});
|
|
31241
|
+
this._enforceLimit();
|
|
31242
|
+
};
|
|
31243
|
+
/**
|
|
31244
|
+
* Lists all stored log entries.
|
|
31245
|
+
* @returns Array of all log entries
|
|
31246
|
+
*/
|
|
31247
|
+
this.getList = async () => {
|
|
31248
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_GET_LIST);
|
|
31249
|
+
return [...this._entries];
|
|
31250
|
+
};
|
|
31251
|
+
}
|
|
31252
|
+
/**
|
|
31253
|
+
* Removes oldest entries if limit is exceeded.
|
|
31254
|
+
*/
|
|
31255
|
+
_enforceLimit() {
|
|
31256
|
+
if (this._entries.length > GLOBAL_CONFIG.CC_MAX_LOG_LINES) {
|
|
31257
|
+
this._entries.splice(0, this._entries.length - GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31258
|
+
}
|
|
31259
|
+
}
|
|
31260
|
+
}
|
|
31261
|
+
/**
|
|
31262
|
+
* Dummy log adapter that discards all writes.
|
|
31263
|
+
*
|
|
31264
|
+
* Features:
|
|
31265
|
+
* - No-op implementation for all methods
|
|
31266
|
+
* - getList always returns empty array
|
|
31267
|
+
*
|
|
31268
|
+
* Use this adapter to disable log storage completely.
|
|
31269
|
+
*/
|
|
31270
|
+
class LogDummyUtils {
|
|
31271
|
+
/**
|
|
31272
|
+
* Always returns empty array (no storage).
|
|
31273
|
+
* @returns Empty array
|
|
31274
|
+
*/
|
|
31275
|
+
async getList() {
|
|
31276
|
+
return [];
|
|
31277
|
+
}
|
|
31278
|
+
/**
|
|
31279
|
+
* No-op handler for general-purpose log.
|
|
31280
|
+
*/
|
|
31281
|
+
log() {
|
|
31282
|
+
}
|
|
31283
|
+
/**
|
|
31284
|
+
* No-op handler for debug-level log.
|
|
31285
|
+
*/
|
|
31286
|
+
debug() {
|
|
31287
|
+
}
|
|
31288
|
+
/**
|
|
31289
|
+
* No-op handler for info-level log.
|
|
31290
|
+
*/
|
|
31291
|
+
info() {
|
|
31292
|
+
}
|
|
31293
|
+
/**
|
|
31294
|
+
* No-op handler for warning-level log.
|
|
31295
|
+
*/
|
|
31296
|
+
warn() {
|
|
31297
|
+
}
|
|
31298
|
+
}
|
|
31299
|
+
/**
|
|
31300
|
+
* Log adapter with pluggable storage backend.
|
|
31301
|
+
*
|
|
31302
|
+
* Features:
|
|
31303
|
+
* - Adapter pattern for swappable log implementations
|
|
31304
|
+
* - Default adapter: LogMemoryUtils (in-memory storage)
|
|
31305
|
+
* - Alternative adapters: LogPersistUtils, LogDummyUtils
|
|
31306
|
+
* - Convenience methods: usePersist(), useMemory(), useDummy()
|
|
31307
|
+
*/
|
|
31308
|
+
class LogAdapter {
|
|
31309
|
+
constructor() {
|
|
31310
|
+
/** Internal log utils instance */
|
|
31311
|
+
this._log = new LogMemoryUtils();
|
|
31312
|
+
/**
|
|
31313
|
+
* Lists all stored log entries.
|
|
31314
|
+
* Proxies call to the underlying log adapter.
|
|
31315
|
+
* @returns Array of all log entries
|
|
31316
|
+
*/
|
|
31317
|
+
this.getList = async () => {
|
|
31318
|
+
if (this._log.getList) {
|
|
31319
|
+
return await this._log.getList();
|
|
31320
|
+
}
|
|
31321
|
+
return [];
|
|
31322
|
+
};
|
|
31323
|
+
/**
|
|
31324
|
+
* Logs a general-purpose message.
|
|
31325
|
+
* Proxies call to the underlying log adapter.
|
|
31326
|
+
* @param topic - The log topic / method name
|
|
31327
|
+
* @param args - Additional arguments
|
|
31328
|
+
*/
|
|
31329
|
+
this.log = (topic, ...args) => {
|
|
31330
|
+
if (this._log.log) {
|
|
31331
|
+
this._log.log(topic, ...args);
|
|
31332
|
+
}
|
|
31333
|
+
};
|
|
31334
|
+
/**
|
|
31335
|
+
* Logs a debug-level message.
|
|
31336
|
+
* Proxies call to the underlying log adapter.
|
|
31337
|
+
* @param topic - The log topic / method name
|
|
31338
|
+
* @param args - Additional arguments
|
|
31339
|
+
*/
|
|
31340
|
+
this.debug = (topic, ...args) => {
|
|
31341
|
+
if (this._log.debug) {
|
|
31342
|
+
this._log.debug(topic, ...args);
|
|
31343
|
+
}
|
|
31344
|
+
};
|
|
31345
|
+
/**
|
|
31346
|
+
* Logs an info-level message.
|
|
31347
|
+
* Proxies call to the underlying log adapter.
|
|
31348
|
+
* @param topic - The log topic / method name
|
|
31349
|
+
* @param args - Additional arguments
|
|
31350
|
+
*/
|
|
31351
|
+
this.info = (topic, ...args) => {
|
|
31352
|
+
if (this._log.info) {
|
|
31353
|
+
this._log.info(topic, ...args);
|
|
31354
|
+
}
|
|
31355
|
+
};
|
|
31356
|
+
/**
|
|
31357
|
+
* Logs a warning-level message.
|
|
31358
|
+
* Proxies call to the underlying log adapter.
|
|
31359
|
+
* @param topic - The log topic / method name
|
|
31360
|
+
* @param args - Additional arguments
|
|
31361
|
+
*/
|
|
31362
|
+
this.warn = (topic, ...args) => {
|
|
31363
|
+
if (this._log.warn) {
|
|
31364
|
+
this._log.warn(topic, ...args);
|
|
31365
|
+
}
|
|
31366
|
+
};
|
|
31367
|
+
/**
|
|
31368
|
+
* Sets the log adapter constructor.
|
|
31369
|
+
* All future log operations will use this adapter.
|
|
31370
|
+
* @param Ctor - Constructor for log adapter
|
|
31371
|
+
*/
|
|
31372
|
+
this.useLogger = (Ctor) => {
|
|
31373
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_LOGGER);
|
|
31374
|
+
this._log = Reflect.construct(Ctor, []);
|
|
31375
|
+
};
|
|
31376
|
+
/**
|
|
31377
|
+
* Switches to persistent log adapter.
|
|
31378
|
+
* Log entries will be persisted to disk.
|
|
31379
|
+
*/
|
|
31380
|
+
this.usePersist = () => {
|
|
31381
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
31382
|
+
this._log = new LogPersistUtils();
|
|
31383
|
+
};
|
|
31384
|
+
/**
|
|
31385
|
+
* Switches to in-memory log adapter (default).
|
|
31386
|
+
* Log entries will be stored in memory only.
|
|
31387
|
+
*/
|
|
31388
|
+
this.useMemory = () => {
|
|
31389
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_MEMORY);
|
|
31390
|
+
this._log = new LogMemoryUtils();
|
|
31391
|
+
};
|
|
31392
|
+
/**
|
|
31393
|
+
* Switches to dummy log adapter.
|
|
31394
|
+
* All future log writes will be no-ops.
|
|
31395
|
+
*/
|
|
31396
|
+
this.useDummy = () => {
|
|
31397
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
31398
|
+
this._log = new LogDummyUtils();
|
|
31399
|
+
};
|
|
31400
|
+
}
|
|
31401
|
+
}
|
|
31402
|
+
/**
|
|
31403
|
+
* Global singleton instance of LogAdapter.
|
|
31404
|
+
* Provides unified log management with pluggable backends.
|
|
31405
|
+
*/
|
|
31406
|
+
const Log = new LogAdapter();
|
|
31407
|
+
|
|
30885
31408
|
const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
|
|
30886
31409
|
const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
|
|
30887
31410
|
const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
|
|
@@ -38289,6 +38812,7 @@ exports.Exchange = Exchange;
|
|
|
38289
38812
|
exports.ExecutionContextService = ExecutionContextService;
|
|
38290
38813
|
exports.Heat = Heat;
|
|
38291
38814
|
exports.Live = Live;
|
|
38815
|
+
exports.Log = Log;
|
|
38292
38816
|
exports.Markdown = Markdown;
|
|
38293
38817
|
exports.MarkdownFileBase = MarkdownFileBase;
|
|
38294
38818
|
exports.MarkdownFolderBase = MarkdownFolderBase;
|
|
@@ -38301,6 +38825,7 @@ exports.Performance = Performance;
|
|
|
38301
38825
|
exports.PersistBase = PersistBase;
|
|
38302
38826
|
exports.PersistBreakevenAdapter = PersistBreakevenAdapter;
|
|
38303
38827
|
exports.PersistCandleAdapter = PersistCandleAdapter;
|
|
38828
|
+
exports.PersistLogAdapter = PersistLogAdapter;
|
|
38304
38829
|
exports.PersistNotificationAdapter = PersistNotificationAdapter;
|
|
38305
38830
|
exports.PersistPartialAdapter = PersistPartialAdapter;
|
|
38306
38831
|
exports.PersistRiskAdapter = PersistRiskAdapter;
|