backtest-kit 12.3.0 → 12.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -6633,6 +6633,21 @@ const CALL_SIGNAL_SYNC_OPEN_FN = functoolsKit.trycatch(async (timestamp, current
6633
6633
  });
6634
6634
  }, {
6635
6635
  defaultValue: false,
6636
+ fallback: (error, timestamp, currentPrice, pendingSignal, self) => {
6637
+ const message = "ClientStrategy CALL_SIGNAL_SYNC_OPEN_FN thrown";
6638
+ const payload = {
6639
+ error: functoolsKit.errorData(error),
6640
+ message: functoolsKit.getErrorMessage(error),
6641
+ data: {
6642
+ timestamp,
6643
+ currentPrice,
6644
+ signalId: pendingSignal.id,
6645
+ }
6646
+ };
6647
+ self.params.logger.warn(message, payload);
6648
+ console.warn(message, payload);
6649
+ errorEmitter.next(error);
6650
+ }
6636
6651
  });
6637
6652
  /**
6638
6653
  * Calls onSignalSync callback for signal-close event.
@@ -6673,6 +6688,22 @@ const CALL_SIGNAL_SYNC_CLOSE_FN = functoolsKit.trycatch(async (timestamp, curren
6673
6688
  });
6674
6689
  }, {
6675
6690
  defaultValue: false,
6691
+ fallback: (error, timestamp, currentPrice, closeReason, signal, self) => {
6692
+ const message = "ClientStrategy CALL_SIGNAL_SYNC_CLOSE_FN thrown";
6693
+ const payload = {
6694
+ error: functoolsKit.errorData(error),
6695
+ message: functoolsKit.getErrorMessage(error),
6696
+ data: {
6697
+ timestamp,
6698
+ currentPrice,
6699
+ closeReason,
6700
+ signalId: signal.id,
6701
+ }
6702
+ };
6703
+ self.params.logger.warn(message, payload);
6704
+ console.warn(message, payload);
6705
+ errorEmitter.next(error);
6706
+ }
6676
6707
  });
6677
6708
  /**
6678
6709
  * Calls onCommit callback with strategy commit event.
@@ -13060,8 +13091,8 @@ class StrategyConnectionService {
13060
13091
  await strategy.waitForInit();
13061
13092
  const tick = await strategy.tick(symbol, context.strategyName);
13062
13093
  {
13063
- this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
13064
- this.timeMetaService.next(symbol, tick.createdAt, context, backtest);
13094
+ await this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
13095
+ await this.timeMetaService.next(symbol, tick.createdAt, context, backtest);
13065
13096
  }
13066
13097
  {
13067
13098
  await CALL_SIGNAL_EMIT_FN(this, tick, context, backtest, symbol);
@@ -19607,6 +19638,10 @@ const RUN_INFINITY_CHUNK_LOOP_FN = async (self, symbol, when, context, initialRe
19607
19638
  if (chunkResult.action !== "active") {
19608
19639
  return chunkResult;
19609
19640
  }
19641
+ {
19642
+ await self.priceMetaService.next(symbol, chunkResult.currentPrice, context, true);
19643
+ await self.timeMetaService.next(symbol, chunkResult._backtestLastTimestamp, context, true);
19644
+ }
19610
19645
  lastChunkCandles = chunkCandles;
19611
19646
  backtestResult = chunkResult;
19612
19647
  chunkStart = new Date(chunkResult._backtestLastTimestamp + 60000 - bufferMs);
@@ -19787,6 +19822,10 @@ const RUN_OPENED_CHUNK_LOOP_FN = async (self, symbol, when, context, bufferStart
19787
19822
  if (chunkResult.action !== "active") {
19788
19823
  return chunkResult;
19789
19824
  }
19825
+ {
19826
+ await self.priceMetaService.next(symbol, chunkResult.currentPrice, context, true);
19827
+ await self.timeMetaService.next(symbol, chunkResult._backtestLastTimestamp, context, true);
19828
+ }
19790
19829
  lastChunkCandles = chunkCandles;
19791
19830
  chunkStart = new Date(chunkResult._backtestLastTimestamp + 60000 - bufferMs);
19792
19831
  }
@@ -19877,6 +19916,8 @@ class BacktestLogicPrivateService {
19877
19916
  this.frameCoreService = inject(TYPES.frameCoreService);
19878
19917
  this.methodContextService = inject(TYPES.methodContextService);
19879
19918
  this.actionCoreService = inject(TYPES.actionCoreService);
19919
+ this.timeMetaService = inject(TYPES.timeMetaService);
19920
+ this.priceMetaService = inject(TYPES.priceMetaService);
19880
19921
  }
19881
19922
  /**
19882
19923
  * Runs backtest for a symbol, streaming closed signals as async generator.
@@ -38687,13 +38728,21 @@ const ALIGN_TO_INTERVAL_FN = (timestamp, intervalMinutes) => {
38687
38728
  const BAR_LENGTH = 30;
38688
38729
  const BAR_FILLED_CHAR = "\u2588";
38689
38730
  const BAR_EMPTY_CHAR = "\u2591";
38731
+ const LINE_WIDTH = 80;
38690
38732
  const PRINT_PROGRESS_FN = (fetched, total, symbol, interval) => {
38691
- const percent = Math.round((fetched / total) * 100);
38692
- const filled = Math.round((fetched / total) * BAR_LENGTH);
38733
+ if (total <= 0) {
38734
+ return;
38735
+ }
38736
+ const ratio = Math.min(fetched / total, 1);
38737
+ const percent = Math.round(ratio * 100);
38738
+ const filled = Math.round(ratio * BAR_LENGTH);
38693
38739
  const empty = BAR_LENGTH - filled;
38694
38740
  const bar = BAR_FILLED_CHAR.repeat(filled) + BAR_EMPTY_CHAR.repeat(empty);
38695
- process.stdout.write(`\r[${bar}] ${percent}% (${fetched}/${total}) ${symbol} ${interval}`);
38696
- if (fetched === total) {
38741
+ // \u0424\u0438\u043a\u0441. \u0448\u0438\u0440\u0438\u043d\u0430: pad \u043f\u0440\u043e\u0431\u0435\u043b\u0430\u043c\u0438 + slice. \u0418\u043d\u0430\u0447\u0435 \u043f\u0440\u0438 \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0439 \u043d\u043e\u0432\u043e\u0439 \u043c\u0435\u0442\u043a\u0435
38742
+ // (SOLUSDT \u043f\u043e\u0441\u043b\u0435 FARTCOINUSDT) \r \u043d\u0435 \u0441\u0442\u0438\u0440\u0430\u0435\u0442 \u0445\u0432\u043e\u0441\u0442 \u2192 "BTCUSDTTUSDT".
38743
+ const line = `[${bar}] ${percent}% (${fetched}/${total}) ${symbol} ${interval}`;
38744
+ process.stdout.write("\r" + line.padEnd(LINE_WIDTH).slice(0, LINE_WIDTH));
38745
+ if (fetched >= total) {
38697
38746
  process.stdout.write("\n");
38698
38747
  }
38699
38748
  };
package/build/index.mjs CHANGED
@@ -6613,6 +6613,21 @@ const CALL_SIGNAL_SYNC_OPEN_FN = trycatch(async (timestamp, currentPrice, pendin
6613
6613
  });
6614
6614
  }, {
6615
6615
  defaultValue: false,
6616
+ fallback: (error, timestamp, currentPrice, pendingSignal, self) => {
6617
+ const message = "ClientStrategy CALL_SIGNAL_SYNC_OPEN_FN thrown";
6618
+ const payload = {
6619
+ error: errorData(error),
6620
+ message: getErrorMessage(error),
6621
+ data: {
6622
+ timestamp,
6623
+ currentPrice,
6624
+ signalId: pendingSignal.id,
6625
+ }
6626
+ };
6627
+ self.params.logger.warn(message, payload);
6628
+ console.warn(message, payload);
6629
+ errorEmitter.next(error);
6630
+ }
6616
6631
  });
6617
6632
  /**
6618
6633
  * Calls onSignalSync callback for signal-close event.
@@ -6653,6 +6668,22 @@ const CALL_SIGNAL_SYNC_CLOSE_FN = trycatch(async (timestamp, currentPrice, close
6653
6668
  });
6654
6669
  }, {
6655
6670
  defaultValue: false,
6671
+ fallback: (error, timestamp, currentPrice, closeReason, signal, self) => {
6672
+ const message = "ClientStrategy CALL_SIGNAL_SYNC_CLOSE_FN thrown";
6673
+ const payload = {
6674
+ error: errorData(error),
6675
+ message: getErrorMessage(error),
6676
+ data: {
6677
+ timestamp,
6678
+ currentPrice,
6679
+ closeReason,
6680
+ signalId: signal.id,
6681
+ }
6682
+ };
6683
+ self.params.logger.warn(message, payload);
6684
+ console.warn(message, payload);
6685
+ errorEmitter.next(error);
6686
+ }
6656
6687
  });
6657
6688
  /**
6658
6689
  * Calls onCommit callback with strategy commit event.
@@ -13040,8 +13071,8 @@ class StrategyConnectionService {
13040
13071
  await strategy.waitForInit();
13041
13072
  const tick = await strategy.tick(symbol, context.strategyName);
13042
13073
  {
13043
- this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
13044
- this.timeMetaService.next(symbol, tick.createdAt, context, backtest);
13074
+ await this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
13075
+ await this.timeMetaService.next(symbol, tick.createdAt, context, backtest);
13045
13076
  }
13046
13077
  {
13047
13078
  await CALL_SIGNAL_EMIT_FN(this, tick, context, backtest, symbol);
@@ -19587,6 +19618,10 @@ const RUN_INFINITY_CHUNK_LOOP_FN = async (self, symbol, when, context, initialRe
19587
19618
  if (chunkResult.action !== "active") {
19588
19619
  return chunkResult;
19589
19620
  }
19621
+ {
19622
+ await self.priceMetaService.next(symbol, chunkResult.currentPrice, context, true);
19623
+ await self.timeMetaService.next(symbol, chunkResult._backtestLastTimestamp, context, true);
19624
+ }
19590
19625
  lastChunkCandles = chunkCandles;
19591
19626
  backtestResult = chunkResult;
19592
19627
  chunkStart = new Date(chunkResult._backtestLastTimestamp + 60000 - bufferMs);
@@ -19767,6 +19802,10 @@ const RUN_OPENED_CHUNK_LOOP_FN = async (self, symbol, when, context, bufferStart
19767
19802
  if (chunkResult.action !== "active") {
19768
19803
  return chunkResult;
19769
19804
  }
19805
+ {
19806
+ await self.priceMetaService.next(symbol, chunkResult.currentPrice, context, true);
19807
+ await self.timeMetaService.next(symbol, chunkResult._backtestLastTimestamp, context, true);
19808
+ }
19770
19809
  lastChunkCandles = chunkCandles;
19771
19810
  chunkStart = new Date(chunkResult._backtestLastTimestamp + 60000 - bufferMs);
19772
19811
  }
@@ -19857,6 +19896,8 @@ class BacktestLogicPrivateService {
19857
19896
  this.frameCoreService = inject(TYPES.frameCoreService);
19858
19897
  this.methodContextService = inject(TYPES.methodContextService);
19859
19898
  this.actionCoreService = inject(TYPES.actionCoreService);
19899
+ this.timeMetaService = inject(TYPES.timeMetaService);
19900
+ this.priceMetaService = inject(TYPES.priceMetaService);
19860
19901
  }
19861
19902
  /**
19862
19903
  * Runs backtest for a symbol, streaming closed signals as async generator.
@@ -38667,13 +38708,21 @@ const ALIGN_TO_INTERVAL_FN = (timestamp, intervalMinutes) => {
38667
38708
  const BAR_LENGTH = 30;
38668
38709
  const BAR_FILLED_CHAR = "\u2588";
38669
38710
  const BAR_EMPTY_CHAR = "\u2591";
38711
+ const LINE_WIDTH = 80;
38670
38712
  const PRINT_PROGRESS_FN = (fetched, total, symbol, interval) => {
38671
- const percent = Math.round((fetched / total) * 100);
38672
- const filled = Math.round((fetched / total) * BAR_LENGTH);
38713
+ if (total <= 0) {
38714
+ return;
38715
+ }
38716
+ const ratio = Math.min(fetched / total, 1);
38717
+ const percent = Math.round(ratio * 100);
38718
+ const filled = Math.round(ratio * BAR_LENGTH);
38673
38719
  const empty = BAR_LENGTH - filled;
38674
38720
  const bar = BAR_FILLED_CHAR.repeat(filled) + BAR_EMPTY_CHAR.repeat(empty);
38675
- process.stdout.write(`\r[${bar}] ${percent}% (${fetched}/${total}) ${symbol} ${interval}`);
38676
- if (fetched === total) {
38721
+ // \u0424\u0438\u043a\u0441. \u0448\u0438\u0440\u0438\u043d\u0430: pad \u043f\u0440\u043e\u0431\u0435\u043b\u0430\u043c\u0438 + slice. \u0418\u043d\u0430\u0447\u0435 \u043f\u0440\u0438 \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0439 \u043d\u043e\u0432\u043e\u0439 \u043c\u0435\u0442\u043a\u0435
38722
+ // (SOLUSDT \u043f\u043e\u0441\u043b\u0435 FARTCOINUSDT) \r \u043d\u0435 \u0441\u0442\u0438\u0440\u0430\u0435\u0442 \u0445\u0432\u043e\u0441\u0442 \u2192 "BTCUSDTTUSDT".
38723
+ const line = `[${bar}] ${percent}% (${fetched}/${total}) ${symbol} ${interval}`;
38724
+ process.stdout.write("\r" + line.padEnd(LINE_WIDTH).slice(0, LINE_WIDTH));
38725
+ if (fetched >= total) {
38677
38726
  process.stdout.write("\n");
38678
38727
  }
38679
38728
  };
package/package.json CHANGED
@@ -1,86 +1,86 @@
1
- {
2
- "name": "backtest-kit",
3
- "version": "12.3.0",
4
- "description": "A TypeScript library for trading system backtest",
5
- "author": {
6
- "name": "Petr Tripolsky",
7
- "email": "tripolskypetr@gmail.com",
8
- "url": "https://github.com/tripolskypetr"
9
- },
10
- "funding": {
11
- "type": "individual",
12
- "url": "http://paypal.me/tripolskypetr"
13
- },
14
- "license": "MIT",
15
- "homepage": "https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html",
16
- "keywords": [
17
- "backtesting",
18
- "backtest",
19
- "finance",
20
- "trading",
21
- "candles",
22
- "indicators",
23
- "multi value",
24
- "multi symbol",
25
- "framework"
26
- ],
27
- "files": [
28
- "build",
29
- "types.d.ts",
30
- "README.md"
31
- ],
32
- "repository": {
33
- "type": "git",
34
- "url": "https://github.com/tripolskypetr/backtest-kit",
35
- "documentation": "https://github.com/tripolskypetr/backtest-kit/tree/master/docs"
36
- },
37
- "bugs": {
38
- "url": "https://github.com/tripolskypetr/backtest-kit/issues"
39
- },
40
- "scripts": {
41
- "build": "rollup -c",
42
- "test": "npm run build && node ./test/index.mjs",
43
- "build:docs": "rimraf docs && mkdir docs && node ./scripts/dts-docs.cjs ./types.d.ts ./docs",
44
- "docs:gpt": "npm run build && node ./tools/gpt-docs/index.mjs",
45
- "docs:uml": "npm run build && node ./scripts/uml.mjs",
46
- "docs:www": "rimraf docs/wwwroot && node ./tools/typedoc-packages-docs/index.mjs && typedoc && node ./tools/typedoc-yandex-metrica/index.mjs",
47
- "repl": "dotenv -e .env -- npm run build && node -e \"import('./scripts/repl.mjs')\" --interactive"
48
- },
49
- "main": "build/index.cjs",
50
- "module": "build/index.mjs",
51
- "source": "src/index.ts",
52
- "types": "./types.d.ts",
53
- "exports": {
54
- "require": "./build/index.cjs",
55
- "types": "./types.d.ts",
56
- "import": "./build/index.mjs",
57
- "default": "./build/index.cjs"
58
- },
59
- "devDependencies": {
60
- "@rollup/plugin-typescript": "11.1.6",
61
- "@types/node": "22.9.0",
62
- "glob": "11.0.1",
63
- "plantuml": "0.0.2",
64
- "rimraf": "6.0.1",
65
- "rollup": "3.29.5",
66
- "rollup-plugin-dts": "6.1.1",
67
- "rollup-plugin-peer-deps-external": "2.2.4",
68
- "ts-morph": "27.0.2",
69
- "tslib": "2.7.0",
70
- "typedoc": "0.27.9",
71
- "worker-testbed": "2.0.0"
72
- },
73
- "peerDependencies": {
74
- "typescript": "^5.0.0"
75
- },
76
- "dependencies": {
77
- "di-kit": "^1.1.1",
78
- "di-scoped": "^1.0.21",
79
- "di-singleton": "^1.0.5",
80
- "functools-kit": "^2.3.0",
81
- "get-moment-stamp": "^2.0.0"
82
- },
83
- "publishConfig": {
84
- "access": "public"
85
- }
86
- }
1
+ {
2
+ "name": "backtest-kit",
3
+ "version": "12.5.0",
4
+ "description": "A TypeScript library for trading system backtest",
5
+ "author": {
6
+ "name": "Petr Tripolsky",
7
+ "email": "tripolskypetr@gmail.com",
8
+ "url": "https://github.com/tripolskypetr"
9
+ },
10
+ "funding": {
11
+ "type": "individual",
12
+ "url": "http://paypal.me/tripolskypetr"
13
+ },
14
+ "license": "MIT",
15
+ "homepage": "https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html",
16
+ "keywords": [
17
+ "backtesting",
18
+ "backtest",
19
+ "finance",
20
+ "trading",
21
+ "candles",
22
+ "indicators",
23
+ "multi value",
24
+ "multi symbol",
25
+ "framework"
26
+ ],
27
+ "files": [
28
+ "build",
29
+ "types.d.ts",
30
+ "README.md"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/tripolskypetr/backtest-kit",
35
+ "documentation": "https://github.com/tripolskypetr/backtest-kit/tree/master/docs"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/tripolskypetr/backtest-kit/issues"
39
+ },
40
+ "scripts": {
41
+ "build": "rollup -c",
42
+ "test": "npm run build && node ./test/index.mjs",
43
+ "build:docs": "rimraf docs && mkdir docs && node ./scripts/dts-docs.cjs ./types.d.ts ./docs",
44
+ "docs:gpt": "npm run build && node ./tools/gpt-docs/index.mjs",
45
+ "docs:uml": "npm run build && node ./scripts/uml.mjs",
46
+ "docs:www": "rimraf docs/wwwroot && node ./tools/typedoc-packages-docs/index.mjs && typedoc && node ./tools/typedoc-yandex-metrica/index.mjs",
47
+ "repl": "dotenv -e .env -- npm run build && node -e \"import('./scripts/repl.mjs')\" --interactive"
48
+ },
49
+ "main": "build/index.cjs",
50
+ "module": "build/index.mjs",
51
+ "source": "src/index.ts",
52
+ "types": "./types.d.ts",
53
+ "exports": {
54
+ "require": "./build/index.cjs",
55
+ "types": "./types.d.ts",
56
+ "import": "./build/index.mjs",
57
+ "default": "./build/index.cjs"
58
+ },
59
+ "devDependencies": {
60
+ "@rollup/plugin-typescript": "11.1.6",
61
+ "@types/node": "22.9.0",
62
+ "glob": "11.0.1",
63
+ "plantuml": "0.0.2",
64
+ "rimraf": "6.0.1",
65
+ "rollup": "3.29.5",
66
+ "rollup-plugin-dts": "6.1.1",
67
+ "rollup-plugin-peer-deps-external": "2.2.4",
68
+ "ts-morph": "27.0.2",
69
+ "tslib": "2.7.0",
70
+ "typedoc": "0.27.9",
71
+ "worker-testbed": "2.0.0"
72
+ },
73
+ "peerDependencies": {
74
+ "typescript": "^5.0.0"
75
+ },
76
+ "dependencies": {
77
+ "di-kit": "^1.1.1",
78
+ "di-scoped": "^1.0.21",
79
+ "di-singleton": "^1.0.5",
80
+ "functools-kit": "^2.3.0",
81
+ "get-moment-stamp": "^2.0.0"
82
+ },
83
+ "publishConfig": {
84
+ "access": "public"
85
+ }
86
+ }
package/types.d.ts CHANGED
@@ -34491,6 +34491,8 @@ declare class BacktestLogicPrivateService {
34491
34491
  readonly context: IMethodContext;
34492
34492
  };
34493
34493
  readonly actionCoreService: ActionCoreService;
34494
+ readonly timeMetaService: TimeMetaService;
34495
+ readonly priceMetaService: PriceMetaService;
34494
34496
  /**
34495
34497
  * Runs backtest for a symbol, streaming closed signals as async generator.
34496
34498
  *
@@ -34582,6 +34584,8 @@ type IBacktestLogicPrivateService = Omit<BacktestLogicPrivateService, keyof {
34582
34584
  frameCoreService: never;
34583
34585
  actionCoreService: never;
34584
34586
  methodContextService: never;
34587
+ priceMetaService: never;
34588
+ timeMetaService: never;
34585
34589
  }>;
34586
34590
  /**
34587
34591
  * Type definition for BacktestLogicPublicService.