backtest-kit 8.1.2 → 8.2.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
@@ -21989,14 +21989,24 @@ let ReportStorage$a = class ReportStorage {
21989
21989
  `**Total PNL:** ${stats.totalPnl === null ? "N/A" : `${stats.totalPnl > 0 ? "+" : ""}${stats.totalPnl.toFixed(2)}% (higher is better)`}`,
21990
21990
  `**Standard Deviation:** ${stats.stdDev === null ? "N/A" : `${stats.stdDev.toFixed(3)}% (lower is better)`}`,
21991
21991
  `**Sharpe Ratio:** ${stats.sharpeRatio === null ? "N/A" : `${stats.sharpeRatio.toFixed(3)} (higher is better)`}`,
21992
- `**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better)`}`,
21992
+ `**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better, theoretical)`}`,
21993
21993
  `**Certainty Ratio:** ${stats.certaintyRatio === null ? "N/A" : `${stats.certaintyRatio.toFixed(3)} (higher is better)`}`,
21994
- `**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better)`}`,
21994
+ `**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better, theoretical)`}`,
21995
21995
  `**Avg Peak PNL:** ${stats.avgPeakPnl === null ? "N/A" : `${stats.avgPeakPnl > 0 ? "+" : ""}${stats.avgPeakPnl.toFixed(2)}% (higher is better)`}`,
21996
21996
  `**Avg Max Drawdown PNL:** ${stats.avgFallPnl === null ? "N/A" : `${stats.avgFallPnl.toFixed(2)}% (closer to 0 is better)`}`,
21997
21997
  `**Sortino Ratio:** ${stats.sortinoRatio === null ? "N/A" : `${stats.sortinoRatio.toFixed(3)} (higher is better)`}`,
21998
- `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better)`}`,
21998
+ `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better, theoretical)`}`,
21999
21999
  `**Recovery Factor:** ${stats.recoveryFactor === null ? "N/A" : `${stats.recoveryFactor.toFixed(3)} (higher is better)`}`,
22000
+ "",
22001
+ `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
22002
+ `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
22003
+ `*Annualized Sharpe Ratio: theoretical maximum assuming continuous trading. Real-world value is lower due to idle periods.*`,
22004
+ `*Sortino Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
22005
+ `*Certainty Ratio: below 1.0 means average loss exceeds average win. Above 1.5 is considered good.*`,
22006
+ `*Expected Yearly Returns: theoretical maximum assuming all capital is deployed continuously with no idle time.*`,
22007
+ `*Calmar Ratio: below 0.5 is poor, 0.5-1.0 is acceptable, above 1.0 is strong. Based on theoretical yearly returns.*`,
22008
+ `*Recovery Factor: below 1.0 means total profit does not cover max drawdown. Above 3.0 is considered good.*`,
22009
+ `*All metrics require 100+ signals to be statistically reliable. Time period matters only for Annualized Sharpe Ratio and Expected Yearly Returns — they assume current market conditions hold year-round, which may not reflect reality.*`,
22000
22010
  ].join("\n");
22001
22011
  }
22002
22012
  /**
@@ -22728,8 +22738,18 @@ let ReportStorage$9 = class ReportStorage {
22728
22738
  `**Avg Peak PNL:** ${stats.avgPeakPnl === null ? "N/A" : `${stats.avgPeakPnl > 0 ? "+" : ""}${stats.avgPeakPnl.toFixed(2)}% (higher is better)`}`,
22729
22739
  `**Avg Max Drawdown PNL:** ${stats.avgFallPnl === null ? "N/A" : `${stats.avgFallPnl.toFixed(2)}% (closer to 0 is better)`}`,
22730
22740
  `**Sortino Ratio:** ${stats.sortinoRatio === null ? "N/A" : `${stats.sortinoRatio.toFixed(3)} (higher is better)`}`,
22731
- `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better)`}`,
22741
+ `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better, theoretical)`}`,
22732
22742
  `**Recovery Factor:** ${stats.recoveryFactor === null ? "N/A" : `${stats.recoveryFactor.toFixed(3)} (higher is better)`}`,
22743
+ "",
22744
+ `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
22745
+ `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
22746
+ `*Annualized Sharpe Ratio: theoretical maximum assuming continuous trading. Real-world value is lower due to idle periods.*`,
22747
+ `*Sortino Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
22748
+ `*Certainty Ratio: below 1.0 means average loss exceeds average win. Above 1.5 is considered good.*`,
22749
+ `*Expected Yearly Returns: theoretical maximum assuming all capital is deployed continuously with no idle time.*`,
22750
+ `*Calmar Ratio: below 0.5 is poor, 0.5-1.0 is acceptable, above 1.0 is strong. Based on theoretical yearly returns.*`,
22751
+ `*Recovery Factor: below 1.0 means total profit does not cover max drawdown. Above 3.0 is considered good.*`,
22752
+ `*All metrics require 100+ signals to be statistically reliable. Time period matters only for Annualized Sharpe Ratio and Expected Yearly Returns — they assume current market conditions hold year-round, which may not reflect reality.*`,
22733
22753
  ].join("\n");
