backtest-kit 1.0.3 → 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 +4132 -407
- package/build/index.mjs +4116 -401
- package/package.json +2 -9
- package/types.d.ts +2432 -69
package/types.d.ts
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
import * as di_scoped from 'di-scoped';
|
|
2
2
|
import * as functools_kit from 'functools-kit';
|
|
3
3
|
|
|
4
|
-
interface IExecutionContext {
|
|
5
|
-
when: Date;
|
|
6
|
-
backtest: boolean;
|
|
7
|
-
}
|
|
8
|
-
declare const ExecutionContextService: (new () => {
|
|
9
|
-
readonly context: IExecutionContext;
|
|
10
|
-
}) & Omit<{
|
|
11
|
-
new (context: IExecutionContext): {
|
|
12
|
-
readonly context: IExecutionContext;
|
|
13
|
-
};
|
|
14
|
-
}, "prototype"> & di_scoped.IScopedClassRun<[context: IExecutionContext]>;
|
|
15
|
-
type TExecutionContextService = InstanceType<typeof ExecutionContextService>;
|
|
16
|
-
|
|
17
4
|
/**
|
|
18
5
|
* Interface representing a logging mechanism for the swarm system.
|
|
19
6
|
* Provides methods to record messages at different severity levels, used across components like agents, sessions, states, storage, swarms, history, embeddings, completions, and policies.
|
|
@@ -35,194 +22,2570 @@ interface ILogger {
|
|
|
35
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.
|
|
36
23
|
*/
|
|
37
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;
|
|
38
30
|
}
|
|
39
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
|
+
*/
|
|
49
|
+
declare function setLogger(logger: ILogger): Promise<void>;
|
|
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
|
+
*/
|
|
57
|
+
interface IExecutionContext {
|
|
58
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
59
|
+
symbol: string;
|
|
60
|
+
/** Current timestamp for operation */
|
|
61
|
+
when: Date;
|
|
62
|
+
/** Whether running in backtest mode (true) or live mode (false) */
|
|
63
|
+
backtest: boolean;
|
|
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
|
+
*/
|
|
84
|
+
declare const ExecutionContextService: (new () => {
|
|
85
|
+
readonly context: IExecutionContext;
|
|
86
|
+
}) & Omit<{
|
|
87
|
+
new (context: IExecutionContext): {
|
|
88
|
+
readonly context: IExecutionContext;
|
|
89
|
+
};
|
|
90
|
+
}, "prototype"> & di_scoped.IScopedClassRun<[context: IExecutionContext]>;
|
|
91
|
+
/**
|
|
92
|
+
* Type helper for ExecutionContextService instance.
|
|
93
|
+
* Used for dependency injection type annotations.
|
|
94
|
+
*/
|
|
95
|
+
type TExecutionContextService = InstanceType<typeof ExecutionContextService>;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Candle time interval for fetching historical data.
|
|
99
|
+
*/
|
|
40
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
|
+
*/
|
|
41
105
|
interface ICandleData {
|
|
106
|
+
/** Unix timestamp in milliseconds when candle opened */
|
|
42
107
|
timestamp: number;
|
|
108
|
+
/** Opening price at candle start */
|
|
43
109
|
open: number;
|
|
110
|
+
/** Highest price during candle period */
|
|
44
111
|
high: number;
|
|
112
|
+
/** Lowest price during candle period */
|
|
45
113
|
low: number;
|
|
114
|
+
/** Closing price at candle end */
|
|
46
115
|
close: number;
|
|
116
|
+
/** Trading volume during candle period */
|
|
47
117
|
volume: number;
|
|
48
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Exchange parameters passed to ClientExchange constructor.
|
|
121
|
+
* Combines schema with runtime dependencies.
|
|
122
|
+
*/
|
|
49
123
|
interface IExchangeParams extends IExchangeSchema {
|
|
124
|
+
/** Logger service for debug output */
|
|
50
125
|
logger: ILogger;
|
|
126
|
+
/** Execution context service (symbol, when, backtest flag) */
|
|
51
127
|
execution: TExecutionContextService;
|
|
52
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Optional callbacks for exchange data events.
|
|
131
|
+
*/
|
|
53
132
|
interface IExchangeCallbacks {
|
|
133
|
+
/** Called when candle data is fetched */
|
|
54
134
|
onCandleData: (symbol: string, interval: CandleInterval, since: Date, limit: number, data: ICandleData[]) => void;
|
|
55
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Exchange schema registered via addExchange().
|
|
138
|
+
* Defines candle data source and formatting logic.
|
|
139
|
+
*/
|
|
56
140
|
interface IExchangeSchema {
|
|
141
|
+
/** Unique exchange identifier for registration */
|
|
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
|
+
*/
|
|
57
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
|
+
*/
|
|
58
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
|
+
*/
|
|
59
168
|
formatPrice: (symbol: string, price: number) => Promise<string>;
|
|
169
|
+
/** Optional lifecycle event callbacks (onCandleData) */
|
|
60
170
|
callbacks?: Partial<IExchangeCallbacks>;
|
|
61
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Exchange interface implemented by ClientExchange.
|
|
174
|
+
* Provides candle data access and VWAP calculation.
|
|
175
|
+
*/
|
|
62
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
|
+
*/
|
|
63
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
|
+
*/
|
|
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
|
+
*/
|
|
64
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
|
+
*/
|
|
65
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
|
+
*/
|
|
66
220
|
getAveragePrice: (symbol: string) => Promise<number>;
|
|
67
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Unique exchange identifier.
|
|
224
|
+
*/
|
|
225
|
+
type ExchangeName = string;
|
|
68
226
|
|
|
69
|
-
|
|
70
|
-
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
361
|
+
interface ISignalDto {
|
|
362
|
+
/** Optional signal ID (auto-generated if not provided) */
|
|
363
|
+
id?: string;
|
|
364
|
+
/** Trade direction: "long" (buy) or "short" (sell) */
|
|
71
365
|
position: "long" | "short";
|
|
72
|
-
|
|
366
|
+
/** Human-readable description of signal reason */
|
|
367
|
+
note?: string;
|
|
368
|
+
/** Entry price for the position */
|
|
73
369
|
priceOpen: number;
|
|
370
|
+
/** Take profit target price (must be > priceOpen for long, < priceOpen for short) */
|
|
74
371
|
priceTakeProfit: number;
|
|
372
|
+
/** Stop loss exit price (must be < priceOpen for long, > priceOpen for short) */
|
|
75
373
|
priceStopLoss: number;
|
|
374
|
+
/** Expected duration in minutes before time_expired */
|
|
76
375
|
minuteEstimatedTime: number;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Complete signal with auto-generated id.
|
|
379
|
+
* Used throughout the system after validation.
|
|
380
|
+
*/
|
|
381
|
+
interface ISignalRow extends ISignalDto {
|
|
382
|
+
/** Unique signal identifier (UUID v4 auto-generated) */
|
|
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 */
|
|
77
389
|
timestamp: number;
|
|
390
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
391
|
+
symbol: string;
|
|
78
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Optional lifecycle callbacks for signal events.
|
|
395
|
+
* Called when signals are opened, active, idle, or closed.
|
|
396
|
+
*/
|
|
79
397
|
interface IStrategyCallbacks {
|
|
80
|
-
|
|
81
|
-
|
|
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;
|
|
82
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Strategy schema registered via addStrategy().
|
|
411
|
+
* Defines signal generation logic and configuration.
|
|
412
|
+
*/
|
|
83
413
|
interface IStrategySchema {
|
|
84
|
-
|
|
414
|
+
/** Unique strategy identifier for registration */
|
|
415
|
+
strategyName: StrategyName;
|
|
416
|
+
/** Minimum interval between getSignal calls (throttling) */
|
|
417
|
+
interval: SignalInterval;
|
|
418
|
+
/** Signal generation function (returns null if no signal, validated DTO if signal) */
|
|
419
|
+
getSignal: (symbol: string) => Promise<ISignalDto | null>;
|
|
420
|
+
/** Optional lifecycle event callbacks (onOpen, onClose) */
|
|
85
421
|
callbacks?: Partial<IStrategyCallbacks>;
|
|
86
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Reason why signal was closed.
|
|
425
|
+
* Used in discriminated union for type-safe handling.
|
|
426
|
+
*/
|
|
87
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
|
+
*/
|
|
88
432
|
interface IStrategyPnL {
|
|
433
|
+
/** Profit/loss as percentage (e.g., 1.5 for +1.5%, -2.3 for -2.3%) */
|
|
89
434
|
pnlPercentage: number;
|
|
435
|
+
/** Entry price adjusted with slippage and fees */
|
|
90
436
|
priceOpen: number;
|
|
437
|
+
/** Exit price adjusted with slippage and fees */
|
|
91
438
|
priceClose: number;
|
|
92
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Tick result: no active signal, idle state.
|
|
442
|
+
*/
|
|
93
443
|
interface IStrategyTickResultIdle {
|
|
444
|
+
/** Discriminator for type-safe union */
|
|
94
445
|
action: "idle";
|
|
446
|
+
/** No signal in idle state */
|
|
95
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;
|
|
96
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Tick result: new signal just created.
|
|
457
|
+
* Triggered after getSignal validation and persistence.
|
|
458
|
+
*/
|
|
97
459
|
interface IStrategyTickResultOpened {
|
|
460
|
+
/** Discriminator for type-safe union */
|
|
98
461
|
action: "opened";
|
|
99
|
-
signal
|
|
462
|
+
/** Newly created and validated signal with generated ID */
|
|
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;
|
|
100
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Tick result: signal is being monitored.
|
|
473
|
+
* Waiting for TP/SL or time expiration.
|
|
474
|
+
*/
|
|
101
475
|
interface IStrategyTickResultActive {
|
|
476
|
+
/** Discriminator for type-safe union */
|
|
102
477
|
action: "active";
|
|
103
|
-
signal
|
|
478
|
+
/** Currently monitored signal */
|
|
479
|
+
signal: ISignalRow;
|
|
480
|
+
/** Current VWAP price for monitoring */
|
|
104
481
|
currentPrice: number;
|
|
482
|
+
/** Strategy name for tracking */
|
|
483
|
+
strategyName: StrategyName;
|
|
484
|
+
/** Exchange name for tracking */
|
|
485
|
+
exchangeName: ExchangeName;
|
|
105
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Tick result: signal closed with PNL.
|
|
489
|
+
* Final state with close reason and profit/loss calculation.
|
|
490
|
+
*/
|
|
106
491
|
interface IStrategyTickResultClosed {
|
|
492
|
+
/** Discriminator for type-safe union */
|
|
107
493
|
action: "closed";
|
|
108
|
-
signal
|
|
494
|
+
/** Completed signal with original parameters */
|
|
495
|
+
signal: ISignalRow;
|
|
496
|
+
/** Final VWAP price at close */
|
|
109
497
|
currentPrice: number;
|
|
498
|
+
/** Why signal closed (time_expired | take_profit | stop_loss) */
|
|
110
499
|
closeReason: StrategyCloseReason;
|
|
500
|
+
/** Unix timestamp in milliseconds when signal closed */
|
|
501
|
+
closeTimestamp: number;
|
|
502
|
+
/** Profit/loss calculation with fees and slippage */
|
|
111
503
|
pnl: IStrategyPnL;
|
|
504
|
+
/** Strategy name for tracking */
|
|
505
|
+
strategyName: StrategyName;
|
|
506
|
+
/** Exchange name for tracking */
|
|
507
|
+
exchangeName: ExchangeName;
|
|
112
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Discriminated union of all tick results.
|
|
511
|
+
* Use type guards: `result.action === "closed"` for type safety.
|
|
512
|
+
*/
|
|
113
513
|
type IStrategyTickResult = IStrategyTickResultIdle | IStrategyTickResultOpened | IStrategyTickResultActive | IStrategyTickResultClosed;
|
|
514
|
+
/**
|
|
515
|
+
* Backtest always returns closed result (TP/SL or time_expired).
|
|
516
|
+
*/
|
|
517
|
+
type IStrategyBacktestResult = IStrategyTickResultClosed;
|
|
518
|
+
/**
|
|
519
|
+
* Strategy interface implemented by ClientStrategy.
|
|
520
|
+
* Defines core strategy execution methods.
|
|
521
|
+
*/
|
|
114
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
|
+
*/
|
|
115
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
|
+
*/
|
|
538
|
+
backtest: (candles: ICandleData[]) => Promise<IStrategyBacktestResult>;
|
|
116
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Unique strategy identifier.
|
|
542
|
+
*/
|
|
543
|
+
type StrategyName = string;
|
|
117
544
|
|
|
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
|
+
*/
|
|
118
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
|
+
*/
|
|
119
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
|
+
*/
|
|
646
|
+
declare function addFrame(frameSchema: IFrameSchema): void;
|
|
120
647
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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;
|
|
143
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
|
+
*/
|
|
144
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
|
+
*/
|
|
145
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
|
+
*/
|
|
146
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
|
+
*/
|
|
147
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
|
+
*/
|
|
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
|
+
*/
|
|
917
|
+
declare function getMode(): Promise<"backtest" | "live">;
|
|
918
|
+
|
|
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
|
+
*/
|
|
924
|
+
interface ISignalData {
|
|
925
|
+
/** Current signal state (null when no active signal) */
|
|
926
|
+
signalRow: ISignalRow | null;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Type helper for PersistBase instance.
|
|
930
|
+
*/
|
|
931
|
+
type TPersistBase = InstanceType<typeof PersistBase>;
|
|
932
|
+
/**
|
|
933
|
+
* Constructor type for PersistBase.
|
|
934
|
+
* Used for custom persistence adapters.
|
|
935
|
+
*/
|
|
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
|
+
*/
|
|
940
|
+
type EntityId = string | number;
|
|
941
|
+
/**
|
|
942
|
+
* Base interface for persisted entities.
|
|
943
|
+
*/
|
|
944
|
+
interface IEntity {
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Persistence interface for CRUD operations.
|
|
948
|
+
* Implemented by PersistBase.
|
|
949
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
982
|
+
writeValue(entityId: EntityId, entity: Entity): Promise<void>;
|
|
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
|
+
*/
|
|
1001
|
+
declare const PersistBase: {
|
|
1002
|
+
new <EntityName extends string = string>(entityName: EntityName, baseDir?: string): {
|
|
1003
|
+
/** Computed directory path for entity storage */
|
|
1004
|
+
_directory: string;
|
|
1005
|
+
readonly entityName: EntityName;
|
|
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
|
+
*/
|
|
1013
|
+
_getFilePath(entityId: EntityId): string;
|
|
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
|
+
*/
|
|
1020
|
+
getCount(): Promise<number>;
|
|
1021
|
+
readValue<T extends IEntity = IEntity>(entityId: EntityId): Promise<T>;
|
|
1022
|
+
hasValue(entityId: EntityId): Promise<boolean>;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1054
|
+
keys(): AsyncGenerator<EntityId>;
|
|
1055
|
+
/**
|
|
1056
|
+
* Filters entities by predicate function.
|
|
1057
|
+
*
|
|
1058
|
+
* @param predicate - Filter function
|
|
1059
|
+
* @returns AsyncGenerator yielding filtered entities
|
|
1060
|
+
*/
|
|
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
|
+
*/
|
|
1069
|
+
take<T extends IEntity = IEntity>(total: number, predicate?: (value: T) => boolean): AsyncGenerator<T>;
|
|
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
|
+
*/
|
|
1077
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<any>;
|
|
1078
|
+
};
|
|
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
|
+
*/
|
|
1091
|
+
declare class PersistSignalUtils {
|
|
1092
|
+
private PersistSignalFactory;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1131
|
+
writeSignalData: (signalRow: ISignalRow | null, strategyName: StrategyName, symbol: string) => Promise<void>;
|
|
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
|
+
*/
|
|
1149
|
+
declare const PersistSignalAdaper: PersistSignalUtils;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1178
|
+
run: (symbol: string, context: {
|
|
1179
|
+
strategyName: string;
|
|
1180
|
+
exchangeName: string;
|
|
1181
|
+
frameName: string;
|
|
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>;
|
|
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
|
+
*/
|
|
1257
|
+
declare const Backtest: BacktestUtils;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1299
|
+
run: (symbol: string, context: {
|
|
1300
|
+
strategyName: string;
|
|
1301
|
+
exchangeName: string;
|
|
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>;
|
|
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
|
+
*/
|
|
1373
|
+
declare const Live: LiveUtils;
|
|
148
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
|
+
*/
|
|
149
1386
|
declare class LoggerService implements ILogger {
|
|
1387
|
+
private readonly methodContextService;
|
|
1388
|
+
private readonly executionContextService;
|
|
150
1389
|
private _commonLogger;
|
|
1390
|
+
/**
|
|
1391
|
+
* Gets current method context if available.
|
|
1392
|
+
* Contains strategyName, exchangeName, frameName from MethodContextService.
|
|
1393
|
+
*/
|
|
1394
|
+
private get methodContext();
|
|
1395
|
+
/**
|
|
1396
|
+
* Gets current execution context if available.
|
|
1397
|
+
* Contains symbol, when, backtest from ExecutionContextService.
|
|
1398
|
+
*/
|
|
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
|
+
*/
|
|
151
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
|
+
*/
|
|
152
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
|
+
*/
|
|
153
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
|
+
*/
|
|
154
1433
|
setLogger: (logger: ILogger) => void;
|
|
155
1434
|
}
|
|
156
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
|
+
*/
|
|
157
1462
|
declare class ClientExchange implements IExchange {
|
|
158
1463
|
readonly params: IExchangeParams;
|
|
159
1464
|
constructor(params: IExchangeParams);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1498
|
+
getAveragePrice(symbol: string): Promise<number>;
|
|
1499
|
+
formatQuantity(symbol: string, quantity: number): Promise<string>;
|
|
1500
|
+
formatPrice(symbol: string, price: number): Promise<string>;
|
|
164
1501
|
}
|
|
165
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
|
+
*/
|
|
166
1525
|
declare class ExchangeConnectionService implements IExchange {
|
|
167
1526
|
private readonly loggerService;
|
|
168
1527
|
private readonly executionContextService;
|
|
169
1528
|
private readonly exchangeSchemaService;
|
|
170
|
-
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
171
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
|
+
*/
|
|
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
|
+
*/
|
|
172
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
|
+
*/
|
|
173
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
|
+
*/
|
|
174
1592
|
formatQuantity: (symbol: string, quantity: number) => Promise<string>;
|
|
175
1593
|
}
|
|
176
1594
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
+
*/
|
|
191
1616
|
declare class StrategyConnectionService implements IStrategy {
|
|
192
1617
|
private readonly loggerService;
|
|
193
1618
|
private readonly executionContextService;
|
|
194
1619
|
private readonly strategySchemaService;
|
|
195
1620
|
private readonly exchangeConnectionService;
|
|
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
|
+
*/
|
|
196
1631
|
private getStrategy;
|
|
197
|
-
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1650
|
+
backtest: (candles: ICandleData[]) => Promise<IStrategyBacktestResult>;
|
|
1651
|
+
}
|
|
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
|
+
*/
|
|
1664
|
+
declare class ClientFrame implements IFrame {
|
|
1665
|
+
readonly params: IFrameParams;
|
|
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
|
+
*/
|
|
1675
|
+
getTimeframe: ((symbol: string) => Promise<Date[]>) & functools_kit.ISingleshotClearable;
|
|
1676
|
+
}
|
|
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
|
+
*/
|
|
1700
|
+
declare class FrameConnectionService implements IFrame {
|
|
1701
|
+
private readonly loggerService;
|
|
1702
|
+
private readonly frameSchemaService;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1723
|
+
getTimeframe: (symbol: string) => Promise<Date[]>;
|
|
198
1724
|
}
|
|
199
1725
|
|
|
200
|
-
|
|
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
|
+
*/
|
|
1734
|
+
declare class ExchangeGlobalService {
|
|
201
1735
|
private readonly loggerService;
|
|
202
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
|
+
*/
|
|
203
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
|
+
*/
|
|
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
|
+
*/
|
|
204
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
|
+
*/
|
|
205
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
|
+
*/
|
|
206
1787
|
formatQuantity: (symbol: string, quantity: number, when: Date, backtest: boolean) => Promise<string>;
|
|
207
1788
|
}
|
|
208
1789
|
|
|
209
|
-
|
|
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
|
+
*/
|
|
1798
|
+
declare class StrategyGlobalService {
|
|
210
1799
|
private readonly loggerService;
|
|
211
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
|
+
*/
|
|
212
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
|
+
*/
|
|
1825
|
+
backtest: (symbol: string, candles: ICandleData[], when: Date, backtest: boolean) => Promise<IStrategyBacktestResult>;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
/**
|
|
1829
|
+
* Global service for frame operations.
|
|
1830
|
+
*
|
|
1831
|
+
* Wraps FrameConnectionService for timeframe generation.
|
|
1832
|
+
* Used internally by BacktestLogicPrivateService.
|
|
1833
|
+
*/
|
|
1834
|
+
declare class FrameGlobalService {
|
|
1835
|
+
private readonly loggerService;
|
|
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
|
+
*/
|
|
1843
|
+
getTimeframe: (symbol: string) => Promise<Date[]>;
|
|
1844
|
+
}
|
|
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
|
+
*/
|
|
1852
|
+
declare class ExchangeSchemaService {
|
|
1853
|
+
readonly loggerService: LoggerService;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1892
|
+
get: (key: ExchangeName) => IExchangeSchema;
|
|
1893
|
+
}
|
|
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
|
+
*/
|
|
1901
|
+
declare class StrategySchemaService {
|
|
1902
|
+
readonly loggerService: LoggerService;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1940
|
+
get: (key: StrategyName) => IStrategySchema;
|
|
1941
|
+
}
|
|
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
|
+
*/
|
|
1949
|
+
declare class FrameSchemaService {
|
|
1950
|
+
readonly loggerService: LoggerService;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
1988
|
+
get(key: FrameName): IFrameSchema;
|
|
1989
|
+
}
|
|
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
|
+
*/
|
|
2004
|
+
declare class BacktestLogicPrivateService {
|
|
2005
|
+
private readonly loggerService;
|
|
2006
|
+
private readonly strategyGlobalService;
|
|
2007
|
+
private readonly exchangeGlobalService;
|
|
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
|
+
*/
|
|
2023
|
+
run(symbol: string): AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
2024
|
+
}
|
|
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
|
+
*/
|
|
2042
|
+
declare class LiveLogicPrivateService {
|
|
2043
|
+
private readonly loggerService;
|
|
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
|
+
*/
|
|
2067
|
+
run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2068
|
+
}
|
|
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
|
+
*/
|
|
2094
|
+
declare class BacktestLogicPublicService {
|
|
2095
|
+
private readonly loggerService;
|
|
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
|
+
*/
|
|
2107
|
+
run: (symbol: string, context: {
|
|
2108
|
+
strategyName: string;
|
|
2109
|
+
exchangeName: string;
|
|
2110
|
+
frameName: string;
|
|
2111
|
+
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
2112
|
+
}
|
|
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
|
+
*/
|
|
2145
|
+
declare class LiveLogicPublicService {
|
|
2146
|
+
private readonly loggerService;
|
|
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
|
+
*/
|
|
2159
|
+
run: (symbol: string, context: {
|
|
2160
|
+
strategyName: string;
|
|
2161
|
+
exchangeName: string;
|
|
2162
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2163
|
+
}
|
|
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
|
+
*/
|
|
2171
|
+
declare class LiveGlobalService {
|
|
2172
|
+
private readonly loggerService;
|
|
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
|
+
*/
|
|
2185
|
+
run: (symbol: string, context: {
|
|
2186
|
+
strategyName: string;
|
|
2187
|
+
exchangeName: string;
|
|
2188
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
2189
|
+
}
|
|
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
|
+
*/
|
|
2197
|
+
declare class BacktestGlobalService {
|
|
2198
|
+
private readonly loggerService;
|
|
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
|
+
*/
|
|
2210
|
+
run: (symbol: string, context: {
|
|
2211
|
+
strategyName: string;
|
|
2212
|
+
exchangeName: string;
|
|
2213
|
+
frameName: string;
|
|
2214
|
+
}) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
|
|
2215
|
+
}
|
|
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;
|
|
213
2559
|
}
|
|
214
2560
|
|
|
215
2561
|
declare const backtest: {
|
|
216
|
-
|
|
217
|
-
|
|
2562
|
+
exchangeValidationService: ExchangeValidationService;
|
|
2563
|
+
strategyValidationService: StrategyValidationService;
|
|
2564
|
+
frameValidationService: FrameValidationService;
|
|
2565
|
+
backtestMarkdownService: BacktestMarkdownService;
|
|
2566
|
+
liveMarkdownService: LiveMarkdownService;
|
|
2567
|
+
backtestLogicPublicService: BacktestLogicPublicService;
|
|
2568
|
+
liveLogicPublicService: LiveLogicPublicService;
|
|
2569
|
+
backtestLogicPrivateService: BacktestLogicPrivateService;
|
|
2570
|
+
liveLogicPrivateService: LiveLogicPrivateService;
|
|
2571
|
+
exchangeGlobalService: ExchangeGlobalService;
|
|
2572
|
+
strategyGlobalService: StrategyGlobalService;
|
|
2573
|
+
frameGlobalService: FrameGlobalService;
|
|
2574
|
+
liveGlobalService: LiveGlobalService;
|
|
2575
|
+
backtestGlobalService: BacktestGlobalService;
|
|
218
2576
|
exchangeSchemaService: ExchangeSchemaService;
|
|
219
2577
|
strategySchemaService: StrategySchemaService;
|
|
2578
|
+
frameSchemaService: FrameSchemaService;
|
|
220
2579
|
exchangeConnectionService: ExchangeConnectionService;
|
|
221
2580
|
strategyConnectionService: StrategyConnectionService;
|
|
2581
|
+
frameConnectionService: FrameConnectionService;
|
|
222
2582
|
executionContextService: {
|
|
223
2583
|
readonly context: IExecutionContext;
|
|
224
2584
|
};
|
|
2585
|
+
methodContextService: {
|
|
2586
|
+
readonly context: IMethodContext;
|
|
2587
|
+
};
|
|
225
2588
|
loggerService: LoggerService;
|
|
226
2589
|
};
|
|
227
2590
|
|
|
228
|
-
export { type CandleInterval, ExecutionContextService, type ICandleData, type IExchangeSchema, type
|
|
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 };
|