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.mjs
CHANGED
|
@@ -533,6 +533,16 @@ const GLOBAL_CONFIG = {
|
|
|
533
533
|
* Default: 500 notifications
|
|
534
534
|
*/
|
|
535
535
|
CC_MAX_NOTIFICATIONS: 500,
|
|
536
|
+
/**
|
|
537
|
+
* Minimum interval between `order_sync.check` notifications for a single signalId.
|
|
538
|
+
* Order-ping events (syncPendingSubject) fire on every live tick, so the
|
|
539
|
+
* NotificationAdapter throttles them: a signal produces at most one check
|
|
540
|
+
* notification per this interval. The throttle entry is removed when the
|
|
541
|
+
* signal is closed or cancelled.
|
|
542
|
+
*
|
|
543
|
+
* Default: 900000 ms (15 minutes)
|
|
544
|
+
*/
|
|
545
|
+
CC_NOTIFICATION_ORDER_CHECK_TTL: 900000,
|
|
536
546
|
/**
|
|
537
547
|
* Maximum number of signals to keep in storage.
|
|
538
548
|
* Older signals are removed when this limit is exceeded.
|
|
@@ -5297,6 +5307,24 @@ const validateCandles = (candles) => {
|
|
|
5297
5307
|
};
|
|
5298
5308
|
|
|
5299
5309
|
const MS_PER_MINUTE$7 = 60000;
|
|
5310
|
+
/**
|
|
5311
|
+
* Normalizes raw adapter output to the {@link IAggregatedTradeData} contract.
|
|
5312
|
+
*
|
|
5313
|
+
* Implicit-API exchange adapters (e.g. ccxt `publicGetAggTrades`) return every
|
|
5314
|
+
* scalar as a string. A string `timestamp` is type-invisible at runtime — it
|
|
5315
|
+
* survives serialization and comparison — and only blows up deep inside a
|
|
5316
|
+
* downstream consumer (`Number.isFinite("1783601403565") === false`). Coercing
|
|
5317
|
+
* the numeric fields here, at the single point where adapter data enters the
|
|
5318
|
+
* framework, guarantees the contract's `number` types hold regardless of
|
|
5319
|
+
* adapter behaviour. `isBuyerMaker` is deliberately left untouched: `Boolean`
|
|
5320
|
+
* coercion of a string `"false"` would yield `true`.
|
|
5321
|
+
*/
|
|
5322
|
+
const NORMALIZE_AGGREGATED_TRADES_FN$1 = (trades) => trades.map((trade) => ({
|
|
5323
|
+
...trade,
|
|
5324
|
+
price: Number(trade.price),
|
|
5325
|
+
qty: Number(trade.qty),
|
|
5326
|
+
timestamp: Number(trade.timestamp),
|
|
5327
|
+
}));
|
|
5300
5328
|
const INTERVAL_MINUTES$9 = {
|
|
5301
5329
|
"1m": 1,
|
|
5302
5330
|
"3m": 3,
|
|
@@ -5992,7 +6020,7 @@ class ClientExchange {
|
|
|
5992
6020
|
if (limit === undefined) {
|
|
5993
6021
|
const to = new Date(alignedTo);
|
|
5994
6022
|
const from = new Date(alignedTo - windowMs);
|
|
5995
|
-
return await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
6023
|
+
return NORMALIZE_AGGREGATED_TRADES_FN$1(await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest));
|
|
5996
6024
|
}
|
|
5997
6025
|
// With limit: paginate backwards until we have enough trades.
|
|
5998
6026
|
// Consecutive empty windows bound the scan: before the symbol's listing
|
|
@@ -6009,7 +6037,7 @@ class ClientExchange {
|
|
|
6009
6037
|
}
|
|
6010
6038
|
const to = new Date(windowEnd);
|
|
6011
6039
|
const from = new Date(windowStart);
|
|
6012
|
-
const chunk = await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
6040
|
+
const chunk = NORMALIZE_AGGREGATED_TRADES_FN$1(await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest));
|
|
6013
6041
|
if (chunk.length === 0) {
|
|
6014
6042
|
emptyWindows += 1;
|
|
6015
6043
|
if (emptyWindows >= MAX_CONSECUTIVE_EMPTY_WINDOWS) {
|
|
@@ -15356,6 +15384,10 @@ class StrategyConnectionService {
|
|
|
15356
15384
|
});
|
|
15357
15385
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
15358
15386
|
await strategy.waitForInit();
|
|
15387
|
+
if (!this.timeMetaService.hasTimestamp(symbol, context, backtest)) {
|
|
15388
|
+
const timestamp = this.executionContextService.context.when.getTime();
|
|
15389
|
+
await this.timeMetaService.next(symbol, timestamp, context, backtest);
|
|
15390
|
+
}
|
|
15359
15391
|
const tick = await strategy.tick(symbol, context.strategyName);
|
|
15360
15392
|
{
|
|
15361
15393
|
await this.priceMetaService.next(symbol, tick.currentPrice, context, backtest);
|
|
@@ -42676,6 +42708,24 @@ const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
|
|
|
42676
42708
|
const EXCHANGE_METHOD_NAME_GET_RAW_CANDLES = "ExchangeUtils.getRawCandles";
|
|
42677
42709
|
const EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES = "ExchangeUtils.getAggregatedTrades";
|
|
42678
42710
|
const MS_PER_MINUTE$4 = 60000;
|
|
42711
|
+
/**
|
|
42712
|
+
* Normalizes raw adapter output to the {@link IAggregatedTradeData} contract.
|
|
42713
|
+
*
|
|
42714
|
+
* Implicit-API exchange adapters (e.g. ccxt `publicGetAggTrades`) return every
|
|
42715
|
+
* scalar as a string. A string `timestamp` is type-invisible at runtime — it
|
|
42716
|
+
* survives serialization and comparison — and only blows up deep inside a
|
|
42717
|
+
* downstream consumer (`Number.isFinite("1783601403565") === false`). Coercing
|
|
42718
|
+
* the numeric fields here, at the single point where adapter data enters the
|
|
42719
|
+
* framework, guarantees the contract's `number` types hold regardless of
|
|
42720
|
+
* adapter behaviour. `isBuyerMaker` is deliberately left untouched: `Boolean`
|
|
42721
|
+
* coercion of a string `"false"` would yield `true`.
|
|
42722
|
+
*/
|
|
42723
|
+
const NORMALIZE_AGGREGATED_TRADES_FN = (trades) => trades.map((trade) => ({
|
|
42724
|
+
...trade,
|
|
42725
|
+
price: Number(trade.price),
|
|
42726
|
+
qty: Number(trade.qty),
|
|
42727
|
+
timestamp: Number(trade.timestamp),
|
|
42728
|
+
}));
|
|
42679
42729
|
/**
|
|
42680
42730
|
* Gets current timestamp from execution context if available.
|
|
42681
42731
|
* Returns current Date() if no execution context exists (non-trading GUI).
|
|
@@ -43172,7 +43222,7 @@ class ExchangeInstance {
|
|
|
43172
43222
|
if (limit === undefined) {
|
|
43173
43223
|
const to = new Date(alignedTo);
|
|
43174
43224
|
const from = new Date(alignedTo - windowMs);
|
|
43175
|
-
return await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
43225
|
+
return NORMALIZE_AGGREGATED_TRADES_FN(await this._methods.getAggregatedTrades(symbol, from, to, isBacktest));
|
|
43176
43226
|
}
|
|
43177
43227
|
// With limit: paginate backwards until we have enough trades.
|
|
43178
43228
|
// Consecutive empty windows bound the scan: before the symbol's listing
|
|
@@ -43189,7 +43239,7 @@ class ExchangeInstance {
|
|
|
43189
43239
|
}
|
|
43190
43240
|
const to = new Date(windowEnd);
|
|
43191
43241
|
const from = new Date(windowStart);
|
|
43192
|
-
const chunk = await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
43242
|
+
const chunk = NORMALIZE_AGGREGATED_TRADES_FN(await this._methods.getAggregatedTrades(symbol, from, to, isBacktest));
|
|
43193
43243
|
if (chunk.length === 0) {
|
|
43194
43244
|
emptyWindows += 1;
|
|
43195
43245
|
if (emptyWindows >= MAX_CONSECUTIVE_EMPTY_WINDOWS) {
|
|
@@ -45688,6 +45738,7 @@ class BrokerAdapter {
|
|
|
45688
45738
|
exchangeName: event.exchangeName,
|
|
45689
45739
|
frameName: event.frameName,
|
|
45690
45740
|
},
|
|
45741
|
+
when: new Date(event.timestamp),
|
|
45691
45742
|
backtest: event.backtest,
|
|
45692
45743
|
});
|
|
45693
45744
|
});
|
|
@@ -45714,6 +45765,7 @@ class BrokerAdapter {
|
|
|
45714
45765
|
exchangeName: event.exchangeName,
|
|
45715
45766
|
frameName: event.frameName,
|
|
45716
45767
|
},
|
|
45768
|
+
when: new Date(event.timestamp),
|
|
45717
45769
|
backtest: event.backtest,
|
|
45718
45770
|
});
|
|
45719
45771
|
});
|
|
@@ -45737,6 +45789,7 @@ class BrokerAdapter {
|
|
|
45737
45789
|
exchangeName: event.exchangeName,
|
|
45738
45790
|
frameName: event.frameName,
|
|
45739
45791
|
},
|
|
45792
|
+
when: new Date(event.timestamp),
|
|
45740
45793
|
backtest: event.backtest,
|
|
45741
45794
|
});
|
|
45742
45795
|
});
|
|
@@ -45755,6 +45808,7 @@ class BrokerAdapter {
|
|
|
45755
45808
|
exchangeName: event.exchangeName,
|
|
45756
45809
|
frameName: event.frameName,
|
|
45757
45810
|
},
|
|
45811
|
+
when: new Date(event.timestamp),
|
|
45758
45812
|
backtest: event.backtest,
|
|
45759
45813
|
});
|
|
45760
45814
|
});
|
|
@@ -45772,6 +45826,7 @@ class BrokerAdapter {
|
|
|
45772
45826
|
exchangeName: event.exchangeName,
|
|
45773
45827
|
frameName: event.frameName,
|
|
45774
45828
|
},
|
|
45829
|
+
when: new Date(event.timestamp),
|
|
45775
45830
|
backtest: event.backtest,
|
|
45776
45831
|
});
|
|
45777
45832
|
});
|
|
@@ -45784,6 +45839,7 @@ class BrokerAdapter {
|
|
|
45784
45839
|
exchangeName: event.exchangeName,
|
|
45785
45840
|
frameName: event.frameName,
|
|
45786
45841
|
},
|
|
45842
|
+
when: new Date(event.timestamp),
|
|
45787
45843
|
backtest: event.backtest,
|
|
45788
45844
|
});
|
|
45789
45845
|
});
|
|
@@ -45801,6 +45857,7 @@ class BrokerAdapter {
|
|
|
45801
45857
|
exchangeName: event.exchangeName,
|
|
45802
45858
|
frameName: event.frameName,
|
|
45803
45859
|
},
|
|
45860
|
+
when: new Date(event.timestamp),
|
|
45804
45861
|
backtest: event.backtest,
|
|
45805
45862
|
};
|
|
45806
45863
|
if (event.action === "scheduled") {
|
|
@@ -45823,6 +45880,7 @@ class BrokerAdapter {
|
|
|
45823
45880
|
exchangeName: event.exchangeName,
|
|
45824
45881
|
frameName: event.frameName,
|
|
45825
45882
|
},
|
|
45883
|
+
when: new Date(event.timestamp),
|
|
45826
45884
|
backtest: event.backtest,
|
|
45827
45885
|
};
|
|
45828
45886
|
if (event.action === "opened") {
|
|
@@ -46597,6 +46655,7 @@ async function commitPartialProfit(symbol, percentToClose) {
|
|
|
46597
46655
|
priceTakeProfit: signalForProfit.priceTakeProfit,
|
|
46598
46656
|
priceStopLoss: signalForProfit.priceStopLoss,
|
|
46599
46657
|
context: { exchangeName, frameName, strategyName },
|
|
46658
|
+
when: backtest.executionContextService.context.when,
|
|
46600
46659
|
backtest: isBacktest,
|
|
46601
46660
|
});
|
|
46602
46661
|
return await backtest.strategyCoreService.partialProfit(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46665,6 +46724,7 @@ async function commitPartialLoss(symbol, percentToClose) {
|
|
|
46665
46724
|
priceTakeProfit: signalForLoss.priceTakeProfit,
|
|
46666
46725
|
priceStopLoss: signalForLoss.priceStopLoss,
|
|
46667
46726
|
context: { exchangeName, frameName, strategyName },
|
|
46727
|
+
when: backtest.executionContextService.context.when,
|
|
46668
46728
|
backtest: isBacktest,
|
|
46669
46729
|
});
|
|
46670
46730
|
return await backtest.strategyCoreService.partialLoss(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46749,6 +46809,7 @@ async function commitTrailingStop(symbol, percentShift, currentPrice) {
|
|
|
46749
46809
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46750
46810
|
position: signal.position,
|
|
46751
46811
|
context: { exchangeName, frameName, strategyName },
|
|
46812
|
+
when: backtest.executionContextService.context.when,
|
|
46752
46813
|
backtest: isBacktest,
|
|
46753
46814
|
});
|
|
46754
46815
|
return await backtest.strategyCoreService.trailingStop(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46833,6 +46894,7 @@ async function commitTrailingTake(symbol, percentShift, currentPrice) {
|
|
|
46833
46894
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46834
46895
|
position: signal.position,
|
|
46835
46896
|
context: { exchangeName, frameName, strategyName },
|
|
46897
|
+
when: backtest.executionContextService.context.when,
|
|
46836
46898
|
backtest: isBacktest,
|
|
46837
46899
|
});
|
|
46838
46900
|
return await backtest.strategyCoreService.trailingTake(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46888,6 +46950,7 @@ async function commitTrailingStopCost(symbol, newStopLossPrice) {
|
|
|
46888
46950
|
position: signal.position,
|
|
46889
46951
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46890
46952
|
context: { exchangeName, frameName, strategyName },
|
|
46953
|
+
when: backtest.executionContextService.context.when,
|
|
46891
46954
|
backtest: isBacktest,
|
|
46892
46955
|
});
|
|
46893
46956
|
return await backtest.strategyCoreService.trailingStop(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -46943,6 +47006,7 @@ async function commitTrailingTakeCost(symbol, newTakeProfitPrice) {
|
|
|
46943
47006
|
takeProfitPrice: signal.priceTakeProfit,
|
|
46944
47007
|
position: signal.position,
|
|
46945
47008
|
context: { exchangeName, frameName, strategyName },
|
|
47009
|
+
when: backtest.executionContextService.context.when,
|
|
46946
47010
|
backtest: isBacktest,
|
|
46947
47011
|
});
|
|
46948
47012
|
return await backtest.strategyCoreService.trailingTake(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -47004,6 +47068,7 @@ async function commitBreakeven(symbol) {
|
|
|
47004
47068
|
newTakeProfitPrice: breakevenNewTakeProfitPrice(signal.priceTakeProfit, signal._trailingPriceTakeProfit),
|
|
47005
47069
|
position: signal.position,
|
|
47006
47070
|
context: { exchangeName, frameName, strategyName },
|
|
47071
|
+
when: backtest.executionContextService.context.when,
|
|
47007
47072
|
backtest: isBacktest,
|
|
47008
47073
|
});
|
|
47009
47074
|
return await backtest.strategyCoreService.breakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -47096,6 +47161,7 @@ async function commitAverageBuy(symbol, cost = GLOBAL_CONFIG.CC_POSITION_ENTRY_C
|
|
|
47096
47161
|
priceTakeProfit: signalForAvgBuy.priceTakeProfit,
|
|
47097
47162
|
priceStopLoss: signalForAvgBuy.priceStopLoss,
|
|
47098
47163
|
context: { exchangeName, frameName, strategyName },
|
|
47164
|
+
when: backtest.executionContextService.context.when,
|
|
47099
47165
|
backtest: isBacktest,
|
|
47100
47166
|
});
|
|
47101
47167
|
return await backtest.strategyCoreService.averageBuy(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName }, cost);
|
|
@@ -47532,6 +47598,7 @@ async function commitPartialProfitCost(symbol, dollarAmount) {
|
|
|
47532
47598
|
priceTakeProfit: signalForProfitCost.priceTakeProfit,
|
|
47533
47599
|
priceStopLoss: signalForProfitCost.priceStopLoss,
|
|
47534
47600
|
context: { exchangeName, frameName, strategyName },
|
|
47601
|
+
when: backtest.executionContextService.context.when,
|
|
47535
47602
|
backtest: isBacktest,
|
|
47536
47603
|
});
|
|
47537
47604
|
return await backtest.strategyCoreService.partialProfit(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -47604,6 +47671,7 @@ async function commitPartialLossCost(symbol, dollarAmount) {
|
|
|
47604
47671
|
priceTakeProfit: signalForLossCost.priceTakeProfit,
|
|
47605
47672
|
priceStopLoss: signalForLossCost.priceStopLoss,
|
|
47606
47673
|
context: { exchangeName, frameName, strategyName },
|
|
47674
|
+
when: backtest.executionContextService.context.when,
|
|
47607
47675
|
backtest: isBacktest,
|
|
47608
47676
|
});
|
|
47609
47677
|
return await backtest.strategyCoreService.partialLoss(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
@@ -52518,6 +52586,7 @@ class BacktestUtils {
|
|
|
52518
52586
|
priceTakeProfit: signalForProfit.priceTakeProfit,
|
|
52519
52587
|
priceStopLoss: signalForProfit.priceStopLoss,
|
|
52520
52588
|
context,
|
|
52589
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52521
52590
|
backtest: true,
|
|
52522
52591
|
});
|
|
52523
52592
|
return await backtest.strategyCoreService.partialProfit(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52592,6 +52661,7 @@ class BacktestUtils {
|
|
|
52592
52661
|
priceTakeProfit: signalForLoss.priceTakeProfit,
|
|
52593
52662
|
priceStopLoss: signalForLoss.priceStopLoss,
|
|
52594
52663
|
context,
|
|
52664
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52595
52665
|
backtest: true,
|
|
52596
52666
|
});
|
|
52597
52667
|
return await backtest.strategyCoreService.partialLoss(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52669,6 +52739,7 @@ class BacktestUtils {
|
|
|
52669
52739
|
priceTakeProfit: signalForProfitCost.priceTakeProfit,
|
|
52670
52740
|
priceStopLoss: signalForProfitCost.priceStopLoss,
|
|
52671
52741
|
context,
|
|
52742
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52672
52743
|
backtest: true,
|
|
52673
52744
|
});
|
|
52674
52745
|
return await backtest.strategyCoreService.partialProfit(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52746,6 +52817,7 @@ class BacktestUtils {
|
|
|
52746
52817
|
priceTakeProfit: signalForLossCost.priceTakeProfit,
|
|
52747
52818
|
priceStopLoss: signalForLossCost.priceStopLoss,
|
|
52748
52819
|
context,
|
|
52820
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52749
52821
|
backtest: true,
|
|
52750
52822
|
});
|
|
52751
52823
|
return await backtest.strategyCoreService.partialLoss(true, symbol, percentToClose, currentPrice, context);
|
|
@@ -52835,6 +52907,7 @@ class BacktestUtils {
|
|
|
52835
52907
|
takeProfitPrice: signal.priceTakeProfit,
|
|
52836
52908
|
position: signal.position,
|
|
52837
52909
|
context,
|
|
52910
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52838
52911
|
backtest: true,
|
|
52839
52912
|
});
|
|
52840
52913
|
return await backtest.strategyCoreService.trailingStop(true, symbol, percentShift, currentPrice, context);
|
|
@@ -52924,6 +52997,7 @@ class BacktestUtils {
|
|
|
52924
52997
|
takeProfitPrice: signal.priceTakeProfit,
|
|
52925
52998
|
position: signal.position,
|
|
52926
52999
|
context,
|
|
53000
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52927
53001
|
backtest: true,
|
|
52928
53002
|
});
|
|
52929
53003
|
return await backtest.strategyCoreService.trailingTake(true, symbol, percentShift, currentPrice, context);
|
|
@@ -52979,6 +53053,7 @@ class BacktestUtils {
|
|
|
52979
53053
|
position: signal.position,
|
|
52980
53054
|
takeProfitPrice: signal.priceTakeProfit,
|
|
52981
53055
|
context,
|
|
53056
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
52982
53057
|
backtest: true,
|
|
52983
53058
|
});
|
|
52984
53059
|
return await backtest.strategyCoreService.trailingStop(true, symbol, percentShift, currentPrice, context);
|
|
@@ -53034,6 +53109,7 @@ class BacktestUtils {
|
|
|
53034
53109
|
position: signal.position,
|
|
53035
53110
|
takeProfitPrice: signal.priceTakeProfit,
|
|
53036
53111
|
context,
|
|
53112
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
53037
53113
|
backtest: true,
|
|
53038
53114
|
});
|
|
53039
53115
|
return await backtest.strategyCoreService.trailingTake(true, symbol, percentShift, currentPrice, context);
|
|
@@ -53095,6 +53171,7 @@ class BacktestUtils {
|
|
|
53095
53171
|
newTakeProfitPrice: breakevenNewTakeProfitPrice(signal.priceTakeProfit, signal._trailingPriceTakeProfit),
|
|
53096
53172
|
position: signal.position,
|
|
53097
53173
|
context,
|
|
53174
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
53098
53175
|
backtest: true,
|
|
53099
53176
|
});
|
|
53100
53177
|
return await backtest.strategyCoreService.breakeven(true, symbol, currentPrice, context);
|
|
@@ -53196,6 +53273,7 @@ class BacktestUtils {
|
|
|
53196
53273
|
priceTakeProfit: signalForAvgBuy.priceTakeProfit,
|
|
53197
53274
|
priceStopLoss: signalForAvgBuy.priceStopLoss,
|
|
53198
53275
|
context,
|
|
53276
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, context, true)),
|
|
53199
53277
|
backtest: true,
|
|
53200
53278
|
});
|
|
53201
53279
|
return await backtest.strategyCoreService.averageBuy(true, symbol, currentPrice, context, cost);
|
|
@@ -55527,6 +55605,7 @@ class LiveUtils {
|
|
|
55527
55605
|
exchangeName: context.exchangeName,
|
|
55528
55606
|
frameName: "",
|
|
55529
55607
|
},
|
|
55608
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55530
55609
|
backtest: false,
|
|
55531
55610
|
});
|
|
55532
55611
|
return await backtest.strategyCoreService.partialProfit(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55622,6 +55701,7 @@ class LiveUtils {
|
|
|
55622
55701
|
exchangeName: context.exchangeName,
|
|
55623
55702
|
frameName: "",
|
|
55624
55703
|
},
|
|
55704
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55625
55705
|
backtest: false,
|
|
55626
55706
|
});
|
|
55627
55707
|
return await backtest.strategyCoreService.partialLoss(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55719,6 +55799,7 @@ class LiveUtils {
|
|
|
55719
55799
|
exchangeName: context.exchangeName,
|
|
55720
55800
|
frameName: "",
|
|
55721
55801
|
},
|
|
55802
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55722
55803
|
backtest: false,
|
|
55723
55804
|
});
|
|
55724
55805
|
return await backtest.strategyCoreService.partialProfit(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55816,6 +55897,7 @@ class LiveUtils {
|
|
|
55816
55897
|
exchangeName: context.exchangeName,
|
|
55817
55898
|
frameName: "",
|
|
55818
55899
|
},
|
|
55900
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55819
55901
|
backtest: false,
|
|
55820
55902
|
});
|
|
55821
55903
|
return await backtest.strategyCoreService.partialLoss(false, symbol, percentToClose, currentPrice, {
|
|
@@ -55920,6 +56002,7 @@ class LiveUtils {
|
|
|
55920
56002
|
takeProfitPrice: signal.priceTakeProfit,
|
|
55921
56003
|
position: signal.position,
|
|
55922
56004
|
context,
|
|
56005
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
55923
56006
|
backtest: false,
|
|
55924
56007
|
});
|
|
55925
56008
|
return await backtest.strategyCoreService.trailingStop(false, symbol, percentShift, currentPrice, {
|
|
@@ -56024,6 +56107,7 @@ class LiveUtils {
|
|
|
56024
56107
|
takeProfitPrice: signal.priceTakeProfit,
|
|
56025
56108
|
position: signal.position,
|
|
56026
56109
|
context,
|
|
56110
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56027
56111
|
backtest: false,
|
|
56028
56112
|
});
|
|
56029
56113
|
return await backtest.strategyCoreService.trailingTake(false, symbol, percentShift, currentPrice, {
|
|
@@ -56095,6 +56179,7 @@ class LiveUtils {
|
|
|
56095
56179
|
takeProfitPrice: signal.priceTakeProfit,
|
|
56096
56180
|
position: signal.position,
|
|
56097
56181
|
context,
|
|
56182
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56098
56183
|
backtest: false,
|
|
56099
56184
|
});
|
|
56100
56185
|
return await backtest.strategyCoreService.trailingStop(false, symbol, percentShift, currentPrice, {
|
|
@@ -56166,6 +56251,7 @@ class LiveUtils {
|
|
|
56166
56251
|
takeProfitPrice: signal.priceTakeProfit,
|
|
56167
56252
|
position: signal.position,
|
|
56168
56253
|
context,
|
|
56254
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56169
56255
|
backtest: false,
|
|
56170
56256
|
});
|
|
56171
56257
|
return await backtest.strategyCoreService.trailingTake(false, symbol, percentShift, currentPrice, {
|
|
@@ -56247,6 +56333,7 @@ class LiveUtils {
|
|
|
56247
56333
|
exchangeName: context.exchangeName,
|
|
56248
56334
|
frameName: "",
|
|
56249
56335
|
},
|
|
56336
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56250
56337
|
backtest: false,
|
|
56251
56338
|
});
|
|
56252
56339
|
return await backtest.strategyCoreService.breakeven(false, symbol, currentPrice, {
|
|
@@ -56366,6 +56453,7 @@ class LiveUtils {
|
|
|
56366
56453
|
exchangeName: context.exchangeName,
|
|
56367
56454
|
frameName: "",
|
|
56368
56455
|
},
|
|
56456
|
+
when: new Date(await backtest.timeMetaService.getTimestamp(symbol, { strategyName: context.strategyName, exchangeName: context.exchangeName, frameName: "" }, false)),
|
|
56369
56457
|
backtest: false,
|
|
56370
56458
|
});
|
|
56371
56459
|
return await backtest.strategyCoreService.averageBuy(false, symbol, currentPrice, {
|
|
@@ -66661,7 +66749,8 @@ const WILDCARD_TARGET = {
|
|
|
66661
66749
|
partial_loss: true,
|
|
66662
66750
|
breakeven: true,
|
|
66663
66751
|
strategy_commit: true,
|
|
66664
|
-
|
|
66752
|
+
order_sync: true,
|
|
66753
|
+
order_check: true,
|
|
66665
66754
|
risk: true,
|
|
66666
66755
|
info: true,
|
|
66667
66756
|
common_error: true,
|
|
@@ -67390,14 +67479,15 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
67390
67479
|
};
|
|
67391
67480
|
/**
|
|
67392
67481
|
* Creates a notification model for signal sync events.
|
|
67393
|
-
* Handles signal-open (
|
|
67482
|
+
* Handles signal-open (position order filled with orderType "active", or resting
|
|
67483
|
+
* entry order placed with orderType "schedule") and signal-close (position exited) actions.
|
|
67394
67484
|
* @param data - The signal sync contract data
|
|
67395
67485
|
* @returns NotificationModel for signal sync event
|
|
67396
67486
|
*/
|
|
67397
67487
|
const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
67398
67488
|
if (data.action === "signal-open") {
|
|
67399
67489
|
return {
|
|
67400
|
-
type: "
|
|
67490
|
+
type: "order_sync.open",
|
|
67401
67491
|
id: CREATE_KEY_FN$2(),
|
|
67402
67492
|
timestamp: data.timestamp,
|
|
67403
67493
|
backtest: data.backtest,
|
|
@@ -67405,6 +67495,7 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67405
67495
|
strategyName: data.strategyName,
|
|
67406
67496
|
exchangeName: data.exchangeName,
|
|
67407
67497
|
signalId: data.signalId,
|
|
67498
|
+
orderType: data.type,
|
|
67408
67499
|
currentPrice: data.currentPrice,
|
|
67409
67500
|
pnl: data.signal.pnl,
|
|
67410
67501
|
maxDrawdown: data.signal.maxDrawdown,
|
|
@@ -67442,7 +67533,7 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67442
67533
|
}
|
|
67443
67534
|
if (data.action === "signal-close") {
|
|
67444
67535
|
return {
|
|
67445
|
-
type: "
|
|
67536
|
+
type: "order_sync.close",
|
|
67446
67537
|
id: CREATE_KEY_FN$2(),
|
|
67447
67538
|
timestamp: data.timestamp,
|
|
67448
67539
|
backtest: data.backtest,
|
|
@@ -67450,6 +67541,7 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67450
67541
|
strategyName: data.strategyName,
|
|
67451
67542
|
exchangeName: data.exchangeName,
|
|
67452
67543
|
signalId: data.signalId,
|
|
67544
|
+
orderType: data.type,
|
|
67453
67545
|
currentPrice: data.currentPrice,
|
|
67454
67546
|
pnl: data.signal.pnl,
|
|
67455
67547
|
maxDrawdown: data.signal.maxDrawdown,
|
|
@@ -67487,6 +67579,54 @@ const CREATE_SIGNAL_SYNC_NOTIFICATION_FN = (data) => {
|
|
|
67487
67579
|
}
|
|
67488
67580
|
throw new Error(`Unrecognized signal sync action: ${get(data, "action")}`);
|
|
67489
67581
|
};
|
|
67582
|
+
/**
|
|
67583
|
+
* Creates a notification model for order-ping check events.
|
|
67584
|
+
* @param data - The order check contract data
|
|
67585
|
+
* @returns NotificationModel for signal sync check event
|
|
67586
|
+
*/
|
|
67587
|
+
const CREATE_ORDER_CHECK_NOTIFICATION_FN = (data) => ({
|
|
67588
|
+
type: "order_sync.check",
|
|
67589
|
+
id: CREATE_KEY_FN$2(),
|
|
67590
|
+
timestamp: data.timestamp,
|
|
67591
|
+
backtest: data.backtest,
|
|
67592
|
+
symbol: data.symbol,
|
|
67593
|
+
strategyName: data.strategyName,
|
|
67594
|
+
exchangeName: data.exchangeName,
|
|
67595
|
+
signalId: data.signalId,
|
|
67596
|
+
orderType: data.type,
|
|
67597
|
+
currentPrice: data.currentPrice,
|
|
67598
|
+
position: data.position,
|
|
67599
|
+
priceOpen: data.priceOpen,
|
|
67600
|
+
priceTakeProfit: data.priceTakeProfit,
|
|
67601
|
+
priceStopLoss: data.priceStopLoss,
|
|
67602
|
+
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
67603
|
+
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
67604
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
67605
|
+
totalEntries: data.totalEntries,
|
|
67606
|
+
totalPartials: data.totalPartials,
|
|
67607
|
+
pnl: data.pnl,
|
|
67608
|
+
maxDrawdown: data.maxDrawdown,
|
|
67609
|
+
peakProfit: data.peakProfit,
|
|
67610
|
+
pnlPercentage: data.pnl.pnlPercentage,
|
|
67611
|
+
pnlPriceOpen: data.pnl.priceOpen,
|
|
67612
|
+
pnlPriceClose: data.pnl.priceClose,
|
|
67613
|
+
pnlCost: data.pnl.pnlCost,
|
|
67614
|
+
pnlEntries: data.pnl.pnlEntries,
|
|
67615
|
+
peakProfitPriceOpen: data.peakProfit.priceOpen,
|
|
67616
|
+
peakProfitPriceClose: data.peakProfit.priceClose,
|
|
67617
|
+
peakProfitPercentage: data.peakProfit.pnlPercentage,
|
|
67618
|
+
peakProfitCost: data.peakProfit.pnlCost,
|
|
67619
|
+
peakProfitEntries: data.peakProfit.pnlEntries,
|
|
67620
|
+
maxDrawdownPriceOpen: data.maxDrawdown.priceOpen,
|
|
67621
|
+
maxDrawdownPriceClose: data.maxDrawdown.priceClose,
|
|
67622
|
+
maxDrawdownPercentage: data.maxDrawdown.pnlPercentage,
|
|
67623
|
+
maxDrawdownCost: data.maxDrawdown.pnlCost,
|
|
67624
|
+
maxDrawdownEntries: data.maxDrawdown.pnlEntries,
|
|
67625
|
+
scheduledAt: data.scheduledAt,
|
|
67626
|
+
pendingAt: data.pendingAt,
|
|
67627
|
+
note: data.signal.note,
|
|
67628
|
+
createdAt: data.timestamp,
|
|
67629
|
+
});
|
|
67490
67630
|
/**
|
|
67491
67631
|
* Creates a notification model for risk rejection events.
|
|
67492
67632
|
* @param data - The risk contract data
|
|
@@ -67604,6 +67744,7 @@ const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_LOSS = "Notificati
|
|
|
67604
67744
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationMemoryBacktestUtils.handleBreakeven";
|
|
67605
67745
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationMemoryBacktestUtils.handleStrategyCommit";
|
|
67606
67746
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_SYNC = "NotificationMemoryBacktestUtils.handleSync";
|
|
67747
|
+
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_CHECK = "NotificationMemoryBacktestUtils.handleCheck";
|
|
67607
67748
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_RISK = "NotificationMemoryBacktestUtils.handleRisk";
|
|
67608
67749
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ERROR = "NotificationMemoryBacktestUtils.handleError";
|
|
67609
67750
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationMemoryBacktestUtils.handleCriticalError";
|
|
@@ -67617,6 +67758,7 @@ const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationMe
|
|
|
67617
67758
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationMemoryLiveUtils.handleBreakeven";
|
|
67618
67759
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationMemoryLiveUtils.handleStrategyCommit";
|
|
67619
67760
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_SYNC = "NotificationMemoryLiveUtils.handleSync";
|
|
67761
|
+
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_CHECK = "NotificationMemoryLiveUtils.handleCheck";
|
|
67620
67762
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_RISK = "NotificationMemoryLiveUtils.handleRisk";
|
|
67621
67763
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_ERROR = "NotificationMemoryLiveUtils.handleError";
|
|
67622
67764
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationMemoryLiveUtils.handleCriticalError";
|
|
@@ -67646,6 +67788,7 @@ const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_LOSS = "Notificat
|
|
|
67646
67788
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationPersistBacktestUtils.handleBreakeven";
|
|
67647
67789
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationPersistBacktestUtils.handleStrategyCommit";
|
|
67648
67790
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_SYNC = "NotificationPersistBacktestUtils.handleSync";
|
|
67791
|
+
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_CHECK = "NotificationPersistBacktestUtils.handleCheck";
|
|
67649
67792
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_RISK = "NotificationPersistBacktestUtils.handleRisk";
|
|
67650
67793
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ERROR = "NotificationPersistBacktestUtils.handleError";
|
|
67651
67794
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationPersistBacktestUtils.handleCriticalError";
|
|
@@ -67661,6 +67804,7 @@ const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationP
|
|
|
67661
67804
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationPersistLiveUtils.handleBreakeven";
|
|
67662
67805
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_STRATEGY_COMMIT = "NotificationPersistLiveUtils.handleStrategyCommit";
|
|
67663
67806
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_SYNC = "NotificationPersistLiveUtils.handleSync";
|
|
67807
|
+
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_CHECK = "NotificationPersistLiveUtils.handleCheck";
|
|
67664
67808
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_RISK = "NotificationPersistLiveUtils.handleRisk";
|
|
67665
67809
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_ERROR = "NotificationPersistLiveUtils.handleError";
|
|
67666
67810
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_CRITICAL_ERROR = "NotificationPersistLiveUtils.handleCriticalError";
|
|
@@ -67759,15 +67903,23 @@ class NotificationMemoryBacktestUtils {
|
|
|
67759
67903
|
signalId: data.signalId,
|
|
67760
67904
|
action: data.action,
|
|
67761
67905
|
});
|
|
67762
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
67763
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
67764
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
67765
|
-
return;
|
|
67766
|
-
}
|
|
67767
67906
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
67768
67907
|
}, {
|
|
67769
67908
|
defaultValue: null,
|
|
67770
67909
|
});
|
|
67910
|
+
/**
|
|
67911
|
+
* Handles order-ping check event.
|
|
67912
|
+
* @param data - The order check contract data
|
|
67913
|
+
*/
|
|
67914
|
+
this.handleCheck = trycatch(async (data) => {
|
|
67915
|
+
backtest.loggerService.info(NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_CHECK, {
|
|
67916
|
+
signalId: data.signalId,
|
|
67917
|
+
type: data.type,
|
|
67918
|
+
});
|
|
67919
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
67920
|
+
}, {
|
|
67921
|
+
defaultValue: null,
|
|
67922
|
+
});
|
|
67771
67923
|
/**
|
|
67772
67924
|
* Handles risk rejection event.
|
|
67773
67925
|
* @param data - The risk contract data
|
|
@@ -67882,6 +68034,13 @@ class NotificationDummyBacktestUtils {
|
|
|
67882
68034
|
}, {
|
|
67883
68035
|
defaultValue: null,
|
|
67884
68036
|
});
|
|
68037
|
+
/**
|
|
68038
|
+
* No-op handler for order-ping check event.
|
|
68039
|
+
*/
|
|
68040
|
+
this.handleCheck = trycatch(async () => {
|
|
68041
|
+
}, {
|
|
68042
|
+
defaultValue: null,
|
|
68043
|
+
});
|
|
67885
68044
|
/**
|
|
67886
68045
|
* No-op handler for risk rejection event.
|
|
67887
68046
|
*/
|
|
@@ -68033,17 +68192,27 @@ class NotificationPersistBacktestUtils {
|
|
|
68033
68192
|
signalId: data.signalId,
|
|
68034
68193
|
action: data.action,
|
|
68035
68194
|
});
|
|
68036
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
68037
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
68038
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
68039
|
-
return;
|
|
68040
|
-
}
|
|
68041
68195
|
await this.waitForInit();
|
|
68042
68196
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
68043
68197
|
await this._updateNotifications();
|
|
68044
68198
|
}, {
|
|
68045
68199
|
defaultValue: null,
|
|
68046
68200
|
});
|
|
68201
|
+
/**
|
|
68202
|
+
* Handles order-ping check event.
|
|
68203
|
+
* @param data - The order check contract data
|
|
68204
|
+
*/
|
|
68205
|
+
this.handleCheck = trycatch(async (data) => {
|
|
68206
|
+
backtest.loggerService.info(NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_CHECK, {
|
|
68207
|
+
signalId: data.signalId,
|
|
68208
|
+
type: data.type,
|
|
68209
|
+
});
|
|
68210
|
+
await this.waitForInit();
|
|
68211
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
68212
|
+
await this._updateNotifications();
|
|
68213
|
+
}, {
|
|
68214
|
+
defaultValue: null,
|
|
68215
|
+
});
|
|
68047
68216
|
/**
|
|
68048
68217
|
* Handles risk rejection event.
|
|
68049
68218
|
* @param data - The risk contract data
|
|
@@ -68237,15 +68406,23 @@ class NotificationMemoryLiveUtils {
|
|
|
68237
68406
|
signalId: data.signalId,
|
|
68238
68407
|
action: data.action,
|
|
68239
68408
|
});
|
|
68240
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
68241
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
68242
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
68243
|
-
return;
|
|
68244
|
-
}
|
|
68245
68409
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
68246
68410
|
}, {
|
|
68247
68411
|
defaultValue: null,
|
|
68248
68412
|
});
|
|
68413
|
+
/**
|
|
68414
|
+
* Handles order-ping check event.
|
|
68415
|
+
* @param data - The order check contract data
|
|
68416
|
+
*/
|
|
68417
|
+
this.handleCheck = trycatch(async (data) => {
|
|
68418
|
+
backtest.loggerService.info(NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_CHECK, {
|
|
68419
|
+
signalId: data.signalId,
|
|
68420
|
+
type: data.type,
|
|
68421
|
+
});
|
|
68422
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
68423
|
+
}, {
|
|
68424
|
+
defaultValue: null,
|
|
68425
|
+
});
|
|
68249
68426
|
/**
|
|
68250
68427
|
* Handles risk rejection event.
|
|
68251
68428
|
* @param data - The risk contract data
|
|
@@ -68360,6 +68537,13 @@ class NotificationDummyLiveUtils {
|
|
|
68360
68537
|
}, {
|
|
68361
68538
|
defaultValue: null,
|
|
68362
68539
|
});
|
|
68540
|
+
/**
|
|
68541
|
+
* No-op handler for order-ping check event.
|
|
68542
|
+
*/
|
|
68543
|
+
this.handleCheck = trycatch(async () => {
|
|
68544
|
+
}, {
|
|
68545
|
+
defaultValue: null,
|
|
68546
|
+
});
|
|
68363
68547
|
/**
|
|
68364
68548
|
* No-op handler for risk rejection event.
|
|
68365
68549
|
*/
|
|
@@ -68512,17 +68696,27 @@ class NotificationPersistLiveUtils {
|
|
|
68512
68696
|
signalId: data.signalId,
|
|
68513
68697
|
action: data.action,
|
|
68514
68698
|
});
|
|
68515
|
-
// A "schedule" open is a resting-order placement, not a fill:
|
|
68516
|
-
// signal_sync.open means "limit order confirmed filled"
|
|
68517
|
-
if (data.action === "signal-open" && data.type === "schedule") {
|
|
68518
|
-
return;
|
|
68519
|
-
}
|
|
68520
68699
|
await this.waitForInit();
|
|
68521
68700
|
this._addNotification(CREATE_SIGNAL_SYNC_NOTIFICATION_FN(data));
|
|
68522
68701
|
await this._updateNotifications();
|
|
68523
68702
|
}, {
|
|
68524
68703
|
defaultValue: null,
|
|
68525
68704
|
});
|
|
68705
|
+
/**
|
|
68706
|
+
* Handles order-ping check event.
|
|
68707
|
+
* @param data - The order check contract data
|
|
68708
|
+
*/
|
|
68709
|
+
this.handleCheck = trycatch(async (data) => {
|
|
68710
|
+
backtest.loggerService.info(NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_CHECK, {
|
|
68711
|
+
signalId: data.signalId,
|
|
68712
|
+
type: data.type,
|
|
68713
|
+
});
|
|
68714
|
+
await this.waitForInit();
|
|
68715
|
+
this._addNotification(CREATE_ORDER_CHECK_NOTIFICATION_FN(data));
|
|
68716
|
+
await this._updateNotifications();
|
|
68717
|
+
}, {
|
|
68718
|
+
defaultValue: null,
|
|
68719
|
+
});
|
|
68526
68720
|
/**
|
|
68527
68721
|
* Handles risk rejection event.
|
|
68528
68722
|
* @param data - The risk contract data
|
|
@@ -68700,6 +68894,16 @@ class NotificationBacktestAdapter {
|
|
|
68700
68894
|
}, {
|
|
68701
68895
|
defaultValue: null,
|
|
68702
68896
|
});
|
|
68897
|
+
/**
|
|
68898
|
+
* Handles order-ping check event.
|
|
68899
|
+
* Proxies call to the underlying notification adapter.
|
|
68900
|
+
* @param data - The order check contract data
|
|
68901
|
+
*/
|
|
68902
|
+
this.handleCheck = trycatch(async (data) => {
|
|
68903
|
+
return await this.getInstance().handleCheck(data);
|
|
68904
|
+
}, {
|
|
68905
|
+
defaultValue: null,
|
|
68906
|
+
});
|
|
68703
68907
|
/**
|
|
68704
68908
|
* Handles risk rejection event.
|
|
68705
68909
|
* Proxies call to the underlying notification adapter.
|
|
@@ -68870,6 +69074,16 @@ class NotificationLiveAdapter {
|
|
|
68870
69074
|
}, {
|
|
68871
69075
|
defaultValue: null,
|
|
68872
69076
|
});
|
|
69077
|
+
/**
|
|
69078
|
+
* Handles order-ping check event.
|
|
69079
|
+
* Proxies call to the underlying notification adapter.
|
|
69080
|
+
* @param data - The order check contract data
|
|
69081
|
+
*/
|
|
69082
|
+
this.handleCheck = trycatch(async (data) => {
|
|
69083
|
+
return await this.getInstance().handleCheck(data);
|
|
69084
|
+
}, {
|
|
69085
|
+
defaultValue: null,
|
|
69086
|
+
});
|
|
68873
69087
|
/**
|
|
68874
69088
|
* Handles risk rejection event.
|
|
68875
69089
|
* Proxies call to the underlying notification adapter.
|
|
@@ -68983,16 +69197,40 @@ class NotificationAdapter {
|
|
|
68983
69197
|
*
|
|
68984
69198
|
* @returns Cleanup function that unsubscribes from all emitters
|
|
68985
69199
|
*/
|
|
68986
|
-
this.enable = singleshot(({ signal = false, info = false, partial_profit = false, partial_loss = false, breakeven = false, strategy_commit = false,
|
|
69200
|
+
this.enable = 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) => {
|
|
68987
69201
|
backtest.loggerService.info(NOTIFICATION_ADAPTER_METHOD_NAME_ENABLE);
|
|
68988
69202
|
let unLive;
|
|
68989
69203
|
let unBacktest;
|
|
68990
69204
|
{
|
|
69205
|
+
// Throttle state for order_sync.check: signalId -> timestamp of the last
|
|
69206
|
+
// emitted check notification. Entries are dropped on signal close/cancel
|
|
69207
|
+
// so the map cannot grow unbounded.
|
|
69208
|
+
const checkThrottleMap = new Map();
|
|
68991
69209
|
const unBacktestSignal = signalBacktestEmitter.subscribe(async (data) => {
|
|
68992
69210
|
if (signal) {
|
|
68993
69211
|
await NotificationBacktest.handleSignal(data);
|
|
68994
69212
|
}
|
|
68995
69213
|
});
|
|
69214
|
+
// Cleanup for checkThrottleMap. Tick-result emitters are NOT sufficient here:
|
|
69215
|
+
// out-of-band closes (commitClosePending, failed order ping, scheduled cancel
|
|
69216
|
+
// via commit) never reach signalBacktestEmitter/signalLiveEmitter. The
|
|
69217
|
+
// lifecycle channels cover every path: signalEventSubject "closed" fires for
|
|
69218
|
+
// all pending-signal closes (TP/SL/time_expired/user/ping), scheduleEventSubject
|
|
69219
|
+
// "cancelled" fires for scheduled signals removed before activation.
|
|
69220
|
+
const unBacktestSignalEvent = signalEventSubject
|
|
69221
|
+
.filter(({ backtest }) => backtest)
|
|
69222
|
+
.connect(async (event) => {
|
|
69223
|
+
if (event.action === "closed") {
|
|
69224
|
+
checkThrottleMap.delete(event.data.id);
|
|
69225
|
+
}
|
|
69226
|
+
});
|
|
69227
|
+
const unBacktestScheduleEvent = scheduleEventSubject
|
|
69228
|
+
.filter(({ backtest }) => backtest)
|
|
69229
|
+
.connect(async (event) => {
|
|
69230
|
+
if (event.action === "cancelled") {
|
|
69231
|
+
checkThrottleMap.delete(event.data.id);
|
|
69232
|
+
}
|
|
69233
|
+
});
|
|
68996
69234
|
const unBacktestPartialProfit = partialProfitSubject
|
|
68997
69235
|
.filter(({ backtest }) => backtest)
|
|
68998
69236
|
.connect(async (data) => {
|
|
@@ -69024,10 +69262,22 @@ class NotificationAdapter {
|
|
|
69024
69262
|
const unBacktestSync = syncSubject
|
|
69025
69263
|
.filter(({ backtest }) => backtest)
|
|
69026
69264
|
.connect(async (data) => {
|
|
69027
|
-
if (
|
|
69265
|
+
if (order_sync) {
|
|
69028
69266
|
await NotificationBacktest.handleSync(data);
|
|
69029
69267
|
}
|
|
69030
69268
|
});
|
|
69269
|
+
const unBacktestCheck = syncPendingSubject
|
|
69270
|
+
.filter(({ backtest }) => backtest)
|
|
69271
|
+
.connect(async (data) => {
|
|
69272
|
+
if (order_check) {
|
|
69273
|
+
const lastTimestamp = checkThrottleMap.get(data.signalId);
|
|
69274
|
+
if (lastTimestamp !== undefined && data.timestamp - lastTimestamp < GLOBAL_CONFIG.CC_NOTIFICATION_ORDER_CHECK_TTL) {
|
|
69275
|
+
return;
|
|
69276
|
+
}
|
|
69277
|
+
checkThrottleMap.set(data.signalId, data.timestamp);
|
|
69278
|
+
await NotificationBacktest.handleCheck(data);
|
|
69279
|
+
}
|
|
69280
|
+
});
|
|
69031
69281
|
const unBacktestRisk = riskSubject
|
|
69032
69282
|
.filter(({ backtest }) => backtest)
|
|
69033
69283
|
.connect(async (data) => {
|
|
@@ -69057,14 +69307,38 @@ class NotificationAdapter {
|
|
|
69057
69307
|
await NotificationBacktest.handleSignalNotify(data);
|
|
69058
69308
|
}
|
|
69059
69309
|
});
|
|
69060
|
-
unBacktest = compose(() => unBacktestSignal(), () => unBacktestPartialProfit(), () => unBacktestPartialLoss(), () => unBacktestBreakeven(), () => unBacktestStrategyCommit(), () => unBacktestSync(), () => unBacktestRisk(), () => unBacktestError(), () => unBacktestExit(), () => unBacktestValidation(), () => unBacktestSignalNotify());
|
|
69310
|
+
unBacktest = compose(() => unBacktestSignal(), () => unBacktestSignalEvent(), () => unBacktestScheduleEvent(), () => unBacktestPartialProfit(), () => unBacktestPartialLoss(), () => unBacktestBreakeven(), () => unBacktestStrategyCommit(), () => unBacktestSync(), () => unBacktestCheck(), () => unBacktestRisk(), () => unBacktestError(), () => unBacktestExit(), () => unBacktestValidation(), () => unBacktestSignalNotify(), () => checkThrottleMap.clear());
|
|
69061
69311
|
}
|
|
69062
69312
|
{
|
|
69313
|
+
// Throttle state for order_sync.check: signalId -> timestamp of the last
|
|
69314
|
+
// emitted check notification. Entries are dropped on signal close/cancel
|
|
69315
|
+
// so the map cannot grow unbounded.
|
|
69316
|
+
const checkThrottleMap = new Map();
|
|
69063
69317
|
const unLiveSignal = signalLiveEmitter.subscribe(async (data) => {
|
|
69064
69318
|
if (signal) {
|
|
69065
69319
|
await NotificationLive.handleSignal(data);
|
|
69066
69320
|
}
|
|
69067
69321
|
});
|
|
69322
|
+
// Cleanup for checkThrottleMap. Tick-result emitters are NOT sufficient here:
|
|
69323
|
+
// out-of-band closes (commitClosePending, failed order ping, scheduled cancel
|
|
69324
|
+
// via commit) never reach signalBacktestEmitter/signalLiveEmitter. The
|
|
69325
|
+
// lifecycle channels cover every path: signalEventSubject "closed" fires for
|
|
69326
|
+
// all pending-signal closes (TP/SL/time_expired/user/ping), scheduleEventSubject
|
|
69327
|
+
// "cancelled" fires for scheduled signals removed before activation.
|
|
69328
|
+
const unLiveSignalEvent = signalEventSubject
|
|
69329
|
+
.filter(({ backtest }) => !backtest)
|
|
69330
|
+
.connect(async (event) => {
|
|
69331
|
+
if (event.action === "closed") {
|
|
69332
|
+
checkThrottleMap.delete(event.data.id);
|
|
69333
|
+
}
|
|
69334
|
+
});
|
|
69335
|
+
const unLiveScheduleEvent = scheduleEventSubject
|
|
69336
|
+
.filter(({ backtest }) => !backtest)
|
|
69337
|
+
.connect(async (event) => {
|
|
69338
|
+
if (event.action === "cancelled") {
|
|
69339
|
+
checkThrottleMap.delete(event.data.id);
|
|
69340
|
+
}
|
|
69341
|
+
});
|
|
69068
69342
|
const unLivePartialProfit = partialProfitSubject
|
|
69069
69343
|
.filter(({ backtest }) => !backtest)
|
|
69070
69344
|
.connect(async (data) => {
|
|
@@ -69096,10 +69370,22 @@ class NotificationAdapter {
|
|
|
69096
69370
|
const unLiveSync = syncSubject
|
|
69097
69371
|
.filter(({ backtest }) => !backtest)
|
|
69098
69372
|
.connect(async (data) => {
|
|
69099
|
-
if (
|
|
69373
|
+
if (order_sync) {
|
|
69100
69374
|
await NotificationLive.handleSync(data);
|
|
69101
69375
|
}
|
|
69102
69376
|
});
|
|
69377
|
+
const unLiveCheck = syncPendingSubject
|
|
69378
|
+
.filter(({ backtest }) => !backtest)
|
|
69379
|
+
.connect(async (data) => {
|
|
69380
|
+
if (order_check) {
|
|
69381
|
+
const lastTimestamp = checkThrottleMap.get(data.signalId);
|
|
69382
|
+
if (lastTimestamp !== undefined && data.timestamp - lastTimestamp < GLOBAL_CONFIG.CC_NOTIFICATION_ORDER_CHECK_TTL) {
|
|
69383
|
+
return;
|
|
69384
|
+
}
|
|
69385
|
+
checkThrottleMap.set(data.signalId, data.timestamp);
|
|
69386
|
+
await NotificationLive.handleCheck(data);
|
|
69387
|
+
}
|
|
69388
|
+
});
|
|
69103
69389
|
const unLiveRisk = riskSubject
|
|
69104
69390
|
.filter(({ backtest }) => !backtest)
|
|
69105
69391
|
.connect(async (data) => {
|
|
@@ -69129,7 +69415,7 @@ class NotificationAdapter {
|
|
|
69129
69415
|
await NotificationLive.handleSignalNotify(data);
|
|
69130
69416
|
}
|
|
69131
69417
|
});
|
|
69132
|
-
unLive = compose(() => unLiveSignal(), () => unLivePartialProfit(), () => unLivePartialLoss(), () => unLiveBreakeven(), () => unLiveStrategyCommit(), () => unLiveSync(), () => unLiveRisk(), () => unLiveError(), () => unLiveExit(), () => unLiveValidation(), () => unLiveSignalNotify());
|
|
69418
|
+
unLive = compose(() => unLiveSignal(), () => unLiveSignalEvent(), () => unLiveScheduleEvent(), () => unLivePartialProfit(), () => unLivePartialLoss(), () => unLiveBreakeven(), () => unLiveStrategyCommit(), () => unLiveSync(), () => unLiveCheck(), () => unLiveRisk(), () => unLiveError(), () => unLiveExit(), () => unLiveValidation(), () => unLiveSignalNotify(), () => checkThrottleMap.clear());
|
|
69133
69419
|
}
|
|
69134
69420
|
return () => {
|
|
69135
69421
|
unLive();
|
|
@@ -70424,17 +70710,31 @@ const CRON_METHOD_NAME_DISPOSE = "CronUtils.dispose";
|
|
|
70424
70710
|
/**
|
|
70425
70711
|
* Watchdog timeout (ms) for a single cron handler invocation.
|
|
70426
70712
|
*
|
|
70427
|
-
* A
|
|
70428
|
-
* `_runEntry` races `entry.handler(info)`
|
|
70429
|
-
*
|
|
70430
|
-
* surfacing `failed = true`, logging
|
|
70431
|
-
* rolling back the watermark so the
|
|
70713
|
+
* A slot that does not settle within this window is treated as failed:
|
|
70714
|
+
* `_runEntry` races the runtime-info assembly plus `entry.handler(info)`
|
|
70715
|
+
* against a timer of this duration and, when the timer wins, rejects into the
|
|
70716
|
+
* same `catch` as any other handler error — surfacing `failed = true`, logging
|
|
70717
|
+
* a warning, and (for periodic entries) rolling back the watermark so the
|
|
70718
|
+
* boundary is retried on the next tick.
|
|
70432
70719
|
*
|
|
70433
70720
|
* This guards the `singlerun`-serialised tick pipeline against a handler that
|
|
70434
|
-
* never resolves (a lost `resolve`, a hung
|
|
70435
|
-
* own): without it such a handler would
|
|
70721
|
+
* never resolves (a lost `resolve`, a hung network call with no timeout of its
|
|
70722
|
+
* own): without it such a handler would hold its `_inFlight` slot forever and
|
|
70723
|
+
* `_tick`'s `Promise.all` would never settle, silently stalling every
|
|
70724
|
+
* subsequent lifecycle tick while the process stays alive and outwardly
|
|
70725
|
+
* healthy.
|
|
70726
|
+
*/
|
|
70727
|
+
const CRON_HANDLER_TIMEOUT = 900000;
|
|
70728
|
+
/**
|
|
70729
|
+
* Early-warning threshold (ms) for a single cron handler invocation.
|
|
70730
|
+
*
|
|
70731
|
+
* Purely observational: when a slot is still running this long after it was
|
|
70732
|
+
* opened, `_runEntry` logs a warning naming the entry — so a slow handler is
|
|
70733
|
+
* visible in the logs long before the {@link CRON_HANDLER_TIMEOUT} watchdog
|
|
70734
|
+
* forcibly fails it. Nothing is interrupted and no rollback happens at this
|
|
70735
|
+
* mark; the slot keeps running and may still succeed.
|
|
70436
70736
|
*/
|
|
70437
|
-
const
|
|
70737
|
+
const CRON_HANDLER_WARN_TIMEOUT = 120000;
|
|
70438
70738
|
/**
|
|
70439
70739
|
* Local logger instance.
|
|
70440
70740
|
*
|
|
@@ -70856,28 +71156,9 @@ class CronUtils {
|
|
|
70856
71156
|
}
|
|
70857
71157
|
taskList.push(pending);
|
|
70858
71158
|
}
|
|
70859
|
-
|
|
70860
|
-
|
|
70861
|
-
|
|
70862
|
-
// awaiting Promise.all so the singlerun pipeline stays serialised and no
|
|
70863
|
-
// duplicate/zombie slots are spawned — the timer only surfaces the stall.
|
|
70864
|
-
// Use a real setTimeout/clearTimeout (not sleep) so the alarm is cancelled
|
|
70865
|
-
// the instant Promise.all resolves, rather than lingering for the full
|
|
70866
|
-
// timeout on every fast tick.
|
|
70867
|
-
const timer = setTimeout(() => {
|
|
70868
|
-
const message = `${CRON_METHOD_NAME_TICK} timed out after ${CRON_HANDLER_TIMEOUT}ms`;
|
|
70869
|
-
const payload = { symbol, when, context };
|
|
70870
|
-
LOGGER_SERVICE$1.warn(message, payload);
|
|
70871
|
-
console.error(message, payload);
|
|
70872
|
-
errorEmitter.next(new Error(message));
|
|
70873
|
-
}, CRON_HANDLER_TIMEOUT);
|
|
70874
|
-
try {
|
|
70875
|
-
await Promise.all(taskList);
|
|
70876
|
-
}
|
|
70877
|
-
finally {
|
|
70878
|
-
clearTimeout(timer);
|
|
70879
|
-
}
|
|
70880
|
-
}
|
|
71159
|
+
// Every slot self-terminates via the `_runEntry` watchdog, so this settles
|
|
71160
|
+
// within CRON_HANDLER_TIMEOUT plus epsilon even when a handler hangs.
|
|
71161
|
+
await Promise.all(taskList);
|
|
70881
71162
|
// Roll back the watermark for any periodic slot THIS tick opened whose
|
|
70882
71163
|
// handler failed, so the next tick re-opens the same boundary and retries
|
|
70883
71164
|
// it — mirroring how a skipped boundary is later caught up. Restoring
|
|
@@ -71061,9 +71342,17 @@ class CronUtils {
|
|
|
71061
71342
|
*
|
|
71062
71343
|
* Assembles the {@link IRuntimeInfo} snapshot via
|
|
71063
71344
|
* `RuntimeMetaService.getRuntimeInfo(symbol, context, backtest)` and invokes
|
|
71064
|
-
* `entry.handler(info)
|
|
71065
|
-
*
|
|
71066
|
-
*
|
|
71345
|
+
* `entry.handler(info)`, racing both against the
|
|
71346
|
+
* {@link CRON_HANDLER_TIMEOUT} watchdog — a slot that does not settle in time
|
|
71347
|
+
* rejects into the same `catch` as any other error, so a hung handler (or a
|
|
71348
|
+
* hung price fetch inside `getRuntimeInfo`) can never hold the `_inFlight`
|
|
71349
|
+
* slot forever and stall the serialised tick pipeline. A slot still running
|
|
71350
|
+
* at {@link CRON_HANDLER_WARN_TIMEOUT} logs an observational warning first,
|
|
71351
|
+
* so a slow handler is visible in the logs well before the watchdog forcibly
|
|
71352
|
+
* fails it. Logs any error via
|
|
71353
|
+
* `console.error` and **returns** a `failed` boolean (`true` when the
|
|
71354
|
+
* handler — or the runtime-info assembly — threw or timed out) so the caller
|
|
71355
|
+
* (`_tick`) can roll back the periodic watermark of the
|
|
71067
71356
|
* slot it opened and retry that boundary. The error is **not** rethrown, so a
|
|
71068
71357
|
* failing handler never produces an unhandled rejection. Clears the
|
|
71069
71358
|
* `_inFlight` slot in `.finally()` so the next boundary produces a fresh
|
|
@@ -71088,9 +71377,37 @@ class CronUtils {
|
|
|
71088
71377
|
*/
|
|
71089
71378
|
async _runEntry(entry, symbol, alignedMs, slotKey, firedKey, backtest, context) {
|
|
71090
71379
|
let failed = false;
|
|
71380
|
+
let watchdog;
|
|
71381
|
+
// Observational early warning: fires while the slot is still running, long
|
|
71382
|
+
// before the watchdog below forcibly fails it. Cancelled in `finally`, so
|
|
71383
|
+
// it never fires for slots that settle in time.
|
|
71384
|
+
const slowAlarm = setTimeout(() => {
|
|
71385
|
+
const message = `${CRON_METHOD_NAME_TICK} entry "${entry.name}" still running after ${CRON_HANDLER_WARN_TIMEOUT}ms (watchdog at ${CRON_HANDLER_TIMEOUT}ms)`;
|
|
71386
|
+
const payload = { symbol, alignedMs };
|
|
71387
|
+
LOGGER_SERVICE$1.warn(message, payload);
|
|
71388
|
+
console.error(message, payload);
|
|
71389
|
+
}, CRON_HANDLER_WARN_TIMEOUT);
|
|
71091
71390
|
try {
|
|
71092
|
-
|
|
71093
|
-
|
|
71391
|
+
// The runtime-info assembly is raced alongside the handler: in live mode
|
|
71392
|
+
// getRuntimeInfo reaches out to the exchange for the current price, so it
|
|
71393
|
+
// can hang on a dead network exactly like a user handler can.
|
|
71394
|
+
const work = (async () => {
|
|
71395
|
+
const info = await RUNTIME_META_SERVICE.getRuntimeInfo(symbol, context, backtest);
|
|
71396
|
+
await entry.handler(info);
|
|
71397
|
+
})();
|
|
71398
|
+
// A timed-out slot abandons `work`; swallow its eventual rejection so a
|
|
71399
|
+
// zombie handler that fails later never surfaces as an unhandled
|
|
71400
|
+
// rejection. Its late success is equally unobserved: `failed` was already
|
|
71401
|
+
// reported and (for fire-once entries) the fired mark deliberately not set.
|
|
71402
|
+
work.catch(() => void 0);
|
|
71403
|
+
await Promise.race([
|
|
71404
|
+
work,
|
|
71405
|
+
new Promise((_, reject) => {
|
|
71406
|
+
watchdog = setTimeout(() => {
|
|
71407
|
+
reject(new Error(`Cron entry "${entry.name}" timed out after ${CRON_HANDLER_TIMEOUT}ms`));
|
|
71408
|
+
}, CRON_HANDLER_TIMEOUT);
|
|
71409
|
+
}),
|
|
71410
|
+
]);
|
|
71094
71411
|
}
|
|
71095
71412
|
catch (error) {
|
|
71096
71413
|
failed = true;
|
|
@@ -71106,6 +71423,8 @@ class CronUtils {
|
|
|
71106
71423
|
errorEmitter.next(error);
|
|
71107
71424
|
}
|
|
71108
71425
|
finally {
|
|
71426
|
+
clearTimeout(slowAlarm);
|
|
71427
|
+
clearTimeout(watchdog);
|
|
71109
71428
|
this._inFlight.delete(slotKey);
|
|
71110
71429
|
if (!failed && firedKey !== null) {
|
|
71111
71430
|
this._firedOnce.add(firedKey);
|