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/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
- * Signal data stored in persistence layer.
1032
- * Contains nullable signal for atomic updates.
1033
- */
1034
- interface ISignalData {
1035
- /** Current signal state (null when no active signal) */
1036
- signalRow: ISignalRow | null;
1037
- }
1038
- /**
1039
- * Type helper for PersistBase instance.
1040
- */
1041
- type TPersistBase = InstanceType<typeof PersistBase>;
1042
- /**
1043
- * Constructor type for PersistBase.
1044
- * Used for custom persistence adapters.
1045
- */
1046
- type TPersistBaseCtor<EntityName extends string = string, Entity extends IEntity = IEntity> = new (entityName: EntityName, baseDir: string) => IPersistBase<Entity>;
1047
- /**
1048
- * Entity identifier - string or number.
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 IEntity {
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
- * Persistence interface for CRUD operations.
1058
- * Implemented by PersistBase.
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
- interface IPersistBase<Entity extends IEntity = IEntity> {
1254
+ declare class BacktestMarkdownService {
1255
+ /** Logger service for debug output */
1256
+ private readonly loggerService;
1061
1257
  /**
1062
- * Initialize persistence directory and validate existing files.
1063
- * Uses singleshot to ensure one-time execution.
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
- * @param initial - Whether this is the first initialization
1066
- * @returns Promise that resolves when initialization is complete
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
- waitForInit(initial: boolean): Promise<void>;
1281
+ private tick;
1069
1282
  /**
1070
- * Read entity from persistence storage.
1283
+ * Gets statistical data from all closed signals for a strategy.
1284
+ * Delegates to ReportStorage.getData().
1071
1285
  *
1072
- * @param entityId - Unique entity identifier
1073
- * @returns Promise resolving to entity data
1074
- * @throws Error if entity not found or read fails
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
- readValue(entityId: EntityId): Promise<Entity>;
1296
+ getData: (strategyName: StrategyName) => Promise<BacktestStatistics>;
1077
1297
  /**
1078
- * Check if entity exists in storage.
1298
+ * Generates markdown report with all closed signals for a strategy.
1299
+ * Delegates to ReportStorage.generateReport().
1079
1300
  *
1080
- * @param entityId - Unique entity identifier
1081
- * @returns Promise resolving to true if exists, false otherwise
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
- hasValue(entityId: EntityId): Promise<boolean>;
1311
+ getReport: (strategyName: StrategyName) => Promise<string>;
1084
1312
  /**
1085
- * Write entity to storage with atomic file writes.
1313
+ * Saves strategy report to disk.
1314
+ * Creates directory if it doesn't exist.
1315
+ * Delegates to ReportStorage.dump().
1086
1316
  *
1087
- * @param entityId - Unique entity identifier
1088
- * @param entity - Entity data to persist
1089
- * @returns Promise that resolves when write is complete
1090
- * @throws Error if write fails
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
- writeValue(entityId: EntityId, entity: Entity): Promise<void>;
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
- * Registers a custom persistence adapter.
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 Ctor - Custom PersistBase constructor
1337
+ * @param strategyName - Optional strategy name to clear specific strategy data
1208
1338
  *
1209
1339
  * @example
1210
1340
  * ```typescript
1211
- * class RedisPersist extends PersistBase {
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
- * Called by ClientStrategy.waitForInit() to restore state.
1223
- * Returns null if no signal exists.
1343
+ * // Clear specific strategy data
1344
+ * await service.clear("my-strategy");
1224
1345
  *
1225
- * @param strategyName - Strategy identifier
1226
- * @param symbol - Trading pair symbol
1227
- * @returns Promise resolving to signal or null
1346
+ * // Clear all strategies' data
1347
+ * await service.clear();
1348
+ * ```
1228
1349
  */
1229
- readSignalData: (strategyName: StrategyName, symbol: string) => Promise<ISignalRow | null>;
1350
+ clear: (strategyName?: StrategyName) => Promise<void>;
1230
1351
  /**
1231
- * Writes signal data to disk with atomic file writes.
1232
- *
1233
- * Called by ClientStrategy.setPendingSignal() to persist state.
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
- * @param signalRow - Signal data (null to clear)
1237
- * @param strategyName - Strategy identifier
1238
- * @param symbol - Trading pair symbol
1239
- * @returns Promise that resolves when write is complete
1356
+ * @example
1357
+ * ```typescript
1358
+ * const service = new BacktestMarkdownService();
1359
+ * await service.init(); // Subscribe to backtest events
1360
+ * ```
1240
1361
  */