22734
22754
  }
22735
22755
  /**
@@ -24851,7 +24871,16 @@ class HeatmapStorage {
24851
24871
  "",
24852
24872
  `**Total Symbols:** ${data.totalSymbols} | **Portfolio PNL:** ${data.portfolioTotalPnl !== null ? functoolsKit.str(data.portfolioTotalPnl, "%") : "N/A"} | **Portfolio Sharpe:** ${data.portfolioSharpeRatio !== null ? functoolsKit.str(data.portfolioSharpeRatio) : "N/A"} | **Total Trades:** ${data.portfolioTotalTrades} | **Avg Peak PNL:** ${data.portfolioAvgPeakPnl !== null ? functoolsKit.str(data.portfolioAvgPeakPnl, "%") : "N/A"} | **Avg Max Drawdown PNL:** ${data.portfolioAvgFallPnl !== null ? functoolsKit.str(data.portfolioAvgFallPnl, "%") : "N/A"}`,
24853
24873
  "",
24854
- table
24874
+ table,
24875
+ "",
24876
+ `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
24877
+ `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals per symbol.*`,
24878
+ `*Sortino Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
24879
+ `*Certainty Ratio: below 1.0 means average loss exceeds average win. Above 1.5 is considered good.*`,
24880
+ `*Profit Factor: below 1.0 means strategy is losing overall. Above 1.5 is considered good.*`,
24881
+ `*Calmar Ratio: below 0.5 is poor, 0.5-1.0 is acceptable, above 1.0 is strong. Based on theoretical yearly returns.*`,
24882
+ `*Recovery Factor: below 1.0 means total profit does not cover max drawdown. Above 3.0 is considered good.*`,
24883
+ `*All metrics require 100+ signals per symbol to be statistically reliable. Time period matters only for Calmar Ratio — it assumes current market conditions hold year-round, which may not reflect reality.*`,
24855
24884
  ].join("\n");
24856
24885
  }
24857
24886
  /**
@@ -35786,6 +35815,7 @@ class BrokerProxy {
35786
35815
  */
