backtest-kit 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +707 -153
- package/build/index.cjs +2906 -192
- package/build/index.mjs +2901 -188
- package/package.json +2 -9
- package/types.d.ts +2205 -60
package/types.d.ts
CHANGED
|
@@ -22,15 +22,65 @@ interface ILogger {
|
|
|
22
22
|
* Used to record informational updates, such as successful completions, policy validations, or history commits, providing a high-level overview of system activity without excessive detail.
|
|
23
23
|
*/
|
|
24
24
|
info(topic: string, ...args: any[]): void;
|
|
25
|
+
/**
|
|
26
|
+
* Logs a warning-level message.
|
|
27
|
+
* Used to record potentially problematic situations that don't prevent execution but may require attention, such as missing data, unexpected conditions, or deprecated usage.
|
|
28
|
+
*/
|
|
29
|
+
warn(topic: string, ...args: any[]): void;
|
|
25
30
|
}
|
|
26
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Sets custom logger implementation for the framework.
|
|
34
|
+
*
|
|
35
|
+
* All log messages from internal services will be forwarded to the provided logger
|
|
36
|
+
* with automatic context injection (strategyName, exchangeName, symbol, etc.).
|
|
37
|
+
*
|
|
38
|
+
* @param logger - Custom logger implementing ILogger interface
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* setLogger({
|
|
43
|
+
* log: (topic, ...args) => console.log(topic, args),
|
|
44
|
+
* debug: (topic, ...args) => console.debug(topic, args),
|
|
45
|
+
* info: (topic, ...args) => console.info(topic, args),
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
27
49
|
declare function setLogger(logger: ILogger): Promise<void>;
|
|
28
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Execution context containing runtime parameters for strategy/exchange operations.
|
|
53
|
+
*
|
|
54
|
+
* Propagated through ExecutionContextService to provide implicit context
|
|
55
|
+
* for getCandles(), tick(), backtest() and other operations.
|
|
56
|
+
*/
|
|
29
57
|
interface IExecutionContext {
|
|
58
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
30
59
|
symbol: string;
|
|
60
|
+
/** Current timestamp for operation */
|
|
31
61
|
when: Date;
|
|
62
|
+
/** Whether running in backtest mode (true) or live mode (false) */
|
|
32
63
|
backtest: boolean;
|
|
33
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Scoped service for execution context propagation.
|
|
67
|
+
*
|
|
68
|
+
* Uses di-scoped for implicit context passing without explicit parameters.
|
|
69
|
+
* Context includes symbol, when (timestamp), and backtest flag.
|
|
70
|
+
*
|
|
71
|
+
* Used by GlobalServices to inject context into operations.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* ExecutionContextService.runInContext(
|
|
76
|
+
* async () => {
|
|
77
|
+
* // Inside this callback, context is automatically available
|
|
78
|
+
* return await someOperation();
|
|
79
|
+
* },
|
|
80
|
+
* { symbol: "BTCUSDT", when: new Date(), backtest: true }
|
|
81
|
+
* );
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
34
84
|
declare const ExecutionContextService: (new () => {
|
|
35
85
|
readonly context: IExecutionContext;
|
|
36
86
|
}) & Omit<{
|
|
@@ -38,351 +88,2022 @@ declare const ExecutionContextService: (new () => {
|
|
|
38
88
|
readonly context: IExecutionContext;
|
|
39
89
|
};
|
|
40
90
|
}, "prototype"> & di_scoped.IScopedClassRun<[context: IExecutionContext]>;
|
|
91
|
+
/**
|
|
92
|
+
* Type helper for ExecutionContextService instance.
|
|
93
|
+
* Used for dependency injection type annotations.
|
|
94
|
+
*/
|
|
41
95
|
type TExecutionContextService = InstanceType<typeof ExecutionContextService>;
|
|
42
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Candle time interval for fetching historical data.
|
|
99
|
+
*/
|
|
43
100
|
type CandleInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h";
|
|
101
|
+
/**
|
|
102
|
+
* Single OHLCV candle data point.
|
|
103
|
+
* Used for VWAP calculation and backtesting.
|
|
104
|
+
*/
|
|
44
105
|
interface ICandleData {
|
|
106
|
+
/** Unix timestamp in milliseconds when candle opened */
|
|
45
107
|
timestamp: number;
|
|
108
|
+
/** Opening price at candle start */
|
|
46
109
|
open: number;
|
|
110
|
+
/** Highest price during candle period */
|
|
47
111
|
high: number;
|
|
112
|
+
/** Lowest price during candle period */
|
|
48
113
|
low: number;
|
|
114
|
+
/** Closing price at candle end */
|
|
49
115
|
close: number;
|
|
116
|
+
/** Trading volume during candle period */
|
|
50
117
|
volume: number;
|
|
51
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Exchange parameters passed to ClientExchange constructor.
|
|
121
|
+
* Combines schema with runtime dependencies.
|
|
122
|
+
*/
|
|
52
123
|
interface IExchangeParams extends IExchangeSchema {
|
|
124
|
+
/** Logger service for debug output */
|
|
53
125
|
logger: ILogger;
|
|
126
|
+
/** Execution context service (symbol, when, backtest flag) */
|
|
54
127
|
execution: TExecutionContextService;
|
|
55
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Optional callbacks for exchange data events.
|
|
131
|
+
*/
|
|
56
132
|
interface IExchangeCallbacks {
|
|
133
|
+
/** Called when candle data is fetched */
|
|
57
134
|
onCandleData: (symbol: string, interval: CandleInterval, since: Date, limit: number, data: ICandleData[]) => void;
|
|
58
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Exchange schema registered via addExchange().
|
|
138
|
+
* Defines candle data source and formatting logic.
|
|
139
|
+
*/
|
|
59
140
|
interface IExchangeSchema {
|
|
141
|
+
/** Unique exchange identifier for registration */
|
|
60
142
|
exchangeName: ExchangeName;
|
|
143
|
+
/**
|
|
144
|
+
* Fetch candles from data source (API or database).
|
|
145
|
+
*
|
|
146
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
147
|
+
* @param interval - Candle time interval (e.g., "1m", "1h")
|
|
148
|
+
* @param since - Start date for candle fetching
|
|
149
|
+
* @param limit - Maximum number of candles to fetch
|
|
150
|
+
* @returns Promise resolving to array of OHLCV candle data
|
|
151
|
+
*/
|
|
61
152
|
getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number) => Promise<ICandleData[]>;
|
|
153
|
+
/**
|
|
154
|
+
* Format quantity according to exchange precision rules.
|
|
155
|
+
*
|
|
156
|
+
* @param symbol - Trading pair symbol
|
|
157
|
+
* @param quantity - Raw quantity value
|
|
158
|
+
* @returns Promise resolving to formatted quantity string
|
|
159
|
+
*/
|
|
62
160
|
formatQuantity: (symbol: string, quantity: number) => Promise<string>;
|
|
161
|
+
/**
|
|
162
|
+
* Format price according to exchange precision rules.
|
|
163
|
+
*
|
|
164
|
+
* @param symbol - Trading pair symbol
|
|
165
|
+
* @param price - Raw price value
|
|
166
|
+
* @returns Promise resolving to formatted price string
|
|
167
|
+
*/
|
|
63
168
|
formatPrice: (symbol: string, price: number) => Promise<string>;
|
|
169
|
+
/** Optional lifecycle event callbacks (onCandleData) */
|
|
64
170
|
callbacks?: Partial<IExchangeCallbacks>;
|
|
65
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Exchange interface implemented by ClientExchange.
|
|
174
|
+
* Provides candle data access and VWAP calculation.
|
|
175
|
+
*/
|
|
66
176
|
interface IExchange {
|
|
177
|
+
/**
|
|
178
|
+
* Fetch historical candles backwards from execution context time.
|
|
179
|
+
*
|
|
180
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
181
|
+
* @param interval - Candle time interval (e.g., "1m", "1h")
|
|
182
|
+
* @param limit - Maximum number of candles to fetch
|
|
183
|
+
* @returns Promise resolving to array of candle data
|
|
184
|
+
*/
|
|
67
185
|
getCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
|
|
186
|
+
/**
|
|
187
|
+
* Fetch future candles forward from execution context time (for backtest).
|
|
188
|
+
*
|
|
189
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
190
|
+
* @param interval - Candle time interval (e.g., "1m", "1h")
|
|
191
|
+
* @param limit - Maximum number of candles to fetch
|
|
192
|
+
* @returns Promise resolving to array of candle data
|
|
193
|
+
*/
|
|
68
194
|
getNextCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
|
|
195
|
+
/**
|
|
196
|
+
* Format quantity for exchange precision.
|
|
197
|
+
*
|
|
198
|
+
* @param symbol - Trading pair symbol
|
|
199
|
+
* @param quantity - Raw quantity value
|
|
200
|
+
* @returns Promise resolving to formatted quantity string
|
|
201
|
+
*/
|
|
69
202
|
formatQuantity: (symbol: string, quantity: number) => Promise<string>;
|
|
203
|
+
/**
|
|
204
|
+
* Format price for exchange precision.
|
|
205
|
+
*
|
|
206
|
+
* @param symbol - Trading pair symbol
|
|
207
|
+
* @param price - Raw price value
|
|
208
|
+
* @returns Promise resolving to formatted price string
|
|
209
|
+
*/
|
|
70
210
|
formatPrice: (symbol: string, price: number) => Promise<string>;
|
|
211
|
+
/**
|
|
212
|
+
* Calculate VWAP from last 5 1-minute candles.
|
|
213
|
+
*
|
|
214
|
+
* Formula: VWAP = Σ(Typical Price × Volume) / Σ(Volume)
|
|
215
|
+
* where Typical Price = (High + Low + Close) / 3
|
|
216
|
+
*
|
|
217
|
+
* @param symbol - Trading pair symbol
|
|
218
|
+
* @returns Promise resolving to volume-weighted average price
|
|
219
|
+
*/
|
|
71
220
|
getAveragePrice: (symbol: string) => Promise<number>;
|
|
72
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Unique exchange identifier.
|
|
224
|
+
*/
|
|
73
225
|
type ExchangeName = string;
|
|
74
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Timeframe interval for backtest period generation.
|
|
229
|
+
* Determines the granularity of timestamps in the generated timeframe array.
|
|
230
|
+
*
|
|
231
|
+
* Minutes: 1m, 3m, 5m, 15m, 30m
|
|
232
|
+
* Hours: 1h, 2h, 4h, 6h, 8h, 12h
|
|
233
|
+
* Days: 1d, 3d
|
|
234
|
+
*/
|
|
235
|
+
type FrameInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h" | "12h" | "1d" | "3d";
|
|
236
|
+
/**
|
|
237
|
+
* Frame parameters passed to ClientFrame constructor.
|
|
238
|
+
* Extends IFrameSchema with logger instance for internal logging.
|
|
239
|
+
*/
|
|
240
|
+
interface IFrameParams extends IFrameSchema {
|
|
241
|
+
/** Logger service for debug output */
|
|
242
|
+
logger: ILogger;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Callbacks for frame lifecycle events.
|
|
246
|
+
*/
|
|
247
|
+
interface IFrameCallbacks {
|
|
248
|
+
/**
|
|
249
|
+
* Called after timeframe array generation.
|
|
250
|
+
* Useful for logging or validating the generated timeframes.
|
|
251
|
+
*
|
|
252
|
+
* @param timeframe - Array of Date objects representing tick timestamps
|
|
253
|
+
* @param startDate - Start of the backtest period
|
|
254
|
+
* @param endDate - End of the backtest period
|
|
255
|
+
* @param interval - Interval used for generation
|
|
256
|
+
*/
|
|
257
|
+
onTimeframe: (timeframe: Date[], startDate: Date, endDate: Date, interval: FrameInterval) => void;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Frame schema registered via addFrame().
|
|
261
|
+
* Defines backtest period and interval for timestamp generation.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* addFrame({
|
|
266
|
+
* frameName: "1d-backtest",
|
|
267
|
+
* interval: "1m",
|
|
268
|
+
* startDate: new Date("2024-01-01T00:00:00Z"),
|
|
269
|
+
* endDate: new Date("2024-01-02T00:00:00Z"),
|
|
270
|
+
* callbacks: {
|
|
271
|
+
* onTimeframe: (timeframe, startDate, endDate, interval) => {
|
|
272
|
+
* console.log(`Generated ${timeframe.length} timestamps`);
|
|
273
|
+
* },
|
|
274
|
+
* },
|
|
275
|
+
* });
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
interface IFrameSchema {
|
|
279
|
+
/** Unique identifier for this frame */
|
|
280
|
+
frameName: FrameName;
|
|
281
|
+
/** Interval for timestamp generation */
|
|
282
|
+
interval: FrameInterval;
|
|
283
|
+
/** Start of backtest period (inclusive) */
|
|
284
|
+
startDate: Date;
|
|
285
|
+
/** End of backtest period (inclusive) */
|
|
286
|
+
endDate: Date;
|
|
287
|
+
/** Optional lifecycle callbacks */
|
|
288
|
+
callbacks?: Partial<IFrameCallbacks>;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Frame interface for timeframe generation.
|
|
292
|
+
* Used internally by backtest orchestration.
|
|
293
|
+
*/
|
|
294
|
+
interface IFrame {
|
|
295
|
+
/**
|
|
296
|
+
* Generates array of timestamps for backtest iteration.
|
|
297
|
+
* Timestamps are spaced according to the configured interval.
|
|
298
|
+
*
|
|
299
|
+
* @param symbol - Trading pair symbol (unused, for API consistency)
|
|
300
|
+
* @returns Promise resolving to array of Date objects
|
|
301
|
+
*/
|
|
302
|
+
getTimeframe: (symbol: string) => Promise<Date[]>;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Unique identifier for a frame schema.
|
|
306
|
+
* Used to retrieve frame instances via dependency injection.
|
|
307
|
+
*/
|
|
308
|
+
type FrameName = string;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Method context containing schema names for operation routing.
|
|
312
|
+
*
|
|
313
|
+
* Propagated through MethodContextService to provide implicit context
|
|
314
|
+
* for retrieving correct strategy/exchange/frame instances.
|
|
315
|
+
*/
|
|
316
|
+
interface IMethodContext {
|
|
317
|
+
/** Name of exchange schema to use */
|
|
318
|
+
exchangeName: ExchangeName;
|
|
319
|
+
/** Name of strategy schema to use */
|
|
320
|
+
strategyName: StrategyName;
|
|
321
|
+
/** Name of frame schema to use (empty string for live mode) */
|
|
322
|
+
frameName: FrameName;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Scoped service for method context propagation.
|
|
326
|
+
*
|
|
327
|
+
* Uses di-scoped for implicit context passing without explicit parameters.
|
|
328
|
+
* Context includes strategyName, exchangeName, and frameName.
|
|
329
|
+
*
|
|
330
|
+
* Used by PublicServices to inject schema names into ConnectionServices.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* MethodContextService.runAsyncIterator(
|
|
335
|
+
* backtestGenerator,
|
|
336
|
+
* {
|
|
337
|
+
* strategyName: "my-strategy",
|
|
338
|
+
* exchangeName: "my-exchange",
|
|
339
|
+
* frameName: "1d-backtest"
|
|
340
|
+
* }
|
|
341
|
+
* );
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
declare const MethodContextService: (new () => {
|
|
345
|
+
readonly context: IMethodContext;
|
|
346
|
+
}) & Omit<{
|
|
347
|
+
new (context: IMethodContext): {
|
|
348
|
+
readonly context: IMethodContext;
|
|
349
|
+
};
|
|
350
|
+
}, "prototype"> & di_scoped.IScopedClassRun<[context: IMethodContext]>;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Signal generation interval for throttling.
|
|
354
|
+
* Enforces minimum time between getSignal calls.
|
|
355
|
+
*/
|
|
75
356
|
type SignalInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h";
|
|
357
|
+
/**
|
|
358
|
+
* Signal data transfer object returned by getSignal.
|
|
359
|
+
* Will be validated and augmented with auto-generated id.
|
|
360
|
+
*/
|
|
76
361
|
interface ISignalDto {
|
|
362
|
+
/** Optional signal ID (auto-generated if not provided) */
|
|
77
363
|
id?: string;
|
|
364
|
+
/** Trade direction: "long" (buy) or "short" (sell) */
|
|
78
365
|
position: "long" | "short";
|
|
79
|
-
|
|
366
|
+
/** Human-readable description of signal reason */
|
|
367
|
+
note?: string;
|
|
368
|
+
/** Entry price for the position */
|
|
80
369
|
priceOpen: number;
|
|
370
|
+
/** Take profit target price (must be > priceOpen for long, < priceOpen for short) */
|
|
81
371
|
priceTakeProfit: number;
|
|
372
|
+
/** Stop loss exit price (must be < priceOpen for long, > priceOpen for short) */
|
|
82
373
|
priceStopLoss: number;
|
|
374
|
+
/** Expected duration in minutes before time_expired */
|
|
83
375
|
minuteEstimatedTime: number;
|
|
84
|
-
timestamp: number;
|
|
85
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Complete signal with auto-generated id.
|
|
379
|
+
* Used throughout the system after validation.
|
|
380
|
+
*/
|
|
86
381
|
interface ISignalRow extends ISignalDto {
|
|
382
|
+
/** Unique signal identifier (UUID v4 auto-generated) */
|
|
87
383
|
id: string;
|
|
384
|
+
/** Unique exchange identifier for execution */
|
|
385
|
+
exchangeName: ExchangeName;
|
|
386
|
+
/** Unique strategy identifier for execution */
|
|
387
|
+
strategyName: StrategyName;
|
|
388
|
+
/** Signal creation timestamp in milliseconds */
|
|
389
|
+
timestamp: number;
|
|
390
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
391
|
+
symbol: string;
|
|
88
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Optional lifecycle callbacks for signal events.
|
|
395
|
+
* Called when signals are opened, active, idle, or closed.
|
|
396
|
+
*/
|
|
89
397
|
interface IStrategyCallbacks {
|
|
90
|
-
|
|
91
|
-
|
|
398
|
+
/** Called on every tick with the result */
|
|
399
|
+
onTick: (symbol: string, result: IStrategyTickResult, backtest: boolean) => void;
|
|
400
|
+
/** Called when new signal is opened (after validation) */
|
|
401
|
+
onOpen: (symbol: string, data: ISignalRow, currentPrice: number, backtest: boolean) => void;
|
|
402
|
+
/** Called when signal is being monitored (active state) */
|
|
403
|
+
onActive: (symbol: string, data: ISignalRow, currentPrice: number, backtest: boolean) => void;
|
|
404
|
+
/** Called when no active signal exists (idle state) */
|
|
405
|
+
onIdle: (symbol: string, currentPrice: number, backtest: boolean) => void;
|
|
406
|
+
/** Called when signal is closed with final price */
|
|
407
|
+
onClose: (symbol: string, data: ISignalRow, priceClose: number, backtest: boolean) => void;
|
|
92
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Strategy schema registered via addStrategy().
|
|
411
|
+
* Defines signal generation logic and configuration.
|
|
412
|
+
*/
|
|
93
413
|
interface IStrategySchema {
|
|
414
|
+
/** Unique strategy identifier for registration */
|
|
94
415
|
strategyName: StrategyName;
|
|
416
|
+
/** Minimum interval between getSignal calls (throttling) */
|
|
95
417
|
interval: SignalInterval;
|
|
418
|
+
/** Signal generation function (returns null if no signal, validated DTO if signal) */
|
|
96
419
|
getSignal: (symbol: string) => Promise<ISignalDto | null>;
|
|
420
|
+
/** Optional lifecycle event callbacks (onOpen, onClose) */
|
|
97
421
|
callbacks?: Partial<IStrategyCallbacks>;
|
|
98
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Reason why signal was closed.
|
|
425
|
+
* Used in discriminated union for type-safe handling.
|
|
426
|
+
*/
|
|
99
427
|
type StrategyCloseReason = "time_expired" | "take_profit" | "stop_loss";
|
|
428
|
+
/**
|
|
429
|
+
* Profit and loss calculation result.
|
|
430
|
+
* Includes adjusted prices with fees (0.1%) and slippage (0.1%).
|
|
431
|
+
*/
|
|
100
432
|
interface IStrategyPnL {
|
|
433
|
+
/** Profit/loss as percentage (e.g., 1.5 for +1.5%, -2.3 for -2.3%) */
|
|
101
434
|
pnlPercentage: number;
|
|
435
|
+
/** Entry price adjusted with slippage and fees */
|
|
102
436
|
priceOpen: number;
|
|
437
|
+
/** Exit price adjusted with slippage and fees */
|
|
103
438
|
priceClose: number;
|
|
104
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Tick result: no active signal, idle state.
|
|
442
|
+
*/
|
|
105
443
|
interface IStrategyTickResultIdle {
|
|
444
|
+
/** Discriminator for type-safe union */
|
|
106
445
|
action: "idle";
|
|
446
|
+
/** No signal in idle state */
|
|
107
447
|
signal: null;
|
|
448
|
+
/** Strategy name for tracking idle events */
|
|
449
|
+
strategyName: StrategyName;
|
|
450
|
+
/** Exchange name for tracking idle events */
|
|
451
|
+
exchangeName: ExchangeName;
|
|
452
|
+
/** Current VWAP price during idle state */
|
|
453
|
+
currentPrice: number;
|
|
108
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Tick result: new signal just created.
|
|
457
|
+
* Triggered after getSignal validation and persistence.
|
|
458
|
+
*/
|
|
109
459
|
interface IStrategyTickResultOpened {
|
|
460
|
+
/** Discriminator for type-safe union */
|
|
110
461
|
action: "opened";
|
|
462
|
+
/** Newly created and validated signal with generated ID */
|
|
111
463
|
signal: ISignalRow;
|
|
464
|
+
/** Strategy name for tracking */
|
|
465
|
+
strategyName: StrategyName;
|
|
466
|
+
/** Exchange name for tracking */
|
|
467
|
+
exchangeName: ExchangeName;
|
|
468
|
+
/** Current VWAP price at signal open */
|
|
469
|
+
currentPrice: number;
|
|
112
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Tick result: signal is being monitored.
|
|
473
|
+
* Waiting for TP/SL or time expiration.
|
|
474
|
+
*/
|
|
113
475
|
interface IStrategyTickResultActive {
|
|
476
|
+
/** Discriminator for type-safe union */
|
|
114
477
|
action: "active";
|
|
478
|
+
/** Currently monitored signal */
|
|
115
479
|
signal: ISignalRow;
|
|
480
|
+
/** Current VWAP price for monitoring */
|
|
116
481
|
currentPrice: number;
|
|
482
|
+
/** Strategy name for tracking */
|
|
483
|
+
strategyName: StrategyName;
|
|
484
|
+
/** Exchange name for tracking */
|
|
485
|
+
exchangeName: ExchangeName;
|
|
117
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Tick result: signal closed with PNL.
|
|
489
|
+
* Final state with close reason and profit/loss calculation.
|
|
490
|
+
*/
|
|
118
491
|
interface IStrategyTickResultClosed {
|
|
492
|
+
/** Discriminator for type-safe union */
|
|
119
493
|
action: "closed";
|
|
494
|
+
/** Completed signal with original parameters */
|
|
120
495
|
signal: ISignalRow;
|
|
496
|
+
/** Final VWAP price at close */
|
|
121
497
|
currentPrice: number;
|
|
498
|
+
/** Why signal closed (time_expired | take_profit | stop_loss) */
|
|
122
499
|
closeReason: StrategyCloseReason;
|
|
500
|
+
/** Unix timestamp in milliseconds when signal closed */
|
|
123
501
|
closeTimestamp: number;
|
|
502
|
+
/** Profit/loss calculation with fees and slippage */
|
|
124
503
|
pnl: IStrategyPnL;
|
|
504
|
+
/** Strategy name for tracking */
|
|
505
|
+
strategyName: StrategyName;
|
|
506
|
+
/** Exchange name for tracking */
|
|
507
|
+
exchangeName: ExchangeName;
|
|
125
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Discriminated union of all tick results.
|
|
511
|
+
* Use type guards: `result.action === "closed"` for type safety.
|
|
512
|
+
*/
|
|
126
513
|
type IStrategyTickResult = IStrategyTickResultIdle | IStrategyTickResultOpened | IStrategyTickResultActive | IStrategyTickResultClosed;
|
|
514
|
+
/**
|
|
515
|
+
* Backtest always returns closed result (TP/SL or time_expired).
|
|
516
|
+
*/
|
|
127
517
|
type IStrategyBacktestResult = IStrategyTickResultClosed;
|
|
518
|
+
/**
|
|
519
|
+
* Strategy interface implemented by ClientStrategy.
|
|
520
|
+
* Defines core strategy execution methods.
|
|
521
|
+
*/
|
|
128
522
|
interface IStrategy {
|
|
523
|
+
/**
|
|
524
|
+
* Single tick of strategy execution with VWAP monitoring.
|
|
525
|
+
* Checks for signal generation (throttled) and TP/SL conditions.
|
|
526
|
+
*
|
|
527
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
528
|
+
* @returns Promise resolving to tick result (idle | opened | active | closed)
|
|
529
|
+
*/
|
|
129
530
|
tick: (symbol: string) => Promise<IStrategyTickResult>;
|
|
531
|
+
/**
|
|
532
|
+
* Fast backtest using historical candles.
|
|
533
|
+
* Iterates through candles, calculates VWAP, checks TP/SL on each candle.
|
|
534
|
+
*
|
|
535
|
+
* @param candles - Array of historical candle data
|
|
536
|
+
* @returns Promise resolving to closed result (always completes signal)
|
|
537
|
+
*/
|
|
130
538
|
backtest: (candles: ICandleData[]) => Promise<IStrategyBacktestResult>;
|
|
131
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Unique strategy identifier.
|
|
542
|
+
*/
|
|
132
543
|
type StrategyName = string;
|
|
133
544
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
545
|
+
/**
|
|
546
|
+
* Registers a trading strategy in the framework.
|
|
547
|
+
*
|
|
548
|
+
* The strategy will be validated for:
|
|
549
|
+
* - Signal validation (prices, TP/SL logic, timestamps)
|
|
550
|
+
* - Interval throttling (prevents signal spam)
|
|
551
|
+
* - Crash-safe persistence in live mode
|
|
552
|
+
*
|
|
553
|
+
* @param strategySchema - Strategy configuration object
|
|
554
|
+
* @param strategySchema.strategyName - Unique strategy identifier
|
|
555
|
+
* @param strategySchema.interval - Signal generation interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h")
|
|
556
|
+
* @param strategySchema.getSignal - Async function that generates trading signals
|
|
557
|
+
* @param strategySchema.callbacks - Optional lifecycle callbacks (onOpen, onClose)
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```typescript
|
|
561
|
+
* addStrategy({
|
|
562
|
+
* strategyName: "my-strategy",
|
|
563
|
+
* interval: "5m",
|
|
564
|
+
* getSignal: async (symbol) => ({
|
|
565
|
+
* position: "long",
|
|
566
|
+
* priceOpen: 50000,
|
|
567
|
+
* priceTakeProfit: 51000,
|
|
568
|
+
* priceStopLoss: 49000,
|
|
569
|
+
* minuteEstimatedTime: 60,
|
|
570
|
+
* timestamp: Date.now(),
|
|
571
|
+
* }),
|
|
572
|
+
* callbacks: {
|
|
573
|
+
* onOpen: (backtest, symbol, signal) => console.log("Signal opened"),
|
|
574
|
+
* onClose: (backtest, symbol, priceClose, signal) => console.log("Signal closed"),
|
|
575
|
+
* },
|
|
576
|
+
* });
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
153
579
|
declare function addStrategy(strategySchema: IStrategySchema): void;
|
|
580
|
+
/**
|
|
581
|
+
* Registers an exchange data source in the framework.
|
|
582
|
+
*
|
|
583
|
+
* The exchange provides:
|
|
584
|
+
* - Historical candle data via getCandles
|
|
585
|
+
* - Price/quantity formatting for the exchange
|
|
586
|
+
* - VWAP calculation from last 5 1m candles
|
|
587
|
+
*
|
|
588
|
+
* @param exchangeSchema - Exchange configuration object
|
|
589
|
+
* @param exchangeSchema.exchangeName - Unique exchange identifier
|
|
590
|
+
* @param exchangeSchema.getCandles - Async function to fetch candle data
|
|
591
|
+
* @param exchangeSchema.formatPrice - Async function to format prices
|
|
592
|
+
* @param exchangeSchema.formatQuantity - Async function to format quantities
|
|
593
|
+
* @param exchangeSchema.callbacks - Optional callback for candle data events
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```typescript
|
|
597
|
+
* addExchange({
|
|
598
|
+
* exchangeName: "binance",
|
|
599
|
+
* getCandles: async (symbol, interval, since, limit) => {
|
|
600
|
+
* // Fetch from Binance API or database
|
|
601
|
+
* return [{
|
|
602
|
+
* timestamp: Date.now(),
|
|
603
|
+
* open: 50000,
|
|
604
|
+
* high: 51000,
|
|
605
|
+
* low: 49000,
|
|
606
|
+
* close: 50500,
|
|
607
|
+
* volume: 1000,
|
|
608
|
+
* }];
|
|
609
|
+
* },
|
|
610
|
+
* formatPrice: async (symbol, price) => price.toFixed(2),
|
|
611
|
+
* formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
|
|
612
|
+
* });
|
|
613
|
+
* ```
|
|
614
|
+
*/
|
|
154
615
|
declare function addExchange(exchangeSchema: IExchangeSchema): void;
|
|
616
|
+
/**
|
|
617
|
+
* Registers a timeframe generator for backtesting.
|
|
618
|
+
*
|
|
619
|
+
* The frame defines:
|
|
620
|
+
* - Start and end dates for backtest period
|
|
621
|
+
* - Interval for timeframe generation
|
|
622
|
+
* - Callback for timeframe generation events
|
|
623
|
+
*
|
|
624
|
+
* @param frameSchema - Frame configuration object
|
|
625
|
+
* @param frameSchema.frameName - Unique frame identifier
|
|
626
|
+
* @param frameSchema.interval - Timeframe interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h" | "12h" | "1d" | "3d")
|
|
627
|
+
* @param frameSchema.startDate - Start date for timeframe generation
|
|
628
|
+
* @param frameSchema.endDate - End date for timeframe generation
|
|
629
|
+
* @param frameSchema.callbacks - Optional callback for timeframe events
|
|
630
|
+
*
|
|
631
|
+
* @example
|
|
632
|
+
* ```typescript
|
|
633
|
+
* addFrame({
|
|
634
|
+
* frameName: "1d-backtest",
|
|
635
|
+
* interval: "1m",
|
|
636
|
+
* startDate: new Date("2024-01-01T00:00:00Z"),
|
|
637
|
+
* endDate: new Date("2024-01-02T00:00:00Z"),
|
|
638
|
+
* callbacks: {
|
|
639
|
+
* onTimeframe: (timeframe, startDate, endDate, interval) => {
|
|
640
|
+
* console.log(`Generated ${timeframe.length} timeframes`);
|
|
641
|
+
* },
|
|
642
|
+
* },
|
|
643
|
+
* });
|
|
644
|
+
* ```
|
|
645
|
+
*/
|
|
155
646
|
declare function addFrame(frameSchema: IFrameSchema): void;
|
|
156
647
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
648
|
+
/**
|
|
649
|
+
* Subscribes to all signal events with queued async processing.
|
|
650
|
+
*
|
|
651
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
652
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
653
|
+
*
|
|
654
|
+
* @param fn - Callback function to handle signal events (idle, opened, active, closed)
|
|
655
|
+
* @returns Unsubscribe function to stop listening
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```typescript
|
|
659
|
+
* import { listenSignal } from "./function/event";
|
|
660
|
+
*
|
|
661
|
+
* const unsubscribe = listenSignal((event) => {
|
|
662
|
+
* if (event.action === "opened") {
|
|
663
|
+
* console.log("New signal opened:", event.signal);
|
|
664
|
+
* } else if (event.action === "closed") {
|
|
665
|
+
* console.log("Signal closed with PNL:", event.pnl.pnlPercentage);
|
|
666
|
+
* }
|
|
667
|
+
* });
|
|
668
|
+
*
|
|
669
|
+
* // Later: stop listening
|
|
670
|
+
* unsubscribe();
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
declare function listenSignal(fn: (event: IStrategyTickResult) => void): () => void;
|
|
674
|
+
/**
|
|
675
|
+
* Subscribes to filtered signal events with one-time execution.
|
|
676
|
+
*
|
|
677
|
+
* Listens for events matching the filter predicate, then executes callback once
|
|
678
|
+
* and automatically unsubscribes. Useful for waiting for specific signal conditions.
|
|
679
|
+
*
|
|
680
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
681
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
682
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
683
|
+
*
|
|
684
|
+
* @example
|
|
685
|
+
* ```typescript
|
|
686
|
+
* import { listenSignalOnce } from "./function/event";
|
|
687
|
+
*
|
|
688
|
+
* // Wait for first take profit hit
|
|
689
|
+
* listenSignalOnce(
|
|
690
|
+
* (event) => event.action === "closed" && event.closeReason === "take_profit",
|
|
691
|
+
* (event) => {
|
|
692
|
+
* console.log("Take profit hit! PNL:", event.pnl.pnlPercentage);
|
|
693
|
+
* }
|
|
694
|
+
* );
|
|
695
|
+
*
|
|
696
|
+
* // Wait for any signal to close on BTCUSDT
|
|
697
|
+
* const cancel = listenSignalOnce(
|
|
698
|
+
* (event) => event.action === "closed" && event.signal.symbol === "BTCUSDT",
|
|
699
|
+
* (event) => console.log("BTCUSDT signal closed")
|
|
700
|
+
* );
|
|
701
|
+
*
|
|
702
|
+
* // Cancel if needed before event fires
|
|
703
|
+
* cancel();
|
|
704
|
+
* ```
|
|
705
|
+
*/
|
|
706
|
+
declare function listenSignalOnce(filterFn: (event: IStrategyTickResult) => boolean, fn: (event: IStrategyTickResult) => void): () => void;
|
|
707
|
+
/**
|
|
708
|
+
* Subscribes to live trading signal events with queued async processing.
|
|
709
|
+
*
|
|
710
|
+
* Only receives events from Live.run() execution.
|
|
711
|
+
* Events are processed sequentially in order received.
|
|
712
|
+
*
|
|
713
|
+
* @param fn - Callback function to handle live signal events
|
|
714
|
+
* @returns Unsubscribe function to stop listening
|
|
715
|
+
*
|
|
716
|
+
* @example
|
|
717
|
+
* ```typescript
|
|
718
|
+
* import { listenSignalLive } from "./function/event";
|
|
719
|
+
*
|
|
720
|
+
* const unsubscribe = listenSignalLive((event) => {
|
|
721
|
+
* if (event.action === "closed") {
|
|
722
|
+
* console.log("Live signal closed:", event.pnl.pnlPercentage);
|
|
723
|
+
* }
|
|
724
|
+
* });
|
|
725
|
+
* ```
|
|
726
|
+
*/
|
|
727
|
+
declare function listenSignalLive(fn: (event: IStrategyTickResult) => void): () => void;
|
|
728
|
+
/**
|
|
729
|
+
* Subscribes to filtered live signal events with one-time execution.
|
|
730
|
+
*
|
|
731
|
+
* Only receives events from Live.run() execution.
|
|
732
|
+
* Executes callback once and automatically unsubscribes.
|
|
733
|
+
*
|
|
734
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
735
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
736
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* import { listenSignalLiveOnce } from "./function/event";
|
|
741
|
+
*
|
|
742
|
+
* // Wait for first live take profit hit
|
|
743
|
+
* listenSignalLiveOnce(
|
|
744
|
+
* (event) => event.action === "closed" && event.closeReason === "take_profit",
|
|
745
|
+
* (event) => console.log("Live take profit:", event.pnl.pnlPercentage)
|
|
746
|
+
* );
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
declare function listenSignalLiveOnce(filterFn: (event: IStrategyTickResult) => boolean, fn: (event: IStrategyTickResult) => void): () => void;
|
|
750
|
+
/**
|
|
751
|
+
* Subscribes to backtest signal events with queued async processing.
|
|
752
|
+
*
|
|
753
|
+
* Only receives events from Backtest.run() execution.
|
|
754
|
+
* Events are processed sequentially in order received.
|
|
755
|
+
*
|
|
756
|
+
* @param fn - Callback function to handle backtest signal events
|
|
757
|
+
* @returns Unsubscribe function to stop listening
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* ```typescript
|
|
761
|
+
* import { listenSignalBacktest } from "./function/event";
|
|
762
|
+
*
|
|
763
|
+
* const unsubscribe = listenSignalBacktest((event) => {
|
|
764
|
+
* if (event.action === "closed") {
|
|
765
|
+
* console.log("Backtest signal closed:", event.pnl.pnlPercentage);
|
|
766
|
+
* }
|
|
767
|
+
* });
|
|
768
|
+
* ```
|
|
769
|
+
*/
|
|
770
|
+
declare function listenSignalBacktest(fn: (event: IStrategyTickResult) => void): () => void;
|
|
771
|
+
/**
|
|
772
|
+
* Subscribes to filtered backtest signal events with one-time execution.
|
|
773
|
+
*
|
|
774
|
+
* Only receives events from Backtest.run() execution.
|
|
775
|
+
* Executes callback once and automatically unsubscribes.
|
|
776
|
+
*
|
|
777
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
778
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
779
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* import { listenSignalBacktestOnce } from "./function/event";
|
|
784
|
+
*
|
|
785
|
+
* // Wait for first backtest stop loss hit
|
|
786
|
+
* listenSignalBacktestOnce(
|
|
787
|
+
* (event) => event.action === "closed" && event.closeReason === "stop_loss",
|
|
788
|
+
* (event) => console.log("Backtest stop loss:", event.pnl.pnlPercentage)
|
|
789
|
+
* );
|
|
790
|
+
* ```
|
|
791
|
+
*/
|
|
792
|
+
declare function listenSignalBacktestOnce(filterFn: (event: IStrategyTickResult) => boolean, fn: (event: IStrategyTickResult) => void): () => void;
|
|
793
|
+
/**
|
|
794
|
+
* Subscribes to background execution errors with queued async processing.
|
|
795
|
+
*
|
|
796
|
+
* Listens to errors caught in Live.background() and Backtest.background() execution.
|
|
797
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
798
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
799
|
+
*
|
|
800
|
+
* @param fn - Callback function to handle error events
|
|
801
|
+
* @returns Unsubscribe function to stop listening
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```typescript
|
|
805
|
+
* import { listenError } from "./function/event";
|
|
806
|
+
*
|
|
807
|
+
* const unsubscribe = listenError((error) => {
|
|
808
|
+
* console.error("Background execution error:", error.message);
|
|
809
|
+
* // Log to monitoring service, send alerts, etc.
|
|
810
|
+
* });
|
|
811
|
+
*
|
|
812
|
+
* // Later: stop listening
|
|
813
|
+
* unsubscribe();
|
|
814
|
+
* ```
|
|
815
|
+
*/
|
|
816
|
+
declare function listenError(fn: (error: Error) => void): () => void;
|
|
179
817
|
|
|
818
|
+
/**
|
|
819
|
+
* Fetches historical candle data from the registered exchange.
|
|
820
|
+
*
|
|
821
|
+
* Candles are fetched backwards from the current execution context time.
|
|
822
|
+
* Uses the exchange's getCandles implementation.
|
|
823
|
+
*
|
|
824
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
825
|
+
* @param interval - Candle interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h")
|
|
826
|
+
* @param limit - Number of candles to fetch
|
|
827
|
+
* @returns Promise resolving to array of candle data
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```typescript
|
|
831
|
+
* const candles = await getCandles("BTCUSDT", "1m", 100);
|
|
832
|
+
* console.log(candles[0]); // { timestamp, open, high, low, close, volume }
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
180
835
|
declare function getCandles(symbol: string, interval: CandleInterval, limit: number): Promise<ICandleData[]>;
|
|
836
|
+
/**
|
|
837
|
+
* Calculates VWAP (Volume Weighted Average Price) for a symbol.
|
|
838
|
+
*
|
|
839
|
+
* Uses the last 5 1-minute candles to calculate:
|
|
840
|
+
* - Typical Price = (high + low + close) / 3
|
|
841
|
+
* - VWAP = sum(typical_price * volume) / sum(volume)
|
|
842
|
+
*
|
|
843
|
+
* If volume is zero, returns simple average of close prices.
|
|
844
|
+
*
|
|
845
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
846
|
+
* @returns Promise resolving to VWAP price
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```typescript
|
|
850
|
+
* const vwap = await getAveragePrice("BTCUSDT");
|
|
851
|
+
* console.log(vwap); // 50125.43
|
|
852
|
+
* ```
|
|
853
|
+
*/
|
|
181
854
|
declare function getAveragePrice(symbol: string): Promise<number>;
|
|
855
|
+
/**
|
|
856
|
+
* Formats a price value according to exchange rules.
|
|
857
|
+
*
|
|
858
|
+
* Uses the exchange's formatPrice implementation for proper decimal places.
|
|
859
|
+
*
|
|
860
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
861
|
+
* @param price - Raw price value
|
|
862
|
+
* @returns Promise resolving to formatted price string
|
|
863
|
+
*
|
|
864
|
+
* @example
|
|
865
|
+
* ```typescript
|
|
866
|
+
* const formatted = await formatPrice("BTCUSDT", 50000.123456);
|
|
867
|
+
* console.log(formatted); // "50000.12"
|
|
868
|
+
* ```
|
|
869
|
+
*/
|
|
182
870
|
declare function formatPrice(symbol: string, price: number): Promise<string>;
|
|
871
|
+
/**
|
|
872
|
+
* Formats a quantity value according to exchange rules.
|
|
873
|
+
*
|
|
874
|
+
* Uses the exchange's formatQuantity implementation for proper decimal places.
|
|
875
|
+
*
|
|
876
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
877
|
+
* @param quantity - Raw quantity value
|
|
878
|
+
* @returns Promise resolving to formatted quantity string
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* const formatted = await formatQuantity("BTCUSDT", 0.123456789);
|
|
883
|
+
* console.log(formatted); // "0.12345678"
|
|
884
|
+
* ```
|
|
885
|
+
*/
|
|
183
886
|
declare function formatQuantity(symbol: string, quantity: number): Promise<string>;
|
|
887
|
+
/**
|
|
888
|
+
* Gets the current date from execution context.
|
|
889
|
+
*
|
|
890
|
+
* In backtest mode: returns the current timeframe date being processed
|
|
891
|
+
* In live mode: returns current real-time date
|
|
892
|
+
*
|
|
893
|
+
* @returns Promise resolving to current execution context date
|
|
894
|
+
*
|
|
895
|
+
* @example
|
|
896
|
+
* ```typescript
|
|
897
|
+
* const date = await getDate();
|
|
898
|
+
* console.log(date); // 2024-01-01T12:00:00.000Z
|
|
899
|
+
* ```
|
|
900
|
+
*/
|
|
184
901
|
declare function getDate(): Promise<Date>;
|
|
902
|
+
/**
|
|
903
|
+
* Gets the current execution mode.
|
|
904
|
+
*
|
|
905
|
+
* @returns Promise resolving to "backtest" or "live"
|
|
906
|
+
*
|
|
907
|
+
* @example
|
|
908
|
+
* ```typescript
|
|
909
|
+
* const mode = await getMode();
|
|
910
|
+
* if (mode === "backtest") {
|
|
911
|
+
* console.log("Running in backtest mode");
|
|
912
|
+
* } else {
|
|
913
|
+
* console.log("Running in live mode");
|
|
914
|
+
* }
|
|
915
|
+
* ```
|
|
916
|
+
*/
|
|
185
917
|
declare function getMode(): Promise<"backtest" | "live">;
|
|
186
918
|
|
|
187
|
-
interface IMethodContext {
|
|
188
|
-
exchangeName: ExchangeName;
|
|
189
|
-
strategyName: StrategyName;
|
|
190
|
-
frameName: FrameName;
|
|
191
|
-
}
|
|
192
|
-
declare const MethodContextService: (new () => {
|
|
193
|
-
readonly context: IMethodContext;
|
|
194
|
-
}) & Omit<{
|
|
195
|
-
new (context: IMethodContext): {
|
|
196
|
-
readonly context: IMethodContext;
|
|
197
|
-
};
|
|
198
|
-
}, "prototype"> & di_scoped.IScopedClassRun<[context: IMethodContext]>;
|
|
199
|
-
|
|
200
919
|
declare const BASE_WAIT_FOR_INIT_SYMBOL: unique symbol;
|
|
920
|
+
/**
|
|
921
|
+
* Signal data stored in persistence layer.
|
|
922
|
+
* Contains nullable signal for atomic updates.
|
|
923
|
+
*/
|
|
201
924
|
interface ISignalData {
|
|
925
|
+
/** Current signal state (null when no active signal) */
|
|
202
926
|
signalRow: ISignalRow | null;
|
|
203
927
|
}
|
|
928
|
+
/**
|
|
929
|
+
* Type helper for PersistBase instance.
|
|
930
|
+
*/
|
|
204
931
|
type TPersistBase = InstanceType<typeof PersistBase>;
|
|
932
|
+
/**
|
|
933
|
+
* Constructor type for PersistBase.
|
|
934
|
+
* Used for custom persistence adapters.
|
|
935
|
+
*/
|
|
205
936
|
type TPersistBaseCtor<EntityName extends string = string, Entity extends IEntity = IEntity> = new (entityName: EntityName, baseDir: string) => IPersistBase<Entity>;
|
|
937
|
+
/**
|
|
938
|
+
* Entity identifier - string or number.
|
|
939
|
+
*/
|
|
206
940
|
type EntityId = string | number;
|
|
941
|
+
/**
|
|
942
|
+
* Base interface for persisted entities.
|
|
943
|
+
*/
|
|
207
944
|
interface IEntity {
|
|
208
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* Persistence interface for CRUD operations.
|
|
948
|
+
* Implemented by PersistBase.
|
|
949
|
+
*/
|
|
209
950
|
interface IPersistBase<Entity extends IEntity = IEntity> {
|
|
951
|
+
/**
|
|
952
|
+
* Initialize persistence directory and validate existing files.
|
|
953
|
+
* Uses singleshot to ensure one-time execution.
|
|
954
|
+
*
|
|
955
|
+
* @param initial - Whether this is the first initialization
|
|
956
|
+
* @returns Promise that resolves when initialization is complete
|
|
957
|
+
*/
|
|
210
958
|
waitForInit(initial: boolean): Promise<void>;
|
|
959
|
+
/**
|
|
960
|
+
* Read entity from persistence storage.
|
|
961
|
+
*
|
|
962
|
+
* @param entityId - Unique entity identifier
|
|
963
|
+
* @returns Promise resolving to entity data
|
|
964
|
+
* @throws Error if entity not found or read fails
|
|
965
|
+
*/
|
|
211
966
|
readValue(entityId: EntityId): Promise<Entity>;
|
|
967
|
+
/**
|
|
968
|
+
* Check if entity exists in storage.
|
|
969
|
+
*
|
|
970
|
+
* @param entityId - Unique entity identifier
|
|
971
|
+
* @returns Promise resolving to true if exists, false otherwise
|
|
972
|
+
*/
|
|
212
973
|
hasValue(entityId: EntityId): Promise<boolean>;
|
|
974
|
+
/**
|
|
975
|
+
* Write entity to storage with atomic file writes.
|
|
976
|
+
*
|
|
977
|
+
* @param entityId - Unique entity identifier
|
|
978
|
+
* @param entity - Entity data to persist
|
|
979
|
+
* @returns Promise that resolves when write is complete
|
|
980
|
+
* @throws Error if write fails
|
|
981
|
+
*/
|
|
213
982
|
writeValue(entityId: EntityId, entity: Entity): Promise<void>;
|
|
214
983
|
}
|
|
984
|
+
/**
|
|
985
|
+
* Base class for file-based persistence with atomic writes.
|
|
986
|
+
*
|
|
987
|
+
* Features:
|
|
988
|
+
* - Atomic file writes using writeFileAtomic
|
|
989
|
+
* - Auto-validation and cleanup of corrupted files
|
|
990
|
+
* - Async generator support for iteration
|
|
991
|
+
* - Retry logic for file deletion
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```typescript
|
|
995
|
+
* const persist = new PersistBase("my-entity", "./data");
|
|
996
|
+
* await persist.waitForInit(true);
|
|
997
|
+
* await persist.writeValue("key1", { data: "value" });
|
|
998
|
+
* const value = await persist.readValue("key1");
|
|
999
|
+
* ```
|
|
1000
|
+
*/
|
|
215
1001
|
declare const PersistBase: {
|
|
216
1002
|
new <EntityName extends string = string>(entityName: EntityName, baseDir?: string): {
|
|
1003
|
+
/** Computed directory path for entity storage */
|
|
217
1004
|
_directory: string;
|
|
218
1005
|
readonly entityName: EntityName;
|
|
219
1006
|
readonly baseDir: string;
|
|
1007
|
+
/**
|
|
1008
|
+
* Computes file path for entity ID.
|
|
1009
|
+
*
|
|
1010
|
+
* @param entityId - Entity identifier
|
|
1011
|
+
* @returns Full file path to entity JSON file
|
|
1012
|
+
*/
|
|
220
1013
|
_getFilePath(entityId: EntityId): string;
|
|
221
1014
|
waitForInit(initial: boolean): Promise<void>;
|
|
1015
|
+
/**
|
|
1016
|
+
* Returns count of persisted entities.
|
|
1017
|
+
*
|
|
1018
|
+
* @returns Promise resolving to number of .json files in directory
|
|
1019
|
+
*/
|
|
222
1020
|
getCount(): Promise<number>;
|
|
223
1021
|
readValue<T extends IEntity = IEntity>(entityId: EntityId): Promise<T>;
|
|
224
1022
|
hasValue(entityId: EntityId): Promise<boolean>;
|
|
225
1023
|
writeValue<T extends IEntity = IEntity>(entityId: EntityId, entity: T): Promise<void>;
|
|
1024
|
+
/**
|
|
1025
|
+
* Removes entity from storage.
|
|
1026
|
+
*
|
|
1027
|
+
* @param entityId - Entity identifier to remove
|
|
1028
|
+
* @returns Promise that resolves when entity is deleted
|
|
1029
|
+
* @throws Error if entity not found or deletion fails
|
|
1030
|
+
*/
|
|
226
1031
|
removeValue(entityId: EntityId): Promise<void>;
|
|
1032
|
+
/**
|
|
1033
|
+
* Removes all entities from storage.
|
|
1034
|
+
*
|
|
1035
|
+
* @returns Promise that resolves when all entities are deleted
|
|
1036
|
+
* @throws Error if deletion fails
|
|
1037
|
+
*/
|
|
227
1038
|
removeAll(): Promise<void>;
|
|
1039
|
+
/**
|
|
1040
|
+
* Async generator yielding all entity values.
|
|
1041
|
+
* Sorted alphanumerically by entity ID.
|
|
1042
|
+
*
|
|
1043
|
+
* @returns AsyncGenerator yielding entities
|
|
1044
|
+
* @throws Error if reading fails
|
|
1045
|
+
*/
|
|
228
1046
|
values<T extends IEntity = IEntity>(): AsyncGenerator<T>;
|
|
1047
|
+
/**
|
|
1048
|
+
* Async generator yielding all entity IDs.
|
|
1049
|
+
* Sorted alphanumerically.
|
|
1050
|
+
*
|
|
1051
|
+
* @returns AsyncGenerator yielding entity IDs
|
|
1052
|
+
* @throws Error if reading fails
|
|
1053
|
+
*/
|
|
229
1054
|
keys(): AsyncGenerator<EntityId>;
|
|
1055
|
+
/**
|
|
1056
|
+
* Filters entities by predicate function.
|
|
1057
|
+
*
|
|
1058
|
+
* @param predicate - Filter function
|
|
1059
|
+
* @returns AsyncGenerator yielding filtered entities
|
|
1060
|
+
*/
|
|
230
1061
|
filter<T extends IEntity = IEntity>(predicate: (value: T) => boolean): AsyncGenerator<T>;
|
|
1062
|
+
/**
|
|
1063
|
+
* Takes first N entities, optionally filtered.
|
|
1064
|
+
*
|
|
1065
|
+
* @param total - Maximum number of entities to yield
|
|
1066
|
+
* @param predicate - Optional filter function
|
|
1067
|
+
* @returns AsyncGenerator yielding up to total entities
|
|
1068
|
+
*/
|
|
231
1069
|
take<T extends IEntity = IEntity>(total: number, predicate?: (value: T) => boolean): AsyncGenerator<T>;
|
|
232
1070
|
[BASE_WAIT_FOR_INIT_SYMBOL]: (() => Promise<void>) & functools_kit.ISingleshotClearable;
|
|
1071
|
+
/**
|
|
1072
|
+
* Async iterator implementation.
|
|
1073
|
+
* Delegates to values() generator.
|
|
1074
|
+
*
|
|
1075
|
+
* @returns AsyncIterableIterator yielding entities
|
|
1076
|
+
*/
|
|
233
1077
|
[Symbol.asyncIterator](): AsyncIterableIterator<any>;
|
|
234
1078
|
};
|
|
235
1079
|
};
|
|
1080
|
+
/**
|
|
1081
|
+
* Utility class for managing signal persistence.
|
|
1082
|
+
*
|
|
1083
|
+
* Features:
|
|
1084
|
+
* - Memoized storage instances per strategy
|
|
1085
|
+
* - Custom adapter support
|
|
1086
|
+
* - Atomic read/write operations
|
|
1087
|
+
* - Crash-safe signal state management
|
|
1088
|
+
*
|
|
1089
|
+
* Used by ClientStrategy for live mode persistence.
|
|
1090
|
+
*/
|
|
236
1091
|
declare class PersistSignalUtils {
|
|
237
1092
|
private PersistSignalFactory;
|
|
238
1093
|
private getSignalStorage;
|
|
1094
|
+
/**
|
|
1095
|
+
* Registers a custom persistence adapter.
|
|
1096
|
+
*
|
|
1097
|
+
* @param Ctor - Custom PersistBase constructor
|
|
1098
|
+
*
|
|
1099
|
+
* @example
|
|
1100
|
+
* ```typescript
|
|
1101
|
+
* class RedisPersist extends PersistBase {
|
|
1102
|
+
* async readValue(id) { return JSON.parse(await redis.get(id)); }
|
|
1103
|
+
* async writeValue(id, entity) { await redis.set(id, JSON.stringify(entity)); }
|
|
1104
|
+
* }
|
|
1105
|
+
* PersistSignalAdaper.usePersistSignalAdapter(RedisPersist);
|
|
1106
|
+
* ```
|
|
1107
|
+
*/
|
|
239
1108
|
usePersistSignalAdapter(Ctor: TPersistBaseCtor<StrategyName, ISignalData>): void;
|
|
1109
|
+
/**
|
|
1110
|
+
* Reads persisted signal data for a strategy and symbol.
|
|
1111
|
+
*
|
|
1112
|
+
* Called by ClientStrategy.waitForInit() to restore state.
|
|
1113
|
+
* Returns null if no signal exists.
|
|
1114
|
+
*
|
|
1115
|
+
* @param strategyName - Strategy identifier
|
|
1116
|
+
* @param symbol - Trading pair symbol
|
|
1117
|
+
* @returns Promise resolving to signal or null
|
|
1118
|
+
*/
|
|
240
1119
|
readSignalData: (strategyName: StrategyName, symbol: string) => Promise<ISignalRow | null>;
|
|
1120
|
+
/**
|
|
1121
|
+
* Writes signal data to disk with atomic file writes.
|
|
1122
|
+
*
|
|
1123
|
+
* Called by ClientStrategy.setPendingSignal() to persist state.
|
|
1124
|
+
* Uses atomic writes to prevent corruption on crashes.
|
|
1125
|
+
*
|
|
1126
|
+
* @param signalRow - Signal data (null to clear)
|
|
1127
|
+
* @param strategyName - Strategy identifier
|
|
1128
|
+
* @param symbol - Trading pair symbol
|
|
1129
|
+
* @returns Promise that resolves when write is complete
|
|
1130
|
+
*/
|
|
241
1131
|
writeSignalData: (signalRow: ISignalRow | null, strategyName: StrategyName, symbol: string) => Promise<void>;
|
|
242
1132
|
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Global singleton instance of PersistSignalUtils.
|
|
1135
|
+
* Used by ClientStrategy for signal persistence.
|
|
1136
|
+
*
|
|
1137
|
+
* @example
|
|
1138
|
+
* ```typescript
|
|
1139
|
+
* // Custom adapter
|
|
1140
|
+
* PersistSignalAdaper.usePersistSignalAdapter(RedisPersist);
|
|
1141
|
+
*
|
|
1142
|
+
* // Read signal
|
|
1143
|
+
* const signal = await PersistSignalAdaper.readSignalData("my-strategy", "BTCUSDT");
|
|
1144
|
+
*
|
|
1145
|
+
* // Write signal
|
|
1146
|
+
* await PersistSignalAdaper.writeSignalData(signal, "my-strategy", "BTCUSDT");
|
|
1147
|
+
* ```
|
|
1148
|
+
*/
|
|
243
1149
|
declare const PersistSignalAdaper: PersistSignalUtils;
|
|
244
1150
|
|
|
1151
|
+
/**
|
|
1152
|
+
* Utility class for backtest operations.
|
|
1153
|
+
*
|
|
1154
|
+
* Provides simplified access to backtestGlobalService.run() with logging.
|
|
1155
|
+
* Exported as singleton instance for convenient usage.
|
|
1156
|
+
*
|
|
1157
|
+
* @example
|
|
1158
|
+
* ```typescript
|
|
1159
|
+
* import { Backtest } from "./classes/Backtest";
|
|
1160
|
+
*
|
|
1161
|
+
* for await (const result of Backtest.run("BTCUSDT", {
|
|
1162
|
+
* strategyName: "my-strategy",
|
|
1163
|
+
* exchangeName: "my-exchange",
|
|
1164
|
+
* frameName: "1d-backtest"
|
|
1165
|
+
* })) {
|
|
1166
|
+
* console.log("Closed signal PNL:", result.pnl.pnlPercentage);
|
|
1167
|
+
* }
|
|
1168
|
+
* ```
|
|
1169
|
+
*/
|
|
245
1170
|
declare class BacktestUtils {
|
|
1171
|
+
/**
|
|
1172
|
+
* Runs backtest for a symbol with context propagation.
|
|
1173
|
+
*
|
|
1174
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1175
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
1176
|
+
* @returns Async generator yielding closed signals with PNL
|
|
1177
|
+
*/
|
|
246
1178
|
run: (symbol: string, context: {
|
|
247
1179
|
strategyName: string;
|
|
248
1180
|
exchangeName: string;
|
|
249
1181
|
frameName: string;
|
|
250
1182
|
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
1183
|
+
/**
|
|
1184
|
+
* Runs backtest in background without yielding results.
|
|
1185
|
+
*
|
|
1186
|
+
* Consumes all backtest results internally without exposing them.
|
|
1187
|
+
* Useful for running backtests for side effects only (callbacks, logging).
|
|
1188
|
+
*
|
|
1189
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1190
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
1191
|
+
* @returns Cancellation closure
|
|
1192
|
+
*
|
|
1193
|
+
* @example
|
|
1194
|
+
* ```typescript
|
|
1195
|
+
* // Run backtest silently, only callbacks will fire
|
|
1196
|
+
* await Backtest.background("BTCUSDT", {
|
|
1197
|
+
* strategyName: "my-strategy",
|
|
1198
|
+
* exchangeName: "my-exchange",
|
|
1199
|
+
* frameName: "1d-backtest"
|
|
1200
|
+
* });
|
|
1201
|
+
* console.log("Backtest completed");
|
|
1202
|
+
* ```
|
|
1203
|
+
*/
|
|
1204
|
+
background: (symbol: string, context: {
|
|
1205
|
+
strategyName: string;
|
|
1206
|
+
exchangeName: string;
|
|
1207
|
+
frameName: string;
|
|
1208
|
+
}) => Promise<() => void>;
|
|
1209
|
+
/**
|
|
1210
|
+
* Generates markdown report with all closed signals for a strategy.
|
|
1211
|
+
*
|
|
1212
|
+
* @param strategyName - Strategy name to generate report for
|
|
1213
|
+
* @returns Promise resolving to markdown formatted report string
|
|
1214
|
+
*
|
|
1215
|
+
* @example
|
|
1216
|
+
* ```typescript
|
|
1217
|
+
* const markdown = await Backtest.getReport("my-strategy");
|
|
1218
|
+
* console.log(markdown);
|
|
1219
|
+
* ```
|
|
1220
|
+
*/
|
|
1221
|
+
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
1222
|
+
/**
|
|
1223
|
+
* Saves strategy report to disk.
|
|
1224
|
+
*
|
|
1225
|
+
* @param strategyName - Strategy name to save report for
|
|
1226
|
+
* @param path - Optional directory path to save report (default: "./logs/backtest")
|
|
1227
|
+
*
|
|
1228
|
+
* @example
|
|
1229
|
+
* ```typescript
|
|
1230
|
+
* // Save to default path: ./logs/backtest/my-strategy.md
|
|
1231
|
+
* await Backtest.dump("my-strategy");
|
|
1232
|
+
*
|
|
1233
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
1234
|
+
* await Backtest.dump("my-strategy", "./custom/path");
|
|
1235
|
+
* ```
|
|
1236
|
+
*/
|
|
1237
|
+
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
251
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Singleton instance of BacktestUtils for convenient backtest operations.
|
|
1241
|
+
*
|
|
1242
|
+
* @example
|
|
1243
|
+
* ```typescript
|
|
1244
|
+
* import { Backtest } from "./classes/Backtest";
|
|
1245
|
+
*
|
|
1246
|
+
* for await (const result of Backtest.run("BTCUSDT", {
|
|
1247
|
+
* strategyName: "my-strategy",
|
|
1248
|
+
* exchangeName: "my-exchange",
|
|
1249
|
+
* frameName: "1d-backtest"
|
|
1250
|
+
* })) {
|
|
1251
|
+
* if (result.action === "closed") {
|
|
1252
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
1253
|
+
* }
|
|
1254
|
+
* }
|
|
1255
|
+
* ```
|
|
1256
|
+
*/
|
|
252
1257
|
declare const Backtest: BacktestUtils;
|
|
253
1258
|
|
|
1259
|
+
/**
|
|
1260
|
+
* Utility class for live trading operations.
|
|
1261
|
+
*
|
|
1262
|
+
* Provides simplified access to liveGlobalService.run() with logging.
|
|
1263
|
+
* Exported as singleton instance for convenient usage.
|
|
1264
|
+
*
|
|
1265
|
+
* Features:
|
|
1266
|
+
* - Infinite async generator (never completes)
|
|
1267
|
+
* - Crash recovery via persisted state
|
|
1268
|
+
* - Real-time progression with Date.now()
|
|
1269
|
+
*
|
|
1270
|
+
* @example
|
|
1271
|
+
* ```typescript
|
|
1272
|
+
* import { Live } from "./classes/Live";
|
|
1273
|
+
*
|
|
1274
|
+
* // Infinite loop - use Ctrl+C to stop
|
|
1275
|
+
* for await (const result of Live.run("BTCUSDT", {
|
|
1276
|
+
* strategyName: "my-strategy",
|
|
1277
|
+
* exchangeName: "my-exchange",
|
|
1278
|
+
* frameName: ""
|
|
1279
|
+
* })) {
|
|
1280
|
+
* if (result.action === "opened") {
|
|
1281
|
+
* console.log("Signal opened:", result.signal);
|
|
1282
|
+
* } else if (result.action === "closed") {
|
|
1283
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
1284
|
+
* }
|
|
1285
|
+
* }
|
|
1286
|
+
* ```
|
|
1287
|
+
*/
|
|
254
1288
|
declare class LiveUtils {
|
|
1289
|
+
/**
|
|
1290
|
+
* Runs live trading for a symbol with context propagation.
|
|
1291
|
+
*
|
|
1292
|
+
* Infinite async generator with crash recovery support.
|
|
1293
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
1294
|
+
*
|
|
1295
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1296
|
+
* @param context - Execution context with strategy and exchange names
|
|
1297
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
1298
|
+
*/
|
|
255
1299
|
run: (symbol: string, context: {
|
|
256
1300
|
strategyName: string;
|
|
257
1301
|
exchangeName: string;
|
|
258
|
-
frameName: string;
|
|
259
1302
|
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
1303
|
+
/**
|
|
1304
|
+
* Runs live trading in background without yielding results.
|
|
1305
|
+
*
|
|
1306
|
+
* Consumes all live trading results internally without exposing them.
|
|
1307
|
+
* Infinite loop - will run until process is stopped or crashes.
|
|
1308
|
+
* Useful for running live trading for side effects only (callbacks, persistence).
|
|
1309
|
+
*
|
|
1310
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1311
|
+
* @param context - Execution context with strategy and exchange names
|
|
1312
|
+
* @returns Cancellation closure
|
|
1313
|
+
*
|
|
1314
|
+
* @example
|
|
1315
|
+
* ```typescript
|
|
1316
|
+
* // Run live trading silently in background, only callbacks will fire
|
|
1317
|
+
* // This will run forever until Ctrl+C
|
|
1318
|
+
* await Live.background("BTCUSDT", {
|
|
1319
|
+
* strategyName: "my-strategy",
|
|
1320
|
+
* exchangeName: "my-exchange"
|
|
1321
|
+
* });
|
|
1322
|
+
* ```
|
|
1323
|
+
*/
|
|
1324
|
+
background: (symbol: string, context: {
|
|
1325
|
+
strategyName: string;
|
|
1326
|
+
exchangeName: string;
|
|
1327
|
+
}) => Promise<() => void>;
|
|
1328
|
+
/**
|
|
1329
|
+
* Generates markdown report with all events for a strategy.
|
|
1330
|
+
*
|
|
1331
|
+
* @param strategyName - Strategy name to generate report for
|
|
1332
|
+
* @returns Promise resolving to markdown formatted report string
|
|
1333
|
+
*
|
|
1334
|
+
* @example
|
|
1335
|
+
* ```typescript
|
|
1336
|
+
* const markdown = await Live.getReport("my-strategy");
|
|
1337
|
+
* console.log(markdown);
|
|
1338
|
+
* ```
|
|
1339
|
+
*/
|
|
1340
|
+
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
1341
|
+
/**
|
|
1342
|
+
* Saves strategy report to disk.
|
|
1343
|
+
*
|
|
1344
|
+
* @param strategyName - Strategy name to save report for
|
|
1345
|
+
* @param path - Optional directory path to save report (default: "./logs/live")
|
|
1346
|
+
*
|
|
1347
|
+
* @example
|
|
1348
|
+
* ```typescript
|
|
1349
|
+
* // Save to default path: ./logs/live/my-strategy.md
|
|
1350
|
+
* await Live.dump("my-strategy");
|
|
1351
|
+
*
|
|
1352
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
1353
|
+
* await Live.dump("my-strategy", "./custom/path");
|
|
1354
|
+
* ```
|
|
1355
|
+
*/
|
|
1356
|
+
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
260
1357
|
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Singleton instance of LiveUtils for convenient live trading operations.
|
|
1360
|
+
*
|
|
1361
|
+
* @example
|
|
1362
|
+
* ```typescript
|
|
1363
|
+
* import { Live } from "./classes/Live";
|
|
1364
|
+
*
|
|
1365
|
+
* for await (const result of Live.run("BTCUSDT", {
|
|
1366
|
+
* strategyName: "my-strategy",
|
|
1367
|
+
* exchangeName: "my-exchange",
|
|
1368
|
+
* })) {
|
|
1369
|
+
* console.log("Result:", result.action);
|
|
1370
|
+
* }
|
|
1371
|
+
* ```
|
|
1372
|
+
*/
|
|
261
1373
|
declare const Live: LiveUtils;
|
|
262
1374
|
|
|
1375
|
+
/**
|
|
1376
|
+
* Logger service with automatic context injection.
|
|
1377
|
+
*
|
|
1378
|
+
* Features:
|
|
1379
|
+
* - Delegates to user-provided logger via setLogger()
|
|
1380
|
+
* - Automatically appends method context (strategyName, exchangeName, frameName)
|
|
1381
|
+
* - Automatically appends execution context (symbol, when, backtest)
|
|
1382
|
+
* - Defaults to NOOP_LOGGER if no logger configured
|
|
1383
|
+
*
|
|
1384
|
+
* Used throughout the framework for consistent logging with context.
|
|
1385
|
+
*/
|
|
263
1386
|
declare class LoggerService implements ILogger {
|
|
264
1387
|
private readonly methodContextService;
|
|
265
1388
|
private readonly executionContextService;
|
|
266
1389
|
private _commonLogger;
|
|
1390
|
+
/**
|
|
1391
|
+
* Gets current method context if available.
|
|
1392
|
+
* Contains strategyName, exchangeName, frameName from MethodContextService.
|
|
1393
|
+
*/
|
|
267
1394
|
private get methodContext();
|
|
1395
|
+
/**
|
|
1396
|
+
* Gets current execution context if available.
|
|
1397
|
+
* Contains symbol, when, backtest from ExecutionContextService.
|
|
1398
|
+
*/
|
|
268
1399
|
private get executionContext();
|
|
1400
|
+
/**
|
|
1401
|
+
* Logs general-purpose message with automatic context injection.
|
|
1402
|
+
*
|
|
1403
|
+
* @param topic - Log topic/category
|
|
1404
|
+
* @param args - Additional log arguments
|
|
1405
|
+
*/
|
|
269
1406
|
log: (topic: string, ...args: any[]) => Promise<void>;
|
|
1407
|
+
/**
|
|
1408
|
+
* Logs debug-level message with automatic context injection.
|
|
1409
|
+
*
|
|
1410
|
+
* @param topic - Log topic/category
|
|
1411
|
+
* @param args - Additional log arguments
|
|
1412
|
+
*/
|
|
270
1413
|
debug: (topic: string, ...args: any[]) => Promise<void>;
|
|
1414
|
+
/**
|
|
1415
|
+
* Logs info-level message with automatic context injection.
|
|
1416
|
+
*
|
|
1417
|
+
* @param topic - Log topic/category
|
|
1418
|
+
* @param args - Additional log arguments
|
|
1419
|
+
*/
|
|
271
1420
|
info: (topic: string, ...args: any[]) => Promise<void>;
|
|
1421
|
+
/**
|
|
1422
|
+
* Logs warning-level message with automatic context injection.
|
|
1423
|
+
*
|
|
1424
|
+
* @param topic - Log topic/category
|
|
1425
|
+
* @param args - Additional log arguments
|
|
1426
|
+
*/
|
|
1427
|
+
warn: (topic: string, ...args: any[]) => Promise<void>;
|
|
1428
|
+
/**
|
|
1429
|
+
* Sets custom logger implementation.
|
|
1430
|
+
*
|
|
1431
|
+
* @param logger - Custom logger implementing ILogger interface
|
|
1432
|
+
*/
|
|
272
1433
|
setLogger: (logger: ILogger) => void;
|
|
273
1434
|
}
|
|
274
1435
|
|
|
1436
|
+
/**
|
|
1437
|
+
* Client implementation for exchange data access.
|
|
1438
|
+
*
|
|
1439
|
+
* Features:
|
|
1440
|
+
* - Historical candle fetching (backwards from execution context)
|
|
1441
|
+
* - Future candle fetching (forwards for backtest)
|
|
1442
|
+
* - VWAP calculation from last 5 1m candles
|
|
1443
|
+
* - Price/quantity formatting for exchange
|
|
1444
|
+
*
|
|
1445
|
+
* All methods use prototype functions for memory efficiency.
|
|
1446
|
+
*
|
|
1447
|
+
* @example
|
|
1448
|
+
* ```typescript
|
|
1449
|
+
* const exchange = new ClientExchange({
|
|
1450
|
+
* exchangeName: "binance",
|
|
1451
|
+
* getCandles: async (symbol, interval, since, limit) => [...],
|
|
1452
|
+
* formatPrice: async (symbol, price) => price.toFixed(2),
|
|
1453
|
+
* formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
|
|
1454
|
+
* execution: executionService,
|
|
1455
|
+
* logger: loggerService,
|
|
1456
|
+
* });
|
|
1457
|
+
*
|
|
1458
|
+
* const candles = await exchange.getCandles("BTCUSDT", "1m", 100);
|
|
1459
|
+
* const vwap = await exchange.getAveragePrice("BTCUSDT");
|
|
1460
|
+
* ```
|
|
1461
|
+
*/
|
|
275
1462
|
declare class ClientExchange implements IExchange {
|
|
276
1463
|
readonly params: IExchangeParams;
|
|
277
1464
|
constructor(params: IExchangeParams);
|
|
1465
|
+
/**
|
|
1466
|
+
* Fetches historical candles backwards from execution context time.
|
|
1467
|
+
*
|
|
1468
|
+
* @param symbol - Trading pair symbol
|
|
1469
|
+
* @param interval - Candle interval
|
|
1470
|
+
* @param limit - Number of candles to fetch
|
|
1471
|
+
* @returns Promise resolving to array of candles
|
|
1472
|
+
*/
|
|
278
1473
|
getCandles(symbol: string, interval: CandleInterval, limit: number): Promise<ICandleData[]>;
|
|
1474
|
+
/**
|
|
1475
|
+
* Fetches future candles forwards from execution context time.
|
|
1476
|
+
* Used in backtest mode to get candles for signal duration.
|
|
1477
|
+
*
|
|
1478
|
+
* @param symbol - Trading pair symbol
|
|
1479
|
+
* @param interval - Candle interval
|
|
1480
|
+
* @param limit - Number of candles to fetch
|
|
1481
|
+
* @returns Promise resolving to array of candles
|
|
1482
|
+
* @throws Error if trying to fetch future candles in live mode
|
|
1483
|
+
*/
|
|
279
1484
|
getNextCandles(symbol: string, interval: CandleInterval, limit: number): Promise<ICandleData[]>;
|
|
1485
|
+
/**
|
|
1486
|
+
* Calculates VWAP (Volume Weighted Average Price) from last 5 1m candles.
|
|
1487
|
+
*
|
|
1488
|
+
* Formula:
|
|
1489
|
+
* - Typical Price = (high + low + close) / 3
|
|
1490
|
+
* - VWAP = sum(typical_price * volume) / sum(volume)
|
|
1491
|
+
*
|
|
1492
|
+
* If volume is zero, returns simple average of close prices.
|
|
1493
|
+
*
|
|
1494
|
+
* @param symbol - Trading pair symbol
|
|
1495
|
+
* @returns Promise resolving to VWAP price
|
|
1496
|
+
* @throws Error if no candles available
|
|
1497
|
+
*/
|
|
280
1498
|
getAveragePrice(symbol: string): Promise<number>;
|
|
281
1499
|
formatQuantity(symbol: string, quantity: number): Promise<string>;
|
|
282
1500
|
formatPrice(symbol: string, price: number): Promise<string>;
|
|
283
1501
|
}
|
|
284
1502
|
|
|
1503
|
+
/**
|
|
1504
|
+
* Connection service routing exchange operations to correct ClientExchange instance.
|
|
1505
|
+
*
|
|
1506
|
+
* Routes all IExchange method calls to the appropriate exchange implementation
|
|
1507
|
+
* based on methodContextService.context.exchangeName. Uses memoization to cache
|
|
1508
|
+
* ClientExchange instances for performance.
|
|
1509
|
+
*
|
|
1510
|
+
* Key features:
|
|
1511
|
+
* - Automatic exchange routing via method context
|
|
1512
|
+
* - Memoized ClientExchange instances by exchangeName
|
|
1513
|
+
* - Implements full IExchange interface
|
|
1514
|
+
* - Logging for all operations
|
|
1515
|
+
*
|
|
1516
|
+
* @example
|
|
1517
|
+
* ```typescript
|
|
1518
|
+
* // Used internally by framework
|
|
1519
|
+
* const candles = await exchangeConnectionService.getCandles(
|
|
1520
|
+
* "BTCUSDT", "1h", 100
|
|
1521
|
+
* );
|
|
1522
|
+
* // Automatically routes to correct exchange based on methodContext
|
|
1523
|
+
* ```
|
|
1524
|
+
*/
|
|
285
1525
|
declare class ExchangeConnectionService implements IExchange {
|
|
286
1526
|
private readonly loggerService;
|
|
287
1527
|
private readonly executionContextService;
|
|
288
1528
|
private readonly exchangeSchemaService;
|
|
289
1529
|
private readonly methodContextService;
|
|
1530
|
+
/**
|
|
1531
|
+
* Retrieves memoized ClientExchange instance for given exchange name.
|
|
1532
|
+
*
|
|
1533
|
+
* Creates ClientExchange on first call, returns cached instance on subsequent calls.
|
|
1534
|
+
* Cache key is exchangeName string.
|
|
1535
|
+
*
|
|
1536
|
+
* @param exchangeName - Name of registered exchange schema
|
|
1537
|
+
* @returns Configured ClientExchange instance
|
|
1538
|
+
*/
|
|
290
1539
|
getExchange: ((exchangeName: ExchangeName) => ClientExchange) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientExchange>;
|
|
1540
|
+
/**
|
|
1541
|
+
* Fetches historical candles for symbol using configured exchange.
|
|
1542
|
+
*
|
|
1543
|
+
* Routes to exchange determined by methodContextService.context.exchangeName.
|
|
1544
|
+
*
|
|
1545
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1546
|
+
* @param interval - Candle interval (e.g., "1h", "1d")
|
|
1547
|
+
* @param limit - Maximum number of candles to fetch
|
|
1548
|
+
* @returns Promise resolving to array of candle data
|
|
1549
|
+
*/
|
|
291
1550
|
getCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
|
|
1551
|
+
/**
|
|
1552
|
+
* Fetches next batch of candles relative to executionContext.when.
|
|
1553
|
+
*
|
|
1554
|
+
* Returns candles that come after the current execution timestamp.
|
|
1555
|
+
* Used for backtest progression and live trading updates.
|
|
1556
|
+
*
|
|
1557
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1558
|
+
* @param interval - Candle interval (e.g., "1h", "1d")
|
|
1559
|
+
* @param limit - Maximum number of candles to fetch
|
|
1560
|
+
* @returns Promise resolving to array of candle data
|
|
1561
|
+
*/
|
|
292
1562
|
getNextCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
|
|
1563
|
+
/**
|
|
1564
|
+
* Retrieves current average price for symbol.
|
|
1565
|
+
*
|
|
1566
|
+
* In live mode: fetches real-time average price from exchange API.
|
|
1567
|
+
* In backtest mode: calculates VWAP from candles in current timeframe.
|
|
1568
|
+
*
|
|
1569
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1570
|
+
* @returns Promise resolving to average price
|
|
1571
|
+
*/
|
|
293
1572
|
getAveragePrice: (symbol: string) => Promise<number>;
|
|
1573
|
+
/**
|
|
1574
|
+
* Formats price according to exchange-specific precision rules.
|
|
1575
|
+
*
|
|
1576
|
+
* Ensures price meets exchange requirements for decimal places and tick size.
|
|
1577
|
+
*
|
|
1578
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1579
|
+
* @param price - Raw price value to format
|
|
1580
|
+
* @returns Promise resolving to formatted price string
|
|
1581
|
+
*/
|
|
294
1582
|
formatPrice: (symbol: string, price: number) => Promise<string>;
|
|
1583
|
+
/**
|
|
1584
|
+
* Formats quantity according to exchange-specific precision rules.
|
|
1585
|
+
*
|
|
1586
|
+
* Ensures quantity meets exchange requirements for decimal places and lot size.
|
|
1587
|
+
*
|
|
1588
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1589
|
+
* @param quantity - Raw quantity value to format
|
|
1590
|
+
* @returns Promise resolving to formatted quantity string
|
|
1591
|
+
*/
|
|
295
1592
|
formatQuantity: (symbol: string, quantity: number) => Promise<string>;
|
|
296
1593
|
}
|
|
297
1594
|
|
|
1595
|
+
/**
|
|
1596
|
+
* Connection service routing strategy operations to correct ClientStrategy instance.
|
|
1597
|
+
*
|
|
1598
|
+
* Routes all IStrategy method calls to the appropriate strategy implementation
|
|
1599
|
+
* based on methodContextService.context.strategyName. Uses memoization to cache
|
|
1600
|
+
* ClientStrategy instances for performance.
|
|
1601
|
+
*
|
|
1602
|
+
* Key features:
|
|
1603
|
+
* - Automatic strategy routing via method context
|
|
1604
|
+
* - Memoized ClientStrategy instances by strategyName
|
|
1605
|
+
* - Implements IStrategy interface
|
|
1606
|
+
* - Ensures initialization with waitForInit() before operations
|
|
1607
|
+
* - Handles both tick() (live) and backtest() operations
|
|
1608
|
+
*
|
|
1609
|
+
* @example
|
|
1610
|
+
* ```typescript
|
|
1611
|
+
* // Used internally by framework
|
|
1612
|
+
* const result = await strategyConnectionService.tick();
|
|
1613
|
+
* // Automatically routes to correct strategy based on methodContext
|
|
1614
|
+
* ```
|
|
1615
|
+
*/
|
|
298
1616
|
declare class StrategyConnectionService implements IStrategy {
|
|
299
1617
|
private readonly loggerService;
|
|
300
1618
|
private readonly executionContextService;
|
|
301
1619
|
private readonly strategySchemaService;
|
|
302
1620
|
private readonly exchangeConnectionService;
|
|
303
1621
|
private readonly methodContextService;
|
|
1622
|
+
/**
|
|
1623
|
+
* Retrieves memoized ClientStrategy instance for given strategy name.
|
|
1624
|
+
*
|
|
1625
|
+
* Creates ClientStrategy on first call, returns cached instance on subsequent calls.
|
|
1626
|
+
* Cache key is strategyName string.
|
|
1627
|
+
*
|
|
1628
|
+
* @param strategyName - Name of registered strategy schema
|
|
1629
|
+
* @returns Configured ClientStrategy instance
|
|
1630
|
+
*/
|
|
304
1631
|
private getStrategy;
|
|
1632
|
+
/**
|
|
1633
|
+
* Executes live trading tick for current strategy.
|
|
1634
|
+
*
|
|
1635
|
+
* Waits for strategy initialization before processing tick.
|
|
1636
|
+
* Evaluates current market conditions and returns signal state.
|
|
1637
|
+
*
|
|
1638
|
+
* @returns Promise resolving to tick result (idle, opened, active, closed)
|
|
1639
|
+
*/
|
|
305
1640
|
tick: () => Promise<IStrategyTickResult>;
|
|
1641
|
+
/**
|
|
1642
|
+
* Executes backtest for current strategy with provided candles.
|
|
1643
|
+
*
|
|
1644
|
+
* Waits for strategy initialization before processing candles.
|
|
1645
|
+
* Evaluates strategy signals against historical data.
|
|
1646
|
+
*
|
|
1647
|
+
* @param candles - Array of historical candle data to backtest
|
|
1648
|
+
* @returns Promise resolving to backtest result (signal or idle)
|
|
1649
|
+
*/
|
|
306
1650
|
backtest: (candles: ICandleData[]) => Promise<IStrategyBacktestResult>;
|
|
307
1651
|
}
|
|
308
1652
|
|
|
1653
|
+
/**
|
|
1654
|
+
* Client implementation for backtest timeframe generation.
|
|
1655
|
+
*
|
|
1656
|
+
* Features:
|
|
1657
|
+
* - Generates timestamp arrays for backtest iteration
|
|
1658
|
+
* - Singleshot caching prevents redundant generation
|
|
1659
|
+
* - Configurable interval spacing (1m to 3d)
|
|
1660
|
+
* - Callback support for validation and logging
|
|
1661
|
+
*
|
|
1662
|
+
* Used by BacktestLogicPrivateService to iterate through historical periods.
|
|
1663
|
+
*/
|
|
309
1664
|
declare class ClientFrame implements IFrame {
|
|
310
1665
|
readonly params: IFrameParams;
|
|
311
1666
|
constructor(params: IFrameParams);
|
|
1667
|
+
/**
|
|
1668
|
+
* Generates timeframe array for backtest period.
|
|
1669
|
+
* Results are cached via singleshot pattern.
|
|
1670
|
+
*
|
|
1671
|
+
* @param symbol - Trading pair symbol (unused, for API consistency)
|
|
1672
|
+
* @returns Promise resolving to array of Date objects
|
|
1673
|
+
* @throws Error if interval is invalid
|
|
1674
|
+
*/
|
|
312
1675
|
getTimeframe: ((symbol: string) => Promise<Date[]>) & functools_kit.ISingleshotClearable;
|
|
313
1676
|
}
|
|
314
1677
|
|
|
1678
|
+
/**
|
|
1679
|
+
* Connection service routing frame operations to correct ClientFrame instance.
|
|
1680
|
+
*
|
|
1681
|
+
* Routes all IFrame method calls to the appropriate frame implementation
|
|
1682
|
+
* based on methodContextService.context.frameName. Uses memoization to cache
|
|
1683
|
+
* ClientFrame instances for performance.
|
|
1684
|
+
*
|
|
1685
|
+
* Key features:
|
|
1686
|
+
* - Automatic frame routing via method context
|
|
1687
|
+
* - Memoized ClientFrame instances by frameName
|
|
1688
|
+
* - Implements IFrame interface
|
|
1689
|
+
* - Backtest timeframe management (startDate, endDate, interval)
|
|
1690
|
+
*
|
|
1691
|
+
* Note: frameName is empty string for live mode (no frame constraints).
|
|
1692
|
+
*
|
|
1693
|
+
* @example
|
|
1694
|
+
* ```typescript
|
|
1695
|
+
* // Used internally by framework
|
|
1696
|
+
* const timeframe = await frameConnectionService.getTimeframe("BTCUSDT");
|
|
1697
|
+
* // Automatically routes to correct frame based on methodContext
|
|
1698
|
+
* ```
|
|
1699
|
+
*/
|
|
315
1700
|
declare class FrameConnectionService implements IFrame {
|
|
316
1701
|
private readonly loggerService;
|
|
317
1702
|
private readonly frameSchemaService;
|
|
318
1703
|
private readonly methodContextService;
|
|
1704
|
+
/**
|
|
1705
|
+
* Retrieves memoized ClientFrame instance for given frame name.
|
|
1706
|
+
*
|
|
1707
|
+
* Creates ClientFrame on first call, returns cached instance on subsequent calls.
|
|
1708
|
+
* Cache key is frameName string.
|
|
1709
|
+
*
|
|
1710
|
+
* @param frameName - Name of registered frame schema
|
|
1711
|
+
* @returns Configured ClientFrame instance
|
|
1712
|
+
*/
|
|
319
1713
|
getFrame: ((frameName: FrameName) => ClientFrame) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientFrame>;
|
|
1714
|
+
/**
|
|
1715
|
+
* Retrieves backtest timeframe boundaries for symbol.
|
|
1716
|
+
*
|
|
1717
|
+
* Returns startDate and endDate from frame configuration.
|
|
1718
|
+
* Used to limit backtest execution to specific date range.
|
|
1719
|
+
*
|
|
1720
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1721
|
+
* @returns Promise resolving to { startDate: Date, endDate: Date }
|
|
1722
|
+
*/
|
|
320
1723
|
getTimeframe: (symbol: string) => Promise<Date[]>;
|
|
321
1724
|
}
|
|
322
1725
|
|
|
1726
|
+
/**
|
|
1727
|
+
* Global service for exchange operations with execution context injection.
|
|
1728
|
+
*
|
|
1729
|
+
* Wraps ExchangeConnectionService with ExecutionContextService to inject
|
|
1730
|
+
* symbol, when, and backtest parameters into the execution context.
|
|
1731
|
+
*
|
|
1732
|
+
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
1733
|
+
*/
|
|
323
1734
|
declare class ExchangeGlobalService {
|
|
324
1735
|
private readonly loggerService;
|
|
325
1736
|
private readonly exchangeConnectionService;
|
|
1737
|
+
/**
|
|
1738
|
+
* Fetches historical candles with execution context.
|
|
1739
|
+
*
|
|
1740
|
+
* @param symbol - Trading pair symbol
|
|
1741
|
+
* @param interval - Candle interval (e.g., "1m", "1h")
|
|
1742
|
+
* @param limit - Maximum number of candles to fetch
|
|
1743
|
+
* @param when - Timestamp for context (used in backtest mode)
|
|
1744
|
+
* @param backtest - Whether running in backtest mode
|
|
1745
|
+
* @returns Promise resolving to array of candles
|
|
1746
|
+
*/
|
|
326
1747
|
getCandles: (symbol: string, interval: CandleInterval, limit: number, when: Date, backtest: boolean) => Promise<ICandleData[]>;
|
|
1748
|
+
/**
|
|
1749
|
+
* Fetches future candles (backtest mode only) with execution context.
|
|
1750
|
+
*
|
|
1751
|
+
* @param symbol - Trading pair symbol
|
|
1752
|
+
* @param interval - Candle interval
|
|
1753
|
+
* @param limit - Maximum number of candles to fetch
|
|
1754
|
+
* @param when - Timestamp for context
|
|
1755
|
+
* @param backtest - Whether running in backtest mode (must be true)
|
|
1756
|
+
* @returns Promise resolving to array of future candles
|
|
1757
|
+
*/
|
|
327
1758
|
getNextCandles: (symbol: string, interval: CandleInterval, limit: number, when: Date, backtest: boolean) => Promise<ICandleData[]>;
|
|
1759
|
+
/**
|
|
1760
|
+
* Calculates VWAP with execution context.
|
|
1761
|
+
*
|
|
1762
|
+
* @param symbol - Trading pair symbol
|
|
1763
|
+
* @param when - Timestamp for context
|
|
1764
|
+
* @param backtest - Whether running in backtest mode
|
|
1765
|
+
* @returns Promise resolving to VWAP price
|
|
1766
|
+
*/
|
|
328
1767
|
getAveragePrice: (symbol: string, when: Date, backtest: boolean) => Promise<number>;
|
|
1768
|
+
/**
|
|
1769
|
+
* Formats price with execution context.
|
|
1770
|
+
*
|
|
1771
|
+
* @param symbol - Trading pair symbol
|
|
1772
|
+
* @param price - Price to format
|
|
1773
|
+
* @param when - Timestamp for context
|
|
1774
|
+
* @param backtest - Whether running in backtest mode
|
|
1775
|
+
* @returns Promise resolving to formatted price string
|
|
1776
|
+
*/
|
|
329
1777
|
formatPrice: (symbol: string, price: number, when: Date, backtest: boolean) => Promise<string>;
|
|
1778
|
+
/**
|
|
1779
|
+
* Formats quantity with execution context.
|
|
1780
|
+
*
|
|
1781
|
+
* @param symbol - Trading pair symbol
|
|
1782
|
+
* @param quantity - Quantity to format
|
|
1783
|
+
* @param when - Timestamp for context
|
|
1784
|
+
* @param backtest - Whether running in backtest mode
|
|
1785
|
+
* @returns Promise resolving to formatted quantity string
|
|
1786
|
+
*/
|
|
330
1787
|
formatQuantity: (symbol: string, quantity: number, when: Date, backtest: boolean) => Promise<string>;
|
|
331
1788
|
}
|
|
332
1789
|
|
|
1790
|
+
/**
|
|
1791
|
+
* Global service for strategy operations with execution context injection.
|
|
1792
|
+
*
|
|
1793
|
+
* Wraps StrategyConnectionService with ExecutionContextService to inject
|
|
1794
|
+
* symbol, when, and backtest parameters into the execution context.
|
|
1795
|
+
*
|
|
1796
|
+
* Used internally by BacktestLogicPrivateService and LiveLogicPrivateService.
|
|
1797
|
+
*/
|
|
333
1798
|
declare class StrategyGlobalService {
|
|
334
1799
|
private readonly loggerService;
|
|
335
1800
|
private readonly strategyConnectionService;
|
|
1801
|
+
/**
|
|
1802
|
+
* Checks signal status at a specific timestamp.
|
|
1803
|
+
*
|
|
1804
|
+
* Wraps strategy tick() with execution context containing symbol, timestamp,
|
|
1805
|
+
* and backtest mode flag.
|
|
1806
|
+
*
|
|
1807
|
+
* @param symbol - Trading pair symbol
|
|
1808
|
+
* @param when - Timestamp for tick evaluation
|
|
1809
|
+
* @param backtest - Whether running in backtest mode
|
|
1810
|
+
* @returns Discriminated union of tick result (idle, opened, active, closed)
|
|
1811
|
+
*/
|
|
336
1812
|
tick: (symbol: string, when: Date, backtest: boolean) => Promise<IStrategyTickResult>;
|
|
1813
|
+
/**
|
|
1814
|
+
* Runs fast backtest against candle array.
|
|
1815
|
+
*
|
|
1816
|
+
* Wraps strategy backtest() with execution context containing symbol,
|
|
1817
|
+
* timestamp, and backtest mode flag.
|
|
1818
|
+
*
|
|
1819
|
+
* @param symbol - Trading pair symbol
|
|
1820
|
+
* @param candles - Array of historical candles to test against
|
|
1821
|
+
* @param when - Starting timestamp for backtest
|
|
1822
|
+
* @param backtest - Whether running in backtest mode (typically true)
|
|
1823
|
+
* @returns Closed signal result with PNL
|
|
1824
|
+
*/
|
|
337
1825
|
backtest: (symbol: string, candles: ICandleData[], when: Date, backtest: boolean) => Promise<IStrategyBacktestResult>;
|
|
338
1826
|
}
|
|
339
1827
|
|
|
1828
|
+
/**
|
|
1829
|
+
* Global service for frame operations.
|
|
1830
|
+
*
|
|
1831
|
+
* Wraps FrameConnectionService for timeframe generation.
|
|
1832
|
+
* Used internally by BacktestLogicPrivateService.
|
|
1833
|
+
*/
|
|
340
1834
|
declare class FrameGlobalService {
|
|
341
1835
|
private readonly loggerService;
|
|
342
1836
|
private readonly frameConnectionService;
|
|
1837
|
+
/**
|
|
1838
|
+
* Generates timeframe array for backtest iteration.
|
|
1839
|
+
*
|
|
1840
|
+
* @param symbol - Trading pair symbol
|
|
1841
|
+
* @returns Promise resolving to array of Date objects
|
|
1842
|
+
*/
|
|
343
1843
|
getTimeframe: (symbol: string) => Promise<Date[]>;
|
|
344
1844
|
}
|
|
345
1845
|
|
|
1846
|
+
/**
|
|
1847
|
+
* Service for managing exchange schema registry.
|
|
1848
|
+
*
|
|
1849
|
+
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
1850
|
+
* Exchanges are registered via addExchange() and retrieved by name.
|
|
1851
|
+
*/
|
|
346
1852
|
declare class ExchangeSchemaService {
|
|
347
1853
|
readonly loggerService: LoggerService;
|
|
348
1854
|
private _registry;
|
|
1855
|
+
/**
|
|
1856
|
+
* Registers a new exchange schema.
|
|
1857
|
+
*
|
|
1858
|
+
* @param key - Unique exchange name
|
|
1859
|
+
* @param value - Exchange schema configuration
|
|
1860
|
+
* @throws Error if exchange name already exists
|
|
1861
|
+
*/
|
|
349
1862
|
register: (key: ExchangeName, value: IExchangeSchema) => void;
|
|
1863
|
+
/**
|
|
1864
|
+
* Validates exchange schema structure for required properties.
|
|
1865
|
+
*
|
|
1866
|
+
* Performs shallow validation to ensure all required properties exist
|
|
1867
|
+
* and have correct types before registration in the registry.
|
|
1868
|
+
*
|
|
1869
|
+
* @param exchangeSchema - Exchange schema to validate
|
|
1870
|
+
* @throws Error if exchangeName is missing or not a string
|
|
1871
|
+
* @throws Error if getCandles is missing or not a function
|
|
1872
|
+
* @throws Error if formatPrice is missing or not a function
|
|
1873
|
+
* @throws Error if formatQuantity is missing or not a function
|
|
1874
|
+
*/
|
|
1875
|
+
private validateShallow;
|
|
1876
|
+
/**
|
|
1877
|
+
* Overrides an existing exchange schema with partial updates.
|
|
1878
|
+
*
|
|
1879
|
+
* @param key - Exchange name to override
|
|
1880
|
+
* @param value - Partial schema updates
|
|
1881
|
+
* @returns Updated exchange schema
|
|
1882
|
+
* @throws Error if exchange name doesn't exist
|
|
1883
|
+
*/
|
|
350
1884
|
override: (key: ExchangeName, value: Partial<IExchangeSchema>) => IExchangeSchema;
|
|
1885
|
+
/**
|
|
1886
|
+
* Retrieves an exchange schema by name.
|
|
1887
|
+
*
|
|
1888
|
+
* @param key - Exchange name
|
|
1889
|
+
* @returns Exchange schema configuration
|
|
1890
|
+
* @throws Error if exchange name doesn't exist
|
|
1891
|
+
*/
|
|
351
1892
|
get: (key: ExchangeName) => IExchangeSchema;
|
|
352
1893
|
}
|
|
353
1894
|
|
|
1895
|
+
/**
|
|
1896
|
+
* Service for managing strategy schema registry.
|
|
1897
|
+
*
|
|
1898
|
+
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
1899
|
+
* Strategies are registered via addStrategy() and retrieved by name.
|
|
1900
|
+
*/
|
|
354
1901
|
declare class StrategySchemaService {
|
|
355
1902
|
readonly loggerService: LoggerService;
|
|
356
1903
|
private _registry;
|
|
1904
|
+
/**
|
|
1905
|
+
* Registers a new strategy schema.
|
|
1906
|
+
*
|
|
1907
|
+
* @param key - Unique strategy name
|
|
1908
|
+
* @param value - Strategy schema configuration
|
|
1909
|
+
* @throws Error if strategy name already exists
|
|
1910
|
+
*/
|
|
357
1911
|
register: (key: StrategyName, value: IStrategySchema) => void;
|
|
1912
|
+
/**
|
|
1913
|
+
* Validates strategy schema structure for required properties.
|
|
1914
|
+
*
|
|
1915
|
+
* Performs shallow validation to ensure all required properties exist
|
|
1916
|
+
* and have correct types before registration in the registry.
|
|
1917
|
+
*
|
|
1918
|
+
* @param strategySchema - Strategy schema to validate
|
|
1919
|
+
* @throws Error if strategyName is missing or not a string
|
|
1920
|
+
* @throws Error if interval is missing or not a valid SignalInterval
|
|
1921
|
+
* @throws Error if getSignal is missing or not a function
|
|
1922
|
+
*/
|
|
1923
|
+
private validateShallow;
|
|
1924
|
+
/**
|
|
1925
|
+
* Overrides an existing strategy schema with partial updates.
|
|
1926
|
+
*
|
|
1927
|
+
* @param key - Strategy name to override
|
|
1928
|
+
* @param value - Partial schema updates
|
|
1929
|
+
* @returns Updated strategy schema
|
|
1930
|
+
* @throws Error if strategy name doesn't exist
|
|
1931
|
+
*/
|
|
358
1932
|
override: (key: StrategyName, value: Partial<IStrategySchema>) => IStrategySchema;
|
|
1933
|
+
/**
|
|
1934
|
+
* Retrieves a strategy schema by name.
|
|
1935
|
+
*
|
|
1936
|
+
* @param key - Strategy name
|
|
1937
|
+
* @returns Strategy schema configuration
|
|
1938
|
+
* @throws Error if strategy name doesn't exist
|
|
1939
|
+
*/
|
|
359
1940
|
get: (key: StrategyName) => IStrategySchema;
|
|
360
1941
|
}
|
|
361
1942
|
|
|
1943
|
+
/**
|
|
1944
|
+
* Service for managing frame schema registry.
|
|
1945
|
+
*
|
|
1946
|
+
* Uses ToolRegistry from functools-kit for type-safe schema storage.
|
|
1947
|
+
* Frames are registered via addFrame() and retrieved by name.
|
|
1948
|
+
*/
|
|
362
1949
|
declare class FrameSchemaService {
|
|
1950
|
+
readonly loggerService: LoggerService;
|
|
363
1951
|
private _registry;
|
|
1952
|
+
/**
|
|
1953
|
+
* Registers a new frame schema.
|
|
1954
|
+
*
|
|
1955
|
+
* @param key - Unique frame name
|
|
1956
|
+
* @param value - Frame schema configuration
|
|
1957
|
+
* @throws Error if frame name already exists
|
|
1958
|
+
*/
|
|
364
1959
|
register(key: FrameName, value: IFrameSchema): void;
|
|
1960
|
+
/**
|
|
1961
|
+
* Validates frame schema structure for required properties.
|
|
1962
|
+
*
|
|
1963
|
+
* Performs shallow validation to ensure all required properties exist
|
|
1964
|
+
* and have correct types before registration in the registry.
|
|
1965
|
+
*
|
|
1966
|
+
* @param frameSchema - Frame schema to validate
|
|
1967
|
+
* @throws Error if frameName is missing or not a string
|
|
1968
|
+
* @throws Error if interval is missing or not a valid FrameInterval
|
|
1969
|
+
* @throws Error if startDate is missing or not a Date
|
|
1970
|
+
* @throws Error if endDate is missing or not a Date
|
|
1971
|
+
*/
|
|
1972
|
+
private validateShallow;
|
|
1973
|
+
/**
|
|
1974
|
+
* Overrides an existing frame schema with partial updates.
|
|
1975
|
+
*
|
|
1976
|
+
* @param key - Frame name to override
|
|
1977
|
+
* @param value - Partial schema updates
|
|
1978
|
+
* @throws Error if frame name doesn't exist
|
|
1979
|
+
*/
|
|
365
1980
|
override(key: FrameName, value: Partial<IFrameSchema>): void;
|
|
1981
|
+
/**
|
|
1982
|
+
* Retrieves a frame schema by name.
|
|
1983
|
+
*
|
|
1984
|
+
* @param key - Frame name
|
|
1985
|
+
* @returns Frame schema configuration
|
|
1986
|
+
* @throws Error if frame name doesn't exist
|
|
1987
|
+
*/
|
|
366
1988
|
get(key: FrameName): IFrameSchema;
|
|
367
1989
|
}
|
|
368
1990
|
|
|
1991
|
+
/**
|
|
1992
|
+
* Private service for backtest orchestration using async generators.
|
|
1993
|
+
*
|
|
1994
|
+
* Flow:
|
|
1995
|
+
* 1. Get timeframes from frame service
|
|
1996
|
+
* 2. Iterate through timeframes calling tick()
|
|
1997
|
+
* 3. When signal opens: fetch candles and call backtest()
|
|
1998
|
+
* 4. Skip timeframes until signal closes
|
|
1999
|
+
* 5. Yield closed result and continue
|
|
2000
|
+
*
|
|
2001
|
+
* Memory efficient: streams results without array accumulation.
|
|
2002
|
+
* Supports early termination via break in consumer.
|
|
2003
|
+
*/
|
|
369
2004
|
declare class BacktestLogicPrivateService {
|
|
370
2005
|
private readonly loggerService;
|
|
371
2006
|
private readonly strategyGlobalService;
|
|
372
2007
|
private readonly exchangeGlobalService;
|
|
373
2008
|
private readonly frameGlobalService;
|
|
2009
|
+
/**
|
|
2010
|
+
* Runs backtest for a symbol, streaming closed signals as async generator.
|
|
2011
|
+
*
|
|
2012
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2013
|
+
* @yields Closed signal results with PNL
|
|
2014
|
+
*
|
|
2015
|
+
* @example
|
|
2016
|
+
* ```typescript
|
|
2017
|
+
* for await (const result of backtestLogic.run("BTCUSDT")) {
|
|
2018
|
+
* console.log(result.closeReason, result.pnl.pnlPercentage);
|
|
2019
|
+
* if (result.pnl.pnlPercentage < -10) break; // Early termination
|
|
2020
|
+
* }
|
|
2021
|
+
* ```
|
|
2022
|
+
*/
|
|
374
2023
|
run(symbol: string): AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
375
2024
|
}
|
|
376
2025
|
|
|
2026
|
+
/**
|
|
2027
|
+
* Private service for live trading orchestration using async generators.
|
|
2028
|
+
*
|
|
2029
|
+
* Flow:
|
|
2030
|
+
* 1. Infinite while(true) loop for continuous monitoring
|
|
2031
|
+
* 2. Create real-time date with new Date()
|
|
2032
|
+
* 3. Call tick() to check signal status
|
|
2033
|
+
* 4. Yield opened/closed results (skip idle/active)
|
|
2034
|
+
* 5. Sleep for TICK_TTL between iterations
|
|
2035
|
+
*
|
|
2036
|
+
* Features:
|
|
2037
|
+
* - Crash recovery via ClientStrategy.waitForInit()
|
|
2038
|
+
* - Real-time progression with new Date()
|
|
2039
|
+
* - Memory efficient streaming
|
|
2040
|
+
* - Never completes (infinite generator)
|
|
2041
|
+
*/
|
|
377
2042
|
declare class LiveLogicPrivateService {
|
|
378
2043
|
private readonly loggerService;
|
|
379
2044
|
private readonly strategyGlobalService;
|
|
2045
|
+
/**
|
|
2046
|
+
* Runs live trading for a symbol, streaming results as async generator.
|
|
2047
|
+
*
|
|
2048
|
+
* Infinite generator that yields opened and closed signals.
|
|
2049
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
2050
|
+
*
|
|
2051
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2052
|
+
* @yields Opened and closed signal results
|
|
2053
|
+
*
|
|
2054
|
+
* @example
|
|
2055
|
+
* ```typescript
|
|
2056
|
+
* for await (const result of liveLogic.run("BTCUSDT")) {
|
|
2057
|
+
* if (result.action === "opened") {
|
|
2058
|
+
* console.log("New signal:", result.signal.id);
|
|
2059
|
+
* }
|
|
2060
|
+
* if (result.action === "closed") {
|
|
2061
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
2062
|
+
* }
|
|
2063
|
+
* // Infinite loop - will never complete
|
|
2064
|
+
* }
|
|
2065
|
+
* ```
|
|
2066
|
+
*/
|
|
380
2067
|
run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
381
2068
|
}
|
|
382
2069
|
|
|
2070
|
+
/**
|
|
2071
|
+
* Public service for backtest orchestration with context management.
|
|
2072
|
+
*
|
|
2073
|
+
* Wraps BacktestLogicPrivateService with MethodContextService to provide
|
|
2074
|
+
* implicit context propagation for strategyName, exchangeName, and frameName.
|
|
2075
|
+
*
|
|
2076
|
+
* This allows getCandles(), getSignal(), and other functions to work without
|
|
2077
|
+
* explicit context parameters.
|
|
2078
|
+
*
|
|
2079
|
+
* @example
|
|
2080
|
+
* ```typescript
|
|
2081
|
+
* const backtestLogicPublicService = inject(TYPES.backtestLogicPublicService);
|
|
2082
|
+
*
|
|
2083
|
+
* for await (const result of backtestLogicPublicService.run("BTCUSDT", {
|
|
2084
|
+
* strategyName: "my-strategy",
|
|
2085
|
+
* exchangeName: "my-exchange",
|
|
2086
|
+
* frameName: "1d-backtest",
|
|
2087
|
+
* })) {
|
|
2088
|
+
* if (result.action === "closed") {
|
|
2089
|
+
* console.log("PNL:", result.pnl.profit);
|
|
2090
|
+
* }
|
|
2091
|
+
* }
|
|
2092
|
+
* ```
|
|
2093
|
+
*/
|
|
383
2094
|
declare class BacktestLogicPublicService {
|
|
384
2095
|
private readonly loggerService;
|
|
385
2096
|
private readonly backtestLogicPrivateService;
|
|
2097
|
+
/**
|
|
2098
|
+
* Runs backtest for a symbol with context propagation.
|
|
2099
|
+
*
|
|
2100
|
+
* Streams closed signals as async generator. Context is automatically
|
|
2101
|
+
* injected into all framework functions called during iteration.
|
|
2102
|
+
*
|
|
2103
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2104
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
2105
|
+
* @returns Async generator yielding closed signals with PNL
|
|
2106
|
+
*/
|
|
386
2107
|
run: (symbol: string, context: {
|
|
387
2108
|
strategyName: string;
|
|
388
2109
|
exchangeName: string;
|
|
@@ -390,27 +2111,102 @@ declare class BacktestLogicPublicService {
|
|
|
390
2111
|
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
391
2112
|
}
|
|
392
2113
|
|
|
2114
|
+
/**
|
|
2115
|
+
* Public service for live trading orchestration with context management.
|
|
2116
|
+
*
|
|
2117
|
+
* Wraps LiveLogicPrivateService with MethodContextService to provide
|
|
2118
|
+
* implicit context propagation for strategyName and exchangeName.
|
|
2119
|
+
*
|
|
2120
|
+
* This allows getCandles(), getSignal(), and other functions to work without
|
|
2121
|
+
* explicit context parameters.
|
|
2122
|
+
*
|
|
2123
|
+
* Features:
|
|
2124
|
+
* - Infinite async generator (never completes)
|
|
2125
|
+
* - Crash recovery via persisted state
|
|
2126
|
+
* - Real-time progression with Date.now()
|
|
2127
|
+
*
|
|
2128
|
+
* @example
|
|
2129
|
+
* ```typescript
|
|
2130
|
+
* const liveLogicPublicService = inject(TYPES.liveLogicPublicService);
|
|
2131
|
+
*
|
|
2132
|
+
* // Infinite loop - use Ctrl+C to stop
|
|
2133
|
+
* for await (const result of liveLogicPublicService.run("BTCUSDT", {
|
|
2134
|
+
* strategyName: "my-strategy",
|
|
2135
|
+
* exchangeName: "my-exchange",
|
|
2136
|
+
* })) {
|
|
2137
|
+
* if (result.action === "opened") {
|
|
2138
|
+
* console.log("Signal opened:", result.signal);
|
|
2139
|
+
* } else if (result.action === "closed") {
|
|
2140
|
+
* console.log("PNL:", result.pnl.profit);
|
|
2141
|
+
* }
|
|
2142
|
+
* }
|
|
2143
|
+
* ```
|
|
2144
|
+
*/
|
|
393
2145
|
declare class LiveLogicPublicService {
|
|
394
2146
|
private readonly loggerService;
|
|
395
2147
|
private readonly liveLogicPrivateService;
|
|
2148
|
+
/**
|
|
2149
|
+
* Runs live trading for a symbol with context propagation.
|
|
2150
|
+
*
|
|
2151
|
+
* Streams opened and closed signals as infinite async generator.
|
|
2152
|
+
* Context is automatically injected into all framework functions.
|
|
2153
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
2154
|
+
*
|
|
2155
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2156
|
+
* @param context - Execution context with strategy and exchange names
|
|
2157
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
2158
|
+
*/
|
|
396
2159
|
run: (symbol: string, context: {
|
|
397
2160
|
strategyName: string;
|
|
398
2161
|
exchangeName: string;
|
|
399
2162
|
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
400
2163
|
}
|
|
401
2164
|
|
|
2165
|
+
/**
|
|
2166
|
+
* Global service providing access to live trading functionality.
|
|
2167
|
+
*
|
|
2168
|
+
* Simple wrapper around LiveLogicPublicService for dependency injection.
|
|
2169
|
+
* Used by public API exports.
|
|
2170
|
+
*/
|
|
402
2171
|
declare class LiveGlobalService {
|
|
403
2172
|
private readonly loggerService;
|
|
404
2173
|
private readonly liveLogicPublicService;
|
|
2174
|
+
private readonly strategyValidationService;
|
|
2175
|
+
private readonly exchangeValidationService;
|
|
2176
|
+
/**
|
|
2177
|
+
* Runs live trading for a symbol with context propagation.
|
|
2178
|
+
*
|
|
2179
|
+
* Infinite async generator with crash recovery support.
|
|
2180
|
+
*
|
|
2181
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2182
|
+
* @param context - Execution context with strategy and exchange names
|
|
2183
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
2184
|
+
*/
|
|
405
2185
|
run: (symbol: string, context: {
|
|
406
2186
|
strategyName: string;
|
|
407
2187
|
exchangeName: string;
|
|
408
2188
|
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
409
2189
|
}
|
|
410
2190
|
|
|
2191
|
+
/**
|
|
2192
|
+
* Global service providing access to backtest functionality.
|
|
2193
|
+
*
|
|
2194
|
+
* Simple wrapper around BacktestLogicPublicService for dependency injection.
|
|
2195
|
+
* Used by public API exports.
|
|
2196
|
+
*/
|
|
411
2197
|
declare class BacktestGlobalService {
|
|
412
2198
|
private readonly loggerService;
|
|
413
2199
|
private readonly backtestLogicPublicService;
|
|
2200
|
+
private readonly strategyValidationService;
|
|
2201
|
+
private readonly exchangeValidationService;
|
|
2202
|
+
private readonly frameValidationService;
|
|
2203
|
+
/**
|
|
2204
|
+
* Runs backtest for a symbol with context propagation.
|
|
2205
|
+
*
|
|
2206
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2207
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
2208
|
+
* @returns Async generator yielding closed signals with PNL
|
|
2209
|
+
*/
|
|
414
2210
|
run: (symbol: string, context: {
|
|
415
2211
|
strategyName: string;
|
|
416
2212
|
exchangeName: string;
|
|
@@ -418,7 +2214,356 @@ declare class BacktestGlobalService {
|
|
|
418
2214
|
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
419
2215
|
}
|
|
420
2216
|
|
|
2217
|
+
/**
|
|
2218
|
+
* Service for generating and saving backtest markdown reports.
|
|
2219
|
+
*
|
|
2220
|
+
* Features:
|
|
2221
|
+
* - Listens to signal events via onTick callback
|
|
2222
|
+
* - Accumulates closed signals per strategy using memoized storage
|
|
2223
|
+
* - Generates markdown tables with detailed signal information
|
|
2224
|
+
* - Saves reports to disk in logs/backtest/{strategyName}.md
|
|
2225
|
+
*
|
|
2226
|
+
* @example
|
|
2227
|
+
* ```typescript
|
|
2228
|
+
* const service = new BacktestMarkdownService();
|
|
2229
|
+
*
|
|
2230
|
+
* // Add to strategy callbacks
|
|
2231
|
+
* addStrategy({
|
|
2232
|
+
* strategyName: "my-strategy",
|
|
2233
|
+
* callbacks: {
|
|
2234
|
+
* onTick: (symbol, result, backtest) => {
|
|
2235
|
+
* service.tick(result);
|
|
2236
|
+
* }
|
|
2237
|
+
* }
|
|
2238
|
+
* });
|
|
2239
|
+
*
|
|
2240
|
+
* // After backtest, generate and save report
|
|
2241
|
+
* await service.saveReport("my-strategy");
|
|
2242
|
+
* ```
|
|
2243
|
+
*/
|
|
2244
|
+
declare class BacktestMarkdownService {
|
|
2245
|
+
/** Logger service for debug output */
|
|
2246
|
+
private readonly loggerService;
|
|
2247
|
+
/**
|
|
2248
|
+
* Memoized function to get or create ReportStorage for a strategy.
|
|
2249
|
+
* Each strategy gets its own isolated storage instance.
|
|
2250
|
+
*/
|
|
2251
|
+
private getStorage;
|
|
2252
|
+
/**
|
|
2253
|
+
* Processes tick events and accumulates closed signals.
|
|
2254
|
+
* Should be called from IStrategyCallbacks.onTick.
|
|
2255
|
+
*
|
|
2256
|
+
* Only processes closed signals - opened signals are ignored.
|
|
2257
|
+
*
|
|
2258
|
+
* @param data - Tick result from strategy execution (opened or closed)
|
|
2259
|
+
*
|
|
2260
|
+
* @example
|
|
2261
|
+
* ```typescript
|
|
2262
|
+
* const service = new BacktestMarkdownService();
|
|
2263
|
+
*
|
|
2264
|
+
* callbacks: {
|
|
2265
|
+
* onTick: (symbol, result, backtest) => {
|
|
2266
|
+
* service.tick(result);
|
|
2267
|
+
* }
|
|
2268
|
+
* }
|
|
2269
|
+
* ```
|
|
2270
|
+
*/
|
|
2271
|
+
private tick;
|
|
2272
|
+
/**
|
|
2273
|
+
* Generates markdown report with all closed signals for a strategy.
|
|
2274
|
+
* Delegates to ReportStorage.generateReport().
|
|
2275
|
+
*
|
|
2276
|
+
* @param strategyName - Strategy name to generate report for
|
|
2277
|
+
* @returns Markdown formatted report string with table of all closed signals
|
|
2278
|
+
*
|
|
2279
|
+
* @example
|
|
2280
|
+
* ```typescript
|
|
2281
|
+
* const service = new BacktestMarkdownService();
|
|
2282
|
+
* const markdown = service.generateReport("my-strategy");
|
|
2283
|
+
* console.log(markdown);
|
|
2284
|
+
* ```
|
|
2285
|
+
*/
|
|
2286
|
+
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
2287
|
+
/**
|
|
2288
|
+
* Saves strategy report to disk.
|
|
2289
|
+
* Creates directory if it doesn't exist.
|
|
2290
|
+
* Delegates to ReportStorage.dump().
|
|
2291
|
+
*
|
|
2292
|
+
* @param strategyName - Strategy name to save report for
|
|
2293
|
+
* @param path - Directory path to save report (default: "./logs/backtest")
|
|
2294
|
+
*
|
|
2295
|
+
* @example
|
|
2296
|
+
* ```typescript
|
|
2297
|
+
* const service = new BacktestMarkdownService();
|
|
2298
|
+
*
|
|
2299
|
+
* // Save to default path: ./logs/backtest/my-strategy.md
|
|
2300
|
+
* await service.dump("my-strategy");
|
|
2301
|
+
*
|
|
2302
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
2303
|
+
* await service.dump("my-strategy", "./custom/path");
|
|
2304
|
+
* ```
|
|
2305
|
+
*/
|
|
2306
|
+
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
2307
|
+
/**
|
|
2308
|
+
* Clears accumulated signal data from storage.
|
|
2309
|
+
* If strategyName is provided, clears only that strategy's data.
|
|
2310
|
+
* If strategyName is omitted, clears all strategies' data.
|
|
2311
|
+
*
|
|
2312
|
+
* @param strategyName - Optional strategy name to clear specific strategy data
|
|
2313
|
+
*
|
|
2314
|
+
* @example
|
|
2315
|
+
* ```typescript
|
|
2316
|
+
* const service = new BacktestMarkdownService();
|
|
2317
|
+
*
|
|
2318
|
+
* // Clear specific strategy data
|
|
2319
|
+
* await service.clear("my-strategy");
|
|
2320
|
+
*
|
|
2321
|
+
* // Clear all strategies' data
|
|
2322
|
+
* await service.clear();
|
|
2323
|
+
* ```
|
|
2324
|
+
*/
|
|
2325
|
+
clear: (strategyName?: StrategyName) => Promise<void>;
|
|
2326
|
+
/**
|
|
2327
|
+
* Initializes the service by subscribing to backtest signal events.
|
|
2328
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
2329
|
+
* Automatically called on first use.
|
|
2330
|
+
*
|
|
2331
|
+
* @example
|
|
2332
|
+
* ```typescript
|
|
2333
|
+
* const service = new BacktestMarkdownService();
|
|
2334
|
+
* await service.init(); // Subscribe to backtest events
|
|
2335
|
+
* ```
|
|
2336
|
+
*/
|
|
2337
|
+
protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
/**
|
|
2341
|
+
* Service for generating and saving live trading markdown reports.
|
|
2342
|
+
*
|
|
2343
|
+
* Features:
|
|
2344
|
+
* - Listens to all signal events via onTick callback
|
|
2345
|
+
* - Accumulates all events (idle, opened, active, closed) per strategy
|
|
2346
|
+
* - Generates markdown tables with detailed event information
|
|
2347
|
+
* - Provides trading statistics (win rate, average PNL)
|
|
2348
|
+
* - Saves reports to disk in logs/live/{strategyName}.md
|
|
2349
|
+
*
|
|
2350
|
+
* @example
|
|
2351
|
+
* ```typescript
|
|
2352
|
+
* const service = new LiveMarkdownService();
|
|
2353
|
+
*
|
|
2354
|
+
* // Add to strategy callbacks
|
|
2355
|
+
* addStrategy({
|
|
2356
|
+
* strategyName: "my-strategy",
|
|
2357
|
+
* callbacks: {
|
|
2358
|
+
* onTick: (symbol, result, backtest) => {
|
|
2359
|
+
* if (!backtest) {
|
|
2360
|
+
* service.tick(result);
|
|
2361
|
+
* }
|
|
2362
|
+
* }
|
|
2363
|
+
* }
|
|
2364
|
+
* });
|
|
2365
|
+
*
|
|
2366
|
+
* // Later: generate and save report
|
|
2367
|
+
* await service.dump("my-strategy");
|
|
2368
|
+
* ```
|
|
2369
|
+
*/
|
|
2370
|
+
declare class LiveMarkdownService {
|
|
2371
|
+
/** Logger service for debug output */
|
|
2372
|
+
private readonly loggerService;
|
|
2373
|
+
/**
|
|
2374
|
+
* Memoized function to get or create ReportStorage for a strategy.
|
|
2375
|
+
* Each strategy gets its own isolated storage instance.
|
|
2376
|
+
*/
|
|
2377
|
+
private getStorage;
|
|
2378
|
+
/**
|
|
2379
|
+
* Processes tick events and accumulates all event types.
|
|
2380
|
+
* Should be called from IStrategyCallbacks.onTick.
|
|
2381
|
+
*
|
|
2382
|
+
* Processes all event types: idle, opened, active, closed.
|
|
2383
|
+
*
|
|
2384
|
+
* @param data - Tick result from strategy execution
|
|
2385
|
+
*
|
|
2386
|
+
* @example
|
|
2387
|
+
* ```typescript
|
|
2388
|
+
* const service = new LiveMarkdownService();
|
|
2389
|
+
*
|
|
2390
|
+
* callbacks: {
|
|
2391
|
+
* onTick: (symbol, result, backtest) => {
|
|
2392
|
+
* if (!backtest) {
|
|
2393
|
+
* service.tick(result);
|
|
2394
|
+
* }
|
|
2395
|
+
* }
|
|
2396
|
+
* }
|
|
2397
|
+
* ```
|
|
2398
|
+
*/
|
|
2399
|
+
private tick;
|
|
2400
|
+
/**
|
|
2401
|
+
* Generates markdown report with all events for a strategy.
|
|
2402
|
+
* Delegates to ReportStorage.getReport().
|
|
2403
|
+
*
|
|
2404
|
+
* @param strategyName - Strategy name to generate report for
|
|
2405
|
+
* @returns Markdown formatted report string with table of all events
|
|
2406
|
+
*
|
|
2407
|
+
* @example
|
|
2408
|
+
* ```typescript
|
|
2409
|
+
* const service = new LiveMarkdownService();
|
|
2410
|
+
* const markdown = await service.getReport("my-strategy");
|
|
2411
|
+
* console.log(markdown);
|
|
2412
|
+
* ```
|
|
2413
|
+
*/
|
|
2414
|
+
getReport: (strategyName: StrategyName) => Promise<string>;
|
|
2415
|
+
/**
|
|
2416
|
+
* Saves strategy report to disk.
|
|
2417
|
+
* Creates directory if it doesn't exist.
|
|
2418
|
+
* Delegates to ReportStorage.dump().
|
|
2419
|
+
*
|
|
2420
|
+
* @param strategyName - Strategy name to save report for
|
|
2421
|
+
* @param path - Directory path to save report (default: "./logs/live")
|
|
2422
|
+
*
|
|
2423
|
+
* @example
|
|
2424
|
+
* ```typescript
|
|
2425
|
+
* const service = new LiveMarkdownService();
|
|
2426
|
+
*
|
|
2427
|
+
* // Save to default path: ./logs/live/my-strategy.md
|
|
2428
|
+
* await service.dump("my-strategy");
|
|
2429
|
+
*
|
|
2430
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
2431
|
+
* await service.dump("my-strategy", "./custom/path");
|
|
2432
|
+
* ```
|
|
2433
|
+
*/
|
|
2434
|
+
dump: (strategyName: StrategyName, path?: string) => Promise<void>;
|
|
2435
|
+
/**
|
|
2436
|
+
* Clears accumulated event data from storage.
|
|
2437
|
+
* If strategyName is provided, clears only that strategy's data.
|
|
2438
|
+
* If strategyName is omitted, clears all strategies' data.
|
|
2439
|
+
*
|
|
2440
|
+
* @param strategyName - Optional strategy name to clear specific strategy data
|
|
2441
|
+
*
|
|
2442
|
+
* @example
|
|
2443
|
+
* ```typescript
|
|
2444
|
+
* const service = new LiveMarkdownService();
|
|
2445
|
+
*
|
|
2446
|
+
* // Clear specific strategy data
|
|
2447
|
+
* await service.clear("my-strategy");
|
|
2448
|
+
*
|
|
2449
|
+
* // Clear all strategies' data
|
|
2450
|
+
* await service.clear();
|
|
2451
|
+
* ```
|
|
2452
|
+
*/
|
|
2453
|
+
clear: (strategyName?: StrategyName) => Promise<void>;
|
|
2454
|
+
/**
|
|
2455
|
+
* Initializes the service by subscribing to live signal events.
|
|
2456
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
2457
|
+
* Automatically called on first use.
|
|
2458
|
+
*
|
|
2459
|
+
* @example
|
|
2460
|
+
* ```typescript
|
|
2461
|
+
* const service = new LiveMarkdownService();
|
|
2462
|
+
* await service.init(); // Subscribe to live events
|
|
2463
|
+
* ```
|
|
2464
|
+
*/
|
|
2465
|
+
protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
/**
|
|
2469
|
+
* @class ExchangeValidationService
|
|
2470
|
+
* Service for managing and validating exchange configurations
|
|
2471
|
+
*/
|
|
2472
|
+
declare class ExchangeValidationService {
|
|
2473
|
+
/**
|
|
2474
|
+
* @private
|
|
2475
|
+
* @readonly
|
|
2476
|
+
* Injected logger service instance
|
|
2477
|
+
*/
|
|
2478
|
+
private readonly loggerService;
|
|
2479
|
+
/**
|
|
2480
|
+
* @private
|
|
2481
|
+
* Map storing exchange schemas by exchange name
|
|
2482
|
+
*/
|
|
2483
|
+
private _exchangeMap;
|
|
2484
|
+
/**
|
|
2485
|
+
* Adds an exchange schema to the validation service
|
|
2486
|
+
* @public
|
|
2487
|
+
* @throws {Error} If exchangeName already exists
|
|
2488
|
+
*/
|
|
2489
|
+
addExchange: (exchangeName: ExchangeName, exchangeSchema: IExchangeSchema) => void;
|
|
2490
|
+
/**
|
|
2491
|
+
* Validates the existence of an exchange
|
|
2492
|
+
* @public
|
|
2493
|
+
* @throws {Error} If exchangeName is not found
|
|
2494
|
+
* Memoized function to cache validation results
|
|
2495
|
+
*/
|
|
2496
|
+
validate: (exchangeName: ExchangeName, source: string) => void;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* @class StrategyValidationService
|
|
2501
|
+
* Service for managing and validating strategy configurations
|
|
2502
|
+
*/
|
|
2503
|
+
declare class StrategyValidationService {
|
|
2504
|
+
/**
|
|
2505
|
+
* @private
|
|
2506
|
+
* @readonly
|
|
2507
|
+
* Injected logger service instance
|
|
2508
|
+
*/
|
|
2509
|
+
private readonly loggerService;
|
|
2510
|
+
/**
|
|
2511
|
+
* @private
|
|
2512
|
+
* Map storing strategy schemas by strategy name
|
|
2513
|
+
*/
|
|
2514
|
+
private _strategyMap;
|
|
2515
|
+
/**
|
|
2516
|
+
* Adds a strategy schema to the validation service
|
|
2517
|
+
* @public
|
|
2518
|
+
* @throws {Error} If strategyName already exists
|
|
2519
|
+
*/
|
|
2520
|
+
addStrategy: (strategyName: StrategyName, strategySchema: IStrategySchema) => void;
|
|
2521
|
+
/**
|
|
2522
|
+
* Validates the existence of a strategy
|
|
2523
|
+
* @public
|
|
2524
|
+
* @throws {Error} If strategyName is not found
|
|
2525
|
+
* Memoized function to cache validation results
|
|
2526
|
+
*/
|
|
2527
|
+
validate: (strategyName: StrategyName, source: string) => void;
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
/**
|
|
2531
|
+
* @class FrameValidationService
|
|
2532
|
+
* Service for managing and validating frame configurations
|
|
2533
|
+
*/
|
|
2534
|
+
declare class FrameValidationService {
|
|
2535
|
+
/**
|
|
2536
|
+
* @private
|
|
2537
|
+
* @readonly
|
|
2538
|
+
* Injected logger service instance
|
|
2539
|
+
*/
|
|
2540
|
+
private readonly loggerService;
|
|
2541
|
+
/**
|
|
2542
|
+
* @private
|
|
2543
|
+
* Map storing frame schemas by frame name
|
|
2544
|
+
*/
|
|
2545
|
+
private _frameMap;
|
|
2546
|
+
/**
|
|
2547
|
+
* Adds a frame schema to the validation service
|
|
2548
|
+
* @public
|
|
2549
|
+
* @throws {Error} If frameName already exists
|
|
2550
|
+
*/
|
|
2551
|
+
addFrame: (frameName: FrameName, frameSchema: IFrameSchema) => void;
|
|
2552
|
+
/**
|
|
2553
|
+
* Validates the existence of a frame
|
|
2554
|
+
* @public
|
|
2555
|
+
* @throws {Error} If frameName is not found
|
|
2556
|
+
* Memoized function to cache validation results
|
|
2557
|
+
*/
|
|
2558
|
+
validate: (frameName: FrameName, source: string) => void;
|
|
2559
|
+
}
|
|
2560
|
+
|
|
421
2561
|
declare const backtest: {
|
|
2562
|
+
exchangeValidationService: ExchangeValidationService;
|
|
2563
|
+
strategyValidationService: StrategyValidationService;
|
|
2564
|
+
frameValidationService: FrameValidationService;
|
|
2565
|
+
backtestMarkdownService: BacktestMarkdownService;
|
|
2566
|
+
liveMarkdownService: LiveMarkdownService;
|
|
422
2567
|
backtestLogicPublicService: BacktestLogicPublicService;
|
|
423
2568
|
liveLogicPublicService: LiveLogicPublicService;
|
|
424
2569
|
backtestLogicPrivateService: BacktestLogicPrivateService;
|
|
@@ -443,4 +2588,4 @@ declare const backtest: {
|
|
|
443
2588
|
loggerService: LoggerService;
|
|
444
2589
|
};
|
|
445
2590
|
|
|
446
|
-
export { Backtest, type CandleInterval, ExecutionContextService, type FrameInterval, type ICandleData, type IExchangeSchema, type IFrameSchema, type IPersistBase, 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,
|
|
2591
|
+
export { Backtest, type CandleInterval, ExecutionContextService, type FrameInterval, type ICandleData, type IExchangeSchema, type IFrameSchema, type IPersistBase, 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, listenError, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, setLogger };
|