backtest-kit 1.11.10 โ†’ 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ Build reliable trading systems: backtest on historical data, deploy live bots wi
24
24
  - ๐Ÿ“Š **Reports & Metrics**: Auto Markdown reports with PNL, Sharpe Ratio, win rate, and more.
25
25
  - ๐Ÿ›ก๏ธ **Risk Management**: Custom rules for position limits, time windows, and multi-strategy coordination.
26
26
  - ๐Ÿ”Œ **Pluggable**: Custom data sources (CCXT), persistence (file/Redis), and sizing calculators.
27
- - ๐Ÿงช **Tested**: 300+ unit/integration tests for validation, recovery, and events.
27
+ - ๐Ÿงช **Tested**: 350+ unit/integration tests for validation, recovery, and events.
28
28
  - ๐Ÿ”“ **Self hosted**: Zero dependency on third-party node_modules or platforms; run entirely in your own environment.
29
29
 
30
30
  ## ๐Ÿ“‹ Supported Order Types
@@ -38,15 +38,27 @@ Build reliable trading systems: backtest on historical data, deploy live bots wi
38
38
 
39
39
  ## ๐Ÿš€ Quick Start
40
40
 
41
- > **Talk is cheap.** Let me show you **the code**
42
- >
43
- > Link to ๐Ÿ‘‰ [the demo app](https://github.com/tripolskypetr/backtest-kit/tree/master/demo) ๐Ÿ‘ˆ
41
+ ### ๐ŸŽฏ The Fastest Way: Sidekick CLI
42
+
43
+ > Create a production-ready trading bot in seconds:
44
+
45
+ ```bash
46
+ # Create project with npx (recommended)
47
+ npx -y @backtest-kit/sidekick my-trading-bot
48
+ cd my-trading-bot
49
+ npm start
50
+ ```
51
+
52
+ ### ๐Ÿ“ฆ Manual Installation
53
+
54
+ > **Want to see the code?** ๐Ÿ‘‰ [Demo app](https://github.com/tripolskypetr/backtest-kit/tree/master/demo) ๐Ÿ‘ˆ
44
55
 
45
- ### ๐Ÿ“ฆ Installation
46
56
  ```bash
47
57
  npm install backtest-kit ccxt ollama uuid
48
58
  ```
49
59
 
60
+ ## ๐Ÿ“š Code Samples
61
+
50
62
  ### โš™๏ธ Basic Configuration
51
63
  ```typescript
52
64
  import { setLogger, setConfig } from 'backtest-kit';
@@ -240,6 +252,52 @@ Unlike cloud-based platforms, backtest-kit runs entirely in your environment. Yo
240
252
  - Full control over execution and data sources
241
253
  - [GUI](https://backtest-kit.github.io/documents/design_30_markdown-report-system.html#method-signatures) for visualization and monitoring
242
254
 
255
+ ## ๐ŸŒ Ecosystem
256
+
257
+ The `backtest-kit` ecosystem extends beyond the core library, offering complementary packages and tools to enhance your trading system development experience:
258
+
259
+ ### @backtest-kit/signals
260
+
261
+ > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/signals)** ๐Ÿ“Š
262
+
263
+ The **@backtest-kit/signals** package is a technical analysis and trading signal generation library designed for AI-powered trading systems. It computes 50+ indicators across 4 timeframes and generates markdown reports optimized for LLM consumption.
264
+
265
+ #### Key Features
266
+ - ๐Ÿ“ˆ **Multi-Timeframe Analysis**: 1m, 15m, 30m, 1h with synchronized indicator computation
267
+ - ๐ŸŽฏ **50+ Technical Indicators**: RSI, MACD, Bollinger Bands, Stochastic, ADX, ATR, CCI, Fibonacci, Support/Resistance
268
+ - ๐Ÿ“Š **Order Book Analysis**: Bid/ask depth, spread, liquidity imbalance, top 20 levels
269
+ - ๐Ÿค– **AI-Ready Output**: Markdown reports formatted for LLM context injection
270
+ - โšก **Performance Optimized**: Intelligent caching with configurable TTL per timeframe
271
+
272
+ #### Use Case
273
+ Perfect for injecting comprehensive market context into your LLM-powered strategies. Instead of manually calculating indicators, `@backtest-kit/signals` provides a single function call that adds all technical analysis to your message context. Works seamlessly with `getSignal` function in backtest-kit strategies.
274
+
275
+ #### Get Started
276
+ ```bash
277
+ npm install @backtest-kit/signals backtest-kit
278
+ ```
279
+
280
+ ### @backtest-kit/ollama
281
+
282
+ > **[Explore on NPM](https://www.npmjs.com/package/@backtest-kit/ollama)** ๐Ÿค–
283
+
284
+ The **@backtest-kit/ollama** package is a multi-provider LLM inference library that supports 10+ providers including OpenAI, Claude, DeepSeek, Grok, Mistral, Perplexity, Cohere, Alibaba, Hugging Face, and Ollama with unified API and automatic token rotation.
285
+
286
+ #### Key Features
287
+ - ๐Ÿ”Œ **10+ LLM Providers**: OpenAI, Claude, DeepSeek, Grok, Mistral, Perplexity, Cohere, Alibaba, Hugging Face, Ollama
288
+ - ๐Ÿ”„ **Token Rotation**: Automatic API key rotation for Ollama (others throw clear errors)
289
+ - ๐ŸŽฏ **Structured Output**: Enforced JSON schema for trading signals (position, price levels, risk notes)
290
+ - ๐Ÿ”‘ **Flexible Auth**: Context-based API keys or environment variables
291
+ - โšก **Unified API**: Single interface across all providers
292
+ - ๐Ÿ“Š **Trading-First**: Built for backtest-kit with position sizing and risk management
293
+
294
+ #### Use Case
295
+ Ideal for building multi-provider LLM strategies with fallback chains and ensemble predictions. The package returns structured trading signals with validated TP/SL levels, making it perfect for use in `getSignal` functions. Supports both backtest and live trading modes.
296
+
297
+ #### Get Started
298
+ ```bash
299
+ npm install @backtest-kit/ollama agent-swarm-kit backtest-kit
300
+ ```
243
301
 
244
302
  ## ๐Ÿค– Are you a robot?
245
303
 
@@ -247,7 +305,7 @@ Unlike cloud-based platforms, backtest-kit runs entirely in your environment. Yo
247
305
 
248
306
  ## โœ… Tested & Reliable
249
307
 
250
- 300+ tests cover validation, recovery, reports, and events.
308
+ 350+ tests cover validation, recovery, reports, and events.
251
309
 
252
310
  ## ๐Ÿค Contribute
253
311
 
package/build/index.cjs CHANGED
@@ -651,7 +651,7 @@ const GET_CANDLES_FN = async (dto, since, self) => {
651
651
  let lastError;
652
652
  for (let i = 0; i !== GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT; i++) {
653
653
  try {
654
- const result = await self.params.getCandles(dto.symbol, dto.interval, since, dto.limit);
654
+ const result = await self.params.getCandles(dto.symbol, dto.interval, since, dto.limit, self.params.execution.context.backtest);
655
655
  VALIDATE_NO_INCOMPLETE_CANDLES_FN(result);
656
656
  return result;
657
657
  }
@@ -878,7 +878,7 @@ class ClientExchange {
878
878
  symbol,
879
879
  quantity,
880
880
  });
881
- return await this.params.formatQuantity(symbol, quantity);
881
+ return await this.params.formatQuantity(symbol, quantity, this.params.execution.context.backtest);
882
882
  }
883
883
  /**
884
884
  * Formats price according to exchange-specific rules for the given symbol.
@@ -893,7 +893,7 @@ class ClientExchange {
893
893
  symbol,
894
894
  price,
895
895
  });
896
- return await this.params.formatPrice(symbol, price);
896
+ return await this.params.formatPrice(symbol, price, this.params.execution.context.backtest);
897
897
  }
898
898
  /**
899
899
  * Fetches order book for a trading pair.
@@ -914,7 +914,7 @@ class ClientExchange {
914
914
  });
915
915
  const to = new Date(this.params.execution.context.when.getTime());
916
916
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
917
- return await this.params.getOrderBook(symbol, depth, from, to);
917
+ return await this.params.getOrderBook(symbol, depth, from, to, this.params.execution.context.backtest);
918
918
  }
919
919
  }
920
920
 
@@ -922,21 +922,21 @@ class ClientExchange {
922
922
  * Default implementation for getCandles.
923
923
  * Throws an error indicating the method is not implemented.
924
924
  */
925
- const DEFAULT_GET_CANDLES_FN$1 = async (_symbol, _interval, _since, _limit) => {
925
+ const DEFAULT_GET_CANDLES_FN$1 = async (_symbol, _interval, _since, _limit, _backtest) => {
926
926
  throw new Error(`getCandles is not implemented for this exchange`);
927
927
  };
928
928
  /**
929
929
  * Default implementation for formatQuantity.
930
930
  * Returns Bitcoin precision on Binance (8 decimal places).
931
931
  */
932
- const DEFAULT_FORMAT_QUANTITY_FN$1 = async (_symbol, quantity) => {
932
+ const DEFAULT_FORMAT_QUANTITY_FN$1 = async (_symbol, quantity, _backtest) => {
933
933
  return quantity.toFixed(8);
934
934
  };
935
935
  /**
936
936
  * Default implementation for formatPrice.
937
937
  * Returns Bitcoin precision on Binance (2 decimal places).
938
938
  */
939
- const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price) => {
939
+ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price, _backtest) => {
940
940
  return price.toFixed(2);
941
941
  };
942
942
  /**
@@ -947,8 +947,9 @@ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price) => {
947
947
  * @param _depth - Maximum depth levels (unused)
948
948
  * @param _from - Start of time range (unused - can be ignored in live implementations)
949
949
  * @param _to - End of time range (unused - can be ignored in live implementations)
950
+ * @param _backtest - Whether running in backtest mode (unused)
950
951
  */
951
- const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to) => {
952
+ const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to, _backtest) => {
952
953
  throw new Error(`getOrderBook is not implemented for this exchange`);
953
954
  };
954
955
  /**
@@ -27213,25 +27214,35 @@ const EXCHANGE_METHOD_NAME_GET_AVERAGE_PRICE = "ExchangeUtils.getAveragePrice";
27213
27214
  const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
27214
27215
  const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
27215
27216
  const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
27217
+ /**
27218
+ * Gets backtest mode flag from execution context if available.
27219
+ * Returns false if no execution context exists (live mode).
27220
+ */
27221
+ const GET_BACKTEST_FN = async () => {
27222
+ if (ExecutionContextService.hasContext()) {
27223
+ return bt.executionContextService.context.backtest;
27224
+ }
27225
+ return false;
27226
+ };
27216
27227
  /**
27217
27228
  * Default implementation for getCandles.
27218
27229
  * Throws an error indicating the method is not implemented.
27219
27230
  */
27220
- const DEFAULT_GET_CANDLES_FN = async (_symbol, _interval, _since, _limit) => {
27231
+ const DEFAULT_GET_CANDLES_FN = async (_symbol, _interval, _since, _limit, _backtest) => {
27221
27232
  throw new Error(`getCandles is not implemented for this exchange`);
27222
27233
  };
27223
27234
  /**
27224
27235
  * Default implementation for formatQuantity.
27225
27236
  * Returns Bitcoin precision on Binance (8 decimal places).
27226
27237
  */
27227
- const DEFAULT_FORMAT_QUANTITY_FN = async (_symbol, quantity) => {
27238
+ const DEFAULT_FORMAT_QUANTITY_FN = async (_symbol, quantity, _backtest) => {
27228
27239
  return quantity.toFixed(8);
27229
27240
  };
27230
27241
  /**
27231
27242
  * Default implementation for formatPrice.
27232
27243
  * Returns Bitcoin precision on Binance (2 decimal places).
27233
27244
  */
27234
- const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price) => {
27245
+ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price, _backtest) => {
27235
27246
  return price.toFixed(2);
27236
27247
  };
27237
27248
  /**
@@ -27242,8 +27253,9 @@ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price) => {
27242
27253
  * @param _depth - Maximum depth levels (unused)
27243
27254
  * @param _from - Start of time range (unused - can be ignored in live implementations)
27244
27255
  * @param _to - End of time range (unused - can be ignored in live implementations)
27256
+ * @param _backtest - Whether running in backtest mode (unused)
27245
27257
  */
27246
- const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to) => {
27258
+ const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to, _backtest) => {
27247
27259
  throw new Error(`getOrderBook is not implemented for this exchange`);
27248
27260
  };
27249
27261
  const INTERVAL_MINUTES$1 = {
@@ -27339,9 +27351,10 @@ class ExchangeInstance {
27339
27351
  if (limit > GLOBAL_CONFIG.CC_MAX_CANDLES_PER_REQUEST) {
27340
27352
  let remaining = limit;
27341
27353
  let currentSince = new Date(since.getTime());
27354
+ const isBacktest = await GET_BACKTEST_FN();
27342
27355
  while (remaining > 0) {
27343
27356
  const chunkLimit = Math.min(remaining, GLOBAL_CONFIG.CC_MAX_CANDLES_PER_REQUEST);
27344
- const chunkData = await getCandles(symbol, interval, currentSince, chunkLimit);
27357
+ const chunkData = await getCandles(symbol, interval, currentSince, chunkLimit, isBacktest);
27345
27358
  allData.push(...chunkData);
27346
27359
  remaining -= chunkLimit;
27347
27360
  if (remaining > 0) {
@@ -27351,7 +27364,8 @@ class ExchangeInstance {
27351
27364
  }
27352
27365
  }
27353
27366
  else {
27354
- allData = await getCandles(symbol, interval, since, limit);
27367
+ const isBacktest = await GET_BACKTEST_FN();
27368
+ allData = await getCandles(symbol, interval, since, limit, isBacktest);
27355
27369
  }
27356
27370
  // Filter candles to strictly match the requested range
27357
27371
  const whenTimestamp = when.getTime();
@@ -27427,7 +27441,8 @@ class ExchangeInstance {
27427
27441
  symbol,
27428
27442
  quantity,
27429
27443
  });
27430
- return await this._methods.formatQuantity(symbol, quantity);
27444
+ const isBacktest = await GET_BACKTEST_FN();
27445
+ return await this._methods.formatQuantity(symbol, quantity, isBacktest);
27431
27446
  };
27432
27447
  /**
27433
27448
  * Format price according to exchange precision rules.
@@ -27449,7 +27464,8 @@ class ExchangeInstance {
27449
27464
  symbol,
27450
27465
  price,
27451
27466
  });
27452
- return await this._methods.formatPrice(symbol, price);
27467
+ const isBacktest = await GET_BACKTEST_FN();
27468
+ return await this._methods.formatPrice(symbol, price, isBacktest);
27453
27469
  };
27454
27470
  /**
27455
27471
  * Fetch order book for a trading pair.
@@ -27479,7 +27495,8 @@ class ExchangeInstance {
27479
27495
  });
27480
27496
  const to = new Date(Date.now());
27481
27497
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
27482
- return await this._methods.getOrderBook(symbol, depth, from, to);
27498
+ const isBacktest = await GET_BACKTEST_FN();
27499
+ return await this._methods.getOrderBook(symbol, depth, from, to, isBacktest);
27483
27500
  };
27484
27501
  const schema = bt.exchangeSchemaService.get(this.exchangeName);
27485
27502
  this._methods = CREATE_EXCHANGE_INSTANCE_FN(schema);
package/build/index.mjs CHANGED
@@ -631,7 +631,7 @@ const GET_CANDLES_FN = async (dto, since, self) => {
631
631
  let lastError;
632
632
  for (let i = 0; i !== GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT; i++) {
633
633
  try {
634
- const result = await self.params.getCandles(dto.symbol, dto.interval, since, dto.limit);
634
+ const result = await self.params.getCandles(dto.symbol, dto.interval, since, dto.limit, self.params.execution.context.backtest);
635
635
  VALIDATE_NO_INCOMPLETE_CANDLES_FN(result);
636
636
  return result;
637
637
  }
@@ -858,7 +858,7 @@ class ClientExchange {
858
858
  symbol,
859
859
  quantity,
860
860
  });
861
- return await this.params.formatQuantity(symbol, quantity);
861
+ return await this.params.formatQuantity(symbol, quantity, this.params.execution.context.backtest);
862
862
  }
863
863
  /**
864
864
  * Formats price according to exchange-specific rules for the given symbol.
@@ -873,7 +873,7 @@ class ClientExchange {
873
873
  symbol,
874
874
  price,
875
875
  });
876
- return await this.params.formatPrice(symbol, price);
876
+ return await this.params.formatPrice(symbol, price, this.params.execution.context.backtest);
877
877
  }
878
878
  /**
879
879
  * Fetches order book for a trading pair.
@@ -894,7 +894,7 @@ class ClientExchange {
894
894
  });
895
895
  const to = new Date(this.params.execution.context.when.getTime());
896
896
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
897
- return await this.params.getOrderBook(symbol, depth, from, to);
897
+ return await this.params.getOrderBook(symbol, depth, from, to, this.params.execution.context.backtest);
898
898
  }
899
899
  }
900
900
 
@@ -902,21 +902,21 @@ class ClientExchange {
902
902
  * Default implementation for getCandles.
903
903
  * Throws an error indicating the method is not implemented.
904
904
  */
905
- const DEFAULT_GET_CANDLES_FN$1 = async (_symbol, _interval, _since, _limit) => {
905
+ const DEFAULT_GET_CANDLES_FN$1 = async (_symbol, _interval, _since, _limit, _backtest) => {
906
906
  throw new Error(`getCandles is not implemented for this exchange`);
907
907
  };
908
908
  /**
909
909
  * Default implementation for formatQuantity.
910
910
  * Returns Bitcoin precision on Binance (8 decimal places).
911
911
  */
912
- const DEFAULT_FORMAT_QUANTITY_FN$1 = async (_symbol, quantity) => {
912
+ const DEFAULT_FORMAT_QUANTITY_FN$1 = async (_symbol, quantity, _backtest) => {
913
913
  return quantity.toFixed(8);
914
914
  };
915
915
  /**
916
916
  * Default implementation for formatPrice.
917
917
  * Returns Bitcoin precision on Binance (2 decimal places).
918
918
  */
919
- const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price) => {
919
+ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price, _backtest) => {
920
920
  return price.toFixed(2);
921
921
  };
922
922
  /**
@@ -927,8 +927,9 @@ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price) => {
927
927
  * @param _depth - Maximum depth levels (unused)
928
928
  * @param _from - Start of time range (unused - can be ignored in live implementations)
929
929
  * @param _to - End of time range (unused - can be ignored in live implementations)
930
+ * @param _backtest - Whether running in backtest mode (unused)
930
931
  */
931
- const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to) => {
932
+ const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to, _backtest) => {
932
933
  throw new Error(`getOrderBook is not implemented for this exchange`);
933
934
  };
934
935
  /**
@@ -27193,25 +27194,35 @@ const EXCHANGE_METHOD_NAME_GET_AVERAGE_PRICE = "ExchangeUtils.getAveragePrice";
27193
27194
  const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
27194
27195
  const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
27195
27196
  const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
27197
+ /**
27198
+ * Gets backtest mode flag from execution context if available.
27199
+ * Returns false if no execution context exists (live mode).
27200
+ */
27201
+ const GET_BACKTEST_FN = async () => {
27202
+ if (ExecutionContextService.hasContext()) {
27203
+ return bt.executionContextService.context.backtest;
27204
+ }
27205
+ return false;
27206
+ };
27196
27207
  /**
27197
27208
  * Default implementation for getCandles.
27198
27209
  * Throws an error indicating the method is not implemented.
27199
27210
  */
27200
- const DEFAULT_GET_CANDLES_FN = async (_symbol, _interval, _since, _limit) => {
27211
+ const DEFAULT_GET_CANDLES_FN = async (_symbol, _interval, _since, _limit, _backtest) => {
27201
27212
  throw new Error(`getCandles is not implemented for this exchange`);
27202
27213
  };
27203
27214
  /**
27204
27215
  * Default implementation for formatQuantity.
27205
27216
  * Returns Bitcoin precision on Binance (8 decimal places).
27206
27217
  */
27207
- const DEFAULT_FORMAT_QUANTITY_FN = async (_symbol, quantity) => {
27218
+ const DEFAULT_FORMAT_QUANTITY_FN = async (_symbol, quantity, _backtest) => {
27208
27219
  return quantity.toFixed(8);
27209
27220
  };
27210
27221
  /**
27211
27222
  * Default implementation for formatPrice.
27212
27223
  * Returns Bitcoin precision on Binance (2 decimal places).
27213
27224
  */
27214
- const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price) => {
27225
+ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price, _backtest) => {
27215
27226
  return price.toFixed(2);
27216
27227
  };
27217
27228
  /**
@@ -27222,8 +27233,9 @@ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price) => {
27222
27233
  * @param _depth - Maximum depth levels (unused)
27223
27234
  * @param _from - Start of time range (unused - can be ignored in live implementations)
27224
27235
  * @param _to - End of time range (unused - can be ignored in live implementations)
27236
+ * @param _backtest - Whether running in backtest mode (unused)
27225
27237
  */
27226
- const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to) => {
27238
+ const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to, _backtest) => {
27227
27239
  throw new Error(`getOrderBook is not implemented for this exchange`);
27228
27240
  };
27229
27241
  const INTERVAL_MINUTES$1 = {
@@ -27319,9 +27331,10 @@ class ExchangeInstance {
27319
27331
  if (limit > GLOBAL_CONFIG.CC_MAX_CANDLES_PER_REQUEST) {
27320
27332
  let remaining = limit;
27321
27333
  let currentSince = new Date(since.getTime());
27334
+ const isBacktest = await GET_BACKTEST_FN();
27322
27335
  while (remaining > 0) {
27323
27336
  const chunkLimit = Math.min(remaining, GLOBAL_CONFIG.CC_MAX_CANDLES_PER_REQUEST);
27324
- const chunkData = await getCandles(symbol, interval, currentSince, chunkLimit);
27337
+ const chunkData = await getCandles(symbol, interval, currentSince, chunkLimit, isBacktest);
27325
27338
  allData.push(...chunkData);
27326
27339
  remaining -= chunkLimit;
27327
27340
  if (remaining > 0) {
@@ -27331,7 +27344,8 @@ class ExchangeInstance {
27331
27344
  }
27332
27345
  }
27333
27346
  else {
27334
- allData = await getCandles(symbol, interval, since, limit);
27347
+ const isBacktest = await GET_BACKTEST_FN();
27348
+ allData = await getCandles(symbol, interval, since, limit, isBacktest);
27335
27349
  }
27336
27350
  // Filter candles to strictly match the requested range
27337
27351
  const whenTimestamp = when.getTime();
@@ -27407,7 +27421,8 @@ class ExchangeInstance {
27407
27421
  symbol,
27408
27422
  quantity,
27409
27423
  });
27410
- return await this._methods.formatQuantity(symbol, quantity);
27424
+ const isBacktest = await GET_BACKTEST_FN();
27425
+ return await this._methods.formatQuantity(symbol, quantity, isBacktest);
27411
27426
  };
27412
27427
  /**
27413
27428
  * Format price according to exchange precision rules.
@@ -27429,7 +27444,8 @@ class ExchangeInstance {
27429
27444
  symbol,
27430
27445
  price,
27431
27446
  });
27432
- return await this._methods.formatPrice(symbol, price);
27447
+ const isBacktest = await GET_BACKTEST_FN();
27448
+ return await this._methods.formatPrice(symbol, price, isBacktest);
27433
27449
  };
27434
27450
  /**
27435
27451
  * Fetch order book for a trading pair.
@@ -27459,7 +27475,8 @@ class ExchangeInstance {
27459
27475
  });
27460
27476
  const to = new Date(Date.now());
27461
27477
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
27462
- return await this._methods.getOrderBook(symbol, depth, from, to);
27478
+ const isBacktest = await GET_BACKTEST_FN();
27479
+ return await this._methods.getOrderBook(symbol, depth, from, to, isBacktest);
27463
27480
  };
27464
27481
  const schema = bt.exchangeSchemaService.get(this.exchangeName);
27465
27482
  this._methods = CREATE_EXCHANGE_INSTANCE_FN(schema);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "1.11.10",
3
+ "version": "1.12.1",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
@@ -41,9 +41,9 @@
41
41
  "build": "rollup -c",
42
42
  "test": "npm run build && node ./test/index.mjs",
43
43
  "build:docs": "rimraf docs && mkdir docs && node ./scripts/dts-docs.cjs ./types.d.ts ./docs",
44
- "docs:gpt": "npm run build && node ./scripts/gpt-docs.mjs",
44
+ "docs:gpt": "npm run build && node ./tools/gpt-docs/index.mjs",
45
45
  "docs:uml": "npm run build && node ./scripts/uml.mjs",
46
- "docs:www": "rimraf docs/wwwroot && typedoc && node ./packages/typedoc-yandex-metrica/index.mjs",
46
+ "docs:www": "rimraf docs/wwwroot && typedoc && node ./tools/typedoc-yandex-metrica/index.mjs",
47
47
  "repl": "dotenv -e .env -- npm run build && node -e \"import('./scripts/repl.mjs')\" --interactive"
48
48
  },
49
49
  "main": "build/index.cjs",
@@ -77,5 +77,8 @@
77
77
  "di-scoped": "^1.0.20",
78
78
  "functools-kit": "^1.0.95",
79
79
  "get-moment-stamp": "^1.1.1"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public"
80
83
  }
81
84
  }
package/types.d.ts CHANGED
@@ -454,13 +454,13 @@ interface IExchangeParams extends IExchangeSchema {
454
454
  /** Execution context service (symbol, when, backtest flag) */
455
455
  execution: TExecutionContextService;
456
456
  /** Fetch candles from data source (required, defaults applied) */
457
- getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number) => Promise<ICandleData[]>;
457
+ getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number, backtest: boolean) => Promise<ICandleData[]>;
458
458
  /** Format quantity according to exchange precision rules (required, defaults applied) */
459
- formatQuantity: (symbol: string, quantity: number) => Promise<string>;
459
+ formatQuantity: (symbol: string, quantity: number, backtest: boolean) => Promise<string>;
460
460
  /** Format price according to exchange precision rules (required, defaults applied) */
461
- formatPrice: (symbol: string, price: number) => Promise<string>;
461
+ formatPrice: (symbol: string, price: number, backtest: boolean) => Promise<string>;
462
462
  /** Fetch order book for a trading pair (required, defaults applied) */
463
- getOrderBook: (symbol: string, depth: number, from: Date, to: Date) => Promise<IOrderBookData>;
463
+ getOrderBook: (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => Promise<IOrderBookData>;
464
464
  }
465
465
  /**
466
466
  * Optional callbacks for exchange data events.
@@ -485,9 +485,10 @@ interface IExchangeSchema {
485
485
  * @param interval - Candle time interval (e.g., "1m", "1h")
486
486
  * @param since - Start date for candle fetching
487
487
  * @param limit - Maximum number of candles to fetch
488
+ * @param backtest - Whether running in backtest mode
488
489
  * @returns Promise resolving to array of OHLCV candle data
489
490
  */
490
- getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number) => Promise<ICandleData[]>;
491
+ getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number, backtest: boolean) => Promise<ICandleData[]>;
491
492
  /**
492
493
  * Format quantity according to exchange precision rules.
493
494
  *
@@ -495,9 +496,10 @@ interface IExchangeSchema {
495
496
  *
496
497
  * @param symbol - Trading pair symbol
497
498
  * @param quantity - Raw quantity value
499
+ * @param backtest - Whether running in backtest mode
498
500
  * @returns Promise resolving to formatted quantity string
499
501
  */
500
- formatQuantity?: (symbol: string, quantity: number) => Promise<string>;
502
+ formatQuantity?: (symbol: string, quantity: number, backtest: boolean) => Promise<string>;
501
503
  /**
502
504
  * Format price according to exchange precision rules.
503
505
  *
@@ -505,9 +507,10 @@ interface IExchangeSchema {
505
507
  *
506
508
  * @param symbol - Trading pair symbol
507
509
  * @param price - Raw price value
510
+ * @param backtest - Whether running in backtest mode
508
511
  * @returns Promise resolving to formatted price string
509
512
  */
510
- formatPrice?: (symbol: string, price: number) => Promise<string>;
513
+ formatPrice?: (symbol: string, price: number, backtest: boolean) => Promise<string>;
511
514
  /**
512
515
  * Fetch order book for a trading pair.
513
516
  *
@@ -517,22 +520,26 @@ interface IExchangeSchema {
517
520
  * @param depth - Maximum depth levels for both bids and asks (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
518
521
  * @param from - Start of time range (used in backtest for historical data, can be ignored in live)
519
522
  * @param to - End of time range (used in backtest for historical data, can be ignored in live)
523
+ * @param backtest - Whether running in backtest mode
520
524
  * @returns Promise resolving to order book data
521
525
  *
522
526
  * @example
523
527
  * ```typescript
524
528
  * // Backtest implementation: returns historical order book for the time range
525
- * const backtestOrderBook = async (symbol: string, depth: number, from: Date, to: Date) => {
526
- * return await database.getOrderBookSnapshot(symbol, depth, from, to);
529
+ * const backtestOrderBook = async (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => {
530
+ * if (backtest) {
531
+ * return await database.getOrderBookSnapshot(symbol, depth, from, to);
532
+ * }
533
+ * return await exchange.fetchOrderBook(symbol, depth);
527
534
  * };
528
535
  *
529
- * // Live implementation: ignores from/to and returns current snapshot
530
- * const liveOrderBook = async (symbol: string, depth: number, _from: Date, _to: Date) => {
536
+ * // Live implementation: ignores from/to when not in backtest mode
537
+ * const liveOrderBook = async (symbol: string, depth: number, _from: Date, _to: Date, backtest: boolean) => {
531
538
  * return await exchange.fetchOrderBook(symbol, depth);
532
539
  * };
533
540
  * ```
534
541
  */
535
- getOrderBook?: (symbol: string, depth: number, from: Date, to: Date) => Promise<IOrderBookData>;
542
+ getOrderBook?: (symbol: string, depth: number, from: Date, to: Date, backtest: boolean) => Promise<IOrderBookData>;
536
543
  /** Optional lifecycle event callbacks (onCandleData) */
537
544
  callbacks?: Partial<IExchangeCallbacks>;
538
545
  }