1241
- writeSignalData: (signalRow: ISignalRow | null, strategyName: StrategyName, symbol: string) => Promise<void>;
1362
+ protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
1242
1363
  }
1364
+
1243
1365
  /**
1244
- * Global singleton instance of PersistSignalUtils.
1245
- * Used by ClientStrategy for signal persistence.
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
- * // Custom adapter
1250
- * PersistSignalAdaper.usePersistSignalAdapter(RedisPersist);
1405
+ * const stats = await Live.getData("my-strategy");
1251
1406
  *
1252
- * // Read signal
1253
- * const signal = await PersistSignalAdaper.readSignalData("my-strategy", "BTCUSDT");
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
- * // Write signal
1256
- * await PersistSignalAdaper.writeSignalData(signal, "my-strategy", "BTCUSDT");
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
- declare const PersistSignalAdaper: PersistSignalUtils;
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
- * Utility class for backtest operations.
1449
+ * Service for generating and saving live trading markdown reports.
1263
1450
  *
1264
- * Provides simplified access to backtestGlobalService.run() with logging.
1265
- * Exported as singleton instance for convenient usage.
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
- * import { Backtest } from "./classes/Backtest";
1460
+ * const service = new LiveMarkdownService();
1270
1461
  *
1271
- * for await (const result of Backtest.run("BTCUSDT", {
1462
+ * // Add to strategy callbacks
1463
+ * addStrategy({
1272
1464
  * strategyName: "my-strategy",
1273
- * exchangeName: "my-exchange",
1274
- * frameName: "1d-backtest"
1275
- * })) {
1276
- * console.log("Closed signal PNL:", result.pnl.pnlPercentage);
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 BacktestUtils {
1478
+ declare class LiveMarkdownService {
1479
+ /** Logger service for debug output */
1480
+ private readonly loggerService;
1281
1481
  /**
1282
- * Runs backtest for a symbol with context propagation.
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
- run: (symbol: string, context: {
1289
- strategyName: string;
1290
- exchangeName: string;
1291
- frameName: string;
1292
- }) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
1485
+ private getStorage;
1293
1486
  /**
1294
- * Runs backtest in background without yielding results.
1487
+ * Processes tick events and accumulates all event types.
1488
+ * Should be called from IStrategyCallbacks.onTick.
1295
1489
  *
1296
- * Consumes all backtest results internally without exposing them.
1297
- * Useful for running backtests for side effects only (callbacks, logging).
1490
+ * Processes all event types: idle, opened, active, closed.
1298
1491
  *
1299
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
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
- * // Run backtest silently, only callbacks will fire
1306
- * await Backtest.background("BTCUSDT", {
1307
- * strategyName: "my-strategy",
1308
- * exchangeName: "my-exchange",
1309
- * frameName: "1d-backtest"
1310
- * });
1311
- * console.log("Backtest completed");
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
- background: (symbol: string, context: {
1315
- strategyName: string;
1316
- exchangeName: string;
1317
- frameName: string;
1318
- }) => () => void;
1507
+ private tick;
1319
1508
  /**
1320
- * Generates markdown report with all closed signals for a strategy.
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 Promise resolving to markdown formatted report string
1528
+ * @returns Markdown formatted report string with table of all events
1324
1529
  *
1325
1530
  * @example
1326
1531
  * ```typescript
1327
- * const markdown = await Backtest.getReport("my-strategy");
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 - Optional directory path to save report (default: "./logs/backtest")
1544
+ * @param path - Directory path to save report (default: "./logs/live")
1337
1545
  *
1338
1546
  * @example
1339
1547
  * ```typescript
1340
- * // Save to default path: ./logs/backtest/my-strategy.md
1341
- * await Backtest.dump("my-strategy");
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 Backtest.dump("my-strategy", "./custom/path");
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
- * Runs live trading in background without yielding results.
1415
- *
1416
- * Consumes all live trading results internally without exposing them.
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 symbol - Trading pair symbol (e.g., "BTCUSDT")
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
- * // Run live trading silently in background, only callbacks will fire
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
- * @param strategyName - Strategy name to generate report for
1442
- * @returns Promise resolving to markdown formatted report string
1569
+ * // Clear specific strategy data
1570
+ * await service.clear("my-strategy");
1443
1571
  *
1444
- * @example
1445
- * ```typescript
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
- getReport: (strategyName: StrategyName) => Promise<string>;
1576
+ clear: (strategyName?: StrategyName) => Promise<void>;
1451
1577
  /**
1452
- * Saves strategy report to disk.
1453
- *
1454
- * @param strategyName - Strategy name to save report for
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
- * // Save to default path: ./logs/live/my-strategy.md
1460
- * await Live.dump("my-strategy");
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
- dump: (strategyName: StrategyName, path?: string) => Promise<void>;
1588
+ protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
1467
1589
  }
1590
+
1591
+ declare const BASE_WAIT_FOR_INIT_SYMBOL: unique symbol;
1468
1592
  /**
1469
- * Singleton instance of LiveUtils for convenient live trading operations.
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
- declare const Live: LiveUtils;
1484
-
1596
+ interface ISignalData {
1597
+ /** Current signal state (null when no active signal) */
1598
+ signalRow: ISignalRow | null;
1599
+ }
1485
1600
  /**
1486
- * Logger service with automatic context injection.
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
- declare class LoggerService implements ILogger {
1497
- private readonly methodContextService;
1498
- private readonly executionContextService;
1499
- private _commonLogger;
1500
- /**
1501
- * Gets current method context if available.
1502
- * Contains strategyName, exchangeName, frameName from MethodContextService.
1503
- */
1504
- private get methodContext();
1505
- /**
1506
- * Gets current execution context if available.
1507
- * Contains symbol, when, backtest from ExecutionContextService.
1508
- */
1509
- private get executionContext();
1510
- /**
1511
- * Logs general-purpose message with automatic context injection.
1512
- *
1513
- * @param topic - Log topic/category
1514
- * @param args - Additional log arguments
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
- * Logs debug-level message with automatic context injection.
1624
+ * Initialize persistence directory and validate existing files.
1625
+ * Uses singleshot to ensure one-time execution.
1519
1626
  *
1520
- * @param topic - Log topic/category
1521
- * @param args - Additional log arguments
1627
+ * @param initial - Whether this is the first initialization
1628
+ * @returns Promise that resolves when initialization is complete
1522
1629
  */
