backtest-kit 1.1.4 → 1.1.6
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 +91 -38
- package/build/index.cjs +464 -31
- package/build/index.mjs +461 -32
- package/package.json +1 -1
- package/types.d.ts +1366 -1010
package/types.d.ts
CHANGED
|
@@ -140,6 +140,8 @@ interface IExchangeCallbacks {
|
|
|
140
140
|
interface IExchangeSchema {
|
|
141
141
|
/** Unique exchange identifier for registration */
|
|
142
142
|
exchangeName: ExchangeName;
|
|
143
|
+
/** Optional developer note for documentation */
|
|
144
|
+
note?: string;
|
|
143
145
|
/**
|
|
144
146
|
* Fetch candles from data source (API or database).
|
|
145
147
|
*
|
|
@@ -278,6 +280,8 @@ interface IFrameCallbacks {
|
|
|
278
280
|
interface IFrameSchema {
|
|
279
281
|
/** Unique identifier for this frame */
|
|
280
282
|
frameName: FrameName;
|
|
283
|
+
/** Optional developer note for documentation */
|
|
284
|
+
note?: string;
|
|
281
285
|
/** Interval for timestamp generation */
|
|
282
286
|
interval: FrameInterval;
|
|
283
287
|
/** Start of backtest period (inclusive) */
|
|
@@ -413,6 +417,8 @@ interface IStrategyCallbacks {
|
|
|
413
417
|
interface IStrategySchema {
|
|
414
418
|
/** Unique strategy identifier for registration */
|
|
415
419
|
strategyName: StrategyName;
|
|
420
|
+
/** Optional developer note for documentation */
|
|
421
|
+
note?: string;
|
|
416
422
|
/** Minimum interval between getSignal calls (throttling) */
|
|
417
423
|
interval: SignalInterval;
|
|
418
424
|
/** Signal generation function (returns null if no signal, validated DTO if signal) */
|
|
@@ -666,6 +672,90 @@ declare function addExchange(exchangeSchema: IExchangeSchema): void;
|
|
|
666
672
|
*/
|
|
667
673
|
declare function addFrame(frameSchema: IFrameSchema): void;
|
|
668
674
|
|
|
675
|
+
/**
|
|
676
|
+
* Returns a list of all registered exchange schemas.
|
|
677
|
+
*
|
|
678
|
+
* Retrieves all exchanges that have been registered via addExchange().
|
|
679
|
+
* Useful for debugging, documentation, or building dynamic UIs.
|
|
680
|
+
*
|
|
681
|
+
* @returns Array of exchange schemas with their configurations
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```typescript
|
|
685
|
+
* import { listExchanges, addExchange } from "backtest-kit";
|
|
686
|
+
*
|
|
687
|
+
* addExchange({
|
|
688
|
+
* exchangeName: "binance",
|
|
689
|
+
* note: "Binance cryptocurrency exchange",
|
|
690
|
+
* getCandles: async (symbol, interval, since, limit) => [...],
|
|
691
|
+
* formatPrice: async (symbol, price) => price.toFixed(2),
|
|
692
|
+
* formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
|
|
693
|
+
* });
|
|
694
|
+
*
|
|
695
|
+
* const exchanges = listExchanges();
|
|
696
|
+
* console.log(exchanges);
|
|
697
|
+
* // [{ exchangeName: "binance", note: "Binance cryptocurrency exchange", ... }]
|
|
698
|
+
* ```
|
|
699
|
+
*/
|
|
700
|
+
declare function listExchanges(): Promise<IExchangeSchema[]>;
|
|
701
|
+
/**
|
|
702
|
+
* Returns a list of all registered strategy schemas.
|
|
703
|
+
*
|
|
704
|
+
* Retrieves all strategies that have been registered via addStrategy().
|
|
705
|
+
* Useful for debugging, documentation, or building dynamic UIs.
|
|
706
|
+
*
|
|
707
|
+
* @returns Array of strategy schemas with their configurations
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```typescript
|
|
711
|
+
* import { listStrategies, addStrategy } from "backtest-kit";
|
|
712
|
+
*
|
|
713
|
+
* addStrategy({
|
|
714
|
+
* strategyName: "my-strategy",
|
|
715
|
+
* note: "Simple moving average crossover strategy",
|
|
716
|
+
* interval: "5m",
|
|
717
|
+
* getSignal: async (symbol) => ({
|
|
718
|
+
* position: "long",
|
|
719
|
+
* priceOpen: 50000,
|
|
720
|
+
* priceTakeProfit: 51000,
|
|
721
|
+
* priceStopLoss: 49000,
|
|
722
|
+
* minuteEstimatedTime: 60,
|
|
723
|
+
* }),
|
|
724
|
+
* });
|
|
725
|
+
*
|
|
726
|
+
* const strategies = listStrategies();
|
|
727
|
+
* console.log(strategies);
|
|
728
|
+
* // [{ strategyName: "my-strategy", note: "Simple moving average...", ... }]
|
|
729
|
+
* ```
|
|
730
|
+
*/
|
|
731
|
+
declare function listStrategies(): Promise<IStrategySchema[]>;
|
|
732
|
+
/**
|
|
733
|
+
* Returns a list of all registered frame schemas.
|
|
734
|
+
*
|
|
735
|
+
* Retrieves all frames that have been registered via addFrame().
|
|
736
|
+
* Useful for debugging, documentation, or building dynamic UIs.
|
|
737
|
+
*
|
|
738
|
+
* @returns Array of frame schemas with their configurations
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```typescript
|
|
742
|
+
* import { listFrames, addFrame } from "backtest-kit";
|
|
743
|
+
*
|
|
744
|
+
* addFrame({
|
|
745
|
+
* frameName: "1d-backtest",
|
|
746
|
+
* note: "One day backtest period for testing",
|
|
747
|
+
* interval: "1m",
|
|
748
|
+
* startDate: new Date("2024-01-01T00:00:00Z"),
|
|
749
|
+
* endDate: new Date("2024-01-02T00:00:00Z"),
|
|
750
|
+
* });
|
|
751
|
+
*
|
|
752
|
+
* const frames = listFrames();
|
|
753
|
+
* console.log(frames);
|
|
754
|
+
* // [{ frameName: "1d-backtest", note: "One day backtest...", ... }]
|
|
755
|
+
* ```
|
|
756
|
+
*/
|
|
757
|
+
declare function listFrames(): Promise<IFrameSchema[]>;
|
|
758
|
+
|
|
669
759
|
/**
|
|
670
760
|
* Contract for background execution completion events.
|
|
671
761
|
*
|
|
@@ -696,6 +786,37 @@ interface DoneContract {
|
|
|
696
786
|
symbol: string;
|
|
697
787
|
}
|
|
698
788
|
|
|
789
|
+
/**
|
|
790
|
+
* Contract for backtest progress events.
|
|
791
|
+
*
|
|
792
|
+
* Emitted during Backtest.background() execution to track progress.
|
|
793
|
+
* Contains information about total frames, processed frames, and completion percentage.
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
* ```typescript
|
|
797
|
+
* import { listenProgress } from "backtest-kit";
|
|
798
|
+
*
|
|
799
|
+
* listenProgress((event) => {
|
|
800
|
+
* console.log(`Progress: ${(event.progress * 100).toFixed(2)}%`);
|
|
801
|
+
* console.log(`Processed: ${event.processedFrames} / ${event.totalFrames}`);
|
|
802
|
+
* });
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
interface ProgressContract {
|
|
806
|
+
/** exchangeName - Name of the exchange used in execution */
|
|
807
|
+
exchangeName: string;
|
|
808
|
+
/** strategyName - Name of the strategy being executed */
|
|
809
|
+
strategyName: string;
|
|
810
|
+
/** symbol - Trading symbol (e.g., "BTCUSDT") */
|
|
811
|
+
symbol: string;
|
|
812
|
+
/** totalFrames - Total number of frames to process */
|
|
813
|
+
totalFrames: number;
|
|
814
|
+
/** processedFrames - Number of frames processed so far */
|
|
815
|
+
processedFrames: number;
|
|
816
|
+
/** progress - Completion percentage from 0.0 to 1.0 */
|
|
817
|
+
progress: number;
|
|
818
|
+
}
|
|
819
|
+
|
|
699
820
|
/**
|
|
700
821
|
* Subscribes to all signal events with queued async processing.
|
|
701
822
|
*
|
|
@@ -924,6 +1045,37 @@ declare function listenDone(fn: (event: DoneContract) => void): () => void;
|
|
|
924
1045
|
* ```
|
|
925
1046
|
*/
|
|
926
1047
|
declare function listenDoneOnce(filterFn: (event: DoneContract) => boolean, fn: (event: DoneContract) => void): () => void;
|
|
1048
|
+
/**
|
|
1049
|
+
* Subscribes to backtest progress events with queued async processing.
|
|
1050
|
+
*
|
|
1051
|
+
* Emits during Backtest.background() execution to track progress.
|
|
1052
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
1053
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
1054
|
+
*
|
|
1055
|
+
* @param fn - Callback function to handle progress events
|
|
1056
|
+
* @returns Unsubscribe function to stop listening to events
|
|
1057
|
+
*
|
|
1058
|
+
* @example
|
|
1059
|
+
* ```typescript
|
|
1060
|
+
* import { listenProgress, Backtest } from "backtest-kit";
|
|
1061
|
+
*
|
|
1062
|
+
* const unsubscribe = listenProgress((event) => {
|
|
1063
|
+
* console.log(`Progress: ${(event.progress * 100).toFixed(2)}%`);
|
|
1064
|
+
* console.log(`${event.processedFrames} / ${event.totalFrames} frames`);
|
|
1065
|
+
* console.log(`Strategy: ${event.strategyName}, Symbol: ${event.symbol}`);
|
|
1066
|
+
* });
|
|
1067
|
+
*
|
|
1068
|
+
* Backtest.background("BTCUSDT", {
|
|
1069
|
+
* strategyName: "my-strategy",
|
|
1070
|
+
* exchangeName: "binance",
|
|
1071
|
+
* frameName: "1d-backtest"
|
|
1072
|
+
* });
|
|
1073
|
+
*
|
|
1074
|
+
* // Later: stop listening
|
|
1075
|
+
* unsubscribe();
|
|
1076
|
+
* ```
|
|
1077
|
+
*/
|
|
1078
|
+
declare function listenProgress(fn: (event: ProgressContract) => void): () => void;
|
|
927
1079
|
|
|
928
1080
|
/**
|
|
929
1081
|
* Fetches historical candle data from the registered exchange.
|
|
@@ -1026,540 +1178,976 @@ declare function getDate(): Promise<Date>;
|
|
|
1026
1178
|
*/
|
|
1027
1179
|
declare function getMode(): Promise<"backtest" | "live">;
|
|
1028
1180
|
|
|
1029
|
-
declare const BASE_WAIT_FOR_INIT_SYMBOL: unique symbol;
|
|
1030
1181
|
/**
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
*
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
*
|
|
1049
|
-
*/
|
|
1050
|
-
type EntityId = string | number;
|
|
1051
|
-
/**
|
|
1052
|
-
* Base interface for persisted entities.
|
|
1182
|
+
* Statistical data calculated from backtest results.
|
|
1183
|
+
*
|
|
1184
|
+
* All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
|
|
1185
|
+
* Provides comprehensive metrics for strategy performance analysis.
|
|
1186
|
+
*
|
|
1187
|
+
* @example
|
|
1188
|
+
* ```typescript
|
|
1189
|
+
* const stats = await Backtest.getData("my-strategy");
|
|
1190
|
+
*
|
|
1191
|
+
* console.log(`Total signals: ${stats.totalSignals}`);
|
|
1192
|
+
* console.log(`Win rate: ${stats.winRate}%`);
|
|
1193
|
+
* console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
|
|
1194
|
+
*
|
|
1195
|
+
* // Access raw signal data
|
|
1196
|
+
* stats.signalList.forEach(signal => {
|
|
1197
|
+
* console.log(`Signal ${signal.signal.id}: ${signal.pnl.pnlPercentage}%`);
|
|
1198
|
+
* });
|
|
1199
|
+
* ```
|
|
1053
1200
|
*/
|
|
1054
|
-
interface
|
|
1201
|
+
interface BacktestStatistics {
|
|
1202
|
+
/** Array of all closed signals with full details (price, PNL, timestamps, etc.) */
|
|
1203
|
+
signalList: IStrategyTickResultClosed[];
|
|
1204
|
+
/** Total number of closed signals */
|
|
1205
|
+
totalSignals: number;
|
|
1206
|
+
/** Number of winning signals (PNL > 0) */
|
|
1207
|
+
winCount: number;
|
|
1208
|
+
/** Number of losing signals (PNL < 0) */
|
|
1209
|
+
lossCount: number;
|
|
1210
|
+
/** Win rate as percentage (0-100), null if unsafe. Higher is better. */
|
|
1211
|
+
winRate: number | null;
|
|
1212
|
+
/** Average PNL per signal as percentage, null if unsafe. Higher is better. */
|
|
1213
|
+
avgPnl: number | null;
|
|
1214
|
+
/** Cumulative PNL across all signals as percentage, null if unsafe. Higher is better. */
|
|
1215
|
+
totalPnl: number | null;
|
|
1216
|
+
/** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
|
|
1217
|
+
stdDev: number | null;
|
|
1218
|
+
/** Sharpe Ratio (risk-adjusted return = avgPnl / stdDev), null if unsafe. Higher is better. */
|
|
1219
|
+
sharpeRatio: number | null;
|
|
1220
|
+
/** Annualized Sharpe Ratio (sharpeRatio × √365), null if unsafe. Higher is better. */
|
|
1221
|
+
annualizedSharpeRatio: number | null;
|
|
1222
|
+
/** Certainty Ratio (avgWin / |avgLoss|), null if unsafe. Higher is better. */
|
|
1223
|
+
certaintyRatio: number | null;
|
|
1224
|
+
/** Expected yearly returns based on average trade duration and PNL, null if unsafe. Higher is better. */
|
|
1225
|
+
expectedYearlyReturns: number | null;
|
|
1055
1226
|
}
|
|
1056
1227
|
/**
|
|
1057
|
-
*
|
|
1058
|
-
*
|
|
1228
|
+
* Service for generating and saving backtest markdown reports.
|
|
1229
|
+
*
|
|
1230
|
+
* Features:
|
|
1231
|
+
* - Listens to signal events via onTick callback
|
|
1232
|
+
* - Accumulates closed signals per strategy using memoized storage
|
|
1233
|
+
* - Generates markdown tables with detailed signal information
|
|
1234
|
+
* - Saves reports to disk in logs/backtest/{strategyName}.md
|
|
1235
|
+
*
|
|
1236
|
+
* @example
|
|
1237
|
+
* ```typescript
|
|
1238
|
+
* const service = new BacktestMarkdownService();
|
|
1239
|
+
*
|
|
1240
|
+
* // Add to strategy callbacks
|
|
1241
|
+
* addStrategy({
|
|
1242
|
+
* strategyName: "my-strategy",
|
|
1243
|
+
* callbacks: {
|
|
1244
|
+
* onTick: (symbol, result, backtest) => {
|
|
1245
|
+
* service.tick(result);
|
|
1246
|
+
* }
|
|
1247
|
+
* }
|
|
1248
|
+
* });
|
|
1249
|
+
*
|
|
1250
|
+
* // After backtest, generate and save report
|
|
1251
|
+
* await service.saveReport("my-strategy");
|
|
1252
|
+
* ```
|
|
1059
1253
|
*/
|
|
1060
|
-
|
|
1254
|
+
declare class BacktestMarkdownService {
|
|
1255
|
+
/** Logger service for debug output */
|
|
1256
|
+
private readonly loggerService;
|
|
1061
1257
|
/**
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1258
|
+
* Memoized function to get or create ReportStorage for a strategy.
|
|
1259
|
+
* Each strategy gets its own isolated storage instance.
|
|
1260
|
+
*/
|
|
1261
|
+
private getStorage;
|
|
1262
|
+
/**
|
|
1263
|
+
* Processes tick events and accumulates closed signals.
|
|
1264
|
+
* Should be called from IStrategyCallbacks.onTick.
|
|
1064
1265
|
*
|
|
1065
|
-
*
|
|
1066
|
-
*
|
|
1266
|
+
* Only processes closed signals - opened signals are ignored.
|
|
1267
|
+
*
|
|
1268
|
+
* @param data - Tick result from strategy execution (opened or closed)
|
|
1269
|
+
*
|
|
1270
|
+
* @example
|
|
1271
|
+
* ```typescript
|
|
1272
|
+
* const service = new BacktestMarkdownService();
|
|
1273
|
+
*
|
|
1274
|
+
* callbacks: {
|
|
1275
|
+
* onTick: (symbol, result, backtest) => {
|
|
1276
|
+
* service.tick(result);
|
|
1277
|
+
* }
|
|
1278
|
+
* }
|
|
1279
|
+
* ```
|
|
1067
1280
|
*/
|
|
1068
|
-
|
|
1281
|
+
private tick;
|
|
1069
1282
|
/**
|
|
1070
|
-
*
|
|
1283
|
+
* Gets statistical data from all closed signals for a strategy.
|
|
1284
|
+
* Delegates to ReportStorage.getData().
|
|
1071
1285
|
*
|
|
1072
|
-
* @param
|
|
1073
|
-
* @returns
|
|
1074
|
-
*
|
|
1286
|
+
* @param strategyName - Strategy name to get data for
|
|
1287
|
+
* @returns Statistical data object with all metrics
|
|
1288
|
+
*
|
|
1289
|
+
* @example
|
|
1290
|
+
* ```typescript
|
|
1291
|
+
* const service = new BacktestMarkdownService();
|
|
1292
|
+
* const stats = await service.getData("my-strategy");
|
|
1293
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
1294
|
+
* ```
|
|
1075
1295
|
*/
|
|
1076
|
-
|
|
1296
|
+
getData: (strategyName: StrategyName) => Promise<BacktestStatistics>;
|
|
1077
1297
|
/**
|
|
1078
|
-
*
|
|
1298
|
+
* Generates markdown report with all closed signals for a strategy.
|
|
1299
|
+
* Delegates to ReportStorage.generateReport().
|
|
1079
1300
|
*
|
|
1080
|
-
* @param
|
|
1081
|
-
* @returns
|
|
1301
|
+
* @param strategyName - Strategy name to generate report for
|
|
1302
|
+
* @returns Markdown formatted report string with table of all closed signals
|
|
1303
|
+
*
|
|
1304
|
+
* @example
|
|
1305
|
+
* ```typescript
|
|
1306
|
+
* const service = new BacktestMarkdownService();
|
|
1307
|
+
* const markdown = await service.getReport("my-strategy");
|
|
1308
|
+
* console.log(markdown);
|
|
1309
|
+
* ```
|
|
1082
1310
|
*/
|
|
1083
|
-
|
|
1311
|
+
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
1084
1312
|
/**
|
|
1085
|
-
*
|
|
1313
|
+
* Saves strategy report to disk.
|
|
1314
|
+
* Creates directory if it doesn't exist.
|
|
1315
|
+
* Delegates to ReportStorage.dump().
|
|
1086
1316
|
*
|
|
1087
|
-
* @param
|
|
1088
|
-
* @param
|
|
1089
|
-
*
|
|
1090
|
-
* @
|
|
1317
|
+
* @param strategyName - Strategy name to save report for
|
|
1318
|
+
* @param path - Directory path to save report (default: "./logs/backtest")
|
|
1319
|
+
*
|
|
1320
|
+
* @example
|
|
1321
|
+
* ```typescript
|
|
1322
|
+
* const service = new BacktestMarkdownService();
|
|
1323
|
+
*
|
|
1324
|
+
* // Save to default path: ./logs/backtest/my-strategy.md
|
|
1325
|
+
* await service.dump("my-strategy");
|
|
1326
|
+
*
|
|
1327
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
1328
|
+
* await service.dump("my-strategy", "./custom/path");
|
|
1329
|
+
* ```
|
|
1091
1330
|
*/
|
|
1092
|
-
|
|
1093
|
-
}
|
|
1094
|
-
/**
|
|
1095
|
-
* Base class for file-based persistence with atomic writes.
|
|
1096
|
-
*
|
|
1097
|
-
* Features:
|
|
1098
|
-
* - Atomic file writes using writeFileAtomic
|
|
1099
|
-
* - Auto-validation and cleanup of corrupted files
|
|
1100
|
-
* - Async generator support for iteration
|
|
1101
|
-
* - Retry logic for file deletion
|
|
1102
|
-
*
|
|
1103
|
-
* @example
|
|
1104
|
-
* ```typescript
|
|
1105
|
-
* const persist = new PersistBase("my-entity", "./data");
|
|
1106
|
-
* await persist.waitForInit(true);
|
|
1107
|
-
* await persist.writeValue("key1", { data: "value" });
|
|
1108
|
-
* const value = await persist.readValue("key1");
|
|
1109
|
-
* ```
|
|
1110
|
-
*/
|
|
1111
|
-
declare const PersistBase: {
|
|
1112
|
-
new <EntityName extends string = string>(entityName: EntityName, baseDir?: string): {
|
|
1113
|
-
/** Computed directory path for entity storage */
|
|
1114
|
-
_directory: string;
|
|
1115
|
-
readonly entityName: EntityName;
|
|
1116
|
-
readonly baseDir: string;
|
|
1117
|
-
/**
|
|
1118
|
-
* Computes file path for entity ID.
|
|
1119
|
-
*
|
|
1120
|
-
* @param entityId - Entity identifier
|
|
1121
|
-
* @returns Full file path to entity JSON file
|
|
1122
|
-
*/
|
|
1123
|
-
_getFilePath(entityId: EntityId): string;
|
|
1124
|
-
waitForInit(initial: boolean): Promise<void>;
|
|
1125
|
-
/**
|
|
1126
|
-
* Returns count of persisted entities.
|
|
1127
|
-
*
|
|
1128
|
-
* @returns Promise resolving to number of .json files in directory
|
|
1129
|
-
*/
|
|
1130
|
-
getCount(): Promise<number>;
|
|
1131
|
-
readValue<T extends IEntity = IEntity>(entityId: EntityId): Promise<T>;
|
|
1132
|
-
hasValue(entityId: EntityId): Promise<boolean>;
|
|
1133
|
-
writeValue<T extends IEntity = IEntity>(entityId: EntityId, entity: T): Promise<void>;
|
|
1134
|
-
/**
|
|
1135
|
-
* Removes entity from storage.
|
|
1136
|
-
*
|
|
1137
|
-
* @param entityId - Entity identifier to remove
|
|
1138
|
-
* @returns Promise that resolves when entity is deleted
|
|
1139
|
-
* @throws Error if entity not found or deletion fails
|
|
1140
|
-
*/
|
|
1141
|
-
removeValue(entityId: EntityId): Promise<void>;
|
|
1142
|
-
/**
|
|
1143
|
-
* Removes all entities from storage.
|
|
1144
|
-
*
|
|
1145
|
-
* @returns Promise that resolves when all entities are deleted
|
|
1146
|
-
* @throws Error if deletion fails
|
|
1147
|
-
*/
|
|
1148
|
-
removeAll(): Promise<void>;
|
|
1149
|
-
/**
|
|
1150
|
-
* Async generator yielding all entity values.
|
|
1151
|
-
* Sorted alphanumerically by entity ID.
|
|
1152
|
-
*
|
|
1153
|
-
* @returns AsyncGenerator yielding entities
|
|
1154
|
-
* @throws Error if reading fails
|
|
1155
|
-
*/
|
|
1156
|
-
values<T extends IEntity = IEntity>(): AsyncGenerator<T>;
|
|
1157
|
-
/**
|
|
1158
|
-
* Async generator yielding all entity IDs.
|
|
1159
|
-
* Sorted alphanumerically.
|
|
1160
|
-
*
|
|
1161
|
-
* @returns AsyncGenerator yielding entity IDs
|
|
1162
|
-
* @throws Error if reading fails
|
|
1163
|
-
*/
|
|
1164
|
-
keys(): AsyncGenerator<EntityId>;
|
|
1165
|
-
/**
|
|
1166
|
-
* Filters entities by predicate function.
|
|
1167
|
-
*
|
|
1168
|
-
* @param predicate - Filter function
|
|
1169
|
-
* @returns AsyncGenerator yielding filtered entities
|
|
1170
|
-
*/
|
|
1171
|
-
filter<T extends IEntity = IEntity>(predicate: (value: T) => boolean): AsyncGenerator<T>;
|
|
1172
|
-
/**
|
|
1173
|
-
* Takes first N entities, optionally filtered.
|
|
1174
|
-
*
|
|
1175
|
-
* @param total - Maximum number of entities to yield
|
|
1176
|
-
* @param predicate - Optional filter function
|
|
1177
|
-
* @returns AsyncGenerator yielding up to total entities
|
|
1178
|
-
*/
|
|
1179
|
-
take<T extends IEntity = IEntity>(total: number, predicate?: (value: T) => boolean): AsyncGenerator<T>;
|
|
1180
|
-
[BASE_WAIT_FOR_INIT_SYMBOL]: (() => Promise<void>) & functools_kit.ISingleshotClearable;
|
|
1181
|
-
/**
|
|
1182
|
-
* Async iterator implementation.
|
|
1183
|
-
* Delegates to values() generator.
|
|
1184
|
-
*
|
|
1185
|
-
* @returns AsyncIterableIterator yielding entities
|
|
1186
|
-
*/
|
|
1187
|
-
[Symbol.asyncIterator](): AsyncIterableIterator<any>;
|
|
1188
|
-
};
|
|
1189
|
-
};
|
|
1190
|
-
/**
|
|
1191
|
-
* Utility class for managing signal persistence.
|
|
1192
|
-
*
|
|
1193
|
-
* Features:
|
|
1194
|
-
* - Memoized storage instances per strategy
|
|
1195
|
-
* - Custom adapter support
|
|
1196
|
-
* - Atomic read/write operations
|
|
1197
|
-
* - Crash-safe signal state management
|
|
1198
|
-
*
|
|
1199
|
-
* Used by ClientStrategy for live mode persistence.
|
|
1200
|
-
*/
|
|
1201
|
-
declare class PersistSignalUtils {
|
|
1202
|
-
private PersistSignalFactory;
|
|
1203
|
-
private getSignalStorage;
|
|
1331
|
+
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
1204
1332
|
/**
|
|
1205
|
-
*
|
|
1333
|
+
* Clears accumulated signal data from storage.
|
|
1334
|
+
* If strategyName is provided, clears only that strategy's data.
|
|
1335
|
+
* If strategyName is omitted, clears all strategies' data.
|
|
1206
1336
|
*
|
|
1207
|
-
* @param
|
|
1337
|
+
* @param strategyName - Optional strategy name to clear specific strategy data
|
|
1208
1338
|
*
|
|
1209
1339
|
* @example
|
|
1210
1340
|
* ```typescript
|
|
1211
|
-
*
|
|
1212
|
-
* async readValue(id) { return JSON.parse(await redis.get(id)); }
|
|
1213
|
-
* async writeValue(id, entity) { await redis.set(id, JSON.stringify(entity)); }
|
|
1214
|
-
* }
|
|
1215
|
-
* PersistSignalAdaper.usePersistSignalAdapter(RedisPersist);
|
|
1216
|
-
* ```
|
|
1217
|
-
*/
|
|
1218
|
-
usePersistSignalAdapter(Ctor: TPersistBaseCtor<StrategyName, ISignalData>): void;
|
|
1219
|
-
/**
|
|
1220
|
-
* Reads persisted signal data for a strategy and symbol.
|
|
1341
|
+
* const service = new BacktestMarkdownService();
|
|
1221
1342
|
*
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1343
|
+
* // Clear specific strategy data
|
|
1344
|
+
* await service.clear("my-strategy");
|
|
1224
1345
|
*
|
|
1225
|
-
*
|
|
1226
|
-
*
|
|
1227
|
-
*
|
|
1346
|
+
* // Clear all strategies' data
|
|
1347
|
+
* await service.clear();
|
|
1348
|
+
* ```
|
|
1228
1349
|
*/
|
|
1229
|
-
|
|
1350
|
+
clear: (strategyName?: StrategyName) => Promise<void>;
|
|
1230
1351
|
/**
|
|
1231
|
-
*
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1234
|
-
* Uses atomic writes to prevent corruption on crashes.
|
|
1352
|
+
* Initializes the service by subscribing to backtest signal events.
|
|
1353
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
1354
|
+
* Automatically called on first use.
|
|
1235
1355
|
*
|
|
1236
|
-
* @
|
|
1237
|
-
*
|
|
1238
|
-
*
|
|
1239
|
-
*
|
|
1356
|
+
* @example
|
|
1357
|
+
* ```typescript
|
|
1358
|
+
* const service = new BacktestMarkdownService();
|
|
1359
|
+
* await service.init(); // Subscribe to backtest events
|
|
1360
|
+
* ```
|
|
1240
1361
|
*/
|
|
1241
|
-
|
|
1362
|
+
protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
|
|
1242
1363
|
}
|
|
1364
|
+
|
|
1243
1365
|
/**
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1366
|
+
* Unified tick event data for report generation.
|
|
1367
|
+
* Contains all information about a tick event regardless of action type.
|
|
1368
|
+
*/
|
|
1369
|
+
interface TickEvent {
|
|
1370
|
+
/** Event timestamp in milliseconds */
|
|
1371
|
+
timestamp: number;
|
|
1372
|
+
/** Event action type */
|
|
1373
|
+
action: "idle" | "opened" | "active" | "closed";
|
|
1374
|
+
/** Trading pair symbol (only for non-idle events) */
|
|
1375
|
+
symbol?: string;
|
|
1376
|
+
/** Signal ID (only for opened/active/closed) */
|
|
1377
|
+
signalId?: string;
|
|
1378
|
+
/** Position type (only for opened/active/closed) */
|
|
1379
|
+
position?: string;
|
|
1380
|
+
/** Signal note (only for opened/active/closed) */
|
|
1381
|
+
note?: string;
|
|
1382
|
+
/** Current price */
|
|
1383
|
+
currentPrice: number;
|
|
1384
|
+
/** Open price (only for opened/active/closed) */
|
|
1385
|
+
openPrice?: number;
|
|
1386
|
+
/** Take profit price (only for opened/active/closed) */
|
|
1387
|
+
takeProfit?: number;
|
|
1388
|
+
/** Stop loss price (only for opened/active/closed) */
|
|
1389
|
+
stopLoss?: number;
|
|
1390
|
+
/** PNL percentage (only for closed) */
|
|
1391
|
+
pnl?: number;
|
|
1392
|
+
/** Close reason (only for closed) */
|
|
1393
|
+
closeReason?: string;
|
|
1394
|
+
/** Duration in minutes (only for closed) */
|
|
1395
|
+
duration?: number;
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Statistical data calculated from live trading results.
|
|
1399
|
+
*
|
|
1400
|
+
* All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
|
|
1401
|
+
* Provides comprehensive metrics for live trading performance analysis.
|
|
1246
1402
|
*
|
|
1247
1403
|
* @example
|
|
1248
1404
|
* ```typescript
|
|
1249
|
-
*
|
|
1250
|
-
* PersistSignalAdaper.usePersistSignalAdapter(RedisPersist);
|
|
1405
|
+
* const stats = await Live.getData("my-strategy");
|
|
1251
1406
|
*
|
|
1252
|
-
*
|
|
1253
|
-
*
|
|
1407
|
+
* console.log(`Total events: ${stats.totalEvents}`);
|
|
1408
|
+
* console.log(`Closed signals: ${stats.totalClosed}`);
|
|
1409
|
+
* console.log(`Win rate: ${stats.winRate}%`);
|
|
1410
|
+
* console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
|
|
1254
1411
|
*
|
|
1255
|
-
* //
|
|
1256
|
-
*
|
|
1412
|
+
* // Access raw event data (includes idle, opened, active, closed)
|
|
1413
|
+
* stats.eventList.forEach(event => {
|
|
1414
|
+
* if (event.action === "closed") {
|
|
1415
|
+
* console.log(`Closed signal: ${event.pnl}%`);
|
|
1416
|
+
* }
|
|
1417
|
+
* });
|
|
1257
1418
|
* ```
|
|
1258
1419
|
*/
|
|
1259
|
-
|
|
1260
|
-
|
|
1420
|
+
interface LiveStatistics {
|
|
1421
|
+
/** Array of all events (idle, opened, active, closed) with full details */
|
|
1422
|
+
eventList: TickEvent[];
|
|
1423
|
+
/** Total number of all events (includes idle, opened, active, closed) */
|
|
1424
|
+
totalEvents: number;
|
|
1425
|
+
/** Total number of closed signals only */
|
|
1426
|
+
totalClosed: number;
|
|
1427
|
+
/** Number of winning closed signals (PNL > 0) */
|
|
1428
|
+
winCount: number;
|
|
1429
|
+
/** Number of losing closed signals (PNL < 0) */
|
|
1430
|
+
lossCount: number;
|
|
1431
|
+
/** Win rate as percentage (0-100) based on closed signals, null if unsafe. Higher is better. */
|
|
1432
|
+
winRate: number | null;
|
|
1433
|
+
/** Average PNL per closed signal as percentage, null if unsafe. Higher is better. */
|
|
1434
|
+
avgPnl: number | null;
|
|
1435
|
+
/** Cumulative PNL across all closed signals as percentage, null if unsafe. Higher is better. */
|
|
1436
|
+
totalPnl: number | null;
|
|
1437
|
+
/** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
|
|
1438
|
+
stdDev: number | null;
|
|
1439
|
+
/** Sharpe Ratio (risk-adjusted return = avgPnl / stdDev), null if unsafe. Higher is better. */
|
|
1440
|
+
sharpeRatio: number | null;
|
|
1441
|
+
/** Annualized Sharpe Ratio (sharpeRatio × √365), null if unsafe. Higher is better. */
|
|
1442
|
+
annualizedSharpeRatio: number | null;
|
|
1443
|
+
/** Certainty Ratio (avgWin / |avgLoss|), null if unsafe. Higher is better. */
|
|
1444
|
+
certaintyRatio: number | null;
|
|
1445
|
+
/** Expected yearly returns based on average trade duration and PNL, null if unsafe. Higher is better. */
|
|
1446
|
+
expectedYearlyReturns: number | null;
|
|
1447
|
+
}
|
|
1261
1448
|
/**
|
|
1262
|
-
*
|
|
1449
|
+
* Service for generating and saving live trading markdown reports.
|
|
1263
1450
|
*
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
1451
|
+
* Features:
|
|
1452
|
+
* - Listens to all signal events via onTick callback
|
|
1453
|
+
* - Accumulates all events (idle, opened, active, closed) per strategy
|
|
1454
|
+
* - Generates markdown tables with detailed event information
|
|
1455
|
+
* - Provides trading statistics (win rate, average PNL)
|
|
1456
|
+
* - Saves reports to disk in logs/live/{strategyName}.md
|
|
1266
1457
|
*
|
|
1267
1458
|
* @example
|
|
1268
1459
|
* ```typescript
|
|
1269
|
-
*
|
|
1460
|
+
* const service = new LiveMarkdownService();
|
|
1270
1461
|
*
|
|
1271
|
-
*
|
|
1462
|
+
* // Add to strategy callbacks
|
|
1463
|
+
* addStrategy({
|
|
1272
1464
|
* strategyName: "my-strategy",
|
|
1273
|
-
*
|
|
1274
|
-
*
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
-
*
|
|
1465
|
+
* callbacks: {
|
|
1466
|
+
* onTick: (symbol, result, backtest) => {
|
|
1467
|
+
* if (!backtest) {
|
|
1468
|
+
* service.tick(result);
|
|
1469
|
+
* }
|
|
1470
|
+
* }
|
|
1471
|
+
* }
|
|
1472
|
+
* });
|
|
1473
|
+
*
|
|
1474
|
+
* // Later: generate and save report
|
|
1475
|
+
* await service.dump("my-strategy");
|
|
1278
1476
|
* ```
|
|
1279
1477
|
*/
|
|
1280
|
-
declare class
|
|
1478
|
+
declare class LiveMarkdownService {
|
|
1479
|
+
/** Logger service for debug output */
|
|
1480
|
+
private readonly loggerService;
|
|
1281
1481
|
/**
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1285
|
-
* @param context - Execution context with strategy, exchange, and frame names
|
|
1286
|
-
* @returns Async generator yielding closed signals with PNL
|
|
1482
|
+
* Memoized function to get or create ReportStorage for a strategy.
|
|
1483
|
+
* Each strategy gets its own isolated storage instance.
|
|
1287
1484
|
*/
|
|
1288
|
-
|
|
1289
|
-
strategyName: string;
|
|
1290
|
-
exchangeName: string;
|
|
1291
|
-
frameName: string;
|
|
1292
|
-
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
1485
|
+
private getStorage;
|
|
1293
1486
|
/**
|
|
1294
|
-
*
|
|
1487
|
+
* Processes tick events and accumulates all event types.
|
|
1488
|
+
* Should be called from IStrategyCallbacks.onTick.
|
|
1295
1489
|
*
|
|
1296
|
-
*
|
|
1297
|
-
* Useful for running backtests for side effects only (callbacks, logging).
|
|
1490
|
+
* Processes all event types: idle, opened, active, closed.
|
|
1298
1491
|
*
|
|
1299
|
-
* @param
|
|
1300
|
-
* @param context - Execution context with strategy, exchange, and frame names
|
|
1301
|
-
* @returns Cancellation closure
|
|
1492
|
+
* @param data - Tick result from strategy execution
|
|
1302
1493
|
*
|
|
1303
1494
|
* @example
|
|
1304
1495
|
* ```typescript
|
|
1305
|
-
*
|
|
1306
|
-
*
|
|
1307
|
-
*
|
|
1308
|
-
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1311
|
-
*
|
|
1496
|
+
* const service = new LiveMarkdownService();
|
|
1497
|
+
*
|
|
1498
|
+
* callbacks: {
|
|
1499
|
+
* onTick: (symbol, result, backtest) => {
|
|
1500
|
+
* if (!backtest) {
|
|
1501
|
+
* service.tick(result);
|
|
1502
|
+
* }
|
|
1503
|
+
* }
|
|
1504
|
+
* }
|
|
1312
1505
|
* ```
|
|
1313
1506
|
*/
|
|
1314
|
-
|
|
1315
|
-
strategyName: string;
|
|
1316
|
-
exchangeName: string;
|
|
1317
|
-
frameName: string;
|
|
1318
|
-
}) => () => void;
|
|
1507
|
+
private tick;
|
|
1319
1508
|
/**
|
|
1320
|
-
*
|
|
1509
|
+
* Gets statistical data from all live trading events for a strategy.
|
|
1510
|
+
* Delegates to ReportStorage.getData().
|
|
1511
|
+
*
|
|
1512
|
+
* @param strategyName - Strategy name to get data for
|
|
1513
|
+
* @returns Statistical data object with all metrics
|
|
1514
|
+
*
|
|
1515
|
+
* @example
|
|
1516
|
+
* ```typescript
|
|
1517
|
+
* const service = new LiveMarkdownService();
|
|
1518
|
+
* const stats = await service.getData("my-strategy");
|
|
1519
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
1520
|
+
* ```
|
|
1521
|
+
*/
|
|
1522
|
+
getData: (strategyName: StrategyName) => Promise<LiveStatistics>;
|
|
1523
|
+
/**
|
|
1524
|
+
* Generates markdown report with all events for a strategy.
|
|
1525
|
+
* Delegates to ReportStorage.getReport().
|
|
1321
1526
|
*
|
|
1322
1527
|
* @param strategyName - Strategy name to generate report for
|
|
1323
|
-
* @returns
|
|
1528
|
+
* @returns Markdown formatted report string with table of all events
|
|
1324
1529
|
*
|
|
1325
1530
|
* @example
|
|
1326
1531
|
* ```typescript
|
|
1327
|
-
* const
|
|
1532
|
+
* const service = new LiveMarkdownService();
|
|
1533
|
+
* const markdown = await service.getReport("my-strategy");
|
|
1328
1534
|
* console.log(markdown);
|
|
1329
1535
|
* ```
|
|
1330
1536
|
*/
|
|
1331
1537
|
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
1332
1538
|
/**
|
|
1333
1539
|
* Saves strategy report to disk.
|
|
1540
|
+
* Creates directory if it doesn't exist.
|
|
1541
|
+
* Delegates to ReportStorage.dump().
|
|
1334
1542
|
*
|
|
1335
1543
|
* @param strategyName - Strategy name to save report for
|
|
1336
|
-
* @param path -
|
|
1544
|
+
* @param path - Directory path to save report (default: "./logs/live")
|
|
1337
1545
|
*
|
|
1338
1546
|
* @example
|
|
1339
1547
|
* ```typescript
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1548
|
+
* const service = new LiveMarkdownService();
|
|
1549
|
+
*
|
|
1550
|
+
* // Save to default path: ./logs/live/my-strategy.md
|
|
1551
|
+
* await service.dump("my-strategy");
|
|
1342
1552
|
*
|
|
1343
1553
|
* // Save to custom path: ./custom/path/my-strategy.md
|
|
1344
|
-
* await
|
|
1554
|
+
* await service.dump("my-strategy", "./custom/path");
|
|
1345
1555
|
* ```
|
|
1346
1556
|
*/
|
|
1347
1557
|
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
1348
|
-
}
|
|
1349
|
-
/**
|
|
1350
|
-
* Singleton instance of BacktestUtils for convenient backtest operations.
|
|
1351
|
-
*
|
|
1352
|
-
* @example
|
|
1353
|
-
* ```typescript
|
|
1354
|
-
* import { Backtest } from "./classes/Backtest";
|
|
1355
|
-
*
|
|
1356
|
-
* for await (const result of Backtest.run("BTCUSDT", {
|
|
1357
|
-
* strategyName: "my-strategy",
|
|
1358
|
-
* exchangeName: "my-exchange",
|
|
1359
|
-
* frameName: "1d-backtest"
|
|
1360
|
-
* })) {
|
|
1361
|
-
* if (result.action === "closed") {
|
|
1362
|
-
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
1363
|
-
* }
|
|
1364
|
-
* }
|
|
1365
|
-
* ```
|
|
1366
|
-
*/
|
|
1367
|
-
declare const Backtest: BacktestUtils;
|
|
1368
|
-
|
|
1369
|
-
/**
|
|
1370
|
-
* Utility class for live trading operations.
|
|
1371
|
-
*
|
|
1372
|
-
* Provides simplified access to liveGlobalService.run() with logging.
|
|
1373
|
-
* Exported as singleton instance for convenient usage.
|
|
1374
|
-
*
|
|
1375
|
-
* Features:
|
|
1376
|
-
* - Infinite async generator (never completes)
|
|
1377
|
-
* - Crash recovery via persisted state
|
|
1378
|
-
* - Real-time progression with Date.now()
|
|
1379
|
-
*
|
|
1380
|
-
* @example
|
|
1381
|
-
* ```typescript
|
|
1382
|
-
* import { Live } from "./classes/Live";
|
|
1383
|
-
*
|
|
1384
|
-
* // Infinite loop - use Ctrl+C to stop
|
|
1385
|
-
* for await (const result of Live.run("BTCUSDT", {
|
|
1386
|
-
* strategyName: "my-strategy",
|
|
1387
|
-
* exchangeName: "my-exchange",
|
|
1388
|
-
* frameName: ""
|
|
1389
|
-
* })) {
|
|
1390
|
-
* if (result.action === "opened") {
|
|
1391
|
-
* console.log("Signal opened:", result.signal);
|
|
1392
|
-
* } else if (result.action === "closed") {
|
|
1393
|
-
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
1394
|
-
* }
|
|
1395
|
-
* }
|
|
1396
|
-
* ```
|
|
1397
|
-
*/
|
|
1398
|
-
declare class LiveUtils {
|
|
1399
|
-
/**
|
|
1400
|
-
* Runs live trading for a symbol with context propagation.
|
|
1401
|
-
*
|
|
1402
|
-
* Infinite async generator with crash recovery support.
|
|
1403
|
-
* Process can crash and restart - state will be recovered from disk.
|
|
1404
|
-
*
|
|
1405
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1406
|
-
* @param context - Execution context with strategy and exchange names
|
|
1407
|
-
* @returns Infinite async generator yielding opened and closed signals
|
|
1408
|
-
*/
|
|
1409
|
-
run: (symbol: string, context: {
|
|
1410
|
-
strategyName: string;
|
|
1411
|
-
exchangeName: string;
|
|
1412
|
-
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
1413
1558
|
/**
|
|
1414
|
-
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1417
|
-
* Infinite loop - will run until process is stopped or crashes.
|
|
1418
|
-
* Useful for running live trading for side effects only (callbacks, persistence).
|
|
1559
|
+
* Clears accumulated event data from storage.
|
|
1560
|
+
* If strategyName is provided, clears only that strategy's data.
|
|
1561
|
+
* If strategyName is omitted, clears all strategies' data.
|
|
1419
1562
|
*
|
|
1420
|
-
* @param
|
|
1421
|
-
* @param context - Execution context with strategy and exchange names
|
|
1422
|
-
* @returns Cancellation closure
|
|
1563
|
+
* @param strategyName - Optional strategy name to clear specific strategy data
|
|
1423
1564
|
*
|
|
1424
1565
|
* @example
|
|
1425
1566
|
* ```typescript
|
|
1426
|
-
*
|
|
1427
|
-
* // This will run forever until Ctrl+C
|
|
1428
|
-
* await Live.background("BTCUSDT", {
|
|
1429
|
-
* strategyName: "my-strategy",
|
|
1430
|
-
* exchangeName: "my-exchange"
|
|
1431
|
-
* });
|
|
1432
|
-
* ```
|
|
1433
|
-
*/
|
|
1434
|
-
background: (symbol: string, context: {
|
|
1435
|
-
strategyName: string;
|
|
1436
|
-
exchangeName: string;
|
|
1437
|
-
}) => () => void;
|
|
1438
|
-
/**
|
|
1439
|
-
* Generates markdown report with all events for a strategy.
|
|
1567
|
+
* const service = new LiveMarkdownService();
|
|
1440
1568
|
*
|
|
1441
|
-
*
|
|
1442
|
-
*
|
|
1569
|
+
* // Clear specific strategy data
|
|
1570
|
+
* await service.clear("my-strategy");
|
|
1443
1571
|
*
|
|
1444
|
-
*
|
|
1445
|
-
*
|
|
1446
|
-
* const markdown = await Live.getReport("my-strategy");
|
|
1447
|
-
* console.log(markdown);
|
|
1572
|
+
* // Clear all strategies' data
|
|
1573
|
+
* await service.clear();
|
|
1448
1574
|
* ```
|
|
1449
1575
|
*/
|
|
1450
|
-
|
|
1576
|
+
clear: (strategyName?: StrategyName) => Promise<void>;
|
|
1451
1577
|
/**
|
|
1452
|
-
*
|
|
1453
|
-
*
|
|
1454
|
-
*
|
|
1455
|
-
* @param path - Optional directory path to save report (default: "./logs/live")
|
|
1578
|
+
* Initializes the service by subscribing to live signal events.
|
|
1579
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
1580
|
+
* Automatically called on first use.
|
|
1456
1581
|
*
|
|
1457
1582
|
* @example
|
|
1458
1583
|
* ```typescript
|
|
1459
|
-
*
|
|
1460
|
-
* await
|
|
1461
|
-
*
|
|
1462
|
-
* // Save to custom path: ./custom/path/my-strategy.md
|
|
1463
|
-
* await Live.dump("my-strategy", "./custom/path");
|
|
1584
|
+
* const service = new LiveMarkdownService();
|
|
1585
|
+
* await service.init(); // Subscribe to live events
|
|
1464
1586
|
* ```
|
|
1465
1587
|
*/
|
|
1466
|
-
|
|
1588
|
+
protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
|
|
1467
1589
|
}
|
|
1590
|
+
|
|
1591
|
+
declare const BASE_WAIT_FOR_INIT_SYMBOL: unique symbol;
|
|
1468
1592
|
/**
|
|
1469
|
-
*
|
|
1470
|
-
*
|
|
1471
|
-
* @example
|
|
1472
|
-
* ```typescript
|
|
1473
|
-
* import { Live } from "./classes/Live";
|
|
1474
|
-
*
|
|
1475
|
-
* for await (const result of Live.run("BTCUSDT", {
|
|
1476
|
-
* strategyName: "my-strategy",
|
|
1477
|
-
* exchangeName: "my-exchange",
|
|
1478
|
-
* })) {
|
|
1479
|
-
* console.log("Result:", result.action);
|
|
1480
|
-
* }
|
|
1481
|
-
* ```
|
|
1593
|
+
* Signal data stored in persistence layer.
|
|
1594
|
+
* Contains nullable signal for atomic updates.
|
|
1482
1595
|
*/
|
|
1483
|
-
|
|
1484
|
-
|
|
1596
|
+
interface ISignalData {
|
|
1597
|
+
/** Current signal state (null when no active signal) */
|
|
1598
|
+
signalRow: ISignalRow | null;
|
|
1599
|
+
}
|
|
1485
1600
|
/**
|
|
1486
|
-
*
|
|
1487
|
-
*
|
|
1488
|
-
* Features:
|
|
1489
|
-
* - Delegates to user-provided logger via setLogger()
|
|
1490
|
-
* - Automatically appends method context (strategyName, exchangeName, frameName)
|
|
1491
|
-
* - Automatically appends execution context (symbol, when, backtest)
|
|
1492
|
-
* - Defaults to NOOP_LOGGER if no logger configured
|
|
1493
|
-
*
|
|
1494
|
-
* Used throughout the framework for consistent logging with context.
|
|
1601
|
+
* Type helper for PersistBase instance.
|
|
1495
1602
|
*/
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
log: (topic: string, ...args: any[]) => Promise<void>;
|
|
1603
|
+
type TPersistBase = InstanceType<typeof PersistBase>;
|
|
1604
|
+
/**
|
|
1605
|
+
* Constructor type for PersistBase.
|
|
1606
|
+
* Used for custom persistence adapters.
|
|
1607
|
+
*/
|
|
1608
|
+
type TPersistBaseCtor<EntityName extends string = string, Entity extends IEntity = IEntity> = new (entityName: EntityName, baseDir: string) => IPersistBase<Entity>;
|
|
1609
|
+
/**
|
|
1610
|
+
* Entity identifier - string or number.
|
|
1611
|
+
*/
|
|
1612
|
+
type EntityId = string | number;
|
|
1613
|
+
/**
|
|
1614
|
+
* Base interface for persisted entities.
|
|
1615
|
+
*/
|
|
1616
|
+
interface IEntity {
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Persistence interface for CRUD operations.
|
|
1620
|
+
* Implemented by PersistBase.
|
|
1621
|
+
*/
|
|
1622
|
+
interface IPersistBase<Entity extends IEntity = IEntity> {
|
|
1517
1623
|
/**
|
|
1518
|
-
*
|
|
1624
|
+
* Initialize persistence directory and validate existing files.
|
|
1625
|
+
* Uses singleshot to ensure one-time execution.
|
|
1519
1626
|
*
|
|
1520
|
-
* @param
|
|
1521
|
-
* @
|
|
1627
|
+
* @param initial - Whether this is the first initialization
|
|
1628
|
+
* @returns Promise that resolves when initialization is complete
|
|
1522
1629
|
*/
|
|
1523
|
-
|
|
1630
|
+
waitForInit(initial: boolean): Promise<void>;
|
|
1524
1631
|
/**
|
|
1525
|
-
*
|
|
1632
|
+
* Read entity from persistence storage.
|
|
1526
1633
|
*
|
|
1527
|
-
* @param
|
|
1528
|
-
* @
|
|
1634
|
+
* @param entityId - Unique entity identifier
|
|
1635
|
+
* @returns Promise resolving to entity data
|
|
1636
|
+
* @throws Error if entity not found or read fails
|
|
1529
1637
|
*/
|
|
1530
|
-
|
|
1638
|
+
readValue(entityId: EntityId): Promise<Entity>;
|
|
1531
1639
|
/**
|
|
1532
|
-
*
|
|
1640
|
+
* Check if entity exists in storage.
|
|
1533
1641
|
*
|
|
1534
|
-
* @param
|
|
1535
|
-
* @
|
|
1642
|
+
* @param entityId - Unique entity identifier
|
|
1643
|
+
* @returns Promise resolving to true if exists, false otherwise
|
|
1536
1644
|
*/
|
|
1537
|
-
|
|
1645
|
+
hasValue(entityId: EntityId): Promise<boolean>;
|
|
1538
1646
|
/**
|
|
1539
|
-
*
|
|
1647
|
+
* Write entity to storage with atomic file writes.
|
|
1540
1648
|
*
|
|
1541
|
-
* @param
|
|
1649
|
+
* @param entityId - Unique entity identifier
|
|
1650
|
+
* @param entity - Entity data to persist
|
|
1651
|
+
* @returns Promise that resolves when write is complete
|
|
1652
|
+
* @throws Error if write fails
|
|
1542
1653
|
*/
|
|
1543
|
-
|
|
1654
|
+
writeValue(entityId: EntityId, entity: Entity): Promise<void>;
|
|
1544
1655
|
}
|
|
1545
|
-
|
|
1546
1656
|
/**
|
|
1547
|
-
*
|
|
1657
|
+
* Base class for file-based persistence with atomic writes.
|
|
1548
1658
|
*
|
|
1549
1659
|
* Features:
|
|
1550
|
-
* -
|
|
1551
|
-
* -
|
|
1552
|
-
* -
|
|
1553
|
-
* -
|
|
1554
|
-
*
|
|
1555
|
-
* All methods use prototype functions for memory efficiency.
|
|
1660
|
+
* - Atomic file writes using writeFileAtomic
|
|
1661
|
+
* - Auto-validation and cleanup of corrupted files
|
|
1662
|
+
* - Async generator support for iteration
|
|
1663
|
+
* - Retry logic for file deletion
|
|
1556
1664
|
*
|
|
1557
1665
|
* @example
|
|
1558
1666
|
* ```typescript
|
|
1559
|
-
* const
|
|
1560
|
-
*
|
|
1561
|
-
*
|
|
1562
|
-
*
|
|
1667
|
+
* const persist = new PersistBase("my-entity", "./data");
|
|
1668
|
+
* await persist.waitForInit(true);
|
|
1669
|
+
* await persist.writeValue("key1", { data: "value" });
|
|
1670
|
+
* const value = await persist.readValue("key1");
|
|
1671
|
+
* ```
|
|
1672
|
+
*/
|
|
1673
|
+
declare const PersistBase: {
|
|
1674
|
+
new <EntityName extends string = string>(entityName: EntityName, baseDir?: string): {
|
|
1675
|
+
/** Computed directory path for entity storage */
|
|
1676
|
+
_directory: string;
|
|
1677
|
+
readonly entityName: EntityName;
|
|
1678
|
+
readonly baseDir: string;
|
|
1679
|
+
/**
|
|
1680
|
+
* Computes file path for entity ID.
|
|
1681
|
+
*
|
|
1682
|
+
* @param entityId - Entity identifier
|
|
1683
|
+
* @returns Full file path to entity JSON file
|
|
1684
|
+
*/
|
|
1685
|
+
_getFilePath(entityId: EntityId): string;
|
|
1686
|
+
waitForInit(initial: boolean): Promise<void>;
|
|
1687
|
+
/**
|
|
1688
|
+
* Returns count of persisted entities.
|
|
1689
|
+
*
|
|
1690
|
+
* @returns Promise resolving to number of .json files in directory
|
|
1691
|
+
*/
|
|
1692
|
+
getCount(): Promise<number>;
|
|
1693
|
+
readValue<T extends IEntity = IEntity>(entityId: EntityId): Promise<T>;
|
|
1694
|
+
hasValue(entityId: EntityId): Promise<boolean>;
|
|
1695
|
+
writeValue<T extends IEntity = IEntity>(entityId: EntityId, entity: T): Promise<void>;
|
|
1696
|
+
/**
|
|
1697
|
+
* Removes entity from storage.
|
|
1698
|
+
*
|
|
1699
|
+
* @param entityId - Entity identifier to remove
|
|
1700
|
+
* @returns Promise that resolves when entity is deleted
|
|
1701
|
+
* @throws Error if entity not found or deletion fails
|
|
1702
|
+
*/
|
|
1703
|
+
removeValue(entityId: EntityId): Promise<void>;
|
|
1704
|
+
/**
|
|
1705
|
+
* Removes all entities from storage.
|
|
1706
|
+
*
|
|
1707
|
+
* @returns Promise that resolves when all entities are deleted
|
|
1708
|
+
* @throws Error if deletion fails
|
|
1709
|
+
*/
|
|
1710
|
+
removeAll(): Promise<void>;
|
|
1711
|
+
/**
|
|
1712
|
+
* Async generator yielding all entity values.
|
|
1713
|
+
* Sorted alphanumerically by entity ID.
|
|
1714
|
+
*
|
|
1715
|
+
* @returns AsyncGenerator yielding entities
|
|
1716
|
+
* @throws Error if reading fails
|
|
1717
|
+
*/
|
|
1718
|
+
values<T extends IEntity = IEntity>(): AsyncGenerator<T>;
|
|
1719
|
+
/**
|
|
1720
|
+
* Async generator yielding all entity IDs.
|
|
1721
|
+
* Sorted alphanumerically.
|
|
1722
|
+
*
|
|
1723
|
+
* @returns AsyncGenerator yielding entity IDs
|
|
1724
|
+
* @throws Error if reading fails
|
|
1725
|
+
*/
|
|
1726
|
+
keys(): AsyncGenerator<EntityId>;
|
|
1727
|
+
/**
|
|
1728
|
+
* Filters entities by predicate function.
|
|
1729
|
+
*
|
|
1730
|
+
* @param predicate - Filter function
|
|
1731
|
+
* @returns AsyncGenerator yielding filtered entities
|
|
1732
|
+
*/
|
|
1733
|
+
filter<T extends IEntity = IEntity>(predicate: (value: T) => boolean): AsyncGenerator<T>;
|
|
1734
|
+
/**
|
|
1735
|
+
* Takes first N entities, optionally filtered.
|
|
1736
|
+
*
|
|
1737
|
+
* @param total - Maximum number of entities to yield
|
|
1738
|
+
* @param predicate - Optional filter function
|
|
1739
|
+
* @returns AsyncGenerator yielding up to total entities
|
|
1740
|
+
*/
|
|
1741
|
+
take<T extends IEntity = IEntity>(total: number, predicate?: (value: T) => boolean): AsyncGenerator<T>;
|
|
1742
|
+
[BASE_WAIT_FOR_INIT_SYMBOL]: (() => Promise<void>) & functools_kit.ISingleshotClearable;
|
|
1743
|
+
/**
|
|
1744
|
+
* Async iterator implementation.
|
|
1745
|
+
* Delegates to values() generator.
|
|
1746
|
+
*
|
|
1747
|
+
* @returns AsyncIterableIterator yielding entities
|
|
1748
|
+
*/
|
|
1749
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<any>;
|
|
1750
|
+
};
|
|
1751
|
+
};
|
|
1752
|
+
/**
|
|
1753
|
+
* Utility class for managing signal persistence.
|
|
1754
|
+
*
|
|
1755
|
+
* Features:
|
|
1756
|
+
* - Memoized storage instances per strategy
|
|
1757
|
+
* - Custom adapter support
|
|
1758
|
+
* - Atomic read/write operations
|
|
1759
|
+
* - Crash-safe signal state management
|
|
1760
|
+
*
|
|
1761
|
+
* Used by ClientStrategy for live mode persistence.
|
|
1762
|
+
*/
|
|
1763
|
+
declare class PersistSignalUtils {
|
|
1764
|
+
private PersistSignalFactory;
|
|
1765
|
+
private getSignalStorage;
|
|
1766
|
+
/**
|
|
1767
|
+
* Registers a custom persistence adapter.
|
|
1768
|
+
*
|
|
1769
|
+
* @param Ctor - Custom PersistBase constructor
|
|
1770
|
+
*
|
|
1771
|
+
* @example
|
|
1772
|
+
* ```typescript
|
|
1773
|
+
* class RedisPersist extends PersistBase {
|
|
1774
|
+
* async readValue(id) { return JSON.parse(await redis.get(id)); }
|
|
1775
|
+
* async writeValue(id, entity) { await redis.set(id, JSON.stringify(entity)); }
|
|
1776
|
+
* }
|
|
1777
|
+
* PersistSignalAdaper.usePersistSignalAdapter(RedisPersist);
|
|
1778
|
+
* ```
|
|
1779
|
+
*/
|
|
1780
|
+
usePersistSignalAdapter(Ctor: TPersistBaseCtor<StrategyName, ISignalData>): void;
|
|
1781
|
+
/**
|
|
1782
|
+
* Reads persisted signal data for a strategy and symbol.
|
|
1783
|
+
*
|
|
1784
|
+
* Called by ClientStrategy.waitForInit() to restore state.
|
|
1785
|
+
* Returns null if no signal exists.
|
|
1786
|
+
*
|
|
1787
|
+
* @param strategyName - Strategy identifier
|
|
1788
|
+
* @param symbol - Trading pair symbol
|
|
1789
|
+
* @returns Promise resolving to signal or null
|
|
1790
|
+
*/
|
|
1791
|
+
readSignalData: (strategyName: StrategyName, symbol: string) => Promise<ISignalRow | null>;
|
|
1792
|
+
/**
|
|
1793
|
+
* Writes signal data to disk with atomic file writes.
|
|
1794
|
+
*
|
|
1795
|
+
* Called by ClientStrategy.setPendingSignal() to persist state.
|
|
1796
|
+
* Uses atomic writes to prevent corruption on crashes.
|
|
1797
|
+
*
|
|
1798
|
+
* @param signalRow - Signal data (null to clear)
|
|
1799
|
+
* @param strategyName - Strategy identifier
|
|
1800
|
+
* @param symbol - Trading pair symbol
|
|
1801
|
+
* @returns Promise that resolves when write is complete
|
|
1802
|
+
*/
|
|
1803
|
+
writeSignalData: (signalRow: ISignalRow | null, strategyName: StrategyName, symbol: string) => Promise<void>;
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Global singleton instance of PersistSignalUtils.
|
|
1807
|
+
* Used by ClientStrategy for signal persistence.
|
|
1808
|
+
*
|
|
1809
|
+
* @example
|
|
1810
|
+
* ```typescript
|
|
1811
|
+
* // Custom adapter
|
|
1812
|
+
* PersistSignalAdaper.usePersistSignalAdapter(RedisPersist);
|
|
1813
|
+
*
|
|
1814
|
+
* // Read signal
|
|
1815
|
+
* const signal = await PersistSignalAdaper.readSignalData("my-strategy", "BTCUSDT");
|
|
1816
|
+
*
|
|
1817
|
+
* // Write signal
|
|
1818
|
+
* await PersistSignalAdaper.writeSignalData(signal, "my-strategy", "BTCUSDT");
|
|
1819
|
+
* ```
|
|
1820
|
+
*/
|
|
1821
|
+
declare const PersistSignalAdaper: PersistSignalUtils;
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* Utility class for backtest operations.
|
|
1825
|
+
*
|
|
1826
|
+
* Provides simplified access to backtestGlobalService.run() with logging.
|
|
1827
|
+
* Exported as singleton instance for convenient usage.
|
|
1828
|
+
*
|
|
1829
|
+
* @example
|
|
1830
|
+
* ```typescript
|
|
1831
|
+
* import { Backtest } from "./classes/Backtest";
|
|
1832
|
+
*
|
|
1833
|
+
* for await (const result of Backtest.run("BTCUSDT", {
|
|
1834
|
+
* strategyName: "my-strategy",
|
|
1835
|
+
* exchangeName: "my-exchange",
|
|
1836
|
+
* frameName: "1d-backtest"
|
|
1837
|
+
* })) {
|
|
1838
|
+
* console.log("Closed signal PNL:", result.pnl.pnlPercentage);
|
|
1839
|
+
* }
|
|
1840
|
+
* ```
|
|
1841
|
+
*/
|
|
1842
|
+
declare class BacktestUtils {
|
|
1843
|
+
/**
|
|
1844
|
+
* Runs backtest for a symbol with context propagation.
|
|
1845
|
+
*
|
|
1846
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1847
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
1848
|
+
* @returns Async generator yielding closed signals with PNL
|
|
1849
|
+
*/
|
|
1850
|
+
run: (symbol: string, context: {
|
|
1851
|
+
strategyName: string;
|
|
1852
|
+
exchangeName: string;
|
|
1853
|
+
frameName: string;
|
|
1854
|
+
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
1855
|
+
/**
|
|
1856
|
+
* Runs backtest in background without yielding results.
|
|
1857
|
+
*
|
|
1858
|
+
* Consumes all backtest results internally without exposing them.
|
|
1859
|
+
* Useful for running backtests for side effects only (callbacks, logging).
|
|
1860
|
+
*
|
|
1861
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1862
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
1863
|
+
* @returns Cancellation closure
|
|
1864
|
+
*
|
|
1865
|
+
* @example
|
|
1866
|
+
* ```typescript
|
|
1867
|
+
* // Run backtest silently, only callbacks will fire
|
|
1868
|
+
* await Backtest.background("BTCUSDT", {
|
|
1869
|
+
* strategyName: "my-strategy",
|
|
1870
|
+
* exchangeName: "my-exchange",
|
|
1871
|
+
* frameName: "1d-backtest"
|
|
1872
|
+
* });
|
|
1873
|
+
* console.log("Backtest completed");
|
|
1874
|
+
* ```
|
|
1875
|
+
*/
|
|
1876
|
+
background: (symbol: string, context: {
|
|
1877
|
+
strategyName: string;
|
|
1878
|
+
exchangeName: string;
|
|
1879
|
+
frameName: string;
|
|
1880
|
+
}) => () => void;
|
|
1881
|
+
/**
|
|
1882
|
+
* Gets statistical data from all closed signals for a strategy.
|
|
1883
|
+
*
|
|
1884
|
+
* @param strategyName - Strategy name to get data for
|
|
1885
|
+
* @returns Promise resolving to statistical data object
|
|
1886
|
+
*
|
|
1887
|
+
* @example
|
|
1888
|
+
* ```typescript
|
|
1889
|
+
* const stats = await Backtest.getData("my-strategy");
|
|
1890
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
1891
|
+
* ```
|
|
1892
|
+
*/
|
|
1893
|
+
getData: (strategyName: StrategyName) => Promise<BacktestStatistics>;
|
|
1894
|
+
/**
|
|
1895
|
+
* Generates markdown report with all closed signals for a strategy.
|
|
1896
|
+
*
|
|
1897
|
+
* @param strategyName - Strategy name to generate report for
|
|
1898
|
+
* @returns Promise resolving to markdown formatted report string
|
|
1899
|
+
*
|
|
1900
|
+
* @example
|
|
1901
|
+
* ```typescript
|
|
1902
|
+
* const markdown = await Backtest.getReport("my-strategy");
|
|
1903
|
+
* console.log(markdown);
|
|
1904
|
+
* ```
|
|
1905
|
+
*/
|
|
1906
|
+
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
1907
|
+
/**
|
|
1908
|
+
* Saves strategy report to disk.
|
|
1909
|
+
*
|
|
1910
|
+
* @param strategyName - Strategy name to save report for
|
|
1911
|
+
* @param path - Optional directory path to save report (default: "./logs/backtest")
|
|
1912
|
+
*
|
|
1913
|
+
* @example
|
|
1914
|
+
* ```typescript
|
|
1915
|
+
* // Save to default path: ./logs/backtest/my-strategy.md
|
|
1916
|
+
* await Backtest.dump("my-strategy");
|
|
1917
|
+
*
|
|
1918
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
1919
|
+
* await Backtest.dump("my-strategy", "./custom/path");
|
|
1920
|
+
* ```
|
|
1921
|
+
*/
|
|
1922
|
+
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* Singleton instance of BacktestUtils for convenient backtest operations.
|
|
1926
|
+
*
|
|
1927
|
+
* @example
|
|
1928
|
+
* ```typescript
|
|
1929
|
+
* import { Backtest } from "./classes/Backtest";
|
|
1930
|
+
*
|
|
1931
|
+
* for await (const result of Backtest.run("BTCUSDT", {
|
|
1932
|
+
* strategyName: "my-strategy",
|
|
1933
|
+
* exchangeName: "my-exchange",
|
|
1934
|
+
* frameName: "1d-backtest"
|
|
1935
|
+
* })) {
|
|
1936
|
+
* if (result.action === "closed") {
|
|
1937
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
1938
|
+
* }
|
|
1939
|
+
* }
|
|
1940
|
+
* ```
|
|
1941
|
+
*/
|
|
1942
|
+
declare const Backtest: BacktestUtils;
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* Utility class for live trading operations.
|
|
1946
|
+
*
|
|
1947
|
+
* Provides simplified access to liveGlobalService.run() with logging.
|
|
1948
|
+
* Exported as singleton instance for convenient usage.
|
|
1949
|
+
*
|
|
1950
|
+
* Features:
|
|
1951
|
+
* - Infinite async generator (never completes)
|
|
1952
|
+
* - Crash recovery via persisted state
|
|
1953
|
+
* - Real-time progression with Date.now()
|
|
1954
|
+
*
|
|
1955
|
+
* @example
|
|
1956
|
+
* ```typescript
|
|
1957
|
+
* import { Live } from "./classes/Live";
|
|
1958
|
+
*
|
|
1959
|
+
* // Infinite loop - use Ctrl+C to stop
|
|
1960
|
+
* for await (const result of Live.run("BTCUSDT", {
|
|
1961
|
+
* strategyName: "my-strategy",
|
|
1962
|
+
* exchangeName: "my-exchange",
|
|
1963
|
+
* frameName: ""
|
|
1964
|
+
* })) {
|
|
1965
|
+
* if (result.action === "opened") {
|
|
1966
|
+
* console.log("Signal opened:", result.signal);
|
|
1967
|
+
* } else if (result.action === "closed") {
|
|
1968
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
1969
|
+
* }
|
|
1970
|
+
* }
|
|
1971
|
+
* ```
|
|
1972
|
+
*/
|
|
1973
|
+
declare class LiveUtils {
|
|
1974
|
+
/**
|
|
1975
|
+
* Runs live trading for a symbol with context propagation.
|
|
1976
|
+
*
|
|
1977
|
+
* Infinite async generator with crash recovery support.
|
|
1978
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
1979
|
+
*
|
|
1980
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1981
|
+
* @param context - Execution context with strategy and exchange names
|
|
1982
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
1983
|
+
*/
|
|
1984
|
+
run: (symbol: string, context: {
|
|
1985
|
+
strategyName: string;
|
|
1986
|
+
exchangeName: string;
|
|
1987
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
1988
|
+
/**
|
|
1989
|
+
* Runs live trading in background without yielding results.
|
|
1990
|
+
*
|
|
1991
|
+
* Consumes all live trading results internally without exposing them.
|
|
1992
|
+
* Infinite loop - will run until process is stopped or crashes.
|
|
1993
|
+
* Useful for running live trading for side effects only (callbacks, persistence).
|
|
1994
|
+
*
|
|
1995
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1996
|
+
* @param context - Execution context with strategy and exchange names
|
|
1997
|
+
* @returns Cancellation closure
|
|
1998
|
+
*
|
|
1999
|
+
* @example
|
|
2000
|
+
* ```typescript
|
|
2001
|
+
* // Run live trading silently in background, only callbacks will fire
|
|
2002
|
+
* // This will run forever until Ctrl+C
|
|
2003
|
+
* await Live.background("BTCUSDT", {
|
|
2004
|
+
* strategyName: "my-strategy",
|
|
2005
|
+
* exchangeName: "my-exchange"
|
|
2006
|
+
* });
|
|
2007
|
+
* ```
|
|
2008
|
+
*/
|
|
2009
|
+
background: (symbol: string, context: {
|
|
2010
|
+
strategyName: string;
|
|
2011
|
+
exchangeName: string;
|
|
2012
|
+
}) => () => void;
|
|
2013
|
+
/**
|
|
2014
|
+
* Gets statistical data from all live trading events for a strategy.
|
|
2015
|
+
*
|
|
2016
|
+
* @param strategyName - Strategy name to get data for
|
|
2017
|
+
* @returns Promise resolving to statistical data object
|
|
2018
|
+
*
|
|
2019
|
+
* @example
|
|
2020
|
+
* ```typescript
|
|
2021
|
+
* const stats = await Live.getData("my-strategy");
|
|
2022
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
2023
|
+
* ```
|
|
2024
|
+
*/
|
|
2025
|
+
getData: (strategyName: StrategyName) => Promise<LiveStatistics>;
|
|
2026
|
+
/**
|
|
2027
|
+
* Generates markdown report with all events for a strategy.
|
|
2028
|
+
*
|
|
2029
|
+
* @param strategyName - Strategy name to generate report for
|
|
2030
|
+
* @returns Promise resolving to markdown formatted report string
|
|
2031
|
+
*
|
|
2032
|
+
* @example
|
|
2033
|
+
* ```typescript
|
|
2034
|
+
* const markdown = await Live.getReport("my-strategy");
|
|
2035
|
+
* console.log(markdown);
|
|
2036
|
+
* ```
|
|
2037
|
+
*/
|
|
2038
|
+
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
2039
|
+
/**
|
|
2040
|
+
* Saves strategy report to disk.
|
|
2041
|
+
*
|
|
2042
|
+
* @param strategyName - Strategy name to save report for
|
|
2043
|
+
* @param path - Optional directory path to save report (default: "./logs/live")
|
|
2044
|
+
*
|
|
2045
|
+
* @example
|
|
2046
|
+
* ```typescript
|
|
2047
|
+
* // Save to default path: ./logs/live/my-strategy.md
|
|
2048
|
+
* await Live.dump("my-strategy");
|
|
2049
|
+
*
|
|
2050
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
2051
|
+
* await Live.dump("my-strategy", "./custom/path");
|
|
2052
|
+
* ```
|
|
2053
|
+
*/
|
|
2054
|
+
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
2055
|
+
}
|
|
2056
|
+
/**
|
|
2057
|
+
* Singleton instance of LiveUtils for convenient live trading operations.
|
|
2058
|
+
*
|
|
2059
|
+
* @example
|
|
2060
|
+
* ```typescript
|
|
2061
|
+
* import { Live } from "./classes/Live";
|
|
2062
|
+
*
|
|
2063
|
+
* for await (const result of Live.run("BTCUSDT", {
|
|
2064
|
+
* strategyName: "my-strategy",
|
|
2065
|
+
* exchangeName: "my-exchange",
|
|
2066
|
+
* })) {
|
|
2067
|
+
* console.log("Result:", result.action);
|
|
2068
|
+
* }
|
|
2069
|
+
* ```
|
|
2070
|
+
*/
|
|
2071
|
+
declare const Live: LiveUtils;
|
|
2072
|
+
|
|
2073
|
+
/**
|
|
2074
|
+
* Logger service with automatic context injection.
|
|
2075
|
+
*
|
|
2076
|
+
* Features:
|
|
2077
|
+
* - Delegates to user-provided logger via setLogger()
|
|
2078
|
+
* - Automatically appends method context (strategyName, exchangeName, frameName)
|
|
2079
|
+
* - Automatically appends execution context (symbol, when, backtest)
|
|
2080
|
+
* - Defaults to NOOP_LOGGER if no logger configured
|
|
2081
|
+
*
|
|
2082
|
+
* Used throughout the framework for consistent logging with context.
|
|
2083
|
+
*/
|
|
2084
|
+
declare class LoggerService implements ILogger {
|
|
2085
|
+
private readonly methodContextService;
|
|
2086
|
+
private readonly executionContextService;
|
|
2087
|
+
private _commonLogger;
|
|
2088
|
+
/**
|
|
2089
|
+
* Gets current method context if available.
|
|
2090
|
+
* Contains strategyName, exchangeName, frameName from MethodContextService.
|
|
2091
|
+
*/
|
|
2092
|
+
private get methodContext();
|
|
2093
|
+
/**
|
|
2094
|
+
* Gets current execution context if available.
|
|
2095
|
+
* Contains symbol, when, backtest from ExecutionContextService.
|
|
2096
|
+
*/
|
|
2097
|
+
private get executionContext();
|
|
2098
|
+
/**
|
|
2099
|
+
* Logs general-purpose message with automatic context injection.
|
|
2100
|
+
*
|
|
2101
|
+
* @param topic - Log topic/category
|
|
2102
|
+
* @param args - Additional log arguments
|
|
2103
|
+
*/
|
|
2104
|
+
log: (topic: string, ...args: any[]) => Promise<void>;
|
|
2105
|
+
/**
|
|
2106
|
+
* Logs debug-level message with automatic context injection.
|
|
2107
|
+
*
|
|
2108
|
+
* @param topic - Log topic/category
|
|
2109
|
+
* @param args - Additional log arguments
|
|
2110
|
+
*/
|
|
2111
|
+
debug: (topic: string, ...args: any[]) => Promise<void>;
|
|
2112
|
+
/**
|
|
2113
|
+
* Logs info-level message with automatic context injection.
|
|
2114
|
+
*
|
|
2115
|
+
* @param topic - Log topic/category
|
|
2116
|
+
* @param args - Additional log arguments
|
|
2117
|
+
*/
|
|
2118
|
+
info: (topic: string, ...args: any[]) => Promise<void>;
|
|
2119
|
+
/**
|
|
2120
|
+
* Logs warning-level message with automatic context injection.
|
|
2121
|
+
*
|
|
2122
|
+
* @param topic - Log topic/category
|
|
2123
|
+
* @param args - Additional log arguments
|
|
2124
|
+
*/
|
|
2125
|
+
warn: (topic: string, ...args: any[]) => Promise<void>;
|
|
2126
|
+
/**
|
|
2127
|
+
* Sets custom logger implementation.
|
|
2128
|
+
*
|
|
2129
|
+
* @param logger - Custom logger implementing ILogger interface
|
|
2130
|
+
*/
|
|
2131
|
+
setLogger: (logger: ILogger) => void;
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
/**
|
|
2135
|
+
* Client implementation for exchange data access.
|
|
2136
|
+
*
|
|
2137
|
+
* Features:
|
|
2138
|
+
* - Historical candle fetching (backwards from execution context)
|
|
2139
|
+
* - Future candle fetching (forwards for backtest)
|
|
2140
|
+
* - VWAP calculation from last 5 1m candles
|
|
2141
|
+
* - Price/quantity formatting for exchange
|
|
2142
|
+
*
|
|
2143
|
+
* All methods use prototype functions for memory efficiency.
|
|
2144
|
+
*
|
|
2145
|
+
* @example
|
|
2146
|
+
* ```typescript
|
|
2147
|
+
* const exchange = new ClientExchange({
|
|
2148
|
+
* exchangeName: "binance",
|
|
2149
|
+
* getCandles: async (symbol, interval, since, limit) => [...],
|
|
2150
|
+
* formatPrice: async (symbol, price) => price.toFixed(2),
|
|
1563
2151
|
* formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
|
|
1564
2152
|
* execution: executionService,
|
|
1565
2153
|
* logger: loggerService,
|
|
@@ -1903,714 +2491,464 @@ declare class ExchangeGlobalService {
|
|
|
1903
2491
|
* @param backtest - Whether running in backtest mode
|
|
1904
2492
|
* @returns Promise resolving to formatted price string
|
|
1905
2493
|
*/
|
|
1906
|
-
formatPrice: (symbol: string, price: number, when: Date, backtest: boolean) => Promise<string>;
|
|
1907
|
-
/**
|
|
1908
|
-
* Formats quantity with execution context.
|
|
1909
|
-
*
|
|
1910
|
-
* @param symbol - Trading pair symbol
|
|
1911
|
-
* @param quantity - Quantity to format
|
|
1912
|
-
* @param when - Timestamp for context
|
|
1913
|
-
* @param backtest - Whether running in backtest mode
|
|
1914
|
-
* @returns Promise resolving to formatted quantity string
|
|
1915
|
-
*/
|
|
1916
|
-
formatQuantity: (symbol: string, quantity: number, when: Date, backtest: boolean) => Promise<string>;
|
|
1917
|
-
}
|
|
1918
|
-
|
|
1919
|
-
/**
|
|
1920
|
-
* Global service for strategy operations with execution context injection.
|
|
1921
|
-
*
|
|
1922
|
-
* Wraps StrategyConnectionService with ExecutionContextService to inject
|
|
1923
|
-
* symbol, when, and backtest parameters into the execution context.
|
|
1924
|
-
*
|
|
1925
|
-
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
1926
|
-
*/
|
|
1927
|
-
declare class StrategyGlobalService {
|
|
1928
|
-
private readonly loggerService;
|
|
1929
|
-
private readonly strategyConnectionService;
|
|
1930
|
-
/**
|
|
1931
|
-
* Checks signal status at a specific timestamp.
|
|
1932
|
-
*
|
|
1933
|
-
* Wraps strategy tick() with execution context containing symbol, timestamp,
|
|
1934
|
-
* and backtest mode flag.
|
|
1935
|
-
*
|
|
1936
|
-
* @param symbol - Trading pair symbol
|
|
1937
|
-
* @param when - Timestamp for tick evaluation
|
|
1938
|
-
* @param backtest - Whether running in backtest mode
|
|
1939
|
-
* @returns Discriminated union of tick result (idle, opened, active, closed)
|
|
1940
|
-
*/
|
|
1941
|
-
tick: (symbol: string, when: Date, backtest: boolean) => Promise<IStrategyTickResult>;
|
|
1942
|
-
/**
|
|
1943
|
-
* Runs fast backtest against candle array.
|
|
1944
|
-
*
|
|
1945
|
-
* Wraps strategy backtest() with execution context containing symbol,
|
|
1946
|
-
* timestamp, and backtest mode flag.
|
|
1947
|
-
*
|
|
1948
|
-
* @param symbol - Trading pair symbol
|
|
1949
|
-
* @param candles - Array of historical candles to test against
|
|
1950
|
-
* @param when - Starting timestamp for backtest
|
|
1951
|
-
* @param backtest - Whether running in backtest mode (typically true)
|
|
1952
|
-
* @returns Closed signal result with PNL
|
|
1953
|
-
*/
|
|
1954
|
-
backtest: (symbol: string, candles: ICandleData[], when: Date, backtest: boolean) => Promise<IStrategyBacktestResult>;
|
|
1955
|
-
/**
|
|
1956
|
-
* Stops the strategy from generating new signals.
|
|
1957
|
-
*
|
|
1958
|
-
* Delegates to StrategyConnectionService.stop() to set internal flag.
|
|
1959
|
-
* Does not require execution context.
|
|
1960
|
-
*
|
|
1961
|
-
* @param strategyName - Name of strategy to stop
|
|
1962
|
-
* @returns Promise that resolves when stop flag is set
|
|
1963
|
-
*/
|
|
1964
|
-
stop: (strategyName: StrategyName) => Promise<void>;
|
|
1965
|
-
/**
|
|
1966
|
-
* Clears the memoized ClientStrategy instance from cache.
|
|
1967
|
-
*
|
|
1968
|
-
* Delegates to StrategyConnectionService.clear() to remove strategy from cache.
|
|
1969
|
-
* Forces re-initialization of strategy on next operation.
|
|
1970
|
-
*
|
|
1971
|
-
* @param strategyName - Name of strategy to clear from cache
|
|
1972
|
-
*/
|
|
1973
|
-
clear: (strategyName: StrategyName) => Promise<void>;
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
/**
|
|
1977
|
-
* Global service for frame operations.
|
|
1978
|
-
*
|
|
1979
|
-
* Wraps FrameConnectionService for timeframe generation.
|
|
1980
|
-
* Used internally by BacktestLogicPrivateService.
|
|
1981
|
-
*/
|
|
1982
|
-
declare class FrameGlobalService {
|
|
1983
|
-
private readonly loggerService;
|
|
1984
|
-
private readonly frameConnectionService;
|
|
1985
|
-
/**
|
|
1986
|
-
* Generates timeframe array for backtest iteration.
|
|
1987
|
-
*
|
|
1988
|
-
* @param symbol - Trading pair symbol
|
|
1989
|
-
* @returns Promise resolving to array of Date objects
|
|
1990
|
-
*/
|
|
1991
|
-
getTimeframe: (symbol: string) => Promise<Date[]>;
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
/**
|
|
1995
|
-
* Service for managing exchange schema registry.
|
|
1996
|
-
*
|
|
1997
|
-
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
1998
|
-
* Exchanges are registered via addExchange() and retrieved by name.
|
|
1999
|
-
*/
|
|
2000
|
-
declare class ExchangeSchemaService {
|
|
2001
|
-
readonly loggerService: LoggerService;
|
|
2002
|
-
private _registry;
|
|
2003
|
-
/**
|
|
2004
|
-
* Registers a new exchange schema.
|
|
2005
|
-
*
|
|
2006
|
-
* @param key - Unique exchange name
|
|
2007
|
-
* @param value - Exchange schema configuration
|
|
2008
|
-
* @throws Error if exchange name already exists
|
|
2009
|
-
*/
|
|
2010
|
-
register: (key: ExchangeName, value: IExchangeSchema) => void;
|
|
2011
|
-
/**
|
|
2012
|
-
* Validates exchange schema structure for required properties.
|
|
2013
|
-
*
|
|
2014
|
-
* Performs shallow validation to ensure all required properties exist
|
|
2015
|
-
* and have correct types before registration in the registry.
|
|
2016
|
-
*
|
|
2017
|
-
* @param exchangeSchema - Exchange schema to validate
|
|
2018
|
-
* @throws Error if exchangeName is missing or not a string
|
|
2019
|
-
* @throws Error if getCandles is missing or not a function
|
|
2020
|
-
* @throws Error if formatPrice is missing or not a function
|
|
2021
|
-
* @throws Error if formatQuantity is missing or not a function
|
|
2022
|
-
*/
|
|
2023
|
-
private validateShallow;
|
|
2024
|
-
/**
|
|
2025
|
-
* Overrides an existing exchange schema with partial updates.
|
|
2026
|
-
*
|
|
2027
|
-
* @param key - Exchange name to override
|
|
2028
|
-
* @param value - Partial schema updates
|
|
2029
|
-
* @returns Updated exchange schema
|
|
2030
|
-
* @throws Error if exchange name doesn't exist
|
|
2031
|
-
*/
|
|
2032
|
-
override: (key: ExchangeName, value: Partial<IExchangeSchema>) => IExchangeSchema;
|
|
2033
|
-
/**
|
|
2034
|
-
* Retrieves an exchange schema by name.
|
|
2035
|
-
*
|
|
2036
|
-
* @param key - Exchange name
|
|
2037
|
-
* @returns Exchange schema configuration
|
|
2038
|
-
* @throws Error if exchange name doesn't exist
|
|
2039
|
-
*/
|
|
2040
|
-
get: (key: ExchangeName) => IExchangeSchema;
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
/**
|
|
2044
|
-
* Service for managing strategy schema registry.
|
|
2045
|
-
*
|
|
2046
|
-
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
2047
|
-
* Strategies are registered via addStrategy() and retrieved by name.
|
|
2048
|
-
*/
|
|
2049
|
-
declare class StrategySchemaService {
|
|
2050
|
-
readonly loggerService: LoggerService;
|
|
2051
|
-
private _registry;
|
|
2052
|
-
/**
|
|
2053
|
-
* Registers a new strategy schema.
|
|
2054
|
-
*
|
|
2055
|
-
* @param key - Unique strategy name
|
|
2056
|
-
* @param value - Strategy schema configuration
|
|
2057
|
-
* @throws Error if strategy name already exists
|
|
2058
|
-
*/
|
|
2059
|
-
register: (key: StrategyName, value: IStrategySchema) => void;
|
|
2060
|
-
/**
|
|
2061
|
-
* Validates strategy schema structure for required properties.
|
|
2062
|
-
*
|
|
2063
|
-
* Performs shallow validation to ensure all required properties exist
|
|
2064
|
-
* and have correct types before registration in the registry.
|
|
2065
|
-
*
|
|
2066
|
-
* @param strategySchema - Strategy schema to validate
|
|
2067
|
-
* @throws Error if strategyName is missing or not a string
|
|
2068
|
-
* @throws Error if interval is missing or not a valid SignalInterval
|
|
2069
|
-
* @throws Error if getSignal is missing or not a function
|
|
2070
|
-
*/
|
|
2071
|
-
private validateShallow;
|
|
2072
|
-
/**
|
|
2073
|
-
* Overrides an existing strategy schema with partial updates.
|
|
2074
|
-
*
|
|
2075
|
-
* @param key - Strategy name to override
|
|
2076
|
-
* @param value - Partial schema updates
|
|
2077
|
-
* @returns Updated strategy schema
|
|
2078
|
-
* @throws Error if strategy name doesn't exist
|
|
2079
|
-
*/
|
|
2080
|
-
override: (key: StrategyName, value: Partial<IStrategySchema>) => IStrategySchema;
|
|
2494
|
+
formatPrice: (symbol: string, price: number, when: Date, backtest: boolean) => Promise<string>;
|
|
2081
2495
|
/**
|
|
2082
|
-
*
|
|
2496
|
+
* Formats quantity with execution context.
|
|
2083
2497
|
*
|
|
2084
|
-
* @param
|
|
2085
|
-
* @
|
|
2086
|
-
* @
|
|
2498
|
+
* @param symbol - Trading pair symbol
|
|
2499
|
+
* @param quantity - Quantity to format
|
|
2500
|
+
* @param when - Timestamp for context
|
|
2501
|
+
* @param backtest - Whether running in backtest mode
|
|
2502
|
+
* @returns Promise resolving to formatted quantity string
|
|
2087
2503
|
*/
|
|
2088
|
-
|
|
2504
|
+
formatQuantity: (symbol: string, quantity: number, when: Date, backtest: boolean) => Promise<string>;
|
|
2089
2505
|
}
|
|
2090
2506
|
|
|
2091
2507
|
/**
|
|
2092
|
-
*
|
|
2508
|
+
* Global service for strategy operations with execution context injection.
|
|
2093
2509
|
*
|
|
2094
|
-
*
|
|
2095
|
-
*
|
|
2510
|
+
* Wraps StrategyConnectionService with ExecutionContextService to inject
|
|
2511
|
+
* symbol, when, and backtest parameters into the execution context.
|
|
2512
|
+
*
|
|
2513
|
+
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
2096
2514
|
*/
|
|
2097
|
-
declare class
|
|
2098
|
-
readonly loggerService
|
|
2099
|
-
private
|
|
2515
|
+
declare class StrategyGlobalService {
|
|
2516
|
+
private readonly loggerService;
|
|
2517
|
+
private readonly strategyConnectionService;
|
|
2100
2518
|
/**
|
|
2101
|
-
*
|
|
2519
|
+
* Checks signal status at a specific timestamp.
|
|
2102
2520
|
*
|
|
2103
|
-
*
|
|
2104
|
-
*
|
|
2105
|
-
*
|
|
2521
|
+
* Wraps strategy tick() with execution context containing symbol, timestamp,
|
|
2522
|
+
* and backtest mode flag.
|
|
2523
|
+
*
|
|
2524
|
+
* @param symbol - Trading pair symbol
|
|
2525
|
+
* @param when - Timestamp for tick evaluation
|
|
2526
|
+
* @param backtest - Whether running in backtest mode
|
|
2527
|
+
* @returns Discriminated union of tick result (idle, opened, active, closed)
|
|
2106
2528
|
*/
|
|
2107
|
-
|
|
2529
|
+
tick: (symbol: string, when: Date, backtest: boolean) => Promise<IStrategyTickResult>;
|
|
2108
2530
|
/**
|
|
2109
|
-
*
|
|
2531
|
+
* Runs fast backtest against candle array.
|
|
2110
2532
|
*
|
|
2111
|
-
*
|
|
2112
|
-
* and
|
|
2533
|
+
* Wraps strategy backtest() with execution context containing symbol,
|
|
2534
|
+
* timestamp, and backtest mode flag.
|
|
2113
2535
|
*
|
|
2114
|
-
* @param
|
|
2115
|
-
* @
|
|
2116
|
-
* @
|
|
2117
|
-
* @
|
|
2118
|
-
* @
|
|
2536
|
+
* @param symbol - Trading pair symbol
|
|
2537
|
+
* @param candles - Array of historical candles to test against
|
|
2538
|
+
* @param when - Starting timestamp for backtest
|
|
2539
|
+
* @param backtest - Whether running in backtest mode (typically true)
|
|
2540
|
+
* @returns Closed signal result with PNL
|
|
2119
2541
|
*/
|
|
2120
|
-
|
|
2542
|
+
backtest: (symbol: string, candles: ICandleData[], when: Date, backtest: boolean) => Promise<IStrategyBacktestResult>;
|
|
2121
2543
|
/**
|
|
2122
|
-
*
|
|
2544
|
+
* Stops the strategy from generating new signals.
|
|
2123
2545
|
*
|
|
2124
|
-
*
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
2546
|
+
* Delegates to StrategyConnectionService.stop() to set internal flag.
|
|
2547
|
+
* Does not require execution context.
|
|
2548
|
+
*
|
|
2549
|
+
* @param strategyName - Name of strategy to stop
|
|
2550
|
+
* @returns Promise that resolves when stop flag is set
|
|
2127
2551
|
*/
|
|
2128
|
-
|
|
2552
|
+
stop: (strategyName: StrategyName) => Promise<void>;
|
|
2129
2553
|
/**
|
|
2130
|
-
*
|
|
2554
|
+
* Clears the memoized ClientStrategy instance from cache.
|
|
2131
2555
|
*
|
|
2132
|
-
*
|
|
2133
|
-
*
|
|
2134
|
-
*
|
|
2556
|
+
* Delegates to StrategyConnectionService.clear() to remove strategy from cache.
|
|
2557
|
+
* Forces re-initialization of strategy on next operation.
|
|
2558
|
+
*
|
|
2559
|
+
* @param strategyName - Name of strategy to clear from cache
|
|
2135
2560
|
*/
|
|
2136
|
-
|
|
2561
|
+
clear: (strategyName: StrategyName) => Promise<void>;
|
|
2137
2562
|
}
|
|
2138
2563
|
|
|
2139
2564
|
/**
|
|
2140
|
-
*
|
|
2141
|
-
*
|
|
2142
|
-
* Flow:
|
|
2143
|
-
* 1. Get timeframes from frame service
|
|
2144
|
-
* 2. Iterate through timeframes calling tick()
|
|
2145
|
-
* 3. When signal opens: fetch candles and call backtest()
|
|
2146
|
-
* 4. Skip timeframes until signal closes
|
|
2147
|
-
* 5. Yield closed result and continue
|
|
2565
|
+
* Global service for frame operations.
|
|
2148
2566
|
*
|
|
2149
|
-
*
|
|
2150
|
-
*
|
|
2567
|
+
* Wraps FrameConnectionService for timeframe generation.
|
|
2568
|
+
* Used internally by BacktestLogicPrivateService.
|
|
2151
2569
|
*/
|
|
2152
|
-
declare class
|
|
2570
|
+
declare class FrameGlobalService {
|
|
2153
2571
|
private readonly loggerService;
|
|
2154
|
-
private readonly
|
|
2155
|
-
private readonly exchangeGlobalService;
|
|
2156
|
-
private readonly frameGlobalService;
|
|
2572
|
+
private readonly frameConnectionService;
|
|
2157
2573
|
/**
|
|
2158
|
-
*
|
|
2159
|
-
*
|
|
2160
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2161
|
-
* @yields Closed signal results with PNL
|
|
2574
|
+
* Generates timeframe array for backtest iteration.
|
|
2162
2575
|
*
|
|
2163
|
-
* @
|
|
2164
|
-
*
|
|
2165
|
-
* for await (const result of backtestLogic.run("BTCUSDT")) {
|
|
2166
|
-
* console.log(result.closeReason, result.pnl.pnlPercentage);
|
|
2167
|
-
* if (result.pnl.pnlPercentage < -10) break; // Early termination
|
|
2168
|
-
* }
|
|
2169
|
-
* ```
|
|
2576
|
+
* @param symbol - Trading pair symbol
|
|
2577
|
+
* @returns Promise resolving to array of Date objects
|
|
2170
2578
|
*/
|
|
2171
|
-
|
|
2579
|
+
getTimeframe: (symbol: string) => Promise<Date[]>;
|
|
2172
2580
|
}
|
|
2173
2581
|
|
|
2174
2582
|
/**
|
|
2175
|
-
*
|
|
2176
|
-
*
|
|
2177
|
-
* Flow:
|
|
2178
|
-
* 1. Infinite while(true) loop for continuous monitoring
|
|
2179
|
-
* 2. Create real-time date with new Date()
|
|
2180
|
-
* 3. Call tick() to check signal status
|
|
2181
|
-
* 4. Yield opened/closed results (skip idle/active)
|
|
2182
|
-
* 5. Sleep for TICK_TTL between iterations
|
|
2583
|
+
* Service for managing exchange schema registry.
|
|
2183
2584
|
*
|
|
2184
|
-
*
|
|
2185
|
-
*
|
|
2186
|
-
* - Real-time progression with new Date()
|
|
2187
|
-
* - Memory efficient streaming
|
|
2188
|
-
* - Never completes (infinite generator)
|
|
2585
|
+
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
2586
|
+
* Exchanges are registered via addExchange() and retrieved by name.
|
|
2189
2587
|
*/
|
|
2190
|
-
declare class
|
|
2191
|
-
|
|
2192
|
-
private
|
|
2588
|
+
declare class ExchangeSchemaService {
|
|
2589
|
+
readonly loggerService: LoggerService;
|
|
2590
|
+
private _registry;
|
|
2193
2591
|
/**
|
|
2194
|
-
*
|
|
2592
|
+
* Registers a new exchange schema.
|
|
2195
2593
|
*
|
|
2196
|
-
*
|
|
2197
|
-
*
|
|
2594
|
+
* @param key - Unique exchange name
|
|
2595
|
+
* @param value - Exchange schema configuration
|
|
2596
|
+
* @throws Error if exchange name already exists
|
|
2597
|
+
*/
|
|
2598
|
+
register: (key: ExchangeName, value: IExchangeSchema) => void;
|
|
2599
|
+
/**
|
|
2600
|
+
* Validates exchange schema structure for required properties.
|
|
2198
2601
|
*
|
|
2199
|
-
*
|
|
2200
|
-
*
|
|
2602
|
+
* Performs shallow validation to ensure all required properties exist
|
|
2603
|
+
* and have correct types before registration in the registry.
|
|
2201
2604
|
*
|
|
2202
|
-
* @
|
|
2203
|
-
*
|
|
2204
|
-
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
* }
|
|
2208
|
-
* if (result.action === "closed") {
|
|
2209
|
-
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
2210
|
-
* }
|
|
2211
|
-
* // Infinite loop - will never complete
|
|
2212
|
-
* }
|
|
2213
|
-
* ```
|
|
2605
|
+
* @param exchangeSchema - Exchange schema to validate
|
|
2606
|
+
* @throws Error if exchangeName is missing or not a string
|
|
2607
|
+
* @throws Error if getCandles is missing or not a function
|
|
2608
|
+
* @throws Error if formatPrice is missing or not a function
|
|
2609
|
+
* @throws Error if formatQuantity is missing or not a function
|
|
2214
2610
|
*/
|
|
2215
|
-
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
/**
|
|
2219
|
-
* Public service for backtest orchestration with context management.
|
|
2220
|
-
*
|
|
2221
|
-
* Wraps BacktestLogicPrivateService with MethodContextService to provide
|
|
2222
|
-
* implicit context propagation for strategyName, exchangeName, and frameName.
|
|
2223
|
-
*
|
|
2224
|
-
* This allows getCandles(), getSignal(), and other functions to work without
|
|
2225
|
-
* explicit context parameters.
|
|
2226
|
-
*
|
|
2227
|
-
* @example
|
|
2228
|
-
* ```typescript
|
|
2229
|
-
* const backtestLogicPublicService = inject(TYPES.backtestLogicPublicService);
|
|
2230
|
-
*
|
|
2231
|
-
* for await (const result of backtestLogicPublicService.run("BTCUSDT", {
|
|
2232
|
-
* strategyName: "my-strategy",
|
|
2233
|
-
* exchangeName: "my-exchange",
|
|
2234
|
-
* frameName: "1d-backtest",
|
|
2235
|
-
* })) {
|
|
2236
|
-
* if (result.action === "closed") {
|
|
2237
|
-
* console.log("PNL:", result.pnl.profit);
|
|
2238
|
-
* }
|
|
2239
|
-
* }
|
|
2240
|
-
* ```
|
|
2241
|
-
*/
|
|
2242
|
-
declare class BacktestLogicPublicService {
|
|
2243
|
-
private readonly loggerService;
|
|
2244
|
-
private readonly backtestLogicPrivateService;
|
|
2611
|
+
private validateShallow;
|
|
2245
2612
|
/**
|
|
2246
|
-
*
|
|
2247
|
-
*
|
|
2248
|
-
* Streams closed signals as async generator. Context is automatically
|
|
2249
|
-
* injected into all framework functions called during iteration.
|
|
2613
|
+
* Overrides an existing exchange schema with partial updates.
|
|
2250
2614
|
*
|
|
2251
|
-
* @param
|
|
2252
|
-
* @param
|
|
2253
|
-
* @returns
|
|
2615
|
+
* @param key - Exchange name to override
|
|
2616
|
+
* @param value - Partial schema updates
|
|
2617
|
+
* @returns Updated exchange schema
|
|
2618
|
+
* @throws Error if exchange name doesn't exist
|
|
2254
2619
|
*/
|
|
2255
|
-
|
|
2256
|
-
strategyName: string;
|
|
2257
|
-
exchangeName: string;
|
|
2258
|
-
frameName: string;
|
|
2259
|
-
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
2260
|
-
}
|
|
2261
|
-
|
|
2262
|
-
/**
|
|
2263
|
-
* Public service for live trading orchestration with context management.
|
|
2264
|
-
*
|
|
2265
|
-
* Wraps LiveLogicPrivateService with MethodContextService to provide
|
|
2266
|
-
* implicit context propagation for strategyName and exchangeName.
|
|
2267
|
-
*
|
|
2268
|
-
* This allows getCandles(), getSignal(), and other functions to work without
|
|
2269
|
-
* explicit context parameters.
|
|
2270
|
-
*
|
|
2271
|
-
* Features:
|
|
2272
|
-
* - Infinite async generator (never completes)
|
|
2273
|
-
* - Crash recovery via persisted state
|
|
2274
|
-
* - Real-time progression with Date.now()
|
|
2275
|
-
*
|
|
2276
|
-
* @example
|
|
2277
|
-
* ```typescript
|
|
2278
|
-
* const liveLogicPublicService = inject(TYPES.liveLogicPublicService);
|
|
2279
|
-
*
|
|
2280
|
-
* // Infinite loop - use Ctrl+C to stop
|
|
2281
|
-
* for await (const result of liveLogicPublicService.run("BTCUSDT", {
|
|
2282
|
-
* strategyName: "my-strategy",
|
|
2283
|
-
* exchangeName: "my-exchange",
|
|
2284
|
-
* })) {
|
|
2285
|
-
* if (result.action === "opened") {
|
|
2286
|
-
* console.log("Signal opened:", result.signal);
|
|
2287
|
-
* } else if (result.action === "closed") {
|
|
2288
|
-
* console.log("PNL:", result.pnl.profit);
|
|
2289
|
-
* }
|
|
2290
|
-
* }
|
|
2291
|
-
* ```
|
|
2292
|
-
*/
|
|
2293
|
-
declare class LiveLogicPublicService {
|
|
2294
|
-
private readonly loggerService;
|
|
2295
|
-
private readonly liveLogicPrivateService;
|
|
2620
|
+
override: (key: ExchangeName, value: Partial<IExchangeSchema>) => IExchangeSchema;
|
|
2296
2621
|
/**
|
|
2297
|
-
*
|
|
2298
|
-
*
|
|
2299
|
-
* Streams opened and closed signals as infinite async generator.
|
|
2300
|
-
* Context is automatically injected into all framework functions.
|
|
2301
|
-
* Process can crash and restart - state will be recovered from disk.
|
|
2622
|
+
* Retrieves an exchange schema by name.
|
|
2302
2623
|
*
|
|
2303
|
-
* @param
|
|
2304
|
-
* @
|
|
2305
|
-
* @
|
|
2624
|
+
* @param key - Exchange name
|
|
2625
|
+
* @returns Exchange schema configuration
|
|
2626
|
+
* @throws Error if exchange name doesn't exist
|
|
2306
2627
|
*/
|
|
2307
|
-
|
|
2308
|
-
strategyName: string;
|
|
2309
|
-
exchangeName: string;
|
|
2310
|
-
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2628
|
+
get: (key: ExchangeName) => IExchangeSchema;
|
|
2311
2629
|
}
|
|
2312
2630
|
|
|
2313
2631
|
/**
|
|
2314
|
-
*
|
|
2632
|
+
* Service for managing strategy schema registry.
|
|
2315
2633
|
*
|
|
2316
|
-
*
|
|
2317
|
-
*
|
|
2634
|
+
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
2635
|
+
* Strategies are registered via addStrategy() and retrieved by name.
|
|
2318
2636
|
*/
|
|
2319
|
-
declare class
|
|
2320
|
-
|
|
2321
|
-
private
|
|
2322
|
-
private readonly strategyValidationService;
|
|
2323
|
-
private readonly exchangeValidationService;
|
|
2637
|
+
declare class StrategySchemaService {
|
|
2638
|
+
readonly loggerService: LoggerService;
|
|
2639
|
+
private _registry;
|
|
2324
2640
|
/**
|
|
2325
|
-
*
|
|
2641
|
+
* Registers a new strategy schema.
|
|
2326
2642
|
*
|
|
2327
|
-
*
|
|
2643
|
+
* @param key - Unique strategy name
|
|
2644
|
+
* @param value - Strategy schema configuration
|
|
2645
|
+
* @throws Error if strategy name already exists
|
|
2646
|
+
*/
|
|
2647
|
+
register: (key: StrategyName, value: IStrategySchema) => void;
|
|
2648
|
+
/**
|
|
2649
|
+
* Validates strategy schema structure for required properties.
|
|
2328
2650
|
*
|
|
2329
|
-
*
|
|
2330
|
-
*
|
|
2331
|
-
*
|
|
2651
|
+
* Performs shallow validation to ensure all required properties exist
|
|
2652
|
+
* and have correct types before registration in the registry.
|
|
2653
|
+
*
|
|
2654
|
+
* @param strategySchema - Strategy schema to validate
|
|
2655
|
+
* @throws Error if strategyName is missing or not a string
|
|
2656
|
+
* @throws Error if interval is missing or not a valid SignalInterval
|
|
2657
|
+
* @throws Error if getSignal is missing or not a function
|
|
2332
2658
|
*/
|
|
2333
|
-
|
|
2334
|
-
strategyName: string;
|
|
2335
|
-
exchangeName: string;
|
|
2336
|
-
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2337
|
-
}
|
|
2338
|
-
|
|
2339
|
-
/**
|
|
2340
|
-
* Global service providing access to backtest functionality.
|
|
2341
|
-
*
|
|
2342
|
-
* Simple wrapper around BacktestLogicPublicService for dependency injection.
|
|
2343
|
-
* Used by public API exports.
|
|
2344
|
-
*/
|
|
2345
|
-
declare class BacktestGlobalService {
|
|
2346
|
-
private readonly loggerService;
|
|
2347
|
-
private readonly backtestLogicPublicService;
|
|
2348
|
-
private readonly strategyValidationService;
|
|
2349
|
-
private readonly exchangeValidationService;
|
|
2350
|
-
private readonly frameValidationService;
|
|
2659
|
+
private validateShallow;
|
|
2351
2660
|
/**
|
|
2352
|
-
*
|
|
2661
|
+
* Overrides an existing strategy schema with partial updates.
|
|
2353
2662
|
*
|
|
2354
|
-
* @param
|
|
2355
|
-
* @param
|
|
2356
|
-
* @returns
|
|
2663
|
+
* @param key - Strategy name to override
|
|
2664
|
+
* @param value - Partial schema updates
|
|
2665
|
+
* @returns Updated strategy schema
|
|
2666
|
+
* @throws Error if strategy name doesn't exist
|
|
2357
2667
|
*/
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2668
|
+
override: (key: StrategyName, value: Partial<IStrategySchema>) => IStrategySchema;
|
|
2669
|
+
/**
|
|
2670
|
+
* Retrieves a strategy schema by name.
|
|
2671
|
+
*
|
|
2672
|
+
* @param key - Strategy name
|
|
2673
|
+
* @returns Strategy schema configuration
|
|
2674
|
+
* @throws Error if strategy name doesn't exist
|
|
2675
|
+
*/
|
|
2676
|
+
get: (key: StrategyName) => IStrategySchema;
|
|
2363
2677
|
}
|
|
2364
2678
|
|
|
2365
2679
|
/**
|
|
2366
|
-
* Service for
|
|
2367
|
-
*
|
|
2368
|
-
* Features:
|
|
2369
|
-
* - Listens to signal events via onTick callback
|
|
2370
|
-
* - Accumulates closed signals per strategy using memoized storage
|
|
2371
|
-
* - Generates markdown tables with detailed signal information
|
|
2372
|
-
* - Saves reports to disk in logs/backtest/{strategyName}.md
|
|
2373
|
-
*
|
|
2374
|
-
* @example
|
|
2375
|
-
* ```typescript
|
|
2376
|
-
* const service = new BacktestMarkdownService();
|
|
2377
|
-
*
|
|
2378
|
-
* // Add to strategy callbacks
|
|
2379
|
-
* addStrategy({
|
|
2380
|
-
* strategyName: "my-strategy",
|
|
2381
|
-
* callbacks: {
|
|
2382
|
-
* onTick: (symbol, result, backtest) => {
|
|
2383
|
-
* service.tick(result);
|
|
2384
|
-
* }
|
|
2385
|
-
* }
|
|
2386
|
-
* });
|
|
2680
|
+
* Service for managing frame schema registry.
|
|
2387
2681
|
*
|
|
2388
|
-
*
|
|
2389
|
-
*
|
|
2390
|
-
* ```
|
|
2682
|
+
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
2683
|
+
* Frames are registered via addFrame() and retrieved by name.
|
|
2391
2684
|
*/
|
|
2392
|
-
declare class
|
|
2393
|
-
|
|
2394
|
-
private
|
|
2685
|
+
declare class FrameSchemaService {
|
|
2686
|
+
readonly loggerService: LoggerService;
|
|
2687
|
+
private _registry;
|
|
2395
2688
|
/**
|
|
2396
|
-
*
|
|
2397
|
-
*
|
|
2689
|
+
* Registers a new frame schema.
|
|
2690
|
+
*
|
|
2691
|
+
* @param key - Unique frame name
|
|
2692
|
+
* @param value - Frame schema configuration
|
|
2693
|
+
* @throws Error if frame name already exists
|
|
2398
2694
|
*/
|
|
2399
|
-
|
|
2695
|
+
register(key: FrameName, value: IFrameSchema): void;
|
|
2400
2696
|
/**
|
|
2401
|
-
*
|
|
2402
|
-
* Should be called from IStrategyCallbacks.onTick.
|
|
2403
|
-
*
|
|
2404
|
-
* Only processes closed signals - opened signals are ignored.
|
|
2405
|
-
*
|
|
2406
|
-
* @param data - Tick result from strategy execution (opened or closed)
|
|
2697
|
+
* Validates frame schema structure for required properties.
|
|
2407
2698
|
*
|
|
2408
|
-
*
|
|
2409
|
-
*
|
|
2410
|
-
* const service = new BacktestMarkdownService();
|
|
2699
|
+
* Performs shallow validation to ensure all required properties exist
|
|
2700
|
+
* and have correct types before registration in the registry.
|
|
2411
2701
|
*
|
|
2412
|
-
*
|
|
2413
|
-
*
|
|
2414
|
-
*
|
|
2415
|
-
*
|
|
2416
|
-
*
|
|
2417
|
-
* ```
|
|
2702
|
+
* @param frameSchema - Frame schema to validate
|
|
2703
|
+
* @throws Error if frameName is missing or not a string
|
|
2704
|
+
* @throws Error if interval is missing or not a valid FrameInterval
|
|
2705
|
+
* @throws Error if startDate is missing or not a Date
|
|
2706
|
+
* @throws Error if endDate is missing or not a Date
|
|
2418
2707
|
*/
|
|
2419
|
-
private
|
|
2708
|
+
private validateShallow;
|
|
2420
2709
|
/**
|
|
2421
|
-
*
|
|
2422
|
-
* Delegates to ReportStorage.generateReport().
|
|
2710
|
+
* Overrides an existing frame schema with partial updates.
|
|
2423
2711
|
*
|
|
2424
|
-
* @param
|
|
2425
|
-
* @
|
|
2712
|
+
* @param key - Frame name to override
|
|
2713
|
+
* @param value - Partial schema updates
|
|
2714
|
+
* @throws Error if frame name doesn't exist
|
|
2715
|
+
*/
|
|
2716
|
+
override(key: FrameName, value: Partial<IFrameSchema>): IFrameSchema;
|
|
2717
|
+
/**
|
|
2718
|
+
* Retrieves a frame schema by name.
|
|
2426
2719
|
*
|
|
2427
|
-
* @
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2430
|
-
* const markdown = service.generateReport("my-strategy");
|
|
2431
|
-
* console.log(markdown);
|
|
2432
|
-
* ```
|
|
2720
|
+
* @param key - Frame name
|
|
2721
|
+
* @returns Frame schema configuration
|
|
2722
|
+
* @throws Error if frame name doesn't exist
|
|
2433
2723
|
*/
|
|
2434
|
-
|
|
2724
|
+
get(key: FrameName): IFrameSchema;
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
/**
|
|
2728
|
+
* Private service for backtest orchestration using async generators.
|
|
2729
|
+
*
|
|
2730
|
+
* Flow:
|
|
2731
|
+
* 1. Get timeframes from frame service
|
|
2732
|
+
* 2. Iterate through timeframes calling tick()
|
|
2733
|
+
* 3. When signal opens: fetch candles and call backtest()
|
|
2734
|
+
* 4. Skip timeframes until signal closes
|
|
2735
|
+
* 5. Yield closed result and continue
|
|
2736
|
+
*
|
|
2737
|
+
* Memory efficient: streams results without array accumulation.
|
|
2738
|
+
* Supports early termination via break in consumer.
|
|
2739
|
+
*/
|
|
2740
|
+
declare class BacktestLogicPrivateService {
|
|
2741
|
+
private readonly loggerService;
|
|
2742
|
+
private readonly strategyGlobalService;
|
|
2743
|
+
private readonly exchangeGlobalService;
|
|
2744
|
+
private readonly frameGlobalService;
|
|
2745
|
+
private readonly methodContextService;
|
|
2435
2746
|
/**
|
|
2436
|
-
*
|
|
2437
|
-
* Creates directory if it doesn't exist.
|
|
2438
|
-
* Delegates to ReportStorage.dump().
|
|
2747
|
+
* Runs backtest for a symbol, streaming closed signals as async generator.
|
|
2439
2748
|
*
|
|
2440
|
-
* @param
|
|
2441
|
-
* @
|
|
2749
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2750
|
+
* @yields Closed signal results with PNL
|
|
2442
2751
|
*
|
|
2443
2752
|
* @example
|
|
2444
2753
|
* ```typescript
|
|
2445
|
-
* const
|
|
2446
|
-
*
|
|
2447
|
-
*
|
|
2448
|
-
*
|
|
2449
|
-
*
|
|
2450
|
-
* // Save to custom path: ./custom/path/my-strategy.md
|
|
2451
|
-
* await service.dump("my-strategy", "./custom/path");
|
|
2754
|
+
* for await (const result of backtestLogic.run("BTCUSDT")) {
|
|
2755
|
+
* console.log(result.closeReason, result.pnl.pnlPercentage);
|
|
2756
|
+
* if (result.pnl.pnlPercentage < -10) break; // Early termination
|
|
2757
|
+
* }
|
|
2452
2758
|
* ```
|
|
2453
2759
|
*/
|
|
2454
|
-
|
|
2760
|
+
run(symbol: string): AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
/**
|
|
2764
|
+
* Private service for live trading orchestration using async generators.
|
|
2765
|
+
*
|
|
2766
|
+
* Flow:
|
|
2767
|
+
* 1. Infinite while(true) loop for continuous monitoring
|
|
2768
|
+
* 2. Create real-time date with new Date()
|
|
2769
|
+
* 3. Call tick() to check signal status
|
|
2770
|
+
* 4. Yield opened/closed results (skip idle/active)
|
|
2771
|
+
* 5. Sleep for TICK_TTL between iterations
|
|
2772
|
+
*
|
|
2773
|
+
* Features:
|
|
2774
|
+
* - Crash recovery via ClientStrategy.waitForInit()
|
|
2775
|
+
* - Real-time progression with new Date()
|
|
2776
|
+
* - Memory efficient streaming
|
|
2777
|
+
* - Never completes (infinite generator)
|
|
2778
|
+
*/
|
|
2779
|
+
declare class LiveLogicPrivateService {
|
|
2780
|
+
private readonly loggerService;
|
|
2781
|
+
private readonly strategyGlobalService;
|
|
2455
2782
|
/**
|
|
2456
|
-
*
|
|
2457
|
-
* If strategyName is provided, clears only that strategy's data.
|
|
2458
|
-
* If strategyName is omitted, clears all strategies' data.
|
|
2459
|
-
*
|
|
2460
|
-
* @param strategyName - Optional strategy name to clear specific strategy data
|
|
2461
|
-
*
|
|
2462
|
-
* @example
|
|
2463
|
-
* ```typescript
|
|
2464
|
-
* const service = new BacktestMarkdownService();
|
|
2783
|
+
* Runs live trading for a symbol, streaming results as async generator.
|
|
2465
2784
|
*
|
|
2466
|
-
*
|
|
2467
|
-
*
|
|
2785
|
+
* Infinite generator that yields opened and closed signals.
|
|
2786
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
2468
2787
|
*
|
|
2469
|
-
*
|
|
2470
|
-
*
|
|
2471
|
-
* ```
|
|
2472
|
-
*/
|
|
2473
|
-
clear: (strategyName?: StrategyName) => Promise<void>;
|
|
2474
|
-
/**
|
|
2475
|
-
* Initializes the service by subscribing to backtest signal events.
|
|
2476
|
-
* Uses singleshot to ensure initialization happens only once.
|
|
2477
|
-
* Automatically called on first use.
|
|
2788
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2789
|
+
* @yields Opened and closed signal results
|
|
2478
2790
|
*
|
|
2479
2791
|
* @example
|
|
2480
2792
|
* ```typescript
|
|
2481
|
-
* const
|
|
2482
|
-
*
|
|
2793
|
+
* for await (const result of liveLogic.run("BTCUSDT")) {
|
|
2794
|
+
* if (result.action === "opened") {
|
|
2795
|
+
* console.log("New signal:", result.signal.id);
|
|
2796
|
+
* }
|
|
2797
|
+
* if (result.action === "closed") {
|
|
2798
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
2799
|
+
* }
|
|
2800
|
+
* // Infinite loop - will never complete
|
|
2801
|
+
* }
|
|
2483
2802
|
* ```
|
|
2484
2803
|
*/
|
|
2485
|
-
|
|
2804
|
+
run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2486
2805
|
}
|
|
2487
2806
|
|
|
2488
2807
|
/**
|
|
2489
|
-
*
|
|
2808
|
+
* Public service for backtest orchestration with context management.
|
|
2490
2809
|
*
|
|
2491
|
-
*
|
|
2492
|
-
*
|
|
2493
|
-
*
|
|
2494
|
-
*
|
|
2495
|
-
*
|
|
2496
|
-
* - Saves reports to disk in logs/live/{strategyName}.md
|
|
2810
|
+
* Wraps BacktestLogicPrivateService with MethodContextService to provide
|
|
2811
|
+
* implicit context propagation for strategyName, exchangeName, and frameName.
|
|
2812
|
+
*
|
|
2813
|
+
* This allows getCandles(), getSignal(), and other functions to work without
|
|
2814
|
+
* explicit context parameters.
|
|
2497
2815
|
*
|
|
2498
2816
|
* @example
|
|
2499
2817
|
* ```typescript
|
|
2500
|
-
* const
|
|
2818
|
+
* const backtestLogicPublicService = inject(TYPES.backtestLogicPublicService);
|
|
2501
2819
|
*
|
|
2502
|
-
*
|
|
2503
|
-
* addStrategy({
|
|
2820
|
+
* for await (const result of backtestLogicPublicService.run("BTCUSDT", {
|
|
2504
2821
|
* strategyName: "my-strategy",
|
|
2505
|
-
*
|
|
2506
|
-
*
|
|
2507
|
-
*
|
|
2508
|
-
*
|
|
2509
|
-
*
|
|
2510
|
-
* }
|
|
2822
|
+
* exchangeName: "my-exchange",
|
|
2823
|
+
* frameName: "1d-backtest",
|
|
2824
|
+
* })) {
|
|
2825
|
+
* if (result.action === "closed") {
|
|
2826
|
+
* console.log("PNL:", result.pnl.profit);
|
|
2511
2827
|
* }
|
|
2512
|
-
* }
|
|
2513
|
-
*
|
|
2514
|
-
* // Later: generate and save report
|
|
2515
|
-
* await service.dump("my-strategy");
|
|
2828
|
+
* }
|
|
2516
2829
|
* ```
|
|
2517
2830
|
*/
|
|
2518
|
-
declare class
|
|
2519
|
-
/** Logger service for debug output */
|
|
2831
|
+
declare class BacktestLogicPublicService {
|
|
2520
2832
|
private readonly loggerService;
|
|
2833
|
+
private readonly backtestLogicPrivateService;
|
|
2521
2834
|
/**
|
|
2522
|
-
*
|
|
2523
|
-
* Each strategy gets its own isolated storage instance.
|
|
2524
|
-
*/
|
|
2525
|
-
private getStorage;
|
|
2526
|
-
/**
|
|
2527
|
-
* Processes tick events and accumulates all event types.
|
|
2528
|
-
* Should be called from IStrategyCallbacks.onTick.
|
|
2529
|
-
*
|
|
2530
|
-
* Processes all event types: idle, opened, active, closed.
|
|
2531
|
-
*
|
|
2532
|
-
* @param data - Tick result from strategy execution
|
|
2533
|
-
*
|
|
2534
|
-
* @example
|
|
2535
|
-
* ```typescript
|
|
2536
|
-
* const service = new LiveMarkdownService();
|
|
2537
|
-
*
|
|
2538
|
-
* callbacks: {
|
|
2539
|
-
* onTick: (symbol, result, backtest) => {
|
|
2540
|
-
* if (!backtest) {
|
|
2541
|
-
* service.tick(result);
|
|
2542
|
-
* }
|
|
2543
|
-
* }
|
|
2544
|
-
* }
|
|
2545
|
-
* ```
|
|
2546
|
-
*/
|
|
2547
|
-
private tick;
|
|
2548
|
-
/**
|
|
2549
|
-
* Generates markdown report with all events for a strategy.
|
|
2550
|
-
* Delegates to ReportStorage.getReport().
|
|
2835
|
+
* Runs backtest for a symbol with context propagation.
|
|
2551
2836
|
*
|
|
2552
|
-
*
|
|
2553
|
-
*
|
|
2837
|
+
* Streams closed signals as async generator. Context is automatically
|
|
2838
|
+
* injected into all framework functions called during iteration.
|
|
2554
2839
|
*
|
|
2555
|
-
* @
|
|
2556
|
-
*
|
|
2557
|
-
*
|
|
2558
|
-
* const markdown = await service.getReport("my-strategy");
|
|
2559
|
-
* console.log(markdown);
|
|
2560
|
-
* ```
|
|
2840
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2841
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
2842
|
+
* @returns Async generator yielding closed signals with PNL
|
|
2561
2843
|
*/
|
|
2562
|
-
|
|
2844
|
+
run: (symbol: string, context: {
|
|
2845
|
+
strategyName: string;
|
|
2846
|
+
exchangeName: string;
|
|
2847
|
+
frameName: string;
|
|
2848
|
+
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
/**
|
|
2852
|
+
* Public service for live trading orchestration with context management.
|
|
2853
|
+
*
|
|
2854
|
+
* Wraps LiveLogicPrivateService with MethodContextService to provide
|
|
2855
|
+
* implicit context propagation for strategyName and exchangeName.
|
|
2856
|
+
*
|
|
2857
|
+
* This allows getCandles(), getSignal(), and other functions to work without
|
|
2858
|
+
* explicit context parameters.
|
|
2859
|
+
*
|
|
2860
|
+
* Features:
|
|
2861
|
+
* - Infinite async generator (never completes)
|
|
2862
|
+
* - Crash recovery via persisted state
|
|
2863
|
+
* - Real-time progression with Date.now()
|
|
2864
|
+
*
|
|
2865
|
+
* @example
|
|
2866
|
+
* ```typescript
|
|
2867
|
+
* const liveLogicPublicService = inject(TYPES.liveLogicPublicService);
|
|
2868
|
+
*
|
|
2869
|
+
* // Infinite loop - use Ctrl+C to stop
|
|
2870
|
+
* for await (const result of liveLogicPublicService.run("BTCUSDT", {
|
|
2871
|
+
* strategyName: "my-strategy",
|
|
2872
|
+
* exchangeName: "my-exchange",
|
|
2873
|
+
* })) {
|
|
2874
|
+
* if (result.action === "opened") {
|
|
2875
|
+
* console.log("Signal opened:", result.signal);
|
|
2876
|
+
* } else if (result.action === "closed") {
|
|
2877
|
+
* console.log("PNL:", result.pnl.profit);
|
|
2878
|
+
* }
|
|
2879
|
+
* }
|
|
2880
|
+
* ```
|
|
2881
|
+
*/
|
|
2882
|
+
declare class LiveLogicPublicService {
|
|
2883
|
+
private readonly loggerService;
|
|
2884
|
+
private readonly liveLogicPrivateService;
|
|
2563
2885
|
/**
|
|
2564
|
-
*
|
|
2565
|
-
* Creates directory if it doesn't exist.
|
|
2566
|
-
* Delegates to ReportStorage.dump().
|
|
2567
|
-
*
|
|
2568
|
-
* @param strategyName - Strategy name to save report for
|
|
2569
|
-
* @param path - Directory path to save report (default: "./logs/live")
|
|
2570
|
-
*
|
|
2571
|
-
* @example
|
|
2572
|
-
* ```typescript
|
|
2573
|
-
* const service = new LiveMarkdownService();
|
|
2886
|
+
* Runs live trading for a symbol with context propagation.
|
|
2574
2887
|
*
|
|
2575
|
-
*
|
|
2576
|
-
*
|
|
2888
|
+
* Streams opened and closed signals as infinite async generator.
|
|
2889
|
+
* Context is automatically injected into all framework functions.
|
|
2890
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
2577
2891
|
*
|
|
2578
|
-
*
|
|
2579
|
-
*
|
|
2580
|
-
*
|
|
2892
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2893
|
+
* @param context - Execution context with strategy and exchange names
|
|
2894
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
2581
2895
|
*/
|
|
2582
|
-
|
|
2896
|
+
run: (symbol: string, context: {
|
|
2897
|
+
strategyName: string;
|
|
2898
|
+
exchangeName: string;
|
|
2899
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
/**
|
|
2903
|
+
* Global service providing access to live trading functionality.
|
|
2904
|
+
*
|
|
2905
|
+
* Simple wrapper around LiveLogicPublicService for dependency injection.
|
|
2906
|
+
* Used by public API exports.
|
|
2907
|
+
*/
|
|
2908
|
+
declare class LiveGlobalService {
|
|
2909
|
+
private readonly loggerService;
|
|
2910
|
+
private readonly liveLogicPublicService;
|
|
2911
|
+
private readonly strategyValidationService;
|
|
2912
|
+
private readonly exchangeValidationService;
|
|
2583
2913
|
/**
|
|
2584
|
-
*
|
|
2585
|
-
* If strategyName is provided, clears only that strategy's data.
|
|
2586
|
-
* If strategyName is omitted, clears all strategies' data.
|
|
2587
|
-
*
|
|
2588
|
-
* @param strategyName - Optional strategy name to clear specific strategy data
|
|
2589
|
-
*
|
|
2590
|
-
* @example
|
|
2591
|
-
* ```typescript
|
|
2592
|
-
* const service = new LiveMarkdownService();
|
|
2914
|
+
* Runs live trading for a symbol with context propagation.
|
|
2593
2915
|
*
|
|
2594
|
-
*
|
|
2595
|
-
* await service.clear("my-strategy");
|
|
2916
|
+
* Infinite async generator with crash recovery support.
|
|
2596
2917
|
*
|
|
2597
|
-
*
|
|
2598
|
-
*
|
|
2599
|
-
*
|
|
2918
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2919
|
+
* @param context - Execution context with strategy and exchange names
|
|
2920
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
2600
2921
|
*/
|
|
2601
|
-
|
|
2922
|
+
run: (symbol: string, context: {
|
|
2923
|
+
strategyName: string;
|
|
2924
|
+
exchangeName: string;
|
|
2925
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
/**
|
|
2929
|
+
* Global service providing access to backtest functionality.
|
|
2930
|
+
*
|
|
2931
|
+
* Simple wrapper around BacktestLogicPublicService for dependency injection.
|
|
2932
|
+
* Used by public API exports.
|
|
2933
|
+
*/
|
|
2934
|
+
declare class BacktestGlobalService {
|
|
2935
|
+
private readonly loggerService;
|
|
2936
|
+
private readonly backtestLogicPublicService;
|
|
2937
|
+
private readonly strategyValidationService;
|
|
2938
|
+
private readonly exchangeValidationService;
|
|
2939
|
+
private readonly frameValidationService;
|
|
2602
2940
|
/**
|
|
2603
|
-
*
|
|
2604
|
-
* Uses singleshot to ensure initialization happens only once.
|
|
2605
|
-
* Automatically called on first use.
|
|
2941
|
+
* Runs backtest for a symbol with context propagation.
|
|
2606
2942
|
*
|
|
2607
|
-
* @
|
|
2608
|
-
*
|
|
2609
|
-
*
|
|
2610
|
-
* await service.init(); // Subscribe to live events
|
|
2611
|
-
* ```
|
|
2943
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2944
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
2945
|
+
* @returns Async generator yielding closed signals with PNL
|
|
2612
2946
|
*/
|
|
2613
|
-
|
|
2947
|
+
run: (symbol: string, context: {
|
|
2948
|
+
strategyName: string;
|
|
2949
|
+
exchangeName: string;
|
|
2950
|
+
frameName: string;
|
|
2951
|
+
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
2614
2952
|
}
|
|
2615
2953
|
|
|
2616
2954
|
/**
|
|
@@ -2642,6 +2980,12 @@ declare class ExchangeValidationService {
|
|
|
2642
2980
|
* Memoized function to cache validation results
|
|
2643
2981
|
*/
|
|
2644
2982
|
validate: (exchangeName: ExchangeName, source: string) => void;
|
|
2983
|
+
/**
|
|
2984
|
+
* Returns a list of all registered exchange schemas
|
|
2985
|
+
* @public
|
|
2986
|
+
* @returns Array of exchange schemas with their configurations
|
|
2987
|
+
*/
|
|
2988
|
+
list: () => Promise<IExchangeSchema[]>;
|
|
2645
2989
|
}
|
|
2646
2990
|
|
|
2647
2991
|
/**
|
|
@@ -2673,6 +3017,12 @@ declare class StrategyValidationService {
|
|
|
2673
3017
|
* Memoized function to cache validation results
|
|
2674
3018
|
*/
|
|
2675
3019
|
validate: (strategyName: StrategyName, source: string) => void;
|
|
3020
|
+
/**
|
|
3021
|
+
* Returns a list of all registered strategy schemas
|
|
3022
|
+
* @public
|
|
3023
|
+
* @returns Array of strategy schemas with their configurations
|
|
3024
|
+
*/
|
|
3025
|
+
list: () => Promise<IStrategySchema[]>;
|
|
2676
3026
|
}
|
|
2677
3027
|
|
|
2678
3028
|
/**
|
|
@@ -2704,6 +3054,12 @@ declare class FrameValidationService {
|
|
|
2704
3054
|
* Memoized function to cache validation results
|
|
2705
3055
|
*/
|
|
2706
3056
|
validate: (frameName: FrameName, source: string) => void;
|
|
3057
|
+
/**
|
|
3058
|
+
* Returns a list of all registered frame schemas
|
|
3059
|
+
* @public
|
|
3060
|
+
* @returns Array of frame schemas with their configurations
|
|
3061
|
+
*/
|
|
3062
|
+
list: () => Promise<IFrameSchema[]>;
|
|
2707
3063
|
}
|
|
2708
3064
|
|
|
2709
3065
|
declare const backtest: {
|
|
@@ -2736,4 +3092,4 @@ declare const backtest: {
|
|
|
2736
3092
|
loggerService: LoggerService;
|
|
2737
3093
|
};
|
|
2738
3094
|
|
|
2739
|
-
export { Backtest, type CandleInterval, type EntityId, ExecutionContextService, type FrameInterval, type ICandleData, type IExchangeSchema, type IFrameSchema, type IPersistBase, type ISignalData, type ISignalDto, type ISignalRow, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, Live, MethodContextService, PersistBase, PersistSignalAdaper, type SignalInterval, type TPersistBase, type TPersistBaseCtor, addExchange, addFrame, addStrategy, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listenDone, listenDoneOnce, listenError, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, setLogger };
|
|
3095
|
+
export { Backtest, type BacktestStatistics, type CandleInterval, type DoneContract, type EntityId, ExecutionContextService, type FrameInterval, type ICandleData, type IExchangeSchema, type IFrameSchema, type IPersistBase, type ISignalData, type ISignalDto, type ISignalRow, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, Live, type LiveStatistics, MethodContextService, PersistBase, PersistSignalAdaper, type ProgressContract, type SignalInterval, type TPersistBase, type TPersistBaseCtor, addExchange, addFrame, addStrategy, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listStrategies, listenDone, listenDoneOnce, listenError, listenProgress, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, setLogger };
|