backtest-kit 1.2.2 → 1.3.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 +96 -25
- package/build/index.mjs +96 -25
- package/package.json +1 -1
- package/types.d.ts +9 -9
package/build/index.cjs
CHANGED
|
@@ -131,12 +131,14 @@ const globalServices$1 = {
|
|
|
131
131
|
exchangeGlobalService: Symbol('exchangeGlobalService'),
|
|
132
132
|
strategyGlobalService: Symbol('strategyGlobalService'),
|
|
133
133
|
frameGlobalService: Symbol('frameGlobalService'),
|
|
134
|
-
liveGlobalService: Symbol('liveGlobalService'),
|
|
135
|
-
backtestGlobalService: Symbol('backtestGlobalService'),
|
|
136
|
-
walkerGlobalService: Symbol('walkerGlobalService'),
|
|
137
134
|
sizingGlobalService: Symbol('sizingGlobalService'),
|
|
138
135
|
riskGlobalService: Symbol('riskGlobalService'),
|
|
139
136
|
};
|
|
137
|
+
const commandServices$1 = {
|
|
138
|
+
liveCommandService: Symbol('liveCommandService'),
|
|
139
|
+
backtestCommandService: Symbol('backtestCommandService'),
|
|
140
|
+
walkerCommandService: Symbol('walkerCommandService'),
|
|
141
|
+
};
|
|
140
142
|
const logicPrivateServices$1 = {
|
|
141
143
|
backtestLogicPrivateService: Symbol('backtestLogicPrivateService'),
|
|
142
144
|
liveLogicPrivateService: Symbol('liveLogicPrivateService'),
|
|
@@ -169,6 +171,7 @@ const TYPES = {
|
|
|
169
171
|
...connectionServices$1,
|
|
170
172
|
...schemaServices$1,
|
|
171
173
|
...globalServices$1,
|
|
174
|
+
...commandServices$1,
|
|
172
175
|
...logicPrivateServices$1,
|
|
173
176
|
...logicPublicServices$1,
|
|
174
177
|
...markdownServices$1,
|
|
@@ -1457,8 +1460,15 @@ const INTERVAL_MINUTES$1 = {
|
|
|
1457
1460
|
"30m": 30,
|
|
1458
1461
|
"1h": 60,
|
|
1459
1462
|
};
|
|
1460
|
-
const VALIDATE_SIGNAL_FN = (signal) => {
|
|
1463
|
+
const VALIDATE_SIGNAL_FN = (signal, currentPrice, isScheduled) => {
|
|
1461
1464
|
const errors = [];
|
|
1465
|
+
// ЗАЩИТА ОТ NaN/Infinity: currentPrice должна быть конечным числом
|
|
1466
|
+
if (!isFinite(currentPrice)) {
|
|
1467
|
+
errors.push(`currentPrice must be a finite number, got ${currentPrice} (${typeof currentPrice})`);
|
|
1468
|
+
}
|
|
1469
|
+
if (isFinite(currentPrice) && currentPrice <= 0) {
|
|
1470
|
+
errors.push(`currentPrice must be positive, got ${currentPrice}`);
|
|
1471
|
+
}
|
|
1462
1472
|
// ЗАЩИТА ОТ NaN/Infinity: все цены должны быть конечными числами
|
|
1463
1473
|
if (!isFinite(signal.priceOpen)) {
|
|
1464
1474
|
errors.push(`priceOpen must be a finite number, got ${signal.priceOpen} (${typeof signal.priceOpen})`);
|
|
@@ -1487,6 +1497,20 @@ const VALIDATE_SIGNAL_FN = (signal) => {
|
|
|
1487
1497
|
if (signal.priceStopLoss >= signal.priceOpen) {
|
|
1488
1498
|
errors.push(`Long: priceStopLoss (${signal.priceStopLoss}) must be < priceOpen (${signal.priceOpen})`);
|
|
1489
1499
|
}
|
|
1500
|
+
// ЗАЩИТА ОТ EDGE CASE: для immediate сигналов проверяем что текущая цена не пробила SL/TP
|
|
1501
|
+
// Для scheduled сигналов эта проверка избыточна т.к. priceOpen уже проверен выше
|
|
1502
|
+
if (!isScheduled) {
|
|
1503
|
+
// Текущая цена уже пробила StopLoss - позиция откроется и сразу закроется по SL
|
|
1504
|
+
if (isFinite(currentPrice) && currentPrice < signal.priceStopLoss) {
|
|
1505
|
+
errors.push(`Long: currentPrice (${currentPrice}) < priceStopLoss (${signal.priceStopLoss}). ` +
|
|
1506
|
+
`Signal would be immediately cancelled. This signal is invalid.`);
|
|
1507
|
+
}
|
|
1508
|
+
// Текущая цена уже достигла TakeProfit - профит упущен
|
|
1509
|
+
if (isFinite(currentPrice) && currentPrice > signal.priceTakeProfit) {
|
|
1510
|
+
errors.push(`Long: currentPrice (${currentPrice}) > priceTakeProfit (${signal.priceTakeProfit}). ` +
|
|
1511
|
+
`Signal is invalid - the profit opportunity has already passed.`);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1490
1514
|
// ЗАЩИТА ОТ МИКРО-ПРОФИТА: TakeProfit должен быть достаточно далеко, чтобы покрыть комиссии
|
|
1491
1515
|
if (GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) {
|
|
1492
1516
|
const tpDistancePercent = ((signal.priceTakeProfit - signal.priceOpen) / signal.priceOpen) * 100;
|
|
@@ -1514,6 +1538,20 @@ const VALIDATE_SIGNAL_FN = (signal) => {
|
|
|
1514
1538
|
if (signal.priceStopLoss <= signal.priceOpen) {
|
|
1515
1539
|
errors.push(`Short: priceStopLoss (${signal.priceStopLoss}) must be > priceOpen (${signal.priceOpen})`);
|
|
1516
1540
|
}
|
|
1541
|
+
// ЗАЩИТА ОТ EDGE CASE: для immediate сигналов проверяем что текущая цена не пробила SL/TP
|
|
1542
|
+
// Для scheduled сигналов эта проверка избыточна т.к. priceOpen уже проверен выше
|
|
1543
|
+
if (!isScheduled) {
|
|
1544
|
+
// Текущая цена уже пробила StopLoss - позиция откроется и сразу закроется по SL
|
|
1545
|
+
if (isFinite(currentPrice) && currentPrice > signal.priceStopLoss) {
|
|
1546
|
+
errors.push(`Short: currentPrice (${currentPrice}) > priceStopLoss (${signal.priceStopLoss}). ` +
|
|
1547
|
+
`Signal would be immediately cancelled. This signal is invalid.`);
|
|
1548
|
+
}
|
|
1549
|
+
// Текущая цена уже достигла TakeProfit - профит упущен
|
|
1550
|
+
if (isFinite(currentPrice) && currentPrice < signal.priceTakeProfit) {
|
|
1551
|
+
errors.push(`Short: currentPrice (${currentPrice}) < priceTakeProfit (${signal.priceTakeProfit}). ` +
|
|
1552
|
+
`Signal is invalid - the profit opportunity has already passed.`);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1517
1555
|
// ЗАЩИТА ОТ МИКРО-ПРОФИТА: TakeProfit должен быть достаточно далеко, чтобы покрыть комиссии
|
|
1518
1556
|
if (GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) {
|
|
1519
1557
|
const tpDistancePercent = ((signal.priceOpen - signal.priceTakeProfit) / signal.priceOpen) * 100;
|
|
@@ -1587,8 +1625,36 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
1587
1625
|
if (!signal) {
|
|
1588
1626
|
return null;
|
|
1589
1627
|
}
|
|
1590
|
-
// Если priceOpen указан -
|
|
1628
|
+
// Если priceOpen указан - проверяем нужно ли ждать активации или открыть сразу
|
|
1591
1629
|
if (signal.priceOpen !== undefined) {
|
|
1630
|
+
// КРИТИЧЕСКАЯ ПРОВЕРКА: достигнут ли priceOpen?
|
|
1631
|
+
// LONG: если currentPrice <= priceOpen - цена уже упала достаточно, открываем сразу
|
|
1632
|
+
// SHORT: если currentPrice >= priceOpen - цена уже выросла достаточно, открываем сразу
|
|
1633
|
+
const shouldActivateImmediately = (signal.position === "long" && currentPrice <= signal.priceOpen) ||
|
|
1634
|
+
(signal.position === "short" && currentPrice >= signal.priceOpen);
|
|
1635
|
+
if (shouldActivateImmediately) {
|
|
1636
|
+
// НЕМЕДЛЕННАЯ АКТИВАЦИЯ: priceOpen уже достигнут
|
|
1637
|
+
// Создаем активный сигнал напрямую (БЕЗ scheduled фазы)
|
|
1638
|
+
const signalRow = {
|
|
1639
|
+
id: functoolsKit.randomString(),
|
|
1640
|
+
priceOpen: signal.priceOpen, // Используем priceOpen из сигнала
|
|
1641
|
+
position: signal.position,
|
|
1642
|
+
note: signal.note,
|
|
1643
|
+
priceTakeProfit: signal.priceTakeProfit,
|
|
1644
|
+
priceStopLoss: signal.priceStopLoss,
|
|
1645
|
+
minuteEstimatedTime: signal.minuteEstimatedTime,
|
|
1646
|
+
symbol: self.params.execution.context.symbol,
|
|
1647
|
+
exchangeName: self.params.method.context.exchangeName,
|
|
1648
|
+
strategyName: self.params.method.context.strategyName,
|
|
1649
|
+
scheduledAt: currentTime,
|
|
1650
|
+
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
1651
|
+
_isScheduled: false,
|
|
1652
|
+
};
|
|
1653
|
+
// Валидируем сигнал перед возвратом
|
|
1654
|
+
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
1655
|
+
return signalRow;
|
|
1656
|
+
}
|
|
1657
|
+
// ОЖИДАНИЕ АКТИВАЦИИ: создаем scheduled signal (risk check при активации)
|
|
1592
1658
|
const scheduledSignalRow = {
|
|
1593
1659
|
id: functoolsKit.randomString(),
|
|
1594
1660
|
priceOpen: signal.priceOpen,
|
|
@@ -1605,7 +1671,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
1605
1671
|
_isScheduled: true,
|
|
1606
1672
|
};
|
|
1607
1673
|
// Валидируем сигнал перед возвратом
|
|
1608
|
-
VALIDATE_SIGNAL_FN(scheduledSignalRow);
|
|
1674
|
+
VALIDATE_SIGNAL_FN(scheduledSignalRow, currentPrice, true);
|
|
1609
1675
|
return scheduledSignalRow;
|
|
1610
1676
|
}
|
|
1611
1677
|
const signalRow = {
|
|
@@ -1620,7 +1686,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
|
|
|
1620
1686
|
_isScheduled: false,
|
|
1621
1687
|
};
|
|
1622
1688
|
// Валидируем сигнал перед возвратом
|
|
1623
|
-
VALIDATE_SIGNAL_FN(signalRow);
|
|
1689
|
+
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
1624
1690
|
return signalRow;
|
|
1625
1691
|
}, {
|
|
1626
1692
|
defaultValue: null,
|
|
@@ -4795,14 +4861,14 @@ class WalkerLogicPublicService {
|
|
|
4795
4861
|
}
|
|
4796
4862
|
}
|
|
4797
4863
|
|
|
4798
|
-
const METHOD_NAME_RUN$2 = "
|
|
4864
|
+
const METHOD_NAME_RUN$2 = "liveCommandService run";
|
|
4799
4865
|
/**
|
|
4800
4866
|
* Global service providing access to live trading functionality.
|
|
4801
4867
|
*
|
|
4802
4868
|
* Simple wrapper around LiveLogicPublicService for dependency injection.
|
|
4803
4869
|
* Used by public API exports.
|
|
4804
4870
|
*/
|
|
4805
|
-
class
|
|
4871
|
+
class LiveCommandService {
|
|
4806
4872
|
constructor() {
|
|
4807
4873
|
this.loggerService = inject(TYPES.loggerService);
|
|
4808
4874
|
this.liveLogicPublicService = inject(TYPES.liveLogicPublicService);
|
|
@@ -4839,14 +4905,14 @@ class LiveGlobalService {
|
|
|
4839
4905
|
}
|
|
4840
4906
|
}
|
|
4841
4907
|
|
|
4842
|
-
const METHOD_NAME_RUN$1 = "
|
|
4908
|
+
const METHOD_NAME_RUN$1 = "backtestCommandService run";
|
|
4843
4909
|
/**
|
|
4844
4910
|
* Global service providing access to backtest functionality.
|
|
4845
4911
|
*
|
|
4846
4912
|
* Simple wrapper around BacktestLogicPublicService for dependency injection.
|
|
4847
4913
|
* Used by public API exports.
|
|
4848
4914
|
*/
|
|
4849
|
-
class
|
|
4915
|
+
class BacktestCommandService {
|
|
4850
4916
|
constructor() {
|
|
4851
4917
|
this.loggerService = inject(TYPES.loggerService);
|
|
4852
4918
|
this.strategySchemaService = inject(TYPES.strategySchemaService);
|
|
@@ -4882,14 +4948,14 @@ class BacktestGlobalService {
|
|
|
4882
4948
|
}
|
|
4883
4949
|
}
|
|
4884
4950
|
|
|
4885
|
-
const METHOD_NAME_RUN = "
|
|
4951
|
+
const METHOD_NAME_RUN = "walkerCommandService run";
|
|
4886
4952
|
/**
|
|
4887
4953
|
* Global service providing access to walker functionality.
|
|
4888
4954
|
*
|
|
4889
4955
|
* Simple wrapper around WalkerLogicPublicService for dependency injection.
|
|
4890
4956
|
* Used by public API exports.
|
|
4891
4957
|
*/
|
|
4892
|
-
class
|
|
4958
|
+
class WalkerCommandService {
|
|
4893
4959
|
constructor() {
|
|
4894
4960
|
this.loggerService = inject(TYPES.loggerService);
|
|
4895
4961
|
this.walkerLogicPublicService = inject(TYPES.walkerLogicPublicService);
|
|
@@ -7764,12 +7830,14 @@ class RiskValidationService {
|
|
|
7764
7830
|
provide(TYPES.exchangeGlobalService, () => new ExchangeGlobalService());
|
|
7765
7831
|
provide(TYPES.strategyGlobalService, () => new StrategyGlobalService());
|
|
7766
7832
|
provide(TYPES.frameGlobalService, () => new FrameGlobalService());
|
|
7767
|
-
provide(TYPES.liveGlobalService, () => new LiveGlobalService());
|
|
7768
|
-
provide(TYPES.backtestGlobalService, () => new BacktestGlobalService());
|
|
7769
|
-
provide(TYPES.walkerGlobalService, () => new WalkerGlobalService());
|
|
7770
7833
|
provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
|
|
7771
7834
|
provide(TYPES.riskGlobalService, () => new RiskGlobalService());
|
|
7772
7835
|
}
|
|
7836
|
+
{
|
|
7837
|
+
provide(TYPES.liveCommandService, () => new LiveCommandService());
|
|
7838
|
+
provide(TYPES.backtestCommandService, () => new BacktestCommandService());
|
|
7839
|
+
provide(TYPES.walkerCommandService, () => new WalkerCommandService());
|
|
7840
|
+
}
|
|
7773
7841
|
{
|
|
7774
7842
|
provide(TYPES.backtestLogicPrivateService, () => new BacktestLogicPrivateService());
|
|
7775
7843
|
provide(TYPES.liveLogicPrivateService, () => new LiveLogicPrivateService());
|
|
@@ -7823,12 +7891,14 @@ const globalServices = {
|
|
|
7823
7891
|
exchangeGlobalService: inject(TYPES.exchangeGlobalService),
|
|
7824
7892
|
strategyGlobalService: inject(TYPES.strategyGlobalService),
|
|
7825
7893
|
frameGlobalService: inject(TYPES.frameGlobalService),
|
|
7826
|
-
liveGlobalService: inject(TYPES.liveGlobalService),
|
|
7827
|
-
backtestGlobalService: inject(TYPES.backtestGlobalService),
|
|
7828
|
-
walkerGlobalService: inject(TYPES.walkerGlobalService),
|
|
7829
7894
|
sizingGlobalService: inject(TYPES.sizingGlobalService),
|
|
7830
7895
|
riskGlobalService: inject(TYPES.riskGlobalService),
|
|
7831
7896
|
};
|
|
7897
|
+
const commandServices = {
|
|
7898
|
+
liveCommandService: inject(TYPES.liveCommandService),
|
|
7899
|
+
backtestCommandService: inject(TYPES.backtestCommandService),
|
|
7900
|
+
walkerCommandService: inject(TYPES.walkerCommandService),
|
|
7901
|
+
};
|
|
7832
7902
|
const logicPrivateServices = {
|
|
7833
7903
|
backtestLogicPrivateService: inject(TYPES.backtestLogicPrivateService),
|
|
7834
7904
|
liveLogicPrivateService: inject(TYPES.liveLogicPrivateService),
|
|
@@ -7861,6 +7931,7 @@ const backtest = {
|
|
|
7861
7931
|
...connectionServices,
|
|
7862
7932
|
...schemaServices,
|
|
7863
7933
|
...globalServices,
|
|
7934
|
+
...commandServices,
|
|
7864
7935
|
...logicPrivateServices,
|
|
7865
7936
|
...logicPublicServices,
|
|
7866
7937
|
...markdownServices,
|
|
@@ -9155,7 +9226,7 @@ const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
|
|
|
9155
9226
|
/**
|
|
9156
9227
|
* Utility class for backtest operations.
|
|
9157
9228
|
*
|
|
9158
|
-
* Provides simplified access to
|
|
9229
|
+
* Provides simplified access to backtestCommandService.run() with logging.
|
|
9159
9230
|
* Exported as singleton instance for convenient usage.
|
|
9160
9231
|
*
|
|
9161
9232
|
* @example
|
|
@@ -9196,7 +9267,7 @@ class BacktestUtils {
|
|
|
9196
9267
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
9197
9268
|
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
9198
9269
|
}
|
|
9199
|
-
return backtest$1.
|
|
9270
|
+
return backtest$1.backtestCommandService.run(symbol, context);
|
|
9200
9271
|
};
|
|
9201
9272
|
/**
|
|
9202
9273
|
* Runs backtest in background without yielding results.
|
|
@@ -9331,7 +9402,7 @@ const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
|
|
|
9331
9402
|
/**
|
|
9332
9403
|
* Utility class for live trading operations.
|
|
9333
9404
|
*
|
|
9334
|
-
* Provides simplified access to
|
|
9405
|
+
* Provides simplified access to liveCommandService.run() with logging.
|
|
9335
9406
|
* Exported as singleton instance for convenient usage.
|
|
9336
9407
|
*
|
|
9337
9408
|
* Features:
|
|
@@ -9385,7 +9456,7 @@ class LiveUtils {
|
|
|
9385
9456
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
9386
9457
|
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
9387
9458
|
}
|
|
9388
|
-
return backtest$1.
|
|
9459
|
+
return backtest$1.liveCommandService.run(symbol, context);
|
|
9389
9460
|
};
|
|
9390
9461
|
/**
|
|
9391
9462
|
* Runs live trading in background without yielding results.
|
|
@@ -9771,7 +9842,7 @@ const WALKER_METHOD_NAME_DUMP = "WalkerUtils.dump";
|
|
|
9771
9842
|
/**
|
|
9772
9843
|
* Utility class for walker operations.
|
|
9773
9844
|
*
|
|
9774
|
-
* Provides simplified access to
|
|
9845
|
+
* Provides simplified access to walkerCommandService.run() with logging.
|
|
9775
9846
|
* Automatically pulls exchangeName and frameName from walker schema.
|
|
9776
9847
|
* Exported as singleton instance for convenient usage.
|
|
9777
9848
|
*
|
|
@@ -9823,7 +9894,7 @@ class WalkerUtils {
|
|
|
9823
9894
|
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
9824
9895
|
}
|
|
9825
9896
|
}
|
|
9826
|
-
return backtest$1.
|
|
9897
|
+
return backtest$1.walkerCommandService.run(symbol, {
|
|
9827
9898
|
walkerName: context.walkerName,
|
|
9828
9899
|
exchangeName: walkerSchema.exchangeName,
|
|
9829
9900
|
frameName: walkerSchema.frameName,
|
package/build/index.mjs
CHANGED
|
@@ -129,12 +129,14 @@ const globalServices$1 = {
|
|
|
129
129
|
exchangeGlobalService: Symbol('exchangeGlobalService'),
|
|
130
130
|
strategyGlobalService: Symbol('strategyGlobalService'),
|
|
131
131
|
frameGlobalService: Symbol('frameGlobalService'),
|
|
132
|
-
liveGlobalService: Symbol('liveGlobalService'),
|
|
133
|
-
backtestGlobalService: Symbol('backtestGlobalService'),
|
|
134
|
-
walkerGlobalService: Symbol('walkerGlobalService'),
|
|
135
132
|
sizingGlobalService: Symbol('sizingGlobalService'),
|
|
136
133
|
riskGlobalService: Symbol('riskGlobalService'),
|
|
137
134
|
};
|
|
135
|
+
const commandServices$1 = {
|
|
136
|
+
liveCommandService: Symbol('liveCommandService'),
|
|
137
|
+
backtestCommandService: Symbol('backtestCommandService'),
|
|
138
|
+
walkerCommandService: Symbol('walkerCommandService'),
|
|
139
|
+
};
|
|
138
140
|
const logicPrivateServices$1 = {
|
|
139
141
|
backtestLogicPrivateService: Symbol('backtestLogicPrivateService'),
|
|
140
142
|
liveLogicPrivateService: Symbol('liveLogicPrivateService'),
|
|
@@ -167,6 +169,7 @@ const TYPES = {
|
|
|
167
169
|
...connectionServices$1,
|
|
168
170
|
...schemaServices$1,
|
|
169
171
|
...globalServices$1,
|
|
172
|
+
...commandServices$1,
|
|
170
173
|
...logicPrivateServices$1,
|
|
171
174
|
...logicPublicServices$1,
|
|
172
175
|
...markdownServices$1,
|
|
@@ -1455,8 +1458,15 @@ const INTERVAL_MINUTES$1 = {
|
|
|
1455
1458
|
"30m": 30,
|
|
1456
1459
|
"1h": 60,
|
|
1457
1460
|
};
|
|
1458
|
-
const VALIDATE_SIGNAL_FN = (signal) => {
|
|
1461
|
+
const VALIDATE_SIGNAL_FN = (signal, currentPrice, isScheduled) => {
|
|
1459
1462
|
const errors = [];
|
|
1463
|
+
// ЗАЩИТА ОТ NaN/Infinity: currentPrice должна быть конечным числом
|
|
1464
|
+
if (!isFinite(currentPrice)) {
|
|
1465
|
+
errors.push(`currentPrice must be a finite number, got ${currentPrice} (${typeof currentPrice})`);
|
|
1466
|
+
}
|
|
1467
|
+
if (isFinite(currentPrice) && currentPrice <= 0) {
|
|
1468
|
+
errors.push(`currentPrice must be positive, got ${currentPrice}`);
|
|
1469
|
+
}
|
|
1460
1470
|
// ЗАЩИТА ОТ NaN/Infinity: все цены должны быть конечными числами
|
|
1461
1471
|
if (!isFinite(signal.priceOpen)) {
|
|
1462
1472
|
errors.push(`priceOpen must be a finite number, got ${signal.priceOpen} (${typeof signal.priceOpen})`);
|
|
@@ -1485,6 +1495,20 @@ const VALIDATE_SIGNAL_FN = (signal) => {
|
|
|
1485
1495
|
if (signal.priceStopLoss >= signal.priceOpen) {
|
|
1486
1496
|
errors.push(`Long: priceStopLoss (${signal.priceStopLoss}) must be < priceOpen (${signal.priceOpen})`);
|
|
1487
1497
|
}
|
|
1498
|
+
// ЗАЩИТА ОТ EDGE CASE: для immediate сигналов проверяем что текущая цена не пробила SL/TP
|
|
1499
|
+
// Для scheduled сигналов эта проверка избыточна т.к. priceOpen уже проверен выше
|
|
1500
|
+
if (!isScheduled) {
|
|
1501
|
+
// Текущая цена уже пробила StopLoss - позиция откроется и сразу закроется по SL
|
|
1502
|
+
if (isFinite(currentPrice) && currentPrice < signal.priceStopLoss) {
|
|
1503
|
+
errors.push(`Long: currentPrice (${currentPrice}) < priceStopLoss (${signal.priceStopLoss}). ` +
|
|
1504
|
+
`Signal would be immediately cancelled. This signal is invalid.`);
|
|
1505
|
+
}
|
|
1506
|
+
// Текущая цена уже достигла TakeProfit - профит упущен
|
|
1507
|
+
if (isFinite(currentPrice) && currentPrice > signal.priceTakeProfit) {
|
|
1508
|
+
errors.push(`Long: currentPrice (${currentPrice}) > priceTakeProfit (${signal.priceTakeProfit}). ` +
|
|
1509
|
+
`Signal is invalid - the profit opportunity has already passed.`);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1488
1512
|
// ЗАЩИТА ОТ МИКРО-ПРОФИТА: TakeProfit должен быть достаточно далеко, чтобы покрыть комиссии
|
|
1489
1513
|
if (GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) {
|
|
1490
1514
|
const tpDistancePercent = ((signal.priceTakeProfit - signal.priceOpen) / signal.priceOpen) * 100;
|
|
@@ -1512,6 +1536,20 @@ const VALIDATE_SIGNAL_FN = (signal) => {
|
|
|
1512
1536
|
if (signal.priceStopLoss <= signal.priceOpen) {
|
|
1513
1537
|
errors.push(`Short: priceStopLoss (${signal.priceStopLoss}) must be > priceOpen (${signal.priceOpen})`);
|
|
1514
1538
|
}
|
|
1539
|
+
// ЗАЩИТА ОТ EDGE CASE: для immediate сигналов проверяем что текущая цена не пробила SL/TP
|
|
1540
|
+
// Для scheduled сигналов эта проверка избыточна т.к. priceOpen уже проверен выше
|
|
1541
|
+
if (!isScheduled) {
|
|
1542
|
+
// Текущая цена уже пробила StopLoss - позиция откроется и сразу закроется по SL
|
|
1543
|
+
if (isFinite(currentPrice) && currentPrice > signal.priceStopLoss) {
|
|
1544
|
+
errors.push(`Short: currentPrice (${currentPrice}) > priceStopLoss (${signal.priceStopLoss}). ` +
|
|
1545
|
+
`Signal would be immediately cancelled. This signal is invalid.`);
|
|
1546
|
+
}
|
|
1547
|
+
// Текущая цена уже достигла TakeProfit - профит упущен
|
|
1548
|
+
if (isFinite(currentPrice) && currentPrice < signal.priceTakeProfit) {
|
|
1549
|
+
errors.push(`Short: currentPrice (${currentPrice}) < priceTakeProfit (${signal.priceTakeProfit}). ` +
|
|
1550
|
+
`Signal is invalid - the profit opportunity has already passed.`);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1515
1553
|
// ЗАЩИТА ОТ МИКРО-ПРОФИТА: TakeProfit должен быть достаточно далеко, чтобы покрыть комиссии
|
|
1516
1554
|
if (GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) {
|
|
1517
1555
|
const tpDistancePercent = ((signal.priceOpen - signal.priceTakeProfit) / signal.priceOpen) * 100;
|
|
@@ -1585,8 +1623,36 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1585
1623
|
if (!signal) {
|
|
1586
1624
|
return null;
|
|
1587
1625
|
}
|
|
1588
|
-
// Если priceOpen указан -
|
|
1626
|
+
// Если priceOpen указан - проверяем нужно ли ждать активации или открыть сразу
|
|
1589
1627
|
if (signal.priceOpen !== undefined) {
|
|
1628
|
+
// КРИТИЧЕСКАЯ ПРОВЕРКА: достигнут ли priceOpen?
|
|
1629
|
+
// LONG: если currentPrice <= priceOpen - цена уже упала достаточно, открываем сразу
|
|
1630
|
+
// SHORT: если currentPrice >= priceOpen - цена уже выросла достаточно, открываем сразу
|
|
1631
|
+
const shouldActivateImmediately = (signal.position === "long" && currentPrice <= signal.priceOpen) ||
|
|
1632
|
+
(signal.position === "short" && currentPrice >= signal.priceOpen);
|
|
1633
|
+
if (shouldActivateImmediately) {
|
|
1634
|
+
// НЕМЕДЛЕННАЯ АКТИВАЦИЯ: priceOpen уже достигнут
|
|
1635
|
+
// Создаем активный сигнал напрямую (БЕЗ scheduled фазы)
|
|
1636
|
+
const signalRow = {
|
|
1637
|
+
id: randomString(),
|
|
1638
|
+
priceOpen: signal.priceOpen, // Используем priceOpen из сигнала
|
|
1639
|
+
position: signal.position,
|
|
1640
|
+
note: signal.note,
|
|
1641
|
+
priceTakeProfit: signal.priceTakeProfit,
|
|
1642
|
+
priceStopLoss: signal.priceStopLoss,
|
|
1643
|
+
minuteEstimatedTime: signal.minuteEstimatedTime,
|
|
1644
|
+
symbol: self.params.execution.context.symbol,
|
|
1645
|
+
exchangeName: self.params.method.context.exchangeName,
|
|
1646
|
+
strategyName: self.params.method.context.strategyName,
|
|
1647
|
+
scheduledAt: currentTime,
|
|
1648
|
+
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
1649
|
+
_isScheduled: false,
|
|
1650
|
+
};
|
|
1651
|
+
// Валидируем сигнал перед возвратом
|
|
1652
|
+
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
1653
|
+
return signalRow;
|
|
1654
|
+
}
|
|
1655
|
+
// ОЖИДАНИЕ АКТИВАЦИИ: создаем scheduled signal (risk check при активации)
|
|
1590
1656
|
const scheduledSignalRow = {
|
|
1591
1657
|
id: randomString(),
|
|
1592
1658
|
priceOpen: signal.priceOpen,
|
|
@@ -1603,7 +1669,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1603
1669
|
_isScheduled: true,
|
|
1604
1670
|
};
|
|
1605
1671
|
// Валидируем сигнал перед возвратом
|
|
1606
|
-
VALIDATE_SIGNAL_FN(scheduledSignalRow);
|
|
1672
|
+
VALIDATE_SIGNAL_FN(scheduledSignalRow, currentPrice, true);
|
|
1607
1673
|
return scheduledSignalRow;
|
|
1608
1674
|
}
|
|
1609
1675
|
const signalRow = {
|
|
@@ -1618,7 +1684,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1618
1684
|
_isScheduled: false,
|
|
1619
1685
|
};
|
|
1620
1686
|
// Валидируем сигнал перед возвратом
|
|
1621
|
-
VALIDATE_SIGNAL_FN(signalRow);
|
|
1687
|
+
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
1622
1688
|
return signalRow;
|
|
1623
1689
|
}, {
|
|
1624
1690
|
defaultValue: null,
|
|
@@ -4793,14 +4859,14 @@ class WalkerLogicPublicService {
|
|
|
4793
4859
|
}
|
|
4794
4860
|
}
|
|
4795
4861
|
|
|
4796
|
-
const METHOD_NAME_RUN$2 = "
|
|
4862
|
+
const METHOD_NAME_RUN$2 = "liveCommandService run";
|
|
4797
4863
|
/**
|
|
4798
4864
|
* Global service providing access to live trading functionality.
|
|
4799
4865
|
*
|
|
4800
4866
|
* Simple wrapper around LiveLogicPublicService for dependency injection.
|
|
4801
4867
|
* Used by public API exports.
|
|
4802
4868
|
*/
|
|
4803
|
-
class
|
|
4869
|
+
class LiveCommandService {
|
|
4804
4870
|
constructor() {
|
|
4805
4871
|
this.loggerService = inject(TYPES.loggerService);
|
|
4806
4872
|
this.liveLogicPublicService = inject(TYPES.liveLogicPublicService);
|
|
@@ -4837,14 +4903,14 @@ class LiveGlobalService {
|
|
|
4837
4903
|
}
|
|
4838
4904
|
}
|
|
4839
4905
|
|
|
4840
|
-
const METHOD_NAME_RUN$1 = "
|
|
4906
|
+
const METHOD_NAME_RUN$1 = "backtestCommandService run";
|
|
4841
4907
|
/**
|
|
4842
4908
|
* Global service providing access to backtest functionality.
|
|
4843
4909
|
*
|
|
4844
4910
|
* Simple wrapper around BacktestLogicPublicService for dependency injection.
|
|
4845
4911
|
* Used by public API exports.
|
|
4846
4912
|
*/
|
|
4847
|
-
class
|
|
4913
|
+
class BacktestCommandService {
|
|
4848
4914
|
constructor() {
|
|
4849
4915
|
this.loggerService = inject(TYPES.loggerService);
|
|
4850
4916
|
this.strategySchemaService = inject(TYPES.strategySchemaService);
|
|
@@ -4880,14 +4946,14 @@ class BacktestGlobalService {
|
|
|
4880
4946
|
}
|
|
4881
4947
|
}
|
|
4882
4948
|
|
|
4883
|
-
const METHOD_NAME_RUN = "
|
|
4949
|
+
const METHOD_NAME_RUN = "walkerCommandService run";
|
|
4884
4950
|
/**
|
|
4885
4951
|
* Global service providing access to walker functionality.
|
|
4886
4952
|
*
|
|
4887
4953
|
* Simple wrapper around WalkerLogicPublicService for dependency injection.
|
|
4888
4954
|
* Used by public API exports.
|
|
4889
4955
|
*/
|
|
4890
|
-
class
|
|
4956
|
+
class WalkerCommandService {
|
|
4891
4957
|
constructor() {
|
|
4892
4958
|
this.loggerService = inject(TYPES.loggerService);
|
|
4893
4959
|
this.walkerLogicPublicService = inject(TYPES.walkerLogicPublicService);
|
|
@@ -7762,12 +7828,14 @@ class RiskValidationService {
|
|
|
7762
7828
|
provide(TYPES.exchangeGlobalService, () => new ExchangeGlobalService());
|
|
7763
7829
|
provide(TYPES.strategyGlobalService, () => new StrategyGlobalService());
|
|
7764
7830
|
provide(TYPES.frameGlobalService, () => new FrameGlobalService());
|
|
7765
|
-
provide(TYPES.liveGlobalService, () => new LiveGlobalService());
|
|
7766
|
-
provide(TYPES.backtestGlobalService, () => new BacktestGlobalService());
|
|
7767
|
-
provide(TYPES.walkerGlobalService, () => new WalkerGlobalService());
|
|
7768
7831
|
provide(TYPES.sizingGlobalService, () => new SizingGlobalService());
|
|
7769
7832
|
provide(TYPES.riskGlobalService, () => new RiskGlobalService());
|
|
7770
7833
|
}
|
|
7834
|
+
{
|
|
7835
|
+
provide(TYPES.liveCommandService, () => new LiveCommandService());
|
|
7836
|
+
provide(TYPES.backtestCommandService, () => new BacktestCommandService());
|
|
7837
|
+
provide(TYPES.walkerCommandService, () => new WalkerCommandService());
|
|
7838
|
+
}
|
|
7771
7839
|
{
|
|
7772
7840
|
provide(TYPES.backtestLogicPrivateService, () => new BacktestLogicPrivateService());
|
|
7773
7841
|
provide(TYPES.liveLogicPrivateService, () => new LiveLogicPrivateService());
|
|
@@ -7821,12 +7889,14 @@ const globalServices = {
|
|
|
7821
7889
|
exchangeGlobalService: inject(TYPES.exchangeGlobalService),
|
|
7822
7890
|
strategyGlobalService: inject(TYPES.strategyGlobalService),
|
|
7823
7891
|
frameGlobalService: inject(TYPES.frameGlobalService),
|
|
7824
|
-
liveGlobalService: inject(TYPES.liveGlobalService),
|
|
7825
|
-
backtestGlobalService: inject(TYPES.backtestGlobalService),
|
|
7826
|
-
walkerGlobalService: inject(TYPES.walkerGlobalService),
|
|
7827
7892
|
sizingGlobalService: inject(TYPES.sizingGlobalService),
|
|
7828
7893
|
riskGlobalService: inject(TYPES.riskGlobalService),
|
|
7829
7894
|
};
|
|
7895
|
+
const commandServices = {
|
|
7896
|
+
liveCommandService: inject(TYPES.liveCommandService),
|
|
7897
|
+
backtestCommandService: inject(TYPES.backtestCommandService),
|
|
7898
|
+
walkerCommandService: inject(TYPES.walkerCommandService),
|
|
7899
|
+
};
|
|
7830
7900
|
const logicPrivateServices = {
|
|
7831
7901
|
backtestLogicPrivateService: inject(TYPES.backtestLogicPrivateService),
|
|
7832
7902
|
liveLogicPrivateService: inject(TYPES.liveLogicPrivateService),
|
|
@@ -7859,6 +7929,7 @@ const backtest = {
|
|
|
7859
7929
|
...connectionServices,
|
|
7860
7930
|
...schemaServices,
|
|
7861
7931
|
...globalServices,
|
|
7932
|
+
...commandServices,
|
|
7862
7933
|
...logicPrivateServices,
|
|
7863
7934
|
...logicPublicServices,
|
|
7864
7935
|
...markdownServices,
|
|
@@ -9153,7 +9224,7 @@ const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
|
|
|
9153
9224
|
/**
|
|
9154
9225
|
* Utility class for backtest operations.
|
|
9155
9226
|
*
|
|
9156
|
-
* Provides simplified access to
|
|
9227
|
+
* Provides simplified access to backtestCommandService.run() with logging.
|
|
9157
9228
|
* Exported as singleton instance for convenient usage.
|
|
9158
9229
|
*
|
|
9159
9230
|
* @example
|
|
@@ -9194,7 +9265,7 @@ class BacktestUtils {
|
|
|
9194
9265
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
9195
9266
|
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
9196
9267
|
}
|
|
9197
|
-
return backtest$1.
|
|
9268
|
+
return backtest$1.backtestCommandService.run(symbol, context);
|
|
9198
9269
|
};
|
|
9199
9270
|
/**
|
|
9200
9271
|
* Runs backtest in background without yielding results.
|
|
@@ -9329,7 +9400,7 @@ const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
|
|
|
9329
9400
|
/**
|
|
9330
9401
|
* Utility class for live trading operations.
|
|
9331
9402
|
*
|
|
9332
|
-
* Provides simplified access to
|
|
9403
|
+
* Provides simplified access to liveCommandService.run() with logging.
|
|
9333
9404
|
* Exported as singleton instance for convenient usage.
|
|
9334
9405
|
*
|
|
9335
9406
|
* Features:
|
|
@@ -9383,7 +9454,7 @@ class LiveUtils {
|
|
|
9383
9454
|
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
9384
9455
|
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
9385
9456
|
}
|
|
9386
|
-
return backtest$1.
|
|
9457
|
+
return backtest$1.liveCommandService.run(symbol, context);
|
|
9387
9458
|
};
|
|
9388
9459
|
/**
|
|
9389
9460
|
* Runs live trading in background without yielding results.
|
|
@@ -9769,7 +9840,7 @@ const WALKER_METHOD_NAME_DUMP = "WalkerUtils.dump";
|
|
|
9769
9840
|
/**
|
|
9770
9841
|
* Utility class for walker operations.
|
|
9771
9842
|
*
|
|
9772
|
-
* Provides simplified access to
|
|
9843
|
+
* Provides simplified access to walkerCommandService.run() with logging.
|
|
9773
9844
|
* Automatically pulls exchangeName and frameName from walker schema.
|
|
9774
9845
|
* Exported as singleton instance for convenient usage.
|
|
9775
9846
|
*
|
|
@@ -9821,7 +9892,7 @@ class WalkerUtils {
|
|
|
9821
9892
|
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
9822
9893
|
}
|
|
9823
9894
|
}
|
|
9824
|
-
return backtest$1.
|
|
9895
|
+
return backtest$1.walkerCommandService.run(symbol, {
|
|
9825
9896
|
walkerName: context.walkerName,
|
|
9826
9897
|
exchangeName: walkerSchema.exchangeName,
|
|
9827
9898
|
frameName: walkerSchema.frameName,
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -3595,7 +3595,7 @@ declare const PersistRiskAdapter: PersistRiskUtils;
|
|
|
3595
3595
|
/**
|
|
3596
3596
|
* Utility class for backtest operations.
|
|
3597
3597
|
*
|
|
3598
|
-
* Provides simplified access to
|
|
3598
|
+
* Provides simplified access to backtestCommandService.run() with logging.
|
|
3599
3599
|
* Exported as singleton instance for convenient usage.
|
|
3600
3600
|
*
|
|
3601
3601
|
* @example
|
|
@@ -3716,7 +3716,7 @@ declare const Backtest: BacktestUtils;
|
|
|
3716
3716
|
/**
|
|
3717
3717
|
* Utility class for live trading operations.
|
|
3718
3718
|
*
|
|
3719
|
-
* Provides simplified access to
|
|
3719
|
+
* Provides simplified access to liveCommandService.run() with logging.
|
|
3720
3720
|
* Exported as singleton instance for convenient usage.
|
|
3721
3721
|
*
|
|
3722
3722
|
* Features:
|
|
@@ -4063,7 +4063,7 @@ declare class Performance {
|
|
|
4063
4063
|
/**
|
|
4064
4064
|
* Utility class for walker operations.
|
|
4065
4065
|
*
|
|
4066
|
-
* Provides simplified access to
|
|
4066
|
+
* Provides simplified access to walkerCommandService.run() with logging.
|
|
4067
4067
|
* Automatically pulls exchangeName and frameName from walker schema.
|
|
4068
4068
|
* Exported as singleton instance for convenient usage.
|
|
4069
4069
|
*
|
|
@@ -5315,7 +5315,7 @@ declare class RiskGlobalService {
|
|
|
5315
5315
|
* Simple wrapper around WalkerLogicPublicService for dependency injection.
|
|
5316
5316
|
* Used by public API exports.
|
|
5317
5317
|
*/
|
|
5318
|
-
declare class
|
|
5318
|
+
declare class WalkerCommandService {
|
|
5319
5319
|
private readonly loggerService;
|
|
5320
5320
|
private readonly walkerLogicPublicService;
|
|
5321
5321
|
private readonly walkerSchemaService;
|
|
@@ -5898,7 +5898,7 @@ declare class WalkerLogicPublicService {
|
|
|
5898
5898
|
* Simple wrapper around LiveLogicPublicService for dependency injection.
|
|
5899
5899
|
* Used by public API exports.
|
|
5900
5900
|
*/
|
|
5901
|
-
declare class
|
|
5901
|
+
declare class LiveCommandService {
|
|
5902
5902
|
private readonly loggerService;
|
|
5903
5903
|
private readonly liveLogicPublicService;
|
|
5904
5904
|
private readonly strategyValidationService;
|
|
@@ -5926,7 +5926,7 @@ declare class LiveGlobalService {
|
|
|
5926
5926
|
* Simple wrapper around BacktestLogicPublicService for dependency injection.
|
|
5927
5927
|
* Used by public API exports.
|
|
5928
5928
|
*/
|
|
5929
|
-
declare class
|
|
5929
|
+
declare class BacktestCommandService {
|
|
5930
5930
|
private readonly loggerService;
|
|
5931
5931
|
private readonly strategySchemaService;
|
|
5932
5932
|
private readonly riskValidationService;
|
|
@@ -6338,12 +6338,12 @@ declare const backtest: {
|
|
|
6338
6338
|
backtestLogicPrivateService: BacktestLogicPrivateService;
|
|
6339
6339
|
liveLogicPrivateService: LiveLogicPrivateService;
|
|
6340
6340
|
walkerLogicPrivateService: WalkerLogicPrivateService;
|
|
6341
|
+
liveCommandService: LiveCommandService;
|
|
6342
|
+
backtestCommandService: BacktestCommandService;
|
|
6343
|
+
walkerCommandService: WalkerCommandService;
|
|
6341
6344
|
exchangeGlobalService: ExchangeGlobalService;
|
|
6342
6345
|
strategyGlobalService: StrategyGlobalService;
|
|
6343
6346
|
frameGlobalService: FrameGlobalService;
|
|
6344
|
-
liveGlobalService: LiveGlobalService;
|
|
6345
|
-
backtestGlobalService: BacktestGlobalService;
|
|
6346
|
-
walkerGlobalService: WalkerGlobalService;
|
|
6347
6347
|
sizingGlobalService: SizingGlobalService;
|
|
6348
6348
|
riskGlobalService: RiskGlobalService;
|
|
6349
6349
|
exchangeSchemaService: ExchangeSchemaService;
|