1523
- debug: (topic: string, ...args: any[]) => Promise<void>;
1630
+ waitForInit(initial: boolean): Promise<void>;
1524
1631
  /**
1525
- * Logs info-level message with automatic context injection.
1632
+ * Read entity from persistence storage.
1526
1633
  *
1527
- * @param topic - Log topic/category
1528
- * @param args - Additional log arguments
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
- info: (topic: string, ...args: any[]) => Promise<void>;
1638
+ readValue(entityId: EntityId): Promise<Entity>;
1531
1639
  /**
1532
- * Logs warning-level message with automatic context injection.
1640
+ * Check if entity exists in storage.
1533
1641
  *
1534
- * @param topic - Log topic/category
1535
- * @param args - Additional log arguments
1642
+ * @param entityId - Unique entity identifier
1643
+ * @returns Promise resolving to true if exists, false otherwise
1536
1644
  */
1537
- warn: (topic: string, ...args: any[]) => Promise<void>;
1645
+ hasValue(entityId: EntityId): Promise<boolean>;
1538
1646
  /**
1539
- * Sets custom logger implementation.
1647
+ * Write entity to storage with atomic file writes.
1540
1648
  *
1541
- * @param logger - Custom logger implementing ILogger interface
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
- setLogger: (logger: ILogger) => void;
1654
+ writeValue(entityId: EntityId, entity: Entity): Promise<void>;
1544
1655
  }
1545
-
1546
1656
  /**
1547
- * Client implementation for exchange data access.
1657
+ * Base class for file-based persistence with atomic writes.
1548
1658
  *
1549
1659
  * Features:
1550
- * - Historical candle fetching (backwards from execution context)
1551
- * - Future candle fetching (forwards for backtest)
1552
- * - VWAP calculation from last 5 1m candles
1553
- * - Price/quantity formatting for exchange
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 exchange = new ClientExchange({
1560
- * exchangeName: "binance",
1561
- * getCandles: async (symbol, interval, since, limit) => [...],
1562
- * formatPrice: async (symbol, price) => price.toFixed(2),
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
- * Retrieves a strategy schema by name.
2496
+ * Formats quantity with execution context.
2083
2497
  *
2084
- * @param key - Strategy name
2085
- * @returns Strategy schema configuration
2086
- * @throws Error if strategy name doesn't exist
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
- get: (key: StrategyName) => IStrategySchema;
2504
+ formatQuantity: (symbol: string, quantity: number, when: Date, backtest: boolean) => Promise<string>;
2089
2505
  }
2090
2506
 
2091
2507
  /**
2092
- * Service for managing frame schema registry.
2508
+ * Global service for strategy operations with execution context injection.
2093
2509
  *
2094
- * Uses ToolRegistry from functools-kit for type-safe schema storage.
2095
- * Frames are registered via addFrame() and retrieved by name.
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 FrameSchemaService {
2098
- readonly loggerService: LoggerService;
2099
- private _registry;
2515
+ declare class StrategyGlobalService {
2516
+ private readonly loggerService;
2517
+ private readonly strategyConnectionService;
2100
2518
  /**
2101
- * Registers a new frame schema.
2519
+ * Checks signal status at a specific timestamp.
2102
2520
  *
2103
- * @param key - Unique frame name
2104
- * @param value - Frame schema configuration
2105
- * @throws Error if frame name already exists
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
- register(key: FrameName, value: IFrameSchema): void;
2529
+ tick: (symbol: string, when: Date, backtest: boolean) => Promise<IStrategyTickResult>;
2108
2530
  /**
2109
- * Validates frame schema structure for required properties.
2531
+ * Runs fast backtest against candle array.
2110
2532
  *
2111
- * Performs shallow validation to ensure all required properties exist
2112
- * and have correct types before registration in the registry.
2533
+ * Wraps strategy backtest() with execution context containing symbol,
2534
+ * timestamp, and backtest mode flag.
2113
2535
  *
2114
- * @param frameSchema - Frame schema to validate
2115
- * @throws Error if frameName is missing or not a string
2116
- * @throws Error if interval is missing or not a valid FrameInterval
2117
- * @throws Error if startDate is missing or not a Date
2118
- * @throws Error if endDate is missing or not a Date
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
- private validateShallow;
2542
+ backtest: (symbol: string, candles: ICandleData[], when: Date, backtest: boolean) => Promise<IStrategyBacktestResult>;
2121
2543
  /**
2122
- * Overrides an existing frame schema with partial updates.
2544
+ * Stops the strategy from generating new signals.
2123
2545
  *
2124
- * @param key - Frame name to override
2125
- * @param value - Partial schema updates
2126
- * @throws Error if frame name doesn't exist
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
- override(key: FrameName, value: Partial<IFrameSchema>): IFrameSchema;
2552
+ stop: (strategyName: StrategyName) => Promise<void>;
2129
2553
  /**
2130
- * Retrieves a frame schema by name.
2554
+ * Clears the memoized ClientStrategy instance from cache.
2131
2555
  *
2132
- * @param key - Frame name
2133
- * @returns Frame schema configuration
2134
- * @throws Error if frame name doesn't exist
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
- get(key: FrameName): IFrameSchema;
2561
+ clear: (strategyName: StrategyName) => Promise<void>;
2137
2562
  }
2138
2563
 
2139
2564
  /**
2140
- * Private service for backtest orchestration using async generators.
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
- * Memory efficient: streams results without array accumulation.
2150
- * Supports early termination via break in consumer.
2567
+ * Wraps FrameConnectionService for timeframe generation.
2568
+ * Used internally by BacktestLogicPrivateService.
2151
2569
  */