35787
35816
  async onSignalOpenCommit(payload) {
35788
35817
  if (this._instance.onSignalOpenCommit) {
35818
+ await this.waitForInit();
35789
35819
  await this._instance.onSignalOpenCommit(payload);
35790
35820
  return;
35791
35821
  }
@@ -35800,6 +35830,7 @@ class BrokerProxy {
35800
35830
  */
35801
35831
  async onSignalCloseCommit(payload) {
35802
35832
  if (this._instance.onSignalCloseCommit) {
35833
+ await this.waitForInit();
35803
35834
  await this._instance.onSignalCloseCommit(payload);
35804
35835
  return;
35805
35836
  }
@@ -35814,6 +35845,7 @@ class BrokerProxy {
35814
35845
  */
35815
35846
  async onPartialProfitCommit(payload) {
35816
35847
  if (this._instance.onPartialProfitCommit) {
35848
+ await this.waitForInit();
35817
35849
  await this._instance.onPartialProfitCommit(payload);
35818
35850
  return;
35819
35851
  }
@@ -35828,6 +35860,7 @@ class BrokerProxy {
35828
35860
  */
35829
35861
  async onPartialLossCommit(payload) {
35830
35862
  if (this._instance.onPartialLossCommit) {
35863
+ await this.waitForInit();
35831
35864
  await this._instance.onPartialLossCommit(payload);
35832
35865
  return;
35833
35866
  }
@@ -35842,6 +35875,7 @@ class BrokerProxy {
35842
35875
  */
35843
35876
  async onTrailingStopCommit(payload) {
35844
35877
  if (this._instance.onTrailingStopCommit) {
35878
+ await this.waitForInit();
35845
35879
  await this._instance.onTrailingStopCommit(payload);
35846
35880
  return;
35847
35881
  }
@@ -35856,6 +35890,7 @@ class BrokerProxy {
35856
35890
  */
35857
35891
  async onTrailingTakeCommit(payload) {
35858
35892
  if (this._instance.onTrailingTakeCommit) {
35893
+ await this.waitForInit();
35859
35894
  await this._instance.onTrailingTakeCommit(payload);
35860
35895
  return;
35861
35896
  }
@@ -35870,6 +35905,7 @@ class BrokerProxy {
35870
35905
  */
35871
35906
  async onBreakevenCommit(payload) {
35872
35907
  if (this._instance.onBreakevenCommit) {
35908
+ await this.waitForInit();
35873
35909
  await this._instance.onBreakevenCommit(payload);
35874
35910
  return;
35875
35911
  }
@@ -35884,6 +35920,7 @@ class BrokerProxy {
35884
35920
  */
35885
35921
  async onAverageBuyCommit(payload) {
35886
35922
  if (this._instance.onAverageBuyCommit) {
35923
+ await this.waitForInit();
35887
35924
  await this._instance.onAverageBuyCommit(payload);
35888
35925
  return;
35889
35926
  }
package/build/index.mjs CHANGED
@@ -21969,14 +21969,24 @@ let ReportStorage$a = class ReportStorage {
21969
21969
  `**Total PNL:** ${stats.totalPnl === null ? "N/A" : `${stats.totalPnl > 0 ? "+" : ""}${stats.totalPnl.toFixed(2)}% (higher is better)`}`,
21970
21970
  `**Standard Deviation:** ${stats.stdDev === null ? "N/A" : `${stats.stdDev.toFixed(3)}% (lower is better)`}`,
21971
21971
  `**Sharpe Ratio:** ${stats.sharpeRatio === null ? "N/A" : `${stats.sharpeRatio.toFixed(3)} (higher is better)`}`,
21972
- `**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better)`}`,
21972
+ `**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better, theoretical)`}`,
21973
21973
  `**Certainty Ratio:** ${stats.certaintyRatio === null ? "N/A" : `${stats.certaintyRatio.toFixed(3)} (higher is better)`}`,
21974
- `**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better)`}`,
21974
+ `**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better, theoretical)`}`,
21975
21975
  `**Avg Peak PNL:** ${stats.avgPeakPnl === null ? "N/A" : `${stats.avgPeakPnl > 0 ? "+" : ""}${stats.avgPeakPnl.toFixed(2)}% (higher is better)`}`,
21976
21976
  `**Avg Max Drawdown PNL:** ${stats.avgFallPnl === null ? "N/A" : `${stats.avgFallPnl.toFixed(2)}% (closer to 0 is better)`}`,
21977
21977
  `**Sortino Ratio:** ${stats.sortinoRatio === null ? "N/A" : `${stats.sortinoRatio.toFixed(3)} (higher is better)`}`,
21978
- `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better)`}`,
21978
+ `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better, theoretical)`}`,
21979
21979
  `**Recovery Factor:** ${stats.recoveryFactor === null ? "N/A" : `${stats.recoveryFactor.toFixed(3)} (higher is better)`}`,
21980
+ "",
21981
+ `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
21982
+ `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
21983
+ `*Annualized Sharpe Ratio: theoretical maximum assuming continuous trading. Real-world value is lower due to idle periods.*`,
21984
+ `*Sortino Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
21985
+ `*Certainty Ratio: below 1.0 means average loss exceeds average win. Above 1.5 is considered good.*`,
21986
+ `*Expected Yearly Returns: theoretical maximum assuming all capital is deployed continuously with no idle time.*`,
21987
+ `*Calmar Ratio: below 0.5 is poor, 0.5-1.0 is acceptable, above 1.0 is strong. Based on theoretical yearly returns.*`,
21988
+ `*Recovery Factor: below 1.0 means total profit does not cover max drawdown. Above 3.0 is considered good.*`,
21989
+ `*All metrics require 100+ signals to be statistically reliable. Time period matters only for Annualized Sharpe Ratio and Expected Yearly Returns — they assume current market conditions hold year-round, which may not reflect reality.*`,
21980
21990
  ].join("\n");
21981
21991
  }
21982
21992
  /**
@@ -22708,8 +22718,18 @@ let ReportStorage$9 = class ReportStorage {
22708
22718
  `**Avg Peak PNL:** ${stats.avgPeakPnl === null ? "N/A" : `${stats.avgPeakPnl > 0 ? "+" : ""}${stats.avgPeakPnl.toFixed(2)}% (higher is better)`}`,
22709
22719
  `**Avg Max Drawdown PNL:** ${stats.avgFallPnl === null ? "N/A" : `${stats.avgFallPnl.toFixed(2)}% (closer to 0 is better)`}`,
22710
22720
  `**Sortino Ratio:** ${stats.sortinoRatio === null ? "N/A" : `${stats.sortinoRatio.toFixed(3)} (higher is better)`}`,
22711
- `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better)`}`,
22721
+ `**Calmar Ratio:** ${stats.calmarRatio === null ? "N/A" : `${stats.calmarRatio.toFixed(3)} (higher is better, theoretical)`}`,
22712
22722
  `**Recovery Factor:** ${stats.recoveryFactor === null ? "N/A" : `${stats.recoveryFactor.toFixed(3)} (higher is better)`}`,
22723
+ "",
22724
+ `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
22725
+ `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
22726
+ `*Annualized Sharpe Ratio: theoretical maximum assuming continuous trading. Real-world value is lower due to idle periods.*`,
22727
+ `*Sortino Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
22728
+ `*Certainty Ratio: below 1.0 means average loss exceeds average win. Above 1.5 is considered good.*`,
22729
+ `*Expected Yearly Returns: theoretical maximum assuming all capital is deployed continuously with no idle time.*`,
22730
+ `*Calmar Ratio: below 0.5 is poor, 0.5-1.0 is acceptable, above 1.0 is strong. Based on theoretical yearly returns.*`,
22731
+ `*Recovery Factor: below 1.0 means total profit does not cover max drawdown. Above 3.0 is considered good.*`,
22732
+ `*All metrics require 100+ signals to be statistically reliable. Time period matters only for Annualized Sharpe Ratio and Expected Yearly Returns — they assume current market conditions hold year-round, which may not reflect reality.*`,
22713
22733
  ].join("\n");
22714
22734
  }
22715
22735
  /**
@@ -24831,7 +24851,16 @@ class HeatmapStorage {
24831
24851
  "",
24832
24852
  `**Total Symbols:** ${data.totalSymbols} | **Portfolio PNL:** ${data.portfolioTotalPnl !== null ? str(data.portfolioTotalPnl, "%") : "N/A"} | **Portfolio Sharpe:** ${data.portfolioSharpeRatio !== null ? str(data.portfolioSharpeRatio) : "N/A"} | **Total Trades:** ${data.portfolioTotalTrades} | **Avg Peak PNL:** ${data.portfolioAvgPeakPnl !== null ? str(data.portfolioAvgPeakPnl, "%") : "N/A"} | **Avg Max Drawdown PNL:** ${data.portfolioAvgFallPnl !== null ? str(data.portfolioAvgFallPnl, "%") : "N/A"}`,
24833
24853
  "",
24834
- table
24854
+ table,
24855
+ "",
24856
+ `*Win Rate: reliable above 200+ signals; below 30 signals a single streak can shift it by 10-20%.*`,
24857
+ `*Sharpe Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals per symbol.*`,
24858
+ `*Sortino Ratio: below 1.0 is poor, 1.0-2.0 is acceptable, above 2.0 is strong. Requires 30+ signals.*`,
24859
+ `*Certainty Ratio: below 1.0 means average loss exceeds average win. Above 1.5 is considered good.*`,
24860
+ `*Profit Factor: below 1.0 means strategy is losing overall. Above 1.5 is considered good.*`,
24861
+ `*Calmar Ratio: below 0.5 is poor, 0.5-1.0 is acceptable, above 1.0 is strong. Based on theoretical yearly returns.*`,
24862
+ `*Recovery Factor: below 1.0 means total profit does not cover max drawdown. Above 3.0 is considered good.*`,
24863
+ `*All metrics require 100+ signals per symbol to be statistically reliable. Time period matters only for Calmar Ratio — it assumes current market conditions hold year-round, which may not reflect reality.*`,
24835
24864
  ].join("\n");
24836
24865
  }
24837
24866
  /**
@@ -35766,6 +35795,7 @@ class BrokerProxy {
35766
35795
  */
35767
35796
  async onSignalOpenCommit(payload) {
35768
35797
  if (this._instance.onSignalOpenCommit) {
35798
+ await this.waitForInit();
35769
35799
  await this._instance.onSignalOpenCommit(payload);
35770
35800
  return;
35771
35801
  }
@@ -35780,6 +35810,7 @@ class BrokerProxy {
35780
35810
  */
35781
35811
  async onSignalCloseCommit(payload) {
35782
35812
  if (this._instance.onSignalCloseCommit) {
35813
+ await this.waitForInit();
35783
35814
  await this._instance.onSignalCloseCommit(payload);
35784
35815
  return;
35785
35816
  }
@@ -35794,6 +35825,7 @@ class BrokerProxy {
35794
35825
  */
35795
35826
  async onPartialProfitCommit(payload) {
35796
35827
  if (this._instance.onPartialProfitCommit) {
35828
+ await this.waitForInit();
35797
35829
  await this._instance.onPartialProfitCommit(payload);
35798
35830
  return;
35799
35831
  }
@@ -35808,6 +35840,7 @@ class BrokerProxy {
35808
35840
  */
35809
35841
  async onPartialLossCommit(payload) {
35810
35842
  if (this._instance.onPartialLossCommit) {
35843
+ await this.waitForInit();
35811
35844
  await this._instance.onPartialLossCommit(payload);
35812
35845
  return;
35813
35846
  }
@@ -35822,6 +35855,7 @@ class BrokerProxy {
35822
35855
  */
35823
35856
  async onTrailingStopCommit(payload) {
35824
35857
  if (this._instance.onTrailingStopCommit) {
35858
+ await this.waitForInit();
35825
35859
  await this._instance.onTrailingStopCommit(payload);
35826
35860
  return;
35827
35861
  }
@@ -35836,6 +35870,7 @@ class BrokerProxy {
35836
35870
  */
35837
35871
  async onTrailingTakeCommit(payload) {
35838
35872
  if (this._instance.onTrailingTakeCommit) {
35873
+ await this.waitForInit();
35839
35874
  await this._instance.onTrailingTakeCommit(payload);
35840
35875
  return;
35841
35876
  }
@@ -35850,6 +35885,7 @@ class BrokerProxy {
35850
35885
  */
35851
35886
  async onBreakevenCommit(payload) {
35852
35887
  if (this._instance.onBreakevenCommit) {
35888
+ await this.waitForInit();
35853
35889
  await this._instance.onBreakevenCommit(payload);
35854
35890
  return;
35855
35891
  }
@@ -35864,6 +35900,7 @@ class BrokerProxy {
35864
35900
  */
35865
35901
  async onAverageBuyCommit(payload) {
35866
35902
  if (this._instance.onAverageBuyCommit) {
35903
+ await this.waitForInit();
35867
35904
  await this._instance.onAverageBuyCommit(payload);
35868
35905
  return;
35869
35906
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "8.1.2",
3
+ "version": "8.2.0",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",