backtest-kit 1.5.12 → 1.5.13
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 +338 -34
- package/build/index.mjs +337 -35
- package/package.json +1 -1
- package/types.d.ts +265 -17
package/build/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var functoolsKit = require('functools-kit');
|
|
3
4
|
var diKit = require('di-kit');
|
|
4
5
|
var diScoped = require('di-scoped');
|
|
5
|
-
var functoolsKit = require('functools-kit');
|
|
6
6
|
var fs = require('fs/promises');
|
|
7
7
|
var path = require('path');
|
|
8
8
|
var crypto = require('crypto');
|
|
@@ -20,12 +20,32 @@ const GLOBAL_CONFIG = {
|
|
|
20
20
|
* Default: 5 candles (last 5 minutes when using 1m interval)
|
|
21
21
|
*/
|
|
22
22
|
CC_AVG_PRICE_CANDLES_COUNT: 5,
|
|
23
|
+
/**
|
|
24
|
+
* Slippage percentage applied to entry and exit prices.
|
|
25
|
+
* Simulates market impact and order book depth.
|
|
26
|
+
* Applied twice (entry and exit) for realistic execution simulation.
|
|
27
|
+
* Default: 0.1% per transaction
|
|
28
|
+
*/
|
|
29
|
+
CC_PERCENT_SLIPPAGE: 0.1,
|
|
30
|
+
/**
|
|
31
|
+
* Fee percentage charged per transaction.
|
|
32
|
+
* Applied twice (entry and exit) for total fee calculation.
|
|
33
|
+
* Default: 0.1% per transaction (total 0.2%)
|
|
34
|
+
*/
|
|
35
|
+
CC_PERCENT_FEE: 0.1,
|
|
23
36
|
/**
|
|
24
37
|
* Minimum TakeProfit distance from priceOpen (percentage)
|
|
25
|
-
* Must be greater than
|
|
26
|
-
*
|
|
38
|
+
* Must be greater than (slippage + fees) to ensure profitable trades
|
|
39
|
+
*
|
|
40
|
+
* Calculation:
|
|
41
|
+
* - Slippage effect: ~0.2% (0.1% × 2 transactions)
|
|
42
|
+
* - Fees: 0.2% (0.1% × 2 transactions)
|
|
43
|
+
* - Minimum profit buffer: 0.1%
|
|
44
|
+
* - Total: 0.5%
|
|
45
|
+
*
|
|
46
|
+
* Default: 0.5% (covers all costs + minimum profit margin)
|
|
27
47
|
*/
|
|
28
|
-
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: 0.
|
|
48
|
+
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: 0.5,
|
|
29
49
|
/**
|
|
30
50
|
* Minimum StopLoss distance from priceOpen (percentage)
|
|
31
51
|
* Prevents signals from being immediately stopped out due to price volatility
|
|
@@ -92,6 +112,7 @@ const GLOBAL_CONFIG = {
|
|
|
92
112
|
*/
|
|
93
113
|
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: 5,
|
|
94
114
|
};
|
|
115
|
+
const DEFAULT_CONFIG = Object.freeze({ ...GLOBAL_CONFIG });
|
|
95
116
|
|
|
96
117
|
const { init, inject, provide } = diKit.createActivator("backtest");
|
|
97
118
|
|
|
@@ -188,6 +209,7 @@ const validationServices$1 = {
|
|
|
188
209
|
sizingValidationService: Symbol('sizingValidationService'),
|
|
189
210
|
riskValidationService: Symbol('riskValidationService'),
|
|
190
211
|
optimizerValidationService: Symbol('optimizerValidationService'),
|
|
212
|
+
configValidationService: Symbol('configValidationService'),
|
|
191
213
|
};
|
|
192
214
|
const templateServices$1 = {
|
|
193
215
|
optimizerTemplateService: Symbol('optimizerTemplateService'),
|
|
@@ -717,16 +739,6 @@ class ExchangeConnectionService {
|
|
|
717
739
|
}
|
|
718
740
|
}
|
|
719
741
|
|
|
720
|
-
/**
|
|
721
|
-
* Slippage percentage applied to entry and exit prices.
|
|
722
|
-
* Simulates market impact and order book depth.
|
|
723
|
-
*/
|
|
724
|
-
const PERCENT_SLIPPAGE = 0.1;
|
|
725
|
-
/**
|
|
726
|
-
* Fee percentage charged per transaction.
|
|
727
|
-
* Applied twice (entry and exit) for total fee calculation.
|
|
728
|
-
*/
|
|
729
|
-
const PERCENT_FEE = 0.1;
|
|
730
742
|
/**
|
|
731
743
|
* Calculates profit/loss for a closed signal with slippage and fees.
|
|
732
744
|
*
|
|
@@ -762,16 +774,16 @@ const toProfitLossDto = (signal, priceClose) => {
|
|
|
762
774
|
let priceCloseWithSlippage;
|
|
763
775
|
if (signal.position === "long") {
|
|
764
776
|
// LONG: покупаем дороже, продаем дешевле
|
|
765
|
-
priceOpenWithSlippage = priceOpen * (1 +
|
|
766
|
-
priceCloseWithSlippage = priceClose * (1 -
|
|
777
|
+
priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
778
|
+
priceCloseWithSlippage = priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
767
779
|
}
|
|
768
780
|
else {
|
|
769
781
|
// SHORT: продаем дешевле, покупаем дороже
|
|
770
|
-
priceOpenWithSlippage = priceOpen * (1 -
|
|
771
|
-
priceCloseWithSlippage = priceClose * (1 +
|
|
782
|
+
priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
783
|
+
priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
772
784
|
}
|
|
773
785
|
// Применяем комиссию дважды (при открытии и закрытии)
|
|
774
|
-
const totalFee =
|
|
786
|
+
const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
|
|
775
787
|
let pnlPercentage;
|
|
776
788
|
if (signal.position === "long") {
|
|
777
789
|
// LONG: прибыль при росте цены
|
|
@@ -8684,8 +8696,27 @@ class HeatMarkdownService {
|
|
|
8684
8696
|
}
|
|
8685
8697
|
|
|
8686
8698
|
/**
|
|
8687
|
-
*
|
|
8688
|
-
*
|
|
8699
|
+
* Service for managing and validating exchange configurations.
|
|
8700
|
+
*
|
|
8701
|
+
* Maintains a registry of all configured exchanges and validates
|
|
8702
|
+
* their existence before operations. Uses memoization for performance.
|
|
8703
|
+
*
|
|
8704
|
+
* Key features:
|
|
8705
|
+
* - Registry management: addExchange() to register new exchanges
|
|
8706
|
+
* - Validation: validate() ensures exchange exists before use
|
|
8707
|
+
* - Memoization: validation results are cached for performance
|
|
8708
|
+
* - Listing: list() returns all registered exchanges
|
|
8709
|
+
*
|
|
8710
|
+
* @throws {Error} If duplicate exchange name is added
|
|
8711
|
+
* @throws {Error} If unknown exchange is referenced
|
|
8712
|
+
*
|
|
8713
|
+
* @example
|
|
8714
|
+
* ```typescript
|
|
8715
|
+
* const exchangeValidation = new ExchangeValidationService();
|
|
8716
|
+
* exchangeValidation.addExchange("binance", binanceSchema);
|
|
8717
|
+
* exchangeValidation.validate("binance", "backtest"); // OK
|
|
8718
|
+
* exchangeValidation.validate("unknown", "live"); // Throws error
|
|
8719
|
+
* ```
|
|
8689
8720
|
*/
|
|
8690
8721
|
class ExchangeValidationService {
|
|
8691
8722
|
constructor() {
|
|
@@ -8745,8 +8776,29 @@ class ExchangeValidationService {
|
|
|
8745
8776
|
}
|
|
8746
8777
|
|
|
8747
8778
|
/**
|
|
8748
|
-
*
|
|
8749
|
-
*
|
|
8779
|
+
* Service for managing and validating trading strategy configurations.
|
|
8780
|
+
*
|
|
8781
|
+
* Maintains a registry of all configured strategies, validates their existence
|
|
8782
|
+
* before operations, and ensures associated risk profiles are valid.
|
|
8783
|
+
* Uses memoization for performance.
|
|
8784
|
+
*
|
|
8785
|
+
* Key features:
|
|
8786
|
+
* - Registry management: addStrategy() to register new strategies
|
|
8787
|
+
* - Dual validation: validates both strategy existence and risk profile (if configured)
|
|
8788
|
+
* - Memoization: validation results are cached for performance
|
|
8789
|
+
* - Listing: list() returns all registered strategies
|
|
8790
|
+
*
|
|
8791
|
+
* @throws {Error} If duplicate strategy name is added
|
|
8792
|
+
* @throws {Error} If unknown strategy is referenced
|
|
8793
|
+
* @throws {Error} If strategy's risk profile doesn't exist
|
|
8794
|
+
*
|
|
8795
|
+
* @example
|
|
8796
|
+
* ```typescript
|
|
8797
|
+
* const strategyValidation = new StrategyValidationService();
|
|
8798
|
+
* strategyValidation.addStrategy("momentum-btc", { ...schema, riskName: "conservative" });
|
|
8799
|
+
* strategyValidation.validate("momentum-btc", "backtest"); // Validates strategy + risk
|
|
8800
|
+
* strategyValidation.validate("unknown", "live"); // Throws error
|
|
8801
|
+
* ```
|
|
8750
8802
|
*/
|
|
8751
8803
|
class StrategyValidationService {
|
|
8752
8804
|
constructor() {
|
|
@@ -8817,8 +8869,27 @@ class StrategyValidationService {
|
|
|
8817
8869
|
}
|
|
8818
8870
|
|
|
8819
8871
|
/**
|
|
8820
|
-
*
|
|
8821
|
-
*
|
|
8872
|
+
* Service for managing and validating frame (timeframe) configurations.
|
|
8873
|
+
*
|
|
8874
|
+
* Maintains a registry of all configured frames and validates
|
|
8875
|
+
* their existence before operations. Uses memoization for performance.
|
|
8876
|
+
*
|
|
8877
|
+
* Key features:
|
|
8878
|
+
* - Registry management: addFrame() to register new timeframes
|
|
8879
|
+
* - Validation: validate() ensures frame exists before use
|
|
8880
|
+
* - Memoization: validation results are cached for performance
|
|
8881
|
+
* - Listing: list() returns all registered frames
|
|
8882
|
+
*
|
|
8883
|
+
* @throws {Error} If duplicate frame name is added
|
|
8884
|
+
* @throws {Error} If unknown frame is referenced
|
|
8885
|
+
*
|
|
8886
|
+
* @example
|
|
8887
|
+
* ```typescript
|
|
8888
|
+
* const frameValidation = new FrameValidationService();
|
|
8889
|
+
* frameValidation.addFrame("2024-Q1", frameSchema);
|
|
8890
|
+
* frameValidation.validate("2024-Q1", "backtest"); // OK
|
|
8891
|
+
* frameValidation.validate("unknown", "live"); // Throws error
|
|
8892
|
+
* ```
|
|
8822
8893
|
*/
|
|
8823
8894
|
class FrameValidationService {
|
|
8824
8895
|
constructor() {
|
|
@@ -8878,8 +8949,29 @@ class FrameValidationService {
|
|
|
8878
8949
|
}
|
|
8879
8950
|
|
|
8880
8951
|
/**
|
|
8881
|
-
*
|
|
8882
|
-
*
|
|
8952
|
+
* Service for managing and validating walker (parameter sweep) configurations.
|
|
8953
|
+
*
|
|
8954
|
+
* Maintains a registry of all configured walkers and validates
|
|
8955
|
+
* their existence before operations. Uses memoization for performance.
|
|
8956
|
+
*
|
|
8957
|
+
* Walkers define parameter ranges for optimization and hyperparameter tuning.
|
|
8958
|
+
*
|
|
8959
|
+
* Key features:
|
|
8960
|
+
* - Registry management: addWalker() to register new walker configurations
|
|
8961
|
+
* - Validation: validate() ensures walker exists before use
|
|
8962
|
+
* - Memoization: validation results are cached for performance
|
|
8963
|
+
* - Listing: list() returns all registered walkers
|
|
8964
|
+
*
|
|
8965
|
+
* @throws {Error} If duplicate walker name is added
|
|
8966
|
+
* @throws {Error} If unknown walker is referenced
|
|
8967
|
+
*
|
|
8968
|
+
* @example
|
|
8969
|
+
* ```typescript
|
|
8970
|
+
* const walkerValidation = new WalkerValidationService();
|
|
8971
|
+
* walkerValidation.addWalker("rsi-sweep", walkerSchema);
|
|
8972
|
+
* walkerValidation.validate("rsi-sweep", "optimizer"); // OK
|
|
8973
|
+
* walkerValidation.validate("unknown", "optimizer"); // Throws error
|
|
8974
|
+
* ```
|
|
8883
8975
|
*/
|
|
8884
8976
|
class WalkerValidationService {
|
|
8885
8977
|
constructor() {
|
|
@@ -8939,8 +9031,27 @@ class WalkerValidationService {
|
|
|
8939
9031
|
}
|
|
8940
9032
|
|
|
8941
9033
|
/**
|
|
8942
|
-
*
|
|
8943
|
-
*
|
|
9034
|
+
* Service for managing and validating position sizing configurations.
|
|
9035
|
+
*
|
|
9036
|
+
* Maintains a registry of all configured sizing strategies and validates
|
|
9037
|
+
* their existence before operations. Uses memoization for performance.
|
|
9038
|
+
*
|
|
9039
|
+
* Key features:
|
|
9040
|
+
* - Registry management: addSizing() to register new sizing strategies
|
|
9041
|
+
* - Validation: validate() ensures sizing strategy exists before use
|
|
9042
|
+
* - Memoization: validation results are cached for performance
|
|
9043
|
+
* - Listing: list() returns all registered sizing strategies
|
|
9044
|
+
*
|
|
9045
|
+
* @throws {Error} If duplicate sizing name is added
|
|
9046
|
+
* @throws {Error} If unknown sizing strategy is referenced
|
|
9047
|
+
*
|
|
9048
|
+
* @example
|
|
9049
|
+
* ```typescript
|
|
9050
|
+
* const sizingValidation = new SizingValidationService();
|
|
9051
|
+
* sizingValidation.addSizing("fixed-1000", fixedSizingSchema);
|
|
9052
|
+
* sizingValidation.validate("fixed-1000", "strategy-1"); // OK
|
|
9053
|
+
* sizingValidation.validate("unknown", "strategy-2"); // Throws error
|
|
9054
|
+
* ```
|
|
8944
9055
|
*/
|
|
8945
9056
|
class SizingValidationService {
|
|
8946
9057
|
constructor() {
|
|
@@ -9005,8 +9116,27 @@ class SizingValidationService {
|
|
|
9005
9116
|
}
|
|
9006
9117
|
|
|
9007
9118
|
/**
|
|
9008
|
-
*
|
|
9009
|
-
*
|
|
9119
|
+
* Service for managing and validating risk management configurations.
|
|
9120
|
+
*
|
|
9121
|
+
* Maintains a registry of all configured risk profiles and validates
|
|
9122
|
+
* their existence before operations. Uses memoization for performance.
|
|
9123
|
+
*
|
|
9124
|
+
* Key features:
|
|
9125
|
+
* - Registry management: addRisk() to register new risk profiles
|
|
9126
|
+
* - Validation: validate() ensures risk profile exists before use
|
|
9127
|
+
* - Memoization: validation results are cached by riskName:source for performance
|
|
9128
|
+
* - Listing: list() returns all registered risk profiles
|
|
9129
|
+
*
|
|
9130
|
+
* @throws {Error} If duplicate risk name is added
|
|
9131
|
+
* @throws {Error} If unknown risk profile is referenced
|
|
9132
|
+
*
|
|
9133
|
+
* @example
|
|
9134
|
+
* ```typescript
|
|
9135
|
+
* const riskValidation = new RiskValidationService();
|
|
9136
|
+
* riskValidation.addRisk("conservative", conservativeSchema);
|
|
9137
|
+
* riskValidation.validate("conservative", "strategy-1"); // OK
|
|
9138
|
+
* riskValidation.validate("unknown", "strategy-2"); // Throws error
|
|
9139
|
+
* ```
|
|
9010
9140
|
*/
|
|
9011
9141
|
class RiskValidationService {
|
|
9012
9142
|
constructor() {
|
|
@@ -11558,6 +11688,133 @@ class OutlineMarkdownService {
|
|
|
11558
11688
|
}
|
|
11559
11689
|
}
|
|
11560
11690
|
|
|
11691
|
+
/**
|
|
11692
|
+
* Service for validating GLOBAL_CONFIG parameters to ensure mathematical correctness
|
|
11693
|
+
* and prevent unprofitable trading configurations.
|
|
11694
|
+
*
|
|
11695
|
+
* Performs comprehensive validation on:
|
|
11696
|
+
* - **Percentage parameters**: Slippage, fees, and profit margins must be non-negative
|
|
11697
|
+
* - **Economic viability**: Ensures CC_MIN_TAKEPROFIT_DISTANCE_PERCENT covers all trading costs
|
|
11698
|
+
* (slippage + fees) to guarantee profitable trades when TakeProfit is hit
|
|
11699
|
+
* - **Range constraints**: Validates MIN < MAX relationships (e.g., StopLoss distances)
|
|
11700
|
+
* - **Time-based parameters**: Ensures positive integer values for timeouts and lifetimes
|
|
11701
|
+
* - **Candle parameters**: Validates retry counts, delays, and anomaly detection thresholds
|
|
11702
|
+
*
|
|
11703
|
+
* @throws {Error} If any validation fails, throws with detailed breakdown of all errors
|
|
11704
|
+
*
|
|
11705
|
+
* @example
|
|
11706
|
+
* ```typescript
|
|
11707
|
+
* const validator = new ConfigValidationService();
|
|
11708
|
+
* validator.validate(); // Throws if config is invalid
|
|
11709
|
+
* ```
|
|
11710
|
+
*
|
|
11711
|
+
* @example Validation failure output:
|
|
11712
|
+
* ```
|
|
11713
|
+
* GLOBAL_CONFIG validation failed:
|
|
11714
|
+
* 1. CC_MIN_TAKEPROFIT_DISTANCE_PERCENT (0.3%) is too low to cover trading costs.
|
|
11715
|
+
* Required minimum: 0.40%
|
|
11716
|
+
* Breakdown:
|
|
11717
|
+
* - Slippage effect: 0.20% (0.1% × 2 transactions)
|
|
11718
|
+
* - Fees: 0.20% (0.1% × 2 transactions)
|
|
11719
|
+
* All TakeProfit signals will be unprofitable with current settings!
|
|
11720
|
+
* ```
|
|
11721
|
+
*/
|
|
11722
|
+
class ConfigValidationService {
|
|
11723
|
+
constructor() {
|
|
11724
|
+
/**
|
|
11725
|
+
* @private
|
|
11726
|
+
* @readonly
|
|
11727
|
+
* Injected logger service instance
|
|
11728
|
+
*/
|
|
11729
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
11730
|
+
/**
|
|
11731
|
+
* Validates GLOBAL_CONFIG parameters for mathematical correctness.
|
|
11732
|
+
*
|
|
11733
|
+
* Checks:
|
|
11734
|
+
* 1. CC_MIN_TAKEPROFIT_DISTANCE_PERCENT must cover slippage + fees
|
|
11735
|
+
* 2. All percentage values must be positive
|
|
11736
|
+
* 3. Time/count values must be positive integers
|
|
11737
|
+
*
|
|
11738
|
+
* @throws Error if configuration is invalid
|
|
11739
|
+
*/
|
|
11740
|
+
this.validate = () => {
|
|
11741
|
+
this.loggerService.log("configValidationService validate");
|
|
11742
|
+
const errors = [];
|
|
11743
|
+
// Validate slippage and fee percentages
|
|
11744
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE) || GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE < 0) {
|
|
11745
|
+
errors.push(`CC_PERCENT_SLIPPAGE must be a non-negative number, got ${GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE}`);
|
|
11746
|
+
}
|
|
11747
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_PERCENT_FEE) || GLOBAL_CONFIG.CC_PERCENT_FEE < 0) {
|
|
11748
|
+
errors.push(`CC_PERCENT_FEE must be a non-negative number, got ${GLOBAL_CONFIG.CC_PERCENT_FEE}`);
|
|
11749
|
+
}
|
|
11750
|
+
// Calculate minimum required TP distance to cover costs
|
|
11751
|
+
const slippageEffect = GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE * 2; // Applied twice (entry + exit)
|
|
11752
|
+
const feesTotal = GLOBAL_CONFIG.CC_PERCENT_FEE * 2; // Applied twice (entry + exit)
|
|
11753
|
+
const minRequiredTpDistance = slippageEffect + feesTotal;
|
|
11754
|
+
// Validate CC_MIN_TAKEPROFIT_DISTANCE_PERCENT
|
|
11755
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) || GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT <= 0) {
|
|
11756
|
+
errors.push(`CC_MIN_TAKEPROFIT_DISTANCE_PERCENT must be a positive number, got ${GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT}`);
|
|
11757
|
+
}
|
|
11758
|
+
else if (GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT < minRequiredTpDistance) {
|
|
11759
|
+
errors.push(`CC_MIN_TAKEPROFIT_DISTANCE_PERCENT (${GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT}%) is too low to cover trading costs.\n` +
|
|
11760
|
+
` Required minimum: ${minRequiredTpDistance.toFixed(2)}%\n` +
|
|
11761
|
+
` Breakdown:\n` +
|
|
11762
|
+
` - Slippage effect: ${slippageEffect.toFixed(2)}% (${GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE}% × 2 transactions)\n` +
|
|
11763
|
+
` - Fees: ${feesTotal.toFixed(2)}% (${GLOBAL_CONFIG.CC_PERCENT_FEE}% × 2 transactions)\n` +
|
|
11764
|
+
` All TakeProfit signals will be unprofitable with current settings!`);
|
|
11765
|
+
}
|
|
11766
|
+
// Validate CC_MIN_STOPLOSS_DISTANCE_PERCENT
|
|
11767
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT) || GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT <= 0) {
|
|
11768
|
+
errors.push(`CC_MIN_STOPLOSS_DISTANCE_PERCENT must be a positive number, got ${GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT}`);
|
|
11769
|
+
}
|
|
11770
|
+
// Validate CC_MAX_STOPLOSS_DISTANCE_PERCENT
|
|
11771
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) || GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT <= 0) {
|
|
11772
|
+
errors.push(`CC_MAX_STOPLOSS_DISTANCE_PERCENT must be a positive number, got ${GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT}`);
|
|
11773
|
+
}
|
|
11774
|
+
// Validate that MIN < MAX for StopLoss
|
|
11775
|
+
if (Number.isFinite(GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT) &&
|
|
11776
|
+
Number.isFinite(GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) &&
|
|
11777
|
+
GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT >= GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) {
|
|
11778
|
+
errors.push(`CC_MIN_STOPLOSS_DISTANCE_PERCENT (${GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT}%) must be less than ` +
|
|
11779
|
+
`CC_MAX_STOPLOSS_DISTANCE_PERCENT (${GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT}%)`);
|
|
11780
|
+
}
|
|
11781
|
+
// Validate time-based parameters
|
|
11782
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES) || GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES <= 0) {
|
|
11783
|
+
errors.push(`CC_SCHEDULE_AWAIT_MINUTES must be a positive integer, got ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}`);
|
|
11784
|
+
}
|
|
11785
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES) || GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES <= 0) {
|
|
11786
|
+
errors.push(`CC_MAX_SIGNAL_LIFETIME_MINUTES must be a positive integer, got ${GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES}`);
|
|
11787
|
+
}
|
|
11788
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS) || GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS <= 0) {
|
|
11789
|
+
errors.push(`CC_MAX_SIGNAL_GENERATION_SECONDS must be a positive integer, got ${GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS}`);
|
|
11790
|
+
}
|
|
11791
|
+
// Validate candle-based parameters
|
|
11792
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT) || GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT <= 0) {
|
|
11793
|
+
errors.push(`CC_AVG_PRICE_CANDLES_COUNT must be a positive integer, got ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT}`);
|
|
11794
|
+
}
|
|
11795
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT) || GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT < 0) {
|
|
11796
|
+
errors.push(`CC_GET_CANDLES_RETRY_COUNT must be a non-negative integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT}`);
|
|
11797
|
+
}
|
|
11798
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS) || GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS < 0) {
|
|
11799
|
+
errors.push(`CC_GET_CANDLES_RETRY_DELAY_MS must be a non-negative integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS}`);
|
|
11800
|
+
}
|
|
11801
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR) || GLOBAL_CONFIG.CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR <= 0) {
|
|
11802
|
+
errors.push(`CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR must be a positive integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR}`);
|
|
11803
|
+
}
|
|
11804
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN) || GLOBAL_CONFIG.CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN <= 0) {
|
|
11805
|
+
errors.push(`CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN must be a positive integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN}`);
|
|
11806
|
+
}
|
|
11807
|
+
// Throw aggregated errors if any
|
|
11808
|
+
if (errors.length > 0) {
|
|
11809
|
+
const errorMessage = `GLOBAL_CONFIG validation failed:\n${errors.map((e, i) => ` ${i + 1}. ${e}`).join('\n')}`;
|
|
11810
|
+
this.loggerService.warn(errorMessage);
|
|
11811
|
+
throw new Error(errorMessage);
|
|
11812
|
+
}
|
|
11813
|
+
this.loggerService.log("configValidationService validation passed");
|
|
11814
|
+
};
|
|
11815
|
+
}
|
|
11816
|
+
}
|
|
11817
|
+
|
|
11561
11818
|
{
|
|
11562
11819
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
11563
11820
|
}
|
|
@@ -11625,6 +11882,7 @@ class OutlineMarkdownService {
|
|
|
11625
11882
|
provide(TYPES.sizingValidationService, () => new SizingValidationService());
|
|
11626
11883
|
provide(TYPES.riskValidationService, () => new RiskValidationService());
|
|
11627
11884
|
provide(TYPES.optimizerValidationService, () => new OptimizerValidationService());
|
|
11885
|
+
provide(TYPES.configValidationService, () => new ConfigValidationService());
|
|
11628
11886
|
}
|
|
11629
11887
|
{
|
|
11630
11888
|
provide(TYPES.optimizerTemplateService, () => new OptimizerTemplateService());
|
|
@@ -11697,6 +11955,7 @@ const validationServices = {
|
|
|
11697
11955
|
sizingValidationService: inject(TYPES.sizingValidationService),
|
|
11698
11956
|
riskValidationService: inject(TYPES.riskValidationService),
|
|
11699
11957
|
optimizerValidationService: inject(TYPES.optimizerValidationService),
|
|
11958
|
+
configValidationService: inject(TYPES.configValidationService),
|
|
11700
11959
|
};
|
|
11701
11960
|
const templateServices = {
|
|
11702
11961
|
optimizerTemplateService: inject(TYPES.optimizerTemplateService),
|
|
@@ -11734,7 +11993,7 @@ var backtest$1 = backtest;
|
|
|
11734
11993
|
* });
|
|
11735
11994
|
* ```
|
|
11736
11995
|
*/
|
|
11737
|
-
|
|
11996
|
+
function setLogger(logger) {
|
|
11738
11997
|
backtest$1.loggerService.setLogger(logger);
|
|
11739
11998
|
}
|
|
11740
11999
|
/**
|
|
@@ -11748,8 +12007,51 @@ async function setLogger(logger) {
|
|
|
11748
12007
|
* });
|
|
11749
12008
|
* ```
|
|
11750
12009
|
*/
|
|
11751
|
-
|
|
11752
|
-
Object.assign(
|
|
12010
|
+
function setConfig(config, _unsafe) {
|
|
12011
|
+
const prevConfig = Object.assign({}, GLOBAL_CONFIG);
|
|
12012
|
+
try {
|
|
12013
|
+
Object.assign(GLOBAL_CONFIG, config);
|
|
12014
|
+
!_unsafe && backtest$1.configValidationService.validate();
|
|
12015
|
+
}
|
|
12016
|
+
catch (error) {
|
|
12017
|
+
console.warn(`backtest-kit setConfig failed: ${functoolsKit.getErrorMessage(error)}`, config);
|
|
12018
|
+
Object.assign(GLOBAL_CONFIG, prevConfig);
|
|
12019
|
+
throw error;
|
|
12020
|
+
}
|
|
12021
|
+
}
|
|
12022
|
+
/**
|
|
12023
|
+
* Retrieves a copy of the current global configuration.
|
|
12024
|
+
*
|
|
12025
|
+
* Returns a shallow copy of the current GLOBAL_CONFIG to prevent accidental mutations.
|
|
12026
|
+
* Use this to inspect the current configuration state without modifying it.
|
|
12027
|
+
*
|
|
12028
|
+
* @returns {GlobalConfig} A copy of the current global configuration object
|
|
12029
|
+
*
|
|
12030
|
+
* @example
|
|
12031
|
+
* ```typescript
|
|
12032
|
+
* const currentConfig = getConfig();
|
|
12033
|
+
* console.log(currentConfig.CC_SCHEDULE_AWAIT_MINUTES);
|
|
12034
|
+
* ```
|
|
12035
|
+
*/
|
|
12036
|
+
function getConfig() {
|
|
12037
|
+
return Object.assign({}, GLOBAL_CONFIG);
|
|
12038
|
+
}
|
|
12039
|
+
/**
|
|
12040
|
+
* Retrieves the default configuration object for the framework.
|
|
12041
|
+
*
|
|
12042
|
+
* Returns a reference to the default configuration with all preset values.
|
|
12043
|
+
* Use this to see what configuration options are available and their default values.
|
|
12044
|
+
*
|
|
12045
|
+
* @returns {GlobalConfig} The default configuration object
|
|
12046
|
+
*
|
|
12047
|
+
* @example
|
|
12048
|
+
* ```typescript
|
|
12049
|
+
* const defaultConfig = getDefaultConfig();
|
|
12050
|
+
* console.log(defaultConfig.CC_SCHEDULE_AWAIT_MINUTES);
|
|
12051
|
+
* ```
|
|
12052
|
+
*/
|
|
12053
|
+
function getDefaultConfig() {
|
|
12054
|
+
return DEFAULT_CONFIG;
|
|
11753
12055
|
}
|
|
11754
12056
|
|
|
11755
12057
|
const ADD_STRATEGY_METHOD_NAME = "add.addStrategy";
|
|
@@ -15868,7 +16170,9 @@ exports.formatPrice = formatPrice;
|
|
|
15868
16170
|
exports.formatQuantity = formatQuantity;
|
|
15869
16171
|
exports.getAveragePrice = getAveragePrice;
|
|
15870
16172
|
exports.getCandles = getCandles;
|
|
16173
|
+
exports.getConfig = getConfig;
|
|
15871
16174
|
exports.getDate = getDate;
|
|
16175
|
+
exports.getDefaultConfig = getDefaultConfig;
|
|
15872
16176
|
exports.getMode = getMode;
|
|
15873
16177
|
exports.lib = backtest;
|
|
15874
16178
|
exports.listExchanges = listExchanges;
|