2152
- declare class BacktestLogicPrivateService {
2570
+ declare class FrameGlobalService {
2153
2571
  private readonly loggerService;
2154
- private readonly strategyGlobalService;
2155
- private readonly exchangeGlobalService;
2156
- private readonly frameGlobalService;
2572
+ private readonly frameConnectionService;
2157
2573
  /**
2158
- * Runs backtest for a symbol, streaming closed signals as async generator.
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
- * @example
2164
- * ```typescript
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
- run(symbol: string): AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
2579
+ getTimeframe: (symbol: string) => Promise<Date[]>;
2172
2580
  }
2173
2581
 
2174
2582
  /**
2175
- * Private service for live trading orchestration using async generators.
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
- * Features:
2185
- * - Crash recovery via ClientStrategy.waitForInit()
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 LiveLogicPrivateService {
2191
- private readonly loggerService;
2192
- private readonly strategyGlobalService;
2588
+ declare class ExchangeSchemaService {
2589
+ readonly loggerService: LoggerService;
2590
+ private _registry;
2193
2591
  /**
2194
- * Runs live trading for a symbol, streaming results as async generator.
2592
+ * Registers a new exchange schema.
2195
2593
  *
2196
- * Infinite generator that yields opened and closed signals.
2197
- * Process can crash and restart - state will be recovered from disk.
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
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2200
- * @yields Opened and closed signal results
2602
+ * Performs shallow validation to ensure all required properties exist
2603
+ * and have correct types before registration in the registry.
2201
2604
  *
2202
- * @example
2203
- * ```typescript
2204
- * for await (const result of liveLogic.run("BTCUSDT")) {
2205
- * if (result.action === "opened") {
2206
- * console.log("New signal:", result.signal.id);
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
- run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
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
- * Runs backtest for a symbol with context propagation.
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 symbol - Trading pair symbol (e.g., "BTCUSDT")
2252
- * @param context - Execution context with strategy, exchange, and frame names
2253
- * @returns Async generator yielding closed signals with PNL
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
- run: (symbol: string, context: {
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
- * Runs live trading for a symbol with context propagation.
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 symbol - Trading pair symbol (e.g., "BTCUSDT")
2304
- * @param context - Execution context with strategy and exchange names
2305
- * @returns Infinite async generator yielding opened and closed signals
2624
+ * @param key - Exchange name
2625
+ * @returns Exchange schema configuration
2626
+ * @throws Error if exchange name doesn't exist
2306
2627
  */
2307
- run: (symbol: string, context: {
2308
- strategyName: string;
2309
- exchangeName: string;
2310
- }) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
2628
+ get: (key: ExchangeName) => IExchangeSchema;
2311
2629
  }
2312
2630
 
2313
2631
  /**
2314
- * Global service providing access to live trading functionality.
2632
+ * Service for managing strategy schema registry.
2315
2633
  *
2316
- * Simple wrapper around LiveLogicPublicService for dependency injection.
2317
- * Used by public API exports.
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 LiveGlobalService {
2320
- private readonly loggerService;
2321
- private readonly liveLogicPublicService;
2322
- private readonly strategyValidationService;
2323
- private readonly exchangeValidationService;
2637
+ declare class StrategySchemaService {
2638
+ readonly loggerService: LoggerService;
2639
+ private _registry;
2324
2640
  /**
2325
- * Runs live trading for a symbol with context propagation.
2641
+ * Registers a new strategy schema.
2326
2642
  *
2327
- * Infinite async generator with crash recovery support.
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
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2330
- * @param context - Execution context with strategy and exchange names
2331
- * @returns Infinite async generator yielding opened and closed signals
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
- run: (symbol: string, context: {
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
- * Runs backtest for a symbol with context propagation.
2661
+ * Overrides an existing strategy schema with partial updates.
2353
2662
  *
2354
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2355
- * @param context - Execution context with strategy, exchange, and frame names
2356
- * @returns Async generator yielding closed signals with PNL
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
- run: (symbol: string, context: {
2359
- strategyName: string;
2360
- exchangeName: string;
2361
- frameName: string;
2362
- }) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
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 generating and saving backtest markdown reports.
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
- * // After backtest, generate and save report
2389
- * await service.saveReport("my-strategy");
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 BacktestMarkdownService {
2393
- /** Logger service for debug output */
2394
- private readonly loggerService;
2685
+ declare class FrameSchemaService {
2686
+ readonly loggerService: LoggerService;
2687
+ private _registry;
2395
2688
  /**
2396
- * Memoized function to get or create ReportStorage for a strategy.
2397
- * Each strategy gets its own isolated storage instance.
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
- private getStorage;
2695
+ register(key: FrameName, value: IFrameSchema): void;
2400
2696
  /**
2401
- * Processes tick events and accumulates closed signals.
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
- * @example
2409
- * ```typescript
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
- * callbacks: {
2413
- * onTick: (symbol, result, backtest) => {
2414
- * service.tick(result);
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 tick;
2708
+ private validateShallow;
2420
2709
  /**
2421
- * Generates markdown report with all closed signals for a strategy.
2422
- * Delegates to ReportStorage.generateReport().
2710
+ * Overrides an existing frame schema with partial updates.
2423
2711
  *
2424
- * @param strategyName - Strategy name to generate report for
2425
- * @returns Markdown formatted report string with table of all closed signals
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
- * @example
2428
- * ```typescript
2429
- * const service = new BacktestMarkdownService();
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
- getReport: (strategyName: StrategyName) => Promise<string>;
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
- * Saves strategy report to disk.
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 strategyName - Strategy name to save report for
2441
- * @param path - Directory path to save report (default: "./logs/backtest")
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 service = new BacktestMarkdownService();
2446
- *
2447
- * // Save to default path: ./logs/backtest/my-strategy.md
2448
- * await service.dump("my-strategy");
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
- dump: (strategyName: StrategyName, path?: string) => Promise<void>;
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
- * Clears accumulated signal data from storage.
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
- * // Clear specific strategy data
2467
- * await service.clear("my-strategy");
2785
+ * Infinite generator that yields opened and closed signals.
2786
+ * Process can crash and restart - state will be recovered from disk.
2468
2787
  *
2469
- * // Clear all strategies' data
2470
- * await service.clear();
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 service = new BacktestMarkdownService();
2482
- * await service.init(); // Subscribe to backtest events
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
- protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
2804
+ run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
2486
2805
  }
2487
2806
 
2488
2807
  /**
2489
- * Service for generating and saving live trading markdown reports.
2808
+ * Public service for backtest orchestration with context management.
2490
2809
  *
2491
- * Features:
2492
- * - Listens to all signal events via onTick callback
2493
- * - Accumulates all events (idle, opened, active, closed) per strategy
2494
- * - Generates markdown tables with detailed event information
2495
- * - Provides trading statistics (win rate, average PNL)
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 service = new LiveMarkdownService();
2818
+ * const backtestLogicPublicService = inject(TYPES.backtestLogicPublicService);
2501
2819
  *
2502
- * // Add to strategy callbacks
2503
- * addStrategy({
2820
+ * for await (const result of backtestLogicPublicService.run("BTCUSDT", {
2504
2821
  * strategyName: "my-strategy",
2505
- * callbacks: {
2506
- * onTick: (symbol, result, backtest) => {
2507
- * if (!backtest) {
2508
- * service.tick(result);
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 LiveMarkdownService {
2519
- /** Logger service for debug output */
2831
+ declare class BacktestLogicPublicService {
2520
2832
  private readonly loggerService;
2833
+ private readonly backtestLogicPrivateService;
2521
2834
  /**
2522
- * Memoized function to get or create ReportStorage for a strategy.
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
- * @param strategyName - Strategy name to generate report for
2553
- * @returns Markdown formatted report string with table of all events
2837
+ * Streams closed signals as async generator. Context is automatically
2838
+ * injected into all framework functions called during iteration.
2554
2839
  *
2555
- * @example
2556
- * ```typescript
2557
- * const service = new LiveMarkdownService();
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
- getReport: (strategyName: StrategyName) => Promise<string>;
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
- * Saves strategy report to disk.
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
- * // Save to default path: ./logs/live/my-strategy.md
2576
- * await service.dump("my-strategy");
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
- * // Save to custom path: ./custom/path/my-strategy.md
2579
- * await service.dump("my-strategy", "./custom/path");
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
- dump: (strategyName: StrategyName, path?: string) => Promise<void>;
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
- * Clears accumulated event data from storage.
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
- * // Clear specific strategy data
2595
- * await service.clear("my-strategy");
2916
+ * Infinite async generator with crash recovery support.
2596
2917
  *
2597
- * // Clear all strategies' data
2598
- * await service.clear();
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
- clear: (strategyName?: StrategyName) => Promise<void>;
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
- * Initializes the service by subscribing to live signal events.
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
- * @example
2608
- * ```typescript
2609
- * const service = new LiveMarkdownService();
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
- protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
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 };