backtest-kit 3.5.0 → 3.5.2

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.
Files changed (4) hide show
  1. package/build/index.cjs +3821 -3544
  2. package/build/index.mjs +3821 -3545
  3. package/package.json +1 -1
  4. package/types.d.ts +2140 -2114
package/types.d.ts CHANGED
@@ -58,2161 +58,1623 @@ declare const ExecutionContextService: (new () => {
58
58
  type TExecutionContextService = InstanceType<typeof ExecutionContextService>;
59
59
 
60
60
  /**
61
- * Single log entry stored in the log history.
61
+ * Timeframe interval for backtest period generation.
62
+ * Determines the granularity of timestamps in the generated timeframe array.
63
+ *
64
+ * Minutes: 1m, 3m, 5m, 15m, 30m
65
+ * Hours: 1h, 2h, 4h, 6h, 8h, 12h
66
+ * Days: 1d, 3d
62
67
  */
63
- interface ILogEntry {
64
- /** Unique entry identifier generated via randomString */
65
- id: string;
66
- /** Log level */
67
- type: "log" | "debug" | "info" | "warn";
68
- /** Unix timestamp in milliseconds when the entry was created */
69
- timestamp: number;
70
- /** Date taken from backtest context to improve user experience */
71
- createdAt: string;
72
- /** Log topic / method name */
73
- topic: string;
74
- /** Additional arguments passed to the log call */
75
- args: unknown[];
68
+ type FrameInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h" | "12h" | "1d" | "3d";
69
+ /**
70
+ * Frame parameters passed to ClientFrame constructor.
71
+ * Extends IFrameSchema with logger instance for internal logging.
72
+ */
73
+ interface IFrameParams extends IFrameSchema {
74
+ /** Logger service for debug output */
75
+ logger: ILogger;
76
76
  }
77
77
  /**
78
- * Interface representing a logging mechanism for the swarm system.
79
- * Provides methods to record messages at different severity levels, used across components like agents, sessions, states, storage, swarms, history, embeddings, completions, and policies.
80
- * Logs are utilized to track lifecycle events (e.g., initialization, disposal), operational details (e.g., tool calls, message emissions), validation outcomes (e.g., policy checks), and errors (e.g., persistence failures), aiding in debugging, monitoring, and auditing.
81
- */
82
- interface ILogger {
83
- /**
84
- * Logs a general-purpose message.
85
- * Used throughout the swarm system to record significant events or state changes, such as agent execution, session connections, or storage updates.
86
- */
87
- log(topic: string, ...args: any[]): void;
88
- /**
89
- * Logs a debug-level message.
90
- * Employed for detailed diagnostic information, such as intermediate states during agent tool calls, swarm navigation changes, or embedding creation processes, typically enabled in development or troubleshooting scenarios.
91
- */
92
- debug(topic: string, ...args: any[]): void;
93
- /**
94
- * Logs an info-level message.
95
- * 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.
96
- */
97
- info(topic: string, ...args: any[]): void;
78
+ * Callbacks for frame lifecycle events.
79
+ */
80
+ interface IFrameCallbacks {
98
81
  /**
99
- * Logs a warning-level message.
100
- * Used to record potentially problematic situations that don't prevent execution but may require attention, such as missing data, unexpected conditions, or deprecated usage.
82
+ * Called after timeframe array generation.
83
+ * Useful for logging or validating the generated timeframes.
84
+ *
85
+ * @param timeframe - Array of Date objects representing tick timestamps
86
+ * @param startDate - Start of the backtest period
87
+ * @param endDate - End of the backtest period
88
+ * @param interval - Interval used for generation
101
89
  */
102
- warn(topic: string, ...args: any[]): void;
90
+ onTimeframe: (timeframe: Date[], startDate: Date, endDate: Date, interval: FrameInterval) => void | Promise<void>;
103
91
  }
104
-
105
92
  /**
106
- * Candle time interval for fetching historical data.
93
+ * Frame schema registered via addFrame().
94
+ * Defines backtest period and interval for timestamp generation.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * addFrame({
99
+ * frameName: "1d-backtest",
100
+ * interval: "1m",
101
+ * startDate: new Date("2024-01-01T00:00:00Z"),
102
+ * endDate: new Date("2024-01-02T00:00:00Z"),
103
+ * callbacks: {
104
+ * onTimeframe: (timeframe, startDate, endDate, interval) => {
105
+ * console.log(`Generated ${timeframe.length} timestamps`);
106
+ * },
107
+ * },
108
+ * });
109
+ * ```
107
110
  */
108
- type CandleInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h";
109
- /** Numeric type that can be undefined (used for optional numeric values) */
110
- type Num = number | undefined;
111
- interface IPublicCandleData {
112
- /** Unix timestamp in milliseconds when candle opened */
113
- timestamp: Num;
114
- /** Opening price at candle start */
115
- open: Num;
116
- /** Highest price during candle period */
117
- high: Num;
118
- /** Lowest price during candle period */
119
- low: Num;
120
- /** Closing price at candle end */
121
- close: Num;
122
- /** Trading volume during candle period */
123
- volume: Num;
111
+ interface IFrameSchema {
112
+ /** Unique identifier for this frame */
113
+ frameName: FrameName;
114
+ /** Optional developer note for documentation */
115
+ note?: string;
116
+ /** Interval for timestamp generation */
117
+ interval: FrameInterval;
118
+ /** Start of backtest period (inclusive) */
119
+ startDate: Date;
120
+ /** End of backtest period (inclusive) */
121
+ endDate: Date;
122
+ /** Optional lifecycle callbacks */
123
+ callbacks?: Partial<IFrameCallbacks>;
124
124
  }
125
125
  /**
126
- * Single OHLCV candle data point.
127
- * Used for VWAP calculation and backtesting.
126
+ * Frame interface for timeframe generation.
127
+ * Used internally by backtest orchestration.
128
128
  */
129
- interface ICandleData {
130
- /** Unix timestamp in milliseconds when candle opened */
131
- timestamp: number;
132
- /** Opening price at candle start */
133
- open: number;
134
- /** Highest price during candle period */
135
- high: number;
136
- /** Lowest price during candle period */
137
- low: number;
138
- /** Closing price at candle end */
139
- close: number;
140
- /** Trading volume during candle period */
141
- volume: number;
129
+ interface IFrame {
130
+ /**
131
+ * Generates array of timestamps for backtest iteration.
132
+ * Timestamps are spaced according to the configured interval.
133
+ *
134
+ * @param symbol - Trading pair symbol (unused, for API consistency)
135
+ * @returns Promise resolving to array of Date objects
136
+ */
137
+ getTimeframe: (symbol: string, frameName: FrameName) => Promise<Date[]>;
142
138
  }
143
139
  /**
144
- * Single bid or ask in order book.
140
+ * Unique identifier for a frame schema.
141
+ * Used to retrieve frame instances via dependency injection.
145
142
  */
146
- interface IBidData {
147
- /** Price level as string */
148
- price: string;
149
- /** Quantity at this price level as string */
150
- quantity: string;
151
- }
143
+ type FrameName = string;
144
+
152
145
  /**
153
- * Order book data containing bids and asks.
146
+ * Risk rejection result type.
147
+ * Can be void, null, or an IRiskRejectionResult object.
154
148
  */
155
- interface IOrderBookData {
156
- /** Trading pair symbol */
149
+ type RiskRejection = void | IRiskRejectionResult | string | null;
150
+ /**
151
+ * Risk check arguments for evaluating whether to allow opening a new position.
152
+ * Called BEFORE signal creation to validate if conditions allow new signals.
153
+ * Contains only passthrough arguments from ClientStrategy context.
154
+ */
155
+ interface IRiskCheckArgs {
156
+ /** Trading pair symbol (e.g., "BTCUSDT") */
157
157
  symbol: string;
158
- /** Array of bid orders (buy orders) */
159
- bids: IBidData[];
160
- /** Array of ask orders (sell orders) */
161
- asks: IBidData[];
158
+ /** Pending signal to apply */
159
+ currentSignal: IPublicSignalRow;
160
+ /** Strategy name requesting to open a position */
161
+ strategyName: StrategyName;
162
+ /** Exchange name */
163
+ exchangeName: ExchangeName;
164
+ /** Risk name */
165
+ riskName: RiskName;
166
+ /** Frame name */
167
+ frameName: FrameName;
168
+ /** Current VWAP price */
169
+ currentPrice: number;
170
+ /** Current timestamp */
171
+ timestamp: number;
162
172
  }
163
173
  /**
164
- * Aggregated trade data point.
165
- * Represents a single trade that has occurred, used for detailed analysis and backtesting.
166
- * Includes price, quantity, timestamp, and whether the buyer is the market maker (which can indicate trade direction).
167
- *
174
+ * Active position tracked by ClientRisk for cross-strategy analysis.
168
175
  */
169
- interface IAggregatedTradeData {
170
- /** Unique identifier for the aggregated trade */
171
- id: string;
172
- /** Price at which the trade occurred */
173
- price: number;
174
- /** Quantity traded */
175
- qty: number;
176
- /** Unix timestamp in milliseconds when the trade occurred */
177
- timestamp: number;
178
- /** Whether the buyer is the market maker (true if buyer is maker, false if seller is maker) */
179
- isBuyerMaker: boolean;
176
+ interface IRiskActivePosition {
177
+ /** Strategy name owning the position */
178
+ strategyName: StrategyName;
179
+ /** Exchange name */
180
+ exchangeName: ExchangeName;
181
+ /** Frame name */
182
+ frameName: FrameName;
183
+ /** Trading pair symbol (e.g., "BTCUSDT") */
184
+ symbol: string;
185
+ /** Position direction ("long" or "short") */
186
+ position: "long" | "short";
187
+ /** Entry price */
188
+ priceOpen: number;
189
+ /** Stop loss price */
190
+ priceStopLoss: number;
191
+ /** Take profit price */
192
+ priceTakeProfit: number;
193
+ /** Estimated time in minutes */
194
+ minuteEstimatedTime: number;
195
+ /** Timestamp when the position was opened */
196
+ openTimestamp: number;
180
197
  }
181
198
  /**
182
- * Exchange parameters passed to ClientExchange constructor.
183
- * Combines schema with runtime dependencies.
184
- * Note: All exchange methods are required in params (defaults are applied during initialization).
199
+ * Optional callbacks for risk events.
185
200
  */
186
- interface IExchangeParams extends IExchangeSchema {
187
- /** Logger service for debug output */
188
- logger: ILogger;
189
- /** Execution context service (symbol, when, backtest flag) */
190
- execution: TExecutionContextService;
191
- /** Fetch candles from data source (required, defaults applied) */
192
- getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number, backtest: boolean) => Promise<ICandleData[]>;
193
- /** Format quantity according to exchange precision rules (required, defaults applied) */
194
- formatQuantity: (symbol: string, quantity: number, backtest: boolean) => Promise<string>;
195
- /** Format price according to exchange precision rules (required, defaults applied) */
196
- formatPrice: (symbol: string, price: number, backtest: boolean) => Promise<string>;
197
- /** Fetch order book for a trading pair (required, defaults applied) */
198
- getOrderBook: (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => Promise<IOrderBookData>;
199
- /** Fetch aggregated trades for a trading pair (required, defaults applied) */
200
- getAggregatedTrades: (symbol: string, from: Date, to: Date, backtest: boolean) => Promise<IAggregatedTradeData[]>;
201
+ interface IRiskCallbacks {
202
+ /** Called when a signal is rejected due to risk limits */
203
+ onRejected: (symbol: string, params: IRiskCheckArgs) => void | Promise<void>;
204
+ /** Called when a signal passes risk checks */
205
+ onAllowed: (symbol: string, params: IRiskCheckArgs) => void | Promise<void>;
201
206
  }
202
207
  /**
203
- * Optional callbacks for exchange data events.
208
+ * Payload passed to risk validation functions.
209
+ * Extends IRiskCheckArgs with portfolio state data.
204
210
  */
205
- interface IExchangeCallbacks {
206
- /** Called when candle data is fetched */
207
- onCandleData: (symbol: string, interval: CandleInterval, since: Date, limit: number, data: ICandleData[]) => void | Promise<void>;
211
+ interface IRiskValidationPayload extends IRiskCheckArgs {
212
+ /** Current signal being validated (IRiskSignalRow is calculated internally so priceOpen always exist) */
213
+ currentSignal: IRiskSignalRow;
214
+ /** Number of currently active positions across all strategies */
215
+ activePositionCount: number;
216
+ /** List of currently active positions across all strategies */
217
+ activePositions: IRiskActivePosition[];
208
218
  }
209
219
  /**
210
- * Exchange schema registered via addExchange().
211
- * Defines candle data source and formatting logic.
220
+ * Risk validation rejection result.
221
+ * Returned when validation fails, contains debugging information.
212
222
  */
213
- interface IExchangeSchema {
214
- /** Unique exchange identifier for registration */
215
- exchangeName: ExchangeName;
223
+ interface IRiskRejectionResult {
224
+ /** Unique identifier for this rejection instance */
225
+ id: string | null;
226
+ /** Human-readable reason for rejection */
227
+ note: string;
228
+ }
229
+ /**
230
+ * Risk validation function type.
231
+ * Returns null/void if validation passes, IRiskRejectionResult if validation fails.
232
+ * Can also throw error which will be caught and converted to IRiskRejectionResult.
233
+ */
234
+ interface IRiskValidationFn {
235
+ (payload: IRiskValidationPayload): RiskRejection | Promise<RiskRejection>;
236
+ }
237
+ /**
238
+ * Risk validation configuration.
239
+ * Defines validation logic with optional documentation.
240
+ */
241
+ interface IRiskValidation {
242
+ /**
243
+ * The validation function to apply to the risk check parameters.
244
+ */
245
+ validate: IRiskValidationFn;
246
+ /**
247
+ * Optional description for documentation purposes.
248
+ * Aids in understanding the purpose or behavior of the validation.
249
+ */
250
+ note?: string;
251
+ }
252
+ /**
253
+ * Risk schema registered via addRisk().
254
+ * Defines portfolio-level risk controls via custom validations.
255
+ */
256
+ interface IRiskSchema {
257
+ /** Unique risk profile identifier */
258
+ riskName: RiskName;
216
259
  /** Optional developer note for documentation */
217
260
  note?: string;
261
+ /** Optional lifecycle event callbacks (onRejected, onAllowed) */
262
+ callbacks?: Partial<IRiskCallbacks>;
263
+ /** Custom validations array for risk logic */
264
+ validations: (IRiskValidation | IRiskValidationFn)[];
265
+ }
266
+ /**
267
+ * Risk parameters passed to ClientRisk constructor.
268
+ * Combines schema with runtime dependencies and emission callbacks.
269
+ */
270
+ interface IRiskParams extends IRiskSchema {
271
+ /** Exchange name (e.g., "binance") */
272
+ exchangeName: ExchangeName;
273
+ /** Logger service for debug output */
274
+ logger: ILogger;
275
+ /** True if backtest mode, false if live mode */
276
+ backtest: boolean;
218
277
  /**
219
- * Fetch candles from data source (API or database).
278
+ * Callback invoked when a signal is rejected due to risk limits.
279
+ * Called before emitting to riskSubject.
280
+ * Used for event emission to riskSubject (separate from schema callbacks).
220
281
  *
221
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
222
- * @param interval - Candle time interval (e.g., "1m", "1h")
223
- * @param since - Start date for candle fetching
224
- * @param limit - Maximum number of candles to fetch
225
- * @param backtest - Whether running in backtest mode
226
- * @returns Promise resolving to array of OHLCV candle data
282
+ * @param symbol - Trading pair symbol
283
+ * @param params - Risk check arguments
284
+ * @param activePositionCount - Number of active positions at rejection time
285
+ * @param rejectionResult - Rejection result with id and note
286
+ * @param timestamp - Event timestamp in milliseconds
287
+ * @param backtest - True if backtest mode, false if live mode
227
288
  */
228
- getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number, backtest: boolean) => Promise<IPublicCandleData[]>;
289
+ onRejected: (symbol: string, params: IRiskCheckArgs, activePositionCount: number, rejectionResult: IRiskRejectionResult, timestamp: number, backtest: boolean) => void | Promise<void>;
290
+ }
291
+ /**
292
+ * Risk interface implemented by ClientRisk.
293
+ * Provides risk checking for signals and position tracking.
294
+ */
295
+ interface IRisk {
229
296
  /**
230
- * Format quantity according to exchange precision rules.
297
+ * Check if a signal should be allowed based on risk limits.
231
298
  *
232
- * Optional. If not provided, defaults to Bitcoin precision on Binance (8 decimal places).
299
+ * @param params - Risk check arguments (position size, portfolio state, etc.)
300
+ * @returns Promise resolving to risk check result
301
+ */
302
+ checkSignal: (params: IRiskCheckArgs) => Promise<boolean>;
303
+ /**
304
+ * Register a new opened signal/position.
233
305
  *
234
306
  * @param symbol - Trading pair symbol
235
- * @param quantity - Raw quantity value
236
- * @param backtest - Whether running in backtest mode
237
- * @returns Promise resolving to formatted quantity string
307
+ * @param context - Context information (strategyName, riskName, exchangeName, frameName)
308
+ * @param positionData - Position data (position, prices, timing)
238
309
  */
239
- formatQuantity?: (symbol: string, quantity: number, backtest: boolean) => Promise<string>;
310
+ addSignal: (symbol: string, context: {
311
+ strategyName: StrategyName;
312
+ riskName: RiskName;
313
+ exchangeName: ExchangeName;
314
+ frameName: FrameName;
315
+ }, positionData: {
316
+ position: "long" | "short";
317
+ priceOpen: number;
318
+ priceStopLoss: number;
319
+ priceTakeProfit: number;
320
+ minuteEstimatedTime: number;
321
+ openTimestamp: number;
322
+ }) => Promise<void>;
240
323
  /**
241
- * Format price according to exchange precision rules.
242
- *
243
- * Optional. If not provided, defaults to Bitcoin precision on Binance (2 decimal places).
324
+ * Remove a closed signal/position.
244
325
  *
245
326
  * @param symbol - Trading pair symbol
246
- * @param price - Raw price value
247
- * @param backtest - Whether running in backtest mode
248
- * @returns Promise resolving to formatted price string
327
+ * @param context - Context information (strategyName, riskName, exchangeName, frameName)
249
328
  */
250
- formatPrice?: (symbol: string, price: number, backtest: boolean) => Promise<string>;
329
+ removeSignal: (symbol: string, context: {
330
+ strategyName: StrategyName;
331
+ riskName: RiskName;
332
+ exchangeName: ExchangeName;
333
+ frameName: FrameName;
334
+ }) => Promise<void>;
335
+ }
336
+ /**
337
+ * Unique risk profile identifier.
338
+ */
339
+ type RiskName = string;
340
+
341
+ /**
342
+ * Profit or loss level milestone in percentage points.
343
+ * Represents 10%, 20%, 30%, ..., 100% profit or loss thresholds.
344
+ *
345
+ * Used to track when a signal reaches specific profit/loss milestones.
346
+ * Each level is emitted only once per signal (deduplication via Set).
347
+ *
348
+ * @example
349
+ * ```typescript
350
+ * const level: PartialLevel = 50; // 50% profit or loss milestone
351
+ * ```
352
+ */
353
+ type PartialLevel = 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100;
354
+ /**
355
+ * Serializable partial data for persistence layer.
356
+ * Converts Sets to arrays for JSON serialization.
357
+ *
358
+ * Stored in PersistPartialAdapter as Record<signalId, IPartialData>.
359
+ * Loaded on initialization and converted back to IPartialState.
360
+ */
361
+ interface IPartialData {
251
362
  /**
252
- * Fetch order book for a trading pair.
363
+ * Array of profit levels that have been reached for this signal.
364
+ * Serialized form of IPartialState.profitLevels Set.
365
+ */
366
+ profitLevels: PartialLevel[];
367
+ /**
368
+ * Array of loss levels that have been reached for this signal.
369
+ * Serialized form of IPartialState.lossLevels Set.
370
+ */
371
+ lossLevels: PartialLevel[];
372
+ }
373
+ /**
374
+ * Partial profit/loss tracking interface.
375
+ * Implemented by ClientPartial and PartialConnectionService.
376
+ *
377
+ * Tracks profit/loss level milestones for active trading signals.
378
+ * Emits events when signals reach 10%, 20%, 30%, etc profit or loss.
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * import { ClientPartial } from "./client/ClientPartial";
383
+ *
384
+ * const partial = new ClientPartial({
385
+ * logger: loggerService,
386
+ * onProfit: (symbol, data, price, level, backtest, timestamp) => {
387
+ * console.log(`Signal ${data.id} reached ${level}% profit`);
388
+ * },
389
+ * onLoss: (symbol, data, price, level, backtest, timestamp) => {
390
+ * console.log(`Signal ${data.id} reached ${level}% loss`);
391
+ * }
392
+ * });
393
+ *
394
+ * await partial.waitForInit("BTCUSDT");
395
+ *
396
+ * // During signal monitoring
397
+ * await partial.profit("BTCUSDT", signal, 51000, 15.5, false, new Date());
398
+ * // Emits event when reaching 10% profit milestone
399
+ *
400
+ * // When signal closes
401
+ * await partial.clear("BTCUSDT", signal, 52000);
402
+ * ```
403
+ */
404
+ interface IPartial {
405
+ /**
406
+ * Processes profit state and emits events for new profit levels reached.
253
407
  *
254
- * Optional. If not provided, throws an error when called.
408
+ * Called by ClientStrategy during signal monitoring when revenuePercent > 0.
409
+ * Checks which profit levels (10%, 20%, 30%, etc) have been reached
410
+ * and emits events for new levels only (Set-based deduplication).
255
411
  *
256
412
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
257
- * @param depth - Maximum depth levels for both bids and asks (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
258
- * @param from - Start of time range (used in backtest for historical data, can be ignored in live)
259
- * @param to - End of time range (used in backtest for historical data, can be ignored in live)
260
- * @param backtest - Whether running in backtest mode
261
- * @returns Promise resolving to order book data
413
+ * @param data - Signal row data
414
+ * @param currentPrice - Current market price
415
+ * @param revenuePercent - Current profit percentage (positive value)
416
+ * @param backtest - True if backtest mode, false if live mode
417
+ * @param when - Event timestamp (current time for live, candle time for backtest)
418
+ * @returns Promise that resolves when profit processing is complete
262
419
  *
263
420
  * @example
264
421
  * ```typescript
265
- * // Backtest implementation: returns historical order book for the time range
266
- * const backtestOrderBook = async (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => {
267
- * if (backtest) {
268
- * return await database.getOrderBookSnapshot(symbol, depth, from, to);
269
- * }
270
- * return await exchange.fetchOrderBook(symbol, depth);
271
- * };
422
+ * // Signal opened at $50000, current price $51500
423
+ * // Revenue: 3% profit
424
+ * await partial.profit("BTCUSDT", signal, 51500, 3.0, false, new Date());
425
+ * // No events emitted (below 10% threshold)
272
426
  *
273
- * // Live implementation: ignores from/to when not in backtest mode
274
- * const liveOrderBook = async (symbol: string, depth: number, _from: Date, _to: Date, backtest: boolean) => {
275
- * return await exchange.fetchOrderBook(symbol, depth);
276
- * };
427
+ * // Price rises to $55000
428
+ * // Revenue: 10% profit
429
+ * await partial.profit("BTCUSDT", signal, 55000, 10.0, false, new Date());
430
+ * // Emits partialProfitSubject event for 10% level
431
+ *
432
+ * // Price rises to $61000
433
+ * // Revenue: 22% profit
434
+ * await partial.profit("BTCUSDT", signal, 61000, 22.0, false, new Date());
435
+ * // Emits events for 20% level only (10% already emitted)
277
436
  * ```
278
437
  */
279
- getOrderBook?: (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => Promise<IOrderBookData>;
438
+ profit(symbol: string, data: IPublicSignalRow, currentPrice: number, revenuePercent: number, backtest: boolean, when: Date): Promise<void>;
280
439
  /**
281
- * Fetch aggregated trades for a trading pair.
282
- * Optional. If not provided, throws an error when called.
440
+ * Processes loss state and emits events for new loss levels reached.
441
+ *
442
+ * Called by ClientStrategy during signal monitoring when revenuePercent < 0.
443
+ * Checks which loss levels (10%, 20%, 30%, etc) have been reached
444
+ * and emits events for new levels only (Set-based deduplication).
445
+ *
283
446
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
284
- * @param from - Start of time range (used in backtest for historical data, can be ignored in live)
285
- * @param to - End of time range (used in backtest for historical data, can be ignored in live)
286
- * @param backtest - Whether running in backtest mode
287
- * @return Promise resolving to array of aggregated trade data
447
+ * @param data - Signal row data
448
+ * @param currentPrice - Current market price
449
+ * @param lossPercent - Current loss percentage (negative value)
450
+ * @param backtest - True if backtest mode, false if live mode
451
+ * @param when - Event timestamp (current time for live, candle time for backtest)
452
+ * @returns Promise that resolves when loss processing is complete
453
+ *
288
454
  * @example
289
455
  * ```typescript
290
- * // Backtest implementation: returns historical aggregated trades for the time range
291
- * const backtestAggregatedTrades = async (symbol: string, from: Date, to: Date, backtest: boolean) => {
292
- * if (backtest) {
293
- * return await database.getAggregatedTrades(symbol, from, to);
294
- * }
295
- * return await exchange.fetchAggregatedTrades(symbol);
296
- * };
456
+ * // Signal opened at $50000, current price $48000
457
+ * // Loss: -4% loss
458
+ * await partial.loss("BTCUSDT", signal, 48000, -4.0, false, new Date());
459
+ * // No events emitted (below -10% threshold)
297
460
  *
298
- * // Live implementation: ignores from/to when not in backtest mode
299
- * const liveAggregatedTrades = async (symbol: string, _from: Date, _to: Date, backtest: boolean) => {
300
- * return await exchange.fetchAggregatedTrades(symbol);
301
- * };
461
+ * // Price drops to $45000
462
+ * // Loss: -10% loss
463
+ * await partial.loss("BTCUSDT", signal, 45000, -10.0, false, new Date());
464
+ * // Emits partialLossSubject event for 10% level
465
+ *
466
+ * // Price drops to $39000
467
+ * // Loss: -22% loss
468
+ * await partial.loss("BTCUSDT", signal, 39000, -22.0, false, new Date());
469
+ * // Emits events for 20% level only (10% already emitted)
302
470
  * ```
303
471
  */
304
- getAggregatedTrades?: (symbol: string, from: Date, to: Date, backtest: boolean) => Promise<IAggregatedTradeData[]>;
305
- /** Optional lifecycle event callbacks (onCandleData) */
306
- callbacks?: Partial<IExchangeCallbacks>;
307
- }
308
- /**
309
- * Exchange interface implemented by ClientExchange.
310
- * Provides candle data access and VWAP calculation.
311
- */
312
- interface IExchange {
472
+ loss(symbol: string, data: IPublicSignalRow, currentPrice: number, lossPercent: number, backtest: boolean, when: Date): Promise<void>;
313
473
  /**
314
- * Fetch historical candles backwards from execution context time.
474
+ * Clears partial profit/loss state when signal closes.
315
475
  *
316
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
317
- * @param interval - Candle time interval (e.g., "1m", "1h")
318
- * @param limit - Maximum number of candles to fetch
319
- * @returns Promise resolving to array of candle data
320
- */
321
- getCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
322
- /**
323
- * Fetch future candles forward from execution context time (for backtest).
476
+ * Called by ClientStrategy when signal completes (TP/SL/time_expired).
477
+ * Removes signal state from memory and persists changes to disk.
478
+ * Cleans up memoized ClientPartial instance in PartialConnectionService.
324
479
  *
325
480
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
326
- * @param interval - Candle time interval (e.g., "1m", "1h")
327
- * @param limit - Maximum number of candles to fetch
328
- * @returns Promise resolving to array of candle data
329
- */
330
- getNextCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
331
- /**
332
- * Format quantity for exchange precision.
481
+ * @param data - Signal row data
482
+ * @param priceClose - Final closing price
483
+ * @returns Promise that resolves when clear is complete
333
484
  *
334
- * @param symbol - Trading pair symbol
335
- * @param quantity - Raw quantity value
336
- * @returns Promise resolving to formatted quantity string
485
+ * @example
486
+ * ```typescript
487
+ * // Signal closes at take profit
488
+ * await partial.clear("BTCUSDT", signal, 52000);
489
+ * // State removed from _states Map
490
+ * // Persisted to disk without this signal's data
491
+ * // Memoized instance cleared from getPartial cache
492
+ * ```
337
493
  */
338
- formatQuantity: (symbol: string, quantity: number) => Promise<string>;
494
+ clear(symbol: string, data: IPublicSignalRow, priceClose: number, backtest: boolean): Promise<void>;
495
+ }
496
+
497
+ /**
498
+ * Serializable breakeven data for persistence layer.
499
+ * Converts state to simple boolean for JSON serialization.
500
+ *
501
+ * Stored in PersistBreakevenAdapter as Record<signalId, IBreakevenData>.
502
+ * Loaded on initialization and converted back to IBreakevenState.
503
+ */
504
+ interface IBreakevenData {
339
505
  /**
340
- * Format price for exchange precision.
341
- *
342
- * @param symbol - Trading pair symbol
343
- * @param price - Raw price value
344
- * @returns Promise resolving to formatted price string
506
+ * Whether breakeven has been reached for this signal.
507
+ * Serialized form of IBreakevenState.reached.
345
508
  */
346
- formatPrice: (symbol: string, price: number) => Promise<string>;
509
+ reached: boolean;
510
+ }
511
+ /**
512
+ * Breakeven tracking interface.
513
+ * Implemented by ClientBreakeven and BreakevenConnectionService.
514
+ *
515
+ * Tracks when a signal's stop-loss is moved to breakeven (entry price).
516
+ * Emits events when threshold is reached (price moves far enough to cover transaction costs).
517
+ *
518
+ * @example
519
+ * ```typescript
520
+ * import { ClientBreakeven } from "./client/ClientBreakeven";
521
+ *
522
+ * const breakeven = new ClientBreakeven({
523
+ * logger: loggerService,
524
+ * onBreakeven: (symbol, data, price, backtest, timestamp) => {
525
+ * console.log(`Signal ${data.id} reached breakeven at ${price}`);
526
+ * }
527
+ * });
528
+ *
529
+ * await breakeven.waitForInit("BTCUSDT");
530
+ *
531
+ * // During signal monitoring
532
+ * await breakeven.check("BTCUSDT", signal, 100.5, false, new Date());
533
+ * // Emits event when threshold reached and SL moved to entry
534
+ *
535
+ * // When signal closes
536
+ * await breakeven.clear("BTCUSDT", signal, 101, false);
537
+ * ```
538
+ */
539
+ interface IBreakeven {
347
540
  /**
348
- * Calculate VWAP from last 5 1-minute candles.
541
+ * Checks if breakeven should be triggered and emits event if conditions met.
349
542
  *
350
- * Formula: VWAP = Σ(Typical Price × Volume) / Σ(Volume)
351
- * where Typical Price = (High + Low + Close) / 3
543
+ * Called by ClientStrategy during signal monitoring.
544
+ * Checks if:
545
+ * 1. Breakeven not already reached
546
+ * 2. Price has moved far enough to cover transaction costs
547
+ * 3. Stop-loss can be moved to entry price
352
548
  *
353
- * @param symbol - Trading pair symbol
354
- * @returns Promise resolving to volume-weighted average price
355
- */
356
- getAveragePrice: (symbol: string) => Promise<number>;
357
- /**
358
- * Fetch order book for a trading pair.
549
+ * If all conditions met:
550
+ * - Marks breakeven as reached
551
+ * - Calls onBreakeven callback (emits to breakevenSubject)
552
+ * - Persists state to disk
359
553
  *
360
554
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
361
- * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
362
- * @returns Promise resolving to order book data
363
- */
364
- getOrderBook: (symbol: string, depth?: number) => Promise<IOrderBookData>;
365
- /**
366
- * Fetch aggregated trades for a trading pair.
555
+ * @param data - Signal row data
556
+ * @param currentPrice - Current market price
557
+ * @param backtest - True if backtest mode, false if live mode
558
+ * @param when - Event timestamp (current time for live, candle time for backtest)
559
+ * @returns Promise that resolves when breakeven check is complete
367
560
  *
368
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
369
- * @param limit - Optional maximum number of aggregated trades to fetch. If empty returns one hour of data.
370
- * @returns Promise resolving to array of aggregated trade data
561
+ * @example
562
+ * ```typescript
563
+ * // LONG: entry=100, slippage=0.1%, fee=0.1%, threshold=0.4%
564
+ * // Price at 100.3 - threshold not reached
565
+ * await breakeven.check("BTCUSDT", signal, 100.3, false, new Date());
566
+ * // No event emitted (price < 100.4)
567
+ *
568
+ * // Price at 100.5 - threshold reached!
569
+ * await breakeven.check("BTCUSDT", signal, 100.5, false, new Date());
570
+ * // Emits breakevenSubject event
571
+ *
572
+ * // Price at 101 - already at breakeven
573
+ * await breakeven.check("BTCUSDT", signal, 101, false, new Date());
574
+ * // No event emitted (already reached)
575
+ * ```
371
576
  */
372
- getAggregatedTrades: (symbol: string, limit?: number) => Promise<IAggregatedTradeData[]>;
577
+ check(symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean, when: Date): Promise<boolean>;
373
578
  /**
374
- * Fetch raw candles with flexible date/limit parameters.
375
- *
376
- * All modes respect execution context and prevent look-ahead bias.
579
+ * Clears breakeven state when signal closes.
377
580
  *
378
- * Parameter combinations:
379
- * 1. sDate + eDate + limit: fetches with explicit parameters, validates eDate <= when
380
- * 2. sDate + eDate: calculates limit from date range, validates eDate <= when
381
- * 3. eDate + limit: calculates sDate backward, validates eDate <= when
382
- * 4. sDate + limit: fetches forward, validates calculated endTimestamp <= when
383
- * 5. Only limit: uses execution.context.when as reference (backward)
581
+ * Called by ClientStrategy when signal completes (TP/SL/time_expired).
582
+ * Removes signal state from memory and persists changes to disk.
583
+ * Cleans up memoized ClientBreakeven instance in BreakevenConnectionService.
384
584
  *
385
585
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
386
- * @param interval - Candle interval (e.g., "1m", "1h")
387
- * @param limit - Optional number of candles to fetch
388
- * @param sDate - Optional start date in milliseconds
389
- * @param eDate - Optional end date in milliseconds
390
- * @returns Promise resolving to array of candles
586
+ * @param data - Signal row data
587
+ * @param priceClose - Final closing price
588
+ * @param backtest - True if backtest mode, false if live mode
589
+ * @returns Promise that resolves when clear is complete
590
+ *
591
+ * @example
592
+ * ```typescript
593
+ * // Signal closes at take profit
594
+ * await breakeven.clear("BTCUSDT", signal, 101);
595
+ * // State removed from _states Map
596
+ * // Persisted to disk without this signal's data
597
+ * // Memoized instance cleared from getBreakeven cache
598
+ * ```
391
599
  */
392
- getRawCandles: (symbol: string, interval: CandleInterval, limit?: number, sDate?: number, eDate?: number) => Promise<ICandleData[]>;
600
+ clear(symbol: string, data: IPublicSignalRow, priceClose: number, backtest: boolean): Promise<void>;
393
601
  }
394
- /**
395
- * Unique exchange identifier.
396
- */
397
- type ExchangeName = string;
398
602
 
399
603
  /**
400
- * Parameters for pre-caching candles into persist storage.
401
- * Used to download historical candle data before running a backtest.
604
+ * Contract for breakeven events.
605
+ *
606
+ * Emitted by breakevenSubject when a signal's stop-loss is moved to breakeven (entry price).
607
+ * Used for tracking risk reduction milestones and monitoring strategy safety.
608
+ *
609
+ * Events are emitted only once per signal (idempotent - protected by ClientBreakeven state).
610
+ * Breakeven is triggered when price moves far enough in profit direction to cover transaction costs.
611
+ *
612
+ * Consumers:
613
+ * - BreakevenMarkdownService: Accumulates events for report generation
614
+ * - User callbacks via listenBreakeven() / listenBreakevenOnce()
615
+ *
616
+ * @example
617
+ * ```typescript
618
+ * import { listenBreakeven } from "backtest-kit";
619
+ *
620
+ * // Listen to all breakeven events
621
+ * listenBreakeven((event) => {
622
+ * console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} moved to breakeven`);
623
+ * console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
624
+ * console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
625
+ * console.log(`Original SL: ${event.data.priceStopLoss}, New SL: ${event.data.priceOpen}`);
626
+ * });
627
+ *
628
+ * // Wait for specific signal to reach breakeven
629
+ * listenBreakevenOnce(
630
+ * (event) => event.data.id === "target-signal-id",
631
+ * (event) => console.log("Signal reached breakeven:", event.data.id)
632
+ * );
633
+ * ```
402
634
  */
403
- interface ICacheCandlesParams {
404
- /** Trading pair symbol (e.g., "BTCUSDT") */
635
+ interface BreakevenContract {
636
+ /**
637
+ * Trading pair symbol (e.g., "BTCUSDT").
638
+ * Identifies which market this breakeven event belongs to.
639
+ */
405
640
  symbol: string;
406
- /** Name of the registered exchange schema */
641
+ /**
642
+ * Strategy name that generated this signal.
643
+ * Identifies which strategy execution this breakeven event belongs to.
644
+ */
645
+ strategyName: StrategyName;
646
+ /**
647
+ * Exchange name where this signal is being executed.
648
+ * Identifies which exchange this breakeven event belongs to.
649
+ */
407
650
  exchangeName: ExchangeName;
408
- /** Candle time interval (e.g., "1m", "4h") */
409
- interval: CandleInterval;
410
- /** Start date of the caching range (inclusive) */
411
- from: Date;
412
- /** End date of the caching range (inclusive) */
413
- to: Date;
414
- }
415
- /**
416
- * Parameters for validating cached candle timestamps.
417
- * Reads JSON files directly from persist storage directory.
418
- */
419
- interface ICheckCandlesParams {
420
- /** Trading pair symbol (e.g., "BTCUSDT") */
421
- symbol: string;
422
- /** Name of the registered exchange schema */
423
- exchangeName: ExchangeName;
424
- /** Candle time interval (e.g., "1m", "4h") */
425
- interval: CandleInterval;
426
- /** Start date of the validation range (inclusive) */
427
- from: Date;
428
- /** End date of the validation range (inclusive) */
429
- to: Date;
430
- /** Base directory of candle persist storage (default: "./dump/data/candle") */
431
- baseDir?: string;
651
+ /**
652
+ * Frame name where this signal is being executed.
653
+ * Identifies which frame this breakeven event belongs to (empty string for live mode).
654
+ */
655
+ frameName: FrameName;
656
+ /**
657
+ * Complete signal row data with original prices.
658
+ * Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and partialExecuted.
659
+ */
660
+ data: IPublicSignalRow;
661
+ /**
662
+ * Current market price at which breakeven was triggered.
663
+ * Used to verify threshold calculation.
664
+ */
665
+ currentPrice: number;
666
+ /**
667
+ * Execution mode flag.
668
+ * - true: Event from backtest execution (historical candle data)
669
+ * - false: Event from live trading (real-time tick)
670
+ */
671
+ backtest: boolean;
672
+ /**
673
+ * Event timestamp in milliseconds since Unix epoch.
674
+ *
675
+ * Timing semantics:
676
+ * - Live mode: when.getTime() at the moment breakeven was set
677
+ * - Backtest mode: candle.timestamp of the candle that triggered breakeven
678
+ *
679
+ * @example
680
+ * ```typescript
681
+ * const eventDate = new Date(event.timestamp);
682
+ * console.log(`Breakeven set at: ${eventDate.toISOString()}`);
683
+ * ```
684
+ */
685
+ timestamp: number;
432
686
  }
687
+
433
688
  /**
434
- * Checks cached candle timestamps for correct interval alignment.
435
- * Reads JSON files directly from persist storage without using abstractions.
689
+ * Contract for partial profit level events.
436
690
  *
437
- * @param params - Validation parameters
438
- */
439
- declare function checkCandles(params: ICheckCandlesParams): Promise<void>;
440
- /**
441
- * Pre-caches candles for a date range into persist storage.
442
- * Downloads all candles matching the interval from `from` to `to`.
691
+ * Emitted by partialProfitSubject when a signal reaches a profit level milestone (10%, 20%, 30%, etc).
692
+ * Used for tracking partial take-profit execution and monitoring strategy performance.
443
693
  *
444
- * @param params - Cache parameters
445
- */
446
- declare function warmCandles(params: ICacheCandlesParams): Promise<void>;
447
-
448
- /**
449
- * Type alias for enum objects with string key-value pairs
450
- */
451
- type Enum = Record<string, string>;
452
- /**
453
- * Type alias for ValidateArgs with any enum type
454
- */
455
- type Args = ValidateArgs<any>;
456
- /**
457
- * Interface defining validation arguments for all entity types.
694
+ * Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
695
+ * Multiple levels can be emitted in a single tick if price jumps significantly.
458
696
  *
459
- * Each property accepts an enum object where values will be validated
460
- * against registered entities in their respective validation services.
697
+ * Consumers:
698
+ * - PartialMarkdownService: Accumulates events for report generation
699
+ * - User callbacks via listenPartialProfit() / listenPartialProfitOnce()
461
700
  *
462
- * @template T - Enum type extending Record<string, string>
701
+ * @example
702
+ * ```typescript
703
+ * import { listenPartialProfit } from "backtest-kit";
704
+ *
705
+ * // Listen to all partial profit events
706
+ * listenPartialProfit((event) => {
707
+ * console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached ${event.level}% profit`);
708
+ * console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
709
+ * console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
710
+ * });
711
+ *
712
+ * // Wait for first 50% profit level
713
+ * listenPartialProfitOnce(
714
+ * (event) => event.level === 50,
715
+ * (event) => console.log("50% profit reached:", event.data.id)
716
+ * );
717
+ * ```
463
718
  */
464
- interface ValidateArgs<T = Enum> {
719
+ interface PartialProfitContract {
465
720
  /**
466
- * Exchange name enum to validate
467
- * @example { BINANCE: "binance", BYBIT: "bybit" }
721
+ * Trading pair symbol (e.g., "BTCUSDT").
722
+ * Identifies which market this profit event belongs to.
468
723
  */
469
- ExchangeName?: T;
724
+ symbol: string;
470
725
  /**
471
- * Frame (timeframe) name enum to validate
472
- * @example { Q1_2024: "2024-Q1", Q2_2024: "2024-Q2" }
726
+ * Strategy name that generated this signal.
727
+ * Identifies which strategy execution this profit event belongs to.
473
728
  */
474
- FrameName?: T;
729
+ strategyName: StrategyName;
475
730
  /**
476
- * Strategy name enum to validate
477
- * @example { MOMENTUM_BTC: "momentum-btc" }
731
+ * Exchange name where this signal is being executed.
732
+ * Identifies which exchange this profit event belongs to.
478
733
  */
479
- StrategyName?: T;
734
+ exchangeName: ExchangeName;
480
735
  /**
481
- * Risk profile name enum to validate
482
- * @example { CONSERVATIVE: "conservative", AGGRESSIVE: "aggressive" }
736
+ * Frame name where this signal is being executed.
737
+ * Identifies which frame this profit event belongs to (empty string for live mode).
483
738
  */
484
- RiskName?: T;
739
+ frameName: FrameName;
485
740
  /**
486
- * Action handler name enum to validate
487
- * @example { TELEGRAM_NOTIFIER: "telegram-notifier" }
741
+ * Complete signal row data with original prices.
742
+ * Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and partialExecuted.
488
743
  */
489
- ActionName?: T;
744
+ data: IPublicSignalRow;
490
745
  /**
491
- * Sizing strategy name enum to validate
492
- * @example { FIXED_1000: "fixed-1000" }
746
+ * Current market price at which this profit level was reached.
747
+ * Used to calculate actual profit percentage.
493
748
  */
494
- SizingName?: T;
749
+ currentPrice: number;
495
750
  /**
496
- * Walker (parameter sweep) name enum to validate
497
- * @example { RSI_SWEEP: "rsi-sweep" }
751
+ * Profit level milestone reached (10, 20, 30, 40, 50, 60, 70, 80, 90, or 100).
752
+ * Represents percentage profit relative to entry price.
753
+ *
754
+ * @example
755
+ * ```typescript
756
+ * // If entry was $50000 and level is 20:
757
+ * // currentPrice >= $60000 (20% profit)
758
+ * ```
498
759
  */
499
- WalkerName?: T;
760
+ level: PartialLevel;
761
+ /**
762
+ * Execution mode flag.
763
+ * - true: Event from backtest execution (historical candle data)
764
+ * - false: Event from live trading (real-time tick)
765
+ */
766
+ backtest: boolean;
767
+ /**
768
+ * Event timestamp in milliseconds since Unix epoch.
769
+ *
770
+ * Timing semantics:
771
+ * - Live mode: when.getTime() at the moment profit level was detected
772
+ * - Backtest mode: candle.timestamp of the candle that triggered the level
773
+ *
774
+ * @example
775
+ * ```typescript
776
+ * const eventDate = new Date(event.timestamp);
777
+ * console.log(`Profit reached at: ${eventDate.toISOString()}`);
778
+ * ```
779
+ */
780
+ timestamp: number;
500
781
  }
782
+
501
783
  /**
502
- * Validates the existence of all provided entity names across validation services.
503
- *
504
- * This function accepts enum objects for various entity types (exchanges, frames,
505
- * strategies, risks, sizings, walkers) and validates that each entity
506
- * name exists in its respective registry. Validation results are memoized for performance.
507
- *
508
- * If no arguments are provided (or specific entity types are omitted), the function
509
- * automatically fetches and validates ALL registered entities from their respective
510
- * validation services. This is useful for comprehensive validation of the entire setup.
784
+ * Contract for partial loss level events.
511
785
  *
512
- * Use this before running backtests or optimizations to ensure all referenced
513
- * entities are properly registered and configured.
786
+ * Emitted by partialLossSubject when a signal reaches a loss level milestone (-10%, -20%, -30%, etc).
787
+ * Used for tracking partial stop-loss execution and monitoring strategy drawdown.
514
788
  *
515
- * @public
516
- * @param args - Partial validation arguments containing entity name enums to validate.
517
- * If empty or omitted, validates all registered entities.
518
- * @throws {Error} If any entity name is not found in its validation service
789
+ * Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
790
+ * Multiple levels can be emitted in a single tick if price drops significantly.
519
791
  *
520
- * @example
521
- * ```typescript
522
- * // Validate ALL registered entities (exchanges, frames, strategies, etc.)
523
- * await validate({});
524
- * ```
792
+ * Consumers:
793
+ * - PartialMarkdownService: Accumulates events for report generation
794
+ * - User callbacks via listenPartialLoss() / listenPartialLossOnce()
525
795
  *
526
796
  * @example
527
797
  * ```typescript
528
- * // Define your entity name enums
529
- * enum ExchangeName {
530
- * BINANCE = "binance",
531
- * BYBIT = "bybit"
532
- * }
798
+ * import { listenPartialLoss } from "backtest-kit";
533
799
  *
534
- * enum StrategyName {
535
- * MOMENTUM_BTC = "momentum-btc"
536
- * }
800
+ * // Listen to all partial loss events
801
+ * listenPartialLoss((event) => {
802
+ * console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached -${event.level}% loss`);
803
+ * console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
804
+ * console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
537
805
  *
538
- * // Validate specific entities before running backtest
539
- * await validate({
540
- * ExchangeName,
541
- * StrategyName,
806
+ * // Alert on significant loss
807
+ * if (event.level >= 30 && !event.backtest) {
808
+ * console.warn("HIGH LOSS ALERT:", event.data.id);
809
+ * }
542
810
  * });
543
- * ```
544
811
  *
545
- * @example
546
- * ```typescript
547
- * // Validate specific entity types
548
- * await validate({
549
- * RiskName: { CONSERVATIVE: "conservative" },
550
- * SizingName: { FIXED_1000: "fixed-1000" },
551
- * });
812
+ * // Wait for first 20% loss level
813
+ * listenPartialLossOnce(
814
+ * (event) => event.level === 20,
815
+ * (event) => console.log("20% loss reached:", event.data.id)
816
+ * );
552
817
  * ```
553
818
  */
554
- declare function validate(args?: Partial<Args>): Promise<void>;
555
-
556
- /**
557
- * Timeframe interval for backtest period generation.
558
- * Determines the granularity of timestamps in the generated timeframe array.
559
- *
560
- * Minutes: 1m, 3m, 5m, 15m, 30m
561
- * Hours: 1h, 2h, 4h, 6h, 8h, 12h
562
- * Days: 1d, 3d
563
- */
564
- type FrameInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h" | "12h" | "1d" | "3d";
565
- /**
566
- * Frame parameters passed to ClientFrame constructor.
567
- * Extends IFrameSchema with logger instance for internal logging.
568
- */
569
- interface IFrameParams extends IFrameSchema {
570
- /** Logger service for debug output */
571
- logger: ILogger;
572
- }
573
- /**
574
- * Callbacks for frame lifecycle events.
575
- */
576
- interface IFrameCallbacks {
819
+ interface PartialLossContract {
577
820
  /**
578
- * Called after timeframe array generation.
579
- * Useful for logging or validating the generated timeframes.
821
+ * Trading pair symbol (e.g., "BTCUSDT").
822
+ * Identifies which market this loss event belongs to.
823
+ */
824
+ symbol: string;
825
+ /**
826
+ * Strategy name that generated this signal.
827
+ * Identifies which strategy execution this loss event belongs to.
828
+ */
829
+ strategyName: StrategyName;
830
+ /**
831
+ * Exchange name where this signal is being executed.
832
+ * Identifies which exchange this loss event belongs to.
833
+ */
834
+ exchangeName: ExchangeName;
835
+ /**
836
+ * Frame name where this signal is being executed.
837
+ * Identifies which frame this loss event belongs to (empty string for live mode).
838
+ */
839
+ frameName: FrameName;
840
+ /**
841
+ * Complete signal row data with original prices.
842
+ * Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and partialExecuted.
843
+ */
844
+ data: IPublicSignalRow;
845
+ /**
846
+ * Current market price at which this loss level was reached.
847
+ * Used to calculate actual loss percentage.
848
+ */
849
+ currentPrice: number;
850
+ /**
851
+ * Loss level milestone reached (10, 20, 30, 40, 50, 60, 70, 80, 90, or 100).
852
+ * Represents percentage loss relative to entry price (absolute value).
580
853
  *
581
- * @param timeframe - Array of Date objects representing tick timestamps
582
- * @param startDate - Start of the backtest period
583
- * @param endDate - End of the backtest period
584
- * @param interval - Interval used for generation
854
+ * Note: Stored as positive number, but represents negative loss.
855
+ * level=20 means -20% loss from entry price.
856
+ *
857
+ * @example
858
+ * ```typescript
859
+ * // If entry was $50000 and level is 20:
860
+ * // currentPrice <= $40000 (-20% loss)
861
+ * // Level is stored as 20, not -20
862
+ * ```
585
863
  */
586
- onTimeframe: (timeframe: Date[], startDate: Date, endDate: Date, interval: FrameInterval) => void | Promise<void>;
864
+ level: PartialLevel;
865
+ /**
866
+ * Execution mode flag.
867
+ * - true: Event from backtest execution (historical candle data)
868
+ * - false: Event from live trading (real-time tick)
869
+ */
870
+ backtest: boolean;
871
+ /**
872
+ * Event timestamp in milliseconds since Unix epoch.
873
+ *
874
+ * Timing semantics:
875
+ * - Live mode: when.getTime() at the moment loss level was detected
876
+ * - Backtest mode: candle.timestamp of the candle that triggered the level
877
+ *
878
+ * @example
879
+ * ```typescript
880
+ * const eventDate = new Date(event.timestamp);
881
+ * console.log(`Loss reached at: ${eventDate.toISOString()}`);
882
+ *
883
+ * // Calculate time in loss
884
+ * const entryTime = event.data.pendingAt;
885
+ * const timeInLoss = event.timestamp - entryTime;
886
+ * console.log(`In loss for ${timeInLoss / 1000 / 60} minutes`);
887
+ * ```
888
+ */
889
+ timestamp: number;
587
890
  }
891
+
588
892
  /**
589
- * Frame schema registered via addFrame().
590
- * Defines backtest period and interval for timestamp generation.
893
+ * Contract for schedule ping events during scheduled signal monitoring.
894
+ *
895
+ * Emitted by schedulePingSubject every minute when a scheduled signal is being monitored.
896
+ * Used for tracking scheduled signal lifecycle and custom monitoring logic.
897
+ *
898
+ * Events are emitted only when scheduled signal is active (not cancelled, not activated).
899
+ * Allows users to implement custom cancellation logic via onSchedulePing callback.
900
+ *
901
+ * Consumers:
902
+ * - User callbacks via listenSchedulePing() / listenSchedulePingOnce()
591
903
  *
592
904
  * @example
593
905
  * ```typescript
594
- * addFrame({
595
- * frameName: "1d-backtest",
596
- * interval: "1m",
597
- * startDate: new Date("2024-01-01T00:00:00Z"),
598
- * endDate: new Date("2024-01-02T00:00:00Z"),
599
- * callbacks: {
600
- * onTimeframe: (timeframe, startDate, endDate, interval) => {
601
- * console.log(`Generated ${timeframe.length} timestamps`);
602
- * },
603
- * },
906
+ * import { listenSchedulePing } from "backtest-kit";
907
+ *
908
+ * // Listen to all schedule ping events
909
+ * listenSchedulePing((event) => {
910
+ * console.log(`[${event.backtest ? "Backtest" : "Live"}] Schedule Ping for ${event.symbol}`);
911
+ * console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
912
+ * console.log(`Signal ID: ${event.data.id}, priceOpen: ${event.data.priceOpen}`);
913
+ * console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
604
914
  * });
915
+ *
916
+ * // Wait for specific schedule ping
917
+ * listenSchedulePingOnce(
918
+ * (event) => event.symbol === "BTCUSDT",
919
+ * (event) => console.log("BTCUSDT schedule ping received:", event.timestamp)
920
+ * );
605
921
  * ```
606
922
  */
607
- interface IFrameSchema {
608
- /** Unique identifier for this frame */
609
- frameName: FrameName;
610
- /** Optional developer note for documentation */
611
- note?: string;
612
- /** Interval for timestamp generation */
613
- interval: FrameInterval;
614
- /** Start of backtest period (inclusive) */
615
- startDate: Date;
616
- /** End of backtest period (inclusive) */
617
- endDate: Date;
618
- /** Optional lifecycle callbacks */
619
- callbacks?: Partial<IFrameCallbacks>;
620
- }
621
- /**
622
- * Frame interface for timeframe generation.
623
- * Used internally by backtest orchestration.
624
- */
625
- interface IFrame {
923
+ interface SchedulePingContract {
626
924
  /**
627
- * Generates array of timestamps for backtest iteration.
628
- * Timestamps are spaced according to the configured interval.
925
+ * Trading pair symbol (e.g., "BTCUSDT").
926
+ * Identifies which market this ping event belongs to.
927
+ */
928
+ symbol: string;
929
+ /**
930
+ * Strategy name that is monitoring this scheduled signal.
931
+ * Identifies which strategy execution this ping event belongs to.
932
+ */
933
+ strategyName: StrategyName;
934
+ /**
935
+ * Exchange name where this scheduled signal is being monitored.
936
+ * Identifies which exchange this ping event belongs to.
937
+ */
938
+ exchangeName: ExchangeName;
939
+ /**
940
+ * Complete scheduled signal row data.
941
+ * Contains all signal information: id, position, priceOpen, priceTakeProfit, priceStopLoss, etc.
942
+ */
943
+ data: IScheduledSignalRow;
944
+ /**
945
+ * Execution mode flag.
946
+ * - true: Event from backtest execution (historical candle data)
947
+ * - false: Event from live trading (real-time tick)
948
+ */
949
+ backtest: boolean;
950
+ /**
951
+ * Event timestamp in milliseconds since Unix epoch.
629
952
  *
630
- * @param symbol - Trading pair symbol (unused, for API consistency)
631
- * @returns Promise resolving to array of Date objects
953
+ * Timing semantics:
954
+ * - Live mode: when.getTime() at the moment of ping
955
+ * - Backtest mode: candle.timestamp of the candle being processed
956
+ *
957
+ * @example
958
+ * ```typescript
959
+ * const eventDate = new Date(event.timestamp);
960
+ * console.log(`Ping at: ${eventDate.toISOString()}`);
961
+ * ```
632
962
  */
633
- getTimeframe: (symbol: string, frameName: FrameName) => Promise<Date[]>;
963
+ timestamp: number;
634
964
  }
635
- /**
636
- * Unique identifier for a frame schema.
637
- * Used to retrieve frame instances via dependency injection.
638
- */
639
- type FrameName = string;
640
965
 
641
966
  /**
642
- * Method context containing schema names for operation routing.
967
+ * Contract for active ping events during active pending signal monitoring.
643
968
  *
644
- * Propagated through MethodContextService to provide implicit context
645
- * for retrieving correct strategy/exchange/frame instances.
646
- */
647
- interface IMethodContext {
648
- /** Name of exchange schema to use */
649
- exchangeName: ExchangeName;
650
- /** Name of strategy schema to use */
651
- strategyName: StrategyName;
652
- /** Name of frame schema to use (empty string for live mode) */
653
- frameName: FrameName;
654
- }
655
- /**
656
- * Scoped service for method context propagation.
969
+ * Emitted by activePingSubject every minute when an active pending signal is being monitored.
970
+ * Used for tracking active signal lifecycle and custom dynamic management logic.
657
971
  *
658
- * Uses di-scoped for implicit context passing without explicit parameters.
659
- * Context includes strategyName, exchangeName, and frameName.
972
+ * Events are emitted only when pending signal is active (not closed yet).
973
+ * Allows users to implement custom management logic via onActivePing callback.
660
974
  *
661
- * Used by PublicServices to inject schema names into ConnectionServices.
975
+ * Consumers:
976
+ * - User callbacks via listenActivePing() / listenActivePingOnce()
662
977
  *
663
978
  * @example
664
979
  * ```typescript
665
- * MethodContextService.runAsyncIterator(
666
- * backtestGenerator,
667
- * {
668
- * strategyName: "my-strategy",
669
- * exchangeName: "my-exchange",
670
- * frameName: "1d-backtest"
671
- * }
980
+ * import { listenActivePing } from "backtest-kit";
981
+ *
982
+ * // Listen to all active ping events
983
+ * listenActivePing((event) => {
984
+ * console.log(`[${event.backtest ? "Backtest" : "Live"}] Active Ping for ${event.symbol}`);
985
+ * console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
986
+ * console.log(`Signal ID: ${event.data.id}, Position: ${event.data.position}`);
987
+ * console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
988
+ * });
989
+ *
990
+ * // Wait for specific active ping
991
+ * listenActivePingOnce(
992
+ * (event) => event.symbol === "BTCUSDT",
993
+ * (event) => console.log("BTCUSDT active ping received:", event.timestamp)
672
994
  * );
673
995
  * ```
674
996
  */
675
- declare const MethodContextService: (new () => {
676
- readonly context: IMethodContext;
677
- }) & Omit<{
678
- new (context: IMethodContext): {
679
- readonly context: IMethodContext;
680
- };
681
- }, "prototype"> & di_scoped.IScopedClassRun<[context: IMethodContext]>;
682
-
683
- /**
684
- * Risk rejection result type.
685
- * Can be void, null, or an IRiskRejectionResult object.
686
- */
687
- type RiskRejection = void | IRiskRejectionResult | string | null;
688
- /**
689
- * Risk check arguments for evaluating whether to allow opening a new position.
690
- * Called BEFORE signal creation to validate if conditions allow new signals.
691
- * Contains only passthrough arguments from ClientStrategy context.
692
- */
693
- interface IRiskCheckArgs {
694
- /** Trading pair symbol (e.g., "BTCUSDT") */
695
- symbol: string;
696
- /** Pending signal to apply */
697
- currentSignal: IPublicSignalRow;
698
- /** Strategy name requesting to open a position */
699
- strategyName: StrategyName;
700
- /** Exchange name */
701
- exchangeName: ExchangeName;
702
- /** Risk name */
703
- riskName: RiskName;
704
- /** Frame name */
705
- frameName: FrameName;
706
- /** Current VWAP price */
707
- currentPrice: number;
708
- /** Current timestamp */
709
- timestamp: number;
710
- }
711
- /**
712
- * Active position tracked by ClientRisk for cross-strategy analysis.
713
- */
714
- interface IRiskActivePosition {
715
- /** Strategy name owning the position */
716
- strategyName: StrategyName;
717
- /** Exchange name */
718
- exchangeName: ExchangeName;
719
- /** Frame name */
720
- frameName: FrameName;
721
- /** Trading pair symbol (e.g., "BTCUSDT") */
997
+ interface ActivePingContract {
998
+ /**
999
+ * Trading pair symbol (e.g., "BTCUSDT").
1000
+ * Identifies which market this ping event belongs to.
1001
+ */
722
1002
  symbol: string;
723
- /** Position direction ("long" or "short") */
724
- position: "long" | "short";
725
- /** Entry price */
726
- priceOpen: number;
727
- /** Stop loss price */
728
- priceStopLoss: number;
729
- /** Take profit price */
730
- priceTakeProfit: number;
731
- /** Estimated time in minutes */
732
- minuteEstimatedTime: number;
733
- /** Timestamp when the position was opened */
734
- openTimestamp: number;
735
- }
736
- /**
737
- * Optional callbacks for risk events.
738
- */
739
- interface IRiskCallbacks {
740
- /** Called when a signal is rejected due to risk limits */
741
- onRejected: (symbol: string, params: IRiskCheckArgs) => void | Promise<void>;
742
- /** Called when a signal passes risk checks */
743
- onAllowed: (symbol: string, params: IRiskCheckArgs) => void | Promise<void>;
744
- }
745
- /**
746
- * Payload passed to risk validation functions.
747
- * Extends IRiskCheckArgs with portfolio state data.
748
- */
749
- interface IRiskValidationPayload extends IRiskCheckArgs {
750
- /** Current signal being validated (IRiskSignalRow is calculated internally so priceOpen always exist) */
751
- currentSignal: IRiskSignalRow;
752
- /** Number of currently active positions across all strategies */
753
- activePositionCount: number;
754
- /** List of currently active positions across all strategies */
755
- activePositions: IRiskActivePosition[];
756
- }
757
- /**
758
- * Risk validation rejection result.
759
- * Returned when validation fails, contains debugging information.
760
- */
761
- interface IRiskRejectionResult {
762
- /** Unique identifier for this rejection instance */
763
- id: string | null;
764
- /** Human-readable reason for rejection */
765
- note: string;
766
- }
767
- /**
768
- * Risk validation function type.
769
- * Returns null/void if validation passes, IRiskRejectionResult if validation fails.
770
- * Can also throw error which will be caught and converted to IRiskRejectionResult.
771
- */
772
- interface IRiskValidationFn {
773
- (payload: IRiskValidationPayload): RiskRejection | Promise<RiskRejection>;
774
- }
775
- /**
776
- * Risk validation configuration.
777
- * Defines validation logic with optional documentation.
778
- */
779
- interface IRiskValidation {
780
1003
  /**
781
- * The validation function to apply to the risk check parameters.
1004
+ * Strategy name that is monitoring this active pending signal.
1005
+ * Identifies which strategy execution this ping event belongs to.
782
1006
  */
783
- validate: IRiskValidationFn;
1007
+ strategyName: StrategyName;
784
1008
  /**
785
- * Optional description for documentation purposes.
786
- * Aids in understanding the purpose or behavior of the validation.
1009
+ * Exchange name where this active pending signal is being monitored.
1010
+ * Identifies which exchange this ping event belongs to.
787
1011
  */
788
- note?: string;
789
- }
790
- /**
791
- * Risk schema registered via addRisk().
792
- * Defines portfolio-level risk controls via custom validations.
793
- */
794
- interface IRiskSchema {
795
- /** Unique risk profile identifier */
796
- riskName: RiskName;
797
- /** Optional developer note for documentation */
798
- note?: string;
799
- /** Optional lifecycle event callbacks (onRejected, onAllowed) */
800
- callbacks?: Partial<IRiskCallbacks>;
801
- /** Custom validations array for risk logic */
802
- validations: (IRiskValidation | IRiskValidationFn)[];
803
- }
804
- /**
805
- * Risk parameters passed to ClientRisk constructor.
806
- * Combines schema with runtime dependencies and emission callbacks.
807
- */
808
- interface IRiskParams extends IRiskSchema {
809
- /** Exchange name (e.g., "binance") */
810
1012
  exchangeName: ExchangeName;
811
- /** Logger service for debug output */
812
- logger: ILogger;
813
- /** True if backtest mode, false if live mode */
814
- backtest: boolean;
815
1013
  /**
816
- * Callback invoked when a signal is rejected due to risk limits.
817
- * Called before emitting to riskSubject.
818
- * Used for event emission to riskSubject (separate from schema callbacks).
819
- *
820
- * @param symbol - Trading pair symbol
821
- * @param params - Risk check arguments
822
- * @param activePositionCount - Number of active positions at rejection time
823
- * @param rejectionResult - Rejection result with id and note
824
- * @param timestamp - Event timestamp in milliseconds
825
- * @param backtest - True if backtest mode, false if live mode
1014
+ * Complete pending signal row data.
1015
+ * Contains all signal information: id, position, priceOpen, priceTakeProfit, priceStopLoss, etc.
826
1016
  */
827
- onRejected: (symbol: string, params: IRiskCheckArgs, activePositionCount: number, rejectionResult: IRiskRejectionResult, timestamp: number, backtest: boolean) => void | Promise<void>;
828
- }
829
- /**
830
- * Risk interface implemented by ClientRisk.
831
- * Provides risk checking for signals and position tracking.
832
- */
833
- interface IRisk {
1017
+ data: ISignalRow;
834
1018
  /**
835
- * Check if a signal should be allowed based on risk limits.
836
- *
837
- * @param params - Risk check arguments (position size, portfolio state, etc.)
838
- * @returns Promise resolving to risk check result
1019
+ * Execution mode flag.
1020
+ * - true: Event from backtest execution (historical candle data)
1021
+ * - false: Event from live trading (real-time tick)
839
1022
  */
840
- checkSignal: (params: IRiskCheckArgs) => Promise<boolean>;
1023
+ backtest: boolean;
841
1024
  /**
842
- * Register a new opened signal/position.
1025
+ * Event timestamp in milliseconds since Unix epoch.
843
1026
  *
844
- * @param symbol - Trading pair symbol
845
- * @param context - Context information (strategyName, riskName, exchangeName, frameName)
846
- * @param positionData - Position data (position, prices, timing)
847
- */
848
- addSignal: (symbol: string, context: {
849
- strategyName: StrategyName;
850
- riskName: RiskName;
851
- exchangeName: ExchangeName;
852
- frameName: FrameName;
853
- }, positionData: {
854
- position: "long" | "short";
855
- priceOpen: number;
856
- priceStopLoss: number;
857
- priceTakeProfit: number;
858
- minuteEstimatedTime: number;
859
- openTimestamp: number;
860
- }) => Promise<void>;
861
- /**
862
- * Remove a closed signal/position.
1027
+ * Timing semantics:
1028
+ * - Live mode: when.getTime() at the moment of ping
1029
+ * - Backtest mode: candle.timestamp of the candle being processed
863
1030
  *
864
- * @param symbol - Trading pair symbol
865
- * @param context - Context information (strategyName, riskName, exchangeName, frameName)
1031
+ * @example
1032
+ * ```typescript
1033
+ * const eventDate = new Date(event.timestamp);
1034
+ * console.log(`Active Ping at: ${eventDate.toISOString()}`);
1035
+ * ```
866
1036
  */
867
- removeSignal: (symbol: string, context: {
868
- strategyName: StrategyName;
869
- riskName: RiskName;
870
- exchangeName: ExchangeName;
871
- frameName: FrameName;
872
- }) => Promise<void>;
1037
+ timestamp: number;
873
1038
  }
874
- /**
875
- * Unique risk profile identifier.
876
- */
877
- type RiskName = string;
878
1039
 
879
1040
  /**
880
- * Profit or loss level milestone in percentage points.
881
- * Represents 10%, 20%, 30%, ..., 100% profit or loss thresholds.
1041
+ * Contract for risk rejection events.
882
1042
  *
883
- * Used to track when a signal reaches specific profit/loss milestones.
884
- * Each level is emitted only once per signal (deduplication via Set).
1043
+ * Emitted by riskSubject ONLY when a signal is REJECTED due to risk validation failure.
1044
+ * Used for tracking actual risk violations and monitoring rejected signals.
1045
+ *
1046
+ * Events are emitted only when risk limits are violated (not for allowed signals).
1047
+ * This prevents spam and allows focusing on actual risk management interventions.
1048
+ *
1049
+ * Consumers:
1050
+ * - RiskMarkdownService: Accumulates rejection events for report generation
1051
+ * - User callbacks via listenRisk() / listenRiskOnce()
885
1052
  *
886
1053
  * @example
887
1054
  * ```typescript
888
- * const level: PartialLevel = 50; // 50% profit or loss milestone
889
- * ```
890
- */
891
- type PartialLevel = 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100;
892
- /**
893
- * Serializable partial data for persistence layer.
894
- * Converts Sets to arrays for JSON serialization.
1055
+ * import { listenRisk } from "backtest-kit";
895
1056
  *
896
- * Stored in PersistPartialAdapter as Record<signalId, IPartialData>.
897
- * Loaded on initialization and converted back to IPartialState.
1057
+ * // Listen to all risk rejection events
1058
+ * listenRisk((event) => {
1059
+ * console.log(`[RISK REJECTED] Signal for ${event.symbol}`);
1060
+ * console.log(`Strategy: ${event.strategyName}`);
1061
+ * console.log(`Active positions: ${event.activePositionCount}`);
1062
+ * console.log(`Price: ${event.currentPrice}`);
1063
+ * console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
1064
+ * });
1065
+ *
1066
+ * // Alert on risk rejections for specific symbol
1067
+ * listenRisk((event) => {
1068
+ * if (event.symbol === "BTCUSDT") {
1069
+ * console.warn("BTC signal rejected due to risk limits!");
1070
+ * }
1071
+ * });
1072
+ * ```
898
1073
  */
899
- interface IPartialData {
1074
+ interface RiskContract {
900
1075
  /**
901
- * Array of profit levels that have been reached for this signal.
902
- * Serialized form of IPartialState.profitLevels Set.
1076
+ * Trading pair symbol (e.g., "BTCUSDT").
1077
+ * Identifies which market this rejected signal belongs to.
903
1078
  */
904
- profitLevels: PartialLevel[];
1079
+ symbol: string;
905
1080
  /**
906
- * Array of loss levels that have been reached for this signal.
907
- * Serialized form of IPartialState.lossLevels Set.
1081
+ * Pending signal to apply.
1082
+ * Contains signal details (position, priceOpen, priceTakeProfit, priceStopLoss, etc).
908
1083
  */
909
- lossLevels: PartialLevel[];
910
- }
911
- /**
912
- * Partial profit/loss tracking interface.
913
- * Implemented by ClientPartial and PartialConnectionService.
914
- *
915
- * Tracks profit/loss level milestones for active trading signals.
916
- * Emits events when signals reach 10%, 20%, 30%, etc profit or loss.
917
- *
918
- * @example
919
- * ```typescript
920
- * import { ClientPartial } from "./client/ClientPartial";
921
- *
922
- * const partial = new ClientPartial({
923
- * logger: loggerService,
924
- * onProfit: (symbol, data, price, level, backtest, timestamp) => {
925
- * console.log(`Signal ${data.id} reached ${level}% profit`);
926
- * },
927
- * onLoss: (symbol, data, price, level, backtest, timestamp) => {
928
- * console.log(`Signal ${data.id} reached ${level}% loss`);
929
- * }
930
- * });
931
- *
932
- * await partial.waitForInit("BTCUSDT");
933
- *
934
- * // During signal monitoring
935
- * await partial.profit("BTCUSDT", signal, 51000, 15.5, false, new Date());
936
- * // Emits event when reaching 10% profit milestone
937
- *
938
- * // When signal closes
939
- * await partial.clear("BTCUSDT", signal, 52000);
940
- * ```
941
- */
942
- interface IPartial {
1084
+ currentSignal: ISignalDto;
943
1085
  /**
944
- * Processes profit state and emits events for new profit levels reached.
945
- *
946
- * Called by ClientStrategy during signal monitoring when revenuePercent > 0.
947
- * Checks which profit levels (10%, 20%, 30%, etc) have been reached
948
- * and emits events for new levels only (Set-based deduplication).
949
- *
950
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
951
- * @param data - Signal row data
952
- * @param currentPrice - Current market price
953
- * @param revenuePercent - Current profit percentage (positive value)
954
- * @param backtest - True if backtest mode, false if live mode
955
- * @param when - Event timestamp (current time for live, candle time for backtest)
956
- * @returns Promise that resolves when profit processing is complete
957
- *
958
- * @example
959
- * ```typescript
960
- * // Signal opened at $50000, current price $51500
961
- * // Revenue: 3% profit
962
- * await partial.profit("BTCUSDT", signal, 51500, 3.0, false, new Date());
963
- * // No events emitted (below 10% threshold)
964
- *
965
- * // Price rises to $55000
966
- * // Revenue: 10% profit
967
- * await partial.profit("BTCUSDT", signal, 55000, 10.0, false, new Date());
968
- * // Emits partialProfitSubject event for 10% level
969
- *
970
- * // Price rises to $61000
971
- * // Revenue: 22% profit
972
- * await partial.profit("BTCUSDT", signal, 61000, 22.0, false, new Date());
973
- * // Emits events for 20% level only (10% already emitted)
974
- * ```
1086
+ * Strategy name requesting to open a position.
1087
+ * Identifies which strategy attempted to create the signal.
975
1088
  */
976
- profit(symbol: string, data: IPublicSignalRow, currentPrice: number, revenuePercent: number, backtest: boolean, when: Date): Promise<void>;
1089
+ strategyName: StrategyName;
977
1090
  /**
978
- * Processes loss state and emits events for new loss levels reached.
979
- *
980
- * Called by ClientStrategy during signal monitoring when revenuePercent < 0.
981
- * Checks which loss levels (10%, 20%, 30%, etc) have been reached
982
- * and emits events for new levels only (Set-based deduplication).
983
- *
984
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
985
- * @param data - Signal row data
986
- * @param currentPrice - Current market price
987
- * @param lossPercent - Current loss percentage (negative value)
988
- * @param backtest - True if backtest mode, false if live mode
989
- * @param when - Event timestamp (current time for live, candle time for backtest)
990
- * @returns Promise that resolves when loss processing is complete
1091
+ * Frame name used in backtest execution.
1092
+ * Identifies which frame this signal was for in backtest execution.
1093
+ */
1094
+ frameName: FrameName;
1095
+ /**
1096
+ * Exchange name.
1097
+ * Identifies which exchange this signal was for.
1098
+ */
1099
+ exchangeName: ExchangeName;
1100
+ /**
1101
+ * Current VWAP price at the time of rejection.
1102
+ * Market price when risk check was performed.
1103
+ */
1104
+ currentPrice: number;
1105
+ /**
1106
+ * Number of currently active positions across all strategies at rejection time.
1107
+ * Used to track portfolio-level exposure when signal was rejected.
1108
+ */
1109
+ activePositionCount: number;
1110
+ /**
1111
+ * Unique identifier for this rejection instance.
1112
+ * Generated by ClientRisk for tracking and debugging purposes.
1113
+ * Null if validation threw exception without custom ID.
1114
+ */
1115
+ rejectionId: string | null;
1116
+ /**
1117
+ * Human-readable reason why the signal was rejected.
1118
+ * Captured from IRiskValidation.note or error message.
991
1119
  *
992
1120
  * @example
993
1121
  * ```typescript
994
- * // Signal opened at $50000, current price $48000
995
- * // Loss: -4% loss
996
- * await partial.loss("BTCUSDT", signal, 48000, -4.0, false, new Date());
997
- * // No events emitted (below -10% threshold)
998
- *
999
- * // Price drops to $45000
1000
- * // Loss: -10% loss
1001
- * await partial.loss("BTCUSDT", signal, 45000, -10.0, false, new Date());
1002
- * // Emits partialLossSubject event for 10% level
1003
- *
1004
- * // Price drops to $39000
1005
- * // Loss: -22% loss
1006
- * await partial.loss("BTCUSDT", signal, 39000, -22.0, false, new Date());
1007
- * // Emits events for 20% level only (10% already emitted)
1122
+ * console.log(`Rejection reason: ${event.rejectionNote}`);
1123
+ * // Output: "Rejection reason: Max 3 positions allowed"
1008
1124
  * ```
1009
1125
  */
1010
- loss(symbol: string, data: IPublicSignalRow, currentPrice: number, lossPercent: number, backtest: boolean, when: Date): Promise<void>;
1126
+ rejectionNote: string;
1011
1127
  /**
1012
- * Clears partial profit/loss state when signal closes.
1013
- *
1014
- * Called by ClientStrategy when signal completes (TP/SL/time_expired).
1015
- * Removes signal state from memory and persists changes to disk.
1016
- * Cleans up memoized ClientPartial instance in PartialConnectionService.
1017
- *
1018
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
1019
- * @param data - Signal row data
1020
- * @param priceClose - Final closing price
1021
- * @returns Promise that resolves when clear is complete
1128
+ * Event timestamp in milliseconds since Unix epoch.
1129
+ * Represents when the signal was rejected.
1022
1130
  *
1023
1131
  * @example
1024
1132
  * ```typescript
1025
- * // Signal closes at take profit
1026
- * await partial.clear("BTCUSDT", signal, 52000);
1027
- * // State removed from _states Map
1028
- * // Persisted to disk without this signal's data
1029
- * // Memoized instance cleared from getPartial cache
1133
+ * const eventDate = new Date(event.timestamp);
1134
+ * console.log(`Signal rejected at: ${eventDate.toISOString()}`);
1030
1135
  * ```
1031
1136
  */
1032
- clear(symbol: string, data: IPublicSignalRow, priceClose: number, backtest: boolean): Promise<void>;
1137
+ timestamp: number;
1138
+ /**
1139
+ * Whether this event is from backtest mode (true) or live mode (false).
1140
+ * Used to separate backtest and live risk rejection tracking.
1141
+ */
1142
+ backtest: boolean;
1033
1143
  }
1034
1144
 
1035
1145
  /**
1036
- * Serializable breakeven data for persistence layer.
1037
- * Converts state to simple boolean for JSON serialization.
1146
+ * Constructor type for action handlers with strategy context.
1038
1147
  *
1039
- * Stored in PersistBreakevenAdapter as Record<signalId, IBreakevenData>.
1040
- * Loaded on initialization and converted back to IBreakevenState.
1148
+ * @param strategyName - Strategy identifier (e.g., "rsi_divergence", "macd_cross")
1149
+ * @param frameName - Timeframe identifier (e.g., "1m", "5m", "1h")
1150
+ * @param backtest - True for backtest mode, false for live trading
1151
+ * @returns Partial implementation of IAction (only required handlers)
1152
+ *
1153
+ * @example
1154
+ * ```typescript
1155
+ * class TelegramNotifier implements Partial<IAction> {
1156
+ * constructor(
1157
+ * private strategyName: StrategyName,
1158
+ * private frameName: FrameName,
1159
+ * private backtest: boolean
1160
+ * ) {}
1161
+ *
1162
+ * signal(event: IStrategyTickResult): void {
1163
+ * if (!this.backtest && event.state === 'opened') {
1164
+ * telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
1165
+ * }
1166
+ * }
1167
+ * }
1168
+ *
1169
+ * const actionCtors: TActionCtor[] = [TelegramNotifier, ReduxLogger];
1170
+ * ```
1041
1171
  */
1042
- interface IBreakevenData {
1043
- /**
1044
- * Whether breakeven has been reached for this signal.
1045
- * Serialized form of IBreakevenState.reached.
1046
- */
1047
- reached: boolean;
1048
- }
1172
+ type TActionCtor = new (strategyName: StrategyName, frameName: FrameName, actionName: ActionName, backtest: boolean) => Partial<IPublicAction>;
1049
1173
  /**
1050
- * Breakeven tracking interface.
1051
- * Implemented by ClientBreakeven and BreakevenConnectionService.
1174
+ * Action parameters passed to ClientAction constructor.
1175
+ * Combines schema with runtime dependencies and execution context.
1052
1176
  *
1053
- * Tracks when a signal's stop-loss is moved to breakeven (entry price).
1054
- * Emits events when threshold is reached (price moves far enough to cover transaction costs).
1177
+ * Extended from IActionSchema with:
1178
+ * - Logger instance for debugging and monitoring
1179
+ * - Strategy context (strategyName, frameName)
1180
+ * - Runtime environment flags
1055
1181
  *
1056
1182
  * @example
1057
1183
  * ```typescript
1058
- * import { ClientBreakeven } from "./client/ClientBreakeven";
1059
- *
1060
- * const breakeven = new ClientBreakeven({
1184
+ * const params: IActionParams = {
1185
+ * actionName: "telegram-notifier",
1186
+ * handler: TelegramNotifier,
1187
+ * callbacks: { onInit, onDispose, onSignal },
1061
1188
  * logger: loggerService,
1062
- * onBreakeven: (symbol, data, price, backtest, timestamp) => {
1063
- * console.log(`Signal ${data.id} reached breakeven at ${price}`);
1064
- * }
1065
- * });
1189
+ * strategyName: "rsi_divergence",
1190
+ * frameName: "1h"
1191
+ * };
1066
1192
  *
1067
- * await breakeven.waitForInit("BTCUSDT");
1193
+ * const actionClient = new ClientAction(params);
1194
+ * ```
1195
+ */
1196
+ interface IActionParams extends IActionSchema {
1197
+ /** Logger service for debugging and monitoring action execution */
1198
+ logger: ILogger;
1199
+ /** Strategy identifier this action is attached to */
1200
+ strategyName: StrategyName;
1201
+ /** Exchange name (e.g., "binance") */
1202
+ exchangeName: ExchangeName;
1203
+ /** Timeframe identifier this action is attached to */
1204
+ frameName: FrameName;
1205
+ /** Whether running in backtest mode */
1206
+ backtest: boolean;
1207
+ }
1208
+ /**
1209
+ * Lifecycle and event callbacks for action handlers.
1068
1210
  *
1069
- * // During signal monitoring
1070
- * await breakeven.check("BTCUSDT", signal, 100.5, false, new Date());
1071
- * // Emits event when threshold reached and SL moved to entry
1211
+ * Provides hooks for initialization, disposal, and event handling.
1212
+ * All callbacks are optional and support both sync and async execution.
1072
1213
  *
1073
- * // When signal closes
1074
- * await breakeven.clear("BTCUSDT", signal, 101, false);
1214
+ * Use cases:
1215
+ * - Resource initialization (database connections, file handles)
1216
+ * - Resource cleanup (close connections, flush buffers)
1217
+ * - Event logging and monitoring
1218
+ * - State persistence
1219
+ *
1220
+ * @example
1221
+ * ```typescript
1222
+ * const callbacks: IActionCallbacks = {
1223
+ * onInit: async (strategyName, frameName, backtest) => {
1224
+ * console.log(`[${strategyName}/${frameName}] Action initialized (backtest=${backtest})`);
1225
+ * await db.connect();
1226
+ * },
1227
+ * onSignal: (event, strategyName, frameName, backtest) => {
1228
+ * if (event.action === 'opened') {
1229
+ * console.log(`New signal opened: ${event.signal.id}`);
1230
+ * }
1231
+ * },
1232
+ * onDispose: async (strategyName, frameName, backtest) => {
1233
+ * await db.disconnect();
1234
+ * console.log(`[${strategyName}/${frameName}] Action disposed`);
1235
+ * }
1236
+ * };
1075
1237
  * ```
1076
1238
  */
1077
- interface IBreakeven {
1239
+ interface IActionCallbacks {
1078
1240
  /**
1079
- * Checks if breakeven should be triggered and emits event if conditions met.
1241
+ * Called when action handler is initialized.
1080
1242
  *
1081
- * Called by ClientStrategy during signal monitoring.
1082
- * Checks if:
1083
- * 1. Breakeven not already reached
1084
- * 2. Price has moved far enough to cover transaction costs
1085
- * 3. Stop-loss can be moved to entry price
1086
- *
1087
- * If all conditions met:
1088
- * - Marks breakeven as reached
1089
- * - Calls onBreakeven callback (emits to breakevenSubject)
1090
- * - Persists state to disk
1091
- *
1092
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
1093
- * @param data - Signal row data
1094
- * @param currentPrice - Current market price
1095
- * @param backtest - True if backtest mode, false if live mode
1096
- * @param when - Event timestamp (current time for live, candle time for backtest)
1097
- * @returns Promise that resolves when breakeven check is complete
1243
+ * Use for:
1244
+ * - Opening database connections
1245
+ * - Initializing external services
1246
+ * - Loading persisted state
1247
+ * - Setting up subscriptions
1098
1248
  *
1099
- * @example
1100
- * ```typescript
1101
- * // LONG: entry=100, slippage=0.1%, fee=0.1%, threshold=0.4%
1102
- * // Price at 100.3 - threshold not reached
1103
- * await breakeven.check("BTCUSDT", signal, 100.3, false, new Date());
1104
- * // No event emitted (price < 100.4)
1249
+ * @param actionName - Action identifier
1250
+ * @param strategyName - Strategy identifier
1251
+ * @param frameName - Timeframe identifier
1252
+ * @param backtest - True for backtest mode, false for live trading
1253
+ */
1254
+ onInit(actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1255
+ /**
1256
+ * Called when action handler is disposed.
1105
1257
  *
1106
- * // Price at 100.5 - threshold reached!
1107
- * await breakeven.check("BTCUSDT", signal, 100.5, false, new Date());
1108
- * // Emits breakevenSubject event
1258
+ * Use for:
1259
+ * - Closing database connections
1260
+ * - Flushing buffers
1261
+ * - Saving state to disk
1262
+ * - Unsubscribing from observables
1109
1263
  *
1110
- * // Price at 101 - already at breakeven
1111
- * await breakeven.check("BTCUSDT", signal, 101, false, new Date());
1112
- * // No event emitted (already reached)
1113
- * ```
1264
+ * @param actionName - Action identifier
1265
+ * @param strategyName - Strategy identifier
1266
+ * @param frameName - Timeframe identifier
1267
+ * @param backtest - True for backtest mode, false for live trading
1114
1268
  */
1115
- check(symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean, when: Date): Promise<boolean>;
1269
+ onDispose(actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1116
1270
  /**
1117
- * Clears breakeven state when signal closes.
1118
- *
1119
- * Called by ClientStrategy when signal completes (TP/SL/time_expired).
1120
- * Removes signal state from memory and persists changes to disk.
1121
- * Cleans up memoized ClientBreakeven instance in BreakevenConnectionService.
1271
+ * Called on signal events from all modes (live + backtest).
1122
1272
  *
1123
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
1124
- * @param data - Signal row data
1125
- * @param priceClose - Final closing price
1126
- * @param backtest - True if backtest mode, false if live mode
1127
- * @returns Promise that resolves when clear is complete
1273
+ * Triggered by: StrategyConnectionService via signalEmitter
1274
+ * Frequency: Every tick/candle when strategy is evaluated
1128
1275
  *
1129
- * @example
1130
- * ```typescript
1131
- * // Signal closes at take profit
1132
- * await breakeven.clear("BTCUSDT", signal, 101);
1133
- * // State removed from _states Map
1134
- * // Persisted to disk without this signal's data
1135
- * // Memoized instance cleared from getBreakeven cache
1136
- * ```
1276
+ * @param event - Signal state result (idle, scheduled, opened, active, closed, cancelled)
1277
+ * @param actionName - Action identifier
1278
+ * @param strategyName - Strategy identifier
1279
+ * @param frameName - Timeframe identifier
1280
+ * @param backtest - True for backtest mode, false for live trading
1137
1281
  */
1138
- clear(symbol: string, data: IPublicSignalRow, priceClose: number, backtest: boolean): Promise<void>;
1139
- }
1140
-
1141
- /**
1142
- * Contract for breakeven events.
1143
- *
1144
- * Emitted by breakevenSubject when a signal's stop-loss is moved to breakeven (entry price).
1145
- * Used for tracking risk reduction milestones and monitoring strategy safety.
1146
- *
1147
- * Events are emitted only once per signal (idempotent - protected by ClientBreakeven state).
1148
- * Breakeven is triggered when price moves far enough in profit direction to cover transaction costs.
1149
- *
1150
- * Consumers:
1151
- * - BreakevenMarkdownService: Accumulates events for report generation
1152
- * - User callbacks via listenBreakeven() / listenBreakevenOnce()
1153
- *
1154
- * @example
1155
- * ```typescript
1156
- * import { listenBreakeven } from "backtest-kit";
1157
- *
1158
- * // Listen to all breakeven events
1159
- * listenBreakeven((event) => {
1160
- * console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} moved to breakeven`);
1161
- * console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
1162
- * console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
1163
- * console.log(`Original SL: ${event.data.priceStopLoss}, New SL: ${event.data.priceOpen}`);
1164
- * });
1165
- *
1166
- * // Wait for specific signal to reach breakeven
1167
- * listenBreakevenOnce(
1168
- * (event) => event.data.id === "target-signal-id",
1169
- * (event) => console.log("Signal reached breakeven:", event.data.id)
1170
- * );
1171
- * ```
1172
- */
1173
- interface BreakevenContract {
1282
+ onSignal(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1174
1283
  /**
1175
- * Trading pair symbol (e.g., "BTCUSDT").
1176
- * Identifies which market this breakeven event belongs to.
1284
+ * Called on signal events from live trading only.
1285
+ *
1286
+ * Triggered by: StrategyConnectionService via signalLiveEmitter
1287
+ * Frequency: Every tick in live mode
1288
+ *
1289
+ * @param event - Signal state result from live trading
1290
+ * @param actionName - Action identifier
1291
+ * @param strategyName - Strategy identifier
1292
+ * @param frameName - Timeframe identifier
1293
+ * @param backtest - Always false (live mode only)
1177
1294
  */
1178
- symbol: string;
1295
+ onSignalLive(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1179
1296
  /**
1180
- * Strategy name that generated this signal.
1181
- * Identifies which strategy execution this breakeven event belongs to.
1297
+ * Called on signal events from backtest only.
1298
+ *
1299
+ * Triggered by: StrategyConnectionService via signalBacktestEmitter
1300
+ * Frequency: Every candle in backtest mode
1301
+ *
1302
+ * @param event - Signal state result from backtest
1303
+ * @param actionName - Action identifier
1304
+ * @param strategyName - Strategy identifier
1305
+ * @param frameName - Timeframe identifier
1306
+ * @param backtest - Always true (backtest mode only)
1182
1307
  */
1183
- strategyName: StrategyName;
1308
+ onSignalBacktest(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1184
1309
  /**
1185
- * Exchange name where this signal is being executed.
1186
- * Identifies which exchange this breakeven event belongs to.
1310
+ * Called when breakeven is triggered (stop-loss moved to entry price).
1311
+ *
1312
+ * Triggered by: BreakevenConnectionService via breakevenSubject
1313
+ * Frequency: Once per signal when breakeven threshold is reached
1314
+ *
1315
+ * @param event - Breakeven milestone data
1316
+ * @param actionName - Action identifier
1317
+ * @param strategyName - Strategy identifier
1318
+ * @param frameName - Timeframe identifier
1319
+ * @param backtest - True for backtest mode, false for live trading
1187
1320
  */
1188
- exchangeName: ExchangeName;
1321
+ onBreakevenAvailable(event: BreakevenContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1189
1322
  /**
1190
- * Frame name where this signal is being executed.
1191
- * Identifies which frame this breakeven event belongs to (empty string for live mode).
1323
+ * Called when partial profit level is reached (10%, 20%, 30%, etc).
1324
+ *
1325
+ * Triggered by: PartialConnectionService via partialProfitSubject
1326
+ * Frequency: Once per profit level per signal (deduplicated)
1327
+ *
1328
+ * @param event - Profit milestone data with level and price
1329
+ * @param actionName - Action identifier
1330
+ * @param strategyName - Strategy identifier
1331
+ * @param frameName - Timeframe identifier
1332
+ * @param backtest - True for backtest mode, false for live trading
1192
1333
  */
1193
- frameName: FrameName;
1334
+ onPartialProfitAvailable(event: PartialProfitContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1194
1335
  /**
1195
- * Complete signal row data with original prices.
1196
- * Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and partialExecuted.
1336
+ * Called when partial loss level is reached (-10%, -20%, -30%, etc).
1337
+ *
1338
+ * Triggered by: PartialConnectionService via partialLossSubject
1339
+ * Frequency: Once per loss level per signal (deduplicated)
1340
+ *
1341
+ * @param event - Loss milestone data with level and price
1342
+ * @param actionName - Action identifier
1343
+ * @param strategyName - Strategy identifier
1344
+ * @param frameName - Timeframe identifier
1345
+ * @param backtest - True for backtest mode, false for live trading
1197
1346
  */
1198
- data: IPublicSignalRow;
1347
+ onPartialLossAvailable(event: PartialLossContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1199
1348
  /**
1200
- * Current market price at which breakeven was triggered.
1201
- * Used to verify threshold calculation.
1349
+ * Called during scheduled signal monitoring (every minute while waiting for activation).
1350
+ *
1351
+ * Triggered by: StrategyConnectionService via schedulePingSubject
1352
+ * Frequency: Every minute while scheduled signal is waiting
1353
+ *
1354
+ * @param event - Scheduled signal monitoring data
1355
+ * @param actionName - Action identifier
1356
+ * @param strategyName - Strategy identifier
1357
+ * @param frameName - Timeframe identifier
1358
+ * @param backtest - True for backtest mode, false for live trading
1202
1359
  */
1203
- currentPrice: number;
1360
+ onPingScheduled(event: SchedulePingContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1204
1361
  /**
1205
- * Execution mode flag.
1206
- * - true: Event from backtest execution (historical candle data)
1207
- * - false: Event from live trading (real-time tick)
1362
+ * Called during active pending signal monitoring (every minute while position is active).
1363
+ *
1364
+ * Triggered by: StrategyConnectionService via activePingSubject
1365
+ * Frequency: Every minute while pending signal is active
1366
+ *
1367
+ * @param event - Active pending signal monitoring data
1368
+ * @param actionName - Action identifier
1369
+ * @param strategyName - Strategy identifier
1370
+ * @param frameName - Timeframe identifier
1371
+ * @param backtest - True for backtest mode, false for live trading
1208
1372
  */
1209
- backtest: boolean;
1373
+ onPingActive(event: ActivePingContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1210
1374
  /**
1211
- * Event timestamp in milliseconds since Unix epoch.
1375
+ * Called when signal is rejected by risk management.
1212
1376
  *
1213
- * Timing semantics:
1214
- * - Live mode: when.getTime() at the moment breakeven was set
1215
- * - Backtest mode: candle.timestamp of the candle that triggered breakeven
1377
+ * Triggered by: RiskConnectionService via riskSubject
1378
+ * Frequency: Only when signal fails risk validation (not emitted for allowed signals)
1216
1379
  *
1217
- * @example
1218
- * ```typescript
1219
- * const eventDate = new Date(event.timestamp);
1220
- * console.log(`Breakeven set at: ${eventDate.toISOString()}`);
1221
- * ```
1380
+ * @param event - Risk rejection data with reason and context
1381
+ * @param actionName - Action identifier
1382
+ * @param strategyName - Strategy identifier
1383
+ * @param frameName - Timeframe identifier
1384
+ * @param backtest - True for backtest mode, false for live trading
1222
1385
  */
1223
- timestamp: number;
1386
+ onRiskRejection(event: RiskContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1224
1387
  }
1225
-
1226
1388
  /**
1227
- * Contract for partial profit level events.
1228
- *
1229
- * Emitted by partialProfitSubject when a signal reaches a profit level milestone (10%, 20%, 30%, etc).
1230
- * Used for tracking partial take-profit execution and monitoring strategy performance.
1389
+ * Action schema registered via addActionSchema().
1390
+ * Defines event handler implementation and lifecycle callbacks for state management integration.
1231
1391
  *
1232
- * Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
1233
- * Multiple levels can be emitted in a single tick if price jumps significantly.
1392
+ * Actions provide a way to attach custom event handlers to strategies for:
1393
+ * - State management (Redux, Zustand, MobX)
1394
+ * - Event logging and monitoring
1395
+ * - Real-time notifications (Telegram, Discord, email)
1396
+ * - Analytics and metrics collection
1397
+ * - Custom business logic triggers
1234
1398
  *
1235
- * Consumers:
1236
- * - PartialMarkdownService: Accumulates events for report generation
1237
- * - User callbacks via listenPartialProfit() / listenPartialProfitOnce()
1399
+ * Each action instance is created per strategy-frame pair and receives all events
1400
+ * emitted during strategy execution. Multiple actions can be attached to a single strategy.
1238
1401
  *
1239
1402
  * @example
1240
1403
  * ```typescript
1241
- * import { listenPartialProfit } from "backtest-kit";
1404
+ * import { addActionSchema } from "backtest-kit";
1242
1405
  *
1243
- * // Listen to all partial profit events
1244
- * listenPartialProfit((event) => {
1245
- * console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached ${event.level}% profit`);
1246
- * console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
1247
- * console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
1406
+ * // Define action handler class
1407
+ * class TelegramNotifier implements Partial<IAction> {
1408
+ * constructor(
1409
+ * private strategyName: StrategyName,
1410
+ * private frameName: FrameName,
1411
+ * private backtest: boolean
1412
+ * ) {}
1413
+ *
1414
+ * signal(event: IStrategyTickResult): void {
1415
+ * if (!this.backtest && event.action === 'opened') {
1416
+ * telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
1417
+ * }
1418
+ * }
1419
+ *
1420
+ * dispose(): void {
1421
+ * telegram.close();
1422
+ * }
1423
+ * }
1424
+ *
1425
+ * // Register action schema
1426
+ * addActionSchema({
1427
+ * actionName: "telegram-notifier",
1428
+ * handler: TelegramNotifier,
1429
+ * callbacks: {
1430
+ * onInit: async (strategyName, frameName, backtest) => {
1431
+ * console.log(`Telegram notifier initialized for ${strategyName}/${frameName}`);
1432
+ * },
1433
+ * onSignal: (event, strategyName, frameName, backtest) => {
1434
+ * console.log(`Signal event: ${event.action}`);
1435
+ * }
1436
+ * }
1248
1437
  * });
1438
+ * ```
1439
+ */
1440
+ interface IActionSchema {
1441
+ /** Unique action identifier for registration */
1442
+ actionName: ActionName;
1443
+ /** Optional developer note for documentation */
1444
+ note?: string;
1445
+ /** Action handler constructor (instantiated per strategy-frame pair) */
1446
+ handler: TActionCtor | Partial<IPublicAction>;
1447
+ /** Optional lifecycle and event callbacks */
1448
+ callbacks?: Partial<IActionCallbacks>;
1449
+ }
1450
+ /**
1451
+ * Public action interface for custom action handler implementations.
1249
1452
  *
1250
- * // Wait for first 50% profit level
1251
- * listenPartialProfitOnce(
1252
- * (event) => event.level === 50,
1253
- * (event) => console.log("50% profit reached:", event.data.id)
1254
- * );
1453
+ * Extends IAction with an initialization lifecycle method.
1454
+ * Action handlers implement this interface to receive strategy events and perform custom logic.
1455
+ *
1456
+ * Lifecycle:
1457
+ * 1. Constructor called with (strategyName, frameName, actionName)
1458
+ * 2. init() called once for async initialization (setup connections, load resources)
1459
+ * 3. Event methods called as strategy executes (signal, breakeven, partialProfit, etc.)
1460
+ * 4. dispose() called once for cleanup (close connections, flush buffers)
1461
+ *
1462
+ * Key features:
1463
+ * - init() for async initialization (database connections, API clients, file handles)
1464
+ * - All IAction methods available for event handling
1465
+ * - dispose() guaranteed to run exactly once via singleshot pattern
1466
+ *
1467
+ * Common use cases:
1468
+ * - State management: Redux/Zustand store integration
1469
+ * - Notifications: Telegram/Discord/Email alerts
1470
+ * - Logging: Custom event tracking and monitoring
1471
+ * - Analytics: Metrics collection and reporting
1472
+ * - External systems: Database writes, API calls, file operations
1473
+ *
1474
+ * @example
1475
+ * ```typescript
1476
+ * class TelegramNotifier implements Partial<IPublicAction> {
1477
+ * private bot: TelegramBot | null = null;
1478
+ *
1479
+ * constructor(
1480
+ * private strategyName: string,
1481
+ * private frameName: string,
1482
+ * private actionName: string
1483
+ * ) {}
1484
+ *
1485
+ * // Called once during initialization
1486
+ * async init() {
1487
+ * this.bot = new TelegramBot(process.env.TELEGRAM_TOKEN);
1488
+ * await this.bot.connect();
1489
+ * }
1490
+ *
1491
+ * // Called on every signal event
1492
+ * async signal(event: IStrategyTickResult) {
1493
+ * if (event.action === 'opened') {
1494
+ * await this.bot.send(
1495
+ * `[${this.strategyName}/${this.frameName}] Signal opened: ${event.signal.side}`
1496
+ * );
1497
+ * }
1498
+ * }
1499
+ *
1500
+ * // Called once during cleanup
1501
+ * async dispose() {
1502
+ * await this.bot?.disconnect();
1503
+ * this.bot = null;
1504
+ * }
1505
+ * }
1255
1506
  * ```
1507
+ *
1508
+ * @see IAction for all available event methods
1509
+ * @see TActionCtor for constructor signature requirements
1510
+ * @see ClientAction for internal wrapper that manages lifecycle
1256
1511
  */
1257
- interface PartialProfitContract {
1512
+ type IPublicAction = {
1513
+ [key in keyof IAction]?: IAction[key];
1514
+ } & {
1515
+ init?(): void | Promise<void>;
1516
+ };
1517
+ /**
1518
+ * Action interface for state manager integration.
1519
+ *
1520
+ * Provides methods to handle all events emitted by connection services.
1521
+ * Each method corresponds to a specific event type emitted via .next() calls.
1522
+ *
1523
+ * Use this interface to implement custom state management logic:
1524
+ * - Redux/Zustand action dispatchers
1525
+ * - Event logging systems
1526
+ * - Real-time monitoring dashboards
1527
+ * - Analytics and metrics collection
1528
+ *
1529
+ * @example
1530
+ * ```typescript
1531
+ * class ReduxStateManager implements IAction {
1532
+ * constructor(private store: Store) {}
1533
+ *
1534
+ * signal(event: IStrategyTickResult): void {
1535
+ * this.store.dispatch({ type: 'SIGNAL', payload: event });
1536
+ * }
1537
+ *
1538
+ * breakeven(event: BreakevenContract): void {
1539
+ * this.store.dispatch({ type: 'BREAKEVEN', payload: event });
1540
+ * }
1541
+ *
1542
+ * // ... implement other methods
1543
+ * }
1544
+ * ```
1545
+ */
1546
+ interface IAction {
1258
1547
  /**
1259
- * Trading pair symbol (e.g., "BTCUSDT").
1260
- * Identifies which market this profit event belongs to.
1548
+ * Handles signal events from all modes (live + backtest).
1549
+ *
1550
+ * Emitted by: StrategyConnectionService via signalEmitter
1551
+ * Source: StrategyConnectionService.tick() and StrategyConnectionService.backtest()
1552
+ * Frequency: Every tick/candle when strategy is evaluated
1553
+ *
1554
+ * @param event - Signal state result (idle, scheduled, opened, active, closed, cancelled)
1261
1555
  */
1262
- symbol: string;
1556
+ signal(event: IStrategyTickResult): void | Promise<void>;
1263
1557
  /**
1264
- * Strategy name that generated this signal.
1265
- * Identifies which strategy execution this profit event belongs to.
1558
+ * Handles signal events from live trading only.
1559
+ *
1560
+ * Emitted by: StrategyConnectionService via signalLiveEmitter
1561
+ * Source: StrategyConnectionService.tick() when backtest=false
1562
+ * Frequency: Every tick in live mode
1563
+ *
1564
+ * @param event - Signal state result from live trading
1266
1565
  */
1267
- strategyName: StrategyName;
1566
+ signalLive(event: IStrategyTickResult): void | Promise<void>;
1268
1567
  /**
1269
- * Exchange name where this signal is being executed.
1270
- * Identifies which exchange this profit event belongs to.
1568
+ * Handles signal events from backtest only.
1569
+ *
1570
+ * Emitted by: StrategyConnectionService via signalBacktestEmitter
1571
+ * Source: StrategyConnectionService.backtest() when backtest=true
1572
+ * Frequency: Every candle in backtest mode
1573
+ *
1574
+ * @param event - Signal state result from backtest
1271
1575
  */
1272
- exchangeName: ExchangeName;
1576
+ signalBacktest(event: IStrategyTickResult): void | Promise<void>;
1273
1577
  /**
1274
- * Frame name where this signal is being executed.
1275
- * Identifies which frame this profit event belongs to (empty string for live mode).
1578
+ * Handles breakeven events when stop-loss is moved to entry price.
1579
+ *
1580
+ * Emitted by: BreakevenConnectionService via breakevenSubject
1581
+ * Source: COMMIT_BREAKEVEN_FN callback in BreakevenConnectionService
1582
+ * Frequency: Once per signal when breakeven threshold is reached
1583
+ *
1584
+ * @param event - Breakeven milestone data
1276
1585
  */
1277
- frameName: FrameName;
1586
+ breakevenAvailable(event: BreakevenContract): void | Promise<void>;
1278
1587
  /**
1279
- * Complete signal row data with original prices.
1280
- * Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and partialExecuted.
1588
+ * Handles partial profit level events (10%, 20%, 30%, etc).
1589
+ *
1590
+ * Emitted by: PartialConnectionService via partialProfitSubject
1591
+ * Source: COMMIT_PROFIT_FN callback in PartialConnectionService
1592
+ * Frequency: Once per profit level per signal (deduplicated)
1593
+ *
1594
+ * @param event - Profit milestone data with level and price
1281
1595
  */
1282
- data: IPublicSignalRow;
1596
+ partialProfitAvailable(event: PartialProfitContract): void | Promise<void>;
1283
1597
  /**
1284
- * Current market price at which this profit level was reached.
1285
- * Used to calculate actual profit percentage.
1598
+ * Handles partial loss level events (-10%, -20%, -30%, etc).
1599
+ *
1600
+ * Emitted by: PartialConnectionService via partialLossSubject
1601
+ * Source: COMMIT_LOSS_FN callback in PartialConnectionService
1602
+ * Frequency: Once per loss level per signal (deduplicated)
1603
+ *
1604
+ * @param event - Loss milestone data with level and price
1286
1605
  */
1287
- currentPrice: number;
1606
+ partialLossAvailable(event: PartialLossContract): void | Promise<void>;
1288
1607
  /**
1289
- * Profit level milestone reached (10, 20, 30, 40, 50, 60, 70, 80, 90, or 100).
1290
- * Represents percentage profit relative to entry price.
1608
+ * Handles scheduled ping events during scheduled signal monitoring.
1291
1609
  *
1292
- * @example
1293
- * ```typescript
1294
- * // If entry was $50000 and level is 20:
1295
- * // currentPrice >= $60000 (20% profit)
1296
- * ```
1610
+ * Emitted by: StrategyConnectionService via schedulePingSubject
1611
+ * Source: CREATE_COMMIT_SCHEDULE_PING_FN callback in StrategyConnectionService
1612
+ * Frequency: Every minute while scheduled signal is waiting for activation
1613
+ *
1614
+ * @param event - Scheduled signal monitoring data
1297
1615
  */
1298
- level: PartialLevel;
1616
+ pingScheduled(event: SchedulePingContract): void | Promise<void>;
1299
1617
  /**
1300
- * Execution mode flag.
1301
- * - true: Event from backtest execution (historical candle data)
1302
- * - false: Event from live trading (real-time tick)
1618
+ * Handles active ping events during active pending signal monitoring.
1619
+ *
1620
+ * Emitted by: StrategyConnectionService via activePingSubject
1621
+ * Source: CREATE_COMMIT_ACTIVE_PING_FN callback in StrategyConnectionService
1622
+ * Frequency: Every minute while pending signal is active
1623
+ *
1624
+ * @param event - Active pending signal monitoring data
1303
1625
  */
1304
- backtest: boolean;
1626
+ pingActive(event: ActivePingContract): void | Promise<void>;
1305
1627
  /**
1306
- * Event timestamp in milliseconds since Unix epoch.
1628
+ * Handles risk rejection events when signals fail risk validation.
1307
1629
  *
1308
- * Timing semantics:
1309
- * - Live mode: when.getTime() at the moment profit level was detected
1310
- * - Backtest mode: candle.timestamp of the candle that triggered the level
1630
+ * Emitted by: RiskConnectionService via riskSubject
1631
+ * Source: COMMIT_REJECTION_FN callback in RiskConnectionService
1632
+ * Frequency: Only when signal is rejected (not emitted for allowed signals)
1311
1633
  *
1312
- * @example
1313
- * ```typescript
1314
- * const eventDate = new Date(event.timestamp);
1315
- * console.log(`Profit reached at: ${eventDate.toISOString()}`);
1316
- * ```
1634
+ * @param event - Risk rejection data with reason and context
1317
1635
  */
1318
- timestamp: number;
1319
- }
1320
-
1321
- /**
1322
- * Contract for partial loss level events.
1323
- *
1324
- * Emitted by partialLossSubject when a signal reaches a loss level milestone (-10%, -20%, -30%, etc).
1325
- * Used for tracking partial stop-loss execution and monitoring strategy drawdown.
1326
- *
1327
- * Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
1328
- * Multiple levels can be emitted in a single tick if price drops significantly.
1329
- *
1330
- * Consumers:
1331
- * - PartialMarkdownService: Accumulates events for report generation
1332
- * - User callbacks via listenPartialLoss() / listenPartialLossOnce()
1333
- *
1334
- * @example
1335
- * ```typescript
1336
- * import { listenPartialLoss } from "backtest-kit";
1337
- *
1338
- * // Listen to all partial loss events
1339
- * listenPartialLoss((event) => {
1340
- * console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached -${event.level}% loss`);
1341
- * console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
1342
- * console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
1343
- *
1344
- * // Alert on significant loss
1345
- * if (event.level >= 30 && !event.backtest) {
1346
- * console.warn("HIGH LOSS ALERT:", event.data.id);
1347
- * }
1348
- * });
1349
- *
1350
- * // Wait for first 20% loss level
1351
- * listenPartialLossOnce(
1352
- * (event) => event.level === 20,
1353
- * (event) => console.log("20% loss reached:", event.data.id)
1354
- * );
1355
- * ```
1356
- */
1357
- interface PartialLossContract {
1358
- /**
1359
- * Trading pair symbol (e.g., "BTCUSDT").
1360
- * Identifies which market this loss event belongs to.
1361
- */
1362
- symbol: string;
1363
- /**
1364
- * Strategy name that generated this signal.
1365
- * Identifies which strategy execution this loss event belongs to.
1366
- */
1367
- strategyName: StrategyName;
1368
- /**
1369
- * Exchange name where this signal is being executed.
1370
- * Identifies which exchange this loss event belongs to.
1371
- */
1372
- exchangeName: ExchangeName;
1373
- /**
1374
- * Frame name where this signal is being executed.
1375
- * Identifies which frame this loss event belongs to (empty string for live mode).
1376
- */
1377
- frameName: FrameName;
1378
- /**
1379
- * Complete signal row data with original prices.
1380
- * Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and partialExecuted.
1381
- */
1382
- data: IPublicSignalRow;
1383
- /**
1384
- * Current market price at which this loss level was reached.
1385
- * Used to calculate actual loss percentage.
1386
- */
1387
- currentPrice: number;
1388
- /**
1389
- * Loss level milestone reached (10, 20, 30, 40, 50, 60, 70, 80, 90, or 100).
1390
- * Represents percentage loss relative to entry price (absolute value).
1391
- *
1392
- * Note: Stored as positive number, but represents negative loss.
1393
- * level=20 means -20% loss from entry price.
1394
- *
1395
- * @example
1396
- * ```typescript
1397
- * // If entry was $50000 and level is 20:
1398
- * // currentPrice <= $40000 (-20% loss)
1399
- * // Level is stored as 20, not -20
1400
- * ```
1401
- */
1402
- level: PartialLevel;
1403
- /**
1404
- * Execution mode flag.
1405
- * - true: Event from backtest execution (historical candle data)
1406
- * - false: Event from live trading (real-time tick)
1407
- */
1408
- backtest: boolean;
1636
+ riskRejection(event: RiskContract): void | Promise<void>;
1409
1637
  /**
1410
- * Event timestamp in milliseconds since Unix epoch.
1411
- *
1412
- * Timing semantics:
1413
- * - Live mode: when.getTime() at the moment loss level was detected
1414
- * - Backtest mode: candle.timestamp of the candle that triggered the level
1415
- *
1416
- * @example
1417
- * ```typescript
1418
- * const eventDate = new Date(event.timestamp);
1419
- * console.log(`Loss reached at: ${eventDate.toISOString()}`);
1638
+ * Cleans up resources and subscriptions when action handler is no longer needed.
1420
1639
  *
1421
- * // Calculate time in loss
1422
- * const entryTime = event.data.pendingAt;
1423
- * const timeInLoss = event.timestamp - entryTime;
1424
- * console.log(`In loss for ${timeInLoss / 1000 / 60} minutes`);
1425
- * ```
1640
+ * Called by: Connection services during shutdown
1641
+ * Use for: Unsubscribing from observables, closing connections, flushing buffers
1426
1642
  */
1427
- timestamp: number;
1643
+ dispose(): void | Promise<void>;
1428
1644
  }
1645
+ /**
1646
+ * Unique action identifier.
1647
+ */
1648
+ type ActionName = string;
1429
1649
 
1430
1650
  /**
1431
- * Contract for schedule ping events during scheduled signal monitoring.
1432
- *
1433
- * Emitted by schedulePingSubject every minute when a scheduled signal is being monitored.
1434
- * Used for tracking scheduled signal lifecycle and custom monitoring logic.
1435
- *
1436
- * Events are emitted only when scheduled signal is active (not cancelled, not activated).
1437
- * Allows users to implement custom cancellation logic via onSchedulePing callback.
1438
- *
1439
- * Consumers:
1440
- * - User callbacks via listenSchedulePing() / listenSchedulePingOnce()
1441
- *
1442
- * @example
1443
- * ```typescript
1444
- * import { listenSchedulePing } from "backtest-kit";
1445
- *
1446
- * // Listen to all schedule ping events
1447
- * listenSchedulePing((event) => {
1448
- * console.log(`[${event.backtest ? "Backtest" : "Live"}] Schedule Ping for ${event.symbol}`);
1449
- * console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
1450
- * console.log(`Signal ID: ${event.data.id}, priceOpen: ${event.data.priceOpen}`);
1451
- * console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
1452
- * });
1453
- *
1454
- * // Wait for specific schedule ping
1455
- * listenSchedulePingOnce(
1456
- * (event) => event.symbol === "BTCUSDT",
1457
- * (event) => console.log("BTCUSDT schedule ping received:", event.timestamp)
1458
- * );
1459
- * ```
1651
+ * Base fields for all signal commit events.
1460
1652
  */
1461
- interface SchedulePingContract {
1462
- /**
1463
- * Trading pair symbol (e.g., "BTCUSDT").
1464
- * Identifies which market this ping event belongs to.
1465
- */
1653
+ interface SignalCommitBase {
1654
+ /** Trading pair symbol (e.g., "BTCUSDT") */
1466
1655
  symbol: string;
1467
- /**
1468
- * Strategy name that is monitoring this scheduled signal.
1469
- * Identifies which strategy execution this ping event belongs to.
1470
- */
1656
+ /** Strategy name that generated this signal */
1471
1657
  strategyName: StrategyName;
1472
- /**
1473
- * Exchange name where this scheduled signal is being monitored.
1474
- * Identifies which exchange this ping event belongs to.
1475
- */
1658
+ /** Exchange name where signal was executed */
1476
1659
  exchangeName: ExchangeName;
1477
- /**
1478
- * Complete scheduled signal row data.
1479
- * Contains all signal information: id, position, priceOpen, priceTakeProfit, priceStopLoss, etc.
1480
- */
1481
- data: IScheduledSignalRow;
1482
- /**
1483
- * Execution mode flag.
1484
- * - true: Event from backtest execution (historical candle data)
1485
- * - false: Event from live trading (real-time tick)
1486
- */
1660
+ /** Timeframe name (used in backtest mode, empty string in live mode) */
1661
+ frameName: FrameName;
1662
+ /** Whether this event is from backtest mode (true) or live mode (false) */
1487
1663
  backtest: boolean;
1664
+ /** Unique signal identifier (UUID v4) */
1665
+ signalId: string;
1666
+ /** Timestamp from execution context (tick's when or backtest candle timestamp) */
1667
+ timestamp: number;
1488
1668
  /**
1489
- * Event timestamp in milliseconds since Unix epoch.
1490
- *
1491
- * Timing semantics:
1492
- * - Live mode: when.getTime() at the moment of ping
1493
- * - Backtest mode: candle.timestamp of the candle being processed
1494
- *
1495
- * @example
1496
- * ```typescript
1497
- * const eventDate = new Date(event.timestamp);
1498
- * console.log(`Ping at: ${eventDate.toISOString()}`);
1499
- * ```
1669
+ * Total number of DCA entries at the time of this event (_entry.length).
1670
+ * 1 = no averaging done (only initial entry). 2+ = averaged positions.
1500
1671
  */
1501
- timestamp: number;
1672
+ totalEntries: number;
1673
+ /** Original entry price at signal creation (unchanged by DCA averaging). */
1674
+ originalPriceOpen: number;
1502
1675
  }
1503
-
1504
1676
  /**
1505
- * Contract for active ping events during active pending signal monitoring.
1506
- *
1507
- * Emitted by activePingSubject every minute when an active pending signal is being monitored.
1508
- * Used for tracking active signal lifecycle and custom dynamic management logic.
1509
- *
1510
- * Events are emitted only when pending signal is active (not closed yet).
1511
- * Allows users to implement custom management logic via onActivePing callback.
1512
- *
1513
- * Consumers:
1514
- * - User callbacks via listenActivePing() / listenActivePingOnce()
1515
- *
1516
- * @example
1517
- * ```typescript
1518
- * import { listenActivePing } from "backtest-kit";
1519
- *
1520
- * // Listen to all active ping events
1521
- * listenActivePing((event) => {
1522
- * console.log(`[${event.backtest ? "Backtest" : "Live"}] Active Ping for ${event.symbol}`);
1523
- * console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
1524
- * console.log(`Signal ID: ${event.data.id}, Position: ${event.data.position}`);
1525
- * console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
1526
- * });
1527
- *
1528
- * // Wait for specific active ping
1529
- * listenActivePingOnce(
1530
- * (event) => event.symbol === "BTCUSDT",
1531
- * (event) => console.log("BTCUSDT active ping received:", event.timestamp)
1532
- * );
1533
- * ```
1534
- */
1535
- interface ActivePingContract {
1536
- /**
1537
- * Trading pair symbol (e.g., "BTCUSDT").
1538
- * Identifies which market this ping event belongs to.
1539
- */
1540
- symbol: string;
1541
- /**
1542
- * Strategy name that is monitoring this active pending signal.
1543
- * Identifies which strategy execution this ping event belongs to.
1544
- */
1545
- strategyName: StrategyName;
1546
- /**
1547
- * Exchange name where this active pending signal is being monitored.
1548
- * Identifies which exchange this ping event belongs to.
1549
- */
1550
- exchangeName: ExchangeName;
1551
- /**
1552
- * Complete pending signal row data.
1553
- * Contains all signal information: id, position, priceOpen, priceTakeProfit, priceStopLoss, etc.
1554
- */
1555
- data: ISignalRow;
1556
- /**
1557
- * Execution mode flag.
1558
- * - true: Event from backtest execution (historical candle data)
1559
- * - false: Event from live trading (real-time tick)
1560
- */
1561
- backtest: boolean;
1562
- /**
1563
- * Event timestamp in milliseconds since Unix epoch.
1564
- *
1565
- * Timing semantics:
1566
- * - Live mode: when.getTime() at the moment of ping
1567
- * - Backtest mode: candle.timestamp of the candle being processed
1568
- *
1569
- * @example
1570
- * ```typescript
1571
- * const eventDate = new Date(event.timestamp);
1572
- * console.log(`Active Ping at: ${eventDate.toISOString()}`);
1573
- * ```
1574
- */
1575
- timestamp: number;
1576
- }
1577
-
1578
- /**
1579
- * Contract for risk rejection events.
1580
- *
1581
- * Emitted by riskSubject ONLY when a signal is REJECTED due to risk validation failure.
1582
- * Used for tracking actual risk violations and monitoring rejected signals.
1583
- *
1584
- * Events are emitted only when risk limits are violated (not for allowed signals).
1585
- * This prevents spam and allows focusing on actual risk management interventions.
1586
- *
1587
- * Consumers:
1588
- * - RiskMarkdownService: Accumulates rejection events for report generation
1589
- * - User callbacks via listenRisk() / listenRiskOnce()
1590
- *
1591
- * @example
1592
- * ```typescript
1593
- * import { listenRisk } from "backtest-kit";
1594
- *
1595
- * // Listen to all risk rejection events
1596
- * listenRisk((event) => {
1597
- * console.log(`[RISK REJECTED] Signal for ${event.symbol}`);
1598
- * console.log(`Strategy: ${event.strategyName}`);
1599
- * console.log(`Active positions: ${event.activePositionCount}`);
1600
- * console.log(`Price: ${event.currentPrice}`);
1601
- * console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
1602
- * });
1603
- *
1604
- * // Alert on risk rejections for specific symbol
1605
- * listenRisk((event) => {
1606
- * if (event.symbol === "BTCUSDT") {
1607
- * console.warn("BTC signal rejected due to risk limits!");
1608
- * }
1609
- * });
1610
- * ```
1611
- */
1612
- interface RiskContract {
1613
- /**
1614
- * Trading pair symbol (e.g., "BTCUSDT").
1615
- * Identifies which market this rejected signal belongs to.
1616
- */
1617
- symbol: string;
1618
- /**
1619
- * Pending signal to apply.
1620
- * Contains signal details (position, priceOpen, priceTakeProfit, priceStopLoss, etc).
1621
- */
1622
- currentSignal: ISignalDto;
1623
- /**
1624
- * Strategy name requesting to open a position.
1625
- * Identifies which strategy attempted to create the signal.
1626
- */
1627
- strategyName: StrategyName;
1628
- /**
1629
- * Frame name used in backtest execution.
1630
- * Identifies which frame this signal was for in backtest execution.
1631
- */
1632
- frameName: FrameName;
1633
- /**
1634
- * Exchange name.
1635
- * Identifies which exchange this signal was for.
1636
- */
1637
- exchangeName: ExchangeName;
1638
- /**
1639
- * Current VWAP price at the time of rejection.
1640
- * Market price when risk check was performed.
1641
- */
1642
- currentPrice: number;
1643
- /**
1644
- * Number of currently active positions across all strategies at rejection time.
1645
- * Used to track portfolio-level exposure when signal was rejected.
1646
- */
1647
- activePositionCount: number;
1648
- /**
1649
- * Unique identifier for this rejection instance.
1650
- * Generated by ClientRisk for tracking and debugging purposes.
1651
- * Null if validation threw exception without custom ID.
1652
- */
1653
- rejectionId: string | null;
1654
- /**
1655
- * Human-readable reason why the signal was rejected.
1656
- * Captured from IRiskValidation.note or error message.
1657
- *
1658
- * @example
1659
- * ```typescript
1660
- * console.log(`Rejection reason: ${event.rejectionNote}`);
1661
- * // Output: "Rejection reason: Max 3 positions allowed"
1662
- * ```
1663
- */
1664
- rejectionNote: string;
1665
- /**
1666
- * Event timestamp in milliseconds since Unix epoch.
1667
- * Represents when the signal was rejected.
1668
- *
1669
- * @example
1670
- * ```typescript
1671
- * const eventDate = new Date(event.timestamp);
1672
- * console.log(`Signal rejected at: ${eventDate.toISOString()}`);
1673
- * ```
1674
- */
1675
- timestamp: number;
1676
- /**
1677
- * Whether this event is from backtest mode (true) or live mode (false).
1678
- * Used to separate backtest and live risk rejection tracking.
1679
- */
1680
- backtest: boolean;
1681
- }
1682
-
1683
- /**
1684
- * Constructor type for action handlers with strategy context.
1685
- *
1686
- * @param strategyName - Strategy identifier (e.g., "rsi_divergence", "macd_cross")
1687
- * @param frameName - Timeframe identifier (e.g., "1m", "5m", "1h")
1688
- * @param backtest - True for backtest mode, false for live trading
1689
- * @returns Partial implementation of IAction (only required handlers)
1690
- *
1691
- * @example
1692
- * ```typescript
1693
- * class TelegramNotifier implements Partial<IAction> {
1694
- * constructor(
1695
- * private strategyName: StrategyName,
1696
- * private frameName: FrameName,
1697
- * private backtest: boolean
1698
- * ) {}
1699
- *
1700
- * signal(event: IStrategyTickResult): void {
1701
- * if (!this.backtest && event.state === 'opened') {
1702
- * telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
1703
- * }
1704
- * }
1705
- * }
1706
- *
1707
- * const actionCtors: TActionCtor[] = [TelegramNotifier, ReduxLogger];
1708
- * ```
1709
- */
1710
- type TActionCtor = new (strategyName: StrategyName, frameName: FrameName, actionName: ActionName, backtest: boolean) => Partial<IPublicAction>;
1711
- /**
1712
- * Action parameters passed to ClientAction constructor.
1713
- * Combines schema with runtime dependencies and execution context.
1714
- *
1715
- * Extended from IActionSchema with:
1716
- * - Logger instance for debugging and monitoring
1717
- * - Strategy context (strategyName, frameName)
1718
- * - Runtime environment flags
1719
- *
1720
- * @example
1721
- * ```typescript
1722
- * const params: IActionParams = {
1723
- * actionName: "telegram-notifier",
1724
- * handler: TelegramNotifier,
1725
- * callbacks: { onInit, onDispose, onSignal },
1726
- * logger: loggerService,
1727
- * strategyName: "rsi_divergence",
1728
- * frameName: "1h"
1729
- * };
1730
- *
1731
- * const actionClient = new ClientAction(params);
1732
- * ```
1733
- */
1734
- interface IActionParams extends IActionSchema {
1735
- /** Logger service for debugging and monitoring action execution */
1736
- logger: ILogger;
1737
- /** Strategy identifier this action is attached to */
1738
- strategyName: StrategyName;
1739
- /** Exchange name (e.g., "binance") */
1740
- exchangeName: ExchangeName;
1741
- /** Timeframe identifier this action is attached to */
1742
- frameName: FrameName;
1743
- /** Whether running in backtest mode */
1744
- backtest: boolean;
1745
- }
1746
- /**
1747
- * Lifecycle and event callbacks for action handlers.
1748
- *
1749
- * Provides hooks for initialization, disposal, and event handling.
1750
- * All callbacks are optional and support both sync and async execution.
1751
- *
1752
- * Use cases:
1753
- * - Resource initialization (database connections, file handles)
1754
- * - Resource cleanup (close connections, flush buffers)
1755
- * - Event logging and monitoring
1756
- * - State persistence
1757
- *
1758
- * @example
1759
- * ```typescript
1760
- * const callbacks: IActionCallbacks = {
1761
- * onInit: async (strategyName, frameName, backtest) => {
1762
- * console.log(`[${strategyName}/${frameName}] Action initialized (backtest=${backtest})`);
1763
- * await db.connect();
1764
- * },
1765
- * onSignal: (event, strategyName, frameName, backtest) => {
1766
- * if (event.action === 'opened') {
1767
- * console.log(`New signal opened: ${event.signal.id}`);
1768
- * }
1769
- * },
1770
- * onDispose: async (strategyName, frameName, backtest) => {
1771
- * await db.disconnect();
1772
- * console.log(`[${strategyName}/${frameName}] Action disposed`);
1773
- * }
1774
- * };
1775
- * ```
1776
- */
1777
- interface IActionCallbacks {
1778
- /**
1779
- * Called when action handler is initialized.
1780
- *
1781
- * Use for:
1782
- * - Opening database connections
1783
- * - Initializing external services
1784
- * - Loading persisted state
1785
- * - Setting up subscriptions
1786
- *
1787
- * @param actionName - Action identifier
1788
- * @param strategyName - Strategy identifier
1789
- * @param frameName - Timeframe identifier
1790
- * @param backtest - True for backtest mode, false for live trading
1791
- */
1792
- onInit(actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1793
- /**
1794
- * Called when action handler is disposed.
1795
- *
1796
- * Use for:
1797
- * - Closing database connections
1798
- * - Flushing buffers
1799
- * - Saving state to disk
1800
- * - Unsubscribing from observables
1801
- *
1802
- * @param actionName - Action identifier
1803
- * @param strategyName - Strategy identifier
1804
- * @param frameName - Timeframe identifier
1805
- * @param backtest - True for backtest mode, false for live trading
1806
- */
1807
- onDispose(actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1808
- /**
1809
- * Called on signal events from all modes (live + backtest).
1810
- *
1811
- * Triggered by: StrategyConnectionService via signalEmitter
1812
- * Frequency: Every tick/candle when strategy is evaluated
1813
- *
1814
- * @param event - Signal state result (idle, scheduled, opened, active, closed, cancelled)
1815
- * @param actionName - Action identifier
1816
- * @param strategyName - Strategy identifier
1817
- * @param frameName - Timeframe identifier
1818
- * @param backtest - True for backtest mode, false for live trading
1819
- */
1820
- onSignal(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1821
- /**
1822
- * Called on signal events from live trading only.
1823
- *
1824
- * Triggered by: StrategyConnectionService via signalLiveEmitter
1825
- * Frequency: Every tick in live mode
1826
- *
1827
- * @param event - Signal state result from live trading
1828
- * @param actionName - Action identifier
1829
- * @param strategyName - Strategy identifier
1830
- * @param frameName - Timeframe identifier
1831
- * @param backtest - Always false (live mode only)
1832
- */
1833
- onSignalLive(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1834
- /**
1835
- * Called on signal events from backtest only.
1836
- *
1837
- * Triggered by: StrategyConnectionService via signalBacktestEmitter
1838
- * Frequency: Every candle in backtest mode
1839
- *
1840
- * @param event - Signal state result from backtest
1841
- * @param actionName - Action identifier
1842
- * @param strategyName - Strategy identifier
1843
- * @param frameName - Timeframe identifier
1844
- * @param backtest - Always true (backtest mode only)
1845
- */
1846
- onSignalBacktest(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1847
- /**
1848
- * Called when breakeven is triggered (stop-loss moved to entry price).
1849
- *
1850
- * Triggered by: BreakevenConnectionService via breakevenSubject
1851
- * Frequency: Once per signal when breakeven threshold is reached
1852
- *
1853
- * @param event - Breakeven milestone data
1854
- * @param actionName - Action identifier
1855
- * @param strategyName - Strategy identifier
1856
- * @param frameName - Timeframe identifier
1857
- * @param backtest - True for backtest mode, false for live trading
1858
- */
1859
- onBreakevenAvailable(event: BreakevenContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1860
- /**
1861
- * Called when partial profit level is reached (10%, 20%, 30%, etc).
1862
- *
1863
- * Triggered by: PartialConnectionService via partialProfitSubject
1864
- * Frequency: Once per profit level per signal (deduplicated)
1865
- *
1866
- * @param event - Profit milestone data with level and price
1867
- * @param actionName - Action identifier
1868
- * @param strategyName - Strategy identifier
1869
- * @param frameName - Timeframe identifier
1870
- * @param backtest - True for backtest mode, false for live trading
1871
- */
1872
- onPartialProfitAvailable(event: PartialProfitContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1873
- /**
1874
- * Called when partial loss level is reached (-10%, -20%, -30%, etc).
1875
- *
1876
- * Triggered by: PartialConnectionService via partialLossSubject
1877
- * Frequency: Once per loss level per signal (deduplicated)
1878
- *
1879
- * @param event - Loss milestone data with level and price
1880
- * @param actionName - Action identifier
1881
- * @param strategyName - Strategy identifier
1882
- * @param frameName - Timeframe identifier
1883
- * @param backtest - True for backtest mode, false for live trading
1884
- */
1885
- onPartialLossAvailable(event: PartialLossContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1886
- /**
1887
- * Called during scheduled signal monitoring (every minute while waiting for activation).
1888
- *
1889
- * Triggered by: StrategyConnectionService via schedulePingSubject
1890
- * Frequency: Every minute while scheduled signal is waiting
1891
- *
1892
- * @param event - Scheduled signal monitoring data
1893
- * @param actionName - Action identifier
1894
- * @param strategyName - Strategy identifier
1895
- * @param frameName - Timeframe identifier
1896
- * @param backtest - True for backtest mode, false for live trading
1897
- */
1898
- onPingScheduled(event: SchedulePingContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1899
- /**
1900
- * Called during active pending signal monitoring (every minute while position is active).
1901
- *
1902
- * Triggered by: StrategyConnectionService via activePingSubject
1903
- * Frequency: Every minute while pending signal is active
1904
- *
1905
- * @param event - Active pending signal monitoring data
1906
- * @param actionName - Action identifier
1907
- * @param strategyName - Strategy identifier
1908
- * @param frameName - Timeframe identifier
1909
- * @param backtest - True for backtest mode, false for live trading
1910
- */
1911
- onPingActive(event: ActivePingContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1912
- /**
1913
- * Called when signal is rejected by risk management.
1914
- *
1915
- * Triggered by: RiskConnectionService via riskSubject
1916
- * Frequency: Only when signal fails risk validation (not emitted for allowed signals)
1917
- *
1918
- * @param event - Risk rejection data with reason and context
1919
- * @param actionName - Action identifier
1920
- * @param strategyName - Strategy identifier
1921
- * @param frameName - Timeframe identifier
1922
- * @param backtest - True for backtest mode, false for live trading
1923
- */
1924
- onRiskRejection(event: RiskContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
1925
- }
1926
- /**
1927
- * Action schema registered via addActionSchema().
1928
- * Defines event handler implementation and lifecycle callbacks for state management integration.
1929
- *
1930
- * Actions provide a way to attach custom event handlers to strategies for:
1931
- * - State management (Redux, Zustand, MobX)
1932
- * - Event logging and monitoring
1933
- * - Real-time notifications (Telegram, Discord, email)
1934
- * - Analytics and metrics collection
1935
- * - Custom business logic triggers
1936
- *
1937
- * Each action instance is created per strategy-frame pair and receives all events
1938
- * emitted during strategy execution. Multiple actions can be attached to a single strategy.
1939
- *
1940
- * @example
1941
- * ```typescript
1942
- * import { addActionSchema } from "backtest-kit";
1943
- *
1944
- * // Define action handler class
1945
- * class TelegramNotifier implements Partial<IAction> {
1946
- * constructor(
1947
- * private strategyName: StrategyName,
1948
- * private frameName: FrameName,
1949
- * private backtest: boolean
1950
- * ) {}
1951
- *
1952
- * signal(event: IStrategyTickResult): void {
1953
- * if (!this.backtest && event.action === 'opened') {
1954
- * telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
1955
- * }
1956
- * }
1957
- *
1958
- * dispose(): void {
1959
- * telegram.close();
1960
- * }
1961
- * }
1962
- *
1963
- * // Register action schema
1964
- * addActionSchema({
1965
- * actionName: "telegram-notifier",
1966
- * handler: TelegramNotifier,
1967
- * callbacks: {
1968
- * onInit: async (strategyName, frameName, backtest) => {
1969
- * console.log(`Telegram notifier initialized for ${strategyName}/${frameName}`);
1970
- * },
1971
- * onSignal: (event, strategyName, frameName, backtest) => {
1972
- * console.log(`Signal event: ${event.action}`);
1973
- * }
1974
- * }
1975
- * });
1976
- * ```
1977
- */
1978
- interface IActionSchema {
1979
- /** Unique action identifier for registration */
1980
- actionName: ActionName;
1981
- /** Optional developer note for documentation */
1982
- note?: string;
1983
- /** Action handler constructor (instantiated per strategy-frame pair) */
1984
- handler: TActionCtor | Partial<IPublicAction>;
1985
- /** Optional lifecycle and event callbacks */
1986
- callbacks?: Partial<IActionCallbacks>;
1987
- }
1988
- /**
1989
- * Public action interface for custom action handler implementations.
1990
- *
1991
- * Extends IAction with an initialization lifecycle method.
1992
- * Action handlers implement this interface to receive strategy events and perform custom logic.
1993
- *
1994
- * Lifecycle:
1995
- * 1. Constructor called with (strategyName, frameName, actionName)
1996
- * 2. init() called once for async initialization (setup connections, load resources)
1997
- * 3. Event methods called as strategy executes (signal, breakeven, partialProfit, etc.)
1998
- * 4. dispose() called once for cleanup (close connections, flush buffers)
1999
- *
2000
- * Key features:
2001
- * - init() for async initialization (database connections, API clients, file handles)
2002
- * - All IAction methods available for event handling
2003
- * - dispose() guaranteed to run exactly once via singleshot pattern
2004
- *
2005
- * Common use cases:
2006
- * - State management: Redux/Zustand store integration
2007
- * - Notifications: Telegram/Discord/Email alerts
2008
- * - Logging: Custom event tracking and monitoring
2009
- * - Analytics: Metrics collection and reporting
2010
- * - External systems: Database writes, API calls, file operations
2011
- *
2012
- * @example
2013
- * ```typescript
2014
- * class TelegramNotifier implements Partial<IPublicAction> {
2015
- * private bot: TelegramBot | null = null;
2016
- *
2017
- * constructor(
2018
- * private strategyName: string,
2019
- * private frameName: string,
2020
- * private actionName: string
2021
- * ) {}
2022
- *
2023
- * // Called once during initialization
2024
- * async init() {
2025
- * this.bot = new TelegramBot(process.env.TELEGRAM_TOKEN);
2026
- * await this.bot.connect();
2027
- * }
2028
- *
2029
- * // Called on every signal event
2030
- * async signal(event: IStrategyTickResult) {
2031
- * if (event.action === 'opened') {
2032
- * await this.bot.send(
2033
- * `[${this.strategyName}/${this.frameName}] Signal opened: ${event.signal.side}`
2034
- * );
2035
- * }
2036
- * }
2037
- *
2038
- * // Called once during cleanup
2039
- * async dispose() {
2040
- * await this.bot?.disconnect();
2041
- * this.bot = null;
2042
- * }
2043
- * }
2044
- * ```
2045
- *
2046
- * @see IAction for all available event methods
2047
- * @see TActionCtor for constructor signature requirements
2048
- * @see ClientAction for internal wrapper that manages lifecycle
2049
- */
2050
- type IPublicAction = {
2051
- [key in keyof IAction]?: IAction[key];
2052
- } & {
2053
- init?(): void | Promise<void>;
2054
- };
2055
- /**
2056
- * Action interface for state manager integration.
2057
- *
2058
- * Provides methods to handle all events emitted by connection services.
2059
- * Each method corresponds to a specific event type emitted via .next() calls.
2060
- *
2061
- * Use this interface to implement custom state management logic:
2062
- * - Redux/Zustand action dispatchers
2063
- * - Event logging systems
2064
- * - Real-time monitoring dashboards
2065
- * - Analytics and metrics collection
2066
- *
2067
- * @example
2068
- * ```typescript
2069
- * class ReduxStateManager implements IAction {
2070
- * constructor(private store: Store) {}
2071
- *
2072
- * signal(event: IStrategyTickResult): void {
2073
- * this.store.dispatch({ type: 'SIGNAL', payload: event });
2074
- * }
2075
- *
2076
- * breakeven(event: BreakevenContract): void {
2077
- * this.store.dispatch({ type: 'BREAKEVEN', payload: event });
2078
- * }
2079
- *
2080
- * // ... implement other methods
2081
- * }
2082
- * ```
2083
- */
2084
- interface IAction {
2085
- /**
2086
- * Handles signal events from all modes (live + backtest).
2087
- *
2088
- * Emitted by: StrategyConnectionService via signalEmitter
2089
- * Source: StrategyConnectionService.tick() and StrategyConnectionService.backtest()
2090
- * Frequency: Every tick/candle when strategy is evaluated
2091
- *
2092
- * @param event - Signal state result (idle, scheduled, opened, active, closed, cancelled)
2093
- */
2094
- signal(event: IStrategyTickResult): void | Promise<void>;
2095
- /**
2096
- * Handles signal events from live trading only.
2097
- *
2098
- * Emitted by: StrategyConnectionService via signalLiveEmitter
2099
- * Source: StrategyConnectionService.tick() when backtest=false
2100
- * Frequency: Every tick in live mode
2101
- *
2102
- * @param event - Signal state result from live trading
2103
- */
2104
- signalLive(event: IStrategyTickResult): void | Promise<void>;
2105
- /**
2106
- * Handles signal events from backtest only.
2107
- *
2108
- * Emitted by: StrategyConnectionService via signalBacktestEmitter
2109
- * Source: StrategyConnectionService.backtest() when backtest=true
2110
- * Frequency: Every candle in backtest mode
2111
- *
2112
- * @param event - Signal state result from backtest
2113
- */
2114
- signalBacktest(event: IStrategyTickResult): void | Promise<void>;
2115
- /**
2116
- * Handles breakeven events when stop-loss is moved to entry price.
2117
- *
2118
- * Emitted by: BreakevenConnectionService via breakevenSubject
2119
- * Source: COMMIT_BREAKEVEN_FN callback in BreakevenConnectionService
2120
- * Frequency: Once per signal when breakeven threshold is reached
2121
- *
2122
- * @param event - Breakeven milestone data
2123
- */
2124
- breakevenAvailable(event: BreakevenContract): void | Promise<void>;
2125
- /**
2126
- * Handles partial profit level events (10%, 20%, 30%, etc).
2127
- *
2128
- * Emitted by: PartialConnectionService via partialProfitSubject
2129
- * Source: COMMIT_PROFIT_FN callback in PartialConnectionService
2130
- * Frequency: Once per profit level per signal (deduplicated)
2131
- *
2132
- * @param event - Profit milestone data with level and price
2133
- */
2134
- partialProfitAvailable(event: PartialProfitContract): void | Promise<void>;
2135
- /**
2136
- * Handles partial loss level events (-10%, -20%, -30%, etc).
2137
- *
2138
- * Emitted by: PartialConnectionService via partialLossSubject
2139
- * Source: COMMIT_LOSS_FN callback in PartialConnectionService
2140
- * Frequency: Once per loss level per signal (deduplicated)
2141
- *
2142
- * @param event - Loss milestone data with level and price
2143
- */
2144
- partialLossAvailable(event: PartialLossContract): void | Promise<void>;
2145
- /**
2146
- * Handles scheduled ping events during scheduled signal monitoring.
2147
- *
2148
- * Emitted by: StrategyConnectionService via schedulePingSubject
2149
- * Source: CREATE_COMMIT_SCHEDULE_PING_FN callback in StrategyConnectionService
2150
- * Frequency: Every minute while scheduled signal is waiting for activation
2151
- *
2152
- * @param event - Scheduled signal monitoring data
2153
- */
2154
- pingScheduled(event: SchedulePingContract): void | Promise<void>;
2155
- /**
2156
- * Handles active ping events during active pending signal monitoring.
2157
- *
2158
- * Emitted by: StrategyConnectionService via activePingSubject
2159
- * Source: CREATE_COMMIT_ACTIVE_PING_FN callback in StrategyConnectionService
2160
- * Frequency: Every minute while pending signal is active
2161
- *
2162
- * @param event - Active pending signal monitoring data
2163
- */
2164
- pingActive(event: ActivePingContract): void | Promise<void>;
2165
- /**
2166
- * Handles risk rejection events when signals fail risk validation.
2167
- *
2168
- * Emitted by: RiskConnectionService via riskSubject
2169
- * Source: COMMIT_REJECTION_FN callback in RiskConnectionService
2170
- * Frequency: Only when signal is rejected (not emitted for allowed signals)
2171
- *
2172
- * @param event - Risk rejection data with reason and context
2173
- */
2174
- riskRejection(event: RiskContract): void | Promise<void>;
2175
- /**
2176
- * Cleans up resources and subscriptions when action handler is no longer needed.
2177
- *
2178
- * Called by: Connection services during shutdown
2179
- * Use for: Unsubscribing from observables, closing connections, flushing buffers
2180
- */
2181
- dispose(): void | Promise<void>;
2182
- }
2183
- /**
2184
- * Unique action identifier.
2185
- */
2186
- type ActionName = string;
2187
-
2188
- /**
2189
- * Base fields for all signal commit events.
2190
- */
2191
- interface SignalCommitBase {
2192
- /** Trading pair symbol (e.g., "BTCUSDT") */
2193
- symbol: string;
2194
- /** Strategy name that generated this signal */
2195
- strategyName: StrategyName;
2196
- /** Exchange name where signal was executed */
2197
- exchangeName: ExchangeName;
2198
- /** Timeframe name (used in backtest mode, empty string in live mode) */
2199
- frameName: FrameName;
2200
- /** Whether this event is from backtest mode (true) or live mode (false) */
2201
- backtest: boolean;
2202
- /** Unique signal identifier (UUID v4) */
2203
- signalId: string;
2204
- /** Timestamp from execution context (tick's when or backtest candle timestamp) */
2205
- timestamp: number;
2206
- /**
2207
- * Total number of DCA entries at the time of this event (_entry.length).
2208
- * 1 = no averaging done (only initial entry). 2+ = averaged positions.
2209
- */
2210
- totalEntries: number;
2211
- /** Original entry price at signal creation (unchanged by DCA averaging). */
2212
- originalPriceOpen: number;
2213
- }
2214
- /**
2215
- * Cancel scheduled signal event.
1677
+ * Cancel scheduled signal event.
2216
1678
  */
2217
1679
  interface CancelScheduledCommit extends SignalCommitBase {
2218
1680
  /** Discriminator for cancel-scheduled action */
@@ -3177,305 +2639,847 @@ interface IStrategy {
3177
2639
  *
3178
2640
  * @example
3179
2641
  * ```typescript
3180
- * // Activate scheduled signal without waiting for priceOpen
3181
- * await strategy.activateScheduled("BTCUSDT", false, "user-activate-123");
3182
- * // Scheduled signal becomes pending signal immediately
2642
+ * // Activate scheduled signal without waiting for priceOpen
2643
+ * await strategy.activateScheduled("BTCUSDT", false, "user-activate-123");
2644
+ * // Scheduled signal becomes pending signal immediately
2645
+ * ```
2646
+ */
2647
+ activateScheduled: (symbol: string, backtest: boolean, activateId?: string) => Promise<void>;
2648
+ /**
2649
+ * Closes the pending signal without stopping the strategy.
2650
+ *
2651
+ * Clears the pending signal (active position).
2652
+ * Does NOT affect scheduled signals or strategy operation.
2653
+ * Does NOT set stop flag - strategy can continue generating new signals.
2654
+ *
2655
+ * Use case: Close an active position that is no longer desired without stopping the entire strategy.
2656
+ *
2657
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2658
+ * @param backtest - Whether running in backtest mode
2659
+ * @param closeId - Optional identifier for this close operation
2660
+ * @returns Promise that resolves when pending signal is cleared
2661
+ *
2662
+ * @example
2663
+ * ```typescript
2664
+ * // Close pending signal without stopping strategy
2665
+ * await strategy.closePending("BTCUSDT", false, "user-close-123");
2666
+ * // Strategy continues, can generate new signals
2667
+ * ```
2668
+ */
2669
+ closePending: (symbol: string, backtest: boolean, closeId?: string) => Promise<void>;
2670
+ /**
2671
+ * Executes partial close at profit level (moving toward TP).
2672
+ *
2673
+ * Closes specified percentage of position at current price.
2674
+ * Updates _tpClosed, _totalClosed, and _partialHistory state.
2675
+ * Persists updated signal state for crash recovery.
2676
+ *
2677
+ * Validations:
2678
+ * - Throws if no pending signal exists
2679
+ * - Throws if called on scheduled signal (not yet activated)
2680
+ * - Throws if percentToClose <= 0 or > 100
2681
+ * - Returns false if _totalClosed + percentToClose > 100 (prevents over-closing)
2682
+ *
2683
+ * Use case: User-controlled partial close triggered from onPartialProfit callback.
2684
+ *
2685
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2686
+ * @param percentToClose - Absolute percentage of position to close (0-100)
2687
+ * @param currentPrice - Current market price for partial close
2688
+ * @param backtest - Whether running in backtest mode
2689
+ * @returns Promise<boolean> - true if partial close executed, false if skipped
2690
+ *
2691
+ * @example
2692
+ * ```typescript
2693
+ * callbacks: {
2694
+ * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
2695
+ * if (percentTp >= 50) {
2696
+ * const success = await strategy.partialProfit(symbol, 25, currentPrice, backtest);
2697
+ * if (success) {
2698
+ * console.log('Partial profit executed');
2699
+ * }
2700
+ * }
2701
+ * }
2702
+ * }
2703
+ * ```
2704
+ */
2705
+ partialProfit: (symbol: string, percentToClose: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
2706
+ /**
2707
+ * Executes partial close at loss level (moving toward SL).
2708
+ *
2709
+ * Closes specified percentage of position at current price.
2710
+ * Updates _slClosed, _totalClosed, and _partialHistory state.
2711
+ * Persists updated signal state for crash recovery.
2712
+ *
2713
+ * Validations:
2714
+ * - Throws if no pending signal exists
2715
+ * - Throws if called on scheduled signal (not yet activated)
2716
+ * - Throws if percentToClose <= 0 or > 100
2717
+ * - Returns false if _totalClosed + percentToClose > 100 (prevents over-closing)
2718
+ *
2719
+ * Use case: User-controlled partial close triggered from onPartialLoss callback.
2720
+ *
2721
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2722
+ * @param percentToClose - Absolute percentage of position to close (0-100)
2723
+ * @param currentPrice - Current market price for partial close
2724
+ * @param backtest - Whether running in backtest mode
2725
+ * @returns Promise<boolean> - true if partial close executed, false if skipped
2726
+ *
2727
+ * @example
2728
+ * ```typescript
2729
+ * callbacks: {
2730
+ * onPartialLoss: async (symbol, signal, currentPrice, percentSl, backtest) => {
2731
+ * if (percentSl >= 80) {
2732
+ * const success = await strategy.partialLoss(symbol, 50, currentPrice, backtest);
2733
+ * if (success) {
2734
+ * console.log('Partial loss executed');
2735
+ * }
2736
+ * }
2737
+ * }
2738
+ * }
2739
+ * ```
2740
+ */
2741
+ partialLoss: (symbol: string, percentToClose: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
2742
+ /**
2743
+ * Adjusts trailing stop-loss by shifting distance between entry and original SL.
2744
+ *
2745
+ * CRITICAL: Always calculates from ORIGINAL SL, not from current trailing SL.
2746
+ * This prevents error accumulation on repeated calls.
2747
+ * Larger percentShift ABSORBS smaller one (updates only towards better protection).
2748
+ *
2749
+ * Calculates new SL based on percentage shift of the ORIGINAL distance (entry - originalSL):
2750
+ * - Negative %: tightens stop (moves SL closer to entry, reduces risk)
2751
+ * - Positive %: loosens stop (moves SL away from entry, allows more drawdown)
2752
+ *
2753
+ * For LONG position (entry=100, originalSL=90, distance=10%):
2754
+ * - percentShift = -50: newSL = 100 - 10%*(1-0.5) = 95 (5% distance, tighter)
2755
+ * - percentShift = +20: newSL = 100 - 10%*(1+0.2) = 88 (12% distance, looser)
2756
+ *
2757
+ * For SHORT position (entry=100, originalSL=110, distance=10%):
2758
+ * - percentShift = -50: newSL = 100 + 10%*(1-0.5) = 105 (5% distance, tighter)
2759
+ * - percentShift = +20: newSL = 100 + 10%*(1+0.2) = 112 (12% distance, looser)
2760
+ *
2761
+ * Absorption behavior:
2762
+ * - First call: sets trailing SL unconditionally
2763
+ * - Subsequent calls: updates only if new SL is BETTER (protects more profit)
2764
+ * - For LONG: only accepts HIGHER SL (never moves down, closer to entry wins)
2765
+ * - For SHORT: only accepts LOWER SL (never moves up, closer to entry wins)
2766
+ * - Stores in _trailingPriceStopLoss, original priceStopLoss always preserved
2767
+ *
2768
+ * Validations:
2769
+ * - Throws if no pending signal exists
2770
+ * - Throws if percentShift < -100 or > 100
2771
+ * - Throws if percentShift === 0
2772
+ * - Skips if new SL would cross entry price
2773
+ * - Skips if currentPrice already crossed new SL level (price intrusion protection)
2774
+ *
2775
+ * Use case: User-controlled trailing stop triggered from onPartialProfit callback.
2776
+ *
2777
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2778
+ * @param percentShift - Percentage shift of ORIGINAL SL distance [-100, 100], excluding 0
2779
+ * @param currentPrice - Current market price to check for intrusion
2780
+ * @param backtest - Whether running in backtest mode
2781
+ * @returns Promise<boolean> - true if trailing SL was set/updated, false if rejected
2782
+ *
2783
+ * @example
2784
+ * ```typescript
2785
+ * callbacks: {
2786
+ * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
2787
+ * if (percentTp >= 50) {
2788
+ * // LONG: entry=100, originalSL=90, distance=10%
2789
+ *
2790
+ * // First call: tighten by 5%
2791
+ * const success1 = await strategy.trailingStop(symbol, -5, currentPrice, backtest);
2792
+ * // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
2793
+ *
2794
+ * // Second call: try weaker protection
2795
+ * const success2 = await strategy.trailingStop(symbol, -3, currentPrice, backtest);
2796
+ * // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
2797
+ *
2798
+ * // Third call: stronger protection
2799
+ * const success3 = await strategy.trailingStop(symbol, -7, currentPrice, backtest);
2800
+ * // success3 = true (ACCEPTED: newDistance = 3%, newSL = 97 > 95, better protection)
2801
+ * }
2802
+ * }
2803
+ * }
2804
+ * ```
2805
+ */
2806
+ trailingStop: (symbol: string, percentShift: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
2807
+ /**
2808
+ * Adjusts the trailing take-profit distance for an active pending signal.
2809
+ *
2810
+ * CRITICAL: Always calculates from ORIGINAL TP, not from current trailing TP.
2811
+ * This prevents error accumulation on repeated calls.
2812
+ * Larger percentShift ABSORBS smaller one (updates only towards more conservative TP).
2813
+ *
2814
+ * Updates the take-profit distance by a percentage adjustment relative to the ORIGINAL TP distance.
2815
+ * Negative percentShift brings TP closer to entry (more conservative).
2816
+ * Positive percentShift moves TP further from entry (more aggressive).
2817
+ *
2818
+ * Absorption behavior:
2819
+ * - First call: sets trailing TP unconditionally
2820
+ * - Subsequent calls: updates only if new TP is MORE CONSERVATIVE (closer to entry)
2821
+ * - For LONG: only accepts LOWER TP (never moves up, closer to entry wins)
2822
+ * - For SHORT: only accepts HIGHER TP (never moves down, closer to entry wins)
2823
+ * - Stores in _trailingPriceTakeProfit, original priceTakeProfit always preserved
2824
+ *
2825
+ * Price intrusion protection: If current price has already crossed the new TP level,
2826
+ * the update is skipped to prevent immediate TP triggering.
2827
+ *
2828
+ * @param symbol - Trading pair symbol
2829
+ * @param percentShift - Percentage adjustment to ORIGINAL TP distance (-100 to 100)
2830
+ * @param currentPrice - Current market price to check for intrusion
2831
+ * @param backtest - Whether running in backtest mode
2832
+ * @returns Promise<boolean> - true if trailing TP was set/updated, false if rejected
2833
+ *
2834
+ * @example
2835
+ * ```typescript
2836
+ * callbacks: {
2837
+ * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
2838
+ * // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
2839
+ *
2840
+ * // First call: bring TP closer by 3%
2841
+ * const success1 = await strategy.trailingTake(symbol, -3, currentPrice, backtest);
2842
+ * // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
2843
+ *
2844
+ * // Second call: try to move TP further (less conservative)
2845
+ * const success2 = await strategy.trailingTake(symbol, 2, currentPrice, backtest);
2846
+ * // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
2847
+ *
2848
+ * // Third call: even more conservative
2849
+ * const success3 = await strategy.trailingTake(symbol, -5, currentPrice, backtest);
2850
+ * // success3 = true (ACCEPTED: newDistance = 5%, newTP = 105 < 107, more conservative)
2851
+ * }
2852
+ * }
2853
+ * ```
2854
+ */
2855
+ trailingTake: (symbol: string, percentShift: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
2856
+ /**
2857
+ * Moves stop-loss to breakeven (entry price) when price reaches threshold.
2858
+ *
2859
+ * Moves SL to entry price (zero-risk position) when current price has moved
2860
+ * far enough in profit direction to cover transaction costs (slippage + fees).
2861
+ * Threshold is calculated as: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2
2862
+ *
2863
+ * Behavior:
2864
+ * - Returns true if SL was moved to breakeven
2865
+ * - Returns false if conditions not met (threshold not reached or already at breakeven)
2866
+ * - Uses _trailingPriceStopLoss to store breakeven SL (preserves original priceStopLoss)
2867
+ * - Only moves SL once per position (idempotent - safe to call multiple times)
2868
+ *
2869
+ * For LONG position (entry=100, slippage=0.1%, fee=0.1%):
2870
+ * - Threshold: (0.1 + 0.1) * 2 = 0.4%
2871
+ * - Breakeven available when price >= 100.4 (entry + 0.4%)
2872
+ * - Moves SL from original (e.g. 95) to 100 (breakeven)
2873
+ * - Returns true on first successful move, false on subsequent calls
2874
+ *
2875
+ * For SHORT position (entry=100, slippage=0.1%, fee=0.1%):
2876
+ * - Threshold: (0.1 + 0.1) * 2 = 0.4%
2877
+ * - Breakeven available when price <= 99.6 (entry - 0.4%)
2878
+ * - Moves SL from original (e.g. 105) to 100 (breakeven)
2879
+ * - Returns true on first successful move, false on subsequent calls
2880
+ *
2881
+ * Validations:
2882
+ * - Throws if no pending signal exists
2883
+ * - Throws if currentPrice is not a positive finite number
2884
+ *
2885
+ * Use case: User-controlled breakeven protection triggered from onPartialProfit callback.
2886
+ *
2887
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2888
+ * @param currentPrice - Current market price to check threshold
2889
+ * @param backtest - Whether running in backtest mode
2890
+ * @returns Promise<boolean> - true if breakeven was set, false if conditions not met
2891
+ *
2892
+ * @example
2893
+ * ```typescript
2894
+ * callbacks: {
2895
+ * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
2896
+ * // Try to move SL to breakeven when threshold reached
2897
+ * const movedToBreakeven = await strategy.breakeven(symbol, currentPrice, backtest);
2898
+ * if (movedToBreakeven) {
2899
+ * console.log(`Position moved to breakeven at ${currentPrice}`);
2900
+ * }
2901
+ * }
2902
+ * }
3183
2903
  * ```
3184
2904
  */
3185
- activateScheduled: (symbol: string, backtest: boolean, activateId?: string) => Promise<void>;
2905
+ breakeven: (symbol: string, currentPrice: number, backtest: boolean) => Promise<boolean>;
3186
2906
  /**
3187
- * Closes the pending signal without stopping the strategy.
2907
+ * Adds a new averaging entry to an open position (DCA — Dollar Cost Averaging).
3188
2908
  *
3189
- * Clears the pending signal (active position).
3190
- * Does NOT affect scheduled signals or strategy operation.
3191
- * Does NOT set stop flag - strategy can continue generating new signals.
2909
+ * Appends currentPrice to the _entry array. The effective entry price used in all
2910
+ * distance and PNL calculations becomes the simple arithmetic mean of all _entry prices.
2911
+ * Original priceOpen is preserved unchanged for identity/audit purposes.
3192
2912
  *
3193
- * Use case: Close an active position that is no longer desired without stopping the entire strategy.
2913
+ * Rejection rules (returns false without throwing):
2914
+ * - LONG: currentPrice >= last entry price (must average down, not up or equal)
2915
+ * - SHORT: currentPrice <= last entry price (must average down, not up or equal)
2916
+ *
2917
+ * Validations (throws):
2918
+ * - No pending signal exists
2919
+ * - currentPrice is not a positive finite number
3194
2920
  *
3195
2921
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2922
+ * @param currentPrice - New entry price to add to the averaging history
3196
2923
  * @param backtest - Whether running in backtest mode
3197
- * @param closeId - Optional identifier for this close operation
3198
- * @returns Promise that resolves when pending signal is cleared
2924
+ * @returns Promise<boolean> - true if entry added, false if rejected by direction check
2925
+ */
2926
+ averageBuy: (symbol: string, currentPrice: number, backtest: boolean) => Promise<boolean>;
2927
+ /**
2928
+ * Disposes the strategy instance and cleans up resources.
3199
2929
  *
3200
- * @example
3201
- * ```typescript
3202
- * // Close pending signal without stopping strategy
3203
- * await strategy.closePending("BTCUSDT", false, "user-close-123");
3204
- * // Strategy continues, can generate new signals
3205
- * ```
2930
+ * Called when the strategy is being removed from cache or shut down.
2931
+ * Invokes the onDispose callback to notify external systems.
2932
+ *
2933
+ * @returns Promise that resolves when disposal is complete
3206
2934
  */
3207
- closePending: (symbol: string, backtest: boolean, closeId?: string) => Promise<void>;
2935
+ dispose: () => Promise<void>;
2936
+ }
2937
+ /**
2938
+ * Unique strategy identifier.
2939
+ */
2940
+ type StrategyName = string;
2941
+
2942
+ /**
2943
+ * Method context containing schema names for operation routing.
2944
+ *
2945
+ * Propagated through MethodContextService to provide implicit context
2946
+ * for retrieving correct strategy/exchange/frame instances.
2947
+ */
2948
+ interface IMethodContext {
2949
+ /** Name of exchange schema to use */
2950
+ exchangeName: ExchangeName;
2951
+ /** Name of strategy schema to use */
2952
+ strategyName: StrategyName;
2953
+ /** Name of frame schema to use (empty string for live mode) */
2954
+ frameName: FrameName;
2955
+ }
2956
+ /**
2957
+ * Scoped service for method context propagation.
2958
+ *
2959
+ * Uses di-scoped for implicit context passing without explicit parameters.
2960
+ * Context includes strategyName, exchangeName, and frameName.
2961
+ *
2962
+ * Used by PublicServices to inject schema names into ConnectionServices.
2963
+ *
2964
+ * @example
2965
+ * ```typescript
2966
+ * MethodContextService.runAsyncIterator(
2967
+ * backtestGenerator,
2968
+ * {
2969
+ * strategyName: "my-strategy",
2970
+ * exchangeName: "my-exchange",
2971
+ * frameName: "1d-backtest"
2972
+ * }
2973
+ * );
2974
+ * ```
2975
+ */
2976
+ declare const MethodContextService: (new () => {
2977
+ readonly context: IMethodContext;
2978
+ }) & Omit<{
2979
+ new (context: IMethodContext): {
2980
+ readonly context: IMethodContext;
2981
+ };
2982
+ }, "prototype"> & di_scoped.IScopedClassRun<[context: IMethodContext]>;
2983
+
2984
+ /**
2985
+ * Single log entry stored in the log history.
2986
+ */
2987
+ interface ILogEntry {
2988
+ /** Unique entry identifier generated via randomString */
2989
+ id: string;
2990
+ /** Log level */
2991
+ type: "log" | "debug" | "info" | "warn";
2992
+ /** Unix timestamp in milliseconds when the entry was created */
2993
+ timestamp: number;
2994
+ /** Date taken from backtest context to improve user experience */
2995
+ createdAt: string;
2996
+ /** Optional method context associated with the log entry, providing additional details about the execution environment or state when the log was recorded */
2997
+ methodContext: IMethodContext | null;
2998
+ /** Optional execution context associated with the log entry, providing additional details about the execution environment or state when the log was recorded */
2999
+ executionContext: IExecutionContext | null;
3000
+ /** Log topic / method name */
3001
+ topic: string;
3002
+ /** Additional arguments passed to the log call */
3003
+ args: unknown[];
3004
+ }
3005
+ /**
3006
+ * Interface representing a logging mechanism for the swarm system.
3007
+ * Provides methods to record messages at different severity levels, used across components like agents, sessions, states, storage, swarms, history, embeddings, completions, and policies.
3008
+ * Logs are utilized to track lifecycle events (e.g., initialization, disposal), operational details (e.g., tool calls, message emissions), validation outcomes (e.g., policy checks), and errors (e.g., persistence failures), aiding in debugging, monitoring, and auditing.
3009
+ */
3010
+ interface ILogger {
3011
+ /**
3012
+ * Logs a general-purpose message.
3013
+ * Used throughout the swarm system to record significant events or state changes, such as agent execution, session connections, or storage updates.
3014
+ */
3015
+ log(topic: string, ...args: any[]): void;
3016
+ /**
3017
+ * Logs a debug-level message.
3018
+ * Employed for detailed diagnostic information, such as intermediate states during agent tool calls, swarm navigation changes, or embedding creation processes, typically enabled in development or troubleshooting scenarios.
3019
+ */
3020
+ debug(topic: string, ...args: any[]): void;
3021
+ /**
3022
+ * Logs an info-level message.
3023
+ * 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.
3024
+ */
3025
+ info(topic: string, ...args: any[]): void;
3026
+ /**
3027
+ * Logs a warning-level message.
3028
+ * Used to record potentially problematic situations that don't prevent execution but may require attention, such as missing data, unexpected conditions, or deprecated usage.
3029
+ */
3030
+ warn(topic: string, ...args: any[]): void;
3031
+ }
3032
+
3033
+ /**
3034
+ * Candle time interval for fetching historical data.
3035
+ */
3036
+ type CandleInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h";
3037
+ /** Numeric type that can be undefined (used for optional numeric values) */
3038
+ type Num = number | undefined;
3039
+ interface IPublicCandleData {
3040
+ /** Unix timestamp in milliseconds when candle opened */
3041
+ timestamp: Num;
3042
+ /** Opening price at candle start */
3043
+ open: Num;
3044
+ /** Highest price during candle period */
3045
+ high: Num;
3046
+ /** Lowest price during candle period */
3047
+ low: Num;
3048
+ /** Closing price at candle end */
3049
+ close: Num;
3050
+ /** Trading volume during candle period */
3051
+ volume: Num;
3052
+ }
3053
+ /**
3054
+ * Single OHLCV candle data point.
3055
+ * Used for VWAP calculation and backtesting.
3056
+ */
3057
+ interface ICandleData {
3058
+ /** Unix timestamp in milliseconds when candle opened */
3059
+ timestamp: number;
3060
+ /** Opening price at candle start */
3061
+ open: number;
3062
+ /** Highest price during candle period */
3063
+ high: number;
3064
+ /** Lowest price during candle period */
3065
+ low: number;
3066
+ /** Closing price at candle end */
3067
+ close: number;
3068
+ /** Trading volume during candle period */
3069
+ volume: number;
3070
+ }
3071
+ /**
3072
+ * Single bid or ask in order book.
3073
+ */
3074
+ interface IBidData {
3075
+ /** Price level as string */
3076
+ price: string;
3077
+ /** Quantity at this price level as string */
3078
+ quantity: string;
3079
+ }
3080
+ /**
3081
+ * Order book data containing bids and asks.
3082
+ */
3083
+ interface IOrderBookData {
3084
+ /** Trading pair symbol */
3085
+ symbol: string;
3086
+ /** Array of bid orders (buy orders) */
3087
+ bids: IBidData[];
3088
+ /** Array of ask orders (sell orders) */
3089
+ asks: IBidData[];
3090
+ }
3091
+ /**
3092
+ * Aggregated trade data point.
3093
+ * Represents a single trade that has occurred, used for detailed analysis and backtesting.
3094
+ * Includes price, quantity, timestamp, and whether the buyer is the market maker (which can indicate trade direction).
3095
+ *
3096
+ */
3097
+ interface IAggregatedTradeData {
3098
+ /** Unique identifier for the aggregated trade */
3099
+ id: string;
3100
+ /** Price at which the trade occurred */
3101
+ price: number;
3102
+ /** Quantity traded */
3103
+ qty: number;
3104
+ /** Unix timestamp in milliseconds when the trade occurred */
3105
+ timestamp: number;
3106
+ /** Whether the buyer is the market maker (true if buyer is maker, false if seller is maker) */
3107
+ isBuyerMaker: boolean;
3108
+ }
3109
+ /**
3110
+ * Exchange parameters passed to ClientExchange constructor.
3111
+ * Combines schema with runtime dependencies.
3112
+ * Note: All exchange methods are required in params (defaults are applied during initialization).
3113
+ */
3114
+ interface IExchangeParams extends IExchangeSchema {
3115
+ /** Logger service for debug output */
3116
+ logger: ILogger;
3117
+ /** Execution context service (symbol, when, backtest flag) */
3118
+ execution: TExecutionContextService;
3119
+ /** Fetch candles from data source (required, defaults applied) */
3120
+ getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number, backtest: boolean) => Promise<ICandleData[]>;
3121
+ /** Format quantity according to exchange precision rules (required, defaults applied) */
3122
+ formatQuantity: (symbol: string, quantity: number, backtest: boolean) => Promise<string>;
3123
+ /** Format price according to exchange precision rules (required, defaults applied) */
3124
+ formatPrice: (symbol: string, price: number, backtest: boolean) => Promise<string>;
3125
+ /** Fetch order book for a trading pair (required, defaults applied) */
3126
+ getOrderBook: (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => Promise<IOrderBookData>;
3127
+ /** Fetch aggregated trades for a trading pair (required, defaults applied) */
3128
+ getAggregatedTrades: (symbol: string, from: Date, to: Date, backtest: boolean) => Promise<IAggregatedTradeData[]>;
3129
+ }
3130
+ /**
3131
+ * Optional callbacks for exchange data events.
3132
+ */
3133
+ interface IExchangeCallbacks {
3134
+ /** Called when candle data is fetched */
3135
+ onCandleData: (symbol: string, interval: CandleInterval, since: Date, limit: number, data: ICandleData[]) => void | Promise<void>;
3136
+ }
3137
+ /**
3138
+ * Exchange schema registered via addExchange().
3139
+ * Defines candle data source and formatting logic.
3140
+ */
3141
+ interface IExchangeSchema {
3142
+ /** Unique exchange identifier for registration */
3143
+ exchangeName: ExchangeName;
3144
+ /** Optional developer note for documentation */
3145
+ note?: string;
3208
3146
  /**
3209
- * Executes partial close at profit level (moving toward TP).
3210
- *
3211
- * Closes specified percentage of position at current price.
3212
- * Updates _tpClosed, _totalClosed, and _partialHistory state.
3213
- * Persists updated signal state for crash recovery.
3214
- *
3215
- * Validations:
3216
- * - Throws if no pending signal exists
3217
- * - Throws if called on scheduled signal (not yet activated)
3218
- * - Throws if percentToClose <= 0 or > 100
3219
- * - Returns false if _totalClosed + percentToClose > 100 (prevents over-closing)
3220
- *
3221
- * Use case: User-controlled partial close triggered from onPartialProfit callback.
3147
+ * Fetch candles from data source (API or database).
3222
3148
  *
3223
3149
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3224
- * @param percentToClose - Absolute percentage of position to close (0-100)
3225
- * @param currentPrice - Current market price for partial close
3150
+ * @param interval - Candle time interval (e.g., "1m", "1h")
3151
+ * @param since - Start date for candle fetching
3152
+ * @param limit - Maximum number of candles to fetch
3226
3153
  * @param backtest - Whether running in backtest mode
3227
- * @returns Promise<boolean> - true if partial close executed, false if skipped
3228
- *
3229
- * @example
3230
- * ```typescript
3231
- * callbacks: {
3232
- * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
3233
- * if (percentTp >= 50) {
3234
- * const success = await strategy.partialProfit(symbol, 25, currentPrice, backtest);
3235
- * if (success) {
3236
- * console.log('Partial profit executed');
3237
- * }
3238
- * }
3239
- * }
3240
- * }
3241
- * ```
3154
+ * @returns Promise resolving to array of OHLCV candle data
3242
3155
  */
3243
- partialProfit: (symbol: string, percentToClose: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
3156
+ getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number, backtest: boolean) => Promise<IPublicCandleData[]>;
3244
3157
  /**
3245
- * Executes partial close at loss level (moving toward SL).
3246
- *
3247
- * Closes specified percentage of position at current price.
3248
- * Updates _slClosed, _totalClosed, and _partialHistory state.
3249
- * Persists updated signal state for crash recovery.
3250
- *
3251
- * Validations:
3252
- * - Throws if no pending signal exists
3253
- * - Throws if called on scheduled signal (not yet activated)
3254
- * - Throws if percentToClose <= 0 or > 100
3255
- * - Returns false if _totalClosed + percentToClose > 100 (prevents over-closing)
3158
+ * Format quantity according to exchange precision rules.
3256
3159
  *
3257
- * Use case: User-controlled partial close triggered from onPartialLoss callback.
3160
+ * Optional. If not provided, defaults to Bitcoin precision on Binance (8 decimal places).
3258
3161
  *
3259
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3260
- * @param percentToClose - Absolute percentage of position to close (0-100)
3261
- * @param currentPrice - Current market price for partial close
3162
+ * @param symbol - Trading pair symbol
3163
+ * @param quantity - Raw quantity value
3262
3164
  * @param backtest - Whether running in backtest mode
3263
- * @returns Promise<boolean> - true if partial close executed, false if skipped
3264
- *
3265
- * @example
3266
- * ```typescript
3267
- * callbacks: {
3268
- * onPartialLoss: async (symbol, signal, currentPrice, percentSl, backtest) => {
3269
- * if (percentSl >= 80) {
3270
- * const success = await strategy.partialLoss(symbol, 50, currentPrice, backtest);
3271
- * if (success) {
3272
- * console.log('Partial loss executed');
3273
- * }
3274
- * }
3275
- * }
3276
- * }
3277
- * ```
3165
+ * @returns Promise resolving to formatted quantity string
3278
3166
  */
3279
- partialLoss: (symbol: string, percentToClose: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
3167
+ formatQuantity?: (symbol: string, quantity: number, backtest: boolean) => Promise<string>;
3280
3168
  /**
3281
- * Adjusts trailing stop-loss by shifting distance between entry and original SL.
3282
- *
3283
- * CRITICAL: Always calculates from ORIGINAL SL, not from current trailing SL.
3284
- * This prevents error accumulation on repeated calls.
3285
- * Larger percentShift ABSORBS smaller one (updates only towards better protection).
3286
- *
3287
- * Calculates new SL based on percentage shift of the ORIGINAL distance (entry - originalSL):
3288
- * - Negative %: tightens stop (moves SL closer to entry, reduces risk)
3289
- * - Positive %: loosens stop (moves SL away from entry, allows more drawdown)
3290
- *
3291
- * For LONG position (entry=100, originalSL=90, distance=10%):
3292
- * - percentShift = -50: newSL = 100 - 10%*(1-0.5) = 95 (5% distance, tighter)
3293
- * - percentShift = +20: newSL = 100 - 10%*(1+0.2) = 88 (12% distance, looser)
3294
- *
3295
- * For SHORT position (entry=100, originalSL=110, distance=10%):
3296
- * - percentShift = -50: newSL = 100 + 10%*(1-0.5) = 105 (5% distance, tighter)
3297
- * - percentShift = +20: newSL = 100 + 10%*(1+0.2) = 112 (12% distance, looser)
3169
+ * Format price according to exchange precision rules.
3298
3170
  *
3299
- * Absorption behavior:
3300
- * - First call: sets trailing SL unconditionally
3301
- * - Subsequent calls: updates only if new SL is BETTER (protects more profit)
3302
- * - For LONG: only accepts HIGHER SL (never moves down, closer to entry wins)
3303
- * - For SHORT: only accepts LOWER SL (never moves up, closer to entry wins)
3304
- * - Stores in _trailingPriceStopLoss, original priceStopLoss always preserved
3171
+ * Optional. If not provided, defaults to Bitcoin precision on Binance (2 decimal places).
3305
3172
  *
3306
- * Validations:
3307
- * - Throws if no pending signal exists
3308
- * - Throws if percentShift < -100 or > 100
3309
- * - Throws if percentShift === 0
3310
- * - Skips if new SL would cross entry price
3311
- * - Skips if currentPrice already crossed new SL level (price intrusion protection)
3173
+ * @param symbol - Trading pair symbol
3174
+ * @param price - Raw price value
3175
+ * @param backtest - Whether running in backtest mode
3176
+ * @returns Promise resolving to formatted price string
3177
+ */
3178
+ formatPrice?: (symbol: string, price: number, backtest: boolean) => Promise<string>;
3179
+ /**
3180
+ * Fetch order book for a trading pair.
3312
3181
  *
3313
- * Use case: User-controlled trailing stop triggered from onPartialProfit callback.
3182
+ * Optional. If not provided, throws an error when called.
3314
3183
  *
3315
3184
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3316
- * @param percentShift - Percentage shift of ORIGINAL SL distance [-100, 100], excluding 0
3317
- * @param currentPrice - Current market price to check for intrusion
3185
+ * @param depth - Maximum depth levels for both bids and asks (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
3186
+ * @param from - Start of time range (used in backtest for historical data, can be ignored in live)
3187
+ * @param to - End of time range (used in backtest for historical data, can be ignored in live)
3318
3188
  * @param backtest - Whether running in backtest mode
3319
- * @returns Promise<boolean> - true if trailing SL was set/updated, false if rejected
3189
+ * @returns Promise resolving to order book data
3320
3190
  *
3321
3191
  * @example
3322
3192
  * ```typescript
3323
- * callbacks: {
3324
- * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
3325
- * if (percentTp >= 50) {
3326
- * // LONG: entry=100, originalSL=90, distance=10%
3327
- *
3328
- * // First call: tighten by 5%
3329
- * const success1 = await strategy.trailingStop(symbol, -5, currentPrice, backtest);
3330
- * // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
3331
- *
3332
- * // Second call: try weaker protection
3333
- * const success2 = await strategy.trailingStop(symbol, -3, currentPrice, backtest);
3334
- * // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
3335
- *
3336
- * // Third call: stronger protection
3337
- * const success3 = await strategy.trailingStop(symbol, -7, currentPrice, backtest);
3338
- * // success3 = true (ACCEPTED: newDistance = 3%, newSL = 97 > 95, better protection)
3339
- * }
3193
+ * // Backtest implementation: returns historical order book for the time range
3194
+ * const backtestOrderBook = async (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => {
3195
+ * if (backtest) {
3196
+ * return await database.getOrderBookSnapshot(symbol, depth, from, to);
3340
3197
  * }
3341
- * }
3198
+ * return await exchange.fetchOrderBook(symbol, depth);
3199
+ * };
3200
+ *
3201
+ * // Live implementation: ignores from/to when not in backtest mode
3202
+ * const liveOrderBook = async (symbol: string, depth: number, _from: Date, _to: Date, backtest: boolean) => {
3203
+ * return await exchange.fetchOrderBook(symbol, depth);
3204
+ * };
3342
3205
  * ```
3343
3206
  */
3344
- trailingStop: (symbol: string, percentShift: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
3207
+ getOrderBook?: (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => Promise<IOrderBookData>;
3345
3208
  /**
3346
- * Adjusts the trailing take-profit distance for an active pending signal.
3347
- *
3348
- * CRITICAL: Always calculates from ORIGINAL TP, not from current trailing TP.
3349
- * This prevents error accumulation on repeated calls.
3350
- * Larger percentShift ABSORBS smaller one (updates only towards more conservative TP).
3351
- *
3352
- * Updates the take-profit distance by a percentage adjustment relative to the ORIGINAL TP distance.
3353
- * Negative percentShift brings TP closer to entry (more conservative).
3354
- * Positive percentShift moves TP further from entry (more aggressive).
3355
- *
3356
- * Absorption behavior:
3357
- * - First call: sets trailing TP unconditionally
3358
- * - Subsequent calls: updates only if new TP is MORE CONSERVATIVE (closer to entry)
3359
- * - For LONG: only accepts LOWER TP (never moves up, closer to entry wins)
3360
- * - For SHORT: only accepts HIGHER TP (never moves down, closer to entry wins)
3361
- * - Stores in _trailingPriceTakeProfit, original priceTakeProfit always preserved
3362
- *
3363
- * Price intrusion protection: If current price has already crossed the new TP level,
3364
- * the update is skipped to prevent immediate TP triggering.
3365
- *
3366
- * @param symbol - Trading pair symbol
3367
- * @param percentShift - Percentage adjustment to ORIGINAL TP distance (-100 to 100)
3368
- * @param currentPrice - Current market price to check for intrusion
3209
+ * Fetch aggregated trades for a trading pair.
3210
+ * Optional. If not provided, throws an error when called.
3211
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3212
+ * @param from - Start of time range (used in backtest for historical data, can be ignored in live)
3213
+ * @param to - End of time range (used in backtest for historical data, can be ignored in live)
3369
3214
  * @param backtest - Whether running in backtest mode
3370
- * @returns Promise<boolean> - true if trailing TP was set/updated, false if rejected
3371
- *
3215
+ * @return Promise resolving to array of aggregated trade data
3372
3216
  * @example
3373
3217
  * ```typescript
3374
- * callbacks: {
3375
- * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
3376
- * // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
3377
- *
3378
- * // First call: bring TP closer by 3%
3379
- * const success1 = await strategy.trailingTake(symbol, -3, currentPrice, backtest);
3380
- * // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
3381
- *
3382
- * // Second call: try to move TP further (less conservative)
3383
- * const success2 = await strategy.trailingTake(symbol, 2, currentPrice, backtest);
3384
- * // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
3385
- *
3386
- * // Third call: even more conservative
3387
- * const success3 = await strategy.trailingTake(symbol, -5, currentPrice, backtest);
3388
- * // success3 = true (ACCEPTED: newDistance = 5%, newTP = 105 < 107, more conservative)
3218
+ * // Backtest implementation: returns historical aggregated trades for the time range
3219
+ * const backtestAggregatedTrades = async (symbol: string, from: Date, to: Date, backtest: boolean) => {
3220
+ * if (backtest) {
3221
+ * return await database.getAggregatedTrades(symbol, from, to);
3389
3222
  * }
3390
- * }
3223
+ * return await exchange.fetchAggregatedTrades(symbol);
3224
+ * };
3225
+ *
3226
+ * // Live implementation: ignores from/to when not in backtest mode
3227
+ * const liveAggregatedTrades = async (symbol: string, _from: Date, _to: Date, backtest: boolean) => {
3228
+ * return await exchange.fetchAggregatedTrades(symbol);
3229
+ * };
3391
3230
  * ```
3392
3231
  */
3393
- trailingTake: (symbol: string, percentShift: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
3232
+ getAggregatedTrades?: (symbol: string, from: Date, to: Date, backtest: boolean) => Promise<IAggregatedTradeData[]>;
3233
+ /** Optional lifecycle event callbacks (onCandleData) */
3234
+ callbacks?: Partial<IExchangeCallbacks>;
3235
+ }
3236
+ /**
3237
+ * Exchange interface implemented by ClientExchange.
3238
+ * Provides candle data access and VWAP calculation.
3239
+ */
3240
+ interface IExchange {
3394
3241
  /**
3395
- * Moves stop-loss to breakeven (entry price) when price reaches threshold.
3242
+ * Fetch historical candles backwards from execution context time.
3396
3243
  *
3397
- * Moves SL to entry price (zero-risk position) when current price has moved
3398
- * far enough in profit direction to cover transaction costs (slippage + fees).
3399
- * Threshold is calculated as: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2
3244
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3245
+ * @param interval - Candle time interval (e.g., "1m", "1h")
3246
+ * @param limit - Maximum number of candles to fetch
3247
+ * @returns Promise resolving to array of candle data
3248
+ */
3249
+ getCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
3250
+ /**
3251
+ * Fetch future candles forward from execution context time (for backtest).
3400
3252
  *
3401
- * Behavior:
3402
- * - Returns true if SL was moved to breakeven
3403
- * - Returns false if conditions not met (threshold not reached or already at breakeven)
3404
- * - Uses _trailingPriceStopLoss to store breakeven SL (preserves original priceStopLoss)
3405
- * - Only moves SL once per position (idempotent - safe to call multiple times)
3253
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3254
+ * @param interval - Candle time interval (e.g., "1m", "1h")
3255
+ * @param limit - Maximum number of candles to fetch
3256
+ * @returns Promise resolving to array of candle data
3257
+ */
3258
+ getNextCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
3259
+ /**
3260
+ * Format quantity for exchange precision.
3406
3261
  *
3407
- * For LONG position (entry=100, slippage=0.1%, fee=0.1%):
3408
- * - Threshold: (0.1 + 0.1) * 2 = 0.4%
3409
- * - Breakeven available when price >= 100.4 (entry + 0.4%)
3410
- * - Moves SL from original (e.g. 95) to 100 (breakeven)
3411
- * - Returns true on first successful move, false on subsequent calls
3262
+ * @param symbol - Trading pair symbol
3263
+ * @param quantity - Raw quantity value
3264
+ * @returns Promise resolving to formatted quantity string
3265
+ */
3266
+ formatQuantity: (symbol: string, quantity: number) => Promise<string>;
3267
+ /**
3268
+ * Format price for exchange precision.
3412
3269
  *
3413
- * For SHORT position (entry=100, slippage=0.1%, fee=0.1%):
3414
- * - Threshold: (0.1 + 0.1) * 2 = 0.4%
3415
- * - Breakeven available when price <= 99.6 (entry - 0.4%)
3416
- * - Moves SL from original (e.g. 105) to 100 (breakeven)
3417
- * - Returns true on first successful move, false on subsequent calls
3270
+ * @param symbol - Trading pair symbol
3271
+ * @param price - Raw price value
3272
+ * @returns Promise resolving to formatted price string
3273
+ */
3274
+ formatPrice: (symbol: string, price: number) => Promise<string>;
3275
+ /**
3276
+ * Calculate VWAP from last 5 1-minute candles.
3418
3277
  *
3419
- * Validations:
3420
- * - Throws if no pending signal exists
3421
- * - Throws if currentPrice is not a positive finite number
3278
+ * Formula: VWAP = Σ(Typical Price × Volume) / Σ(Volume)
3279
+ * where Typical Price = (High + Low + Close) / 3
3422
3280
  *
3423
- * Use case: User-controlled breakeven protection triggered from onPartialProfit callback.
3281
+ * @param symbol - Trading pair symbol
3282
+ * @returns Promise resolving to volume-weighted average price
3283
+ */
3284
+ getAveragePrice: (symbol: string) => Promise<number>;
3285
+ /**
3286
+ * Fetch order book for a trading pair.
3424
3287
  *
3425
3288
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3426
- * @param currentPrice - Current market price to check threshold
3427
- * @param backtest - Whether running in backtest mode
3428
- * @returns Promise<boolean> - true if breakeven was set, false if conditions not met
3429
- *
3430
- * @example
3431
- * ```typescript
3432
- * callbacks: {
3433
- * onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
3434
- * // Try to move SL to breakeven when threshold reached
3435
- * const movedToBreakeven = await strategy.breakeven(symbol, currentPrice, backtest);
3436
- * if (movedToBreakeven) {
3437
- * console.log(`Position moved to breakeven at ${currentPrice}`);
3438
- * }
3439
- * }
3440
- * }
3441
- * ```
3289
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
3290
+ * @returns Promise resolving to order book data
3442
3291
  */
3443
- breakeven: (symbol: string, currentPrice: number, backtest: boolean) => Promise<boolean>;
3292
+ getOrderBook: (symbol: string, depth?: number) => Promise<IOrderBookData>;
3444
3293
  /**
3445
- * Adds a new averaging entry to an open position (DCA — Dollar Cost Averaging).
3294
+ * Fetch aggregated trades for a trading pair.
3446
3295
  *
3447
- * Appends currentPrice to the _entry array. The effective entry price used in all
3448
- * distance and PNL calculations becomes the simple arithmetic mean of all _entry prices.
3449
- * Original priceOpen is preserved unchanged for identity/audit purposes.
3296
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3297
+ * @param limit - Optional maximum number of aggregated trades to fetch. If empty returns one hour of data.
3298
+ * @returns Promise resolving to array of aggregated trade data
3299
+ */
3300
+ getAggregatedTrades: (symbol: string, limit?: number) => Promise<IAggregatedTradeData[]>;
3301
+ /**
3302
+ * Fetch raw candles with flexible date/limit parameters.
3450
3303
  *
3451
- * Rejection rules (returns false without throwing):
3452
- * - LONG: currentPrice >= last entry price (must average down, not up or equal)
3453
- * - SHORT: currentPrice <= last entry price (must average down, not up or equal)
3304
+ * All modes respect execution context and prevent look-ahead bias.
3454
3305
  *
3455
- * Validations (throws):
3456
- * - No pending signal exists
3457
- * - currentPrice is not a positive finite number
3306
+ * Parameter combinations:
3307
+ * 1. sDate + eDate + limit: fetches with explicit parameters, validates eDate <= when
3308
+ * 2. sDate + eDate: calculates limit from date range, validates eDate <= when
3309
+ * 3. eDate + limit: calculates sDate backward, validates eDate <= when
3310
+ * 4. sDate + limit: fetches forward, validates calculated endTimestamp <= when
3311
+ * 5. Only limit: uses execution.context.when as reference (backward)
3458
3312
  *
3459
3313
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3460
- * @param currentPrice - New entry price to add to the averaging history
3461
- * @param backtest - Whether running in backtest mode
3462
- * @returns Promise<boolean> - true if entry added, false if rejected by direction check
3314
+ * @param interval - Candle interval (e.g., "1m", "1h")
3315
+ * @param limit - Optional number of candles to fetch
3316
+ * @param sDate - Optional start date in milliseconds
3317
+ * @param eDate - Optional end date in milliseconds
3318
+ * @returns Promise resolving to array of candles
3463
3319
  */
3464
- averageBuy: (symbol: string, currentPrice: number, backtest: boolean) => Promise<boolean>;
3320
+ getRawCandles: (symbol: string, interval: CandleInterval, limit?: number, sDate?: number, eDate?: number) => Promise<ICandleData[]>;
3321
+ }
3322
+ /**
3323
+ * Unique exchange identifier.
3324
+ */
3325
+ type ExchangeName = string;
3326
+
3327
+ /**
3328
+ * Parameters for pre-caching candles into persist storage.
3329
+ * Used to download historical candle data before running a backtest.
3330
+ */
3331
+ interface ICacheCandlesParams {
3332
+ /** Trading pair symbol (e.g., "BTCUSDT") */
3333
+ symbol: string;
3334
+ /** Name of the registered exchange schema */
3335
+ exchangeName: ExchangeName;
3336
+ /** Candle time interval (e.g., "1m", "4h") */
3337
+ interval: CandleInterval;
3338
+ /** Start date of the caching range (inclusive) */
3339
+ from: Date;
3340
+ /** End date of the caching range (inclusive) */
3341
+ to: Date;
3342
+ }
3343
+ /**
3344
+ * Parameters for validating cached candle timestamps.
3345
+ * Reads JSON files directly from persist storage directory.
3346
+ */
3347
+ interface ICheckCandlesParams {
3348
+ /** Trading pair symbol (e.g., "BTCUSDT") */
3349
+ symbol: string;
3350
+ /** Name of the registered exchange schema */
3351
+ exchangeName: ExchangeName;
3352
+ /** Candle time interval (e.g., "1m", "4h") */
3353
+ interval: CandleInterval;
3354
+ /** Start date of the validation range (inclusive) */
3355
+ from: Date;
3356
+ /** End date of the validation range (inclusive) */
3357
+ to: Date;
3358
+ /** Base directory of candle persist storage (default: "./dump/data/candle") */
3359
+ baseDir?: string;
3360
+ }
3361
+ /**
3362
+ * Checks cached candle timestamps for correct interval alignment.
3363
+ * Reads JSON files directly from persist storage without using abstractions.
3364
+ *
3365
+ * @param params - Validation parameters
3366
+ */
3367
+ declare function checkCandles(params: ICheckCandlesParams): Promise<void>;
3368
+ /**
3369
+ * Pre-caches candles for a date range into persist storage.
3370
+ * Downloads all candles matching the interval from `from` to `to`.
3371
+ *
3372
+ * @param params - Cache parameters
3373
+ */
3374
+ declare function warmCandles(params: ICacheCandlesParams): Promise<void>;
3375
+
3376
+ /**
3377
+ * Type alias for enum objects with string key-value pairs
3378
+ */
3379
+ type Enum = Record<string, string>;
3380
+ /**
3381
+ * Type alias for ValidateArgs with any enum type
3382
+ */
3383
+ type Args = ValidateArgs<any>;
3384
+ /**
3385
+ * Interface defining validation arguments for all entity types.
3386
+ *
3387
+ * Each property accepts an enum object where values will be validated
3388
+ * against registered entities in their respective validation services.
3389
+ *
3390
+ * @template T - Enum type extending Record<string, string>
3391
+ */
3392
+ interface ValidateArgs<T = Enum> {
3465
3393
  /**
3466
- * Disposes the strategy instance and cleans up resources.
3467
- *
3468
- * Called when the strategy is being removed from cache or shut down.
3469
- * Invokes the onDispose callback to notify external systems.
3470
- *
3471
- * @returns Promise that resolves when disposal is complete
3394
+ * Exchange name enum to validate
3395
+ * @example { BINANCE: "binance", BYBIT: "bybit" }
3472
3396
  */
3473
- dispose: () => Promise<void>;
3397
+ ExchangeName?: T;
3398
+ /**
3399
+ * Frame (timeframe) name enum to validate
3400
+ * @example { Q1_2024: "2024-Q1", Q2_2024: "2024-Q2" }
3401
+ */
3402
+ FrameName?: T;
3403
+ /**
3404
+ * Strategy name enum to validate
3405
+ * @example { MOMENTUM_BTC: "momentum-btc" }
3406
+ */
3407
+ StrategyName?: T;
3408
+ /**
3409
+ * Risk profile name enum to validate
3410
+ * @example { CONSERVATIVE: "conservative", AGGRESSIVE: "aggressive" }
3411
+ */
3412
+ RiskName?: T;
3413
+ /**
3414
+ * Action handler name enum to validate
3415
+ * @example { TELEGRAM_NOTIFIER: "telegram-notifier" }
3416
+ */
3417
+ ActionName?: T;
3418
+ /**
3419
+ * Sizing strategy name enum to validate
3420
+ * @example { FIXED_1000: "fixed-1000" }
3421
+ */
3422
+ SizingName?: T;
3423
+ /**
3424
+ * Walker (parameter sweep) name enum to validate
3425
+ * @example { RSI_SWEEP: "rsi-sweep" }
3426
+ */
3427
+ WalkerName?: T;
3474
3428
  }
3475
3429
  /**
3476
- * Unique strategy identifier.
3430
+ * Validates the existence of all provided entity names across validation services.
3431
+ *
3432
+ * This function accepts enum objects for various entity types (exchanges, frames,
3433
+ * strategies, risks, sizings, walkers) and validates that each entity
3434
+ * name exists in its respective registry. Validation results are memoized for performance.
3435
+ *
3436
+ * If no arguments are provided (or specific entity types are omitted), the function
3437
+ * automatically fetches and validates ALL registered entities from their respective
3438
+ * validation services. This is useful for comprehensive validation of the entire setup.
3439
+ *
3440
+ * Use this before running backtests or optimizations to ensure all referenced
3441
+ * entities are properly registered and configured.
3442
+ *
3443
+ * @public
3444
+ * @param args - Partial validation arguments containing entity name enums to validate.
3445
+ * If empty or omitted, validates all registered entities.
3446
+ * @throws {Error} If any entity name is not found in its validation service
3447
+ *
3448
+ * @example
3449
+ * ```typescript
3450
+ * // Validate ALL registered entities (exchanges, frames, strategies, etc.)
3451
+ * await validate({});
3452
+ * ```
3453
+ *
3454
+ * @example
3455
+ * ```typescript
3456
+ * // Define your entity name enums
3457
+ * enum ExchangeName {
3458
+ * BINANCE = "binance",
3459
+ * BYBIT = "bybit"
3460
+ * }
3461
+ *
3462
+ * enum StrategyName {
3463
+ * MOMENTUM_BTC = "momentum-btc"
3464
+ * }
3465
+ *
3466
+ * // Validate specific entities before running backtest
3467
+ * await validate({
3468
+ * ExchangeName,
3469
+ * StrategyName,
3470
+ * });
3471
+ * ```
3472
+ *
3473
+ * @example
3474
+ * ```typescript
3475
+ * // Validate specific entity types
3476
+ * await validate({
3477
+ * RiskName: { CONSERVATIVE: "conservative" },
3478
+ * SizingName: { FIXED_1000: "fixed-1000" },
3479
+ * });
3480
+ * ```
3477
3481
  */
3478
- type StrategyName = string;
3482
+ declare function validate(args?: Partial<Args>): Promise<void>;
3479
3483
 
3480
3484
  /**
3481
3485
  * Statistical data calculated from backtest results.
@@ -4228,6 +4232,13 @@ declare function commitAverageBuy(symbol: string): Promise<boolean>;
4228
4232
  */
4229
4233
  declare function stopStrategy(symbol: string): Promise<void>;
4230
4234
 
4235
+ /**
4236
+ * Gracefully shuts down the backtest execution by emitting a shutdown event.
4237
+ * This allows all components that subscribe to the shutdownEmitter to perform necessary cleanup before the process exits.
4238
+ * The shutdown method is typically called in response to a termination signal (e.g., SIGINT) to ensure a clean exit.
4239
+ */
4240
+ declare function shutdown(): void;
4241
+
4231
4242
  /**
4232
4243
  * Unified breakeven event data for report generation.
4233
4244
  * Contains all information about when signals reached breakeven.
@@ -9995,6 +10006,15 @@ declare class LogAdapter implements ILog {
9995
10006
  * All future log writes will be no-ops.
9996
10007
  */
9997
10008
  useDummy: () => void;
10009
+ /**
10010
+ * Switches to JSONL file log adapter.
10011
+ * Log entries will be appended to {dirName}/{fileName}.jsonl.
10012
+ * Reads are performed by parsing all lines from the file.
10013
+ *
10014
+ * @param fileName - Base file name without extension (default: "log")
10015
+ * @param dirName - Directory for the JSONL file (default: ./dump/log)
10016
+ */
10017
+ useJsonl: (fileName?: string, dirName?: string) => void;
9998
10018
  }
9999
10019
  /**
10000
10020
  * Global singleton instance of LogAdapter.
@@ -16705,6 +16725,11 @@ declare const errorEmitter: Subject<Error>;
16705
16725
  * Unlike errorEmitter (for recoverable errors), exitEmitter signals fatal errors.
16706
16726
  */
16707
16727
  declare const exitEmitter: Subject<Error>;
16728
+ /**
16729
+ * Shutdown emitter for graceful shutdown events.
16730
+ * Emits when a shutdown signal is received (e.g., SIGINT) to allow components to perform cleanup before process exit.
16731
+ */
16732
+ declare const shutdownEmitter: Subject<void>;
16708
16733
  /**
16709
16734
  * Done emitter for live background execution completion.
16710
16735
  * Emits when live background tasks complete (Live.background).
@@ -16828,6 +16853,7 @@ declare const emitters_progressBacktestEmitter: typeof progressBacktestEmitter;
16828
16853
  declare const emitters_progressWalkerEmitter: typeof progressWalkerEmitter;
16829
16854
  declare const emitters_riskSubject: typeof riskSubject;
16830
16855
  declare const emitters_schedulePingSubject: typeof schedulePingSubject;
16856
+ declare const emitters_shutdownEmitter: typeof shutdownEmitter;
16831
16857
  declare const emitters_signalBacktestEmitter: typeof signalBacktestEmitter;
16832
16858
  declare const emitters_signalEmitter: typeof signalEmitter;
16833
16859
  declare const emitters_signalLiveEmitter: typeof signalLiveEmitter;
@@ -16837,7 +16863,7 @@ declare const emitters_walkerCompleteSubject: typeof walkerCompleteSubject;
16837
16863
  declare const emitters_walkerEmitter: typeof walkerEmitter;
16838
16864
  declare const emitters_walkerStopSubject: typeof walkerStopSubject;
16839
16865
  declare namespace emitters {
16840
- export { emitters_activePingSubject as activePingSubject, emitters_backtestScheduleOpenSubject as backtestScheduleOpenSubject, emitters_breakevenSubject as breakevenSubject, emitters_doneBacktestSubject as doneBacktestSubject, emitters_doneLiveSubject as doneLiveSubject, emitters_doneWalkerSubject as doneWalkerSubject, emitters_errorEmitter as errorEmitter, emitters_exitEmitter as exitEmitter, emitters_partialLossSubject as partialLossSubject, emitters_partialProfitSubject as partialProfitSubject, emitters_performanceEmitter as performanceEmitter, emitters_progressBacktestEmitter as progressBacktestEmitter, emitters_progressWalkerEmitter as progressWalkerEmitter, emitters_riskSubject as riskSubject, emitters_schedulePingSubject as schedulePingSubject, emitters_signalBacktestEmitter as signalBacktestEmitter, emitters_signalEmitter as signalEmitter, emitters_signalLiveEmitter as signalLiveEmitter, emitters_strategyCommitSubject as strategyCommitSubject, emitters_validationSubject as validationSubject, emitters_walkerCompleteSubject as walkerCompleteSubject, emitters_walkerEmitter as walkerEmitter, emitters_walkerStopSubject as walkerStopSubject };
16866
+ export { emitters_activePingSubject as activePingSubject, emitters_backtestScheduleOpenSubject as backtestScheduleOpenSubject, emitters_breakevenSubject as breakevenSubject, emitters_doneBacktestSubject as doneBacktestSubject, emitters_doneLiveSubject as doneLiveSubject, emitters_doneWalkerSubject as doneWalkerSubject, emitters_errorEmitter as errorEmitter, emitters_exitEmitter as exitEmitter, emitters_partialLossSubject as partialLossSubject, emitters_partialProfitSubject as partialProfitSubject, emitters_performanceEmitter as performanceEmitter, emitters_progressBacktestEmitter as progressBacktestEmitter, emitters_progressWalkerEmitter as progressWalkerEmitter, emitters_riskSubject as riskSubject, emitters_schedulePingSubject as schedulePingSubject, emitters_shutdownEmitter as shutdownEmitter, emitters_signalBacktestEmitter as signalBacktestEmitter, emitters_signalEmitter as signalEmitter, emitters_signalLiveEmitter as signalLiveEmitter, emitters_strategyCommitSubject as strategyCommitSubject, emitters_validationSubject as validationSubject, emitters_walkerCompleteSubject as walkerCompleteSubject, emitters_walkerEmitter as walkerEmitter, emitters_walkerStopSubject as walkerStopSubject };
16841
16867
  }
16842
16868
 
16843
16869
  /**
@@ -21586,4 +21612,4 @@ declare const backtest: {
21586
21612
  loggerService: LoggerService;
21587
21613
  };
21588
21614
 
21589
- export { ActionBase, type ActivateScheduledCommit, type ActivateScheduledCommitNotification, type ActivePingContract, type AverageBuyCommit, Backtest, type BacktestStatisticsModel, Breakeven, type BreakevenAvailableNotification, type BreakevenCommit, type BreakevenCommitNotification, type BreakevenContract, type BreakevenData, Cache, type CancelScheduledCommit, type CandleData, type CandleInterval, type ClosePendingCommit, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IActionSchema, type IActivateScheduledCommitRow, type IAggregatedTradeData, type IBidData, type IBreakevenCommitRow, type ICandleData, type ICommitRow, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type ILog, type ILogEntry, type ILogger, type IMarkdownDumpOptions, type INotificationUtils, type IOrderBookData, type IPartialLossCommitRow, type IPartialProfitCommitRow, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicAction, type IPublicCandleData, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskSignalRow, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingParams, type ISizingParamsATR, type ISizingParamsFixedPercentage, type ISizingParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStorageSignalRow, type IStorageUtils, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IStrategyTickResultWaiting, type ITrailingStopCommitRow, type ITrailingTakeCommitRow, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveStatisticsModel, Log, type LogData, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, MethodContextService, type MetricStats, Notification, NotificationBacktest, type NotificationData, NotificationLive, type NotificationModel, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossAvailableNotification, type PartialLossCommit, type PartialLossCommitNotification, type PartialLossContract, type PartialProfitAvailableNotification, type PartialProfitCommit, type PartialProfitCommitNotification, type PartialProfitContract, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistLogAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, type ProgressBacktestContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type SchedulePingContract, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, Storage, StorageBacktest, type StorageData, StorageLive, Strategy, type StrategyActionType, type StrategyCancelReason, type StrategyCloseReason, type StrategyCommitContract, type StrategyEvent, type StrategyStatisticsModel, type TLogCtor, type TMarkdownBase, type TNotificationUtilsCtor, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TStorageUtilsCtor, type TickEvent, type TrailingStopCommit, type TrailingStopCommitNotification, type TrailingTakeCommit, type TrailingTakeCommitNotification, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
21615
+ export { ActionBase, type ActivateScheduledCommit, type ActivateScheduledCommitNotification, type ActivePingContract, type AverageBuyCommit, Backtest, type BacktestStatisticsModel, Breakeven, type BreakevenAvailableNotification, type BreakevenCommit, type BreakevenCommitNotification, type BreakevenContract, type BreakevenData, Cache, type CancelScheduledCommit, type CandleData, type CandleInterval, type ClosePendingCommit, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IActionSchema, type IActivateScheduledCommitRow, type IAggregatedTradeData, type IBidData, type IBreakevenCommitRow, type ICandleData, type ICommitRow, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type ILog, type ILogEntry, type ILogger, type IMarkdownDumpOptions, type INotificationUtils, type IOrderBookData, type IPartialLossCommitRow, type IPartialProfitCommitRow, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicAction, type IPublicCandleData, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskSignalRow, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingParams, type ISizingParamsATR, type ISizingParamsFixedPercentage, type ISizingParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStorageSignalRow, type IStorageUtils, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IStrategyTickResultWaiting, type ITrailingStopCommitRow, type ITrailingTakeCommitRow, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveStatisticsModel, Log, type LogData, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, MethodContextService, type MetricStats, Notification, NotificationBacktest, type NotificationData, NotificationLive, type NotificationModel, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossAvailableNotification, type PartialLossCommit, type PartialLossCommitNotification, type PartialLossContract, type PartialProfitAvailableNotification, type PartialProfitCommit, type PartialProfitCommitNotification, type PartialProfitContract, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistLogAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, type ProgressBacktestContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type SchedulePingContract, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, Storage, StorageBacktest, type StorageData, StorageLive, Strategy, type StrategyActionType, type StrategyCancelReason, type StrategyCloseReason, type StrategyCommitContract, type StrategyEvent, type StrategyStatisticsModel, type TLogCtor, type TMarkdownBase, type TNotificationUtilsCtor, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TStorageUtilsCtor, type TickEvent, type TrailingStopCommit, type TrailingStopCommitNotification, type TrailingTakeCommit, type TrailingTakeCommitNotification, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, shutdown, stopStrategy, validate, waitForCandle, warmCandles };