backtest-kit 15.1.0 → 15.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/README.md +34 -1
- package/build/index.cjs +387 -68
- package/build/index.mjs +387 -68
- package/package.json +2 -2
- package/types.d.ts +202 -14
package/build/index.cjs
CHANGED
|
@@ -553,6 +553,16 @@ const GLOBAL_CONFIG = {
|
|
|
553
553
|
* Default: 500 notifications
|
|
554
554
|
*/
|
|
555
555
|
CC_MAX_NOTIFICATIONS: 500,
|
|
556
|
+
/**
|
|
557
|
+
* Minimum interval between `order_sync.check` notifications for a single signalId.
|
|
558
|
+
* Order-ping events (syncPendingSubject) fire on every live tick, so the
|
|
559
|
+
* NotificationAdapter throttles them: a signal produces at most one check
|
|
560
|
+
* notification per this interval. The throttle entry is removed when the
|
|
561
|
+
* signal is closed or cancelled.
|
|
562
|
+
*
|
|
563
|
+
* Default: 900000 ms (15 minutes)
|
|
564
|
+
*/
|
|
565
|
+
CC_NOTIFICATION_ORDER_CHECK_TTL: 900000,
|
|
556
566
|
/**
|
|
557
567
|
* Maximum number of signals to keep in storage.
|
|
558
568
|
* Older signals are removed when this limit is exceeded.
|
|
@@ -5317,6 +5327,24 @@ const validateCandles = (candles) => {
|
|
|
5317
5327
|
};
|
|
5318
5328
|
|
|
5319
5329
|
const MS_PER_MINUTE$7 = 60000;
|
|
5330
|
+
/**
|
|
5331
|
+
* Normalizes raw adapter output to the {@link IAggregatedTradeData} contract.
|
|
5332
|
+
*
|
|
5333
|
+
* Implicit-API exchange adapters (e.g. ccxt `publicGetAggTrades`) return every
|
|
5334
|
+
* scalar as a string. A string `timestamp` is type-invisible at runtime — it
|
|
5335
|
+
* survives serialization and comparison — and only blows up deep inside a
|
|
5336
|
+
* downstream consumer (`Number.isFinite("1783601403565") === false`). Coercing
|
|
5337
|
+
* the numeric fields here, at the single point where adapter data enters the
|
|
5338
|
+
* framework, guarantees the contract's `number` types hold regardless of
|
|
5339
|
+
* adapter behaviour. `isBuyerMaker` is deliberately left untouched: `Boolean`
|
|
5340
|
+
* coercion of a string `"false"` would yield `true`.
|
|
5341
|
+
*/
|
|
5342
|
+
const NORMALIZE_AGGREGATED_TRADES_FN$1 = (trades) => trades.map((trade) => ({
|
|
5343
|
+
...trade,
|
|
5344
|
+
price: Number(trade.price),
|
|
5345
|
+
qty: Number(trade.qty),
|
|
5346
|
+
timestamp: Number(trade.timestamp),
|
|
5347
|
+
}));
|
|
5320
5348
|
const INTERVAL_MINUTES$9 = {
|
|
5321
5349
|
"1m": 1,
|
|
5322
5350
|
"3m": 3,
|
|
@@ -6012,7 +6040,7 @@ class ClientExchange {
|
|
|
6012
6040
|
if (limit === undefined) {
|
|
6013
6041
|
const to = new Date(alignedTo);
|
|
6014
6042
|
const from = new Date(alignedTo - windowMs);
|
|
6015
|
-
return await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
6043
|
+
return NORMALIZE_AGGREGATED_TRADES_FN$1(await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest));
|
|
6016
6044
|
}
|
|
6017
6045
|
// With limit: paginate backwards until we have enough trades.
|
|
6018
6046
|
// Consecutive empty windows bound the scan: before the symbol's listing
|
|
@@ -6029,7 +6057,7 @@ class ClientExchange {
|
|
|
6029
6057
|
}
|
|
6030
6058
|
const to = new Date(windowEnd);
|
|
6031
6059
|
const from = new Date(windowStart);
|
|
6032
|
-
const chunk = await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
6060
|
+
const chunk = NORMALIZE_AGGREGATED_TRADES_FN$1(await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest));
|
|
6033
6061
|
if (chunk.length === 0) {
|
|
6034
6062
|
emptyWindows += 1;
|
|
6035
6063
|
if (emptyWindows >= MAX_CONSECUTIVE_EMPTY_WINDOWS) {
|
|
@@ -15376,6 +15404,10 @@ class StrategyConnectionService {
|
|
|
15376
15404
|
});
|
|
15377
15405
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
15378
15406
|
await strategy.waitForInit();
|
|
15407
|
+
if (!this.timeMetaService.hasTimestamp(symbol, context, backtest)) {
|
|
15408
|
+
const timestamp = this.executionContextService.context.when.getTime();
|
|
15409
|
+
await this.timeMetaService.next(symbol, timestamp, context, backtest);
|
|
15410
|
+
}
|
|
15379
15411
|
const tick = await strategy.tick(symbol, context.strategyName);
|
|
15380
15412
|
{
|
|
15381
15413
|
await this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
|
|
@@ -42696,6 +42728,24 @@ const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
|
|
|
42696
42728
|
const EXCHANGE_METHOD_NAME_GET_RAW_CANDLES = "ExchangeUtils.getRawCandles";
|
|
42697
42729
|
const EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES = "ExchangeUtils.getAggregatedTrades";
|
|
42698
42730
|
const MS_PER_MINUTE$4 = 60000;
|
|
42731
|
+
/**
|
|
42732
|
+
* Normalizes raw adapter output to the {@link IAggregatedTradeData} contract.
|
|
42733
|
+
*
|
|
42734
|
+
* Implicit-API exchange adapters (e.g. ccxt `publicGetAggTrades`) return every
|
|
42735
|
+
* scalar as a string. A string `timestamp` is type-invisible at runtime — it
|
|
42736
|
+
* survives serialization and comparison — and only blows up deep inside a
|
|
42737
|
+
* downstream consumer (`Number.isFinite("1783601403565") === false`). Coercing
|
|
42738
|
+
* the numeric fields here, at the single point where adapter data enters the
|
|
42739
|
+
* framework, guarantees the contract's `number` types hold regardless of
|
|
42740
|
+
* adapter behaviour. `isBuyerMaker` is deliberately left untouched: `Boolean`
|
|
42741
|
+
* coercion of a string `"false"` would yield `true`.
|
|
42742
|
+
*/
|
|
42743
|
+
const NORMALIZE_AGGREGATED_TRADES_FN = (trades) => trades.map((trade) => ({
|
|
42744
|
+
...trade,
|
|
42745
|
+
price: Number(trade.price),
|
|
42746
|
+
qty: Number(trade.qty),
|
|
42747
|
+
timestamp: Number(trade.timestamp),
|
|
42748
|
+
}));
|
|
42699
42749
|
/**
|
|
42700
42750
|
* Gets current timestamp from execution context if available.
|
|
42701
42751
|
* Returns current Date() if no execution context exists (non-trading GUI).
|
|
@@ -43192,7 +43242,7 @@ class ExchangeInstance {
|
|
|
43192
43242
|
if (limit === undefined) {
|
|
43193
43243
|
const to = new Date(alignedTo);
|
|
43194
43244
|
const from = new Date(alignedTo - windowMs);
|
|
43195
|
-
return await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
43245
|
+
return NORMALIZE_AGGREGATED_TRADES_FN(await this._methods.getAggregatedTrades(symbol, from, to, isBacktest));
|
|
43196
43246
|
}
|
|
43197
43247
|
// With limit: paginate backwards until we have enough trades.
|
|
43198
43248
|
// Consecutive empty windows bound the scan: before the symbol's listing
|
|
@@ -43209,7 +43259,7 @@ class ExchangeInstance {
|
|
|
43209
43259
|
}
|
|
43210
43260
|
const to = new Date(windowEnd);
|
|
43211
43261
|
const from = new Date(windowStart);
|
|
43212
|
-
const chunk = await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
43262
|
+
const chunk = NORMALIZE_AGGREGATED_TRADES_FN(await this._methods.getAggregatedTrades(symbol, from, to, isBacktest));
|
|
43213
43263
|
if (chunk.length === 0) {
|
|
43214
43264
|
emptyWindows += 1;
|
|
43215
43265
|
if (emptyWindows >= MAX_CONSECUTIVE_EMPTY_WINDOWS) {
|
|
@@ -45708,6 +45758,7 @@ class BrokerAdapter {
|
|
|
45708
45758
|
exchangeName: event.exchangeName,
|
|
45709
45759
|
frameName: event.frameName,
|
|
45710
45760
|
},
|
|
45761
|
+
when: new Date(event.timestamp),
|
|
45711
45762
|
backtest: event.backtest,
|
|
45712
45763
|
});
|
|
45713
45764
|
});
|
|
@@ -45734,6 +45785,7 @@ class BrokerAdapter {
|
|
|
45734
45785
|
exchangeName: event.exchangeName,
|
|
45735
45786
|
frameName: event.frameName,
|
|
45736
45787
|
},
|
|
45788
|
+
when: new Date(event.timestamp),
|
|
45737
45789
|
backtest: event.backtest,
|
|
45738
45790
|
});
|
|
45739
45791
|
});
|
|
@@ -45757,6 +45809,7 @@ class BrokerAdapter {
|
|
|
45757
45809
|
exchangeName: event.exchangeName,
|
|
45758
45810
|
frameName: event.frameName,
|
|
45759
45811
|
},
|
|
45812
|
+
when: new Date(event.timestamp),
|
|
45760
45813
|
backtest: event.backtest,
|
|
45761
45814
|
});
|
|
45762
45815
|
});
|
|
@@ -45775,6 +45828,7 @@ class BrokerAdapter {
|
|
|
45775
45828
|
exchangeName: event.exchangeName,
|
|
45776
45829
|
frameName: event.frameName,
|
|
45777
45830
|
},
|
|
45831
|
+
when: new Date(event.timestamp),
|
|
45778
45832
|
backtest: event.backtest,
|
|
45779
45833
|
});
|
|
45780
45834
|
});
|
|
@@ -45792,6 +45846,7 @@ class BrokerAdapter {
|
|
|
45792
45846
|
exchangeName: event.exchangeName,
|
|
45793
45847
|
frameName: event.frameName,
|
|
45794
45848
|
},
|
|
45849
|
+
when: new Date(event.timestamp),
|
|
45795
45850
|
backtest: event.backtest,
|
|
45796
45851
|
});
|
|
45797
45852
|
});
|
|
@@ -45804,6 +45859,7 @@ class BrokerAdapter {
|
|
|
45804
45859
|
exchangeName: event.exchangeName,
|
|
45805
45860
|
frameName: event.frameName,
|
|
45806
45861
|
},
|
|
45862
|
+
when: new Date(event.timestamp),
|
|
45807
45863
|
backtest: event.backtest,
|
|
45808
45864
|
});
|
|
45809
45865
|
});
|
|
@@ -45821,6 +45877,7 @@ class BrokerAdapter {
|
|
|
45821
45877
|
exchangeName: event.exchangeName,
|
|
45822
45878
|
frameName: event.frameName,
|
|
45823
45879
|
},
|
|
45880
|
+
when: new Date(event.timestamp),
|
|
45824
45881
|
backtest: event.backtest,
|
|
45825
45882
|
};
|
|
45826
45883
|
if (event.action === "scheduled") {
|
|
@@ -45843,6 +45900,7 @@ class BrokerAdapter {
|
|
|
45843
45900
|
exchangeName: event.exchangeName,
|
|
45844
45901
|
frameName: event.frameName,
|
|
45845
45902
|
},
|
|
45903
|
+
when: new Date(event.timestamp),
|
|
45846
45904
|
backtest: event.backtest,
|
|
45847
45905
|
};
|
|
45848
45906
|
if (event.action === "opened") {
|
|
@@ -46617,6 +46675,7 @@ async function commitPartialProfit(symbol, percentToClose) {
|
|
|
46617
46675
|
priceTakeProfit: signalForProfit.priceTakeProfit,
|
|
46618
46676
|
priceStopLoss: signalForProfit.priceStopLoss,
|
|
46619
46677
|
context: { exchangeName, frameName, strategyName },
|
|
46678
|
+
when: backtest.executionContextService.context.when,
|
|
46620
46679
|
backtest: isBacktest,
|
|
46621
46680
|
});
|
|
46622
46681
|
return await backtest.strategyCoreService.partialProfit(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46685,6 +46744,7 @@ async function commitPartialLoss(symbol, percentToClose) {
|
|
|
46685
46744
|
priceTakeProfit: signalForLoss.priceTakeProfit,
|
|
46686
46745
|
priceStopLoss: signalForLoss.priceStopLoss,
|
|
46687
46746
|
context: { exchangeName, frameName, strategyName },
|
|
46747
|
+
when: backtest.executionContextService.context.when,
|
|
46688
46748
|
backtest: isBacktest,
|
|
46689
46749
|
});
|
|
46690
46750
|
return await backtest.strategyCoreService.partialLoss(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46769,6 +46829,7 @@ async function commitTrailingStop(symbol, percentShift, currentPrice) {
|
|
|
46769
46829
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46770
46830
|
position: signal.position,
|
|
46771
46831
|
context: { exchangeName, frameName, strategyName },
|
|
46832
|
+
when: backtest.executionContextService.context.when,
|
|
46772
46833
|
backtest: isBacktest,
|
|
46773
46834
|
});
|
|
46774
46835
|
return await backtest.strategyCoreService.trailingStop(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46853,6 +46914,7 @@ async function commitTrailingTake(symbol, percentShift, currentPrice) {
|
|
|
46853
46914
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46854
46915
|
position: signal.position,
|
|
46855
46916
|
context: { exchangeName, frameName, strategyName },
|
|
46917
|
+
when: backtest.executionContextService.context.when,
|
|
46856
46918
|
backtest: isBacktest,
|
|
46857
46919
|
});
|
|
46858
46920
|
return await backtest.strategyCoreService.trailingTake(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46908,6 +46970,7 @@ async function commitTrailingStopCost(symbol, newStopLossPrice) {
|
|
|
46908
46970
|
position: signal.position,
|
|
46909
46971
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46910
46972
|
context: { exchangeName, frameName, strategyName },
|
|
46973
|
+
when: backtest.executionContextService.context.when,
|
|
46911
46974
|
backtest: isBacktest,
|
|
46912
46975
|
});
|
|
46913
46976
|
return await backtest.strategyCoreService.trailingStop(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46963,6 +47026,7 @@ async function commitTrailingTakeCost(symbol, newTakeProfitPrice) {
|
|
|
46963
47026
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46964
47027
|
position: signal.position,
|
|
46965
47028
|
context: { exchangeName, frameName, strategyName },
|
|
47029
|
+
when: backtest.executionContextService.context.when,
|
|
46966
47030
|
backtest: isBacktest,
|
|
46967
47031
|
});
|
|
46968
47032
|
return await backtest.strategyCoreService.trailingTake(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -47024,6 +47088,7 @@ async function commitBreakeven(symbol) {
|
|
|
47024
47088
|
newTakeProfitPrice: breakevenNewTakeProfitPrice(signal.priceTakeProfit, signal._trailingPriceTakeProfit),
|
|
47025
47089
|
position: signal.position,
|
|
47026
47090
|
context: { exchangeName, frameName, strategyName },
|
|
47091
|
+
when: backtest.executionContextService.context.when,
|
|
47027
47092
|
backtest: isBacktest,
|
|
47028
47093
|
});
|
|
47029
47094
|
return await backtest.strategyCoreService.breakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -47116,6 +47181,7 @@ async function commitAverageBuy(symbol, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_C
|
|
|
47116
47181
|
priceTakeProfit: signalForAvgBuy.priceTakeProfit,
|
|
47117
47182
|
priceStopLoss: signalForAvgBuy.priceStopLoss,
|
|
47118
47183
|
context: { exchangeName, frameName, strategyName },
|
|
47184
|
+
when: backtest.executionContextService.context.when,
|
|
47119
47185
|
backtest: isBacktest,
|
|
47120
47186
|
});
|
|
47121
47187
|
return await backtest.strategyCoreService.averageBuy(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName }, cost);
|
|
@@ -47552,6 +47618,7 @@ async function commitPartialProfitCost(symbol, dollarAmount) {
|
|
|
47552
47618
|
priceTakeProfit: signalForProfitCost.priceTakeProfit,
|
|
47553
47619
|
priceStopLoss: signalForProfitCost.priceStopLoss,
|
|
47554
47620
|
context: { exchangeName, frameName, strategyName },
|
|
47621
|
+
when: backtest.executionContextService.context.when,
|
|
47555
47622
|
backtest: isBacktest,
|
|
47556
47623
|
});
|
|
47557
47624
|
return await backtest.strategyCoreService.partialProfit(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -47624,6 +47691,7 @@ async function commitPartialLossCost(symbol, dollarAmount) {
|
|
|
47624
47691
|
priceTakeProfit: signalForLossCost.priceTakeProfit,
|
|
47625
47692
|
priceStopLoss: signalForLossCost.priceStopLoss,
|
|
47626
47693
|
context: { exchangeName, frameName, strategyName },
|
|
47694
|
+
when: backtest.executionContextService.context.when,
|
|
47627
47695
|
backtest: isBacktest,
|
|
47628
47696
|
});
|
|
47629
47697
|
return await backtest.strategyCoreService.partialLoss(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -52538,6 +52606,7 @@ class BacktestUtils {
|
|
|
52538
52606
|
priceTakeProfit: signalForProfit.priceTakeProfit,
|
|
52539
52607
|
priceStopLoss: signalForProfit.priceStopLoss,
|
|
52540
52608
|
context,
|
|
52609
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52541
52610
|
backtest: true,
|
|
52542
52611
|
});
|
|
52543
52612
|
return await backtest.strategyCoreService.partialProfit(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52612,6 +52681,7 @@ class BacktestUtils {
|
|
|
52612
52681
|
priceTakeProfit: signalForLoss.priceTakeProfit,
|
|
52613
52682
|
priceStopLoss: signalForLoss.priceStopLoss,
|
|
52614
52683
|
context,
|
|
52684
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52615
52685
|
backtest: true,
|
|
52616
52686
|
});
|
|
52617
52687
|
return await backtest.strategyCoreService.partialLoss(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52689,6 +52759,7 @@ class BacktestUtils {
|
|
|
52689
52759
|
priceTakeProfit: signalForProfitCost.priceTakeProfit,
|
|
52690
52760
|
priceStopLoss: signalForProfitCost.priceStopLoss,
|
|
52691
52761
|
context,
|
|
52762
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52692
52763
|
backtest: true,
|
|
52693
52764
|
});
|
|
52694
52765
|
return await backtest.strategyCoreService.partialProfit(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52766,6 +52837,7 @@ class BacktestUtils {
|
|
|
52766
52837
|
priceTakeProfit: signalForLossCost.priceTakeProfit,
|
|
52767
52838
|
priceStopLoss: signalForLossCost.priceStopLoss,
|
|
52768
52839
|
context,
|
|
52840
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52769
52841
|
backtest: true,
|
|
52770
52842
|
});
|
|
52771
52843
|
return await backtest.strategyCoreService.partialLoss(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52855,6 +52927,7 @@ class BacktestUtils {
|
|
|
52855
52927
|
takeProfitPrice: signal.priceTakeProfit,
|
|
52856
52928
|
position: signal.position,
|
|
52857
52929
|
context,
|
|
52930
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52858
52931
|
backtest: true,
|
|
52859
52932
|
});
|
|
52860
52933
|
return await backtest.strategyCoreService.trailingStop(true, symbol, percentShift, currentPrice, context);
|
|
@@ -52944,6 +53017,7 @@ class BacktestUtils {
|
|
|
52944
53017
|
takeProfitPrice: signal.priceTakeProfit,
|
|
52945
53018
|
position: signal.position,
|
|
52946
53019
|
context,
|
|
53020
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52947
53021
|
backtest: true,
|
|
52948
53022
|
});
|
|
52949
53023
|
return await backtest.strategyCoreService.trailingTake(true, symbol, percentShift, currentPrice, context);
|
|
@@ -52999,6 +53073,7 @@ class BacktestUtils {
|
|
|
52999
53073
|
position: signal.position,
|
|
53000
53074
|
takeProfitPrice: signal.priceTakeProfit,
|
|
53001
53075
|
context,
|
|
53076
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
53002
53077
|
backtest: true,
|
|
53003
53078
|
});
|
|
53004
53079
|
return await backtest.strategyCoreService.trailingStop(true, symbol, percentShift, currentPrice, context);
|
|
@@ -53054,6 +53129,7 @@ class BacktestUtils {
|
|
|
53054
53129
|
position: signal.position,
|
|
53055
53130
|
takeProfitPrice: signal.priceTakeProfit,
|
|
53056
53131
|
context,
|
|
53132
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
53057
53133
|
backtest: true,
|
|
53058
53134
|
});
|
|
53059
53135
|
return await backtest.strategyCoreService.trailingTake(true, symbol, percentShift, currentPrice, context);
|
|
@@ -53115,6 +53191,7 @@ class BacktestUtils {
|
|
|
53115
53191
|
newTakeProfitPrice: breakevenNewTakeProfitPrice(signal.priceTakeProfit, signal._trailingPriceTakeProfit),
|
|
53116
53192
|
position: signal.position,
|
|
53117
53193
|
context,
|
|
53194
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
53118
53195
|
backtest: true,
|
|
53119
53196
|
});
|
|
53120
53197
|
return await backtest.strategyCoreService.breakeven(true, symbol, currentPrice, context);
|
|
@@ -53216,6 +53293,7 @@ class BacktestUtils {
|
|
|
53216
53293
|
priceTakeProfit: signalForAvgBuy.priceTakeProfit,
|
|
53217
53294
|
priceStopLoss: signalForAvgBuy.priceStopLoss,
|
|
53218
53295
|
context,
|
|
53296
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
53219
53297
|
backtest: true,
|
|
53220
53298
|
});
|
|
53221
53299
|
return await backtest.strategyCoreService.averageBuy(true, symbol, currentPrice, context, cost);
|
|
@@ -55547,6 +55625,7 @@ class LiveUtils {
|
|
|
55547
55625
|
exchangeName: context.exchangeName,
|
|
55548
55626
|
frameName: "",
|
|
55549
55627
|
},
|
|
55628
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55550
55629
|
backtest: false,
|
|
55551
55630
|
});
|
|
55552
55631
|
return await backtest.strategyCoreService.partialProfit(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55642,6 +55721,7 @@ class LiveUtils {
|
|
|
55642
55721
|
exchangeName: context.exchangeName,
|
|
55643
55722
|
frameName: "",
|
|
55644
55723
|
},
|
|
55724
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55645
55725
|
backtest: false,
|
|
55646
55726
|
});
|
|
55647
55727
|
return await backtest.strategyCoreService.partialLoss(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55739,6 +55819,7 @@ class LiveUtils {
|
|
|
55739
55819
|
exchangeName: context.exchangeName,
|
|
55740
55820
|
frameName: "",
|
|
55741
55821
|
},
|
|
55822
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55742
55823
|
backtest: false,
|
|
55743
55824
|
});
|
|
55744
55825
|
return await backtest.strategyCoreService.partialProfit(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55836,6 +55917,7 @@ class LiveUtils {
|
|
|
55836
55917
|
exchangeName: context.exchangeName,
|
|
55837
55918
|
frameName: "",
|
|
55838
55919
|
},
|
|
55920
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55839
55921
|
backtest: false,
|
|
55840
55922
|
});
|
|
55841
55923
|
return await backtest.strategyCoreService.partialLoss(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55940,6 +56022,7 @@ class LiveUtils {
|
|
|
55940
56022
|
takeProfitPrice: signal.priceTakeProfit,
|
|
55941
56023
|
position: signal.position,
|
|
55942
56024
|
context,
|
|
56025
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55943
56026
|
backtest: false,
|
|
55944
56027
|
});
|
|
55945
56028
|
return await backtest.strategyCoreService.trailingStop(false, symbol, percentShift, currentPrice, {
|
|
@@ -56044,6 +56127,7 @@ class LiveUtils {
|
|
|
56044
56127
|
takeProfitPrice: signal.priceTakeProfit,
|
|
56045
56128
|
position: signal.position,
|
|
56046
56129
|
context,
|
|
56130
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56047
56131
|
backtest: false,
|
|
56048
56132
|
});
|
|
56049
56133
|
return await backtest.strategyCoreService.trailingTake(false, symbol, percentShift, currentPrice, {
|
|
@@ -56115,6 +56199,7 @@ class LiveUtils {
|
|
|
56115
56199
|
takeProfitPrice: signal.priceTakeProfit,
|
|
56116
56200
|
position: signal.position,
|
|
56117
56201
|
context,
|
|
56202
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56118
56203
|
backtest: false,
|
|
56119
56204
|
});
|
|
56120
56205
|
return await backtest.strategyCoreService.trailingStop(false, symbol, percentShift, currentPrice, {
|
|
@@ -56186,6 +56271,7 @@ class LiveUtils {
|
|
|
56186
56271
|
takeProfitPrice: signal.priceTakeProfit,
|
|
56187
56272
|
position: signal.position,
|
|
56188
56273
|
context,
|
|
56274
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56189
56275
|
backtest: false,
|
|
56190
56276
|
});
|
|
56191
56277
|
return await backtest.strategyCoreService.trailingTake(false, symbol, percentShift, currentPrice, {
|
|
@@ -56267,6 +56353,7 @@ class LiveUtils {
|
|
|
56267
56353
|
exchangeName: context.exchangeName,
|
|
56268
56354
|
frameName: "",
|
|
56269
56355
|
},
|
|
56356
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56270
56357
|
backtest: false,
|
|
56271
56358
|
});
|
|
56272
56359
|
return await backtest.strategyCoreService.breakeven(false, symbol, currentPrice, {
|
|
@@ -56386,6 +56473,7 @@ class LiveUtils {
|
|
|
56386
56473
|
exchangeName: context.exchangeName,
|
|
56387
56474
|
frameName: "",
|
|
56388
56475
|
},
|
|
56476
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56389
56477
|
backtest: false,
|
|
56390
56478
|
});
|
|
56391
56479
|
return await backtest.strategyCoreService.averageBuy(false, symbol, currentPrice, {
|
|
@@ -66681,7 +66769,8 @@ const WILDCARD_TARGET = {
|
|
|
66681
66769
|
partial_loss: true,
|
|
66682
66770
|
breakeven: true,
|
|
66683
66771
|
strategy_commit: true,
|
|
66684
|
-
|
|
66772
|
+
order_sync: true,
|
|
66773
|
+
order_check: true,
|
|
66685
66774
|
risk: true,
|
|
66686
66775
|
info: true,
|
|
66687
66776
|
common_error: true,
|
|
@@ -67410,14 +67499,15 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
67410
67499
|
};
|
|
67411
67500
|
/**
|
|
67412
67501
|
* Creates a notification model for signal sync events.
|
|
67413
|
-
* Handles signal-open (
|
|
67502
|
+
* Handles signal-open (position order filled with orderType "active", or resting
|
|
67503
|
+
* entry order placed with orderType "schedule") and signal-close (position exited) actions.
|
|
67414
67504
|
* @param data - The signal sync contract data
|
|
67415
67505
|
* @returns NotificationModel for signal sync event
|
|
67416
67506
|
*/
|
|
67417
67507
|
const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
67418
67508
|
if (data.action === "signal-open") {
|
|
67419
67509
|
return {
|
|
67420
|
-
type: "
|
|
67510
|
+
type: "order_sync.open",
|
|
67421
67511
|
id: CREATE_KEY_FN$2(),
|
|
67422
67512
|
timestamp: data.timestamp,
|
|
67423
67513
|
backtest: data.backtest,
|
|
@@ -67425,6 +67515,7 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67425
67515
|
strategyName: data.strategyName,
|
|
67426
67516
|
exchangeName: data.exchangeName,
|
|
67427
67517
|
signalId: data.signalId,
|
|
67518
|
+
orderType: data.type,
|
|
67428
67519
|
currentPrice: data.currentPrice,
|
|
67429
67520
|
pnl: data.signal.pnl,
|
|
67430
67521
|
maxDrawdown: data.signal.maxDrawdown,
|
|
@@ -67462,7 +67553,7 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67462
67553
|
}
|
|
67463
67554
|
if (data.action === "signal-close") {
|
|
67464
67555
|
return {
|
|
67465
|
-
type: "
|
|
67556
|
+
type: "order_sync.close",
|
|
67466
67557
|
id: CREATE_KEY_FN$2(),
|
|
67467
67558
|
timestamp: data.timestamp,
|
|
67468
67559
|
backtest: data.backtest,
|
|
@@ -67470,6 +67561,7 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67470
67561
|
strategyName: data.strategyName,
|
|
67471
67562
|
exchangeName: data.exchangeName,
|
|
67472
67563
|
signalId: data.signalId,
|
|
67564
|
+
orderType: data.type,
|
|
67473
67565
|
currentPrice: data.currentPrice,
|
|
67474
67566
|
pnl: data.signal.pnl,
|
|
67475
67567
|
maxDrawdown: data.signal.maxDrawdown,
|
|
@@ -67507,6 +67599,54 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67507
67599
|
}
|
|
67508
67600
|
throw new Error(`Unrecognized signal sync action: ${get(data, "action")}`);
|
|
67509
67601
|
};
|
|
67602
|
+
/**
|
|
67603
|
+
* Creates a notification model for order-ping check events.
|
|
67604
|
+
* @param data - The order check contract data
|
|
67605
|
+
* @returns NotificationModel for signal sync check event
|
|
67606
|
+
*/
|
|
67607
|
+
const CREATE_ORDER_CHECK_NOTIFICATION_FN = (data) => ({
|
|
67608
|
+
type: "order_sync.check",
|
|
67609
|
+
id: CREATE_KEY_FN$2(),
|
|
67610
|
+
timestamp: data.timestamp,
|
|
67611
|
+
backtest: data.backtest,
|
|
67612
|
+
symbol: data.symbol,
|
|
67613
|
+
strategyName: data.strategyName,
|
|
67614
|
+
exchangeName: data.exchangeName,
|
|
67615
|
+
signalId: data.signalId,
|
|
67616
|
+
orderType: data.type,
|
|
67617
|
+
currentPrice: data.currentPrice,
|
|
67618
|
+
position: data.position,
|
|
67619
|
+
priceOpen: data.priceOpen,
|
|
67620
|
+
priceTakeProfit: data.priceTakeProfit,
|
|
67621
|
+
priceStopLoss: data.priceStopLoss,
|
|
67622
|
+
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
67623
|
+
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
67624
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
67625
|
+
totalEntries: data.totalEntries,
|
|
67626
|
+
totalPartials: data.totalPartials,
|
|
67627
|
+
pnl: data.pnl,
|
|
67628
|
+
maxDrawdown: data.maxDrawdown,
|
|
67629
|
+
peakProfit: data.peakProfit,
|
|
67630
|
+
pnlPercentage: data.pnl.pnlPercentage,
|
|
67631
|
+
pnlPriceOpen: data.pnl.priceOpen,
|
|
67632
|
+
pnlPriceClose: data.pnl.priceClose,
|
|
67633
|
+
pnlCost: data.pnl.pnlCost,
|
|
67634
|
+
pnlEntries: data.pnl.pnlEntries,
|
|
67635
|
+
peakProfitPriceOpen: data.peakProfit.priceOpen,
|
|
67636
|
+
peakProfitPriceClose: data.peakProfit.priceClose,
|
|
67637
|
+
peakProfitPercentage: data.peakProfit.pnlPercentage,
|
|
67638
|
+
peakProfitCost: data.peakProfit.pnlCost,
|
|
67639
|
+
peakProfitEntries: data.peakProfit.pnlEntries,
|
|
67640
|
+
maxDrawdownPriceOpen: data.maxDrawdown.priceOpen,
|
|
67641
|
+
maxDrawdownPriceClose: data.maxDrawdown.priceClose,
|
|
67642
|
+
maxDrawdownPercentage: data.maxDrawdown.pnlPercentage,
|
|
67643
|
+
maxDrawdownCost: data.maxDrawdown.pnlCost,
|
|
67644
|
+
maxDrawdownEntries: data.maxDrawdown.pnlEntries,
|
|
67645
|
+
scheduledAt: data.scheduledAt,
|
|
67646
|
+
pendingAt: data.pendingAt,
|
|
67647
|
+
note: data.signal.note,
|
|
67648
|
+
createdAt: data.timestamp,
|
|
67649
|
+
});
|
|
67510
67650
|
/**
|
|
67511
67651
|
* Creates a notification model for risk rejection events.
|
|
67512
67652
|
* @param data - The risk contract data
|
|
@@ -67624,6 +67764,7 @@ const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_LOSS = "Notificati
|
|
|
67624
67764
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationMemoryBacktestUtils.handleBreakeven";
|
|
67625
67765
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationMemoryBacktestUtils.handleStrategyCommit";
|
|
67626
67766
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_SYNC = "NotificationMemoryBacktestUtils.handleSync";
|
|
67767
|
+
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_CHECK = "NotificationMemoryBacktestUtils.handleCheck";
|
|
67627
67768
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_RISK = "NotificationMemoryBacktestUtils.handleRisk";
|
|
67628
67769
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ERROR = "NotificationMemoryBacktestUtils.handleError";
|
|
67629
67770
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationMemoryBacktestUtils.handleCriticalError";
|
|
@@ -67637,6 +67778,7 @@ const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationMe
|
|
|
67637
67778
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationMemoryLiveUtils.handleBreakeven";
|
|
67638
67779
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationMemoryLiveUtils.handleStrategyCommit";
|
|
67639
67780
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_SYNC = "NotificationMemoryLiveUtils.handleSync";
|
|
67781
|
+
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_CHECK = "NotificationMemoryLiveUtils.handleCheck";
|
|
67640
67782
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_RISK = "NotificationMemoryLiveUtils.handleRisk";
|
|
67641
67783
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_ERROR = "NotificationMemoryLiveUtils.handleError";
|
|
67642
67784
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationMemoryLiveUtils.handleCriticalError";
|
|
@@ -67666,6 +67808,7 @@ const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_LOSS = "Notificat
|
|
|
67666
67808
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationPersistBacktestUtils.handleBreakeven";
|
|
67667
67809
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationPersistBacktestUtils.handleStrategyCommit";
|
|
67668
67810
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_SYNC = "NotificationPersistBacktestUtils.handleSync";
|
|
67811
|
+
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_CHECK = "NotificationPersistBacktestUtils.handleCheck";
|
|
67669
67812
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_RISK = "NotificationPersistBacktestUtils.handleRisk";
|
|
67670
67813
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ERROR = "NotificationPersistBacktestUtils.handleError";
|
|
67671
67814
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationPersistBacktestUtils.handleCriticalError";
|
|
@@ -67681,6 +67824,7 @@ const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationP
|
|
|
67681
67824
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationPersistLiveUtils.handleBreakeven";
|
|
67682
67825
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationPersistLiveUtils.handleStrategyCommit";
|
|
67683
67826
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_SYNC = "NotificationPersistLiveUtils.handleSync";
|
|
67827
|
+
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_CHECK = "NotificationPersistLiveUtils.handleCheck";
|
|
67684
67828
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_RISK = "NotificationPersistLiveUtils.handleRisk";
|
|
67685
67829
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_ERROR = "NotificationPersistLiveUtils.handleError";
|
|
67686
67830
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationPersistLiveUtils.handleCriticalError";
|
|
@@ -67779,15 +67923,23 @@ class NotificationMemoryBacktestUtils {
|
|
|
67779
67923
|
signalId: data.signalId,
|
|
67780
67924
|
action: data.action,
|
|
67781
67925
|
});
|
|
67782
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
67783
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
67784
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
67785
|
-
return;
|
|
67786
|
-
}
|
|
67787
67926
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
67788
67927
|
}, {
|
|
67789
67928
|
defaultValue: null,
|
|
67790
67929
|
});
|
|
67930
|
+
/**
|
|
67931
|
+
* Handles order-ping check event.
|
|
67932
|
+
* @param data - The order check contract data
|
|
67933
|
+
*/
|
|
67934
|
+
this.handleCheck = functoolsKit.trycatch(async (data) => {
|
|
67935
|
+
backtest.loggerService.info(NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_CHECK, {
|
|
67936
|
+
signalId: data.signalId,
|
|
67937
|
+
type: data.type,
|
|
67938
|
+
});
|
|
67939
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
67940
|
+
}, {
|
|
67941
|
+
defaultValue: null,
|
|
67942
|
+
});
|
|
67791
67943
|
/**
|
|
67792
67944
|
* Handles risk rejection event.
|
|
67793
67945
|
* @param data - The risk contract data
|
|
@@ -67902,6 +68054,13 @@ class NotificationDummyBacktestUtils {
|
|
|
67902
68054
|
}, {
|
|
67903
68055
|
defaultValue: null,
|
|
67904
68056
|
});
|
|
68057
|
+
/**
|
|
68058
|
+
* No-op handler for order-ping check event.
|
|
68059
|
+
*/
|
|
68060
|
+
this.handleCheck = functoolsKit.trycatch(async () => {
|
|
68061
|
+
}, {
|
|
68062
|
+
defaultValue: null,
|
|
68063
|
+
});
|
|
67905
68064
|
/**
|
|
67906
68065
|
* No-op handler for risk rejection event.
|
|
67907
68066
|
*/
|
|
@@ -68053,17 +68212,27 @@ class NotificationPersistBacktestUtils {
|
|
|
68053
68212
|
signalId: data.signalId,
|
|
68054
68213
|
action: data.action,
|
|
68055
68214
|
});
|
|
68056
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
68057
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
68058
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
68059
|
-
return;
|
|
68060
|
-
}
|
|
68061
68215
|
await this.waitForInit();
|
|
68062
68216
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
68063
68217
|
await this._updateNotifications();
|
|
68064
68218
|
}, {
|
|
68065
68219
|
defaultValue: null,
|
|
68066
68220
|
});
|
|
68221
|
+
/**
|
|
68222
|
+
* Handles order-ping check event.
|
|
68223
|
+
* @param data - The order check contract data
|
|
68224
|
+
*/
|
|
68225
|
+
this.handleCheck = functoolsKit.trycatch(async (data) => {
|
|
68226
|
+
backtest.loggerService.info(NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_CHECK, {
|
|
68227
|
+
signalId: data.signalId,
|
|
68228
|
+
type: data.type,
|
|
68229
|
+
});
|
|
68230
|
+
await this.waitForInit();
|
|
68231
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
68232
|
+
await this._updateNotifications();
|
|
68233
|
+
}, {
|
|
68234
|
+
defaultValue: null,
|
|
68235
|
+
});
|
|
68067
68236
|
/**
|
|
68068
68237
|
* Handles risk rejection event.
|
|
68069
68238
|
* @param data - The risk contract data
|
|
@@ -68257,15 +68426,23 @@ class NotificationMemoryLiveUtils {
|
|
|
68257
68426
|
signalId: data.signalId,
|
|
68258
68427
|
action: data.action,
|
|
68259
68428
|
});
|
|
68260
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
68261
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
68262
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
68263
|
-
return;
|
|
68264
|
-
}
|
|
68265
68429
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
68266
68430
|
}, {
|
|
68267
68431
|
defaultValue: null,
|
|
68268
68432
|
});
|
|
68433
|
+
/**
|
|
68434
|
+
* Handles order-ping check event.
|
|
68435
|
+
* @param data - The order check contract data
|
|
68436
|
+
*/
|
|
68437
|
+
this.handleCheck = functoolsKit.trycatch(async (data) => {
|
|
68438
|
+
backtest.loggerService.info(NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_CHECK, {
|
|
68439
|
+
signalId: data.signalId,
|
|
68440
|
+
type: data.type,
|
|
68441
|
+
});
|
|
68442
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
68443
|
+
}, {
|
|
68444
|
+
defaultValue: null,
|
|
68445
|
+
});
|
|
68269
68446
|
/**
|
|
68270
68447
|
* Handles risk rejection event.
|
|
68271
68448
|
* @param data - The risk contract data
|
|
@@ -68380,6 +68557,13 @@ class NotificationDummyLiveUtils {
|
|
|
68380
68557
|
}, {
|
|
68381
68558
|
defaultValue: null,
|
|
68382
68559
|
});
|
|
68560
|
+
/**
|
|
68561
|
+
* No-op handler for order-ping check event.
|
|
68562
|
+
*/
|
|
68563
|
+
this.handleCheck = functoolsKit.trycatch(async () => {
|
|
68564
|
+
}, {
|
|
68565
|
+
defaultValue: null,
|
|
68566
|
+
});
|
|
68383
68567
|
/**
|
|
68384
68568
|
* No-op handler for risk rejection event.
|
|
68385
68569
|
*/
|
|
@@ -68532,17 +68716,27 @@ class NotificationPersistLiveUtils {
|
|
|
68532
68716
|
signalId: data.signalId,
|
|
68533
68717
|
action: data.action,
|
|
68534
68718
|
});
|
|
68535
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
68536
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
68537
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
68538
|
-
return;
|
|
68539
|
-
}
|
|
68540
68719
|
await this.waitForInit();
|
|
68541
68720
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
68542
68721
|
await this._updateNotifications();
|
|
68543
68722
|
}, {
|
|
68544
68723
|
defaultValue: null,
|
|
68545
68724
|
});
|
|
68725
|
+
/**
|
|
68726
|
+
* Handles order-ping check event.
|
|
68727
|
+
* @param data - The order check contract data
|
|
68728
|
+
*/
|
|
68729
|
+
this.handleCheck = functoolsKit.trycatch(async (data) => {
|
|
68730
|
+
backtest.loggerService.info(NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_CHECK, {
|
|
68731
|
+
signalId: data.signalId,
|
|
68732
|
+
type: data.type,
|
|
68733
|
+
});
|
|
68734
|
+
await this.waitForInit();
|
|
68735
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
68736
|
+
await this._updateNotifications();
|
|
68737
|
+
}, {
|
|
68738
|
+
defaultValue: null,
|
|
68739
|
+
});
|
|
68546
68740
|
/**
|
|
68547
68741
|
* Handles risk rejection event.
|
|
68548
68742
|
* @param data - The risk contract data
|
|
@@ -68720,6 +68914,16 @@ class NotificationBacktestAdapter {
|
|
|
68720
68914
|
}, {
|
|
68721
68915
|
defaultValue: null,
|
|
68722
68916
|
});
|
|
68917
|
+
/**
|
|
68918
|
+
* Handles order-ping check event.
|
|
68919
|
+
* Proxies call to the underlying notification adapter.
|
|
68920
|
+
* @param data - The order check contract data
|
|
68921
|
+
*/
|
|
68922
|
+
this.handleCheck = functoolsKit.trycatch(async (data) => {
|
|
68923
|
+
return await this.getInstance().handleCheck(data);
|
|
68924
|
+
}, {
|
|
68925
|
+
defaultValue: null,
|
|
68926
|
+
});
|
|
68723
68927
|
/**
|
|
68724
68928
|
* Handles risk rejection event.
|
|
68725
68929
|
* Proxies call to the underlying notification adapter.
|
|
@@ -68890,6 +69094,16 @@ class NotificationLiveAdapter {
|
|
|
68890
69094
|
}, {
|
|
68891
69095
|
defaultValue: null,
|
|
68892
69096
|
});
|
|
69097
|
+
/**
|
|
69098
|
+
* Handles order-ping check event.
|
|
69099
|
+
* Proxies call to the underlying notification adapter.
|
|
69100
|
+
* @param data - The order check contract data
|
|
69101
|
+
*/
|
|
69102
|
+
this.handleCheck = functoolsKit.trycatch(async (data) => {
|
|
69103
|
+
return await this.getInstance().handleCheck(data);
|
|
69104
|
+
}, {
|
|
69105
|
+
defaultValue: null,
|
|
69106
|
+
});
|
|
68893
69107
|
/**
|
|
68894
69108
|
* Handles risk rejection event.
|
|
68895
69109
|
* Proxies call to the underlying notification adapter.
|
|
@@ -69003,16 +69217,40 @@ class NotificationAdapter {
|
|
|
69003
69217
|
*
|
|
69004
69218
|
* @returns Cleanup function that unsubscribes from all emitters
|
|
69005
69219
|
*/
|
|
69006
|
-
this.enable = functoolsKit.singleshot(({ signal = false, info = false, partial_profit = false, partial_loss = false, breakeven = false, strategy_commit = false,
|
|
69220
|
+
this.enable = functoolsKit.singleshot(({ signal = false, info = false, partial_profit = false, partial_loss = false, breakeven = false, strategy_commit = false, order_sync = false, order_check = false, risk = false, common_error = false, critical_error = false, validation_error = false, } = WILDCARD_TARGET) => {
|
|
69007
69221
|
backtest.loggerService.info(NOTIFICATION_ADAPTER_METHOD_NAME_ENABLE);
|
|
69008
69222
|
let unLive;
|
|
69009
69223
|
let unBacktest;
|
|
69010
69224
|
{
|
|
69225
|
+
// Throttle state for order_sync.check: signalId -> timestamp of the last
|
|
69226
|
+
// emitted check notification. Entries are dropped on signal close/cancel
|
|
69227
|
+
// so the map cannot grow unbounded.
|
|
69228
|
+
const checkThrottleMap = new Map();
|
|
69011
69229
|
const unBacktestSignal = signalBacktestEmitter.subscribe(async (data) => {
|
|
69012
69230
|
if (signal) {
|
|
69013
69231
|
await NotificationBacktest.handleSignal(data);
|
|
69014
69232
|
}
|
|
69015
69233
|
});
|
|
69234
|
+
// Cleanup for checkThrottleMap. Tick-result emitters are NOT sufficient here:
|
|
69235
|
+
// out-of-band closes (commitClosePending, failed order ping, scheduled cancel
|
|
69236
|
+
// via commit) never reach signalBacktestEmitter/signalLiveEmitter. The
|
|
69237
|
+
// lifecycle channels cover every path: signalEventSubject "closed" fires for
|
|
69238
|
+
// all pending-signal closes (TP/SL/time_expired/user/ping), scheduleEventSubject
|
|
69239
|
+
// "cancelled" fires for scheduled signals removed before activation.
|
|
69240
|
+
const unBacktestSignalEvent = signalEventSubject
|
|
69241
|
+
.filter(({ backtest }) => backtest)
|
|
69242
|
+
.connect(async (event) => {
|
|
69243
|
+
if (event.action === "closed") {
|
|
69244
|
+
checkThrottleMap.delete(event.data.id);
|
|
69245
|
+
}
|
|
69246
|
+
});
|
|
69247
|
+
const unBacktestScheduleEvent = scheduleEventSubject
|
|
69248
|
+
.filter(({ backtest }) => backtest)
|
|
69249
|
+
.connect(async (event) => {
|
|
69250
|
+
if (event.action === "cancelled") {
|
|
69251
|
+
checkThrottleMap.delete(event.data.id);
|
|
69252
|
+
}
|
|
69253
|
+
});
|
|
69016
69254
|
const unBacktestPartialProfit = partialProfitSubject
|
|
69017
69255
|
.filter(({ backtest }) => backtest)
|
|
69018
69256
|
.connect(async (data) => {
|
|
@@ -69044,10 +69282,22 @@ class NotificationAdapter {
|
|
|
69044
69282
|
const unBacktestSync = syncSubject
|
|
69045
69283
|
.filter(({ backtest }) => backtest)
|
|
69046
69284
|
.connect(async (data) => {
|
|
69047
|
-
if (
|
|
69285
|
+
if (order_sync) {
|
|
69048
69286
|
await NotificationBacktest.handleSync(data);
|
|
69049
69287
|
}
|
|
69050
69288
|
});
|
|
69289
|
+
const unBacktestCheck = syncPendingSubject
|
|
69290
|
+
.filter(({ backtest }) => backtest)
|
|
69291
|
+
.connect(async (data) => {
|
|
69292
|
+
if (order_check) {
|
|
69293
|
+
const lastTimestamp = checkThrottleMap.get(data.signalId);
|
|
69294
|
+
if (lastTimestamp !== undefined && data.timestamp - lastTimestamp < GLOBAL_CONFIG.CC_NOTIFICATION_ORDER_CHECK_TTL) {
|
|
69295
|
+
return;
|
|
69296
|
+
}
|
|
69297
|
+
checkThrottleMap.set(data.signalId, data.timestamp);
|
|
69298
|
+
await NotificationBacktest.handleCheck(data);
|
|
69299
|
+
}
|
|
69300
|
+
});
|
|
69051
69301
|
const unBacktestRisk = riskSubject
|
|
69052
69302
|
.filter(({ backtest }) => backtest)
|
|
69053
69303
|
.connect(async (data) => {
|
|
@@ -69077,14 +69327,38 @@ class NotificationAdapter {
|
|
|
69077
69327
|
await NotificationBacktest.handleSignalNotify(data);
|
|
69078
69328
|
}
|
|
69079
69329
|
});
|
|
69080
|
-
unBacktest = functoolsKit.compose(() => unBacktestSignal(), () => unBacktestPartialProfit(), () => unBacktestPartialLoss(), () => unBacktestBreakeven(), () => unBacktestStrategyCommit(), () => unBacktestSync(), () => unBacktestRisk(), () => unBacktestError(), () => unBacktestExit(), () => unBacktestValidation(), () => unBacktestSignalNotify());
|
|
69330
|
+
unBacktest = functoolsKit.compose(() => unBacktestSignal(), () => unBacktestSignalEvent(), () => unBacktestScheduleEvent(), () => unBacktestPartialProfit(), () => unBacktestPartialLoss(), () => unBacktestBreakeven(), () => unBacktestStrategyCommit(), () => unBacktestSync(), () => unBacktestCheck(), () => unBacktestRisk(), () => unBacktestError(), () => unBacktestExit(), () => unBacktestValidation(), () => unBacktestSignalNotify(), () => checkThrottleMap.clear());
|
|
69081
69331
|
}
|
|
69082
69332
|
{
|
|
69333
|
+
// Throttle state for order_sync.check: signalId -> timestamp of the last
|
|
69334
|
+
// emitted check notification. Entries are dropped on signal close/cancel
|
|
69335
|
+
// so the map cannot grow unbounded.
|
|
69336
|
+
const checkThrottleMap = new Map();
|
|
69083
69337
|
const unLiveSignal = signalLiveEmitter.subscribe(async (data) => {
|
|
69084
69338
|
if (signal) {
|
|
69085
69339
|
await NotificationLive.handleSignal(data);
|
|
69086
69340
|
}
|
|
69087
69341
|
});
|
|
69342
|
+
// Cleanup for checkThrottleMap. Tick-result emitters are NOT sufficient here:
|
|
69343
|
+
// out-of-band closes (commitClosePending, failed order ping, scheduled cancel
|
|
69344
|
+
// via commit) never reach signalBacktestEmitter/signalLiveEmitter. The
|
|
69345
|
+
// lifecycle channels cover every path: signalEventSubject "closed" fires for
|
|
69346
|
+
// all pending-signal closes (TP/SL/time_expired/user/ping), scheduleEventSubject
|
|
69347
|
+
// "cancelled" fires for scheduled signals removed before activation.
|
|
69348
|
+
const unLiveSignalEvent = signalEventSubject
|
|
69349
|
+
.filter(({ backtest }) => !backtest)
|
|
69350
|
+
.connect(async (event) => {
|
|
69351
|
+
if (event.action === "closed") {
|
|
69352
|
+
checkThrottleMap.delete(event.data.id);
|
|
69353
|
+
}
|
|
69354
|
+
});
|
|
69355
|
+
const unLiveScheduleEvent = scheduleEventSubject
|
|
69356
|
+
.filter(({ backtest }) => !backtest)
|
|
69357
|
+
.connect(async (event) => {
|
|
69358
|
+
if (event.action === "cancelled") {
|
|
69359
|
+
checkThrottleMap.delete(event.data.id);
|
|
69360
|
+
}
|
|
69361
|
+
});
|
|
69088
69362
|
const unLivePartialProfit = partialProfitSubject
|
|
69089
69363
|
.filter(({ backtest }) => !backtest)
|
|
69090
69364
|
.connect(async (data) => {
|
|
@@ -69116,10 +69390,22 @@ class NotificationAdapter {
|
|
|
69116
69390
|
const unLiveSync = syncSubject
|
|
69117
69391
|
.filter(({ backtest }) => !backtest)
|
|
69118
69392
|
.connect(async (data) => {
|
|
69119
|
-
if (
|
|
69393
|
+
if (order_sync) {
|
|
69120
69394
|
await NotificationLive.handleSync(data);
|
|
69121
69395
|
}
|
|
69122
69396
|
});
|
|
69397
|
+
const unLiveCheck = syncPendingSubject
|
|
69398
|
+
.filter(({ backtest }) => !backtest)
|
|
69399
|
+
.connect(async (data) => {
|
|
69400
|
+
if (order_check) {
|
|
69401
|
+
const lastTimestamp = checkThrottleMap.get(data.signalId);
|
|
69402
|
+
if (lastTimestamp !== undefined && data.timestamp - lastTimestamp < GLOBAL_CONFIG.CC_NOTIFICATION_ORDER_CHECK_TTL) {
|
|
69403
|
+
return;
|
|
69404
|
+
}
|
|
69405
|
+
checkThrottleMap.set(data.signalId, data.timestamp);
|
|
69406
|
+
await NotificationLive.handleCheck(data);
|
|
69407
|
+
}
|
|
69408
|
+
});
|
|
69123
69409
|
const unLiveRisk = riskSubject
|
|
69124
69410
|
.filter(({ backtest }) => !backtest)
|
|
69125
69411
|
.connect(async (data) => {
|
|
@@ -69149,7 +69435,7 @@ class NotificationAdapter {
|
|
|
69149
69435
|
await NotificationLive.handleSignalNotify(data);
|
|
69150
69436
|
}
|
|
69151
69437
|
});
|
|
69152
|
-
unLive = functoolsKit.compose(() => unLiveSignal(), () => unLivePartialProfit(), () => unLivePartialLoss(), () => unLiveBreakeven(), () => unLiveStrategyCommit(), () => unLiveSync(), () => unLiveRisk(), () => unLiveError(), () => unLiveExit(), () => unLiveValidation(), () => unLiveSignalNotify());
|
|
69438
|
+
unLive = functoolsKit.compose(() => unLiveSignal(), () => unLiveSignalEvent(), () => unLiveScheduleEvent(), () => unLivePartialProfit(), () => unLivePartialLoss(), () => unLiveBreakeven(), () => unLiveStrategyCommit(), () => unLiveSync(), () => unLiveCheck(), () => unLiveRisk(), () => unLiveError(), () => unLiveExit(), () => unLiveValidation(), () => unLiveSignalNotify(), () => checkThrottleMap.clear());
|
|
69153
69439
|
}
|
|
69154
69440
|
return () => {
|
|
69155
69441
|
unLive();
|
|
@@ -70444,17 +70730,31 @@ const CRON_METHOD_NAME_DISPOSE = "CronUtils.dispose";
|
|
|
70444
70730
|
/**
|
|
70445
70731
|
* Watchdog timeout (ms) for a single cron handler invocation.
|
|
70446
70732
|
*
|
|
70447
|
-
* A
|
|
70448
|
-
* `_runEntry` races `entry.handler(info)`
|
|
70449
|
-
*
|
|
70450
|
-
* surfacing `failed = true`, logging
|
|
70451
|
-
* rolling back the watermark so the
|
|
70733
|
+
* A slot that does not settle within this window is treated as failed:
|
|
70734
|
+
* `_runEntry` races the runtime-info assembly plus `entry.handler(info)`
|
|
70735
|
+
* against a timer of this duration and, when the timer wins, rejects into the
|
|
70736
|
+
* same `catch` as any other handler error — surfacing `failed = true`, logging
|
|
70737
|
+
* a warning, and (for periodic entries) rolling back the watermark so the
|
|
70738
|
+
* boundary is retried on the next tick.
|
|
70452
70739
|
*
|
|
70453
70740
|
* This guards the `singlerun`-serialised tick pipeline against a handler that
|
|
70454
|
-
* never resolves (a lost `resolve`, a hung
|
|
70455
|
-
* own): without it such a handler would
|
|
70741
|
+
* never resolves (a lost `resolve`, a hung network call with no timeout of its
|
|
70742
|
+
* own): without it such a handler would hold its `_inFlight` slot forever and
|
|
70743
|
+
* `_tick`'s `Promise.all` would never settle, silently stalling every
|
|
70744
|
+
* subsequent lifecycle tick while the process stays alive and outwardly
|
|
70745
|
+
* healthy.
|
|
70746
|
+
*/
|
|
70747
|
+
const CRON_HANDLER_TIMEOUT = 900000;
|
|
70748
|
+
/**
|
|
70749
|
+
* Early-warning threshold (ms) for a single cron handler invocation.
|
|
70750
|
+
*
|
|
70751
|
+
* Purely observational: when a slot is still running this long after it was
|
|
70752
|
+
* opened, `_runEntry` logs a warning naming the entry — so a slow handler is
|
|
70753
|
+
* visible in the logs long before the {@link CRON_HANDLER_TIMEOUT} watchdog
|
|
70754
|
+
* forcibly fails it. Nothing is interrupted and no rollback happens at this
|
|
70755
|
+
* mark; the slot keeps running and may still succeed.
|
|
70456
70756
|
*/
|
|
70457
|
-
const
|
|
70757
|
+
const CRON_HANDLER_WARN_TIMEOUT = 120000;
|
|
70458
70758
|
/**
|
|
70459
70759
|
* Local logger instance.
|
|
70460
70760
|
*
|
|
@@ -70876,28 +71176,9 @@ class CronUtils {
|
|
|
70876
71176
|
}
|
|
70877
71177
|
taskList.push(pending);
|
|
70878
71178
|
}
|
|
70879
|
-
|
|
70880
|
-
|
|
70881
|
-
|
|
70882
|
-
// awaiting Promise.all so the singlerun pipeline stays serialised and no
|
|
70883
|
-
// duplicate/zombie slots are spawned — the timer only surfaces the stall.
|
|
70884
|
-
// Use a real setTimeout/clearTimeout (not sleep) so the alarm is cancelled
|
|
70885
|
-
// the instant Promise.all resolves, rather than lingering for the full
|
|
70886
|
-
// timeout on every fast tick.
|
|
70887
|
-
const timer = setTimeout(() => {
|
|
70888
|
-
const message = `${CRON_METHOD_NAME_TICK} timed out after ${CRON_HANDLER_TIMEOUT}ms`;
|
|
70889
|
-
const payload = { symbol, when, context };
|
|
70890
|
-
LOGGER_SERVICE$1.warn(message, payload);
|
|
70891
|
-
console.error(message, payload);
|
|
70892
|
-
errorEmitter.next(new Error(message));
|
|
70893
|
-
}, CRON_HANDLER_TIMEOUT);
|
|
70894
|
-
try {
|
|
70895
|
-
await Promise.all(taskList);
|
|
70896
|
-
}
|
|
70897
|
-
finally {
|
|
70898
|
-
clearTimeout(timer);
|
|
70899
|
-
}
|
|
70900
|
-
}
|
|
71179
|
+
// Every slot self-terminates via the `_runEntry` watchdog, so this settles
|
|
71180
|
+
// within CRON_HANDLER_TIMEOUT plus epsilon even when a handler hangs.
|
|
71181
|
+
await Promise.all(taskList);
|
|
70901
71182
|
// Roll back the watermark for any periodic slot THIS tick opened whose
|
|
70902
71183
|
// handler failed, so the next tick re-opens the same boundary and retries
|
|
70903
71184
|
// it — mirroring how a skipped boundary is later caught up. Restoring
|
|
@@ -71081,9 +71362,17 @@ class CronUtils {
|
|
|
71081
71362
|
*
|
|
71082
71363
|
* Assembles the {@link IRuntimeInfo} snapshot via
|
|
71083
71364
|
* `RuntimeMetaService.getRuntimeInfo(symbol, context, backtest)` and invokes
|
|
71084
|
-
* `entry.handler(info)
|
|
71085
|
-
*
|
|
71086
|
-
*
|
|
71365
|
+
* `entry.handler(info)`, racing both against the
|
|
71366
|
+
* {@link CRON_HANDLER_TIMEOUT} watchdog — a slot that does not settle in time
|
|
71367
|
+
* rejects into the same `catch` as any other error, so a hung handler (or a
|
|
71368
|
+
* hung price fetch inside `getRuntimeInfo`) can never hold the `_inFlight`
|
|
71369
|
+
* slot forever and stall the serialised tick pipeline. A slot still running
|
|
71370
|
+
* at {@link CRON_HANDLER_WARN_TIMEOUT} logs an observational warning first,
|
|
71371
|
+
* so a slow handler is visible in the logs well before the watchdog forcibly
|
|
71372
|
+
* fails it. Logs any error via
|
|
71373
|
+
* `console.error` and **returns** a `failed` boolean (`true` when the
|
|
71374
|
+
* handler — or the runtime-info assembly — threw or timed out) so the caller
|
|
71375
|
+
* (`_tick`) can roll back the periodic watermark of the
|
|
71087
71376
|
* slot it opened and retry that boundary. The error is **not** rethrown, so a
|
|
71088
71377
|
* failing handler never produces an unhandled rejection. Clears the
|
|
71089
71378
|
* `_inFlight` slot in `.finally()` so the next boundary produces a fresh
|
|
@@ -71108,9 +71397,37 @@ class CronUtils {
|
|
|
71108
71397
|
*/
|
|
71109
71398
|
async _runEntry(entry, symbol, alignedMs, slotKey, firedKey, backtest, context) {
|
|
71110
71399
|
let failed = false;
|
|
71400
|
+
let watchdog;
|
|
71401
|
+
// Observational early warning: fires while the slot is still running, long
|
|
71402
|
+
// before the watchdog below forcibly fails it. Cancelled in `finally`, so
|
|
71403
|
+
// it never fires for slots that settle in time.
|
|
71404
|
+
const slowAlarm = setTimeout(() => {
|
|
71405
|
+
const message = `${CRON_METHOD_NAME_TICK} entry "${entry.name}" still running after ${CRON_HANDLER_WARN_TIMEOUT}ms (watchdog at ${CRON_HANDLER_TIMEOUT}ms)`;
|
|
71406
|
+
const payload = { symbol, alignedMs };
|
|
71407
|
+
LOGGER_SERVICE$1.warn(message, payload);
|
|
71408
|
+
console.error(message, payload);
|
|
71409
|
+
}, CRON_HANDLER_WARN_TIMEOUT);
|
|
71111
71410
|
try {
|
|
71112
|
-
|
|
71113
|
-
|
|
71411
|
+
// The runtime-info assembly is raced alongside the handler: in live mode
|
|
71412
|
+
// getRuntimeInfo reaches out to the exchange for the current price, so it
|
|
71413
|
+
// can hang on a dead network exactly like a user handler can.
|
|
71414
|
+
const work = (async () => {
|
|
71415
|
+
const info = await RUNTIME_META_SERVICE.getRuntimeInfo(symbol, context, backtest);
|
|
71416
|
+
await entry.handler(info);
|
|
71417
|
+
})();
|
|
71418
|
+
// A timed-out slot abandons `work`; swallow its eventual rejection so a
|
|
71419
|
+
// zombie handler that fails later never surfaces as an unhandled
|
|
71420
|
+
// rejection. Its late success is equally unobserved: `failed` was already
|
|
71421
|
+
// reported and (for fire-once entries) the fired mark deliberately not set.
|
|
71422
|
+
work.catch(() => void 0);
|
|
71423
|
+
await Promise.race([
|
|
71424
|
+
work,
|
|
71425
|
+
new Promise((_, reject) => {
|
|
71426
|
+
watchdog = setTimeout(() => {
|
|
71427
|
+
reject(new Error(`Cron entry "${entry.name}" timed out after ${CRON_HANDLER_TIMEOUT}ms`));
|
|
71428
|
+
}, CRON_HANDLER_TIMEOUT);
|
|
71429
|
+
}),
|
|
71430
|
+
]);
|
|
71114
71431
|
}
|
|
71115
71432
|
catch (error) {
|
|
71116
71433
|
failed = true;
|
|
@@ -71126,6 +71443,8 @@ class CronUtils {
|
|
|
71126
71443
|
errorEmitter.next(error);
|
|
71127
71444
|
}
|
|
71128
71445
|
finally {
|
|
71446
|
+
clearTimeout(slowAlarm);
|
|
71447
|
+
clearTimeout(watchdog);
|
|
71129
71448
|
this._inFlight.delete(slotKey);
|
|
71130
71449
|
if (!failed && firedKey !== null) {
|
|
71131
71450
|
this._firedOnce.add(firedKey);
|