backtest-kit 1.13.3 → 1.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +213 -0
- package/build/index.mjs +205 -1
- package/package.json +1 -1
- package/types.d.ts +2323 -2188
package/types.d.ts
CHANGED
|
@@ -3,6 +3,14 @@ import * as functools_kit from 'functools-kit';
|
|
|
3
3
|
import { Subject } from 'functools-kit';
|
|
4
4
|
import { WriteStream } from 'fs';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves current backtest timeframe for given symbol.
|
|
8
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
9
|
+
* @returns Promise resolving to array of Date objects representing tick timestamps
|
|
10
|
+
* @throws Error if called outside of backtest execution context
|
|
11
|
+
*/
|
|
12
|
+
declare function getCurrentTimeframe(symbol: string): Promise<Date[]>;
|
|
13
|
+
|
|
6
14
|
/**
|
|
7
15
|
* Type alias for enum objects with string key-value pairs
|
|
8
16
|
*/
|
|
@@ -116,222 +124,6 @@ interface ValidateArgs<T = Enum> {
|
|
|
116
124
|
*/
|
|
117
125
|
declare function validate(args?: Partial<Args>): Promise<void>;
|
|
118
126
|
|
|
119
|
-
/**
|
|
120
|
-
* Stops the strategy from generating new signals.
|
|
121
|
-
*
|
|
122
|
-
* Sets internal flag to prevent strategy from opening new signals.
|
|
123
|
-
* Current active signal (if any) will complete normally.
|
|
124
|
-
* Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
|
|
125
|
-
*
|
|
126
|
-
* Automatically detects backtest/live mode from execution context.
|
|
127
|
-
*
|
|
128
|
-
* @param symbol - Trading pair symbol
|
|
129
|
-
* @param strategyName - Strategy name to stop
|
|
130
|
-
* @returns Promise that resolves when stop flag is set
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* ```typescript
|
|
134
|
-
* import { stop } from "backtest-kit";
|
|
135
|
-
*
|
|
136
|
-
* // Stop strategy after some condition
|
|
137
|
-
* await stop("BTCUSDT", "my-strategy");
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
declare function stop(symbol: string): Promise<void>;
|
|
141
|
-
/**
|
|
142
|
-
* Cancels the scheduled signal without stopping the strategy.
|
|
143
|
-
*
|
|
144
|
-
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
145
|
-
* Does NOT affect active pending signals or strategy operation.
|
|
146
|
-
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
147
|
-
*
|
|
148
|
-
* Automatically detects backtest/live mode from execution context.
|
|
149
|
-
*
|
|
150
|
-
* @param symbol - Trading pair symbol
|
|
151
|
-
* @param strategyName - Strategy name
|
|
152
|
-
* @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
|
|
153
|
-
* @returns Promise that resolves when scheduled signal is cancelled
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* ```typescript
|
|
157
|
-
* import { cancel } from "backtest-kit";
|
|
158
|
-
*
|
|
159
|
-
* // Cancel scheduled signal with custom ID
|
|
160
|
-
* await cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
161
|
-
* ```
|
|
162
|
-
*/
|
|
163
|
-
declare function cancel(symbol: string, cancelId?: string): Promise<void>;
|
|
164
|
-
/**
|
|
165
|
-
* Executes partial close at profit level (moving toward TP).
|
|
166
|
-
*
|
|
167
|
-
* Closes a percentage of the active pending position at profit.
|
|
168
|
-
* Price must be moving toward take profit (in profit direction).
|
|
169
|
-
*
|
|
170
|
-
* Automatically detects backtest/live mode from execution context.
|
|
171
|
-
*
|
|
172
|
-
* @param symbol - Trading pair symbol
|
|
173
|
-
* @param percentToClose - Percentage of position to close (0-100, absolute value)
|
|
174
|
-
* @returns Promise<boolean> - true if partial close executed, false if skipped
|
|
175
|
-
*
|
|
176
|
-
* @throws Error if currentPrice is not in profit direction:
|
|
177
|
-
* - LONG: currentPrice must be > priceOpen
|
|
178
|
-
* - SHORT: currentPrice must be < priceOpen
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* ```typescript
|
|
182
|
-
* import { partialProfit } from "backtest-kit";
|
|
183
|
-
*
|
|
184
|
-
* // Close 30% of LONG position at profit
|
|
185
|
-
* const success = await partialProfit("BTCUSDT", 30);
|
|
186
|
-
* if (success) {
|
|
187
|
-
* console.log('Partial profit executed');
|
|
188
|
-
* }
|
|
189
|
-
* ```
|
|
190
|
-
*/
|
|
191
|
-
declare function partialProfit(symbol: string, percentToClose: number): Promise<boolean>;
|
|
192
|
-
/**
|
|
193
|
-
* Executes partial close at loss level (moving toward SL).
|
|
194
|
-
*
|
|
195
|
-
* Closes a percentage of the active pending position at loss.
|
|
196
|
-
* Price must be moving toward stop loss (in loss direction).
|
|
197
|
-
*
|
|
198
|
-
* Automatically detects backtest/live mode from execution context.
|
|
199
|
-
*
|
|
200
|
-
* @param symbol - Trading pair symbol
|
|
201
|
-
* @param percentToClose - Percentage of position to close (0-100, absolute value)
|
|
202
|
-
* @returns Promise<boolean> - true if partial close executed, false if skipped
|
|
203
|
-
*
|
|
204
|
-
* @throws Error if currentPrice is not in loss direction:
|
|
205
|
-
* - LONG: currentPrice must be < priceOpen
|
|
206
|
-
* - SHORT: currentPrice must be > priceOpen
|
|
207
|
-
*
|
|
208
|
-
* @example
|
|
209
|
-
* ```typescript
|
|
210
|
-
* import { partialLoss } from "backtest-kit";
|
|
211
|
-
*
|
|
212
|
-
* // Close 40% of LONG position at loss
|
|
213
|
-
* const success = await partialLoss("BTCUSDT", 40);
|
|
214
|
-
* if (success) {
|
|
215
|
-
* console.log('Partial loss executed');
|
|
216
|
-
* }
|
|
217
|
-
* ```
|
|
218
|
-
*/
|
|
219
|
-
declare function partialLoss(symbol: string, percentToClose: number): Promise<boolean>;
|
|
220
|
-
/**
|
|
221
|
-
* Adjusts the trailing stop-loss distance for an active pending signal.
|
|
222
|
-
*
|
|
223
|
-
* CRITICAL: Always calculates from ORIGINAL SL, not from current trailing SL.
|
|
224
|
-
* This prevents error accumulation on repeated calls.
|
|
225
|
-
* Larger percentShift ABSORBS smaller one (updates only towards better protection).
|
|
226
|
-
*
|
|
227
|
-
* Updates the stop-loss distance by a percentage adjustment relative to the ORIGINAL SL distance.
|
|
228
|
-
* Negative percentShift tightens the SL (reduces distance, moves closer to entry).
|
|
229
|
-
* Positive percentShift loosens the SL (increases distance, moves away from entry).
|
|
230
|
-
*
|
|
231
|
-
* Absorption behavior:
|
|
232
|
-
* - First call: sets trailing SL unconditionally
|
|
233
|
-
* - Subsequent calls: updates only if new SL is BETTER (protects more profit)
|
|
234
|
-
* - For LONG: only accepts HIGHER SL (never moves down, closer to entry wins)
|
|
235
|
-
* - For SHORT: only accepts LOWER SL (never moves up, closer to entry wins)
|
|
236
|
-
*
|
|
237
|
-
* Automatically detects backtest/live mode from execution context.
|
|
238
|
-
*
|
|
239
|
-
* @param symbol - Trading pair symbol
|
|
240
|
-
* @param percentShift - Percentage adjustment to ORIGINAL SL distance (-100 to 100)
|
|
241
|
-
* @param currentPrice - Current market price to check for intrusion
|
|
242
|
-
* @returns Promise<boolean> - true if trailing SL was set/updated, false if rejected (absorption/intrusion/conflict)
|
|
243
|
-
*
|
|
244
|
-
* @example
|
|
245
|
-
* ```typescript
|
|
246
|
-
* import { trailingStop } from "backtest-kit";
|
|
247
|
-
*
|
|
248
|
-
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
249
|
-
*
|
|
250
|
-
* // First call: tighten by 5%
|
|
251
|
-
* const success1 = await trailingStop("BTCUSDT", -5, 102);
|
|
252
|
-
* // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
|
|
253
|
-
*
|
|
254
|
-
* // Second call: try weaker protection (smaller percentShift)
|
|
255
|
-
* const success2 = await trailingStop("BTCUSDT", -3, 102);
|
|
256
|
-
* // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
|
|
257
|
-
*
|
|
258
|
-
* // Third call: stronger protection (larger percentShift)
|
|
259
|
-
* const success3 = await trailingStop("BTCUSDT", -7, 102);
|
|
260
|
-
* // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
|
|
261
|
-
* ```
|
|
262
|
-
*/
|
|
263
|
-
declare function trailingStop(symbol: string, percentShift: number, currentPrice: number): Promise<boolean>;
|
|
264
|
-
/**
|
|
265
|
-
* Adjusts the trailing take-profit distance for an active pending signal.
|
|
266
|
-
*
|
|
267
|
-
* CRITICAL: Always calculates from ORIGINAL TP, not from current trailing TP.
|
|
268
|
-
* This prevents error accumulation on repeated calls.
|
|
269
|
-
* Larger percentShift ABSORBS smaller one (updates only towards more conservative TP).
|
|
270
|
-
*
|
|
271
|
-
* Updates the take-profit distance by a percentage adjustment relative to the ORIGINAL TP distance.
|
|
272
|
-
* Negative percentShift brings TP closer to entry (more conservative).
|
|
273
|
-
* Positive percentShift moves TP further from entry (more aggressive).
|
|
274
|
-
*
|
|
275
|
-
* Absorption behavior:
|
|
276
|
-
* - First call: sets trailing TP unconditionally
|
|
277
|
-
* - Subsequent calls: updates only if new TP is MORE CONSERVATIVE (closer to entry)
|
|
278
|
-
* - For LONG: only accepts LOWER TP (never moves up, closer to entry wins)
|
|
279
|
-
* - For SHORT: only accepts HIGHER TP (never moves down, closer to entry wins)
|
|
280
|
-
*
|
|
281
|
-
* Automatically detects backtest/live mode from execution context.
|
|
282
|
-
*
|
|
283
|
-
* @param symbol - Trading pair symbol
|
|
284
|
-
* @param percentShift - Percentage adjustment to ORIGINAL TP distance (-100 to 100)
|
|
285
|
-
* @param currentPrice - Current market price to check for intrusion
|
|
286
|
-
* @returns Promise<boolean> - true if trailing TP was set/updated, false if rejected (absorption/intrusion/conflict)
|
|
287
|
-
*
|
|
288
|
-
* @example
|
|
289
|
-
* ```typescript
|
|
290
|
-
* import { trailingTake } from "backtest-kit";
|
|
291
|
-
*
|
|
292
|
-
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
293
|
-
*
|
|
294
|
-
* // First call: bring TP closer by 3%
|
|
295
|
-
* const success1 = await trailingTake("BTCUSDT", -3, 102);
|
|
296
|
-
* // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
|
|
297
|
-
*
|
|
298
|
-
* // Second call: try to move TP further (less conservative)
|
|
299
|
-
* const success2 = await trailingTake("BTCUSDT", 2, 102);
|
|
300
|
-
* // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
|
|
301
|
-
*
|
|
302
|
-
* // Third call: even more conservative
|
|
303
|
-
* const success3 = await trailingTake("BTCUSDT", -5, 102);
|
|
304
|
-
* // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
|
|
305
|
-
* ```
|
|
306
|
-
*/
|
|
307
|
-
declare function trailingTake(symbol: string, percentShift: number, currentPrice: number): Promise<boolean>;
|
|
308
|
-
/**
|
|
309
|
-
* Moves stop-loss to breakeven when price reaches threshold.
|
|
310
|
-
*
|
|
311
|
-
* Moves SL to entry price (zero-risk position) when current price has moved
|
|
312
|
-
* far enough in profit direction to cover transaction costs.
|
|
313
|
-
* Threshold is calculated as: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2
|
|
314
|
-
*
|
|
315
|
-
* Automatically detects backtest/live mode from execution context.
|
|
316
|
-
* Automatically fetches current price via getAveragePrice.
|
|
317
|
-
*
|
|
318
|
-
* @param symbol - Trading pair symbol
|
|
319
|
-
* @returns Promise<boolean> - true if breakeven was set, false if conditions not met
|
|
320
|
-
*
|
|
321
|
-
* @example
|
|
322
|
-
* ```typescript
|
|
323
|
-
* import { breakeven } from "backtest-kit";
|
|
324
|
-
*
|
|
325
|
-
* // LONG: entry=100, slippage=0.1%, fee=0.1%, threshold=0.4%
|
|
326
|
-
* // Try to move SL to breakeven (activates when price >= 100.4)
|
|
327
|
-
* const moved = await breakeven("BTCUSDT");
|
|
328
|
-
* if (moved) {
|
|
329
|
-
* console.log("Position moved to breakeven!");
|
|
330
|
-
* }
|
|
331
|
-
* ```
|
|
332
|
-
*/
|
|
333
|
-
declare function breakeven(symbol: string): Promise<boolean>;
|
|
334
|
-
|
|
335
127
|
/**
|
|
336
128
|
* Execution context containing runtime parameters for strategy/exchange operations.
|
|
337
129
|
*
|
|
@@ -1197,2262 +989,1845 @@ interface IBreakeven {
|
|
|
1197
989
|
}
|
|
1198
990
|
|
|
1199
991
|
/**
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1202
|
-
* Emitted by breakevenSubject when a signal's stop-loss is moved to breakeven (entry price).
|
|
1203
|
-
* Used for tracking risk reduction milestones and monitoring strategy safety.
|
|
1204
|
-
*
|
|
1205
|
-
* Events are emitted only once per signal (idempotent - protected by ClientBreakeven state).
|
|
1206
|
-
* Breakeven is triggered when price moves far enough in profit direction to cover transaction costs.
|
|
1207
|
-
*
|
|
1208
|
-
* Consumers:
|
|
1209
|
-
* - BreakevenMarkdownService: Accumulates events for report generation
|
|
1210
|
-
* - User callbacks via listenBreakeven() / listenBreakevenOnce()
|
|
1211
|
-
*
|
|
1212
|
-
* @example
|
|
1213
|
-
* ```typescript
|
|
1214
|
-
* import { listenBreakeven } from "backtest-kit";
|
|
1215
|
-
*
|
|
1216
|
-
* // Listen to all breakeven events
|
|
1217
|
-
* listenBreakeven((event) => {
|
|
1218
|
-
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} moved to breakeven`);
|
|
1219
|
-
* console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
|
|
1220
|
-
* console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
|
|
1221
|
-
* console.log(`Original SL: ${event.data.priceStopLoss}, New SL: ${event.data.priceOpen}`);
|
|
1222
|
-
* });
|
|
1223
|
-
*
|
|
1224
|
-
* // Wait for specific signal to reach breakeven
|
|
1225
|
-
* listenBreakevenOnce(
|
|
1226
|
-
* (event) => event.data.id === "target-signal-id",
|
|
1227
|
-
* (event) => console.log("Signal reached breakeven:", event.data.id)
|
|
1228
|
-
* );
|
|
1229
|
-
* ```
|
|
992
|
+
* Signal generation interval for throttling.
|
|
993
|
+
* Enforces minimum time between getSignal calls.
|
|
1230
994
|
*/
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
/**
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
995
|
+
type SignalInterval = "1m" | "3m" | "5m" | "15m" | "30m" | "1h";
|
|
996
|
+
/**
|
|
997
|
+
* Signal data transfer object returned by getSignal.
|
|
998
|
+
* Will be validated and augmented with auto-generated id.
|
|
999
|
+
*/
|
|
1000
|
+
interface ISignalDto {
|
|
1001
|
+
/** Optional signal ID (auto-generated if not provided) */
|
|
1002
|
+
id?: string;
|
|
1003
|
+
/** Trade direction: "long" (buy) or "short" (sell) */
|
|
1004
|
+
position: "long" | "short";
|
|
1005
|
+
/** Human-readable description of signal reason */
|
|
1006
|
+
note?: string;
|
|
1007
|
+
/** Entry price for the position */
|
|
1008
|
+
priceOpen?: number;
|
|
1009
|
+
/** Take profit target price (must be > priceOpen for long, < priceOpen for short) */
|
|
1010
|
+
priceTakeProfit: number;
|
|
1011
|
+
/** Stop loss exit price (must be < priceOpen for long, > priceOpen for short) */
|
|
1012
|
+
priceStopLoss: number;
|
|
1013
|
+
/** Expected duration in minutes before time_expired */
|
|
1014
|
+
minuteEstimatedTime: number;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Complete signal with auto-generated id.
|
|
1018
|
+
* Used throughout the system after validation.
|
|
1019
|
+
*/
|
|
1020
|
+
interface ISignalRow extends ISignalDto {
|
|
1021
|
+
/** Unique signal identifier (UUID v4 auto-generated) */
|
|
1022
|
+
id: string;
|
|
1023
|
+
/** Entry price for the position */
|
|
1024
|
+
priceOpen: number;
|
|
1025
|
+
/** Unique exchange identifier for execution */
|
|
1246
1026
|
exchangeName: ExchangeName;
|
|
1247
|
-
/**
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
*/
|
|
1027
|
+
/** Unique strategy identifier for execution */
|
|
1028
|
+
strategyName: StrategyName;
|
|
1029
|
+
/** Unique frame identifier for execution (empty string for live mode) */
|
|
1251
1030
|
frameName: FrameName;
|
|
1031
|
+
/** Signal creation timestamp in milliseconds (when signal was first created/scheduled) */
|
|
1032
|
+
scheduledAt: number;
|
|
1033
|
+
/** Pending timestamp in milliseconds (when position became pending/active at priceOpen) */
|
|
1034
|
+
pendingAt: number;
|
|
1035
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
1036
|
+
symbol: string;
|
|
1037
|
+
/** Internal runtime marker for scheduled signals */
|
|
1038
|
+
_isScheduled: boolean;
|
|
1252
1039
|
/**
|
|
1253
|
-
*
|
|
1254
|
-
*
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1040
|
+
* History of partial closes for PNL calculation.
|
|
1041
|
+
* Each entry contains type (profit/loss), percent closed, and price.
|
|
1042
|
+
* Used to calculate weighted PNL: Σ(percent_i × pnl_i) for each partial + (remaining% × final_pnl)
|
|
1043
|
+
*
|
|
1044
|
+
* Computed values (derived from this array):
|
|
1045
|
+
* - _tpClosed: Sum of all "profit" type partial close percentages
|
|
1046
|
+
* - _slClosed: Sum of all "loss" type partial close percentages
|
|
1047
|
+
* - _totalClosed: Sum of all partial close percentages (profit + loss)
|
|
1260
1048
|
*/
|
|
1261
|
-
|
|
1049
|
+
_partial?: Array<{
|
|
1050
|
+
/** Type of partial close: profit (moving toward TP) or loss (moving toward SL) */
|
|
1051
|
+
type: "profit" | "loss";
|
|
1052
|
+
/** Percentage of position closed (0-100) */
|
|
1053
|
+
percent: number;
|
|
1054
|
+
/** Price at which this partial was executed */
|
|
1055
|
+
price: number;
|
|
1056
|
+
}>;
|
|
1262
1057
|
/**
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1265
|
-
* -
|
|
1058
|
+
* Trailing stop-loss price that overrides priceStopLoss when set.
|
|
1059
|
+
* Updated by trailing() method based on position type and percentage distance.
|
|
1060
|
+
* - For LONG: moves upward as price moves toward TP (never moves down)
|
|
1061
|
+
* - For SHORT: moves downward as price moves toward TP (never moves up)
|
|
1062
|
+
* When _trailingPriceStopLoss is set, it replaces priceStopLoss for TP/SL checks.
|
|
1063
|
+
* Original priceStopLoss is preserved in persistence but ignored during execution.
|
|
1266
1064
|
*/
|
|
1267
|
-
|
|
1065
|
+
_trailingPriceStopLoss?: number;
|
|
1268
1066
|
/**
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1272
|
-
*
|
|
1273
|
-
* -
|
|
1274
|
-
*
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
-
* const eventDate = new Date(event.timestamp);
|
|
1278
|
-
* console.log(`Breakeven set at: ${eventDate.toISOString()}`);
|
|
1279
|
-
* ```
|
|
1067
|
+
* Trailing take-profit price that overrides priceTakeProfit when set.
|
|
1068
|
+
* Created and managed by trailingTake() method for dynamic TP adjustment.
|
|
1069
|
+
* Allows moving TP further from or closer to current price based on strategy.
|
|
1070
|
+
* Updated by trailingTake() method based on position type and percentage distance.
|
|
1071
|
+
* - For LONG: can move upward (further) or downward (closer) from entry
|
|
1072
|
+
* - For SHORT: can move downward (further) or upward (closer) from entry
|
|
1073
|
+
* When _trailingPriceTakeProfit is set, it replaces priceTakeProfit for TP/SL checks.
|
|
1074
|
+
* Original priceTakeProfit is preserved in persistence but ignored during execution.
|
|
1280
1075
|
*/
|
|
1281
|
-
|
|
1076
|
+
_trailingPriceTakeProfit?: number;
|
|
1282
1077
|
}
|
|
1283
|
-
|
|
1284
1078
|
/**
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1290
|
-
* Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
|
|
1291
|
-
* Multiple levels can be emitted in a single tick if price jumps significantly.
|
|
1292
|
-
*
|
|
1293
|
-
* Consumers:
|
|
1294
|
-
* - PartialMarkdownService: Accumulates events for report generation
|
|
1295
|
-
* - User callbacks via listenPartialProfit() / listenPartialProfitOnce()
|
|
1296
|
-
*
|
|
1297
|
-
* @example
|
|
1298
|
-
* ```typescript
|
|
1299
|
-
* import { listenPartialProfit } from "backtest-kit";
|
|
1300
|
-
*
|
|
1301
|
-
* // Listen to all partial profit events
|
|
1302
|
-
* listenPartialProfit((event) => {
|
|
1303
|
-
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached ${event.level}% profit`);
|
|
1304
|
-
* console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
|
|
1305
|
-
* console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
|
|
1306
|
-
* });
|
|
1307
|
-
*
|
|
1308
|
-
* // Wait for first 50% profit level
|
|
1309
|
-
* listenPartialProfitOnce(
|
|
1310
|
-
* (event) => event.level === 50,
|
|
1311
|
-
* (event) => console.log("50% profit reached:", event.data.id)
|
|
1312
|
-
* );
|
|
1313
|
-
* ```
|
|
1079
|
+
* Scheduled signal row for delayed entry at specific price.
|
|
1080
|
+
* Inherits from ISignalRow - represents a signal waiting for price to reach priceOpen.
|
|
1081
|
+
* Once price reaches priceOpen, will be converted to regular _pendingSignal.
|
|
1082
|
+
* Note: pendingAt will be set to scheduledAt until activation, then updated to actual pending time.
|
|
1314
1083
|
*/
|
|
1315
|
-
interface
|
|
1316
|
-
/**
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
*/
|
|
1330
|
-
exchangeName: ExchangeName;
|
|
1084
|
+
interface IScheduledSignalRow extends ISignalRow {
|
|
1085
|
+
/** Entry price for the position */
|
|
1086
|
+
priceOpen: number;
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Public signal row with original stop-loss and take-profit prices.
|
|
1090
|
+
* Extends ISignalRow to include originalPriceStopLoss and originalPriceTakeProfit for external visibility.
|
|
1091
|
+
* Used in public APIs to show user the original SL/TP even if trailing SL/TP are active.
|
|
1092
|
+
* This allows users to see both the current effective SL/TP and the original values set at signal creation.
|
|
1093
|
+
* The original prices remain unchanged even if _trailingPriceStopLoss or _trailingPriceTakeProfit modify the effective values.
|
|
1094
|
+
* Useful for transparency in reporting and user interfaces.
|
|
1095
|
+
* Note: originalPriceStopLoss/originalPriceTakeProfit are identical to priceStopLoss/priceTakeProfit at signal creation time.
|
|
1096
|
+
*/
|
|
1097
|
+
interface IPublicSignalRow extends ISignalRow {
|
|
1331
1098
|
/**
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1099
|
+
* Original stop-loss price set at signal creation.
|
|
1100
|
+
* Remains unchanged even if trailing stop-loss modifies effective SL.
|
|
1101
|
+
* Used for user visibility of initial SL parameters.
|
|
1334
1102
|
*/
|
|
1335
|
-
|
|
1103
|
+
originalPriceStopLoss: number;
|
|
1336
1104
|
/**
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1105
|
+
* Original take-profit price set at signal creation.
|
|
1106
|
+
* Remains unchanged even if trailing take-profit modifies effective TP.
|
|
1107
|
+
* Used for user visibility of initial TP parameters.
|
|
1339
1108
|
*/
|
|
1340
|
-
|
|
1109
|
+
originalPriceTakeProfit: number;
|
|
1341
1110
|
/**
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1111
|
+
* Total executed percentage from partial closes.
|
|
1112
|
+
* Sum of all percent values from _partial array (both profit and loss types).
|
|
1113
|
+
* Represents the total portion of the position that has been closed through partial executions.
|
|
1114
|
+
* Range: 0-100. Value of 0 means no partial closes, 100 means position fully closed through partials.
|
|
1344
1115
|
*/
|
|
1345
|
-
|
|
1116
|
+
totalExecuted: number;
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Risk signal row for internal risk management.
|
|
1120
|
+
* Extends ISignalDto to include priceOpen, originalPriceStopLoss and originalPriceTakeProfit.
|
|
1121
|
+
* Used in risk validation to access entry price and original SL/TP.
|
|
1122
|
+
*/
|
|
1123
|
+
interface IRiskSignalRow extends IPublicSignalRow {
|
|
1346
1124
|
/**
|
|
1347
|
-
*
|
|
1348
|
-
* Represents percentage profit relative to entry price.
|
|
1349
|
-
*
|
|
1350
|
-
* @example
|
|
1351
|
-
* ```typescript
|
|
1352
|
-
* // If entry was $50000 and level is 20:
|
|
1353
|
-
* // currentPrice >= $60000 (20% profit)
|
|
1354
|
-
* ```
|
|
1125
|
+
* Entry price for the position.
|
|
1355
1126
|
*/
|
|
1356
|
-
|
|
1127
|
+
priceOpen: number;
|
|
1357
1128
|
/**
|
|
1358
|
-
*
|
|
1359
|
-
* - true: Event from backtest execution (historical candle data)
|
|
1360
|
-
* - false: Event from live trading (real-time tick)
|
|
1129
|
+
* Original stop-loss price set at signal creation.
|
|
1361
1130
|
*/
|
|
1362
|
-
|
|
1131
|
+
originalPriceStopLoss: number;
|
|
1363
1132
|
/**
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
1366
|
-
* Timing semantics:
|
|
1367
|
-
* - Live mode: when.getTime() at the moment profit level was detected
|
|
1368
|
-
* - Backtest mode: candle.timestamp of the candle that triggered the level
|
|
1369
|
-
*
|
|
1370
|
-
* @example
|
|
1371
|
-
* ```typescript
|
|
1372
|
-
* const eventDate = new Date(event.timestamp);
|
|
1373
|
-
* console.log(`Profit reached at: ${eventDate.toISOString()}`);
|
|
1374
|
-
* ```
|
|
1133
|
+
* Original take-profit price set at signal creation.
|
|
1375
1134
|
*/
|
|
1376
|
-
|
|
1135
|
+
originalPriceTakeProfit: number;
|
|
1377
1136
|
}
|
|
1378
|
-
|
|
1379
1137
|
/**
|
|
1380
|
-
*
|
|
1381
|
-
*
|
|
1382
|
-
* Emitted by partialLossSubject when a signal reaches a loss level milestone (-10%, -20%, -30%, etc).
|
|
1383
|
-
* Used for tracking partial stop-loss execution and monitoring strategy drawdown.
|
|
1384
|
-
*
|
|
1385
|
-
* Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
|
|
1386
|
-
* Multiple levels can be emitted in a single tick if price drops significantly.
|
|
1387
|
-
*
|
|
1388
|
-
* Consumers:
|
|
1389
|
-
* - PartialMarkdownService: Accumulates events for report generation
|
|
1390
|
-
* - User callbacks via listenPartialLoss() / listenPartialLossOnce()
|
|
1391
|
-
*
|
|
1392
|
-
* @example
|
|
1393
|
-
* ```typescript
|
|
1394
|
-
* import { listenPartialLoss } from "backtest-kit";
|
|
1395
|
-
*
|
|
1396
|
-
* // Listen to all partial loss events
|
|
1397
|
-
* listenPartialLoss((event) => {
|
|
1398
|
-
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached -${event.level}% loss`);
|
|
1399
|
-
* console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
|
|
1400
|
-
* console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
|
|
1401
|
-
*
|
|
1402
|
-
* // Alert on significant loss
|
|
1403
|
-
* if (event.level >= 30 && !event.backtest) {
|
|
1404
|
-
* console.warn("HIGH LOSS ALERT:", event.data.id);
|
|
1405
|
-
* }
|
|
1406
|
-
* });
|
|
1407
|
-
*
|
|
1408
|
-
* // Wait for first 20% loss level
|
|
1409
|
-
* listenPartialLossOnce(
|
|
1410
|
-
* (event) => event.level === 20,
|
|
1411
|
-
* (event) => console.log("20% loss reached:", event.data.id)
|
|
1412
|
-
* );
|
|
1413
|
-
* ```
|
|
1138
|
+
* Scheduled signal row with cancellation ID.
|
|
1139
|
+
* Extends IScheduledSignalRow to include optional cancelId for user-initiated cancellations.
|
|
1414
1140
|
*/
|
|
1415
|
-
interface
|
|
1416
|
-
/**
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1141
|
+
interface IScheduledSignalCancelRow extends IScheduledSignalRow {
|
|
1142
|
+
/** Cancellation ID (only for user-initiated cancellations) */
|
|
1143
|
+
cancelId?: string;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Optional lifecycle callbacks for signal events.
|
|
1147
|
+
* Called when signals are opened, active, idle, closed, scheduled, or cancelled.
|
|
1148
|
+
*/
|
|
1149
|
+
interface IStrategyCallbacks {
|
|
1150
|
+
/** Called on every tick with the result */
|
|
1151
|
+
onTick: (symbol: string, result: IStrategyTickResult, backtest: boolean) => void | Promise<void>;
|
|
1152
|
+
/** Called when new signal is opened (after validation) */
|
|
1153
|
+
onOpen: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
1154
|
+
/** Called when signal is being monitored (active state) */
|
|
1155
|
+
onActive: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
1156
|
+
/** Called when no active signal exists (idle state) */
|
|
1157
|
+
onIdle: (symbol: string, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
1158
|
+
/** Called when signal is closed with final price */
|
|
1159
|
+
onClose: (symbol: string, data: IPublicSignalRow, priceClose: number, backtest: boolean) => void | Promise<void>;
|
|
1160
|
+
/** Called when scheduled signal is created (delayed entry) */
|
|
1161
|
+
onSchedule: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
1162
|
+
/** Called when scheduled signal is cancelled without opening position */
|
|
1163
|
+
onCancel: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
1164
|
+
/** Called when signal is written to persist storage (for testing) */
|
|
1165
|
+
onWrite: (symbol: string, data: IPublicSignalRow | null, backtest: boolean) => void;
|
|
1166
|
+
/** Called when signal is in partial profit state (price moved favorably but not reached TP yet) */
|
|
1167
|
+
onPartialProfit: (symbol: string, data: IPublicSignalRow, currentPrice: number, revenuePercent: number, backtest: boolean) => void | Promise<void>;
|
|
1168
|
+
/** Called when signal is in partial loss state (price moved against position but not hit SL yet) */
|
|
1169
|
+
onPartialLoss: (symbol: string, data: IPublicSignalRow, currentPrice: number, lossPercent: number, backtest: boolean) => void | Promise<void>;
|
|
1170
|
+
/** Called when signal reaches breakeven (stop-loss moved to entry price to protect capital) */
|
|
1171
|
+
onBreakeven: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
1172
|
+
/** Called every minute regardless of strategy interval (for custom monitoring like checking if signal should be cancelled) */
|
|
1173
|
+
onPing: (symbol: string, data: IPublicSignalRow, when: Date, backtest: boolean) => void | Promise<void>;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Strategy schema registered via addStrategy().
|
|
1177
|
+
* Defines signal generation logic and configuration.
|
|
1178
|
+
*/
|
|
1179
|
+
interface IStrategySchema {
|
|
1180
|
+
/** Unique strategy identifier for registration */
|
|
1425
1181
|
strategyName: StrategyName;
|
|
1182
|
+
/** Optional developer note for documentation */
|
|
1183
|
+
note?: string;
|
|
1184
|
+
/** Minimum interval between getSignal calls (throttling) */
|
|
1185
|
+
interval: SignalInterval;
|
|
1426
1186
|
/**
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1187
|
+
* Signal generation function (returns null if no signal, validated DTO if signal).
|
|
1188
|
+
* If priceOpen is provided - becomes scheduled signal waiting for price to reach entry point.
|
|
1189
|
+
* If priceOpen is omitted - opens immediately at current price.
|
|
1429
1190
|
*/
|
|
1191
|
+
getSignal: (symbol: string, when: Date) => Promise<ISignalDto | null>;
|
|
1192
|
+
/** Optional lifecycle event callbacks (onOpen, onClose) */
|
|
1193
|
+
callbacks?: Partial<IStrategyCallbacks>;
|
|
1194
|
+
/** Optional risk profile identifier for risk management */
|
|
1195
|
+
riskName?: RiskName;
|
|
1196
|
+
/** Optional several risk profile list for risk management (if multiple required) */
|
|
1197
|
+
riskList?: RiskName[];
|
|
1198
|
+
/** Optional list of action identifiers to attach to this strategy */
|
|
1199
|
+
actions?: ActionName[];
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Reason why signal was closed.
|
|
1203
|
+
* Used in discriminated union for type-safe handling.
|
|
1204
|
+
*/
|
|
1205
|
+
type StrategyCloseReason = "time_expired" | "take_profit" | "stop_loss";
|
|
1206
|
+
/**
|
|
1207
|
+
* Reason why scheduled signal was cancelled.
|
|
1208
|
+
* Used in discriminated union for type-safe handling.
|
|
1209
|
+
*/
|
|
1210
|
+
type StrategyCancelReason = "timeout" | "price_reject" | "user";
|
|
1211
|
+
/**
|
|
1212
|
+
* Profit and loss calculation result.
|
|
1213
|
+
* Includes adjusted prices with fees (0.1%) and slippage (0.1%).
|
|
1214
|
+
*/
|
|
1215
|
+
interface IStrategyPnL {
|
|
1216
|
+
/** Profit/loss as percentage (e.g., 1.5 for +1.5%, -2.3 for -2.3%) */
|
|
1217
|
+
pnlPercentage: number;
|
|
1218
|
+
/** Entry price adjusted with slippage and fees */
|
|
1219
|
+
priceOpen: number;
|
|
1220
|
+
/** Exit price adjusted with slippage and fees */
|
|
1221
|
+
priceClose: number;
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Tick result: no active signal, idle state.
|
|
1225
|
+
*/
|
|
1226
|
+
interface IStrategyTickResultIdle {
|
|
1227
|
+
/** Discriminator for type-safe union */
|
|
1228
|
+
action: "idle";
|
|
1229
|
+
/** No signal in idle state */
|
|
1230
|
+
signal: null;
|
|
1231
|
+
/** Strategy name for tracking idle events */
|
|
1232
|
+
strategyName: StrategyName;
|
|
1233
|
+
/** Exchange name for tracking idle events */
|
|
1430
1234
|
exchangeName: ExchangeName;
|
|
1431
|
-
/**
|
|
1432
|
-
* Frame name where this signal is being executed.
|
|
1433
|
-
* Identifies which frame this loss event belongs to (empty string for live mode).
|
|
1434
|
-
*/
|
|
1235
|
+
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
1435
1236
|
frameName: FrameName;
|
|
1436
|
-
/**
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
*/
|
|
1440
|
-
data: IPublicSignalRow;
|
|
1441
|
-
/**
|
|
1442
|
-
* Current market price at which this loss level was reached.
|
|
1443
|
-
* Used to calculate actual loss percentage.
|
|
1444
|
-
*/
|
|
1237
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
1238
|
+
symbol: string;
|
|
1239
|
+
/** Current VWAP price during idle state */
|
|
1445
1240
|
currentPrice: number;
|
|
1446
|
-
/**
|
|
1447
|
-
* Loss level milestone reached (10, 20, 30, 40, 50, 60, 70, 80, 90, or 100).
|
|
1448
|
-
* Represents percentage loss relative to entry price (absolute value).
|
|
1449
|
-
*
|
|
1450
|
-
* Note: Stored as positive number, but represents negative loss.
|
|
1451
|
-
* level=20 means -20% loss from entry price.
|
|
1452
|
-
*
|
|
1453
|
-
* @example
|
|
1454
|
-
* ```typescript
|
|
1455
|
-
* // If entry was $50000 and level is 20:
|
|
1456
|
-
* // currentPrice <= $40000 (-20% loss)
|
|
1457
|
-
* // Level is stored as 20, not -20
|
|
1458
|
-
* ```
|
|
1459
|
-
*/
|
|
1460
|
-
level: PartialLevel;
|
|
1461
|
-
/**
|
|
1462
|
-
* Execution mode flag.
|
|
1463
|
-
* - true: Event from backtest execution (historical candle data)
|
|
1464
|
-
* - false: Event from live trading (real-time tick)
|
|
1465
|
-
*/
|
|
1241
|
+
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
1466
1242
|
backtest: boolean;
|
|
1467
|
-
/**
|
|
1468
|
-
* Event timestamp in milliseconds since Unix epoch.
|
|
1469
|
-
*
|
|
1470
|
-
* Timing semantics:
|
|
1471
|
-
* - Live mode: when.getTime() at the moment loss level was detected
|
|
1472
|
-
* - Backtest mode: candle.timestamp of the candle that triggered the level
|
|
1473
|
-
*
|
|
1474
|
-
* @example
|
|
1475
|
-
* ```typescript
|
|
1476
|
-
* const eventDate = new Date(event.timestamp);
|
|
1477
|
-
* console.log(`Loss reached at: ${eventDate.toISOString()}`);
|
|
1478
|
-
*
|
|
1479
|
-
* // Calculate time in loss
|
|
1480
|
-
* const entryTime = event.data.pendingAt;
|
|
1481
|
-
* const timeInLoss = event.timestamp - entryTime;
|
|
1482
|
-
* console.log(`In loss for ${timeInLoss / 1000 / 60} minutes`);
|
|
1483
|
-
* ```
|
|
1484
|
-
*/
|
|
1485
|
-
timestamp: number;
|
|
1486
1243
|
}
|
|
1487
|
-
|
|
1488
1244
|
/**
|
|
1489
|
-
*
|
|
1490
|
-
*
|
|
1491
|
-
* Emitted by pingSubject every minute when a scheduled signal is being monitored.
|
|
1492
|
-
* Used for tracking scheduled signal lifecycle and custom monitoring logic.
|
|
1493
|
-
*
|
|
1494
|
-
* Events are emitted only when scheduled signal is active (not cancelled, not activated).
|
|
1495
|
-
* Allows users to implement custom cancellation logic via onPing callback.
|
|
1496
|
-
*
|
|
1497
|
-
* Consumers:
|
|
1498
|
-
* - User callbacks via listenPing() / listenPingOnce()
|
|
1499
|
-
*
|
|
1500
|
-
* @example
|
|
1501
|
-
* ```typescript
|
|
1502
|
-
* import { listenPing } from "backtest-kit";
|
|
1503
|
-
*
|
|
1504
|
-
* // Listen to all ping events
|
|
1505
|
-
* listenPing((event) => {
|
|
1506
|
-
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Ping for ${event.symbol}`);
|
|
1507
|
-
* console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
|
|
1508
|
-
* console.log(`Signal ID: ${event.data.id}, priceOpen: ${event.data.priceOpen}`);
|
|
1509
|
-
* console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
|
|
1510
|
-
* });
|
|
1511
|
-
*
|
|
1512
|
-
* // Wait for specific ping
|
|
1513
|
-
* listenPingOnce(
|
|
1514
|
-
* (event) => event.symbol === "BTCUSDT",
|
|
1515
|
-
* (event) => console.log("BTCUSDT ping received:", event.timestamp)
|
|
1516
|
-
* );
|
|
1517
|
-
* ```
|
|
1245
|
+
* Tick result: scheduled signal created, waiting for price to reach entry point.
|
|
1246
|
+
* Triggered when getSignal returns signal with priceOpen specified.
|
|
1518
1247
|
*/
|
|
1519
|
-
interface
|
|
1520
|
-
/**
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
/**
|
|
1526
|
-
* Strategy name that is monitoring this scheduled signal.
|
|
1527
|
-
* Identifies which strategy execution this ping event belongs to.
|
|
1528
|
-
*/
|
|
1248
|
+
interface IStrategyTickResultScheduled {
|
|
1249
|
+
/** Discriminator for type-safe union */
|
|
1250
|
+
action: "scheduled";
|
|
1251
|
+
/** Scheduled signal waiting for activation */
|
|
1252
|
+
signal: IPublicSignalRow;
|
|
1253
|
+
/** Strategy name for tracking */
|
|
1529
1254
|
strategyName: StrategyName;
|
|
1530
|
-
/**
|
|
1531
|
-
* Exchange name where this scheduled signal is being monitored.
|
|
1532
|
-
* Identifies which exchange this ping event belongs to.
|
|
1533
|
-
*/
|
|
1255
|
+
/** Exchange name for tracking */
|
|
1534
1256
|
exchangeName: ExchangeName;
|
|
1535
|
-
/**
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
* - true: Event from backtest execution (historical candle data)
|
|
1543
|
-
* - false: Event from live trading (real-time tick)
|
|
1544
|
-
*/
|
|
1257
|
+
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
1258
|
+
frameName: FrameName;
|
|
1259
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
1260
|
+
symbol: string;
|
|
1261
|
+
/** Current VWAP price when scheduled signal created */
|
|
1262
|
+
currentPrice: number;
|
|
1263
|
+
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
1545
1264
|
backtest: boolean;
|
|
1546
|
-
/**
|
|
1547
|
-
* Event timestamp in milliseconds since Unix epoch.
|
|
1548
|
-
*
|
|
1549
|
-
* Timing semantics:
|
|
1550
|
-
* - Live mode: when.getTime() at the moment of ping
|
|
1551
|
-
* - Backtest mode: candle.timestamp of the candle being processed
|
|
1552
|
-
*
|
|
1553
|
-
* @example
|
|
1554
|
-
* ```typescript
|
|
1555
|
-
* const eventDate = new Date(event.timestamp);
|
|
1556
|
-
* console.log(`Ping at: ${eventDate.toISOString()}`);
|
|
1557
|
-
* ```
|
|
1558
|
-
*/
|
|
1559
|
-
timestamp: number;
|
|
1560
1265
|
}
|
|
1561
|
-
|
|
1562
1266
|
/**
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1565
|
-
* Emitted by riskSubject ONLY when a signal is REJECTED due to risk validation failure.
|
|
1566
|
-
* Used for tracking actual risk violations and monitoring rejected signals.
|
|
1567
|
-
*
|
|
1568
|
-
* Events are emitted only when risk limits are violated (not for allowed signals).
|
|
1569
|
-
* This prevents spam and allows focusing on actual risk management interventions.
|
|
1570
|
-
*
|
|
1571
|
-
* Consumers:
|
|
1572
|
-
* - RiskMarkdownService: Accumulates rejection events for report generation
|
|
1573
|
-
* - User callbacks via listenRisk() / listenRiskOnce()
|
|
1574
|
-
*
|
|
1575
|
-
* @example
|
|
1576
|
-
* ```typescript
|
|
1577
|
-
* import { listenRisk } from "backtest-kit";
|
|
1578
|
-
*
|
|
1579
|
-
* // Listen to all risk rejection events
|
|
1580
|
-
* listenRisk((event) => {
|
|
1581
|
-
* console.log(`[RISK REJECTED] Signal for ${event.symbol}`);
|
|
1582
|
-
* console.log(`Strategy: ${event.strategyName}`);
|
|
1583
|
-
* console.log(`Active positions: ${event.activePositionCount}`);
|
|
1584
|
-
* console.log(`Price: ${event.currentPrice}`);
|
|
1585
|
-
* console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
|
|
1586
|
-
* });
|
|
1587
|
-
*
|
|
1588
|
-
* // Alert on risk rejections for specific symbol
|
|
1589
|
-
* listenRisk((event) => {
|
|
1590
|
-
* if (event.symbol === "BTCUSDT") {
|
|
1591
|
-
* console.warn("BTC signal rejected due to risk limits!");
|
|
1592
|
-
* }
|
|
1593
|
-
* });
|
|
1594
|
-
* ```
|
|
1267
|
+
* Tick result: new signal just created.
|
|
1268
|
+
* Triggered after getSignal validation and persistence.
|
|
1595
1269
|
*/
|
|
1596
|
-
interface
|
|
1597
|
-
/**
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1270
|
+
interface IStrategyTickResultOpened {
|
|
1271
|
+
/** Discriminator for type-safe union */
|
|
1272
|
+
action: "opened";
|
|
1273
|
+
/** Newly created and validated signal with generated ID */
|
|
1274
|
+
signal: IPublicSignalRow;
|
|
1275
|
+
/** Strategy name for tracking */
|
|
1276
|
+
strategyName: StrategyName;
|
|
1277
|
+
/** Exchange name for tracking */
|
|
1278
|
+
exchangeName: ExchangeName;
|
|
1279
|
+
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
1280
|
+
frameName: FrameName;
|
|
1281
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
1601
1282
|
symbol: string;
|
|
1602
|
-
/**
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1283
|
+
/** Current VWAP price at signal open */
|
|
1284
|
+
currentPrice: number;
|
|
1285
|
+
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
1286
|
+
backtest: boolean;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Tick result: signal is being monitored.
|
|
1290
|
+
* Waiting for TP/SL or time expiration.
|
|
1291
|
+
*/
|
|
1292
|
+
interface IStrategyTickResultActive {
|
|
1293
|
+
/** Discriminator for type-safe union */
|
|
1294
|
+
action: "active";
|
|
1295
|
+
/** Currently monitored signal */
|
|
1296
|
+
signal: IPublicSignalRow;
|
|
1297
|
+
/** Current VWAP price for monitoring */
|
|
1298
|
+
currentPrice: number;
|
|
1299
|
+
/** Strategy name for tracking */
|
|
1611
1300
|
strategyName: StrategyName;
|
|
1612
|
-
/**
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
*/
|
|
1301
|
+
/** Exchange name for tracking */
|
|
1302
|
+
exchangeName: ExchangeName;
|
|
1303
|
+
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
1616
1304
|
frameName: FrameName;
|
|
1617
|
-
/**
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1305
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
1306
|
+
symbol: string;
|
|
1307
|
+
/** Percentage progress towards take profit (0-100%, 0 if moving towards SL) */
|
|
1308
|
+
percentTp: number;
|
|
1309
|
+
/** Percentage progress towards stop loss (0-100%, 0 if moving towards TP) */
|
|
1310
|
+
percentSl: number;
|
|
1311
|
+
/** Unrealized PNL for active position with fees, slippage, and partial closes */
|
|
1312
|
+
pnl: IStrategyPnL;
|
|
1313
|
+
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
1314
|
+
backtest: boolean;
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Tick result: signal closed with PNL.
|
|
1318
|
+
* Final state with close reason and profit/loss calculation.
|
|
1319
|
+
*/
|
|
1320
|
+
interface IStrategyTickResultClosed {
|
|
1321
|
+
/** Discriminator for type-safe union */
|
|
1322
|
+
action: "closed";
|
|
1323
|
+
/** Completed signal with original parameters */
|
|
1324
|
+
signal: IPublicSignalRow;
|
|
1325
|
+
/** Final VWAP price at close */
|
|
1326
|
+
currentPrice: number;
|
|
1327
|
+
/** Why signal closed (time_expired | take_profit | stop_loss) */
|
|
1328
|
+
closeReason: StrategyCloseReason;
|
|
1329
|
+
/** Unix timestamp in milliseconds when signal closed */
|
|
1330
|
+
closeTimestamp: number;
|
|
1331
|
+
/** Profit/loss calculation with fees and slippage */
|
|
1332
|
+
pnl: IStrategyPnL;
|
|
1333
|
+
/** Strategy name for tracking */
|
|
1334
|
+
strategyName: StrategyName;
|
|
1335
|
+
/** Exchange name for tracking */
|
|
1621
1336
|
exchangeName: ExchangeName;
|
|
1622
|
-
/**
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1337
|
+
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
1338
|
+
frameName: FrameName;
|
|
1339
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
1340
|
+
symbol: string;
|
|
1341
|
+
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
1342
|
+
backtest: boolean;
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Tick result: scheduled signal cancelled without opening position.
|
|
1346
|
+
* Occurs when scheduled signal doesn't activate or hits stop loss before entry.
|
|
1347
|
+
*/
|
|
1348
|
+
interface IStrategyTickResultCancelled {
|
|
1349
|
+
/** Discriminator for type-safe union */
|
|
1350
|
+
action: "cancelled";
|
|
1351
|
+
/** Cancelled scheduled signal */
|
|
1352
|
+
signal: IPublicSignalRow;
|
|
1353
|
+
/** Final VWAP price at cancellation */
|
|
1626
1354
|
currentPrice: number;
|
|
1355
|
+
/** Unix timestamp in milliseconds when signal cancelled */
|
|
1356
|
+
closeTimestamp: number;
|
|
1357
|
+
/** Strategy name for tracking */
|
|
1358
|
+
strategyName: StrategyName;
|
|
1359
|
+
/** Exchange name for tracking */
|
|
1360
|
+
exchangeName: ExchangeName;
|
|
1361
|
+
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
1362
|
+
frameName: FrameName;
|
|
1363
|
+
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
1364
|
+
symbol: string;
|
|
1365
|
+
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
1366
|
+
backtest: boolean;
|
|
1367
|
+
/** Reason for cancellation */
|
|
1368
|
+
reason: StrategyCancelReason;
|
|
1369
|
+
/** Optional cancellation ID (provided when user calls Backtest.cancel() or Live.cancel()) */
|
|
1370
|
+
cancelId?: string;
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Discriminated union of all tick results.
|
|
1374
|
+
* Use type guards: `result.action === "closed"` for type safety.
|
|
1375
|
+
*/
|
|
1376
|
+
type IStrategyTickResult = IStrategyTickResultIdle | IStrategyTickResultScheduled | IStrategyTickResultOpened | IStrategyTickResultActive | IStrategyTickResultClosed | IStrategyTickResultCancelled;
|
|
1377
|
+
/**
|
|
1378
|
+
* Backtest returns closed result (TP/SL or time_expired) or cancelled result (scheduled signal never activated).
|
|
1379
|
+
*/
|
|
1380
|
+
type IStrategyBacktestResult = IStrategyTickResultClosed | IStrategyTickResultCancelled;
|
|
1381
|
+
/**
|
|
1382
|
+
* Strategy interface implemented by ClientStrategy.
|
|
1383
|
+
* Defines core strategy execution methods.
|
|
1384
|
+
*/
|
|
1385
|
+
interface IStrategy {
|
|
1627
1386
|
/**
|
|
1628
|
-
*
|
|
1629
|
-
*
|
|
1387
|
+
* Single tick of strategy execution with VWAP monitoring.
|
|
1388
|
+
* Checks for signal generation (throttled) and TP/SL conditions.
|
|
1389
|
+
*
|
|
1390
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1391
|
+
* @param strategyName - Name of the strategy
|
|
1392
|
+
* @returns Promise resolving to tick result (idle | opened | active | closed)
|
|
1630
1393
|
*/
|
|
1631
|
-
|
|
1394
|
+
tick: (symbol: string, strategyName: StrategyName) => Promise<IStrategyTickResult>;
|
|
1632
1395
|
/**
|
|
1633
|
-
*
|
|
1634
|
-
*
|
|
1635
|
-
*
|
|
1396
|
+
* Retrieves the currently active pending signal for the symbol.
|
|
1397
|
+
* If no active signal exists, returns null.
|
|
1398
|
+
* Used internally for monitoring TP/SL and time expiration.
|
|
1399
|
+
*
|
|
1400
|
+
* @param symbol - Trading pair symbol
|
|
1401
|
+
* @returns Promise resolving to pending signal or null
|
|
1636
1402
|
*/
|
|
1637
|
-
|
|
1403
|
+
getPendingSignal: (symbol: string) => Promise<IPublicSignalRow | null>;
|
|
1638
1404
|
/**
|
|
1639
|
-
*
|
|
1640
|
-
*
|
|
1405
|
+
* Retrieves the currently active scheduled signal for the symbol.
|
|
1406
|
+
* If no scheduled signal exists, returns null.
|
|
1407
|
+
* Used internally for monitoring scheduled signal activation.
|
|
1641
1408
|
*
|
|
1642
|
-
* @
|
|
1643
|
-
*
|
|
1644
|
-
* console.log(`Rejection reason: ${event.rejectionNote}`);
|
|
1645
|
-
* // Output: "Rejection reason: Max 3 positions allowed"
|
|
1646
|
-
* ```
|
|
1409
|
+
* @param symbol - Trading pair symbol
|
|
1410
|
+
* @returns Promise resolving to scheduled signal or null
|
|
1647
1411
|
*/
|
|
1648
|
-
|
|
1412
|
+
getScheduledSignal: (symbol: string) => Promise<IPublicSignalRow | null>;
|
|
1649
1413
|
/**
|
|
1650
|
-
*
|
|
1651
|
-
*
|
|
1414
|
+
* Checks if breakeven threshold has been reached for the current pending signal.
|
|
1415
|
+
*
|
|
1416
|
+
* Uses the same formula as BREAKEVEN_FN to determine if price has moved far enough
|
|
1417
|
+
* to cover transaction costs (slippage + fees) and allow breakeven to be set.
|
|
1418
|
+
* Threshold: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2 transactions
|
|
1419
|
+
*
|
|
1420
|
+
* For LONG position:
|
|
1421
|
+
* - Returns true when: currentPrice >= priceOpen * (1 + threshold%)
|
|
1422
|
+
* - Example: entry=100, threshold=0.4% → true when price >= 100.4
|
|
1423
|
+
*
|
|
1424
|
+
* For SHORT position:
|
|
1425
|
+
* - Returns true when: currentPrice <= priceOpen * (1 - threshold%)
|
|
1426
|
+
* - Example: entry=100, threshold=0.4% → true when price <= 99.6
|
|
1427
|
+
*
|
|
1428
|
+
* Special cases:
|
|
1429
|
+
* - Returns false if no pending signal exists
|
|
1430
|
+
* - Returns true if trailing stop is already in profit zone (breakeven already achieved)
|
|
1431
|
+
* - Returns false if threshold not reached yet
|
|
1432
|
+
*
|
|
1433
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1434
|
+
* @param currentPrice - Current market price to check against threshold
|
|
1435
|
+
* @returns Promise<boolean> - true if breakeven threshold reached, false otherwise
|
|
1652
1436
|
*
|
|
1653
1437
|
* @example
|
|
1654
1438
|
* ```typescript
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
1439
|
+
* // Check if breakeven is available for LONG position (entry=100, threshold=0.4%)
|
|
1440
|
+
* const canBreakeven = await strategy.getBreakeven("BTCUSDT", 100.5);
|
|
1441
|
+
* // Returns true (price >= 100.4)
|
|
1442
|
+
*
|
|
1443
|
+
* if (canBreakeven) {
|
|
1444
|
+
* await strategy.breakeven("BTCUSDT", 100.5, false);
|
|
1445
|
+
* }
|
|
1657
1446
|
* ```
|
|
1658
1447
|
*/
|
|
1659
|
-
|
|
1448
|
+
getBreakeven: (symbol: string, currentPrice: number) => Promise<boolean>;
|
|
1660
1449
|
/**
|
|
1661
|
-
*
|
|
1662
|
-
*
|
|
1450
|
+
* Checks if the strategy has been stopped.
|
|
1451
|
+
*
|
|
1452
|
+
* Returns the stopped state indicating whether the strategy should
|
|
1453
|
+
* cease processing new ticks or signals.
|
|
1454
|
+
*
|
|
1455
|
+
* @param symbol - Trading pair symbol
|
|
1456
|
+
* @returns Promise resolving to true if strategy is stopped, false otherwise
|
|
1663
1457
|
*/
|
|
1664
|
-
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
/**
|
|
1668
|
-
* Constructor type for action handlers with strategy context.
|
|
1669
|
-
*
|
|
1670
|
-
* @param strategyName - Strategy identifier (e.g., "rsi_divergence", "macd_cross")
|
|
1671
|
-
* @param frameName - Timeframe identifier (e.g., "1m", "5m", "1h")
|
|
1672
|
-
* @param backtest - True for backtest mode, false for live trading
|
|
1673
|
-
* @returns Partial implementation of IAction (only required handlers)
|
|
1674
|
-
*
|
|
1675
|
-
* @example
|
|
1676
|
-
* ```typescript
|
|
1677
|
-
* class TelegramNotifier implements Partial<IAction> {
|
|
1678
|
-
* constructor(
|
|
1679
|
-
* private strategyName: StrategyName,
|
|
1680
|
-
* private frameName: FrameName,
|
|
1681
|
-
* private backtest: boolean
|
|
1682
|
-
* ) {}
|
|
1683
|
-
*
|
|
1684
|
-
* signal(event: IStrategyTickResult): void {
|
|
1685
|
-
* if (!this.backtest && event.state === 'opened') {
|
|
1686
|
-
* telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
|
|
1687
|
-
* }
|
|
1688
|
-
* }
|
|
1689
|
-
* }
|
|
1690
|
-
*
|
|
1691
|
-
* const actionCtors: TActionCtor[] = [TelegramNotifier, ReduxLogger];
|
|
1692
|
-
* ```
|
|
1693
|
-
*/
|
|
1694
|
-
type TActionCtor = new (strategyName: StrategyName, frameName: FrameName, actionName: ActionName) => Partial<IPublicAction>;
|
|
1695
|
-
/**
|
|
1696
|
-
* Action parameters passed to ClientAction constructor.
|
|
1697
|
-
* Combines schema with runtime dependencies and execution context.
|
|
1698
|
-
*
|
|
1699
|
-
* Extended from IActionSchema with:
|
|
1700
|
-
* - Logger instance for debugging and monitoring
|
|
1701
|
-
* - Strategy context (strategyName, frameName)
|
|
1702
|
-
* - Runtime environment flags
|
|
1703
|
-
*
|
|
1704
|
-
* @example
|
|
1705
|
-
* ```typescript
|
|
1706
|
-
* const params: IActionParams = {
|
|
1707
|
-
* actionName: "telegram-notifier",
|
|
1708
|
-
* handler: TelegramNotifier,
|
|
1709
|
-
* callbacks: { onInit, onDispose, onSignal },
|
|
1710
|
-
* logger: loggerService,
|
|
1711
|
-
* strategyName: "rsi_divergence",
|
|
1712
|
-
* frameName: "1h"
|
|
1713
|
-
* };
|
|
1714
|
-
*
|
|
1715
|
-
* const actionClient = new ClientAction(params);
|
|
1716
|
-
* ```
|
|
1717
|
-
*/
|
|
1718
|
-
interface IActionParams extends IActionSchema {
|
|
1719
|
-
/** Logger service for debugging and monitoring action execution */
|
|
1720
|
-
logger: ILogger;
|
|
1721
|
-
/** Strategy identifier this action is attached to */
|
|
1722
|
-
strategyName: StrategyName;
|
|
1723
|
-
/** Exchange name (e.g., "binance") */
|
|
1724
|
-
exchangeName: ExchangeName;
|
|
1725
|
-
/** Timeframe identifier this action is attached to */
|
|
1726
|
-
frameName: FrameName;
|
|
1727
|
-
/** Whether running in backtest mode */
|
|
1728
|
-
backtest: boolean;
|
|
1729
|
-
}
|
|
1730
|
-
/**
|
|
1731
|
-
* Lifecycle and event callbacks for action handlers.
|
|
1732
|
-
*
|
|
1733
|
-
* Provides hooks for initialization, disposal, and event handling.
|
|
1734
|
-
* All callbacks are optional and support both sync and async execution.
|
|
1735
|
-
*
|
|
1736
|
-
* Use cases:
|
|
1737
|
-
* - Resource initialization (database connections, file handles)
|
|
1738
|
-
* - Resource cleanup (close connections, flush buffers)
|
|
1739
|
-
* - Event logging and monitoring
|
|
1740
|
-
* - State persistence
|
|
1741
|
-
*
|
|
1742
|
-
* @example
|
|
1743
|
-
* ```typescript
|
|
1744
|
-
* const callbacks: IActionCallbacks = {
|
|
1745
|
-
* onInit: async (strategyName, frameName, backtest) => {
|
|
1746
|
-
* console.log(`[${strategyName}/${frameName}] Action initialized (backtest=${backtest})`);
|
|
1747
|
-
* await db.connect();
|
|
1748
|
-
* },
|
|
1749
|
-
* onSignal: (event, strategyName, frameName, backtest) => {
|
|
1750
|
-
* if (event.action === 'opened') {
|
|
1751
|
-
* console.log(`New signal opened: ${event.signal.id}`);
|
|
1752
|
-
* }
|
|
1753
|
-
* },
|
|
1754
|
-
* onDispose: async (strategyName, frameName, backtest) => {
|
|
1755
|
-
* await db.disconnect();
|
|
1756
|
-
* console.log(`[${strategyName}/${frameName}] Action disposed`);
|
|
1757
|
-
* }
|
|
1758
|
-
* };
|
|
1759
|
-
* ```
|
|
1760
|
-
*/
|
|
1761
|
-
interface IActionCallbacks {
|
|
1458
|
+
getStopped: (symbol: string) => Promise<boolean>;
|
|
1762
1459
|
/**
|
|
1763
|
-
*
|
|
1460
|
+
* Fast backtest using historical candles.
|
|
1461
|
+
* Iterates through candles, calculates VWAP, checks TP/SL on each candle.
|
|
1764
1462
|
*
|
|
1765
|
-
*
|
|
1766
|
-
*
|
|
1767
|
-
* - Initializing external services
|
|
1768
|
-
* - Loading persisted state
|
|
1769
|
-
* - Setting up subscriptions
|
|
1463
|
+
* For scheduled signals: first monitors activation/cancellation,
|
|
1464
|
+
* then if activated continues with TP/SL monitoring.
|
|
1770
1465
|
*
|
|
1771
|
-
* @param
|
|
1772
|
-
* @param strategyName -
|
|
1773
|
-
* @param
|
|
1774
|
-
* @
|
|
1466
|
+
* @param symbol - Trading pair symbol
|
|
1467
|
+
* @param strategyName - Name of the strategy
|
|
1468
|
+
* @param candles - Array of historical candle data
|
|
1469
|
+
* @returns Promise resolving to closed result (always completes signal)
|
|
1775
1470
|
*/
|
|
1776
|
-
|
|
1471
|
+
backtest: (symbol: string, strategyName: StrategyName, candles: ICandleData[]) => Promise<IStrategyBacktestResult>;
|
|
1777
1472
|
/**
|
|
1778
|
-
*
|
|
1779
|
-
*
|
|
1780
|
-
* Use for:
|
|
1781
|
-
* - Closing database connections
|
|
1782
|
-
* - Flushing buffers
|
|
1783
|
-
* - Saving state to disk
|
|
1784
|
-
* - Unsubscribing from observables
|
|
1473
|
+
* Stops the strategy from generating new signals.
|
|
1785
1474
|
*
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1788
|
-
* @param frameName - Timeframe identifier
|
|
1789
|
-
* @param backtest - True for backtest mode, false for live trading
|
|
1790
|
-
*/
|
|
1791
|
-
onDispose(actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
1792
|
-
/**
|
|
1793
|
-
* Called on signal events from all modes (live + backtest).
|
|
1475
|
+
* Sets internal flag to prevent getSignal from being called on subsequent ticks.
|
|
1476
|
+
* Does NOT force-close active pending signals - they continue monitoring until natural closure (TP/SL/time_expired).
|
|
1794
1477
|
*
|
|
1795
|
-
*
|
|
1796
|
-
* Frequency: Every tick/candle when strategy is evaluated
|
|
1478
|
+
* Use case: Graceful shutdown in live trading mode without abandoning open positions.
|
|
1797
1479
|
*
|
|
1798
|
-
* @param
|
|
1799
|
-
* @
|
|
1800
|
-
* @param strategyName - Strategy identifier
|
|
1801
|
-
* @param frameName - Timeframe identifier
|
|
1802
|
-
* @param backtest - True for backtest mode, false for live trading
|
|
1803
|
-
*/
|
|
1804
|
-
onSignal(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
1805
|
-
/**
|
|
1806
|
-
* Called on signal events from live trading only.
|
|
1480
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1481
|
+
* @returns Promise that resolves immediately when stop flag is set
|
|
1807
1482
|
*
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1483
|
+
* @example
|
|
1484
|
+
* ```typescript
|
|
1485
|
+
* // Graceful shutdown in Live.background() cancellation
|
|
1486
|
+
* const cancel = await Live.background("BTCUSDT", { ... });
|
|
1810
1487
|
*
|
|
1811
|
-
*
|
|
1812
|
-
*
|
|
1813
|
-
*
|
|
1814
|
-
* @param frameName - Timeframe identifier
|
|
1815
|
-
* @param backtest - Always false (live mode only)
|
|
1488
|
+
* // Later: stop new signals, let existing ones close naturally
|
|
1489
|
+
* await cancel();
|
|
1490
|
+
* ```
|
|
1816
1491
|
*/
|
|
1817
|
-
|
|
1492
|
+
stop: (symbol: string, backtest: boolean) => Promise<void>;
|
|
1818
1493
|
/**
|
|
1819
|
-
*
|
|
1494
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
1820
1495
|
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1496
|
+
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
1497
|
+
* Does NOT affect active pending signals or strategy operation.
|
|
1498
|
+
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
1823
1499
|
*
|
|
1824
|
-
*
|
|
1825
|
-
* @param actionName - Action identifier
|
|
1826
|
-
* @param strategyName - Strategy identifier
|
|
1827
|
-
* @param frameName - Timeframe identifier
|
|
1828
|
-
* @param backtest - Always true (backtest mode only)
|
|
1829
|
-
*/
|
|
1830
|
-
onSignalBacktest(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
1831
|
-
/**
|
|
1832
|
-
* Called when breakeven is triggered (stop-loss moved to entry price).
|
|
1500
|
+
* Use case: Cancel a scheduled entry that is no longer desired without stopping the entire strategy.
|
|
1833
1501
|
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1502
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1503
|
+
* @param cancelId - Optional cancellation ID
|
|
1504
|
+
* @returns Promise that resolves when scheduled signal is cleared
|
|
1836
1505
|
*
|
|
1837
|
-
* @
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1506
|
+
* @example
|
|
1507
|
+
* ```typescript
|
|
1508
|
+
* // Cancel scheduled signal without stopping strategy
|
|
1509
|
+
* await strategy.cancel("BTCUSDT");
|
|
1510
|
+
* // Strategy continues, can generate new signals
|
|
1511
|
+
* ```
|
|
1842
1512
|
*/
|
|
1843
|
-
|
|
1513
|
+
cancel: (symbol: string, backtest: boolean, cancelId?: string) => Promise<void>;
|
|
1844
1514
|
/**
|
|
1845
|
-
*
|
|
1515
|
+
* Executes partial close at profit level (moving toward TP).
|
|
1846
1516
|
*
|
|
1847
|
-
*
|
|
1848
|
-
*
|
|
1517
|
+
* Closes specified percentage of position at current price.
|
|
1518
|
+
* Updates _tpClosed, _totalClosed, and _partialHistory state.
|
|
1519
|
+
* Persists updated signal state for crash recovery.
|
|
1849
1520
|
*
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
1852
|
-
*
|
|
1853
|
-
*
|
|
1854
|
-
*
|
|
1855
|
-
*/
|
|
1856
|
-
onPartialProfit(event: PartialProfitContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
1857
|
-
/**
|
|
1858
|
-
* Called when partial loss level is reached (-10%, -20%, -30%, etc).
|
|
1521
|
+
* Validations:
|
|
1522
|
+
* - Throws if no pending signal exists
|
|
1523
|
+
* - Throws if called on scheduled signal (not yet activated)
|
|
1524
|
+
* - Throws if percentToClose <= 0 or > 100
|
|
1525
|
+
* - Returns false if _totalClosed + percentToClose > 100 (prevents over-closing)
|
|
1859
1526
|
*
|
|
1860
|
-
*
|
|
1861
|
-
* Frequency: Once per loss level per signal (deduplicated)
|
|
1527
|
+
* Use case: User-controlled partial close triggered from onPartialProfit callback.
|
|
1862
1528
|
*
|
|
1863
|
-
* @param
|
|
1864
|
-
* @param
|
|
1865
|
-
* @param
|
|
1866
|
-
* @param
|
|
1867
|
-
* @
|
|
1529
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1530
|
+
* @param percentToClose - Absolute percentage of position to close (0-100)
|
|
1531
|
+
* @param currentPrice - Current market price for partial close
|
|
1532
|
+
* @param backtest - Whether running in backtest mode
|
|
1533
|
+
* @returns Promise<boolean> - true if partial close executed, false if skipped
|
|
1534
|
+
*
|
|
1535
|
+
* @example
|
|
1536
|
+
* ```typescript
|
|
1537
|
+
* callbacks: {
|
|
1538
|
+
* onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
|
|
1539
|
+
* if (percentTp >= 50) {
|
|
1540
|
+
* const success = await strategy.partialProfit(symbol, 25, currentPrice, backtest);
|
|
1541
|
+
* if (success) {
|
|
1542
|
+
* console.log('Partial profit executed');
|
|
1543
|
+
* }
|
|
1544
|
+
* }
|
|
1545
|
+
* }
|
|
1546
|
+
* }
|
|
1547
|
+
* ```
|
|
1868
1548
|
*/
|
|
1869
|
-
|
|
1549
|
+
partialProfit: (symbol: string, percentToClose: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
|
|
1870
1550
|
/**
|
|
1871
|
-
*
|
|
1551
|
+
* Executes partial close at loss level (moving toward SL).
|
|
1872
1552
|
*
|
|
1873
|
-
*
|
|
1874
|
-
*
|
|
1875
|
-
*
|
|
1876
|
-
* @param event - Scheduled signal monitoring data
|
|
1877
|
-
* @param actionName - Action identifier
|
|
1878
|
-
* @param strategyName - Strategy identifier
|
|
1879
|
-
* @param frameName - Timeframe identifier
|
|
1880
|
-
* @param backtest - True for backtest mode, false for live trading
|
|
1881
|
-
*/
|
|
1882
|
-
onPing(event: PingContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
1883
|
-
/**
|
|
1884
|
-
* Called when signal is rejected by risk management.
|
|
1885
|
-
*
|
|
1886
|
-
* Triggered by: RiskConnectionService via riskSubject
|
|
1887
|
-
* Frequency: Only when signal fails risk validation (not emitted for allowed signals)
|
|
1888
|
-
*
|
|
1889
|
-
* @param event - Risk rejection data with reason and context
|
|
1890
|
-
* @param actionName - Action identifier
|
|
1891
|
-
* @param strategyName - Strategy identifier
|
|
1892
|
-
* @param frameName - Timeframe identifier
|
|
1893
|
-
* @param backtest - True for backtest mode, false for live trading
|
|
1894
|
-
*/
|
|
1895
|
-
onRiskRejection(event: RiskContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
1896
|
-
}
|
|
1897
|
-
/**
|
|
1898
|
-
* Action schema registered via addAction().
|
|
1899
|
-
* Defines event handler implementation and lifecycle callbacks for state management integration.
|
|
1900
|
-
*
|
|
1901
|
-
* Actions provide a way to attach custom event handlers to strategies for:
|
|
1902
|
-
* - State management (Redux, Zustand, MobX)
|
|
1903
|
-
* - Event logging and monitoring
|
|
1904
|
-
* - Real-time notifications (Telegram, Discord, email)
|
|
1905
|
-
* - Analytics and metrics collection
|
|
1906
|
-
* - Custom business logic triggers
|
|
1907
|
-
*
|
|
1908
|
-
* Each action instance is created per strategy-frame pair and receives all events
|
|
1909
|
-
* emitted during strategy execution. Multiple actions can be attached to a single strategy.
|
|
1910
|
-
*
|
|
1911
|
-
* @example
|
|
1912
|
-
* ```typescript
|
|
1913
|
-
* import { addAction } from "backtest-kit";
|
|
1914
|
-
*
|
|
1915
|
-
* // Define action handler class
|
|
1916
|
-
* class TelegramNotifier implements Partial<IAction> {
|
|
1917
|
-
* constructor(
|
|
1918
|
-
* private strategyName: StrategyName,
|
|
1919
|
-
* private frameName: FrameName,
|
|
1920
|
-
* private backtest: boolean
|
|
1921
|
-
* ) {}
|
|
1922
|
-
*
|
|
1923
|
-
* signal(event: IStrategyTickResult): void {
|
|
1924
|
-
* if (!this.backtest && event.action === 'opened') {
|
|
1925
|
-
* telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
|
|
1926
|
-
* }
|
|
1927
|
-
* }
|
|
1928
|
-
*
|
|
1929
|
-
* dispose(): void {
|
|
1930
|
-
* telegram.close();
|
|
1931
|
-
* }
|
|
1932
|
-
* }
|
|
1933
|
-
*
|
|
1934
|
-
* // Register action schema
|
|
1935
|
-
* addAction({
|
|
1936
|
-
* actionName: "telegram-notifier",
|
|
1937
|
-
* handler: TelegramNotifier,
|
|
1938
|
-
* callbacks: {
|
|
1939
|
-
* onInit: async (strategyName, frameName, backtest) => {
|
|
1940
|
-
* console.log(`Telegram notifier initialized for ${strategyName}/${frameName}`);
|
|
1941
|
-
* },
|
|
1942
|
-
* onSignal: (event, strategyName, frameName, backtest) => {
|
|
1943
|
-
* console.log(`Signal event: ${event.action}`);
|
|
1944
|
-
* }
|
|
1945
|
-
* }
|
|
1946
|
-
* });
|
|
1947
|
-
* ```
|
|
1948
|
-
*/
|
|
1949
|
-
interface IActionSchema {
|
|
1950
|
-
/** Unique action identifier for registration */
|
|
1951
|
-
actionName: ActionName;
|
|
1952
|
-
/** Action handler constructor (instantiated per strategy-frame pair) */
|
|
1953
|
-
handler: TActionCtor | Partial<IPublicAction>;
|
|
1954
|
-
/** Optional lifecycle and event callbacks */
|
|
1955
|
-
callbacks?: Partial<IActionCallbacks>;
|
|
1956
|
-
}
|
|
1957
|
-
/**
|
|
1958
|
-
* Public action interface for custom action handler implementations.
|
|
1959
|
-
*
|
|
1960
|
-
* Extends IAction with an initialization lifecycle method.
|
|
1961
|
-
* Action handlers implement this interface to receive strategy events and perform custom logic.
|
|
1962
|
-
*
|
|
1963
|
-
* Lifecycle:
|
|
1964
|
-
* 1. Constructor called with (strategyName, frameName, actionName)
|
|
1965
|
-
* 2. init() called once for async initialization (setup connections, load resources)
|
|
1966
|
-
* 3. Event methods called as strategy executes (signal, breakeven, partialProfit, etc.)
|
|
1967
|
-
* 4. dispose() called once for cleanup (close connections, flush buffers)
|
|
1968
|
-
*
|
|
1969
|
-
* Key features:
|
|
1970
|
-
* - init() for async initialization (database connections, API clients, file handles)
|
|
1971
|
-
* - All IAction methods available for event handling
|
|
1972
|
-
* - dispose() guaranteed to run exactly once via singleshot pattern
|
|
1973
|
-
*
|
|
1974
|
-
* Common use cases:
|
|
1975
|
-
* - State management: Redux/Zustand store integration
|
|
1976
|
-
* - Notifications: Telegram/Discord/Email alerts
|
|
1977
|
-
* - Logging: Custom event tracking and monitoring
|
|
1978
|
-
* - Analytics: Metrics collection and reporting
|
|
1979
|
-
* - External systems: Database writes, API calls, file operations
|
|
1980
|
-
*
|
|
1981
|
-
* @example
|
|
1982
|
-
* ```typescript
|
|
1983
|
-
* class TelegramNotifier implements Partial<IPublicAction> {
|
|
1984
|
-
* private bot: TelegramBot | null = null;
|
|
1985
|
-
*
|
|
1986
|
-
* constructor(
|
|
1987
|
-
* private strategyName: string,
|
|
1988
|
-
* private frameName: string,
|
|
1989
|
-
* private actionName: string
|
|
1990
|
-
* ) {}
|
|
1991
|
-
*
|
|
1992
|
-
* // Called once during initialization
|
|
1993
|
-
* async init() {
|
|
1994
|
-
* this.bot = new TelegramBot(process.env.TELEGRAM_TOKEN);
|
|
1995
|
-
* await this.bot.connect();
|
|
1996
|
-
* }
|
|
1997
|
-
*
|
|
1998
|
-
* // Called on every signal event
|
|
1999
|
-
* async signal(event: IStrategyTickResult) {
|
|
2000
|
-
* if (event.action === 'opened') {
|
|
2001
|
-
* await this.bot.send(
|
|
2002
|
-
* `[${this.strategyName}/${this.frameName}] Signal opened: ${event.signal.side}`
|
|
2003
|
-
* );
|
|
2004
|
-
* }
|
|
2005
|
-
* }
|
|
2006
|
-
*
|
|
2007
|
-
* // Called once during cleanup
|
|
2008
|
-
* async dispose() {
|
|
2009
|
-
* await this.bot?.disconnect();
|
|
2010
|
-
* this.bot = null;
|
|
2011
|
-
* }
|
|
2012
|
-
* }
|
|
2013
|
-
* ```
|
|
2014
|
-
*
|
|
2015
|
-
* @see IAction for all available event methods
|
|
2016
|
-
* @see TActionCtor for constructor signature requirements
|
|
2017
|
-
* @see ClientAction for internal wrapper that manages lifecycle
|
|
2018
|
-
*/
|
|
2019
|
-
interface IPublicAction extends IAction {
|
|
2020
|
-
/**
|
|
2021
|
-
* Async initialization method called once after construction.
|
|
1553
|
+
* Closes specified percentage of position at current price.
|
|
1554
|
+
* Updates _slClosed, _totalClosed, and _partialHistory state.
|
|
1555
|
+
* Persists updated signal state for crash recovery.
|
|
2022
1556
|
*
|
|
2023
|
-
*
|
|
2024
|
-
* -
|
|
2025
|
-
* -
|
|
2026
|
-
* -
|
|
2027
|
-
* -
|
|
2028
|
-
* - Perform any async setup required before handling events
|
|
1557
|
+
* Validations:
|
|
1558
|
+
* - Throws if no pending signal exists
|
|
1559
|
+
* - Throws if called on scheduled signal (not yet activated)
|
|
1560
|
+
* - Throws if percentToClose <= 0 or > 100
|
|
1561
|
+
* - Returns false if _totalClosed + percentToClose > 100 (prevents over-closing)
|
|
2029
1562
|
*
|
|
2030
|
-
*
|
|
2031
|
-
* - Run exactly once per action handler instance
|
|
2032
|
-
* - Complete before any event methods are called
|
|
2033
|
-
* - Run after constructor but before first event
|
|
1563
|
+
* Use case: User-controlled partial close triggered from onPartialLoss callback.
|
|
2034
1564
|
*
|
|
2035
|
-
* @
|
|
2036
|
-
* @
|
|
1565
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1566
|
+
* @param percentToClose - Absolute percentage of position to close (0-100)
|
|
1567
|
+
* @param currentPrice - Current market price for partial close
|
|
1568
|
+
* @param backtest - Whether running in backtest mode
|
|
1569
|
+
* @returns Promise<boolean> - true if partial close executed, false if skipped
|
|
2037
1570
|
*
|
|
2038
1571
|
* @example
|
|
2039
1572
|
* ```typescript
|
|
2040
|
-
*
|
|
2041
|
-
*
|
|
2042
|
-
*
|
|
2043
|
-
*
|
|
2044
|
-
*
|
|
1573
|
+
* callbacks: {
|
|
1574
|
+
* onPartialLoss: async (symbol, signal, currentPrice, percentSl, backtest) => {
|
|
1575
|
+
* if (percentSl >= 80) {
|
|
1576
|
+
* const success = await strategy.partialLoss(symbol, 50, currentPrice, backtest);
|
|
1577
|
+
* if (success) {
|
|
1578
|
+
* console.log('Partial loss executed');
|
|
1579
|
+
* }
|
|
1580
|
+
* }
|
|
1581
|
+
* }
|
|
2045
1582
|
* }
|
|
2046
1583
|
* ```
|
|
2047
1584
|
*/
|
|
2048
|
-
|
|
2049
|
-
}
|
|
2050
|
-
/**
|
|
2051
|
-
* Action interface for state manager integration.
|
|
2052
|
-
*
|
|
2053
|
-
* Provides methods to handle all events emitted by connection services.
|
|
2054
|
-
* Each method corresponds to a specific event type emitted via .next() calls.
|
|
2055
|
-
*
|
|
2056
|
-
* Use this interface to implement custom state management logic:
|
|
2057
|
-
* - Redux/Zustand action dispatchers
|
|
2058
|
-
* - Event logging systems
|
|
2059
|
-
* - Real-time monitoring dashboards
|
|
2060
|
-
* - Analytics and metrics collection
|
|
2061
|
-
*
|
|
2062
|
-
* @example
|
|
2063
|
-
* ```typescript
|
|
2064
|
-
* class ReduxStateManager implements IAction {
|
|
2065
|
-
* constructor(private store: Store) {}
|
|
2066
|
-
*
|
|
2067
|
-
* signal(event: IStrategyTickResult): void {
|
|
2068
|
-
* this.store.dispatch({ type: 'SIGNAL', payload: event });
|
|
2069
|
-
* }
|
|
2070
|
-
*
|
|
2071
|
-
* breakeven(event: BreakevenContract): void {
|
|
2072
|
-
* this.store.dispatch({ type: 'BREAKEVEN', payload: event });
|
|
2073
|
-
* }
|
|
2074
|
-
*
|
|
2075
|
-
* // ... implement other methods
|
|
2076
|
-
* }
|
|
2077
|
-
* ```
|
|
2078
|
-
*/
|
|
2079
|
-
interface IAction {
|
|
1585
|
+
partialLoss: (symbol: string, percentToClose: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
|
|
2080
1586
|
/**
|
|
2081
|
-
*
|
|
2082
|
-
*
|
|
2083
|
-
* Emitted by: StrategyConnectionService via signalEmitter
|
|
2084
|
-
* Source: StrategyConnectionService.tick() and StrategyConnectionService.backtest()
|
|
2085
|
-
* Frequency: Every tick/candle when strategy is evaluated
|
|
1587
|
+
* Adjusts trailing stop-loss by shifting distance between entry and original SL.
|
|
2086
1588
|
*
|
|
2087
|
-
*
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
/**
|
|
2091
|
-
* Handles signal events from live trading only.
|
|
1589
|
+
* CRITICAL: Always calculates from ORIGINAL SL, not from current trailing SL.
|
|
1590
|
+
* This prevents error accumulation on repeated calls.
|
|
1591
|
+
* Larger percentShift ABSORBS smaller one (updates only towards better protection).
|
|
2092
1592
|
*
|
|
2093
|
-
*
|
|
2094
|
-
*
|
|
2095
|
-
*
|
|
1593
|
+
* Calculates new SL based on percentage shift of the ORIGINAL distance (entry - originalSL):
|
|
1594
|
+
* - Negative %: tightens stop (moves SL closer to entry, reduces risk)
|
|
1595
|
+
* - Positive %: loosens stop (moves SL away from entry, allows more drawdown)
|
|
2096
1596
|
*
|
|
2097
|
-
*
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
/**
|
|
2101
|
-
* Handles signal events from backtest only.
|
|
1597
|
+
* For LONG position (entry=100, originalSL=90, distance=10%):
|
|
1598
|
+
* - percentShift = -50: newSL = 100 - 10%*(1-0.5) = 95 (5% distance, tighter)
|
|
1599
|
+
* - percentShift = +20: newSL = 100 - 10%*(1+0.2) = 88 (12% distance, looser)
|
|
2102
1600
|
*
|
|
2103
|
-
*
|
|
2104
|
-
*
|
|
2105
|
-
*
|
|
1601
|
+
* For SHORT position (entry=100, originalSL=110, distance=10%):
|
|
1602
|
+
* - percentShift = -50: newSL = 100 + 10%*(1-0.5) = 105 (5% distance, tighter)
|
|
1603
|
+
* - percentShift = +20: newSL = 100 + 10%*(1+0.2) = 112 (12% distance, looser)
|
|
2106
1604
|
*
|
|
2107
|
-
*
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
*
|
|
1605
|
+
* Absorption behavior:
|
|
1606
|
+
* - First call: sets trailing SL unconditionally
|
|
1607
|
+
* - Subsequent calls: updates only if new SL is BETTER (protects more profit)
|
|
1608
|
+
* - For LONG: only accepts HIGHER SL (never moves down, closer to entry wins)
|
|
1609
|
+
* - For SHORT: only accepts LOWER SL (never moves up, closer to entry wins)
|
|
1610
|
+
* - Stores in _trailingPriceStopLoss, original priceStopLoss always preserved
|
|
2112
1611
|
*
|
|
2113
|
-
*
|
|
2114
|
-
*
|
|
2115
|
-
*
|
|
1612
|
+
* Validations:
|
|
1613
|
+
* - Throws if no pending signal exists
|
|
1614
|
+
* - Throws if percentShift < -100 or > 100
|
|
1615
|
+
* - Throws if percentShift === 0
|
|
1616
|
+
* - Skips if new SL would cross entry price
|
|
1617
|
+
* - Skips if currentPrice already crossed new SL level (price intrusion protection)
|
|
2116
1618
|
*
|
|
2117
|
-
*
|
|
2118
|
-
*/
|
|
2119
|
-
breakeven(event: BreakevenContract): void | Promise<void>;
|
|
2120
|
-
/**
|
|
2121
|
-
* Handles partial profit level events (10%, 20%, 30%, etc).
|
|
1619
|
+
* Use case: User-controlled trailing stop triggered from onPartialProfit callback.
|
|
2122
1620
|
*
|
|
2123
|
-
*
|
|
2124
|
-
*
|
|
2125
|
-
*
|
|
1621
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1622
|
+
* @param percentShift - Percentage shift of ORIGINAL SL distance [-100, 100], excluding 0
|
|
1623
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
1624
|
+
* @param backtest - Whether running in backtest mode
|
|
1625
|
+
* @returns Promise<boolean> - true if trailing SL was set/updated, false if rejected
|
|
2126
1626
|
*
|
|
2127
|
-
* @
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
*
|
|
1627
|
+
* @example
|
|
1628
|
+
* ```typescript
|
|
1629
|
+
* callbacks: {
|
|
1630
|
+
* onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
|
|
1631
|
+
* if (percentTp >= 50) {
|
|
1632
|
+
* // LONG: entry=100, originalSL=90, distance=10%
|
|
2132
1633
|
*
|
|
2133
|
-
*
|
|
2134
|
-
*
|
|
2135
|
-
*
|
|
1634
|
+
* // First call: tighten by 5%
|
|
1635
|
+
* const success1 = await strategy.trailingStop(symbol, -5, currentPrice, backtest);
|
|
1636
|
+
* // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
|
|
2136
1637
|
*
|
|
2137
|
-
*
|
|
1638
|
+
* // Second call: try weaker protection
|
|
1639
|
+
* const success2 = await strategy.trailingStop(symbol, -3, currentPrice, backtest);
|
|
1640
|
+
* // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
|
|
1641
|
+
*
|
|
1642
|
+
* // Third call: stronger protection
|
|
1643
|
+
* const success3 = await strategy.trailingStop(symbol, -7, currentPrice, backtest);
|
|
1644
|
+
* // success3 = true (ACCEPTED: newDistance = 3%, newSL = 97 > 95, better protection)
|
|
1645
|
+
* }
|
|
1646
|
+
* }
|
|
1647
|
+
* }
|
|
1648
|
+
* ```
|
|
2138
1649
|
*/
|
|
2139
|
-
|
|
1650
|
+
trailingStop: (symbol: string, percentShift: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
|
|
2140
1651
|
/**
|
|
2141
|
-
*
|
|
1652
|
+
* Adjusts the trailing take-profit distance for an active pending signal.
|
|
2142
1653
|
*
|
|
2143
|
-
*
|
|
2144
|
-
*
|
|
2145
|
-
*
|
|
1654
|
+
* CRITICAL: Always calculates from ORIGINAL TP, not from current trailing TP.
|
|
1655
|
+
* This prevents error accumulation on repeated calls.
|
|
1656
|
+
* Larger percentShift ABSORBS smaller one (updates only towards more conservative TP).
|
|
2146
1657
|
*
|
|
2147
|
-
*
|
|
1658
|
+
* Updates the take-profit distance by a percentage adjustment relative to the ORIGINAL TP distance.
|
|
1659
|
+
* Negative percentShift brings TP closer to entry (more conservative).
|
|
1660
|
+
* Positive percentShift moves TP further from entry (more aggressive).
|
|
1661
|
+
*
|
|
1662
|
+
* Absorption behavior:
|
|
1663
|
+
* - First call: sets trailing TP unconditionally
|
|
1664
|
+
* - Subsequent calls: updates only if new TP is MORE CONSERVATIVE (closer to entry)
|
|
1665
|
+
* - For LONG: only accepts LOWER TP (never moves up, closer to entry wins)
|
|
1666
|
+
* - For SHORT: only accepts HIGHER TP (never moves down, closer to entry wins)
|
|
1667
|
+
* - Stores in _trailingPriceTakeProfit, original priceTakeProfit always preserved
|
|
1668
|
+
*
|
|
1669
|
+
* Price intrusion protection: If current price has already crossed the new TP level,
|
|
1670
|
+
* the update is skipped to prevent immediate TP triggering.
|
|
1671
|
+
*
|
|
1672
|
+
* @param symbol - Trading pair symbol
|
|
1673
|
+
* @param percentShift - Percentage adjustment to ORIGINAL TP distance (-100 to 100)
|
|
1674
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
1675
|
+
* @param backtest - Whether running in backtest mode
|
|
1676
|
+
* @returns Promise<boolean> - true if trailing TP was set/updated, false if rejected
|
|
1677
|
+
*
|
|
1678
|
+
* @example
|
|
1679
|
+
* ```typescript
|
|
1680
|
+
* callbacks: {
|
|
1681
|
+
* onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
|
|
1682
|
+
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
1683
|
+
*
|
|
1684
|
+
* // First call: bring TP closer by 3%
|
|
1685
|
+
* const success1 = await strategy.trailingTake(symbol, -3, currentPrice, backtest);
|
|
1686
|
+
* // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
|
|
1687
|
+
*
|
|
1688
|
+
* // Second call: try to move TP further (less conservative)
|
|
1689
|
+
* const success2 = await strategy.trailingTake(symbol, 2, currentPrice, backtest);
|
|
1690
|
+
* // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
|
|
1691
|
+
*
|
|
1692
|
+
* // Third call: even more conservative
|
|
1693
|
+
* const success3 = await strategy.trailingTake(symbol, -5, currentPrice, backtest);
|
|
1694
|
+
* // success3 = true (ACCEPTED: newDistance = 5%, newTP = 105 < 107, more conservative)
|
|
1695
|
+
* }
|
|
1696
|
+
* }
|
|
1697
|
+
* ```
|
|
2148
1698
|
*/
|
|
2149
|
-
|
|
1699
|
+
trailingTake: (symbol: string, percentShift: number, currentPrice: number, backtest: boolean) => Promise<boolean>;
|
|
2150
1700
|
/**
|
|
2151
|
-
*
|
|
1701
|
+
* Moves stop-loss to breakeven (entry price) when price reaches threshold.
|
|
2152
1702
|
*
|
|
2153
|
-
*
|
|
2154
|
-
*
|
|
2155
|
-
*
|
|
1703
|
+
* Moves SL to entry price (zero-risk position) when current price has moved
|
|
1704
|
+
* far enough in profit direction to cover transaction costs (slippage + fees).
|
|
1705
|
+
* Threshold is calculated as: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2
|
|
2156
1706
|
*
|
|
2157
|
-
*
|
|
1707
|
+
* Behavior:
|
|
1708
|
+
* - Returns true if SL was moved to breakeven
|
|
1709
|
+
* - Returns false if conditions not met (threshold not reached or already at breakeven)
|
|
1710
|
+
* - Uses _trailingPriceStopLoss to store breakeven SL (preserves original priceStopLoss)
|
|
1711
|
+
* - Only moves SL once per position (idempotent - safe to call multiple times)
|
|
1712
|
+
*
|
|
1713
|
+
* For LONG position (entry=100, slippage=0.1%, fee=0.1%):
|
|
1714
|
+
* - Threshold: (0.1 + 0.1) * 2 = 0.4%
|
|
1715
|
+
* - Breakeven available when price >= 100.4 (entry + 0.4%)
|
|
1716
|
+
* - Moves SL from original (e.g. 95) to 100 (breakeven)
|
|
1717
|
+
* - Returns true on first successful move, false on subsequent calls
|
|
1718
|
+
*
|
|
1719
|
+
* For SHORT position (entry=100, slippage=0.1%, fee=0.1%):
|
|
1720
|
+
* - Threshold: (0.1 + 0.1) * 2 = 0.4%
|
|
1721
|
+
* - Breakeven available when price <= 99.6 (entry - 0.4%)
|
|
1722
|
+
* - Moves SL from original (e.g. 105) to 100 (breakeven)
|
|
1723
|
+
* - Returns true on first successful move, false on subsequent calls
|
|
1724
|
+
*
|
|
1725
|
+
* Validations:
|
|
1726
|
+
* - Throws if no pending signal exists
|
|
1727
|
+
* - Throws if currentPrice is not a positive finite number
|
|
1728
|
+
*
|
|
1729
|
+
* Use case: User-controlled breakeven protection triggered from onPartialProfit callback.
|
|
1730
|
+
*
|
|
1731
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
1732
|
+
* @param currentPrice - Current market price to check threshold
|
|
1733
|
+
* @param backtest - Whether running in backtest mode
|
|
1734
|
+
* @returns Promise<boolean> - true if breakeven was set, false if conditions not met
|
|
1735
|
+
*
|
|
1736
|
+
* @example
|
|
1737
|
+
* ```typescript
|
|
1738
|
+
* callbacks: {
|
|
1739
|
+
* onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
|
|
1740
|
+
* // Try to move SL to breakeven when threshold reached
|
|
1741
|
+
* const movedToBreakeven = await strategy.breakeven(symbol, currentPrice, backtest);
|
|
1742
|
+
* if (movedToBreakeven) {
|
|
1743
|
+
* console.log(`Position moved to breakeven at ${currentPrice}`);
|
|
1744
|
+
* }
|
|
1745
|
+
* }
|
|
1746
|
+
* }
|
|
1747
|
+
* ```
|
|
2158
1748
|
*/
|
|
2159
|
-
|
|
1749
|
+
breakeven: (symbol: string, currentPrice: number, backtest: boolean) => Promise<boolean>;
|
|
2160
1750
|
/**
|
|
2161
|
-
*
|
|
1751
|
+
* Disposes the strategy instance and cleans up resources.
|
|
2162
1752
|
*
|
|
2163
|
-
* Called
|
|
2164
|
-
*
|
|
1753
|
+
* Called when the strategy is being removed from cache or shut down.
|
|
1754
|
+
* Invokes the onDispose callback to notify external systems.
|
|
1755
|
+
*
|
|
1756
|
+
* @returns Promise that resolves when disposal is complete
|
|
2165
1757
|
*/
|
|
2166
|
-
dispose()
|
|
1758
|
+
dispose: () => Promise<void>;
|
|
2167
1759
|
}
|
|
2168
1760
|
/**
|
|
2169
|
-
* Unique
|
|
1761
|
+
* Unique strategy identifier.
|
|
2170
1762
|
*/
|
|
2171
|
-
type
|
|
1763
|
+
type StrategyName = string;
|
|
2172
1764
|
|
|
2173
1765
|
/**
|
|
2174
|
-
*
|
|
2175
|
-
*
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
*
|
|
2180
|
-
*
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
*
|
|
2200
|
-
*
|
|
1766
|
+
* Contract for breakeven events.
|
|
1767
|
+
*
|
|
1768
|
+
* Emitted by breakevenSubject when a signal's stop-loss is moved to breakeven (entry price).
|
|
1769
|
+
* Used for tracking risk reduction milestones and monitoring strategy safety.
|
|
1770
|
+
*
|
|
1771
|
+
* Events are emitted only once per signal (idempotent - protected by ClientBreakeven state).
|
|
1772
|
+
* Breakeven is triggered when price moves far enough in profit direction to cover transaction costs.
|
|
1773
|
+
*
|
|
1774
|
+
* Consumers:
|
|
1775
|
+
* - BreakevenMarkdownService: Accumulates events for report generation
|
|
1776
|
+
* - User callbacks via listenBreakeven() / listenBreakevenOnce()
|
|
1777
|
+
*
|
|
1778
|
+
* @example
|
|
1779
|
+
* ```typescript
|
|
1780
|
+
* import { listenBreakeven } from "backtest-kit";
|
|
1781
|
+
*
|
|
1782
|
+
* // Listen to all breakeven events
|
|
1783
|
+
* listenBreakeven((event) => {
|
|
1784
|
+
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} moved to breakeven`);
|
|
1785
|
+
* console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
|
|
1786
|
+
* console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
|
|
1787
|
+
* console.log(`Original SL: ${event.data.priceStopLoss}, New SL: ${event.data.priceOpen}`);
|
|
1788
|
+
* });
|
|
1789
|
+
*
|
|
1790
|
+
* // Wait for specific signal to reach breakeven
|
|
1791
|
+
* listenBreakevenOnce(
|
|
1792
|
+
* (event) => event.data.id === "target-signal-id",
|
|
1793
|
+
* (event) => console.log("Signal reached breakeven:", event.data.id)
|
|
1794
|
+
* );
|
|
1795
|
+
* ```
|
|
2201
1796
|
*/
|
|
2202
|
-
interface
|
|
2203
|
-
/**
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
1797
|
+
interface BreakevenContract {
|
|
1798
|
+
/**
|
|
1799
|
+
* Trading pair symbol (e.g., "BTCUSDT").
|
|
1800
|
+
* Identifies which market this breakeven event belongs to.
|
|
1801
|
+
*/
|
|
1802
|
+
symbol: string;
|
|
1803
|
+
/**
|
|
1804
|
+
* Strategy name that generated this signal.
|
|
1805
|
+
* Identifies which strategy execution this breakeven event belongs to.
|
|
1806
|
+
*/
|
|
2210
1807
|
strategyName: StrategyName;
|
|
2211
|
-
/**
|
|
1808
|
+
/**
|
|
1809
|
+
* Exchange name where this signal is being executed.
|
|
1810
|
+
* Identifies which exchange this breakeven event belongs to.
|
|
1811
|
+
*/
|
|
1812
|
+
exchangeName: ExchangeName;
|
|
1813
|
+
/**
|
|
1814
|
+
* Frame name where this signal is being executed.
|
|
1815
|
+
* Identifies which frame this breakeven event belongs to (empty string for live mode).
|
|
1816
|
+
*/
|
|
2212
1817
|
frameName: FrameName;
|
|
2213
|
-
/** Signal creation timestamp in milliseconds (when signal was first created/scheduled) */
|
|
2214
|
-
scheduledAt: number;
|
|
2215
|
-
/** Pending timestamp in milliseconds (when position became pending/active at priceOpen) */
|
|
2216
|
-
pendingAt: number;
|
|
2217
|
-
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
2218
|
-
symbol: string;
|
|
2219
|
-
/** Internal runtime marker for scheduled signals */
|
|
2220
|
-
_isScheduled: boolean;
|
|
2221
1818
|
/**
|
|
2222
|
-
*
|
|
2223
|
-
*
|
|
2224
|
-
* Used to calculate weighted PNL: Σ(percent_i × pnl_i) for each partial + (remaining% × final_pnl)
|
|
2225
|
-
*
|
|
2226
|
-
* Computed values (derived from this array):
|
|
2227
|
-
* - _tpClosed: Sum of all "profit" type partial close percentages
|
|
2228
|
-
* - _slClosed: Sum of all "loss" type partial close percentages
|
|
2229
|
-
* - _totalClosed: Sum of all partial close percentages (profit + loss)
|
|
1819
|
+
* Complete signal row data with original prices.
|
|
1820
|
+
* Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and totalExecuted.
|
|
2230
1821
|
*/
|
|
2231
|
-
|
|
2232
|
-
/** Type of partial close: profit (moving toward TP) or loss (moving toward SL) */
|
|
2233
|
-
type: "profit" | "loss";
|
|
2234
|
-
/** Percentage of position closed (0-100) */
|
|
2235
|
-
percent: number;
|
|
2236
|
-
/** Price at which this partial was executed */
|
|
2237
|
-
price: number;
|
|
2238
|
-
}>;
|
|
1822
|
+
data: IPublicSignalRow;
|
|
2239
1823
|
/**
|
|
2240
|
-
*
|
|
2241
|
-
*
|
|
2242
|
-
* - For LONG: moves upward as price moves toward TP (never moves down)
|
|
2243
|
-
* - For SHORT: moves downward as price moves toward TP (never moves up)
|
|
2244
|
-
* When _trailingPriceStopLoss is set, it replaces priceStopLoss for TP/SL checks.
|
|
2245
|
-
* Original priceStopLoss is preserved in persistence but ignored during execution.
|
|
1824
|
+
* Current market price at which breakeven was triggered.
|
|
1825
|
+
* Used to verify threshold calculation.
|
|
2246
1826
|
*/
|
|
2247
|
-
|
|
1827
|
+
currentPrice: number;
|
|
2248
1828
|
/**
|
|
2249
|
-
*
|
|
2250
|
-
*
|
|
2251
|
-
*
|
|
2252
|
-
* Updated by trailingTake() method based on position type and percentage distance.
|
|
2253
|
-
* - For LONG: can move upward (further) or downward (closer) from entry
|
|
2254
|
-
* - For SHORT: can move downward (further) or upward (closer) from entry
|
|
2255
|
-
* When _trailingPriceTakeProfit is set, it replaces priceTakeProfit for TP/SL checks.
|
|
2256
|
-
* Original priceTakeProfit is preserved in persistence but ignored during execution.
|
|
1829
|
+
* Execution mode flag.
|
|
1830
|
+
* - true: Event from backtest execution (historical candle data)
|
|
1831
|
+
* - false: Event from live trading (real-time tick)
|
|
2257
1832
|
*/
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
1833
|
+
backtest: boolean;
|
|
1834
|
+
/**
|
|
1835
|
+
* Event timestamp in milliseconds since Unix epoch.
|
|
1836
|
+
*
|
|
1837
|
+
* Timing semantics:
|
|
1838
|
+
* - Live mode: when.getTime() at the moment breakeven was set
|
|
1839
|
+
* - Backtest mode: candle.timestamp of the candle that triggered breakeven
|
|
1840
|
+
*
|
|
1841
|
+
* @example
|
|
1842
|
+
* ```typescript
|
|
1843
|
+
* const eventDate = new Date(event.timestamp);
|
|
1844
|
+
* console.log(`Breakeven set at: ${eventDate.toISOString()}`);
|
|
1845
|
+
* ```
|
|
1846
|
+
*/
|
|
1847
|
+
timestamp: number;
|
|
2269
1848
|
}
|
|
1849
|
+
|
|
2270
1850
|
/**
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2273
|
-
*
|
|
2274
|
-
*
|
|
2275
|
-
*
|
|
2276
|
-
*
|
|
2277
|
-
*
|
|
1851
|
+
* Contract for partial profit level events.
|
|
1852
|
+
*
|
|
1853
|
+
* Emitted by partialProfitSubject when a signal reaches a profit level milestone (10%, 20%, 30%, etc).
|
|
1854
|
+
* Used for tracking partial take-profit execution and monitoring strategy performance.
|
|
1855
|
+
*
|
|
1856
|
+
* Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
|
|
1857
|
+
* Multiple levels can be emitted in a single tick if price jumps significantly.
|
|
1858
|
+
*
|
|
1859
|
+
* Consumers:
|
|
1860
|
+
* - PartialMarkdownService: Accumulates events for report generation
|
|
1861
|
+
* - User callbacks via listenPartialProfit() / listenPartialProfitOnce()
|
|
1862
|
+
*
|
|
1863
|
+
* @example
|
|
1864
|
+
* ```typescript
|
|
1865
|
+
* import { listenPartialProfit } from "backtest-kit";
|
|
1866
|
+
*
|
|
1867
|
+
* // Listen to all partial profit events
|
|
1868
|
+
* listenPartialProfit((event) => {
|
|
1869
|
+
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached ${event.level}% profit`);
|
|
1870
|
+
* console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
|
|
1871
|
+
* console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
|
|
1872
|
+
* });
|
|
1873
|
+
*
|
|
1874
|
+
* // Wait for first 50% profit level
|
|
1875
|
+
* listenPartialProfitOnce(
|
|
1876
|
+
* (event) => event.level === 50,
|
|
1877
|
+
* (event) => console.log("50% profit reached:", event.data.id)
|
|
1878
|
+
* );
|
|
1879
|
+
* ```
|
|
2278
1880
|
*/
|
|
2279
|
-
interface
|
|
1881
|
+
interface PartialProfitContract {
|
|
2280
1882
|
/**
|
|
2281
|
-
*
|
|
2282
|
-
*
|
|
2283
|
-
* Used for user visibility of initial SL parameters.
|
|
1883
|
+
* Trading pair symbol (e.g., "BTCUSDT").
|
|
1884
|
+
* Identifies which market this profit event belongs to.
|
|
2284
1885
|
*/
|
|
2285
|
-
|
|
1886
|
+
symbol: string;
|
|
2286
1887
|
/**
|
|
2287
|
-
*
|
|
2288
|
-
*
|
|
2289
|
-
* Used for user visibility of initial TP parameters.
|
|
1888
|
+
* Strategy name that generated this signal.
|
|
1889
|
+
* Identifies which strategy execution this profit event belongs to.
|
|
2290
1890
|
*/
|
|
2291
|
-
|
|
1891
|
+
strategyName: StrategyName;
|
|
2292
1892
|
/**
|
|
2293
|
-
*
|
|
2294
|
-
*
|
|
2295
|
-
* Represents the total portion of the position that has been closed through partial executions.
|
|
2296
|
-
* Range: 0-100. Value of 0 means no partial closes, 100 means position fully closed through partials.
|
|
1893
|
+
* Exchange name where this signal is being executed.
|
|
1894
|
+
* Identifies which exchange this profit event belongs to.
|
|
2297
1895
|
*/
|
|
2298
|
-
|
|
2299
|
-
}
|
|
2300
|
-
/**
|
|
2301
|
-
* Risk signal row for internal risk management.
|
|
2302
|
-
* Extends ISignalDto to include priceOpen, originalPriceStopLoss and originalPriceTakeProfit.
|
|
2303
|
-
* Used in risk validation to access entry price and original SL/TP.
|
|
2304
|
-
*/
|
|
2305
|
-
interface IRiskSignalRow extends IPublicSignalRow {
|
|
1896
|
+
exchangeName: ExchangeName;
|
|
2306
1897
|
/**
|
|
2307
|
-
*
|
|
1898
|
+
* Frame name where this signal is being executed.
|
|
1899
|
+
* Identifies which frame this profit event belongs to (empty string for live mode).
|
|
2308
1900
|
*/
|
|
2309
|
-
|
|
1901
|
+
frameName: FrameName;
|
|
2310
1902
|
/**
|
|
2311
|
-
*
|
|
1903
|
+
* Complete signal row data with original prices.
|
|
1904
|
+
* Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and totalExecuted.
|
|
2312
1905
|
*/
|
|
2313
|
-
|
|
1906
|
+
data: IPublicSignalRow;
|
|
2314
1907
|
/**
|
|
2315
|
-
*
|
|
1908
|
+
* Current market price at which this profit level was reached.
|
|
1909
|
+
* Used to calculate actual profit percentage.
|
|
2316
1910
|
*/
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
1911
|
+
currentPrice: number;
|
|
1912
|
+
/**
|
|
1913
|
+
* Profit level milestone reached (10, 20, 30, 40, 50, 60, 70, 80, 90, or 100).
|
|
1914
|
+
* Represents percentage profit relative to entry price.
|
|
1915
|
+
*
|
|
1916
|
+
* @example
|
|
1917
|
+
* ```typescript
|
|
1918
|
+
* // If entry was $50000 and level is 20:
|
|
1919
|
+
* // currentPrice >= $60000 (20% profit)
|
|
1920
|
+
* ```
|
|
1921
|
+
*/
|
|
1922
|
+
level: PartialLevel;
|
|
1923
|
+
/**
|
|
1924
|
+
* Execution mode flag.
|
|
1925
|
+
* - true: Event from backtest execution (historical candle data)
|
|
1926
|
+
* - false: Event from live trading (real-time tick)
|
|
1927
|
+
*/
|
|
1928
|
+
backtest: boolean;
|
|
1929
|
+
/**
|
|
1930
|
+
* Event timestamp in milliseconds since Unix epoch.
|
|
1931
|
+
*
|
|
1932
|
+
* Timing semantics:
|
|
1933
|
+
* - Live mode: when.getTime() at the moment profit level was detected
|
|
1934
|
+
* - Backtest mode: candle.timestamp of the candle that triggered the level
|
|
1935
|
+
*
|
|
1936
|
+
* @example
|
|
1937
|
+
* ```typescript
|
|
1938
|
+
* const eventDate = new Date(event.timestamp);
|
|
1939
|
+
* console.log(`Profit reached at: ${eventDate.toISOString()}`);
|
|
1940
|
+
* ```
|
|
1941
|
+
*/
|
|
1942
|
+
timestamp: number;
|
|
2326
1943
|
}
|
|
1944
|
+
|
|
2327
1945
|
/**
|
|
2328
|
-
*
|
|
2329
|
-
*
|
|
1946
|
+
* Contract for partial loss level events.
|
|
1947
|
+
*
|
|
1948
|
+
* Emitted by partialLossSubject when a signal reaches a loss level milestone (-10%, -20%, -30%, etc).
|
|
1949
|
+
* Used for tracking partial stop-loss execution and monitoring strategy drawdown.
|
|
1950
|
+
*
|
|
1951
|
+
* Events are emitted only once per level per signal (Set-based deduplication in ClientPartial).
|
|
1952
|
+
* Multiple levels can be emitted in a single tick if price drops significantly.
|
|
1953
|
+
*
|
|
1954
|
+
* Consumers:
|
|
1955
|
+
* - PartialMarkdownService: Accumulates events for report generation
|
|
1956
|
+
* - User callbacks via listenPartialLoss() / listenPartialLossOnce()
|
|
1957
|
+
*
|
|
1958
|
+
* @example
|
|
1959
|
+
* ```typescript
|
|
1960
|
+
* import { listenPartialLoss } from "backtest-kit";
|
|
1961
|
+
*
|
|
1962
|
+
* // Listen to all partial loss events
|
|
1963
|
+
* listenPartialLoss((event) => {
|
|
1964
|
+
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Signal ${event.data.id} reached -${event.level}% loss`);
|
|
1965
|
+
* console.log(`Symbol: ${event.symbol}, Price: ${event.currentPrice}`);
|
|
1966
|
+
* console.log(`Position: ${event.data.position}, Entry: ${event.data.priceOpen}`);
|
|
1967
|
+
*
|
|
1968
|
+
* // Alert on significant loss
|
|
1969
|
+
* if (event.level >= 30 && !event.backtest) {
|
|
1970
|
+
* console.warn("HIGH LOSS ALERT:", event.data.id);
|
|
1971
|
+
* }
|
|
1972
|
+
* });
|
|
1973
|
+
*
|
|
1974
|
+
* // Wait for first 20% loss level
|
|
1975
|
+
* listenPartialLossOnce(
|
|
1976
|
+
* (event) => event.level === 20,
|
|
1977
|
+
* (event) => console.log("20% loss reached:", event.data.id)
|
|
1978
|
+
* );
|
|
1979
|
+
* ```
|
|
2330
1980
|
*/
|
|
2331
|
-
interface
|
|
2332
|
-
/** Called on every tick with the result */
|
|
2333
|
-
onTick: (symbol: string, result: IStrategyTickResult, backtest: boolean) => void | Promise<void>;
|
|
2334
|
-
/** Called when new signal is opened (after validation) */
|
|
2335
|
-
onOpen: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
2336
|
-
/** Called when signal is being monitored (active state) */
|
|
2337
|
-
onActive: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
2338
|
-
/** Called when no active signal exists (idle state) */
|
|
2339
|
-
onIdle: (symbol: string, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
2340
|
-
/** Called when signal is closed with final price */
|
|
2341
|
-
onClose: (symbol: string, data: IPublicSignalRow, priceClose: number, backtest: boolean) => void | Promise<void>;
|
|
2342
|
-
/** Called when scheduled signal is created (delayed entry) */
|
|
2343
|
-
onSchedule: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
2344
|
-
/** Called when scheduled signal is cancelled without opening position */
|
|
2345
|
-
onCancel: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
2346
|
-
/** Called when signal is written to persist storage (for testing) */
|
|
2347
|
-
onWrite: (symbol: string, data: IPublicSignalRow | null, backtest: boolean) => void;
|
|
2348
|
-
/** Called when signal is in partial profit state (price moved favorably but not reached TP yet) */
|
|
2349
|
-
onPartialProfit: (symbol: string, data: IPublicSignalRow, currentPrice: number, revenuePercent: number, backtest: boolean) => void | Promise<void>;
|
|
2350
|
-
/** Called when signal is in partial loss state (price moved against position but not hit SL yet) */
|
|
2351
|
-
onPartialLoss: (symbol: string, data: IPublicSignalRow, currentPrice: number, lossPercent: number, backtest: boolean) => void | Promise<void>;
|
|
2352
|
-
/** Called when signal reaches breakeven (stop-loss moved to entry price to protect capital) */
|
|
2353
|
-
onBreakeven: (symbol: string, data: IPublicSignalRow, currentPrice: number, backtest: boolean) => void | Promise<void>;
|
|
2354
|
-
/** Called every minute regardless of strategy interval (for custom monitoring like checking if signal should be cancelled) */
|
|
2355
|
-
onPing: (symbol: string, data: IPublicSignalRow, when: Date, backtest: boolean) => void | Promise<void>;
|
|
2356
|
-
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Strategy schema registered via addStrategy().
|
|
2359
|
-
* Defines signal generation logic and configuration.
|
|
2360
|
-
*/
|
|
2361
|
-
interface IStrategySchema {
|
|
2362
|
-
/** Unique strategy identifier for registration */
|
|
2363
|
-
strategyName: StrategyName;
|
|
2364
|
-
/** Optional developer note for documentation */
|
|
2365
|
-
note?: string;
|
|
2366
|
-
/** Minimum interval between getSignal calls (throttling) */
|
|
2367
|
-
interval: SignalInterval;
|
|
1981
|
+
interface PartialLossContract {
|
|
2368
1982
|
/**
|
|
2369
|
-
*
|
|
2370
|
-
*
|
|
2371
|
-
* If priceOpen is omitted - opens immediately at current price.
|
|
1983
|
+
* Trading pair symbol (e.g., "BTCUSDT").
|
|
1984
|
+
* Identifies which market this loss event belongs to.
|
|
2372
1985
|
*/
|
|
2373
|
-
getSignal: (symbol: string, when: Date) => Promise<ISignalDto | null>;
|
|
2374
|
-
/** Optional lifecycle event callbacks (onOpen, onClose) */
|
|
2375
|
-
callbacks?: Partial<IStrategyCallbacks>;
|
|
2376
|
-
/** Optional risk profile identifier for risk management */
|
|
2377
|
-
riskName?: RiskName;
|
|
2378
|
-
/** Optional several risk profile list for risk management (if multiple required) */
|
|
2379
|
-
riskList?: RiskName[];
|
|
2380
|
-
/** Optional list of action identifiers to attach to this strategy */
|
|
2381
|
-
actions?: ActionName[];
|
|
2382
|
-
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Reason why signal was closed.
|
|
2385
|
-
* Used in discriminated union for type-safe handling.
|
|
2386
|
-
*/
|
|
2387
|
-
type StrategyCloseReason = "time_expired" | "take_profit" | "stop_loss";
|
|
2388
|
-
/**
|
|
2389
|
-
* Reason why scheduled signal was cancelled.
|
|
2390
|
-
* Used in discriminated union for type-safe handling.
|
|
2391
|
-
*/
|
|
2392
|
-
type StrategyCancelReason = "timeout" | "price_reject" | "user";
|
|
2393
|
-
/**
|
|
2394
|
-
* Profit and loss calculation result.
|
|
2395
|
-
* Includes adjusted prices with fees (0.1%) and slippage (0.1%).
|
|
2396
|
-
*/
|
|
2397
|
-
interface IStrategyPnL {
|
|
2398
|
-
/** Profit/loss as percentage (e.g., 1.5 for +1.5%, -2.3 for -2.3%) */
|
|
2399
|
-
pnlPercentage: number;
|
|
2400
|
-
/** Entry price adjusted with slippage and fees */
|
|
2401
|
-
priceOpen: number;
|
|
2402
|
-
/** Exit price adjusted with slippage and fees */
|
|
2403
|
-
priceClose: number;
|
|
2404
|
-
}
|
|
2405
|
-
/**
|
|
2406
|
-
* Tick result: no active signal, idle state.
|
|
2407
|
-
*/
|
|
2408
|
-
interface IStrategyTickResultIdle {
|
|
2409
|
-
/** Discriminator for type-safe union */
|
|
2410
|
-
action: "idle";
|
|
2411
|
-
/** No signal in idle state */
|
|
2412
|
-
signal: null;
|
|
2413
|
-
/** Strategy name for tracking idle events */
|
|
2414
|
-
strategyName: StrategyName;
|
|
2415
|
-
/** Exchange name for tracking idle events */
|
|
2416
|
-
exchangeName: ExchangeName;
|
|
2417
|
-
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
2418
|
-
frameName: FrameName;
|
|
2419
|
-
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
2420
1986
|
symbol: string;
|
|
2421
|
-
/**
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
}
|
|
2426
|
-
/**
|
|
2427
|
-
* Tick result: scheduled signal created, waiting for price to reach entry point.
|
|
2428
|
-
* Triggered when getSignal returns signal with priceOpen specified.
|
|
2429
|
-
*/
|
|
2430
|
-
interface IStrategyTickResultScheduled {
|
|
2431
|
-
/** Discriminator for type-safe union */
|
|
2432
|
-
action: "scheduled";
|
|
2433
|
-
/** Scheduled signal waiting for activation */
|
|
2434
|
-
signal: IPublicSignalRow;
|
|
2435
|
-
/** Strategy name for tracking */
|
|
1987
|
+
/**
|
|
1988
|
+
* Strategy name that generated this signal.
|
|
1989
|
+
* Identifies which strategy execution this loss event belongs to.
|
|
1990
|
+
*/
|
|
2436
1991
|
strategyName: StrategyName;
|
|
2437
|
-
/**
|
|
1992
|
+
/**
|
|
1993
|
+
* Exchange name where this signal is being executed.
|
|
1994
|
+
* Identifies which exchange this loss event belongs to.
|
|
1995
|
+
*/
|
|
2438
1996
|
exchangeName: ExchangeName;
|
|
2439
|
-
/**
|
|
1997
|
+
/**
|
|
1998
|
+
* Frame name where this signal is being executed.
|
|
1999
|
+
* Identifies which frame this loss event belongs to (empty string for live mode).
|
|
2000
|
+
*/
|
|
2440
2001
|
frameName: FrameName;
|
|
2441
|
-
/**
|
|
2442
|
-
|
|
2443
|
-
|
|
2002
|
+
/**
|
|
2003
|
+
* Complete signal row data with original prices.
|
|
2004
|
+
* Contains all signal information including originalPriceStopLoss, originalPriceTakeProfit, and totalExecuted.
|
|
2005
|
+
*/
|
|
2006
|
+
data: IPublicSignalRow;
|
|
2007
|
+
/**
|
|
2008
|
+
* Current market price at which this loss level was reached.
|
|
2009
|
+
* Used to calculate actual loss percentage.
|
|
2010
|
+
*/
|
|
2444
2011
|
currentPrice: number;
|
|
2445
|
-
/**
|
|
2012
|
+
/**
|
|
2013
|
+
* Loss level milestone reached (10, 20, 30, 40, 50, 60, 70, 80, 90, or 100).
|
|
2014
|
+
* Represents percentage loss relative to entry price (absolute value).
|
|
2015
|
+
*
|
|
2016
|
+
* Note: Stored as positive number, but represents negative loss.
|
|
2017
|
+
* level=20 means -20% loss from entry price.
|
|
2018
|
+
*
|
|
2019
|
+
* @example
|
|
2020
|
+
* ```typescript
|
|
2021
|
+
* // If entry was $50000 and level is 20:
|
|
2022
|
+
* // currentPrice <= $40000 (-20% loss)
|
|
2023
|
+
* // Level is stored as 20, not -20
|
|
2024
|
+
* ```
|
|
2025
|
+
*/
|
|
2026
|
+
level: PartialLevel;
|
|
2027
|
+
/**
|
|
2028
|
+
* Execution mode flag.
|
|
2029
|
+
* - true: Event from backtest execution (historical candle data)
|
|
2030
|
+
* - false: Event from live trading (real-time tick)
|
|
2031
|
+
*/
|
|
2446
2032
|
backtest: boolean;
|
|
2033
|
+
/**
|
|
2034
|
+
* Event timestamp in milliseconds since Unix epoch.
|
|
2035
|
+
*
|
|
2036
|
+
* Timing semantics:
|
|
2037
|
+
* - Live mode: when.getTime() at the moment loss level was detected
|
|
2038
|
+
* - Backtest mode: candle.timestamp of the candle that triggered the level
|
|
2039
|
+
*
|
|
2040
|
+
* @example
|
|
2041
|
+
* ```typescript
|
|
2042
|
+
* const eventDate = new Date(event.timestamp);
|
|
2043
|
+
* console.log(`Loss reached at: ${eventDate.toISOString()}`);
|
|
2044
|
+
*
|
|
2045
|
+
* // Calculate time in loss
|
|
2046
|
+
* const entryTime = event.data.pendingAt;
|
|
2047
|
+
* const timeInLoss = event.timestamp - entryTime;
|
|
2048
|
+
* console.log(`In loss for ${timeInLoss / 1000 / 60} minutes`);
|
|
2049
|
+
* ```
|
|
2050
|
+
*/
|
|
2051
|
+
timestamp: number;
|
|
2447
2052
|
}
|
|
2053
|
+
|
|
2448
2054
|
/**
|
|
2449
|
-
*
|
|
2450
|
-
*
|
|
2055
|
+
* Contract for ping events during scheduled signal monitoring.
|
|
2056
|
+
*
|
|
2057
|
+
* Emitted by pingSubject every minute when a scheduled signal is being monitored.
|
|
2058
|
+
* Used for tracking scheduled signal lifecycle and custom monitoring logic.
|
|
2059
|
+
*
|
|
2060
|
+
* Events are emitted only when scheduled signal is active (not cancelled, not activated).
|
|
2061
|
+
* Allows users to implement custom cancellation logic via onPing callback.
|
|
2062
|
+
*
|
|
2063
|
+
* Consumers:
|
|
2064
|
+
* - User callbacks via listenPing() / listenPingOnce()
|
|
2065
|
+
*
|
|
2066
|
+
* @example
|
|
2067
|
+
* ```typescript
|
|
2068
|
+
* import { listenPing } from "backtest-kit";
|
|
2069
|
+
*
|
|
2070
|
+
* // Listen to all ping events
|
|
2071
|
+
* listenPing((event) => {
|
|
2072
|
+
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Ping for ${event.symbol}`);
|
|
2073
|
+
* console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
|
|
2074
|
+
* console.log(`Signal ID: ${event.data.id}, priceOpen: ${event.data.priceOpen}`);
|
|
2075
|
+
* console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
|
|
2076
|
+
* });
|
|
2077
|
+
*
|
|
2078
|
+
* // Wait for specific ping
|
|
2079
|
+
* listenPingOnce(
|
|
2080
|
+
* (event) => event.symbol === "BTCUSDT",
|
|
2081
|
+
* (event) => console.log("BTCUSDT ping received:", event.timestamp)
|
|
2082
|
+
* );
|
|
2083
|
+
* ```
|
|
2451
2084
|
*/
|
|
2452
|
-
interface
|
|
2453
|
-
/**
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2085
|
+
interface PingContract {
|
|
2086
|
+
/**
|
|
2087
|
+
* Trading pair symbol (e.g., "BTCUSDT").
|
|
2088
|
+
* Identifies which market this ping event belongs to.
|
|
2089
|
+
*/
|
|
2090
|
+
symbol: string;
|
|
2091
|
+
/**
|
|
2092
|
+
* Strategy name that is monitoring this scheduled signal.
|
|
2093
|
+
* Identifies which strategy execution this ping event belongs to.
|
|
2094
|
+
*/
|
|
2458
2095
|
strategyName: StrategyName;
|
|
2459
|
-
/**
|
|
2096
|
+
/**
|
|
2097
|
+
* Exchange name where this scheduled signal is being monitored.
|
|
2098
|
+
* Identifies which exchange this ping event belongs to.
|
|
2099
|
+
*/
|
|
2460
2100
|
exchangeName: ExchangeName;
|
|
2461
|
-
/**
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2101
|
+
/**
|
|
2102
|
+
* Complete scheduled signal row data.
|
|
2103
|
+
* Contains all signal information: id, position, priceOpen, priceTakeProfit, priceStopLoss, etc.
|
|
2104
|
+
*/
|
|
2105
|
+
data: IScheduledSignalRow;
|
|
2106
|
+
/**
|
|
2107
|
+
* Execution mode flag.
|
|
2108
|
+
* - true: Event from backtest execution (historical candle data)
|
|
2109
|
+
* - false: Event from live trading (real-time tick)
|
|
2110
|
+
*/
|
|
2468
2111
|
backtest: boolean;
|
|
2112
|
+
/**
|
|
2113
|
+
* Event timestamp in milliseconds since Unix epoch.
|
|
2114
|
+
*
|
|
2115
|
+
* Timing semantics:
|
|
2116
|
+
* - Live mode: when.getTime() at the moment of ping
|
|
2117
|
+
* - Backtest mode: candle.timestamp of the candle being processed
|
|
2118
|
+
*
|
|
2119
|
+
* @example
|
|
2120
|
+
* ```typescript
|
|
2121
|
+
* const eventDate = new Date(event.timestamp);
|
|
2122
|
+
* console.log(`Ping at: ${eventDate.toISOString()}`);
|
|
2123
|
+
* ```
|
|
2124
|
+
*/
|
|
2125
|
+
timestamp: number;
|
|
2469
2126
|
}
|
|
2127
|
+
|
|
2470
2128
|
/**
|
|
2471
|
-
*
|
|
2472
|
-
*
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
*
|
|
2500
|
-
*
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
/** Discriminator for type-safe union */
|
|
2504
|
-
action: "closed";
|
|
2505
|
-
/** Completed signal with original parameters */
|
|
2506
|
-
signal: IPublicSignalRow;
|
|
2507
|
-
/** Final VWAP price at close */
|
|
2508
|
-
currentPrice: number;
|
|
2509
|
-
/** Why signal closed (time_expired | take_profit | stop_loss) */
|
|
2510
|
-
closeReason: StrategyCloseReason;
|
|
2511
|
-
/** Unix timestamp in milliseconds when signal closed */
|
|
2512
|
-
closeTimestamp: number;
|
|
2513
|
-
/** Profit/loss calculation with fees and slippage */
|
|
2514
|
-
pnl: IStrategyPnL;
|
|
2515
|
-
/** Strategy name for tracking */
|
|
2516
|
-
strategyName: StrategyName;
|
|
2517
|
-
/** Exchange name for tracking */
|
|
2518
|
-
exchangeName: ExchangeName;
|
|
2519
|
-
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
2520
|
-
frameName: FrameName;
|
|
2521
|
-
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
2522
|
-
symbol: string;
|
|
2523
|
-
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
2524
|
-
backtest: boolean;
|
|
2525
|
-
}
|
|
2526
|
-
/**
|
|
2527
|
-
* Tick result: scheduled signal cancelled without opening position.
|
|
2528
|
-
* Occurs when scheduled signal doesn't activate or hits stop loss before entry.
|
|
2129
|
+
* Contract for risk rejection events.
|
|
2130
|
+
*
|
|
2131
|
+
* Emitted by riskSubject ONLY when a signal is REJECTED due to risk validation failure.
|
|
2132
|
+
* Used for tracking actual risk violations and monitoring rejected signals.
|
|
2133
|
+
*
|
|
2134
|
+
* Events are emitted only when risk limits are violated (not for allowed signals).
|
|
2135
|
+
* This prevents spam and allows focusing on actual risk management interventions.
|
|
2136
|
+
*
|
|
2137
|
+
* Consumers:
|
|
2138
|
+
* - RiskMarkdownService: Accumulates rejection events for report generation
|
|
2139
|
+
* - User callbacks via listenRisk() / listenRiskOnce()
|
|
2140
|
+
*
|
|
2141
|
+
* @example
|
|
2142
|
+
* ```typescript
|
|
2143
|
+
* import { listenRisk } from "backtest-kit";
|
|
2144
|
+
*
|
|
2145
|
+
* // Listen to all risk rejection events
|
|
2146
|
+
* listenRisk((event) => {
|
|
2147
|
+
* console.log(`[RISK REJECTED] Signal for ${event.symbol}`);
|
|
2148
|
+
* console.log(`Strategy: ${event.strategyName}`);
|
|
2149
|
+
* console.log(`Active positions: ${event.activePositionCount}`);
|
|
2150
|
+
* console.log(`Price: ${event.currentPrice}`);
|
|
2151
|
+
* console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
|
|
2152
|
+
* });
|
|
2153
|
+
*
|
|
2154
|
+
* // Alert on risk rejections for specific symbol
|
|
2155
|
+
* listenRisk((event) => {
|
|
2156
|
+
* if (event.symbol === "BTCUSDT") {
|
|
2157
|
+
* console.warn("BTC signal rejected due to risk limits!");
|
|
2158
|
+
* }
|
|
2159
|
+
* });
|
|
2160
|
+
* ```
|
|
2529
2161
|
*/
|
|
2530
|
-
interface
|
|
2531
|
-
/**
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
/** Final VWAP price at cancellation */
|
|
2536
|
-
currentPrice: number;
|
|
2537
|
-
/** Unix timestamp in milliseconds when signal cancelled */
|
|
2538
|
-
closeTimestamp: number;
|
|
2539
|
-
/** Strategy name for tracking */
|
|
2540
|
-
strategyName: StrategyName;
|
|
2541
|
-
/** Exchange name for tracking */
|
|
2542
|
-
exchangeName: ExchangeName;
|
|
2543
|
-
/** Time frame name for tracking (e.g., "1m", "5m") */
|
|
2544
|
-
frameName: FrameName;
|
|
2545
|
-
/** Trading pair symbol (e.g., "BTCUSDT") */
|
|
2162
|
+
interface RiskContract {
|
|
2163
|
+
/**
|
|
2164
|
+
* Trading pair symbol (e.g., "BTCUSDT").
|
|
2165
|
+
* Identifies which market this rejected signal belongs to.
|
|
2166
|
+
*/
|
|
2546
2167
|
symbol: string;
|
|
2547
|
-
/** Whether this event is from backtest mode (true) or live mode (false) */
|
|
2548
|
-
backtest: boolean;
|
|
2549
|
-
/** Reason for cancellation */
|
|
2550
|
-
reason: StrategyCancelReason;
|
|
2551
|
-
/** Optional cancellation ID (provided when user calls Backtest.cancel() or Live.cancel()) */
|
|
2552
|
-
cancelId?: string;
|
|
2553
|
-
}
|
|
2554
|
-
/**
|
|
2555
|
-
* Discriminated union of all tick results.
|
|
2556
|
-
* Use type guards: `result.action === "closed"` for type safety.
|
|
2557
|
-
*/
|
|
2558
|
-
type IStrategyTickResult = IStrategyTickResultIdle | IStrategyTickResultScheduled | IStrategyTickResultOpened | IStrategyTickResultActive | IStrategyTickResultClosed | IStrategyTickResultCancelled;
|
|
2559
|
-
/**
|
|
2560
|
-
* Backtest returns closed result (TP/SL or time_expired) or cancelled result (scheduled signal never activated).
|
|
2561
|
-
*/
|
|
2562
|
-
type IStrategyBacktestResult = IStrategyTickResultClosed | IStrategyTickResultCancelled;
|
|
2563
|
-
/**
|
|
2564
|
-
* Strategy interface implemented by ClientStrategy.
|
|
2565
|
-
* Defines core strategy execution methods.
|
|
2566
|
-
*/
|
|
2567
|
-
interface IStrategy {
|
|
2568
2168
|
/**
|
|
2569
|
-
*
|
|
2570
|
-
*
|
|
2571
|
-
*
|
|
2572
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2573
|
-
* @param strategyName - Name of the strategy
|
|
2574
|
-
* @returns Promise resolving to tick result (idle | opened | active | closed)
|
|
2169
|
+
* Pending signal to apply.
|
|
2170
|
+
* Contains signal details (position, priceOpen, priceTakeProfit, priceStopLoss, etc).
|
|
2575
2171
|
*/
|
|
2576
|
-
|
|
2172
|
+
pendingSignal: ISignalDto;
|
|
2577
2173
|
/**
|
|
2578
|
-
*
|
|
2579
|
-
*
|
|
2580
|
-
* Used internally for monitoring TP/SL and time expiration.
|
|
2581
|
-
*
|
|
2582
|
-
* @param symbol - Trading pair symbol
|
|
2583
|
-
* @returns Promise resolving to pending signal or null
|
|
2174
|
+
* Strategy name requesting to open a position.
|
|
2175
|
+
* Identifies which strategy attempted to create the signal.
|
|
2584
2176
|
*/
|
|
2585
|
-
|
|
2177
|
+
strategyName: StrategyName;
|
|
2586
2178
|
/**
|
|
2587
|
-
*
|
|
2588
|
-
*
|
|
2589
|
-
* Used internally for monitoring scheduled signal activation.
|
|
2590
|
-
*
|
|
2591
|
-
* @param symbol - Trading pair symbol
|
|
2592
|
-
* @returns Promise resolving to scheduled signal or null
|
|
2179
|
+
* Frame name used in backtest execution.
|
|
2180
|
+
* Identifies which frame this signal was for in backtest execution.
|
|
2593
2181
|
*/
|
|
2594
|
-
|
|
2182
|
+
frameName: FrameName;
|
|
2595
2183
|
/**
|
|
2596
|
-
*
|
|
2597
|
-
*
|
|
2598
|
-
* Uses the same formula as BREAKEVEN_FN to determine if price has moved far enough
|
|
2599
|
-
* to cover transaction costs (slippage + fees) and allow breakeven to be set.
|
|
2600
|
-
* Threshold: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2 transactions
|
|
2601
|
-
*
|
|
2602
|
-
* For LONG position:
|
|
2603
|
-
* - Returns true when: currentPrice >= priceOpen * (1 + threshold%)
|
|
2604
|
-
* - Example: entry=100, threshold=0.4% → true when price >= 100.4
|
|
2605
|
-
*
|
|
2606
|
-
* For SHORT position:
|
|
2607
|
-
* - Returns true when: currentPrice <= priceOpen * (1 - threshold%)
|
|
2608
|
-
* - Example: entry=100, threshold=0.4% → true when price <= 99.6
|
|
2609
|
-
*
|
|
2610
|
-
* Special cases:
|
|
2611
|
-
* - Returns false if no pending signal exists
|
|
2612
|
-
* - Returns true if trailing stop is already in profit zone (breakeven already achieved)
|
|
2613
|
-
* - Returns false if threshold not reached yet
|
|
2614
|
-
*
|
|
2615
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2616
|
-
* @param currentPrice - Current market price to check against threshold
|
|
2617
|
-
* @returns Promise<boolean> - true if breakeven threshold reached, false otherwise
|
|
2618
|
-
*
|
|
2619
|
-
* @example
|
|
2620
|
-
* ```typescript
|
|
2621
|
-
* // Check if breakeven is available for LONG position (entry=100, threshold=0.4%)
|
|
2622
|
-
* const canBreakeven = await strategy.getBreakeven("BTCUSDT", 100.5);
|
|
2623
|
-
* // Returns true (price >= 100.4)
|
|
2624
|
-
*
|
|
2625
|
-
* if (canBreakeven) {
|
|
2626
|
-
* await strategy.breakeven("BTCUSDT", 100.5, false);
|
|
2627
|
-
* }
|
|
2628
|
-
* ```
|
|
2184
|
+
* Exchange name.
|
|
2185
|
+
* Identifies which exchange this signal was for.
|
|
2629
2186
|
*/
|
|
2630
|
-
|
|
2187
|
+
exchangeName: ExchangeName;
|
|
2631
2188
|
/**
|
|
2632
|
-
*
|
|
2633
|
-
*
|
|
2634
|
-
* Returns the stopped state indicating whether the strategy should
|
|
2635
|
-
* cease processing new ticks or signals.
|
|
2636
|
-
*
|
|
2637
|
-
* @param symbol - Trading pair symbol
|
|
2638
|
-
* @returns Promise resolving to true if strategy is stopped, false otherwise
|
|
2189
|
+
* Current VWAP price at the time of rejection.
|
|
2190
|
+
* Market price when risk check was performed.
|
|
2639
2191
|
*/
|
|
2640
|
-
|
|
2192
|
+
currentPrice: number;
|
|
2641
2193
|
/**
|
|
2642
|
-
*
|
|
2643
|
-
*
|
|
2644
|
-
*
|
|
2645
|
-
* For scheduled signals: first monitors activation/cancellation,
|
|
2646
|
-
* then if activated continues with TP/SL monitoring.
|
|
2647
|
-
*
|
|
2648
|
-
* @param symbol - Trading pair symbol
|
|
2649
|
-
* @param strategyName - Name of the strategy
|
|
2650
|
-
* @param candles - Array of historical candle data
|
|
2651
|
-
* @returns Promise resolving to closed result (always completes signal)
|
|
2194
|
+
* Number of currently active positions across all strategies at rejection time.
|
|
2195
|
+
* Used to track portfolio-level exposure when signal was rejected.
|
|
2652
2196
|
*/
|
|
2653
|
-
|
|
2197
|
+
activePositionCount: number;
|
|
2654
2198
|
/**
|
|
2655
|
-
*
|
|
2656
|
-
*
|
|
2657
|
-
*
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
*
|
|
2662
|
-
*
|
|
2663
|
-
* @returns Promise that resolves immediately when stop flag is set
|
|
2199
|
+
* Unique identifier for this rejection instance.
|
|
2200
|
+
* Generated by ClientRisk for tracking and debugging purposes.
|
|
2201
|
+
* Null if validation threw exception without custom ID.
|
|
2202
|
+
*/
|
|
2203
|
+
rejectionId: string | null;
|
|
2204
|
+
/**
|
|
2205
|
+
* Human-readable reason why the signal was rejected.
|
|
2206
|
+
* Captured from IRiskValidation.note or error message.
|
|
2664
2207
|
*
|
|
2665
2208
|
* @example
|
|
2666
2209
|
* ```typescript
|
|
2667
|
-
*
|
|
2668
|
-
*
|
|
2669
|
-
*
|
|
2670
|
-
* // Later: stop new signals, let existing ones close naturally
|
|
2671
|
-
* await cancel();
|
|
2210
|
+
* console.log(`Rejection reason: ${event.rejectionNote}`);
|
|
2211
|
+
* // Output: "Rejection reason: Max 3 positions allowed"
|
|
2672
2212
|
* ```
|
|
2673
2213
|
*/
|
|
2674
|
-
|
|
2214
|
+
rejectionNote: string;
|
|
2675
2215
|
/**
|
|
2676
|
-
*
|
|
2677
|
-
*
|
|
2678
|
-
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
2679
|
-
* Does NOT affect active pending signals or strategy operation.
|
|
2680
|
-
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
2681
|
-
*
|
|
2682
|
-
* Use case: Cancel a scheduled entry that is no longer desired without stopping the entire strategy.
|
|
2683
|
-
*
|
|
2684
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2685
|
-
* @param cancelId - Optional cancellation ID
|
|
2686
|
-
* @returns Promise that resolves when scheduled signal is cleared
|
|
2216
|
+
* Event timestamp in milliseconds since Unix epoch.
|
|
2217
|
+
* Represents when the signal was rejected.
|
|
2687
2218
|
*
|
|
2688
2219
|
* @example
|
|
2689
2220
|
* ```typescript
|
|
2690
|
-
*
|
|
2691
|
-
*
|
|
2692
|
-
* // Strategy continues, can generate new signals
|
|
2221
|
+
* const eventDate = new Date(event.timestamp);
|
|
2222
|
+
* console.log(`Signal rejected at: ${eventDate.toISOString()}`);
|
|
2693
2223
|
* ```
|
|
2694
2224
|
*/
|
|
2695
|
-
|
|
2225
|
+
timestamp: number;
|
|
2696
2226
|
/**
|
|
2697
|
-
*
|
|
2698
|
-
*
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2227
|
+
* Whether this event is from backtest mode (true) or live mode (false).
|
|
2228
|
+
* Used to separate backtest and live risk rejection tracking.
|
|
2229
|
+
*/
|
|
2230
|
+
backtest: boolean;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
/**
|
|
2234
|
+
* Constructor type for action handlers with strategy context.
|
|
2235
|
+
*
|
|
2236
|
+
* @param strategyName - Strategy identifier (e.g., "rsi_divergence", "macd_cross")
|
|
2237
|
+
* @param frameName - Timeframe identifier (e.g., "1m", "5m", "1h")
|
|
2238
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2239
|
+
* @returns Partial implementation of IAction (only required handlers)
|
|
2240
|
+
*
|
|
2241
|
+
* @example
|
|
2242
|
+
* ```typescript
|
|
2243
|
+
* class TelegramNotifier implements Partial<IAction> {
|
|
2244
|
+
* constructor(
|
|
2245
|
+
* private strategyName: StrategyName,
|
|
2246
|
+
* private frameName: FrameName,
|
|
2247
|
+
* private backtest: boolean
|
|
2248
|
+
* ) {}
|
|
2249
|
+
*
|
|
2250
|
+
* signal(event: IStrategyTickResult): void {
|
|
2251
|
+
* if (!this.backtest && event.state === 'opened') {
|
|
2252
|
+
* telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
|
|
2253
|
+
* }
|
|
2254
|
+
* }
|
|
2255
|
+
* }
|
|
2256
|
+
*
|
|
2257
|
+
* const actionCtors: TActionCtor[] = [TelegramNotifier, ReduxLogger];
|
|
2258
|
+
* ```
|
|
2259
|
+
*/
|
|
2260
|
+
type TActionCtor = new (strategyName: StrategyName, frameName: FrameName, actionName: ActionName) => Partial<IPublicAction>;
|
|
2261
|
+
/**
|
|
2262
|
+
* Action parameters passed to ClientAction constructor.
|
|
2263
|
+
* Combines schema with runtime dependencies and execution context.
|
|
2264
|
+
*
|
|
2265
|
+
* Extended from IActionSchema with:
|
|
2266
|
+
* - Logger instance for debugging and monitoring
|
|
2267
|
+
* - Strategy context (strategyName, frameName)
|
|
2268
|
+
* - Runtime environment flags
|
|
2269
|
+
*
|
|
2270
|
+
* @example
|
|
2271
|
+
* ```typescript
|
|
2272
|
+
* const params: IActionParams = {
|
|
2273
|
+
* actionName: "telegram-notifier",
|
|
2274
|
+
* handler: TelegramNotifier,
|
|
2275
|
+
* callbacks: { onInit, onDispose, onSignal },
|
|
2276
|
+
* logger: loggerService,
|
|
2277
|
+
* strategyName: "rsi_divergence",
|
|
2278
|
+
* frameName: "1h"
|
|
2279
|
+
* };
|
|
2280
|
+
*
|
|
2281
|
+
* const actionClient = new ClientAction(params);
|
|
2282
|
+
* ```
|
|
2283
|
+
*/
|
|
2284
|
+
interface IActionParams extends IActionSchema {
|
|
2285
|
+
/** Logger service for debugging and monitoring action execution */
|
|
2286
|
+
logger: ILogger;
|
|
2287
|
+
/** Strategy identifier this action is attached to */
|
|
2288
|
+
strategyName: StrategyName;
|
|
2289
|
+
/** Exchange name (e.g., "binance") */
|
|
2290
|
+
exchangeName: ExchangeName;
|
|
2291
|
+
/** Timeframe identifier this action is attached to */
|
|
2292
|
+
frameName: FrameName;
|
|
2293
|
+
/** Whether running in backtest mode */
|
|
2294
|
+
backtest: boolean;
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Lifecycle and event callbacks for action handlers.
|
|
2298
|
+
*
|
|
2299
|
+
* Provides hooks for initialization, disposal, and event handling.
|
|
2300
|
+
* All callbacks are optional and support both sync and async execution.
|
|
2301
|
+
*
|
|
2302
|
+
* Use cases:
|
|
2303
|
+
* - Resource initialization (database connections, file handles)
|
|
2304
|
+
* - Resource cleanup (close connections, flush buffers)
|
|
2305
|
+
* - Event logging and monitoring
|
|
2306
|
+
* - State persistence
|
|
2307
|
+
*
|
|
2308
|
+
* @example
|
|
2309
|
+
* ```typescript
|
|
2310
|
+
* const callbacks: IActionCallbacks = {
|
|
2311
|
+
* onInit: async (strategyName, frameName, backtest) => {
|
|
2312
|
+
* console.log(`[${strategyName}/${frameName}] Action initialized (backtest=${backtest})`);
|
|
2313
|
+
* await db.connect();
|
|
2314
|
+
* },
|
|
2315
|
+
* onSignal: (event, strategyName, frameName, backtest) => {
|
|
2316
|
+
* if (event.action === 'opened') {
|
|
2317
|
+
* console.log(`New signal opened: ${event.signal.id}`);
|
|
2318
|
+
* }
|
|
2319
|
+
* },
|
|
2320
|
+
* onDispose: async (strategyName, frameName, backtest) => {
|
|
2321
|
+
* await db.disconnect();
|
|
2322
|
+
* console.log(`[${strategyName}/${frameName}] Action disposed`);
|
|
2323
|
+
* }
|
|
2324
|
+
* };
|
|
2325
|
+
* ```
|
|
2326
|
+
*/
|
|
2327
|
+
interface IActionCallbacks {
|
|
2328
|
+
/**
|
|
2329
|
+
* Called when action handler is initialized.
|
|
2702
2330
|
*
|
|
2703
|
-
*
|
|
2704
|
-
* -
|
|
2705
|
-
* -
|
|
2706
|
-
* -
|
|
2707
|
-
* -
|
|
2331
|
+
* Use for:
|
|
2332
|
+
* - Opening database connections
|
|
2333
|
+
* - Initializing external services
|
|
2334
|
+
* - Loading persisted state
|
|
2335
|
+
* - Setting up subscriptions
|
|
2708
2336
|
*
|
|
2709
|
-
*
|
|
2337
|
+
* @param actionName - Action identifier
|
|
2338
|
+
* @param strategyName - Strategy identifier
|
|
2339
|
+
* @param frameName - Timeframe identifier
|
|
2340
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2341
|
+
*/
|
|
2342
|
+
onInit(actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2343
|
+
/**
|
|
2344
|
+
* Called when action handler is disposed.
|
|
2710
2345
|
*
|
|
2711
|
-
*
|
|
2712
|
-
*
|
|
2713
|
-
*
|
|
2714
|
-
*
|
|
2715
|
-
*
|
|
2346
|
+
* Use for:
|
|
2347
|
+
* - Closing database connections
|
|
2348
|
+
* - Flushing buffers
|
|
2349
|
+
* - Saving state to disk
|
|
2350
|
+
* - Unsubscribing from observables
|
|
2716
2351
|
*
|
|
2717
|
-
* @
|
|
2718
|
-
*
|
|
2719
|
-
*
|
|
2720
|
-
*
|
|
2721
|
-
* if (percentTp >= 50) {
|
|
2722
|
-
* const success = await strategy.partialProfit(symbol, 25, currentPrice, backtest);
|
|
2723
|
-
* if (success) {
|
|
2724
|
-
* console.log('Partial profit executed');
|
|
2725
|
-
* }
|
|
2726
|
-
* }
|
|
2727
|
-
* }
|
|
2728
|
-
* }
|
|
2729
|
-
* ```
|
|
2352
|
+
* @param actionName - Action identifier
|
|
2353
|
+
* @param strategyName - Strategy identifier
|
|
2354
|
+
* @param frameName - Timeframe identifier
|
|
2355
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2730
2356
|
*/
|
|
2731
|
-
|
|
2357
|
+
onDispose(actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2732
2358
|
/**
|
|
2733
|
-
*
|
|
2734
|
-
*
|
|
2735
|
-
* Closes specified percentage of position at current price.
|
|
2736
|
-
* Updates _slClosed, _totalClosed, and _partialHistory state.
|
|
2737
|
-
* Persists updated signal state for crash recovery.
|
|
2359
|
+
* Called on signal events from all modes (live + backtest).
|
|
2738
2360
|
*
|
|
2739
|
-
*
|
|
2740
|
-
*
|
|
2741
|
-
* - Throws if called on scheduled signal (not yet activated)
|
|
2742
|
-
* - Throws if percentToClose <= 0 or > 100
|
|
2743
|
-
* - Returns false if _totalClosed + percentToClose > 100 (prevents over-closing)
|
|
2361
|
+
* Triggered by: StrategyConnectionService via signalEmitter
|
|
2362
|
+
* Frequency: Every tick/candle when strategy is evaluated
|
|
2744
2363
|
*
|
|
2745
|
-
*
|
|
2364
|
+
* @param event - Signal state result (idle, scheduled, opened, active, closed, cancelled)
|
|
2365
|
+
* @param actionName - Action identifier
|
|
2366
|
+
* @param strategyName - Strategy identifier
|
|
2367
|
+
* @param frameName - Timeframe identifier
|
|
2368
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2369
|
+
*/
|
|
2370
|
+
onSignal(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2371
|
+
/**
|
|
2372
|
+
* Called on signal events from live trading only.
|
|
2746
2373
|
*
|
|
2747
|
-
*
|
|
2748
|
-
*
|
|
2749
|
-
* @param currentPrice - Current market price for partial close
|
|
2750
|
-
* @param backtest - Whether running in backtest mode
|
|
2751
|
-
* @returns Promise<boolean> - true if partial close executed, false if skipped
|
|
2374
|
+
* Triggered by: StrategyConnectionService via signalLiveEmitter
|
|
2375
|
+
* Frequency: Every tick in live mode
|
|
2752
2376
|
*
|
|
2753
|
-
* @
|
|
2754
|
-
*
|
|
2755
|
-
*
|
|
2756
|
-
*
|
|
2757
|
-
*
|
|
2758
|
-
* const success = await strategy.partialLoss(symbol, 50, currentPrice, backtest);
|
|
2759
|
-
* if (success) {
|
|
2760
|
-
* console.log('Partial loss executed');
|
|
2761
|
-
* }
|
|
2762
|
-
* }
|
|
2763
|
-
* }
|
|
2764
|
-
* }
|
|
2765
|
-
* ```
|
|
2377
|
+
* @param event - Signal state result from live trading
|
|
2378
|
+
* @param actionName - Action identifier
|
|
2379
|
+
* @param strategyName - Strategy identifier
|
|
2380
|
+
* @param frameName - Timeframe identifier
|
|
2381
|
+
* @param backtest - Always false (live mode only)
|
|
2766
2382
|
*/
|
|
2767
|
-
|
|
2383
|
+
onSignalLive(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2768
2384
|
/**
|
|
2769
|
-
*
|
|
2385
|
+
* Called on signal events from backtest only.
|
|
2770
2386
|
*
|
|
2771
|
-
*
|
|
2772
|
-
*
|
|
2773
|
-
* Larger percentShift ABSORBS smaller one (updates only towards better protection).
|
|
2387
|
+
* Triggered by: StrategyConnectionService via signalBacktestEmitter
|
|
2388
|
+
* Frequency: Every candle in backtest mode
|
|
2774
2389
|
*
|
|
2775
|
-
*
|
|
2776
|
-
*
|
|
2777
|
-
*
|
|
2390
|
+
* @param event - Signal state result from backtest
|
|
2391
|
+
* @param actionName - Action identifier
|
|
2392
|
+
* @param strategyName - Strategy identifier
|
|
2393
|
+
* @param frameName - Timeframe identifier
|
|
2394
|
+
* @param backtest - Always true (backtest mode only)
|
|
2395
|
+
*/
|
|
2396
|
+
onSignalBacktest(event: IStrategyTickResult, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2397
|
+
/**
|
|
2398
|
+
* Called when breakeven is triggered (stop-loss moved to entry price).
|
|
2778
2399
|
*
|
|
2779
|
-
*
|
|
2780
|
-
*
|
|
2781
|
-
* - percentShift = +20: newSL = 100 - 10%*(1+0.2) = 88 (12% distance, looser)
|
|
2400
|
+
* Triggered by: BreakevenConnectionService via breakevenSubject
|
|
2401
|
+
* Frequency: Once per signal when breakeven threshold is reached
|
|
2782
2402
|
*
|
|
2783
|
-
*
|
|
2784
|
-
*
|
|
2785
|
-
*
|
|
2403
|
+
* @param event - Breakeven milestone data
|
|
2404
|
+
* @param actionName - Action identifier
|
|
2405
|
+
* @param strategyName - Strategy identifier
|
|
2406
|
+
* @param frameName - Timeframe identifier
|
|
2407
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2408
|
+
*/
|
|
2409
|
+
onBreakeven(event: BreakevenContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2410
|
+
/**
|
|
2411
|
+
* Called when partial profit level is reached (10%, 20%, 30%, etc).
|
|
2786
2412
|
*
|
|
2787
|
-
*
|
|
2788
|
-
*
|
|
2789
|
-
* - Subsequent calls: updates only if new SL is BETTER (protects more profit)
|
|
2790
|
-
* - For LONG: only accepts HIGHER SL (never moves down, closer to entry wins)
|
|
2791
|
-
* - For SHORT: only accepts LOWER SL (never moves up, closer to entry wins)
|
|
2792
|
-
* - Stores in _trailingPriceStopLoss, original priceStopLoss always preserved
|
|
2413
|
+
* Triggered by: PartialConnectionService via partialProfitSubject
|
|
2414
|
+
* Frequency: Once per profit level per signal (deduplicated)
|
|
2793
2415
|
*
|
|
2794
|
-
*
|
|
2795
|
-
*
|
|
2796
|
-
*
|
|
2797
|
-
*
|
|
2798
|
-
* -
|
|
2799
|
-
* - Skips if currentPrice already crossed new SL level (price intrusion protection)
|
|
2800
|
-
*
|
|
2801
|
-
* Use case: User-controlled trailing stop triggered from onPartialProfit callback.
|
|
2802
|
-
*
|
|
2803
|
-
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2804
|
-
* @param percentShift - Percentage shift of ORIGINAL SL distance [-100, 100], excluding 0
|
|
2805
|
-
* @param currentPrice - Current market price to check for intrusion
|
|
2806
|
-
* @param backtest - Whether running in backtest mode
|
|
2807
|
-
* @returns Promise<boolean> - true if trailing SL was set/updated, false if rejected
|
|
2808
|
-
*
|
|
2809
|
-
* @example
|
|
2810
|
-
* ```typescript
|
|
2811
|
-
* callbacks: {
|
|
2812
|
-
* onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
|
|
2813
|
-
* if (percentTp >= 50) {
|
|
2814
|
-
* // LONG: entry=100, originalSL=90, distance=10%
|
|
2815
|
-
*
|
|
2816
|
-
* // First call: tighten by 5%
|
|
2817
|
-
* const success1 = await strategy.trailingStop(symbol, -5, currentPrice, backtest);
|
|
2818
|
-
* // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
|
|
2819
|
-
*
|
|
2820
|
-
* // Second call: try weaker protection
|
|
2821
|
-
* const success2 = await strategy.trailingStop(symbol, -3, currentPrice, backtest);
|
|
2822
|
-
* // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
|
|
2823
|
-
*
|
|
2824
|
-
* // Third call: stronger protection
|
|
2825
|
-
* const success3 = await strategy.trailingStop(symbol, -7, currentPrice, backtest);
|
|
2826
|
-
* // success3 = true (ACCEPTED: newDistance = 3%, newSL = 97 > 95, better protection)
|
|
2827
|
-
* }
|
|
2828
|
-
* }
|
|
2829
|
-
* }
|
|
2830
|
-
* ```
|
|
2416
|
+
* @param event - Profit milestone data with level and price
|
|
2417
|
+
* @param actionName - Action identifier
|
|
2418
|
+
* @param strategyName - Strategy identifier
|
|
2419
|
+
* @param frameName - Timeframe identifier
|
|
2420
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2831
2421
|
*/
|
|
2832
|
-
|
|
2422
|
+
onPartialProfit(event: PartialProfitContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2833
2423
|
/**
|
|
2834
|
-
*
|
|
2835
|
-
*
|
|
2836
|
-
* CRITICAL: Always calculates from ORIGINAL TP, not from current trailing TP.
|
|
2837
|
-
* This prevents error accumulation on repeated calls.
|
|
2838
|
-
* Larger percentShift ABSORBS smaller one (updates only towards more conservative TP).
|
|
2839
|
-
*
|
|
2840
|
-
* Updates the take-profit distance by a percentage adjustment relative to the ORIGINAL TP distance.
|
|
2841
|
-
* Negative percentShift brings TP closer to entry (more conservative).
|
|
2842
|
-
* Positive percentShift moves TP further from entry (more aggressive).
|
|
2843
|
-
*
|
|
2844
|
-
* Absorption behavior:
|
|
2845
|
-
* - First call: sets trailing TP unconditionally
|
|
2846
|
-
* - Subsequent calls: updates only if new TP is MORE CONSERVATIVE (closer to entry)
|
|
2847
|
-
* - For LONG: only accepts LOWER TP (never moves up, closer to entry wins)
|
|
2848
|
-
* - For SHORT: only accepts HIGHER TP (never moves down, closer to entry wins)
|
|
2849
|
-
* - Stores in _trailingPriceTakeProfit, original priceTakeProfit always preserved
|
|
2850
|
-
*
|
|
2851
|
-
* Price intrusion protection: If current price has already crossed the new TP level,
|
|
2852
|
-
* the update is skipped to prevent immediate TP triggering.
|
|
2853
|
-
*
|
|
2854
|
-
* @param symbol - Trading pair symbol
|
|
2855
|
-
* @param percentShift - Percentage adjustment to ORIGINAL TP distance (-100 to 100)
|
|
2856
|
-
* @param currentPrice - Current market price to check for intrusion
|
|
2857
|
-
* @param backtest - Whether running in backtest mode
|
|
2858
|
-
* @returns Promise<boolean> - true if trailing TP was set/updated, false if rejected
|
|
2859
|
-
*
|
|
2860
|
-
* @example
|
|
2861
|
-
* ```typescript
|
|
2862
|
-
* callbacks: {
|
|
2863
|
-
* onPartialProfit: async (symbol, signal, currentPrice, percentTp, backtest) => {
|
|
2864
|
-
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
2865
|
-
*
|
|
2866
|
-
* // First call: bring TP closer by 3%
|
|
2867
|
-
* const success1 = await strategy.trailingTake(symbol, -3, currentPrice, backtest);
|
|
2868
|
-
* // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
|
|
2424
|
+
* Called when partial loss level is reached (-10%, -20%, -30%, etc).
|
|
2869
2425
|
*
|
|
2870
|
-
*
|
|
2871
|
-
*
|
|
2872
|
-
* // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
|
|
2426
|
+
* Triggered by: PartialConnectionService via partialLossSubject
|
|
2427
|
+
* Frequency: Once per loss level per signal (deduplicated)
|
|
2873
2428
|
*
|
|
2874
|
-
*
|
|
2875
|
-
*
|
|
2876
|
-
*
|
|
2877
|
-
*
|
|
2878
|
-
*
|
|
2879
|
-
* ```
|
|
2429
|
+
* @param event - Loss milestone data with level and price
|
|
2430
|
+
* @param actionName - Action identifier
|
|
2431
|
+
* @param strategyName - Strategy identifier
|
|
2432
|
+
* @param frameName - Timeframe identifier
|
|
2433
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2880
2434
|
*/
|
|
2881
|
-
|
|
2435
|
+
onPartialLoss(event: PartialLossContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2882
2436
|
/**
|
|
2883
|
-
*
|
|
2884
|
-
*
|
|
2885
|
-
* Moves SL to entry price (zero-risk position) when current price has moved
|
|
2886
|
-
* far enough in profit direction to cover transaction costs (slippage + fees).
|
|
2887
|
-
* Threshold is calculated as: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2
|
|
2888
|
-
*
|
|
2889
|
-
* Behavior:
|
|
2890
|
-
* - Returns true if SL was moved to breakeven
|
|
2891
|
-
* - Returns false if conditions not met (threshold not reached or already at breakeven)
|
|
2892
|
-
* - Uses _trailingPriceStopLoss to store breakeven SL (preserves original priceStopLoss)
|
|
2893
|
-
* - Only moves SL once per position (idempotent - safe to call multiple times)
|
|
2894
|
-
*
|
|
2895
|
-
* For LONG position (entry=100, slippage=0.1%, fee=0.1%):
|
|
2896
|
-
* - Threshold: (0.1 + 0.1) * 2 = 0.4%
|
|
2897
|
-
* - Breakeven available when price >= 100.4 (entry + 0.4%)
|
|
2898
|
-
* - Moves SL from original (e.g. 95) to 100 (breakeven)
|
|
2899
|
-
* - Returns true on first successful move, false on subsequent calls
|
|
2900
|
-
*
|
|
2901
|
-
* For SHORT position (entry=100, slippage=0.1%, fee=0.1%):
|
|
2902
|
-
* - Threshold: (0.1 + 0.1) * 2 = 0.4%
|
|
2903
|
-
* - Breakeven available when price <= 99.6 (entry - 0.4%)
|
|
2904
|
-
* - Moves SL from original (e.g. 105) to 100 (breakeven)
|
|
2905
|
-
* - Returns true on first successful move, false on subsequent calls
|
|
2906
|
-
*
|
|
2907
|
-
* Validations:
|
|
2908
|
-
* - Throws if no pending signal exists
|
|
2909
|
-
* - Throws if currentPrice is not a positive finite number
|
|
2910
|
-
*
|
|
2911
|
-
* Use case: User-controlled breakeven protection triggered from onPartialProfit callback.
|
|
2437
|
+
* Called during scheduled signal monitoring (every minute while waiting for activation).
|
|
2912
2438
|
*
|
|
2913
|
-
*
|
|
2914
|
-
*
|
|
2915
|
-
* @param backtest - Whether running in backtest mode
|
|
2916
|
-
* @returns Promise<boolean> - true if breakeven was set, false if conditions not met
|
|
2439
|
+
* Triggered by: StrategyConnectionService via pingSubject
|
|
2440
|
+
* Frequency: Every minute while scheduled signal is waiting
|
|
2917
2441
|
*
|
|
2918
|
-
* @
|
|
2919
|
-
*
|
|
2920
|
-
*
|
|
2921
|
-
*
|
|
2922
|
-
*
|
|
2923
|
-
* const movedToBreakeven = await strategy.breakeven(symbol, currentPrice, backtest);
|
|
2924
|
-
* if (movedToBreakeven) {
|
|
2925
|
-
* console.log(`Position moved to breakeven at ${currentPrice}`);
|
|
2926
|
-
* }
|
|
2927
|
-
* }
|
|
2928
|
-
* }
|
|
2929
|
-
* ```
|
|
2442
|
+
* @param event - Scheduled signal monitoring data
|
|
2443
|
+
* @param actionName - Action identifier
|
|
2444
|
+
* @param strategyName - Strategy identifier
|
|
2445
|
+
* @param frameName - Timeframe identifier
|
|
2446
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2930
2447
|
*/
|
|
2931
|
-
|
|
2448
|
+
onPing(event: PingContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2932
2449
|
/**
|
|
2933
|
-
*
|
|
2450
|
+
* Called when signal is rejected by risk management.
|
|
2934
2451
|
*
|
|
2935
|
-
*
|
|
2936
|
-
*
|
|
2452
|
+
* Triggered by: RiskConnectionService via riskSubject
|
|
2453
|
+
* Frequency: Only when signal fails risk validation (not emitted for allowed signals)
|
|
2937
2454
|
*
|
|
2938
|
-
* @
|
|
2455
|
+
* @param event - Risk rejection data with reason and context
|
|
2456
|
+
* @param actionName - Action identifier
|
|
2457
|
+
* @param strategyName - Strategy identifier
|
|
2458
|
+
* @param frameName - Timeframe identifier
|
|
2459
|
+
* @param backtest - True for backtest mode, false for live trading
|
|
2939
2460
|
*/
|
|
2940
|
-
|
|
2941
|
-
}
|
|
2942
|
-
/**
|
|
2943
|
-
* Unique strategy identifier.
|
|
2944
|
-
*/
|
|
2945
|
-
type StrategyName = string;
|
|
2946
|
-
|
|
2947
|
-
/**
|
|
2948
|
-
* Unified breakeven event data for report generation.
|
|
2949
|
-
* Contains all information about when signals reached breakeven.
|
|
2950
|
-
*/
|
|
2951
|
-
interface BreakevenEvent {
|
|
2952
|
-
/** Event timestamp in milliseconds */
|
|
2953
|
-
timestamp: number;
|
|
2954
|
-
/** Trading pair symbol */
|
|
2955
|
-
symbol: string;
|
|
2956
|
-
/** Strategy name */
|
|
2957
|
-
strategyName: StrategyName;
|
|
2958
|
-
/** Signal ID */
|
|
2959
|
-
signalId: string;
|
|
2960
|
-
/** Position type */
|
|
2961
|
-
position: string;
|
|
2962
|
-
/** Current market price when breakeven was reached */
|
|
2963
|
-
currentPrice: number;
|
|
2964
|
-
/** Entry price (breakeven level) */
|
|
2965
|
-
priceOpen: number;
|
|
2966
|
-
/** Take profit target price */
|
|
2967
|
-
priceTakeProfit?: number;
|
|
2968
|
-
/** Stop loss exit price */
|
|
2969
|
-
priceStopLoss?: number;
|
|
2970
|
-
/** Original take profit price set at signal creation */
|
|
2971
|
-
originalPriceTakeProfit?: number;
|
|
2972
|
-
/** Original stop loss price set at signal creation */
|
|
2973
|
-
originalPriceStopLoss?: number;
|
|
2974
|
-
/** Total executed percentage from partial closes */
|
|
2975
|
-
totalExecuted?: number;
|
|
2976
|
-
/** Human-readable description of signal reason */
|
|
2977
|
-
note?: string;
|
|
2978
|
-
/** True if backtest mode, false if live mode */
|
|
2979
|
-
backtest: boolean;
|
|
2461
|
+
onRiskRejection(event: RiskContract, actionName: ActionName, strategyName: StrategyName, frameName: FrameName, backtest: boolean): void | Promise<void>;
|
|
2980
2462
|
}
|
|
2981
2463
|
/**
|
|
2982
|
-
*
|
|
2464
|
+
* Action schema registered via addAction().
|
|
2465
|
+
* Defines event handler implementation and lifecycle callbacks for state management integration.
|
|
2983
2466
|
*
|
|
2984
|
-
*
|
|
2467
|
+
* Actions provide a way to attach custom event handlers to strategies for:
|
|
2468
|
+
* - State management (Redux, Zustand, MobX)
|
|
2469
|
+
* - Event logging and monitoring
|
|
2470
|
+
* - Real-time notifications (Telegram, Discord, email)
|
|
2471
|
+
* - Analytics and metrics collection
|
|
2472
|
+
* - Custom business logic triggers
|
|
2473
|
+
*
|
|
2474
|
+
* Each action instance is created per strategy-frame pair and receives all events
|
|
2475
|
+
* emitted during strategy execution. Multiple actions can be attached to a single strategy.
|
|
2985
2476
|
*
|
|
2986
2477
|
* @example
|
|
2987
2478
|
* ```typescript
|
|
2988
|
-
*
|
|
2479
|
+
* import { addAction } from "backtest-kit";
|
|
2989
2480
|
*
|
|
2990
|
-
*
|
|
2991
|
-
*
|
|
2481
|
+
* // Define action handler class
|
|
2482
|
+
* class TelegramNotifier implements Partial<IAction> {
|
|
2483
|
+
* constructor(
|
|
2484
|
+
* private strategyName: StrategyName,
|
|
2485
|
+
* private frameName: FrameName,
|
|
2486
|
+
* private backtest: boolean
|
|
2487
|
+
* ) {}
|
|
2488
|
+
*
|
|
2489
|
+
* signal(event: IStrategyTickResult): void {
|
|
2490
|
+
* if (!this.backtest && event.action === 'opened') {
|
|
2491
|
+
* telegram.send(`[${this.strategyName}/${this.frameName}] New signal`);
|
|
2492
|
+
* }
|
|
2493
|
+
* }
|
|
2494
|
+
*
|
|
2495
|
+
* dispose(): void {
|
|
2496
|
+
* telegram.close();
|
|
2497
|
+
* }
|
|
2498
|
+
* }
|
|
2499
|
+
*
|
|
2500
|
+
* // Register action schema
|
|
2501
|
+
* addAction({
|
|
2502
|
+
* actionName: "telegram-notifier",
|
|
2503
|
+
* handler: TelegramNotifier,
|
|
2504
|
+
* callbacks: {
|
|
2505
|
+
* onInit: async (strategyName, frameName, backtest) => {
|
|
2506
|
+
* console.log(`Telegram notifier initialized for ${strategyName}/${frameName}`);
|
|
2507
|
+
* },
|
|
2508
|
+
* onSignal: (event, strategyName, frameName, backtest) => {
|
|
2509
|
+
* console.log(`Signal event: ${event.action}`);
|
|
2510
|
+
* }
|
|
2511
|
+
* }
|
|
2512
|
+
* });
|
|
2992
2513
|
* ```
|
|
2993
2514
|
*/
|
|
2994
|
-
interface
|
|
2995
|
-
/**
|
|
2996
|
-
|
|
2997
|
-
/**
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
2515
|
+
interface IActionSchema {
|
|
2516
|
+
/** Unique action identifier for registration */
|
|
2517
|
+
actionName: ActionName;
|
|
2518
|
+
/** Action handler constructor (instantiated per strategy-frame pair) */
|
|
2519
|
+
handler: TActionCtor | Partial<IPublicAction>;
|
|
2520
|
+
/** Optional lifecycle and event callbacks */
|
|
2521
|
+
callbacks?: Partial<IActionCallbacks>;
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Public action interface for custom action handler implementations.
|
|
2525
|
+
*
|
|
2526
|
+
* Extends IAction with an initialization lifecycle method.
|
|
2527
|
+
* Action handlers implement this interface to receive strategy events and perform custom logic.
|
|
2528
|
+
*
|
|
2529
|
+
* Lifecycle:
|
|
2530
|
+
* 1. Constructor called with (strategyName, frameName, actionName)
|
|
2531
|
+
* 2. init() called once for async initialization (setup connections, load resources)
|
|
2532
|
+
* 3. Event methods called as strategy executes (signal, breakeven, partialProfit, etc.)
|
|
2533
|
+
* 4. dispose() called once for cleanup (close connections, flush buffers)
|
|
2534
|
+
*
|
|
2535
|
+
* Key features:
|
|
2536
|
+
* - init() for async initialization (database connections, API clients, file handles)
|
|
2537
|
+
* - All IAction methods available for event handling
|
|
2538
|
+
* - dispose() guaranteed to run exactly once via singleshot pattern
|
|
2539
|
+
*
|
|
2540
|
+
* Common use cases:
|
|
2541
|
+
* - State management: Redux/Zustand store integration
|
|
2542
|
+
* - Notifications: Telegram/Discord/Email alerts
|
|
2543
|
+
* - Logging: Custom event tracking and monitoring
|
|
2544
|
+
* - Analytics: Metrics collection and reporting
|
|
2545
|
+
* - External systems: Database writes, API calls, file operations
|
|
2546
|
+
*
|
|
2547
|
+
* @example
|
|
2548
|
+
* ```typescript
|
|
2549
|
+
* class TelegramNotifier implements Partial<IPublicAction> {
|
|
2550
|
+
* private bot: TelegramBot | null = null;
|
|
2551
|
+
*
|
|
2552
|
+
* constructor(
|
|
2553
|
+
* private strategyName: string,
|
|
2554
|
+
* private frameName: string,
|
|
2555
|
+
* private actionName: string
|
|
2556
|
+
* ) {}
|
|
2557
|
+
*
|
|
2558
|
+
* // Called once during initialization
|
|
2559
|
+
* async init() {
|
|
2560
|
+
* this.bot = new TelegramBot(process.env.TELEGRAM_TOKEN);
|
|
2561
|
+
* await this.bot.connect();
|
|
2562
|
+
* }
|
|
2563
|
+
*
|
|
2564
|
+
* // Called on every signal event
|
|
2565
|
+
* async signal(event: IStrategyTickResult) {
|
|
2566
|
+
* if (event.action === 'opened') {
|
|
2567
|
+
* await this.bot.send(
|
|
2568
|
+
* `[${this.strategyName}/${this.frameName}] Signal opened: ${event.signal.side}`
|
|
2569
|
+
* );
|
|
2570
|
+
* }
|
|
2571
|
+
* }
|
|
2572
|
+
*
|
|
2573
|
+
* // Called once during cleanup
|
|
2574
|
+
* async dispose() {
|
|
2575
|
+
* await this.bot?.disconnect();
|
|
2576
|
+
* this.bot = null;
|
|
2577
|
+
* }
|
|
2578
|
+
* }
|
|
2579
|
+
* ```
|
|
2580
|
+
*
|
|
2581
|
+
* @see IAction for all available event methods
|
|
2582
|
+
* @see TActionCtor for constructor signature requirements
|
|
2583
|
+
* @see ClientAction for internal wrapper that manages lifecycle
|
|
2584
|
+
*/
|
|
2585
|
+
interface IPublicAction extends IAction {
|
|
3025
2586
|
/**
|
|
3026
|
-
*
|
|
3027
|
-
* Must be greater than (slippage + fees) to ensure profitable trades
|
|
2587
|
+
* Async initialization method called once after construction.
|
|
3028
2588
|
*
|
|
3029
|
-
*
|
|
3030
|
-
* -
|
|
3031
|
-
* -
|
|
3032
|
-
* -
|
|
3033
|
-
* -
|
|
2589
|
+
* Use this method to:
|
|
2590
|
+
* - Establish database connections
|
|
2591
|
+
* - Initialize API clients
|
|
2592
|
+
* - Load configuration files
|
|
2593
|
+
* - Open file handles or network sockets
|
|
2594
|
+
* - Perform any async setup required before handling events
|
|
3034
2595
|
*
|
|
3035
|
-
*
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
*
|
|
3040
|
-
*
|
|
3041
|
-
*
|
|
2596
|
+
* Guaranteed to:
|
|
2597
|
+
* - Run exactly once per action handler instance
|
|
2598
|
+
* - Complete before any event methods are called
|
|
2599
|
+
* - Run after constructor but before first event
|
|
2600
|
+
*
|
|
2601
|
+
* @returns Promise that resolves when initialization is complete
|
|
2602
|
+
* @throws Error if initialization fails (will prevent strategy execution)
|
|
2603
|
+
*
|
|
2604
|
+
* @example
|
|
2605
|
+
* ```typescript
|
|
2606
|
+
* async init() {
|
|
2607
|
+
* this.db = await connectToDatabase();
|
|
2608
|
+
* this.cache = new Redis(process.env.REDIS_URL);
|
|
2609
|
+
* await this.cache.connect();
|
|
2610
|
+
* console.log('Action initialized');
|
|
2611
|
+
* }
|
|
2612
|
+
* ```
|
|
3042
2613
|
*/
|
|
3043
|
-
|
|
2614
|
+
init(): void | Promise<void>;
|
|
2615
|
+
}
|
|
2616
|
+
/**
|
|
2617
|
+
* Action interface for state manager integration.
|
|
2618
|
+
*
|
|
2619
|
+
* Provides methods to handle all events emitted by connection services.
|
|
2620
|
+
* Each method corresponds to a specific event type emitted via .next() calls.
|
|
2621
|
+
*
|
|
2622
|
+
* Use this interface to implement custom state management logic:
|
|
2623
|
+
* - Redux/Zustand action dispatchers
|
|
2624
|
+
* - Event logging systems
|
|
2625
|
+
* - Real-time monitoring dashboards
|
|
2626
|
+
* - Analytics and metrics collection
|
|
2627
|
+
*
|
|
2628
|
+
* @example
|
|
2629
|
+
* ```typescript
|
|
2630
|
+
* class ReduxStateManager implements IAction {
|
|
2631
|
+
* constructor(private store: Store) {}
|
|
2632
|
+
*
|
|
2633
|
+
* signal(event: IStrategyTickResult): void {
|
|
2634
|
+
* this.store.dispatch({ type: 'SIGNAL', payload: event });
|
|
2635
|
+
* }
|
|
2636
|
+
*
|
|
2637
|
+
* breakeven(event: BreakevenContract): void {
|
|
2638
|
+
* this.store.dispatch({ type: 'BREAKEVEN', payload: event });
|
|
2639
|
+
* }
|
|
2640
|
+
*
|
|
2641
|
+
* // ... implement other methods
|
|
2642
|
+
* }
|
|
2643
|
+
* ```
|
|
2644
|
+
*/
|
|
2645
|
+
interface IAction {
|
|
3044
2646
|
/**
|
|
3045
|
-
*
|
|
3046
|
-
*
|
|
3047
|
-
*
|
|
2647
|
+
* Handles signal events from all modes (live + backtest).
|
|
2648
|
+
*
|
|
2649
|
+
* Emitted by: StrategyConnectionService via signalEmitter
|
|
2650
|
+
* Source: StrategyConnectionService.tick() and StrategyConnectionService.backtest()
|
|
2651
|
+
* Frequency: Every tick/candle when strategy is evaluated
|
|
2652
|
+
*
|
|
2653
|
+
* @param event - Signal state result (idle, scheduled, opened, active, closed, cancelled)
|
|
3048
2654
|
*/
|
|
3049
|
-
|
|
2655
|
+
signal(event: IStrategyTickResult): void | Promise<void>;
|
|
3050
2656
|
/**
|
|
3051
|
-
*
|
|
3052
|
-
*
|
|
3053
|
-
*
|
|
2657
|
+
* Handles signal events from live trading only.
|
|
2658
|
+
*
|
|
2659
|
+
* Emitted by: StrategyConnectionService via signalLiveEmitter
|
|
2660
|
+
* Source: StrategyConnectionService.tick() when backtest=false
|
|
2661
|
+
* Frequency: Every tick in live mode
|
|
2662
|
+
*
|
|
2663
|
+
* @param event - Signal state result from live trading
|
|
3054
2664
|
*/
|
|
3055
|
-
|
|
2665
|
+
signalLive(event: IStrategyTickResult): void | Promise<void>;
|
|
3056
2666
|
/**
|
|
3057
|
-
*
|
|
3058
|
-
* Prevents long-running or stuck signal generation routines from blocking
|
|
3059
|
-
* execution or consuming resources indefinitely. If generation exceeds this
|
|
3060
|
-
* threshold the attempt should be aborted, logged and optionally retried.
|
|
2667
|
+
* Handles signal events from backtest only.
|
|
3061
2668
|
*
|
|
3062
|
-
*
|
|
2669
|
+
* Emitted by: StrategyConnectionService via signalBacktestEmitter
|
|
2670
|
+
* Source: StrategyConnectionService.backtest() when backtest=true
|
|
2671
|
+
* Frequency: Every candle in backtest mode
|
|
2672
|
+
*
|
|
2673
|
+
* @param event - Signal state result from backtest
|
|
3063
2674
|
*/
|
|
3064
|
-
|
|
2675
|
+
signalBacktest(event: IStrategyTickResult): void | Promise<void>;
|
|
3065
2676
|
/**
|
|
3066
|
-
*
|
|
3067
|
-
*
|
|
2677
|
+
* Handles breakeven events when stop-loss is moved to entry price.
|
|
2678
|
+
*
|
|
2679
|
+
* Emitted by: BreakevenConnectionService via breakevenSubject
|
|
2680
|
+
* Source: COMMIT_BREAKEVEN_FN callback in BreakevenConnectionService
|
|
2681
|
+
* Frequency: Once per signal when breakeven threshold is reached
|
|
2682
|
+
*
|
|
2683
|
+
* @param event - Breakeven milestone data
|
|
3068
2684
|
*/
|
|
3069
|
-
|
|
2685
|
+
breakeven(event: BreakevenContract): void | Promise<void>;
|
|
3070
2686
|
/**
|
|
3071
|
-
*
|
|
3072
|
-
*
|
|
2687
|
+
* Handles partial profit level events (10%, 20%, 30%, etc).
|
|
2688
|
+
*
|
|
2689
|
+
* Emitted by: PartialConnectionService via partialProfitSubject
|
|
2690
|
+
* Source: COMMIT_PROFIT_FN callback in PartialConnectionService
|
|
2691
|
+
* Frequency: Once per profit level per signal (deduplicated)
|
|
2692
|
+
*
|
|
2693
|
+
* @param event - Profit milestone data with level and price
|
|
3073
2694
|
*/
|
|
3074
|
-
|
|
2695
|
+
partialProfit(event: PartialProfitContract): void | Promise<void>;
|
|
3075
2696
|
/**
|
|
3076
|
-
*
|
|
3077
|
-
*
|
|
3078
|
-
*
|
|
2697
|
+
* Handles partial loss level events (-10%, -20%, -30%, etc).
|
|
2698
|
+
*
|
|
2699
|
+
* Emitted by: PartialConnectionService via partialLossSubject
|
|
2700
|
+
* Source: COMMIT_LOSS_FN callback in PartialConnectionService
|
|
2701
|
+
* Frequency: Once per loss level per signal (deduplicated)
|
|
2702
|
+
*
|
|
2703
|
+
* @param event - Loss milestone data with level and price
|
|
3079
2704
|
*/
|
|
3080
|
-
|
|
2705
|
+
partialLoss(event: PartialLossContract): void | Promise<void>;
|
|
3081
2706
|
/**
|
|
3082
|
-
*
|
|
3083
|
-
* Price should not be more than this factor lower than reference price.
|
|
2707
|
+
* Handles ping events during scheduled signal monitoring.
|
|
3084
2708
|
*
|
|
3085
|
-
*
|
|
3086
|
-
*
|
|
3087
|
-
*
|
|
3088
|
-
* - Factor 1000 catches prices below $20-100 when median is $20,000-100,000
|
|
3089
|
-
* - Factor 100 would be too permissive (allows $200 when median is $20,000)
|
|
3090
|
-
* - Factor 10000 might be too strict for low-cap altcoins
|
|
2709
|
+
* Emitted by: StrategyConnectionService via pingSubject
|
|
2710
|
+
* Source: COMMIT_PING_FN callback in StrategyConnectionService
|
|
2711
|
+
* Frequency: Every minute while scheduled signal is waiting for activation
|
|
3091
2712
|
*
|
|
3092
|
-
*
|
|
2713
|
+
* @param event - Scheduled signal monitoring data
|
|
3093
2714
|
*/
|
|
3094
|
-
|
|
2715
|
+
ping(event: PingContract): void | Promise<void>;
|
|
3095
2716
|
/**
|
|
3096
|
-
*
|
|
3097
|
-
* Below this threshold, use simple average instead of median.
|
|
2717
|
+
* Handles risk rejection events when signals fail risk validation.
|
|
3098
2718
|
*
|
|
3099
|
-
*
|
|
3100
|
-
*
|
|
3101
|
-
*
|
|
3102
|
-
* - Below 5 candles, single anomaly can heavily skew median
|
|
3103
|
-
* - Statistical rule of thumb: minimum 7-10 data points for median stability
|
|
3104
|
-
* - Average is more stable than median for small datasets (n < 20)
|
|
3105
|
-
*
|
|
3106
|
-
* Example: 3 candles = 12 points (use average), 5 candles = 20 points (use median)
|
|
3107
|
-
*/
|
|
3108
|
-
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: number;
|
|
3109
|
-
/**
|
|
3110
|
-
* Controls visibility of signal notes in markdown report tables.
|
|
3111
|
-
* When enabled, the "Note" column will be displayed in all markdown reports
|
|
3112
|
-
* (backtest, live, schedule, risk, etc.)
|
|
3113
|
-
*
|
|
3114
|
-
* Default: false (notes are hidden to reduce table width and improve readability)
|
|
3115
|
-
*/
|
|
3116
|
-
CC_REPORT_SHOW_SIGNAL_NOTE: boolean;
|
|
3117
|
-
/**
|
|
3118
|
-
* Breakeven threshold percentage - minimum profit distance from entry to enable breakeven.
|
|
3119
|
-
* When price moves this percentage in profit direction, stop-loss can be moved to entry (breakeven).
|
|
3120
|
-
*
|
|
3121
|
-
* Calculation:
|
|
3122
|
-
* - Slippage effect: ~0.2% (0.1% × 2 transactions)
|
|
3123
|
-
* - Fees: 0.2% (0.1% × 2 transactions)
|
|
3124
|
-
* - Total: 0.4%
|
|
3125
|
-
* - Added buffer: 0.2%
|
|
3126
|
-
* - Overall: 0.6%
|
|
3127
|
-
*
|
|
3128
|
-
* Default: 0.2% (additional buffer above costs to ensure no loss when moving to breakeven)
|
|
3129
|
-
*/
|
|
3130
|
-
CC_BREAKEVEN_THRESHOLD: number;
|
|
3131
|
-
/**
|
|
3132
|
-
* Time offset in minutes for order book fetching.
|
|
3133
|
-
* Subtracts this amount from the current time when fetching order book data.
|
|
3134
|
-
* This helps get a more stable snapshot of the order book by avoiding real-time volatility.
|
|
2719
|
+
* Emitted by: RiskConnectionService via riskSubject
|
|
2720
|
+
* Source: COMMIT_REJECTION_FN callback in RiskConnectionService
|
|
2721
|
+
* Frequency: Only when signal is rejected (not emitted for allowed signals)
|
|
3135
2722
|
*
|
|
3136
|
-
*
|
|
2723
|
+
* @param event - Risk rejection data with reason and context
|
|
3137
2724
|
*/
|
|
3138
|
-
|
|
2725
|
+
riskRejection(event: RiskContract): void | Promise<void>;
|
|
3139
2726
|
/**
|
|
3140
|
-
*
|
|
3141
|
-
* Specifies how many price levels to fetch from both bids and asks.
|
|
2727
|
+
* Cleans up resources and subscriptions when action handler is no longer needed.
|
|
3142
2728
|
*
|
|
3143
|
-
*
|
|
2729
|
+
* Called by: Connection services during shutdown
|
|
2730
|
+
* Use for: Unsubscribing from observables, closing connections, flushing buffers
|
|
3144
2731
|
*/
|
|
3145
|
-
|
|
3146
|
-
}
|
|
3147
|
-
/**
|
|
3148
|
-
* Type for global configuration object.
|
|
3149
|
-
*/
|
|
3150
|
-
type GlobalConfig = typeof GLOBAL_CONFIG;
|
|
3151
|
-
|
|
3152
|
-
/**
|
|
3153
|
-
* Mapping of available table/markdown reports to their column definitions.
|
|
3154
|
-
*
|
|
3155
|
-
* Each property references a column definition object imported from
|
|
3156
|
-
* `src/assets/*.columns`. These are used by markdown/report generators
|
|
3157
|
-
* (backtest, live, schedule, risk, heat, performance, partial, walker).
|
|
3158
|
-
*/
|
|
3159
|
-
declare const COLUMN_CONFIG: {
|
|
3160
|
-
/** Columns used in backtest markdown tables and reports */
|
|
3161
|
-
backtest_columns: ColumnModel<IStrategyTickResultClosed>[];
|
|
3162
|
-
/** Columns used by heatmap / heat reports */
|
|
3163
|
-
heat_columns: ColumnModel<IHeatmapRow>[];
|
|
3164
|
-
/** Columns for live trading reports and logs */
|
|
3165
|
-
live_columns: ColumnModel<TickEvent>[];
|
|
3166
|
-
/** Columns for partial-results / incremental reports */
|
|
3167
|
-
partial_columns: ColumnModel<PartialEvent>[];
|
|
3168
|
-
/** Columns for breakeven protection events */
|
|
3169
|
-
breakeven_columns: ColumnModel<BreakevenEvent>[];
|
|
3170
|
-
/** Columns for performance summary reports */
|
|
3171
|
-
performance_columns: ColumnModel<MetricStats>[];
|
|
3172
|
-
/** Columns for risk-related reports */
|
|
3173
|
-
risk_columns: ColumnModel<RiskEvent>[];
|
|
3174
|
-
/** Columns for scheduled report output */
|
|
3175
|
-
schedule_columns: ColumnModel<ScheduledEvent>[];
|
|
3176
|
-
/** Walker: PnL summary columns */
|
|
3177
|
-
walker_pnl_columns: ColumnModel<SignalData$1>[];
|
|
3178
|
-
/** Walker: strategy-level summary columns */
|
|
3179
|
-
walker_strategy_columns: ColumnModel<IStrategyResult>[];
|
|
3180
|
-
};
|
|
2732
|
+
dispose(): void | Promise<void>;
|
|
2733
|
+
}
|
|
3181
2734
|
/**
|
|
3182
|
-
*
|
|
2735
|
+
* Unique action identifier.
|
|
3183
2736
|
*/
|
|
3184
|
-
type
|
|
2737
|
+
type ActionName = string;
|
|
3185
2738
|
|
|
3186
2739
|
/**
|
|
3187
|
-
*
|
|
3188
|
-
*
|
|
3189
|
-
* All log messages from internal services will be forwarded to the provided logger
|
|
3190
|
-
* with automatic context injection (strategyName, exchangeName, symbol, etc.).
|
|
2740
|
+
* Statistical data calculated from backtest results.
|
|
3191
2741
|
*
|
|
3192
|
-
*
|
|
2742
|
+
* All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
|
|
2743
|
+
* Provides comprehensive metrics for strategy performance analysis.
|
|
3193
2744
|
*
|
|
3194
2745
|
* @example
|
|
3195
2746
|
* ```typescript
|
|
3196
|
-
*
|
|
3197
|
-
* log: (topic, ...args) => console.log(topic, args),
|
|
3198
|
-
* debug: (topic, ...args) => console.debug(topic, args),
|
|
3199
|
-
* info: (topic, ...args) => console.info(topic, args),
|
|
3200
|
-
* });
|
|
3201
|
-
* ```
|
|
3202
|
-
*/
|
|
3203
|
-
declare function setLogger(logger: ILogger): void;
|
|
3204
|
-
/**
|
|
3205
|
-
* Sets global configuration parameters for the framework.
|
|
3206
|
-
* @param config - Partial configuration object to override default settings
|
|
3207
|
-
* @param _unsafe - Skip config validations - required for testbed
|
|
2747
|
+
* const stats = await Backtest.getData("my-strategy");
|
|
3208
2748
|
*
|
|
3209
|
-
*
|
|
3210
|
-
*
|
|
3211
|
-
*
|
|
3212
|
-
*
|
|
2749
|
+
* console.log(`Total signals: ${stats.totalSignals}`);
|
|
2750
|
+
* console.log(`Win rate: ${stats.winRate}%`);
|
|
2751
|
+
* console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
|
|
2752
|
+
*
|
|
2753
|
+
* // Access raw signal data
|
|
2754
|
+
* stats.signalList.forEach(signal => {
|
|
2755
|
+
* console.log(`Signal ${signal.signal.id}: ${signal.pnl.pnlPercentage}%`);
|
|
3213
2756
|
* });
|
|
3214
2757
|
* ```
|
|
3215
2758
|
*/
|
|
3216
|
-
|
|
2759
|
+
interface BacktestStatisticsModel {
|
|
2760
|
+
/** Array of all closed signals with full details (price, PNL, timestamps, etc.) */
|
|
2761
|
+
signalList: IStrategyTickResultClosed[];
|
|
2762
|
+
/** Total number of closed signals */
|
|
2763
|
+
totalSignals: number;
|
|
2764
|
+
/** Number of winning signals (PNL > 0) */
|
|
2765
|
+
winCount: number;
|
|
2766
|
+
/** Number of losing signals (PNL < 0) */
|
|
2767
|
+
lossCount: number;
|
|
2768
|
+
/** Win rate as percentage (0-100), null if unsafe. Higher is better. */
|
|
2769
|
+
winRate: number | null;
|
|
2770
|
+
/** Average PNL per signal as percentage, null if unsafe. Higher is better. */
|
|
2771
|
+
avgPnl: number | null;
|
|
2772
|
+
/** Cumulative PNL across all signals as percentage, null if unsafe. Higher is better. */
|
|
2773
|
+
totalPnl: number | null;
|
|
2774
|
+
/** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
|
|
2775
|
+
stdDev: number | null;
|
|
2776
|
+
/** Sharpe Ratio (risk-adjusted return = avgPnl / stdDev), null if unsafe. Higher is better. */
|
|
2777
|
+
sharpeRatio: number | null;
|
|
2778
|
+
/** Annualized Sharpe Ratio (sharpeRatio × √365), null if unsafe. Higher is better. */
|
|
2779
|
+
annualizedSharpeRatio: number | null;
|
|
2780
|
+
/** Certainty Ratio (avgWin / |avgLoss|), null if unsafe. Higher is better. */
|
|
2781
|
+
certaintyRatio: number | null;
|
|
2782
|
+
/** Expected yearly returns based on average trade duration and PNL, null if unsafe. Higher is better. */
|
|
2783
|
+
expectedYearlyReturns: number | null;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
3217
2786
|
/**
|
|
3218
|
-
*
|
|
3219
|
-
*
|
|
3220
|
-
* Returns a shallow copy of the current GLOBAL_CONFIG to prevent accidental mutations.
|
|
3221
|
-
* Use this to inspect the current configuration state without modifying it.
|
|
2787
|
+
* Contract for walker completion events.
|
|
3222
2788
|
*
|
|
3223
|
-
*
|
|
2789
|
+
* Emitted when all strategies have been tested and final results are available.
|
|
2790
|
+
* Contains complete results of the walker comparison including the best strategy.
|
|
3224
2791
|
*
|
|
3225
2792
|
* @example
|
|
3226
2793
|
* ```typescript
|
|
3227
|
-
*
|
|
3228
|
-
*
|
|
2794
|
+
* import { walkerCompleteSubject } from "backtest-kit";
|
|
2795
|
+
*
|
|
2796
|
+
* walkerCompleteSubject
|
|
2797
|
+
* .filter((event) => event.symbol === "BTCUSDT")
|
|
2798
|
+
* .connect((event) => {
|
|
2799
|
+
* console.log("Walker completed:", event.walkerName);
|
|
2800
|
+
* console.log("Best strategy:", event.bestStrategy);
|
|
2801
|
+
* console.log("Best metric:", event.bestMetric);
|
|
2802
|
+
* });
|
|
3229
2803
|
* ```
|
|
3230
2804
|
*/
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
}
|
|
2805
|
+
interface WalkerCompleteContract {
|
|
2806
|
+
/** walkerName - Walker name */
|
|
2807
|
+
walkerName: WalkerName;
|
|
2808
|
+
/** symbol - Symbol tested */
|
|
2809
|
+
symbol: string;
|
|
2810
|
+
/** exchangeName - Exchange used */
|
|
2811
|
+
exchangeName: ExchangeName;
|
|
2812
|
+
/** frameName - Frame used */
|
|
2813
|
+
frameName: FrameName;
|
|
2814
|
+
/** metric - Metric used for optimization */
|
|
2815
|
+
metric: WalkerMetric;
|
|
2816
|
+
/** totalStrategies - Total number of strategies tested */
|
|
2817
|
+
totalStrategies: number;
|
|
2818
|
+
/** bestStrategy - Best performing strategy name */
|
|
2819
|
+
bestStrategy: StrategyName | null;
|
|
2820
|
+
/** bestMetric - Best metric value achieved */
|
|
2821
|
+
bestMetric: number | null;
|
|
2822
|
+
/** bestStats - Best strategy statistics */
|
|
2823
|
+
bestStats: BacktestStatisticsModel | null;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
3251
2826
|
/**
|
|
3252
|
-
*
|
|
3253
|
-
*
|
|
3254
|
-
* Returns a reference to the default configuration with all preset values.
|
|
3255
|
-
* Use this to see what configuration options are available and their default values.
|
|
3256
|
-
*
|
|
3257
|
-
* @returns {GlobalConfig} The default configuration object
|
|
3258
|
-
*
|
|
3259
|
-
* @example
|
|
3260
|
-
* ```typescript
|
|
3261
|
-
* const defaultConfig = getDefaultConfig();
|
|
3262
|
-
* console.log(defaultConfig.CC_SCHEDULE_AWAIT_MINUTES);
|
|
3263
|
-
* ```
|
|
2827
|
+
* Optimization metric for comparing strategies.
|
|
2828
|
+
* Higher values are always better (metric is maximized).
|
|
3264
2829
|
*/
|
|
3265
|
-
|
|
3266
|
-
CC_SCHEDULE_AWAIT_MINUTES: number;
|
|
3267
|
-
CC_AVG_PRICE_CANDLES_COUNT: number;
|
|
3268
|
-
CC_PERCENT_SLIPPAGE: number;
|
|
3269
|
-
CC_PERCENT_FEE: number;
|
|
3270
|
-
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: number;
|
|
3271
|
-
CC_MIN_STOPLOSS_DISTANCE_PERCENT: number;
|
|
3272
|
-
CC_MAX_STOPLOSS_DISTANCE_PERCENT: number;
|
|
3273
|
-
CC_MAX_SIGNAL_LIFETIME_MINUTES: number;
|
|
3274
|
-
CC_MAX_SIGNAL_GENERATION_SECONDS: number;
|
|
3275
|
-
CC_GET_CANDLES_RETRY_COUNT: number;
|
|
3276
|
-
CC_GET_CANDLES_RETRY_DELAY_MS: number;
|
|
3277
|
-
CC_MAX_CANDLES_PER_REQUEST: number;
|
|
3278
|
-
CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR: number;
|
|
3279
|
-
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: number;
|
|
3280
|
-
CC_REPORT_SHOW_SIGNAL_NOTE: boolean;
|
|
3281
|
-
CC_BREAKEVEN_THRESHOLD: number;
|
|
3282
|
-
CC_ORDER_BOOK_TIME_OFFSET_MINUTES: number;
|
|
3283
|
-
CC_ORDER_BOOK_MAX_DEPTH_LEVELS: number;
|
|
3284
|
-
}>;
|
|
3285
|
-
/**
|
|
3286
|
-
* Sets custom column configurations for markdown report generation.
|
|
3287
|
-
*
|
|
3288
|
-
* Allows overriding default column definitions for any report type.
|
|
3289
|
-
* All columns are validated before assignment to ensure structural correctness.
|
|
3290
|
-
*
|
|
3291
|
-
* @param columns - Partial column configuration object to override default column settings
|
|
3292
|
-
* @param _unsafe - Skip column validations - required for testbed
|
|
3293
|
-
*
|
|
3294
|
-
* @example
|
|
3295
|
-
* ```typescript
|
|
3296
|
-
* setColumns({
|
|
3297
|
-
* backtest_columns: [
|
|
3298
|
-
* {
|
|
3299
|
-
* key: "customId",
|
|
3300
|
-
* label: "Custom ID",
|
|
3301
|
-
* format: (data) => data.signal.id,
|
|
3302
|
-
* isVisible: () => true
|
|
3303
|
-
* }
|
|
3304
|
-
* ],
|
|
3305
|
-
* });
|
|
3306
|
-
* ```
|
|
3307
|
-
*
|
|
3308
|
-
* @throws {Error} If column configuration is invalid
|
|
3309
|
-
*/
|
|
3310
|
-
declare function setColumns(columns: Partial<ColumnConfig>, _unsafe?: boolean): void;
|
|
3311
|
-
/**
|
|
3312
|
-
* Retrieves a copy of the current column configuration for markdown report generation.
|
|
3313
|
-
*
|
|
3314
|
-
* Returns a shallow copy of the current COLUMN_CONFIG to prevent accidental mutations.
|
|
3315
|
-
* Use this to inspect the current column definitions without modifying them.
|
|
3316
|
-
*
|
|
3317
|
-
* @returns {ColumnConfig} A copy of the current column configuration object
|
|
3318
|
-
*
|
|
3319
|
-
* @example
|
|
3320
|
-
* ```typescript
|
|
3321
|
-
* const currentColumns = getColumns();
|
|
3322
|
-
* console.log(currentColumns.backtest_columns.length);
|
|
3323
|
-
* ```
|
|
3324
|
-
*/
|
|
3325
|
-
declare function getColumns(): {
|
|
3326
|
-
backtest_columns: ColumnModel<IStrategyTickResultClosed>[];
|
|
3327
|
-
heat_columns: ColumnModel<IHeatmapRow>[];
|
|
3328
|
-
live_columns: ColumnModel<TickEvent>[];
|
|
3329
|
-
partial_columns: ColumnModel<PartialEvent>[];
|
|
3330
|
-
breakeven_columns: ColumnModel<BreakevenEvent>[];
|
|
3331
|
-
performance_columns: ColumnModel<MetricStats>[];
|
|
3332
|
-
risk_columns: ColumnModel<RiskEvent>[];
|
|
3333
|
-
schedule_columns: ColumnModel<ScheduledEvent>[];
|
|
3334
|
-
walker_pnl_columns: ColumnModel<SignalData$1>[];
|
|
3335
|
-
walker_strategy_columns: ColumnModel<IStrategyResult>[];
|
|
3336
|
-
};
|
|
3337
|
-
/**
|
|
3338
|
-
* Retrieves the default column configuration object for markdown report generation.
|
|
3339
|
-
*
|
|
3340
|
-
* Returns a reference to the default column definitions with all preset values.
|
|
3341
|
-
* Use this to see what column options are available and their default definitions.
|
|
3342
|
-
*
|
|
3343
|
-
* @returns {ColumnConfig} The default column configuration object
|
|
3344
|
-
*
|
|
3345
|
-
* @example
|
|
3346
|
-
* ```typescript
|
|
3347
|
-
* const defaultColumns = getDefaultColumns();
|
|
3348
|
-
* console.log(defaultColumns.backtest_columns);
|
|
3349
|
-
* ```
|
|
3350
|
-
*/
|
|
3351
|
-
declare function getDefaultColumns(): Readonly<{
|
|
3352
|
-
backtest_columns: ColumnModel<IStrategyTickResultClosed>[];
|
|
3353
|
-
heat_columns: ColumnModel<IHeatmapRow>[];
|
|
3354
|
-
live_columns: ColumnModel<TickEvent>[];
|
|
3355
|
-
partial_columns: ColumnModel<PartialEvent>[];
|
|
3356
|
-
breakeven_columns: ColumnModel<BreakevenEvent>[];
|
|
3357
|
-
performance_columns: ColumnModel<MetricStats>[];
|
|
3358
|
-
risk_columns: ColumnModel<RiskEvent>[];
|
|
3359
|
-
schedule_columns: ColumnModel<ScheduledEvent>[];
|
|
3360
|
-
walker_pnl_columns: ColumnModel<SignalData$1>[];
|
|
3361
|
-
walker_strategy_columns: ColumnModel<IStrategyResult>[];
|
|
3362
|
-
}>;
|
|
3363
|
-
|
|
3364
|
-
/**
|
|
3365
|
-
* Statistical data calculated from backtest results.
|
|
3366
|
-
*
|
|
3367
|
-
* All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
|
|
3368
|
-
* Provides comprehensive metrics for strategy performance analysis.
|
|
3369
|
-
*
|
|
3370
|
-
* @example
|
|
3371
|
-
* ```typescript
|
|
3372
|
-
* const stats = await Backtest.getData("my-strategy");
|
|
3373
|
-
*
|
|
3374
|
-
* console.log(`Total signals: ${stats.totalSignals}`);
|
|
3375
|
-
* console.log(`Win rate: ${stats.winRate}%`);
|
|
3376
|
-
* console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
|
|
3377
|
-
*
|
|
3378
|
-
* // Access raw signal data
|
|
3379
|
-
* stats.signalList.forEach(signal => {
|
|
3380
|
-
* console.log(`Signal ${signal.signal.id}: ${signal.pnl.pnlPercentage}%`);
|
|
3381
|
-
* });
|
|
3382
|
-
* ```
|
|
3383
|
-
*/
|
|
3384
|
-
interface BacktestStatisticsModel {
|
|
3385
|
-
/** Array of all closed signals with full details (price, PNL, timestamps, etc.) */
|
|
3386
|
-
signalList: IStrategyTickResultClosed[];
|
|
3387
|
-
/** Total number of closed signals */
|
|
3388
|
-
totalSignals: number;
|
|
3389
|
-
/** Number of winning signals (PNL > 0) */
|
|
3390
|
-
winCount: number;
|
|
3391
|
-
/** Number of losing signals (PNL < 0) */
|
|
3392
|
-
lossCount: number;
|
|
3393
|
-
/** Win rate as percentage (0-100), null if unsafe. Higher is better. */
|
|
3394
|
-
winRate: number | null;
|
|
3395
|
-
/** Average PNL per signal as percentage, null if unsafe. Higher is better. */
|
|
3396
|
-
avgPnl: number | null;
|
|
3397
|
-
/** Cumulative PNL across all signals as percentage, null if unsafe. Higher is better. */
|
|
3398
|
-
totalPnl: number | null;
|
|
3399
|
-
/** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
|
|
3400
|
-
stdDev: number | null;
|
|
3401
|
-
/** Sharpe Ratio (risk-adjusted return = avgPnl / stdDev), null if unsafe. Higher is better. */
|
|
3402
|
-
sharpeRatio: number | null;
|
|
3403
|
-
/** Annualized Sharpe Ratio (sharpeRatio × √365), null if unsafe. Higher is better. */
|
|
3404
|
-
annualizedSharpeRatio: number | null;
|
|
3405
|
-
/** Certainty Ratio (avgWin / |avgLoss|), null if unsafe. Higher is better. */
|
|
3406
|
-
certaintyRatio: number | null;
|
|
3407
|
-
/** Expected yearly returns based on average trade duration and PNL, null if unsafe. Higher is better. */
|
|
3408
|
-
expectedYearlyReturns: number | null;
|
|
3409
|
-
}
|
|
3410
|
-
|
|
3411
|
-
/**
|
|
3412
|
-
* Contract for walker completion events.
|
|
3413
|
-
*
|
|
3414
|
-
* Emitted when all strategies have been tested and final results are available.
|
|
3415
|
-
* Contains complete results of the walker comparison including the best strategy.
|
|
3416
|
-
*
|
|
3417
|
-
* @example
|
|
3418
|
-
* ```typescript
|
|
3419
|
-
* import { walkerCompleteSubject } from "backtest-kit";
|
|
3420
|
-
*
|
|
3421
|
-
* walkerCompleteSubject
|
|
3422
|
-
* .filter((event) => event.symbol === "BTCUSDT")
|
|
3423
|
-
* .connect((event) => {
|
|
3424
|
-
* console.log("Walker completed:", event.walkerName);
|
|
3425
|
-
* console.log("Best strategy:", event.bestStrategy);
|
|
3426
|
-
* console.log("Best metric:", event.bestMetric);
|
|
3427
|
-
* });
|
|
3428
|
-
* ```
|
|
3429
|
-
*/
|
|
3430
|
-
interface WalkerCompleteContract {
|
|
3431
|
-
/** walkerName - Walker name */
|
|
3432
|
-
walkerName: WalkerName;
|
|
3433
|
-
/** symbol - Symbol tested */
|
|
3434
|
-
symbol: string;
|
|
3435
|
-
/** exchangeName - Exchange used */
|
|
3436
|
-
exchangeName: ExchangeName;
|
|
3437
|
-
/** frameName - Frame used */
|
|
3438
|
-
frameName: FrameName;
|
|
3439
|
-
/** metric - Metric used for optimization */
|
|
3440
|
-
metric: WalkerMetric;
|
|
3441
|
-
/** totalStrategies - Total number of strategies tested */
|
|
3442
|
-
totalStrategies: number;
|
|
3443
|
-
/** bestStrategy - Best performing strategy name */
|
|
3444
|
-
bestStrategy: StrategyName | null;
|
|
3445
|
-
/** bestMetric - Best metric value achieved */
|
|
3446
|
-
bestMetric: number | null;
|
|
3447
|
-
/** bestStats - Best strategy statistics */
|
|
3448
|
-
bestStats: BacktestStatisticsModel | null;
|
|
3449
|
-
}
|
|
3450
|
-
|
|
3451
|
-
/**
|
|
3452
|
-
* Optimization metric for comparing strategies.
|
|
3453
|
-
* Higher values are always better (metric is maximized).
|
|
3454
|
-
*/
|
|
3455
|
-
type WalkerMetric = "sharpeRatio" | "annualizedSharpeRatio" | "winRate" | "totalPnl" | "certaintyRatio" | "avgPnl" | "expectedYearlyReturns";
|
|
2830
|
+
type WalkerMetric = "sharpeRatio" | "annualizedSharpeRatio" | "winRate" | "totalPnl" | "certaintyRatio" | "avgPnl" | "expectedYearlyReturns";
|
|
3456
2831
|
/**
|
|
3457
2832
|
* Walker schema registered via addWalker().
|
|
3458
2833
|
* Defines A/B testing configuration for multiple strategies.
|
|
@@ -4129,6 +3504,766 @@ interface IOptimizer {
|
|
|
4129
3504
|
*/
|
|
4130
3505
|
type OptimizerName = string;
|
|
4131
3506
|
|
|
3507
|
+
/**
|
|
3508
|
+
* Retrieves a registered strategy schema by name.
|
|
3509
|
+
*
|
|
3510
|
+
* @param strategyName - Unique strategy identifier
|
|
3511
|
+
* @returns The strategy schema configuration object
|
|
3512
|
+
* @throws Error if strategy is not registered
|
|
3513
|
+
*
|
|
3514
|
+
* @example
|
|
3515
|
+
* ```typescript
|
|
3516
|
+
* const strategy = getStrategy("my-strategy");
|
|
3517
|
+
* console.log(strategy.interval); // "5m"
|
|
3518
|
+
* console.log(strategy.getSignal); // async function
|
|
3519
|
+
* ```
|
|
3520
|
+
*/
|
|
3521
|
+
declare function getStrategy(strategyName: StrategyName): IStrategySchema;
|
|
3522
|
+
/**
|
|
3523
|
+
* Retrieves a registered exchange schema by name.
|
|
3524
|
+
*
|
|
3525
|
+
* @param exchangeName - Unique exchange identifier
|
|
3526
|
+
* @returns The exchange schema configuration object
|
|
3527
|
+
* @throws Error if exchange is not registered
|
|
3528
|
+
*
|
|
3529
|
+
* @example
|
|
3530
|
+
* ```typescript
|
|
3531
|
+
* const exchange = getExchange("binance");
|
|
3532
|
+
* console.log(exchange.getCandles); // async function
|
|
3533
|
+
* console.log(exchange.formatPrice); // async function
|
|
3534
|
+
* ```
|
|
3535
|
+
*/
|
|
3536
|
+
declare function getExchange(exchangeName: ExchangeName): IExchangeSchema;
|
|
3537
|
+
/**
|
|
3538
|
+
* Retrieves a registered frame schema by name.
|
|
3539
|
+
*
|
|
3540
|
+
* @param frameName - Unique frame identifier
|
|
3541
|
+
* @returns The frame schema configuration object
|
|
3542
|
+
* @throws Error if frame is not registered
|
|
3543
|
+
*
|
|
3544
|
+
* @example
|
|
3545
|
+
* ```typescript
|
|
3546
|
+
* const frame = getFrame("1d-backtest");
|
|
3547
|
+
* console.log(frame.interval); // "1m"
|
|
3548
|
+
* console.log(frame.startDate); // Date object
|
|
3549
|
+
* console.log(frame.endDate); // Date object
|
|
3550
|
+
* ```
|
|
3551
|
+
*/
|
|
3552
|
+
declare function getFrame(frameName: FrameName): IFrameSchema;
|
|
3553
|
+
/**
|
|
3554
|
+
* Retrieves a registered walker schema by name.
|
|
3555
|
+
*
|
|
3556
|
+
* @param walkerName - Unique walker identifier
|
|
3557
|
+
* @returns The walker schema configuration object
|
|
3558
|
+
* @throws Error if walker is not registered
|
|
3559
|
+
*
|
|
3560
|
+
* @example
|
|
3561
|
+
* ```typescript
|
|
3562
|
+
* const walker = getWalker("llm-prompt-optimizer");
|
|
3563
|
+
* console.log(walker.exchangeName); // "binance"
|
|
3564
|
+
* console.log(walker.frameName); // "1d-backtest"
|
|
3565
|
+
* console.log(walker.strategies); // ["my-strategy-v1", "my-strategy-v2"]
|
|
3566
|
+
* console.log(walker.metric); // "sharpeRatio"
|
|
3567
|
+
* ```
|
|
3568
|
+
*/
|
|
3569
|
+
declare function getWalker(walkerName: WalkerName): IWalkerSchema;
|
|
3570
|
+
/**
|
|
3571
|
+
* Retrieves a registered sizing schema by name.
|
|
3572
|
+
*
|
|
3573
|
+
* @param sizingName - Unique sizing identifier
|
|
3574
|
+
* @returns The sizing schema configuration object
|
|
3575
|
+
* @throws Error if sizing is not registered
|
|
3576
|
+
*
|
|
3577
|
+
* @example
|
|
3578
|
+
* ```typescript
|
|
3579
|
+
* const sizing = getSizing("conservative");
|
|
3580
|
+
* console.log(sizing.method); // "fixed-percentage"
|
|
3581
|
+
* console.log(sizing.riskPercentage); // 1
|
|
3582
|
+
* console.log(sizing.maxPositionPercentage); // 10
|
|
3583
|
+
* ```
|
|
3584
|
+
*/
|
|
3585
|
+
declare function getSizing(sizingName: SizingName): ISizingSchema;
|
|
3586
|
+
/**
|
|
3587
|
+
* Retrieves a registered risk schema by name.
|
|
3588
|
+
*
|
|
3589
|
+
* @param riskName - Unique risk identifier
|
|
3590
|
+
* @returns The risk schema configuration object
|
|
3591
|
+
* @throws Error if risk is not registered
|
|
3592
|
+
*
|
|
3593
|
+
* @example
|
|
3594
|
+
* ```typescript
|
|
3595
|
+
* const risk = getRisk("conservative");
|
|
3596
|
+
* console.log(risk.maxConcurrentPositions); // 5
|
|
3597
|
+
* console.log(risk.validations); // Array of validation functions
|
|
3598
|
+
* ```
|
|
3599
|
+
*/
|
|
3600
|
+
declare function getRisk(riskName: RiskName): IRiskSchema;
|
|
3601
|
+
/**
|
|
3602
|
+
* Retrieves a registered optimizer schema by name.
|
|
3603
|
+
*
|
|
3604
|
+
* @param optimizerName - Unique optimizer identifier
|
|
3605
|
+
* @returns The optimizer schema configuration object
|
|
3606
|
+
* @throws Error if optimizer is not registered
|
|
3607
|
+
*
|
|
3608
|
+
* @example
|
|
3609
|
+
* ```typescript
|
|
3610
|
+
* const optimizer = getOptimizer("llm-strategy-generator");
|
|
3611
|
+
* console.log(optimizer.rangeTrain); // Array of training ranges
|
|
3612
|
+
* console.log(optimizer.rangeTest); // Testing range
|
|
3613
|
+
* console.log(optimizer.source); // Array of data sources
|
|
3614
|
+
* console.log(optimizer.getPrompt); // async function
|
|
3615
|
+
* ```
|
|
3616
|
+
*/
|
|
3617
|
+
declare function getOptimizer(optimizerName: OptimizerName): IOptimizerSchema;
|
|
3618
|
+
/**
|
|
3619
|
+
* Retrieves a registered action schema by name.
|
|
3620
|
+
*
|
|
3621
|
+
* @param actionName - Unique action identifier
|
|
3622
|
+
* @returns The action schema configuration object
|
|
3623
|
+
* @throws Error if action is not registered
|
|
3624
|
+
*
|
|
3625
|
+
* @example
|
|
3626
|
+
* ```typescript
|
|
3627
|
+
* const action = getAction("telegram-notifier");
|
|
3628
|
+
* console.log(action.handler); // Class constructor or object
|
|
3629
|
+
* console.log(action.callbacks); // Optional lifecycle callbacks
|
|
3630
|
+
* ```
|
|
3631
|
+
*/
|
|
3632
|
+
declare function getAction(actionName: ActionName): IActionSchema;
|
|
3633
|
+
|
|
3634
|
+
/**
|
|
3635
|
+
* Stops the strategy from generating new signals.
|
|
3636
|
+
*
|
|
3637
|
+
* Sets internal flag to prevent strategy from opening new signals.
|
|
3638
|
+
* Current active signal (if any) will complete normally.
|
|
3639
|
+
* Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
|
|
3640
|
+
*
|
|
3641
|
+
* Automatically detects backtest/live mode from execution context.
|
|
3642
|
+
*
|
|
3643
|
+
* @param symbol - Trading pair symbol
|
|
3644
|
+
* @param strategyName - Strategy name to stop
|
|
3645
|
+
* @returns Promise that resolves when stop flag is set
|
|
3646
|
+
*
|
|
3647
|
+
* @example
|
|
3648
|
+
* ```typescript
|
|
3649
|
+
* import { stop } from "backtest-kit";
|
|
3650
|
+
*
|
|
3651
|
+
* // Stop strategy after some condition
|
|
3652
|
+
* await stop("BTCUSDT", "my-strategy");
|
|
3653
|
+
* ```
|
|
3654
|
+
*/
|
|
3655
|
+
declare function stop(symbol: string): Promise<void>;
|
|
3656
|
+
/**
|
|
3657
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
3658
|
+
*
|
|
3659
|
+
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
3660
|
+
* Does NOT affect active pending signals or strategy operation.
|
|
3661
|
+
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
3662
|
+
*
|
|
3663
|
+
* Automatically detects backtest/live mode from execution context.
|
|
3664
|
+
*
|
|
3665
|
+
* @param symbol - Trading pair symbol
|
|
3666
|
+
* @param strategyName - Strategy name
|
|
3667
|
+
* @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
|
|
3668
|
+
* @returns Promise that resolves when scheduled signal is cancelled
|
|
3669
|
+
*
|
|
3670
|
+
* @example
|
|
3671
|
+
* ```typescript
|
|
3672
|
+
* import { cancel } from "backtest-kit";
|
|
3673
|
+
*
|
|
3674
|
+
* // Cancel scheduled signal with custom ID
|
|
3675
|
+
* await cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
3676
|
+
* ```
|
|
3677
|
+
*/
|
|
3678
|
+
declare function cancel(symbol: string, cancelId?: string): Promise<void>;
|
|
3679
|
+
/**
|
|
3680
|
+
* Executes partial close at profit level (moving toward TP).
|
|
3681
|
+
*
|
|
3682
|
+
* Closes a percentage of the active pending position at profit.
|
|
3683
|
+
* Price must be moving toward take profit (in profit direction).
|
|
3684
|
+
*
|
|
3685
|
+
* Automatically detects backtest/live mode from execution context.
|
|
3686
|
+
*
|
|
3687
|
+
* @param symbol - Trading pair symbol
|
|
3688
|
+
* @param percentToClose - Percentage of position to close (0-100, absolute value)
|
|
3689
|
+
* @returns Promise<boolean> - true if partial close executed, false if skipped
|
|
3690
|
+
*
|
|
3691
|
+
* @throws Error if currentPrice is not in profit direction:
|
|
3692
|
+
* - LONG: currentPrice must be > priceOpen
|
|
3693
|
+
* - SHORT: currentPrice must be < priceOpen
|
|
3694
|
+
*
|
|
3695
|
+
* @example
|
|
3696
|
+
* ```typescript
|
|
3697
|
+
* import { partialProfit } from "backtest-kit";
|
|
3698
|
+
*
|
|
3699
|
+
* // Close 30% of LONG position at profit
|
|
3700
|
+
* const success = await partialProfit("BTCUSDT", 30);
|
|
3701
|
+
* if (success) {
|
|
3702
|
+
* console.log('Partial profit executed');
|
|
3703
|
+
* }
|
|
3704
|
+
* ```
|
|
3705
|
+
*/
|
|
3706
|
+
declare function partialProfit(symbol: string, percentToClose: number): Promise<boolean>;
|
|
3707
|
+
/**
|
|
3708
|
+
* Executes partial close at loss level (moving toward SL).
|
|
3709
|
+
*
|
|
3710
|
+
* Closes a percentage of the active pending position at loss.
|
|
3711
|
+
* Price must be moving toward stop loss (in loss direction).
|
|
3712
|
+
*
|
|
3713
|
+
* Automatically detects backtest/live mode from execution context.
|
|
3714
|
+
*
|
|
3715
|
+
* @param symbol - Trading pair symbol
|
|
3716
|
+
* @param percentToClose - Percentage of position to close (0-100, absolute value)
|
|
3717
|
+
* @returns Promise<boolean> - true if partial close executed, false if skipped
|
|
3718
|
+
*
|
|
3719
|
+
* @throws Error if currentPrice is not in loss direction:
|
|
3720
|
+
* - LONG: currentPrice must be < priceOpen
|
|
3721
|
+
* - SHORT: currentPrice must be > priceOpen
|
|
3722
|
+
*
|
|
3723
|
+
* @example
|
|
3724
|
+
* ```typescript
|
|
3725
|
+
* import { partialLoss } from "backtest-kit";
|
|
3726
|
+
*
|
|
3727
|
+
* // Close 40% of LONG position at loss
|
|
3728
|
+
* const success = await partialLoss("BTCUSDT", 40);
|
|
3729
|
+
* if (success) {
|
|
3730
|
+
* console.log('Partial loss executed');
|
|
3731
|
+
* }
|
|
3732
|
+
* ```
|
|
3733
|
+
*/
|
|
3734
|
+
declare function partialLoss(symbol: string, percentToClose: number): Promise<boolean>;
|
|
3735
|
+
/**
|
|
3736
|
+
* Adjusts the trailing stop-loss distance for an active pending signal.
|
|
3737
|
+
*
|
|
3738
|
+
* CRITICAL: Always calculates from ORIGINAL SL, not from current trailing SL.
|
|
3739
|
+
* This prevents error accumulation on repeated calls.
|
|
3740
|
+
* Larger percentShift ABSORBS smaller one (updates only towards better protection).
|
|
3741
|
+
*
|
|
3742
|
+
* Updates the stop-loss distance by a percentage adjustment relative to the ORIGINAL SL distance.
|
|
3743
|
+
* Negative percentShift tightens the SL (reduces distance, moves closer to entry).
|
|
3744
|
+
* Positive percentShift loosens the SL (increases distance, moves away from entry).
|
|
3745
|
+
*
|
|
3746
|
+
* Absorption behavior:
|
|
3747
|
+
* - First call: sets trailing SL unconditionally
|
|
3748
|
+
* - Subsequent calls: updates only if new SL is BETTER (protects more profit)
|
|
3749
|
+
* - For LONG: only accepts HIGHER SL (never moves down, closer to entry wins)
|
|
3750
|
+
* - For SHORT: only accepts LOWER SL (never moves up, closer to entry wins)
|
|
3751
|
+
*
|
|
3752
|
+
* Automatically detects backtest/live mode from execution context.
|
|
3753
|
+
*
|
|
3754
|
+
* @param symbol - Trading pair symbol
|
|
3755
|
+
* @param percentShift - Percentage adjustment to ORIGINAL SL distance (-100 to 100)
|
|
3756
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
3757
|
+
* @returns Promise<boolean> - true if trailing SL was set/updated, false if rejected (absorption/intrusion/conflict)
|
|
3758
|
+
*
|
|
3759
|
+
* @example
|
|
3760
|
+
* ```typescript
|
|
3761
|
+
* import { trailingStop } from "backtest-kit";
|
|
3762
|
+
*
|
|
3763
|
+
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
3764
|
+
*
|
|
3765
|
+
* // First call: tighten by 5%
|
|
3766
|
+
* const success1 = await trailingStop("BTCUSDT", -5, 102);
|
|
3767
|
+
* // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
|
|
3768
|
+
*
|
|
3769
|
+
* // Second call: try weaker protection (smaller percentShift)
|
|
3770
|
+
* const success2 = await trailingStop("BTCUSDT", -3, 102);
|
|
3771
|
+
* // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
|
|
3772
|
+
*
|
|
3773
|
+
* // Third call: stronger protection (larger percentShift)
|
|
3774
|
+
* const success3 = await trailingStop("BTCUSDT", -7, 102);
|
|
3775
|
+
* // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
|
|
3776
|
+
* ```
|
|
3777
|
+
*/
|
|
3778
|
+
declare function trailingStop(symbol: string, percentShift: number, currentPrice: number): Promise<boolean>;
|
|
3779
|
+
/**
|
|
3780
|
+
* Adjusts the trailing take-profit distance for an active pending signal.
|
|
3781
|
+
*
|
|
3782
|
+
* CRITICAL: Always calculates from ORIGINAL TP, not from current trailing TP.
|
|
3783
|
+
* This prevents error accumulation on repeated calls.
|
|
3784
|
+
* Larger percentShift ABSORBS smaller one (updates only towards more conservative TP).
|
|
3785
|
+
*
|
|
3786
|
+
* Updates the take-profit distance by a percentage adjustment relative to the ORIGINAL TP distance.
|
|
3787
|
+
* Negative percentShift brings TP closer to entry (more conservative).
|
|
3788
|
+
* Positive percentShift moves TP further from entry (more aggressive).
|
|
3789
|
+
*
|
|
3790
|
+
* Absorption behavior:
|
|
3791
|
+
* - First call: sets trailing TP unconditionally
|
|
3792
|
+
* - Subsequent calls: updates only if new TP is MORE CONSERVATIVE (closer to entry)
|
|
3793
|
+
* - For LONG: only accepts LOWER TP (never moves up, closer to entry wins)
|
|
3794
|
+
* - For SHORT: only accepts HIGHER TP (never moves down, closer to entry wins)
|
|
3795
|
+
*
|
|
3796
|
+
* Automatically detects backtest/live mode from execution context.
|
|
3797
|
+
*
|
|
3798
|
+
* @param symbol - Trading pair symbol
|
|
3799
|
+
* @param percentShift - Percentage adjustment to ORIGINAL TP distance (-100 to 100)
|
|
3800
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
3801
|
+
* @returns Promise<boolean> - true if trailing TP was set/updated, false if rejected (absorption/intrusion/conflict)
|
|
3802
|
+
*
|
|
3803
|
+
* @example
|
|
3804
|
+
* ```typescript
|
|
3805
|
+
* import { trailingTake } from "backtest-kit";
|
|
3806
|
+
*
|
|
3807
|
+
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
3808
|
+
*
|
|
3809
|
+
* // First call: bring TP closer by 3%
|
|
3810
|
+
* const success1 = await trailingTake("BTCUSDT", -3, 102);
|
|
3811
|
+
* // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
|
|
3812
|
+
*
|
|
3813
|
+
* // Second call: try to move TP further (less conservative)
|
|
3814
|
+
* const success2 = await trailingTake("BTCUSDT", 2, 102);
|
|
3815
|
+
* // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
|
|
3816
|
+
*
|
|
3817
|
+
* // Third call: even more conservative
|
|
3818
|
+
* const success3 = await trailingTake("BTCUSDT", -5, 102);
|
|
3819
|
+
* // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
|
|
3820
|
+
* ```
|
|
3821
|
+
*/
|
|
3822
|
+
declare function trailingTake(symbol: string, percentShift: number, currentPrice: number): Promise<boolean>;
|
|
3823
|
+
/**
|
|
3824
|
+
* Moves stop-loss to breakeven when price reaches threshold.
|
|
3825
|
+
*
|
|
3826
|
+
* Moves SL to entry price (zero-risk position) when current price has moved
|
|
3827
|
+
* far enough in profit direction to cover transaction costs.
|
|
3828
|
+
* Threshold is calculated as: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2
|
|
3829
|
+
*
|
|
3830
|
+
* Automatically detects backtest/live mode from execution context.
|
|
3831
|
+
* Automatically fetches current price via getAveragePrice.
|
|
3832
|
+
*
|
|
3833
|
+
* @param symbol - Trading pair symbol
|
|
3834
|
+
* @returns Promise<boolean> - true if breakeven was set, false if conditions not met
|
|
3835
|
+
*
|
|
3836
|
+
* @example
|
|
3837
|
+
* ```typescript
|
|
3838
|
+
* import { breakeven } from "backtest-kit";
|
|
3839
|
+
*
|
|
3840
|
+
* // LONG: entry=100, slippage=0.1%, fee=0.1%, threshold=0.4%
|
|
3841
|
+
* // Try to move SL to breakeven (activates when price >= 100.4)
|
|
3842
|
+
* const moved = await breakeven("BTCUSDT");
|
|
3843
|
+
* if (moved) {
|
|
3844
|
+
* console.log("Position moved to breakeven!");
|
|
3845
|
+
* }
|
|
3846
|
+
* ```
|
|
3847
|
+
*/
|
|
3848
|
+
declare function breakeven(symbol: string): Promise<boolean>;
|
|
3849
|
+
|
|
3850
|
+
/**
|
|
3851
|
+
* Unified breakeven event data for report generation.
|
|
3852
|
+
* Contains all information about when signals reached breakeven.
|
|
3853
|
+
*/
|
|
3854
|
+
interface BreakevenEvent {
|
|
3855
|
+
/** Event timestamp in milliseconds */
|
|
3856
|
+
timestamp: number;
|
|
3857
|
+
/** Trading pair symbol */
|
|
3858
|
+
symbol: string;
|
|
3859
|
+
/** Strategy name */
|
|
3860
|
+
strategyName: StrategyName;
|
|
3861
|
+
/** Signal ID */
|
|
3862
|
+
signalId: string;
|
|
3863
|
+
/** Position type */
|
|
3864
|
+
position: string;
|
|
3865
|
+
/** Current market price when breakeven was reached */
|
|
3866
|
+
currentPrice: number;
|
|
3867
|
+
/** Entry price (breakeven level) */
|
|
3868
|
+
priceOpen: number;
|
|
3869
|
+
/** Take profit target price */
|
|
3870
|
+
priceTakeProfit?: number;
|
|
3871
|
+
/** Stop loss exit price */
|
|
3872
|
+
priceStopLoss?: number;
|
|
3873
|
+
/** Original take profit price set at signal creation */
|
|
3874
|
+
originalPriceTakeProfit?: number;
|
|
3875
|
+
/** Original stop loss price set at signal creation */
|
|
3876
|
+
originalPriceStopLoss?: number;
|
|
3877
|
+
/** Total executed percentage from partial closes */
|
|
3878
|
+
totalExecuted?: number;
|
|
3879
|
+
/** Human-readable description of signal reason */
|
|
3880
|
+
note?: string;
|
|
3881
|
+
/** True if backtest mode, false if live mode */
|
|
3882
|
+
backtest: boolean;
|
|
3883
|
+
}
|
|
3884
|
+
/**
|
|
3885
|
+
* Statistical data calculated from breakeven events.
|
|
3886
|
+
*
|
|
3887
|
+
* Provides metrics for breakeven milestone tracking.
|
|
3888
|
+
*
|
|
3889
|
+
* @example
|
|
3890
|
+
* ```typescript
|
|
3891
|
+
* const stats = await Breakeven.getData("BTCUSDT", "my-strategy");
|
|
3892
|
+
*
|
|
3893
|
+
* console.log(`Total breakeven events: ${stats.totalEvents}`);
|
|
3894
|
+
* console.log(`Average threshold: ${stats.averageThreshold}%`);
|
|
3895
|
+
* ```
|
|
3896
|
+
*/
|
|
3897
|
+
interface BreakevenStatisticsModel {
|
|
3898
|
+
/** Array of all breakeven events with full details */
|
|
3899
|
+
eventList: BreakevenEvent[];
|
|
3900
|
+
/** Total number of breakeven events */
|
|
3901
|
+
totalEvents: number;
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3904
|
+
declare const GLOBAL_CONFIG: {
|
|
3905
|
+
/**
|
|
3906
|
+
* Time to wait for scheduled signal to activate (in minutes)
|
|
3907
|
+
* If signal does not activate within this time, it will be cancelled.
|
|
3908
|
+
*/
|
|
3909
|
+
CC_SCHEDULE_AWAIT_MINUTES: number;
|
|
3910
|
+
/**
|
|
3911
|
+
* Number of candles to use for average price calculation (VWAP)
|
|
3912
|
+
* Default: 5 candles (last 5 minutes when using 1m interval)
|
|
3913
|
+
*/
|
|
3914
|
+
CC_AVG_PRICE_CANDLES_COUNT: number;
|
|
3915
|
+
/**
|
|
3916
|
+
* Slippage percentage applied to entry and exit prices.
|
|
3917
|
+
* Simulates market impact and order book depth.
|
|
3918
|
+
* Applied twice (entry and exit) for realistic execution simulation.
|
|
3919
|
+
* Default: 0.1% per transaction
|
|
3920
|
+
*/
|
|
3921
|
+
CC_PERCENT_SLIPPAGE: number;
|
|
3922
|
+
/**
|
|
3923
|
+
* Fee percentage charged per transaction.
|
|
3924
|
+
* Applied twice (entry and exit) for total fee calculation.
|
|
3925
|
+
* Default: 0.1% per transaction (total 0.2%)
|
|
3926
|
+
*/
|
|
3927
|
+
CC_PERCENT_FEE: number;
|
|
3928
|
+
/**
|
|
3929
|
+
* Minimum TakeProfit distance from priceOpen (percentage)
|
|
3930
|
+
* Must be greater than (slippage + fees) to ensure profitable trades
|
|
3931
|
+
*
|
|
3932
|
+
* Calculation:
|
|
3933
|
+
* - Slippage effect: ~0.2% (0.1% × 2 transactions)
|
|
3934
|
+
* - Fees: 0.2% (0.1% × 2 transactions)
|
|
3935
|
+
* - Minimum profit buffer: 0.1%
|
|
3936
|
+
* - Total: 0.5%
|
|
3937
|
+
*
|
|
3938
|
+
* Default: 0.5% (covers all costs + minimum profit margin)
|
|
3939
|
+
*/
|
|
3940
|
+
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: number;
|
|
3941
|
+
/**
|
|
3942
|
+
* Minimum StopLoss distance from priceOpen (percentage)
|
|
3943
|
+
* Prevents signals from being immediately stopped out due to price volatility
|
|
3944
|
+
* Default: 0.5% (buffer to avoid instant stop loss on normal market fluctuations)
|
|
3945
|
+
*/
|
|
3946
|
+
CC_MIN_STOPLOSS_DISTANCE_PERCENT: number;
|
|
3947
|
+
/**
|
|
3948
|
+
* Maximum StopLoss distance from priceOpen (percentage)
|
|
3949
|
+
* Prevents catastrophic losses from extreme StopLoss values
|
|
3950
|
+
* Default: 20% (one signal cannot lose more than 20% of position)
|
|
3951
|
+
*/
|
|
3952
|
+
CC_MAX_STOPLOSS_DISTANCE_PERCENT: number;
|
|
3953
|
+
/**
|
|
3954
|
+
* Maximum signal lifetime in minutes
|
|
3955
|
+
* Prevents eternal signals that block risk limits for weeks/months
|
|
3956
|
+
* Default: 1440 minutes (1 day)
|
|
3957
|
+
*/
|
|
3958
|
+
CC_MAX_SIGNAL_LIFETIME_MINUTES: number;
|
|
3959
|
+
/**
|
|
3960
|
+
* Maximum time allowed for signal generation (in seconds).
|
|
3961
|
+
* Prevents long-running or stuck signal generation routines from blocking
|
|
3962
|
+
* execution or consuming resources indefinitely. If generation exceeds this
|
|
3963
|
+
* threshold the attempt should be aborted, logged and optionally retried.
|
|
3964
|
+
*
|
|
3965
|
+
* Default: 180 seconds (3 minutes)
|
|
3966
|
+
*/
|
|
3967
|
+
CC_MAX_SIGNAL_GENERATION_SECONDS: number;
|
|
3968
|
+
/**
|
|
3969
|
+
* Number of retries for getCandles function
|
|
3970
|
+
* Default: 3 retries
|
|
3971
|
+
*/
|
|
3972
|
+
CC_GET_CANDLES_RETRY_COUNT: number;
|
|
3973
|
+
/**
|
|
3974
|
+
* Delay between retries for getCandles function (in milliseconds)
|
|
3975
|
+
* Default: 5000 ms (5 seconds)
|
|
3976
|
+
*/
|
|
3977
|
+
CC_GET_CANDLES_RETRY_DELAY_MS: number;
|
|
3978
|
+
/**
|
|
3979
|
+
* Maximum number of candles to request per single API call.
|
|
3980
|
+
* If a request exceeds this limit, data will be fetched using pagination.
|
|
3981
|
+
* Default: 1000 candles per request
|
|
3982
|
+
*/
|
|
3983
|
+
CC_MAX_CANDLES_PER_REQUEST: number;
|
|
3984
|
+
/**
|
|
3985
|
+
* Maximum allowed deviation factor for price anomaly detection.
|
|
3986
|
+
* Price should not be more than this factor lower than reference price.
|
|
3987
|
+
*
|
|
3988
|
+
* Reasoning:
|
|
3989
|
+
* - Incomplete candles from Binance API typically have prices near 0 (e.g., $0.01-1)
|
|
3990
|
+
* - Normal BTC price ranges: $20,000-100,000
|
|
3991
|
+
* - Factor 1000 catches prices below $20-100 when median is $20,000-100,000
|
|
3992
|
+
* - Factor 100 would be too permissive (allows $200 when median is $20,000)
|
|
3993
|
+
* - Factor 10000 might be too strict for low-cap altcoins
|
|
3994
|
+
*
|
|
3995
|
+
* Example: BTC at $50,000 median → threshold $50 (catches $0.01-1 anomalies)
|
|
3996
|
+
*/
|
|
3997
|
+
CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR: number;
|
|
3998
|
+
/**
|
|
3999
|
+
* Minimum number of candles required for reliable median calculation.
|
|
4000
|
+
* Below this threshold, use simple average instead of median.
|
|
4001
|
+
*
|
|
4002
|
+
* Reasoning:
|
|
4003
|
+
* - Each candle provides 4 price points (OHLC)
|
|
4004
|
+
* - 5 candles = 20 price points, sufficient for robust median calculation
|
|
4005
|
+
* - Below 5 candles, single anomaly can heavily skew median
|
|
4006
|
+
* - Statistical rule of thumb: minimum 7-10 data points for median stability
|
|
4007
|
+
* - Average is more stable than median for small datasets (n < 20)
|
|
4008
|
+
*
|
|
4009
|
+
* Example: 3 candles = 12 points (use average), 5 candles = 20 points (use median)
|
|
4010
|
+
*/
|
|
4011
|
+
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: number;
|
|
4012
|
+
/**
|
|
4013
|
+
* Controls visibility of signal notes in markdown report tables.
|
|
4014
|
+
* When enabled, the "Note" column will be displayed in all markdown reports
|
|
4015
|
+
* (backtest, live, schedule, risk, etc.)
|
|
4016
|
+
*
|
|
4017
|
+
* Default: false (notes are hidden to reduce table width and improve readability)
|
|
4018
|
+
*/
|
|
4019
|
+
CC_REPORT_SHOW_SIGNAL_NOTE: boolean;
|
|
4020
|
+
/**
|
|
4021
|
+
* Breakeven threshold percentage - minimum profit distance from entry to enable breakeven.
|
|
4022
|
+
* When price moves this percentage in profit direction, stop-loss can be moved to entry (breakeven).
|
|
4023
|
+
*
|
|
4024
|
+
* Calculation:
|
|
4025
|
+
* - Slippage effect: ~0.2% (0.1% × 2 transactions)
|
|
4026
|
+
* - Fees: 0.2% (0.1% × 2 transactions)
|
|
4027
|
+
* - Total: 0.4%
|
|
4028
|
+
* - Added buffer: 0.2%
|
|
4029
|
+
* - Overall: 0.6%
|
|
4030
|
+
*
|
|
4031
|
+
* Default: 0.2% (additional buffer above costs to ensure no loss when moving to breakeven)
|
|
4032
|
+
*/
|
|
4033
|
+
CC_BREAKEVEN_THRESHOLD: number;
|
|
4034
|
+
/**
|
|
4035
|
+
* Time offset in minutes for order book fetching.
|
|
4036
|
+
* Subtracts this amount from the current time when fetching order book data.
|
|
4037
|
+
* This helps get a more stable snapshot of the order book by avoiding real-time volatility.
|
|
4038
|
+
*
|
|
4039
|
+
* Default: 10 minutes
|
|
4040
|
+
*/
|
|
4041
|
+
CC_ORDER_BOOK_TIME_OFFSET_MINUTES: number;
|
|
4042
|
+
/**
|
|
4043
|
+
* Maximum depth levels for order book fetching.
|
|
4044
|
+
* Specifies how many price levels to fetch from both bids and asks.
|
|
4045
|
+
*
|
|
4046
|
+
* Default: 20 levels
|
|
4047
|
+
*/
|
|
4048
|
+
CC_ORDER_BOOK_MAX_DEPTH_LEVELS: number;
|
|
4049
|
+
};
|
|
4050
|
+
/**
|
|
4051
|
+
* Type for global configuration object.
|
|
4052
|
+
*/
|
|
4053
|
+
type GlobalConfig = typeof GLOBAL_CONFIG;
|
|
4054
|
+
|
|
4055
|
+
/**
|
|
4056
|
+
* Mapping of available table/markdown reports to their column definitions.
|
|
4057
|
+
*
|
|
4058
|
+
* Each property references a column definition object imported from
|
|
4059
|
+
* `src/assets/*.columns`. These are used by markdown/report generators
|
|
4060
|
+
* (backtest, live, schedule, risk, heat, performance, partial, walker).
|
|
4061
|
+
*/
|
|
4062
|
+
declare const COLUMN_CONFIG: {
|
|
4063
|
+
/** Columns used in backtest markdown tables and reports */
|
|
4064
|
+
backtest_columns: ColumnModel<IStrategyTickResultClosed>[];
|
|
4065
|
+
/** Columns used by heatmap / heat reports */
|
|
4066
|
+
heat_columns: ColumnModel<IHeatmapRow>[];
|
|
4067
|
+
/** Columns for live trading reports and logs */
|
|
4068
|
+
live_columns: ColumnModel<TickEvent>[];
|
|
4069
|
+
/** Columns for partial-results / incremental reports */
|
|
4070
|
+
partial_columns: ColumnModel<PartialEvent>[];
|
|
4071
|
+
/** Columns for breakeven protection events */
|
|
4072
|
+
breakeven_columns: ColumnModel<BreakevenEvent>[];
|
|
4073
|
+
/** Columns for performance summary reports */
|
|
4074
|
+
performance_columns: ColumnModel<MetricStats>[];
|
|
4075
|
+
/** Columns for risk-related reports */
|
|
4076
|
+
risk_columns: ColumnModel<RiskEvent>[];
|
|
4077
|
+
/** Columns for scheduled report output */
|
|
4078
|
+
schedule_columns: ColumnModel<ScheduledEvent>[];
|
|
4079
|
+
/** Walker: PnL summary columns */
|
|
4080
|
+
walker_pnl_columns: ColumnModel<SignalData$1>[];
|
|
4081
|
+
/** Walker: strategy-level summary columns */
|
|
4082
|
+
walker_strategy_columns: ColumnModel<IStrategyResult>[];
|
|
4083
|
+
};
|
|
4084
|
+
/**
|
|
4085
|
+
* Type for the column configuration object.
|
|
4086
|
+
*/
|
|
4087
|
+
type ColumnConfig = typeof COLUMN_CONFIG;
|
|
4088
|
+
|
|
4089
|
+
/**
|
|
4090
|
+
* Sets custom logger implementation for the framework.
|
|
4091
|
+
*
|
|
4092
|
+
* All log messages from internal services will be forwarded to the provided logger
|
|
4093
|
+
* with automatic context injection (strategyName, exchangeName, symbol, etc.).
|
|
4094
|
+
*
|
|
4095
|
+
* @param logger - Custom logger implementing ILogger interface
|
|
4096
|
+
*
|
|
4097
|
+
* @example
|
|
4098
|
+
* ```typescript
|
|
4099
|
+
* setLogger({
|
|
4100
|
+
* log: (topic, ...args) => console.log(topic, args),
|
|
4101
|
+
* debug: (topic, ...args) => console.debug(topic, args),
|
|
4102
|
+
* info: (topic, ...args) => console.info(topic, args),
|
|
4103
|
+
* });
|
|
4104
|
+
* ```
|
|
4105
|
+
*/
|
|
4106
|
+
declare function setLogger(logger: ILogger): void;
|
|
4107
|
+
/**
|
|
4108
|
+
* Sets global configuration parameters for the framework.
|
|
4109
|
+
* @param config - Partial configuration object to override default settings
|
|
4110
|
+
* @param _unsafe - Skip config validations - required for testbed
|
|
4111
|
+
*
|
|
4112
|
+
* @example
|
|
4113
|
+
* ```typescript
|
|
4114
|
+
* setConfig({
|
|
4115
|
+
* CC_SCHEDULE_AWAIT_MINUTES: 90,
|
|
4116
|
+
* });
|
|
4117
|
+
* ```
|
|
4118
|
+
*/
|
|
4119
|
+
declare function setConfig(config: Partial<GlobalConfig>, _unsafe?: boolean): void;
|
|
4120
|
+
/**
|
|
4121
|
+
* Retrieves a copy of the current global configuration.
|
|
4122
|
+
*
|
|
4123
|
+
* Returns a shallow copy of the current GLOBAL_CONFIG to prevent accidental mutations.
|
|
4124
|
+
* Use this to inspect the current configuration state without modifying it.
|
|
4125
|
+
*
|
|
4126
|
+
* @returns {GlobalConfig} A copy of the current global configuration object
|
|
4127
|
+
*
|
|
4128
|
+
* @example
|
|
4129
|
+
* ```typescript
|
|
4130
|
+
* const currentConfig = getConfig();
|
|
4131
|
+
* console.log(currentConfig.CC_SCHEDULE_AWAIT_MINUTES);
|
|
4132
|
+
* ```
|
|
4133
|
+
*/
|
|
4134
|
+
declare function getConfig(): {
|
|
4135
|
+
CC_SCHEDULE_AWAIT_MINUTES: number;
|
|
4136
|
+
CC_AVG_PRICE_CANDLES_COUNT: number;
|
|
4137
|
+
CC_PERCENT_SLIPPAGE: number;
|
|
4138
|
+
CC_PERCENT_FEE: number;
|
|
4139
|
+
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: number;
|
|
4140
|
+
CC_MIN_STOPLOSS_DISTANCE_PERCENT: number;
|
|
4141
|
+
CC_MAX_STOPLOSS_DISTANCE_PERCENT: number;
|
|
4142
|
+
CC_MAX_SIGNAL_LIFETIME_MINUTES: number;
|
|
4143
|
+
CC_MAX_SIGNAL_GENERATION_SECONDS: number;
|
|
4144
|
+
CC_GET_CANDLES_RETRY_COUNT: number;
|
|
4145
|
+
CC_GET_CANDLES_RETRY_DELAY_MS: number;
|
|
4146
|
+
CC_MAX_CANDLES_PER_REQUEST: number;
|
|
4147
|
+
CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR: number;
|
|
4148
|
+
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: number;
|
|
4149
|
+
CC_REPORT_SHOW_SIGNAL_NOTE: boolean;
|
|
4150
|
+
CC_BREAKEVEN_THRESHOLD: number;
|
|
4151
|
+
CC_ORDER_BOOK_TIME_OFFSET_MINUTES: number;
|
|
4152
|
+
CC_ORDER_BOOK_MAX_DEPTH_LEVELS: number;
|
|
4153
|
+
};
|
|
4154
|
+
/**
|
|
4155
|
+
* Retrieves the default configuration object for the framework.
|
|
4156
|
+
*
|
|
4157
|
+
* Returns a reference to the default configuration with all preset values.
|
|
4158
|
+
* Use this to see what configuration options are available and their default values.
|
|
4159
|
+
*
|
|
4160
|
+
* @returns {GlobalConfig} The default configuration object
|
|
4161
|
+
*
|
|
4162
|
+
* @example
|
|
4163
|
+
* ```typescript
|
|
4164
|
+
* const defaultConfig = getDefaultConfig();
|
|
4165
|
+
* console.log(defaultConfig.CC_SCHEDULE_AWAIT_MINUTES);
|
|
4166
|
+
* ```
|
|
4167
|
+
*/
|
|
4168
|
+
declare function getDefaultConfig(): Readonly<{
|
|
4169
|
+
CC_SCHEDULE_AWAIT_MINUTES: number;
|
|
4170
|
+
CC_AVG_PRICE_CANDLES_COUNT: number;
|
|
4171
|
+
CC_PERCENT_SLIPPAGE: number;
|
|
4172
|
+
CC_PERCENT_FEE: number;
|
|
4173
|
+
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: number;
|
|
4174
|
+
CC_MIN_STOPLOSS_DISTANCE_PERCENT: number;
|
|
4175
|
+
CC_MAX_STOPLOSS_DISTANCE_PERCENT: number;
|
|
4176
|
+
CC_MAX_SIGNAL_LIFETIME_MINUTES: number;
|
|
4177
|
+
CC_MAX_SIGNAL_GENERATION_SECONDS: number;
|
|
4178
|
+
CC_GET_CANDLES_RETRY_COUNT: number;
|
|
4179
|
+
CC_GET_CANDLES_RETRY_DELAY_MS: number;
|
|
4180
|
+
CC_MAX_CANDLES_PER_REQUEST: number;
|
|
4181
|
+
CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR: number;
|
|
4182
|
+
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: number;
|
|
4183
|
+
CC_REPORT_SHOW_SIGNAL_NOTE: boolean;
|
|
4184
|
+
CC_BREAKEVEN_THRESHOLD: number;
|
|
4185
|
+
CC_ORDER_BOOK_TIME_OFFSET_MINUTES: number;
|
|
4186
|
+
CC_ORDER_BOOK_MAX_DEPTH_LEVELS: number;
|
|
4187
|
+
}>;
|
|
4188
|
+
/**
|
|
4189
|
+
* Sets custom column configurations for markdown report generation.
|
|
4190
|
+
*
|
|
4191
|
+
* Allows overriding default column definitions for any report type.
|
|
4192
|
+
* All columns are validated before assignment to ensure structural correctness.
|
|
4193
|
+
*
|
|
4194
|
+
* @param columns - Partial column configuration object to override default column settings
|
|
4195
|
+
* @param _unsafe - Skip column validations - required for testbed
|
|
4196
|
+
*
|
|
4197
|
+
* @example
|
|
4198
|
+
* ```typescript
|
|
4199
|
+
* setColumns({
|
|
4200
|
+
* backtest_columns: [
|
|
4201
|
+
* {
|
|
4202
|
+
* key: "customId",
|
|
4203
|
+
* label: "Custom ID",
|
|
4204
|
+
* format: (data) => data.signal.id,
|
|
4205
|
+
* isVisible: () => true
|
|
4206
|
+
* }
|
|
4207
|
+
* ],
|
|
4208
|
+
* });
|
|
4209
|
+
* ```
|
|
4210
|
+
*
|
|
4211
|
+
* @throws {Error} If column configuration is invalid
|
|
4212
|
+
*/
|
|
4213
|
+
declare function setColumns(columns: Partial<ColumnConfig>, _unsafe?: boolean): void;
|
|
4214
|
+
/**
|
|
4215
|
+
* Retrieves a copy of the current column configuration for markdown report generation.
|
|
4216
|
+
*
|
|
4217
|
+
* Returns a shallow copy of the current COLUMN_CONFIG to prevent accidental mutations.
|
|
4218
|
+
* Use this to inspect the current column definitions without modifying them.
|
|
4219
|
+
*
|
|
4220
|
+
* @returns {ColumnConfig} A copy of the current column configuration object
|
|
4221
|
+
*
|
|
4222
|
+
* @example
|
|
4223
|
+
* ```typescript
|
|
4224
|
+
* const currentColumns = getColumns();
|
|
4225
|
+
* console.log(currentColumns.backtest_columns.length);
|
|
4226
|
+
* ```
|
|
4227
|
+
*/
|
|
4228
|
+
declare function getColumns(): {
|
|
4229
|
+
backtest_columns: ColumnModel<IStrategyTickResultClosed>[];
|
|
4230
|
+
heat_columns: ColumnModel<IHeatmapRow>[];
|
|
4231
|
+
live_columns: ColumnModel<TickEvent>[];
|
|
4232
|
+
partial_columns: ColumnModel<PartialEvent>[];
|
|
4233
|
+
breakeven_columns: ColumnModel<BreakevenEvent>[];
|
|
4234
|
+
performance_columns: ColumnModel<MetricStats>[];
|
|
4235
|
+
risk_columns: ColumnModel<RiskEvent>[];
|
|
4236
|
+
schedule_columns: ColumnModel<ScheduledEvent>[];
|
|
4237
|
+
walker_pnl_columns: ColumnModel<SignalData$1>[];
|
|
4238
|
+
walker_strategy_columns: ColumnModel<IStrategyResult>[];
|
|
4239
|
+
};
|
|
4240
|
+
/**
|
|
4241
|
+
* Retrieves the default column configuration object for markdown report generation.
|
|
4242
|
+
*
|
|
4243
|
+
* Returns a reference to the default column definitions with all preset values.
|
|
4244
|
+
* Use this to see what column options are available and their default definitions.
|
|
4245
|
+
*
|
|
4246
|
+
* @returns {ColumnConfig} The default column configuration object
|
|
4247
|
+
*
|
|
4248
|
+
* @example
|
|
4249
|
+
* ```typescript
|
|
4250
|
+
* const defaultColumns = getDefaultColumns();
|
|
4251
|
+
* console.log(defaultColumns.backtest_columns);
|
|
4252
|
+
* ```
|
|
4253
|
+
*/
|
|
4254
|
+
declare function getDefaultColumns(): Readonly<{
|
|
4255
|
+
backtest_columns: ColumnModel<IStrategyTickResultClosed>[];
|
|
4256
|
+
heat_columns: ColumnModel<IHeatmapRow>[];
|
|
4257
|
+
live_columns: ColumnModel<TickEvent>[];
|
|
4258
|
+
partial_columns: ColumnModel<PartialEvent>[];
|
|
4259
|
+
breakeven_columns: ColumnModel<BreakevenEvent>[];
|
|
4260
|
+
performance_columns: ColumnModel<MetricStats>[];
|
|
4261
|
+
risk_columns: ColumnModel<RiskEvent>[];
|
|
4262
|
+
schedule_columns: ColumnModel<ScheduledEvent>[];
|
|
4263
|
+
walker_pnl_columns: ColumnModel<SignalData$1>[];
|
|
4264
|
+
walker_strategy_columns: ColumnModel<IStrategyResult>[];
|
|
4265
|
+
}>;
|
|
4266
|
+
|
|
4132
4267
|
/**
|
|
4133
4268
|
* Registers a trading strategy in the framework.
|
|
4134
4269
|
*
|
|
@@ -18393,4 +18528,4 @@ declare const backtest: {
|
|
|
18393
18528
|
loggerService: LoggerService;
|
|
18394
18529
|
};
|
|
18395
18530
|
|
|
18396
|
-
export { ActionBase, Backtest, type BacktestDoneNotification, type BacktestStatisticsModel, type BootstrapNotification, Breakeven, type BreakevenContract, type BreakevenData, Cache, type CandleInterval, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IBidData, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IMarkdownDumpOptions, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IOrderBookData, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveDoneNotification, type LiveStatisticsModel, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, type MessageModel, type MessageRole, MethodContextService, type MetricStats, Notification, type NotificationModel, Optimizer, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossContract, type PartialLossNotification, type PartialProfitContract, type PartialProfitNotification, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, type PingContract, PositionSize, type ProgressBacktestContract, type ProgressBacktestNotification, type ProgressOptimizerContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, type TMarkdownBase, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TickEvent, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addAction, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, breakeven, cancel, dumpSignal, emitters, formatPrice, formatQuantity, get, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, getOrderBook, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenBreakeven, listenBreakevenOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideAction, overrideExchange, overrideFrame, overrideOptimizer, overrideRisk, overrideSizing, overrideStrategy, overrideWalker, partialLoss, partialProfit, roundTicks, set, setColumns, setConfig, setLogger, stop, trailingStop, trailingTake, validate };
|
|
18531
|
+
export { ActionBase, Backtest, type BacktestDoneNotification, type BacktestStatisticsModel, type BootstrapNotification, Breakeven, type BreakevenContract, type BreakevenData, Cache, type CandleInterval, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IBidData, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IMarkdownDumpOptions, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IOrderBookData, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveDoneNotification, type LiveStatisticsModel, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, type MessageModel, type MessageRole, MethodContextService, type MetricStats, Notification, type NotificationModel, Optimizer, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossContract, type PartialLossNotification, type PartialProfitContract, type PartialProfitNotification, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, type PingContract, PositionSize, type ProgressBacktestContract, type ProgressBacktestNotification, type ProgressOptimizerContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, type TMarkdownBase, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TickEvent, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addAction, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, breakeven, cancel, dumpSignal, emitters, formatPrice, formatQuantity, get, getAction, getAveragePrice, getCandles, getColumns, getConfig, getCurrentTimeframe, getDate, getDefaultColumns, getDefaultConfig, getExchange, getFrame, getMode, getOptimizer, getOrderBook, getRisk, getSizing, getStrategy, getWalker, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenBreakeven, listenBreakevenOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideAction, overrideExchange, overrideFrame, overrideOptimizer, overrideRisk, overrideSizing, overrideStrategy, overrideWalker, partialLoss, partialProfit, roundTicks, set, setColumns, setConfig, setLogger, stop, trailingStop, trailingTake, validate };
|