backtest-kit 1.5.11 → 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 +347 -34
- package/build/index.mjs +346 -35
- package/package.json +1 -1
- package/types.d.ts +268 -17
package/build/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, ToolRegistry, isObject, and, resolveDocuments, str, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
|
|
1
2
|
import { createActivator } from 'di-kit';
|
|
2
3
|
import { scoped } from 'di-scoped';
|
|
3
|
-
import { errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, ToolRegistry, isObject, and, resolveDocuments, str, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
|
|
4
4
|
import fs, { mkdir, writeFile } from 'fs/promises';
|
|
5
5
|
import path, { join } from 'path';
|
|
6
6
|
import crypto from 'crypto';
|
|
@@ -18,12 +18,32 @@ const GLOBAL_CONFIG = {
|
|
|
18
18
|
* Default: 5 candles (last 5 minutes when using 1m interval)
|
|
19
19
|
*/
|
|
20
20
|
CC_AVG_PRICE_CANDLES_COUNT: 5,
|
|
21
|
+
/**
|
|
22
|
+
* Slippage percentage applied to entry and exit prices.
|
|
23
|
+
* Simulates market impact and order book depth.
|
|
24
|
+
* Applied twice (entry and exit) for realistic execution simulation.
|
|
25
|
+
* Default: 0.1% per transaction
|
|
26
|
+
*/
|
|
27
|
+
CC_PERCENT_SLIPPAGE: 0.1,
|
|
28
|
+
/**
|
|
29
|
+
* Fee percentage charged per transaction.
|
|
30
|
+
* Applied twice (entry and exit) for total fee calculation.
|
|
31
|
+
* Default: 0.1% per transaction (total 0.2%)
|
|
32
|
+
*/
|
|
33
|
+
CC_PERCENT_FEE: 0.1,
|
|
21
34
|
/**
|
|
22
35
|
* Minimum TakeProfit distance from priceOpen (percentage)
|
|
23
|
-
* Must be greater than
|
|
24
|
-
*
|
|
36
|
+
* Must be greater than (slippage + fees) to ensure profitable trades
|
|
37
|
+
*
|
|
38
|
+
* Calculation:
|
|
39
|
+
* - Slippage effect: ~0.2% (0.1% × 2 transactions)
|
|
40
|
+
* - Fees: 0.2% (0.1% × 2 transactions)
|
|
41
|
+
* - Minimum profit buffer: 0.1%
|
|
42
|
+
* - Total: 0.5%
|
|
43
|
+
*
|
|
44
|
+
* Default: 0.5% (covers all costs + minimum profit margin)
|
|
25
45
|
*/
|
|
26
|
-
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: 0.
|
|
46
|
+
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: 0.5,
|
|
27
47
|
/**
|
|
28
48
|
* Minimum StopLoss distance from priceOpen (percentage)
|
|
29
49
|
* Prevents signals from being immediately stopped out due to price volatility
|
|
@@ -90,6 +110,7 @@ const GLOBAL_CONFIG = {
|
|
|
90
110
|
*/
|
|
91
111
|
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: 5,
|
|
92
112
|
};
|
|
113
|
+
const DEFAULT_CONFIG = Object.freeze({ ...GLOBAL_CONFIG });
|
|
93
114
|
|
|
94
115
|
const { init, inject, provide } = createActivator("backtest");
|
|
95
116
|
|
|
@@ -186,6 +207,7 @@ const validationServices$1 = {
|
|
|
186
207
|
sizingValidationService: Symbol('sizingValidationService'),
|
|
187
208
|
riskValidationService: Symbol('riskValidationService'),
|
|
188
209
|
optimizerValidationService: Symbol('optimizerValidationService'),
|
|
210
|
+
configValidationService: Symbol('configValidationService'),
|
|
189
211
|
};
|
|
190
212
|
const templateServices$1 = {
|
|
191
213
|
optimizerTemplateService: Symbol('optimizerTemplateService'),
|
|
@@ -715,16 +737,6 @@ class ExchangeConnectionService {
|
|
|
715
737
|
}
|
|
716
738
|
}
|
|
717
739
|
|
|
718
|
-
/**
|
|
719
|
-
* Slippage percentage applied to entry and exit prices.
|
|
720
|
-
* Simulates market impact and order book depth.
|
|
721
|
-
*/
|
|
722
|
-
const PERCENT_SLIPPAGE = 0.1;
|
|
723
|
-
/**
|
|
724
|
-
* Fee percentage charged per transaction.
|
|
725
|
-
* Applied twice (entry and exit) for total fee calculation.
|
|
726
|
-
*/
|
|
727
|
-
const PERCENT_FEE = 0.1;
|
|
728
740
|
/**
|
|
729
741
|
* Calculates profit/loss for a closed signal with slippage and fees.
|
|
730
742
|
*
|
|
@@ -760,16 +772,16 @@ const toProfitLossDto = (signal, priceClose) => {
|
|
|
760
772
|
let priceCloseWithSlippage;
|
|
761
773
|
if (signal.position === "long") {
|
|
762
774
|
// LONG: покупаем дороже, продаем дешевле
|
|
763
|
-
priceOpenWithSlippage = priceOpen * (1 +
|
|
764
|
-
priceCloseWithSlippage = priceClose * (1 -
|
|
775
|
+
priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
776
|
+
priceCloseWithSlippage = priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
765
777
|
}
|
|
766
778
|
else {
|
|
767
779
|
// SHORT: продаем дешевле, покупаем дороже
|
|
768
|
-
priceOpenWithSlippage = priceOpen * (1 -
|
|
769
|
-
priceCloseWithSlippage = priceClose * (1 +
|
|
780
|
+
priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
781
|
+
priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
770
782
|
}
|
|
771
783
|
// Применяем комиссию дважды (при открытии и закрытии)
|
|
772
|
-
const totalFee =
|
|
784
|
+
const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE * 2;
|
|
773
785
|
let pnlPercentage;
|
|
774
786
|
if (signal.position === "long") {
|
|
775
787
|
// LONG: прибыль при росте цены
|
|
@@ -8682,8 +8694,27 @@ class HeatMarkdownService {
|
|
|
8682
8694
|
}
|
|
8683
8695
|
|
|
8684
8696
|
/**
|
|
8685
|
-
*
|
|
8686
|
-
*
|
|
8697
|
+
* Service for managing and validating exchange configurations.
|
|
8698
|
+
*
|
|
8699
|
+
* Maintains a registry of all configured exchanges and validates
|
|
8700
|
+
* their existence before operations. Uses memoization for performance.
|
|
8701
|
+
*
|
|
8702
|
+
* Key features:
|
|
8703
|
+
* - Registry management: addExchange() to register new exchanges
|
|
8704
|
+
* - Validation: validate() ensures exchange exists before use
|
|
8705
|
+
* - Memoization: validation results are cached for performance
|
|
8706
|
+
* - Listing: list() returns all registered exchanges
|
|
8707
|
+
*
|
|
8708
|
+
* @throws {Error} If duplicate exchange name is added
|
|
8709
|
+
* @throws {Error} If unknown exchange is referenced
|
|
8710
|
+
*
|
|
8711
|
+
* @example
|
|
8712
|
+
* ```typescript
|
|
8713
|
+
* const exchangeValidation = new ExchangeValidationService();
|
|
8714
|
+
* exchangeValidation.addExchange("binance", binanceSchema);
|
|
8715
|
+
* exchangeValidation.validate("binance", "backtest"); // OK
|
|
8716
|
+
* exchangeValidation.validate("unknown", "live"); // Throws error
|
|
8717
|
+
* ```
|
|
8687
8718
|
*/
|
|
8688
8719
|
class ExchangeValidationService {
|
|
8689
8720
|
constructor() {
|
|
@@ -8743,8 +8774,29 @@ class ExchangeValidationService {
|
|
|
8743
8774
|
}
|
|
8744
8775
|
|
|
8745
8776
|
/**
|
|
8746
|
-
*
|
|
8747
|
-
*
|
|
8777
|
+
* Service for managing and validating trading strategy configurations.
|
|
8778
|
+
*
|
|
8779
|
+
* Maintains a registry of all configured strategies, validates their existence
|
|
8780
|
+
* before operations, and ensures associated risk profiles are valid.
|
|
8781
|
+
* Uses memoization for performance.
|
|
8782
|
+
*
|
|
8783
|
+
* Key features:
|
|
8784
|
+
* - Registry management: addStrategy() to register new strategies
|
|
8785
|
+
* - Dual validation: validates both strategy existence and risk profile (if configured)
|
|
8786
|
+
* - Memoization: validation results are cached for performance
|
|
8787
|
+
* - Listing: list() returns all registered strategies
|
|
8788
|
+
*
|
|
8789
|
+
* @throws {Error} If duplicate strategy name is added
|
|
8790
|
+
* @throws {Error} If unknown strategy is referenced
|
|
8791
|
+
* @throws {Error} If strategy's risk profile doesn't exist
|
|
8792
|
+
*
|
|
8793
|
+
* @example
|
|
8794
|
+
* ```typescript
|
|
8795
|
+
* const strategyValidation = new StrategyValidationService();
|
|
8796
|
+
* strategyValidation.addStrategy("momentum-btc", { ...schema, riskName: "conservative" });
|
|
8797
|
+
* strategyValidation.validate("momentum-btc", "backtest"); // Validates strategy + risk
|
|
8798
|
+
* strategyValidation.validate("unknown", "live"); // Throws error
|
|
8799
|
+
* ```
|
|
8748
8800
|
*/
|
|
8749
8801
|
class StrategyValidationService {
|
|
8750
8802
|
constructor() {
|
|
@@ -8815,8 +8867,27 @@ class StrategyValidationService {
|
|
|
8815
8867
|
}
|
|
8816
8868
|
|
|
8817
8869
|
/**
|
|
8818
|
-
*
|
|
8819
|
-
*
|
|
8870
|
+
* Service for managing and validating frame (timeframe) configurations.
|
|
8871
|
+
*
|
|
8872
|
+
* Maintains a registry of all configured frames and validates
|
|
8873
|
+
* their existence before operations. Uses memoization for performance.
|
|
8874
|
+
*
|
|
8875
|
+
* Key features:
|
|
8876
|
+
* - Registry management: addFrame() to register new timeframes
|
|
8877
|
+
* - Validation: validate() ensures frame exists before use
|
|
8878
|
+
* - Memoization: validation results are cached for performance
|
|
8879
|
+
* - Listing: list() returns all registered frames
|
|
8880
|
+
*
|
|
8881
|
+
* @throws {Error} If duplicate frame name is added
|
|
8882
|
+
* @throws {Error} If unknown frame is referenced
|
|
8883
|
+
*
|
|
8884
|
+
* @example
|
|
8885
|
+
* ```typescript
|
|
8886
|
+
* const frameValidation = new FrameValidationService();
|
|
8887
|
+
* frameValidation.addFrame("2024-Q1", frameSchema);
|
|
8888
|
+
* frameValidation.validate("2024-Q1", "backtest"); // OK
|
|
8889
|
+
* frameValidation.validate("unknown", "live"); // Throws error
|
|
8890
|
+
* ```
|
|
8820
8891
|
*/
|
|
8821
8892
|
class FrameValidationService {
|
|
8822
8893
|
constructor() {
|
|
@@ -8876,8 +8947,29 @@ class FrameValidationService {
|
|
|
8876
8947
|
}
|
|
8877
8948
|
|
|
8878
8949
|
/**
|
|
8879
|
-
*
|
|
8880
|
-
*
|
|
8950
|
+
* Service for managing and validating walker (parameter sweep) configurations.
|
|
8951
|
+
*
|
|
8952
|
+
* Maintains a registry of all configured walkers and validates
|
|
8953
|
+
* their existence before operations. Uses memoization for performance.
|
|
8954
|
+
*
|
|
8955
|
+
* Walkers define parameter ranges for optimization and hyperparameter tuning.
|
|
8956
|
+
*
|
|
8957
|
+
* Key features:
|
|
8958
|
+
* - Registry management: addWalker() to register new walker configurations
|
|
8959
|
+
* - Validation: validate() ensures walker exists before use
|
|
8960
|
+
* - Memoization: validation results are cached for performance
|
|
8961
|
+
* - Listing: list() returns all registered walkers
|
|
8962
|
+
*
|
|
8963
|
+
* @throws {Error} If duplicate walker name is added
|
|
8964
|
+
* @throws {Error} If unknown walker is referenced
|
|
8965
|
+
*
|
|
8966
|
+
* @example
|
|
8967
|
+
* ```typescript
|
|
8968
|
+
* const walkerValidation = new WalkerValidationService();
|
|
8969
|
+
* walkerValidation.addWalker("rsi-sweep", walkerSchema);
|
|
8970
|
+
* walkerValidation.validate("rsi-sweep", "optimizer"); // OK
|
|
8971
|
+
* walkerValidation.validate("unknown", "optimizer"); // Throws error
|
|
8972
|
+
* ```
|
|
8881
8973
|
*/
|
|
8882
8974
|
class WalkerValidationService {
|
|
8883
8975
|
constructor() {
|
|
@@ -8937,8 +9029,27 @@ class WalkerValidationService {
|
|
|
8937
9029
|
}
|
|
8938
9030
|
|
|
8939
9031
|
/**
|
|
8940
|
-
*
|
|
8941
|
-
*
|
|
9032
|
+
* Service for managing and validating position sizing configurations.
|
|
9033
|
+
*
|
|
9034
|
+
* Maintains a registry of all configured sizing strategies and validates
|
|
9035
|
+
* their existence before operations. Uses memoization for performance.
|
|
9036
|
+
*
|
|
9037
|
+
* Key features:
|
|
9038
|
+
* - Registry management: addSizing() to register new sizing strategies
|
|
9039
|
+
* - Validation: validate() ensures sizing strategy exists before use
|
|
9040
|
+
* - Memoization: validation results are cached for performance
|
|
9041
|
+
* - Listing: list() returns all registered sizing strategies
|
|
9042
|
+
*
|
|
9043
|
+
* @throws {Error} If duplicate sizing name is added
|
|
9044
|
+
* @throws {Error} If unknown sizing strategy is referenced
|
|
9045
|
+
*
|
|
9046
|
+
* @example
|
|
9047
|
+
* ```typescript
|
|
9048
|
+
* const sizingValidation = new SizingValidationService();
|
|
9049
|
+
* sizingValidation.addSizing("fixed-1000", fixedSizingSchema);
|
|
9050
|
+
* sizingValidation.validate("fixed-1000", "strategy-1"); // OK
|
|
9051
|
+
* sizingValidation.validate("unknown", "strategy-2"); // Throws error
|
|
9052
|
+
* ```
|
|
8942
9053
|
*/
|
|
8943
9054
|
class SizingValidationService {
|
|
8944
9055
|
constructor() {
|
|
@@ -9003,8 +9114,27 @@ class SizingValidationService {
|
|
|
9003
9114
|
}
|
|
9004
9115
|
|
|
9005
9116
|
/**
|
|
9006
|
-
*
|
|
9007
|
-
*
|
|
9117
|
+
* Service for managing and validating risk management configurations.
|
|
9118
|
+
*
|
|
9119
|
+
* Maintains a registry of all configured risk profiles and validates
|
|
9120
|
+
* their existence before operations. Uses memoization for performance.
|
|
9121
|
+
*
|
|
9122
|
+
* Key features:
|
|
9123
|
+
* - Registry management: addRisk() to register new risk profiles
|
|
9124
|
+
* - Validation: validate() ensures risk profile exists before use
|
|
9125
|
+
* - Memoization: validation results are cached by riskName:source for performance
|
|
9126
|
+
* - Listing: list() returns all registered risk profiles
|
|
9127
|
+
*
|
|
9128
|
+
* @throws {Error} If duplicate risk name is added
|
|
9129
|
+
* @throws {Error} If unknown risk profile is referenced
|
|
9130
|
+
*
|
|
9131
|
+
* @example
|
|
9132
|
+
* ```typescript
|
|
9133
|
+
* const riskValidation = new RiskValidationService();
|
|
9134
|
+
* riskValidation.addRisk("conservative", conservativeSchema);
|
|
9135
|
+
* riskValidation.validate("conservative", "strategy-1"); // OK
|
|
9136
|
+
* riskValidation.validate("unknown", "strategy-2"); // Throws error
|
|
9137
|
+
* ```
|
|
9008
9138
|
*/
|
|
9009
9139
|
class RiskValidationService {
|
|
9010
9140
|
constructor() {
|
|
@@ -11556,6 +11686,133 @@ class OutlineMarkdownService {
|
|
|
11556
11686
|
}
|
|
11557
11687
|
}
|
|
11558
11688
|
|
|
11689
|
+
/**
|
|
11690
|
+
* Service for validating GLOBAL_CONFIG parameters to ensure mathematical correctness
|
|
11691
|
+
* and prevent unprofitable trading configurations.
|
|
11692
|
+
*
|
|
11693
|
+
* Performs comprehensive validation on:
|
|
11694
|
+
* - **Percentage parameters**: Slippage, fees, and profit margins must be non-negative
|
|
11695
|
+
* - **Economic viability**: Ensures CC_MIN_TAKEPROFIT_DISTANCE_PERCENT covers all trading costs
|
|
11696
|
+
* (slippage + fees) to guarantee profitable trades when TakeProfit is hit
|
|
11697
|
+
* - **Range constraints**: Validates MIN < MAX relationships (e.g., StopLoss distances)
|
|
11698
|
+
* - **Time-based parameters**: Ensures positive integer values for timeouts and lifetimes
|
|
11699
|
+
* - **Candle parameters**: Validates retry counts, delays, and anomaly detection thresholds
|
|
11700
|
+
*
|
|
11701
|
+
* @throws {Error} If any validation fails, throws with detailed breakdown of all errors
|
|
11702
|
+
*
|
|
11703
|
+
* @example
|
|
11704
|
+
* ```typescript
|
|
11705
|
+
* const validator = new ConfigValidationService();
|
|
11706
|
+
* validator.validate(); // Throws if config is invalid
|
|
11707
|
+
* ```
|
|
11708
|
+
*
|
|
11709
|
+
* @example Validation failure output:
|
|
11710
|
+
* ```
|
|
11711
|
+
* GLOBAL_CONFIG validation failed:
|
|
11712
|
+
* 1. CC_MIN_TAKEPROFIT_DISTANCE_PERCENT (0.3%) is too low to cover trading costs.
|
|
11713
|
+
* Required minimum: 0.40%
|
|
11714
|
+
* Breakdown:
|
|
11715
|
+
* - Slippage effect: 0.20% (0.1% × 2 transactions)
|
|
11716
|
+
* - Fees: 0.20% (0.1% × 2 transactions)
|
|
11717
|
+
* All TakeProfit signals will be unprofitable with current settings!
|
|
11718
|
+
* ```
|
|
11719
|
+
*/
|
|
11720
|
+
class ConfigValidationService {
|
|
11721
|
+
constructor() {
|
|
11722
|
+
/**
|
|
11723
|
+
* @private
|
|
11724
|
+
* @readonly
|
|
11725
|
+
* Injected logger service instance
|
|
11726
|
+
*/
|
|
11727
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
11728
|
+
/**
|
|
11729
|
+
* Validates GLOBAL_CONFIG parameters for mathematical correctness.
|
|
11730
|
+
*
|
|
11731
|
+
* Checks:
|
|
11732
|
+
* 1. CC_MIN_TAKEPROFIT_DISTANCE_PERCENT must cover slippage + fees
|
|
11733
|
+
* 2. All percentage values must be positive
|
|
11734
|
+
* 3. Time/count values must be positive integers
|
|
11735
|
+
*
|
|
11736
|
+
* @throws Error if configuration is invalid
|
|
11737
|
+
*/
|
|
11738
|
+
this.validate = () => {
|
|
11739
|
+
this.loggerService.log("configValidationService validate");
|
|
11740
|
+
const errors = [];
|
|
11741
|
+
// Validate slippage and fee percentages
|
|
11742
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE) || GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE < 0) {
|
|
11743
|
+
errors.push(`CC_PERCENT_SLIPPAGE must be a non-negative number, got ${GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE}`);
|
|
11744
|
+
}
|
|
11745
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_PERCENT_FEE) || GLOBAL_CONFIG.CC_PERCENT_FEE < 0) {
|
|
11746
|
+
errors.push(`CC_PERCENT_FEE must be a non-negative number, got ${GLOBAL_CONFIG.CC_PERCENT_FEE}`);
|
|
11747
|
+
}
|
|
11748
|
+
// Calculate minimum required TP distance to cover costs
|
|
11749
|
+
const slippageEffect = GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE * 2; // Applied twice (entry + exit)
|
|
11750
|
+
const feesTotal = GLOBAL_CONFIG.CC_PERCENT_FEE * 2; // Applied twice (entry + exit)
|
|
11751
|
+
const minRequiredTpDistance = slippageEffect + feesTotal;
|
|
11752
|
+
// Validate CC_MIN_TAKEPROFIT_DISTANCE_PERCENT
|
|
11753
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) || GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT <= 0) {
|
|
11754
|
+
errors.push(`CC_MIN_TAKEPROFIT_DISTANCE_PERCENT must be a positive number, got ${GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT}`);
|
|
11755
|
+
}
|
|
11756
|
+
else if (GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT < minRequiredTpDistance) {
|
|
11757
|
+
errors.push(`CC_MIN_TAKEPROFIT_DISTANCE_PERCENT (${GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT}%) is too low to cover trading costs.\n` +
|
|
11758
|
+
` Required minimum: ${minRequiredTpDistance.toFixed(2)}%\n` +
|
|
11759
|
+
` Breakdown:\n` +
|
|
11760
|
+
` - Slippage effect: ${slippageEffect.toFixed(2)}% (${GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE}% × 2 transactions)\n` +
|
|
11761
|
+
` - Fees: ${feesTotal.toFixed(2)}% (${GLOBAL_CONFIG.CC_PERCENT_FEE}% × 2 transactions)\n` +
|
|
11762
|
+
` All TakeProfit signals will be unprofitable with current settings!`);
|
|
11763
|
+
}
|
|
11764
|
+
// Validate CC_MIN_STOPLOSS_DISTANCE_PERCENT
|
|
11765
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT) || GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT <= 0) {
|
|
11766
|
+
errors.push(`CC_MIN_STOPLOSS_DISTANCE_PERCENT must be a positive number, got ${GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT}`);
|
|
11767
|
+
}
|
|
11768
|
+
// Validate CC_MAX_STOPLOSS_DISTANCE_PERCENT
|
|
11769
|
+
if (!Number.isFinite(GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) || GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT <= 0) {
|
|
11770
|
+
errors.push(`CC_MAX_STOPLOSS_DISTANCE_PERCENT must be a positive number, got ${GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT}`);
|
|
11771
|
+
}
|
|
11772
|
+
// Validate that MIN < MAX for StopLoss
|
|
11773
|
+
if (Number.isFinite(GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT) &&
|
|
11774
|
+
Number.isFinite(GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) &&
|
|
11775
|
+
GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT >= GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) {
|
|
11776
|
+
errors.push(`CC_MIN_STOPLOSS_DISTANCE_PERCENT (${GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT}%) must be less than ` +
|
|
11777
|
+
`CC_MAX_STOPLOSS_DISTANCE_PERCENT (${GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT}%)`);
|
|
11778
|
+
}
|
|
11779
|
+
// Validate time-based parameters
|
|
11780
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES) || GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES <= 0) {
|
|
11781
|
+
errors.push(`CC_SCHEDULE_AWAIT_MINUTES must be a positive integer, got ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}`);
|
|
11782
|
+
}
|
|
11783
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES) || GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES <= 0) {
|
|
11784
|
+
errors.push(`CC_MAX_SIGNAL_LIFETIME_MINUTES must be a positive integer, got ${GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES}`);
|
|
11785
|
+
}
|
|
11786
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS) || GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS <= 0) {
|
|
11787
|
+
errors.push(`CC_MAX_SIGNAL_GENERATION_SECONDS must be a positive integer, got ${GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS}`);
|
|
11788
|
+
}
|
|
11789
|
+
// Validate candle-based parameters
|
|
11790
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT) || GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT <= 0) {
|
|
11791
|
+
errors.push(`CC_AVG_PRICE_CANDLES_COUNT must be a positive integer, got ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT}`);
|
|
11792
|
+
}
|
|
11793
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT) || GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT < 0) {
|
|
11794
|
+
errors.push(`CC_GET_CANDLES_RETRY_COUNT must be a non-negative integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_COUNT}`);
|
|
11795
|
+
}
|
|
11796
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS) || GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS < 0) {
|
|
11797
|
+
errors.push(`CC_GET_CANDLES_RETRY_DELAY_MS must be a non-negative integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS}`);
|
|
11798
|
+
}
|
|
11799
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR) || GLOBAL_CONFIG.CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR <= 0) {
|
|
11800
|
+
errors.push(`CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR must be a positive integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_PRICE_ANOMALY_THRESHOLD_FACTOR}`);
|
|
11801
|
+
}
|
|
11802
|
+
if (!Number.isInteger(GLOBAL_CONFIG.CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN) || GLOBAL_CONFIG.CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN <= 0) {
|
|
11803
|
+
errors.push(`CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN must be a positive integer, got ${GLOBAL_CONFIG.CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN}`);
|
|
11804
|
+
}
|
|
11805
|
+
// Throw aggregated errors if any
|
|
11806
|
+
if (errors.length > 0) {
|
|
11807
|
+
const errorMessage = `GLOBAL_CONFIG validation failed:\n${errors.map((e, i) => ` ${i + 1}. ${e}`).join('\n')}`;
|
|
11808
|
+
this.loggerService.warn(errorMessage);
|
|
11809
|
+
throw new Error(errorMessage);
|
|
11810
|
+
}
|
|
11811
|
+
this.loggerService.log("configValidationService validation passed");
|
|
11812
|
+
};
|
|
11813
|
+
}
|
|
11814
|
+
}
|
|
11815
|
+
|
|
11559
11816
|
{
|
|
11560
11817
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
11561
11818
|
}
|
|
@@ -11623,6 +11880,7 @@ class OutlineMarkdownService {
|
|
|
11623
11880
|
provide(TYPES.sizingValidationService, () => new SizingValidationService());
|
|
11624
11881
|
provide(TYPES.riskValidationService, () => new RiskValidationService());
|
|
11625
11882
|
provide(TYPES.optimizerValidationService, () => new OptimizerValidationService());
|
|
11883
|
+
provide(TYPES.configValidationService, () => new ConfigValidationService());
|
|
11626
11884
|
}
|
|
11627
11885
|
{
|
|
11628
11886
|
provide(TYPES.optimizerTemplateService, () => new OptimizerTemplateService());
|
|
@@ -11695,6 +11953,7 @@ const validationServices = {
|
|
|
11695
11953
|
sizingValidationService: inject(TYPES.sizingValidationService),
|
|
11696
11954
|
riskValidationService: inject(TYPES.riskValidationService),
|
|
11697
11955
|
optimizerValidationService: inject(TYPES.optimizerValidationService),
|
|
11956
|
+
configValidationService: inject(TYPES.configValidationService),
|
|
11698
11957
|
};
|
|
11699
11958
|
const templateServices = {
|
|
11700
11959
|
optimizerTemplateService: inject(TYPES.optimizerTemplateService),
|
|
@@ -11732,7 +11991,7 @@ var backtest$1 = backtest;
|
|
|
11732
11991
|
* });
|
|
11733
11992
|
* ```
|
|
11734
11993
|
*/
|
|
11735
|
-
|
|
11994
|
+
function setLogger(logger) {
|
|
11736
11995
|
backtest$1.loggerService.setLogger(logger);
|
|
11737
11996
|
}
|
|
11738
11997
|
/**
|
|
@@ -11746,8 +12005,51 @@ async function setLogger(logger) {
|
|
|
11746
12005
|
* });
|
|
11747
12006
|
* ```
|
|
11748
12007
|
*/
|
|
11749
|
-
|
|
11750
|
-
Object.assign(
|
|
12008
|
+
function setConfig(config, _unsafe) {
|
|
12009
|
+
const prevConfig = Object.assign({}, GLOBAL_CONFIG);
|
|
12010
|
+
try {
|
|
12011
|
+
Object.assign(GLOBAL_CONFIG, config);
|
|
12012
|
+
!_unsafe && backtest$1.configValidationService.validate();
|
|
12013
|
+
}
|
|
12014
|
+
catch (error) {
|
|
12015
|
+
console.warn(`backtest-kit setConfig failed: ${getErrorMessage(error)}`, config);
|
|
12016
|
+
Object.assign(GLOBAL_CONFIG, prevConfig);
|
|
12017
|
+
throw error;
|
|
12018
|
+
}
|
|
12019
|
+
}
|
|
12020
|
+
/**
|
|
12021
|
+
* Retrieves a copy of the current global configuration.
|
|
12022
|
+
*
|
|
12023
|
+
* Returns a shallow copy of the current GLOBAL_CONFIG to prevent accidental mutations.
|
|
12024
|
+
* Use this to inspect the current configuration state without modifying it.
|
|
12025
|
+
*
|
|
12026
|
+
* @returns {GlobalConfig} A copy of the current global configuration object
|
|
12027
|
+
*
|
|
12028
|
+
* @example
|
|
12029
|
+
* ```typescript
|
|
12030
|
+
* const currentConfig = getConfig();
|
|
12031
|
+
* console.log(currentConfig.CC_SCHEDULE_AWAIT_MINUTES);
|
|
12032
|
+
* ```
|
|
12033
|
+
*/
|
|
12034
|
+
function getConfig() {
|
|
12035
|
+
return Object.assign({}, GLOBAL_CONFIG);
|
|
12036
|
+
}
|
|
12037
|
+
/**
|
|
12038
|
+
* Retrieves the default configuration object for the framework.
|
|
12039
|
+
*
|
|
12040
|
+
* Returns a reference to the default configuration with all preset values.
|
|
12041
|
+
* Use this to see what configuration options are available and their default values.
|
|
12042
|
+
*
|
|
12043
|
+
* @returns {GlobalConfig} The default configuration object
|
|
12044
|
+
*
|
|
12045
|
+
* @example
|
|
12046
|
+
* ```typescript
|
|
12047
|
+
* const defaultConfig = getDefaultConfig();
|
|
12048
|
+
* console.log(defaultConfig.CC_SCHEDULE_AWAIT_MINUTES);
|
|
12049
|
+
* ```
|
|
12050
|
+
*/
|
|
12051
|
+
function getDefaultConfig() {
|
|
12052
|
+
return DEFAULT_CONFIG;
|
|
11751
12053
|
}
|
|
11752
12054
|
|
|
11753
12055
|
const ADD_STRATEGY_METHOD_NAME = "add.addStrategy";
|
|
@@ -13496,6 +13798,8 @@ class BacktestInstance {
|
|
|
13496
13798
|
constructor(symbol, strategyName) {
|
|
13497
13799
|
this.symbol = symbol;
|
|
13498
13800
|
this.strategyName = strategyName;
|
|
13801
|
+
/** A randomly generated string. */
|
|
13802
|
+
this.id = randomString();
|
|
13499
13803
|
/** Internal flag indicating if backtest was stopped manually */
|
|
13500
13804
|
this._isStopped = false;
|
|
13501
13805
|
/** Internal flag indicating if backtest task completed */
|
|
@@ -13532,6 +13836,7 @@ class BacktestInstance {
|
|
|
13532
13836
|
this.getStatus = async () => {
|
|
13533
13837
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_STATUS);
|
|
13534
13838
|
return {
|
|
13839
|
+
id: this.id,
|
|
13535
13840
|
symbol: this.symbol,
|
|
13536
13841
|
strategyName: this.strategyName,
|
|
13537
13842
|
status: this.task.getStatus(),
|
|
@@ -13982,6 +14287,8 @@ class LiveInstance {
|
|
|
13982
14287
|
constructor(symbol, strategyName) {
|
|
13983
14288
|
this.symbol = symbol;
|
|
13984
14289
|
this.strategyName = strategyName;
|
|
14290
|
+
/** A randomly generated string. */
|
|
14291
|
+
this.id = randomString();
|
|
13985
14292
|
/** Internal flag indicating if live trading was stopped manually */
|
|
13986
14293
|
this._isStopped = false;
|
|
13987
14294
|
/** Internal flag indicating if live trading task completed */
|
|
@@ -14018,6 +14325,7 @@ class LiveInstance {
|
|
|
14018
14325
|
this.getStatus = async () => {
|
|
14019
14326
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_STATUS);
|
|
14020
14327
|
return {
|
|
14328
|
+
id: this.id,
|
|
14021
14329
|
symbol: this.symbol,
|
|
14022
14330
|
strategyName: this.strategyName,
|
|
14023
14331
|
status: this.task.getStatus(),
|
|
@@ -14730,6 +15038,8 @@ class WalkerInstance {
|
|
|
14730
15038
|
constructor(symbol, walkerName) {
|
|
14731
15039
|
this.symbol = symbol;
|
|
14732
15040
|
this.walkerName = walkerName;
|
|
15041
|
+
/** A randomly generated string. */
|
|
15042
|
+
this.id = randomString();
|
|
14733
15043
|
/** Internal flag indicating if walker was stopped manually */
|
|
14734
15044
|
this._isStopped = false;
|
|
14735
15045
|
/** Internal flag indicating if walker task completed */
|
|
@@ -14766,6 +15076,7 @@ class WalkerInstance {
|
|
|
14766
15076
|
this.getStatus = async () => {
|
|
14767
15077
|
backtest$1.loggerService.info(WALKER_METHOD_NAME_GET_STATUS);
|
|
14768
15078
|
return {
|
|
15079
|
+
id: this.id,
|
|
14769
15080
|
symbol: this.symbol,
|
|
14770
15081
|
walkerName: this.walkerName,
|
|
14771
15082
|
status: this.task.getStatus(),
|
|
@@ -15827,4 +16138,4 @@ class ConstantUtils {
|
|
|
15827
16138
|
*/
|
|
15828
16139
|
const Constant = new ConstantUtils();
|
|
15829
16140
|
|
|
15830
|
-
export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
|
|
16141
|
+
export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getConfig, getDate, getDefaultConfig, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
|