backtest-kit 14.0.0 → 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +1640 -54
- package/build/index.mjs +1635 -55
- package/package.json +1 -1
- package/types.d.ts +1516 -40
package/build/index.mjs
CHANGED
|
@@ -748,6 +748,22 @@ const riskSubject = new Subject();
|
|
|
748
748
|
* Allows users to track scheduled signal lifecycle and implement custom cancellation logic.
|
|
749
749
|
*/
|
|
750
750
|
const schedulePingSubject = new Subject();
|
|
751
|
+
/**
|
|
752
|
+
* Scheduled signal lifecycle emitter (creation and cancellation).
|
|
753
|
+
* Emits when a scheduled signal is created (action "scheduled") or cancelled before
|
|
754
|
+
* activation (action "cancelled": timeout / price_reject / user) during tick()/backtest().
|
|
755
|
+
*
|
|
756
|
+
* The scheduled -> active transition (activation) is intentionally NOT emitted here — that
|
|
757
|
+
* produces an "opened" signal on the regular signal emitters instead.
|
|
758
|
+
*/
|
|
759
|
+
const scheduleEventSubject = new Subject();
|
|
760
|
+
/**
|
|
761
|
+
* Pending signal lifecycle emitter (open and close).
|
|
762
|
+
* Emits when a pending position is opened (action "opened": new signal / immediate / scheduled
|
|
763
|
+
* or user activation) or closed (action "closed" with closeReason take_profit / stop_loss /
|
|
764
|
+
* time_expired / closed) during tick()/backtest().
|
|
765
|
+
*/
|
|
766
|
+
const signalEventSubject = new Subject();
|
|
751
767
|
/**
|
|
752
768
|
* Active ping emitter for active pending signal monitoring events.
|
|
753
769
|
* Emits every minute when an active pending signal is being monitored.
|
|
@@ -838,10 +854,12 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
838
854
|
progressBacktestEmitter: progressBacktestEmitter,
|
|
839
855
|
progressWalkerEmitter: progressWalkerEmitter,
|
|
840
856
|
riskSubject: riskSubject,
|
|
857
|
+
scheduleEventSubject: scheduleEventSubject,
|
|
841
858
|
schedulePingSubject: schedulePingSubject,
|
|
842
859
|
shutdownEmitter: shutdownEmitter,
|
|
843
860
|
signalBacktestEmitter: signalBacktestEmitter,
|
|
844
861
|
signalEmitter: signalEmitter,
|
|
862
|
+
signalEventSubject: signalEventSubject,
|
|
845
863
|
signalLiveEmitter: signalLiveEmitter,
|
|
846
864
|
signalNotifySubject: signalNotifySubject,
|
|
847
865
|
strategyCommitSubject: strategyCommitSubject,
|
|
@@ -1909,7 +1927,7 @@ class PersistStrategyInstance {
|
|
|
1909
1927
|
const strategyData = await this._storage.readValue(PersistStrategyInstance.STORAGE_KEY);
|
|
1910
1928
|
// JSON serializes Infinity as null, so an eternal-hold signal
|
|
1911
1929
|
// (minuteEstimatedTime: Infinity) reads back as null — restore it.
|
|
1912
|
-
for (const signal of [strategyData.closedSignal, strategyData.cancelledSignal, strategyData.activatedSignal]) {
|
|
1930
|
+
for (const signal of [strategyData.closedSignal, strategyData.cancelledSignal, strategyData.activatedSignal, strategyData.takeProfitSignal, strategyData.stopLossSignal]) {
|
|
1913
1931
|
if (signal && signal.minuteEstimatedTime == null) {
|
|
1914
1932
|
signal.minuteEstimatedTime = Infinity;
|
|
1915
1933
|
}
|
|
@@ -7653,6 +7671,10 @@ const WAIT_FOR_INIT_FN$4 = async (self) => {
|
|
|
7653
7671
|
self._closedSignal = strategyData.closedSignal;
|
|
7654
7672
|
self._cancelledSignal = strategyData.cancelledSignal;
|
|
7655
7673
|
self._activatedSignal = strategyData.activatedSignal;
|
|
7674
|
+
// Deferred broker-confirmed TP/SL fills snapshot the pending signal and clear it (like
|
|
7675
|
+
// _closedSignal), so they stand on their own and are restored unconditionally too.
|
|
7676
|
+
self._takeProfitSignal = strategyData.takeProfitSignal;
|
|
7677
|
+
self._stopLossSignal = strategyData.stopLossSignal;
|
|
7656
7678
|
}
|
|
7657
7679
|
// Restore pending signal
|
|
7658
7680
|
const pendingSignal = await PersistSignalAdapter.readSignalData(self.params.execution.context.symbol, self.params.strategyName, self.params.exchangeName);
|
|
@@ -7733,6 +7755,8 @@ const PERSIST_STRATEGY_FN = async (self) => {
|
|
|
7733
7755
|
closedSignal: self._closedSignal,
|
|
7734
7756
|
cancelledSignal: self._cancelledSignal,
|
|
7735
7757
|
activatedSignal: self._activatedSignal,
|
|
7758
|
+
takeProfitSignal: self._takeProfitSignal,
|
|
7759
|
+
stopLossSignal: self._stopLossSignal,
|
|
7736
7760
|
}, self.params.symbol, self.params.strategyName, self.params.exchangeName);
|
|
7737
7761
|
};
|
|
7738
7762
|
const PARTIAL_PROFIT_FN = (self, signal, percentToClose, currentPrice, timestamp) => {
|
|
@@ -8266,6 +8290,7 @@ const CHECK_SCHEDULED_SIGNAL_TIMEOUT_FN = async (self, scheduled, currentPrice)
|
|
|
8266
8290
|
maxMinutes: GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES,
|
|
8267
8291
|
});
|
|
8268
8292
|
await self.setScheduledSignal(null);
|
|
8293
|
+
await CALL_SCHEDULE_EVENT_FN$1(self, "cancelled", scheduled, currentPrice, currentTime, "timeout");
|
|
8269
8294
|
await CALL_CANCEL_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
8270
8295
|
const result = {
|
|
8271
8296
|
action: "cancelled",
|
|
@@ -8322,6 +8347,7 @@ const CANCEL_SCHEDULED_SIGNAL_BY_STOPLOSS_FN = async (self, scheduled, currentPr
|
|
|
8322
8347
|
});
|
|
8323
8348
|
await self.setScheduledSignal(null);
|
|
8324
8349
|
const currentTime = self.params.execution.context.when.getTime();
|
|
8350
|
+
await CALL_SCHEDULE_EVENT_FN$1(self, "cancelled", scheduled, currentPrice, currentTime, "price_reject");
|
|
8325
8351
|
await CALL_CANCEL_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
8326
8352
|
const result = {
|
|
8327
8353
|
action: "cancelled",
|
|
@@ -8415,6 +8441,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
|
|
|
8415
8441
|
await self.setScheduledSignal(null);
|
|
8416
8442
|
await self.setPendingSignal(activatedSignal, activatedSignal.priceOpen);
|
|
8417
8443
|
await CALL_RISK_ADD_SIGNAL_FN(self, self.params.execution.context.symbol, activatedSignal, activationTime, self.params.execution.context.backtest);
|
|
8444
|
+
await CALL_SIGNAL_EVENT_FN(self, "opened", self._pendingSignal, self._pendingSignal.priceOpen, activationTime);
|
|
8418
8445
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, self._pendingSignal, self._pendingSignal.priceOpen, activationTime, self.params.execution.context.backtest);
|
|
8419
8446
|
const result = {
|
|
8420
8447
|
action: "opened",
|
|
@@ -8549,6 +8576,53 @@ const CALL_SCHEDULE_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signa
|
|
|
8549
8576
|
errorEmitter.next(error);
|
|
8550
8577
|
},
|
|
8551
8578
|
});
|
|
8579
|
+
/**
|
|
8580
|
+
* Emits a scheduled signal lifecycle event (creation / cancellation) to onScheduleEvent.
|
|
8581
|
+
*
|
|
8582
|
+
* Called when a scheduled signal is created (action "scheduled") or cancelled before activation
|
|
8583
|
+
* (action "cancelled" with reason timeout / price_reject / user). The scheduled -> active
|
|
8584
|
+
* transition is intentionally NOT emitted through this path.
|
|
8585
|
+
*
|
|
8586
|
+
* Unlike the CALL_*_CALLBACKS_FN helpers this does not run inside beginTime/runInContext: the
|
|
8587
|
+
* timestamp is passed explicitly by the caller (tick when / candle timestamp) and forwarded to
|
|
8588
|
+
* the connection-level emitter, mirroring how onSchedulePing carries its own timestamp.
|
|
8589
|
+
*/
|
|
8590
|
+
const CALL_SCHEDULE_EVENT_FN$1 = trycatch(async (self, action, signal, currentPrice, timestamp, reason) => {
|
|
8591
|
+
await self.params.onScheduleEvent(action, self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, TO_PUBLIC_SIGNAL("scheduled", signal, currentPrice), currentPrice, self.params.execution.context.backtest, timestamp, reason);
|
|
8592
|
+
}, {
|
|
8593
|
+
fallback: (error, self) => {
|
|
8594
|
+
const message = "ClientStrategy CALL_SCHEDULE_EVENT_FN thrown";
|
|
8595
|
+
const payload = {
|
|
8596
|
+
error: errorData(error),
|
|
8597
|
+
message: getErrorMessage(error),
|
|
8598
|
+
};
|
|
8599
|
+
self.params.logger.warn(message, payload);
|
|
8600
|
+
console.warn(message, payload);
|
|
8601
|
+
errorEmitter.next(error);
|
|
8602
|
+
},
|
|
8603
|
+
});
|
|
8604
|
+
/**
|
|
8605
|
+
* Emits a pending signal lifecycle event (open / close) to onSignalEvent.
|
|
8606
|
+
*
|
|
8607
|
+
* Called when a pending position is opened (action "opened") or closed (action "closed" with a
|
|
8608
|
+
* closeReason). Like CALL_SCHEDULE_EVENT_FN it does not run inside beginTime/runInContext: the
|
|
8609
|
+
* timestamp is passed explicitly by the caller (tick when / candle timestamp) and forwarded to
|
|
8610
|
+
* the connection-level emitter.
|
|
8611
|
+
*/
|
|
8612
|
+
const CALL_SIGNAL_EVENT_FN = trycatch(async (self, action, signal, currentPrice, timestamp, closeReason) => {
|
|
8613
|
+
await self.params.onSignalEvent(action, self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, TO_PUBLIC_SIGNAL("pending", signal, currentPrice), currentPrice, self.params.execution.context.backtest, timestamp, closeReason);
|
|
8614
|
+
}, {
|
|
8615
|
+
fallback: (error, self) => {
|
|
8616
|
+
const message = "ClientStrategy CALL_SIGNAL_EVENT_FN thrown";
|
|
8617
|
+
const payload = {
|
|
8618
|
+
error: errorData(error),
|
|
8619
|
+
message: getErrorMessage(error),
|
|
8620
|
+
};
|
|
8621
|
+
self.params.logger.warn(message, payload);
|
|
8622
|
+
console.warn(message, payload);
|
|
8623
|
+
errorEmitter.next(error);
|
|
8624
|
+
},
|
|
8625
|
+
});
|
|
8552
8626
|
const CALL_CANCEL_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
|
|
8553
8627
|
await ExecutionContextService.runInContext(async () => {
|
|
8554
8628
|
if (self.params.callbacks?.onCancel) {
|
|
@@ -8924,6 +8998,7 @@ const OPEN_NEW_SCHEDULED_SIGNAL_FN = async (self, signal) => {
|
|
|
8924
8998
|
priceOpen: signal.priceOpen,
|
|
8925
8999
|
currentPrice: currentPrice,
|
|
8926
9000
|
});
|
|
9001
|
+
await CALL_SCHEDULE_EVENT_FN$1(self, "scheduled", signal, currentPrice, currentTime);
|
|
8927
9002
|
await CALL_SCHEDULE_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
8928
9003
|
const result = {
|
|
8929
9004
|
action: "scheduled",
|
|
@@ -8954,6 +9029,7 @@ const OPEN_NEW_PENDING_SIGNAL_FN = async (self, signal) => {
|
|
|
8954
9029
|
return null;
|
|
8955
9030
|
}
|
|
8956
9031
|
await CALL_RISK_ADD_SIGNAL_FN(self, self.params.execution.context.symbol, signal, currentTime, self.params.execution.context.backtest);
|
|
9032
|
+
await CALL_SIGNAL_EVENT_FN(self, "opened", signal, signal.priceOpen, currentTime);
|
|
8957
9033
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, signal.priceOpen, currentTime, self.params.execution.context.backtest);
|
|
8958
9034
|
const result = {
|
|
8959
9035
|
action: "opened",
|
|
@@ -9020,6 +9096,7 @@ const CLOSE_PENDING_SIGNAL_FN = async (self, signal, currentPrice, closeReason)
|
|
|
9020
9096
|
priceClose: currentPrice,
|
|
9021
9097
|
pnlPercentage: publicSignal.pnl.pnlPercentage,
|
|
9022
9098
|
});
|
|
9099
|
+
await CALL_SIGNAL_EVENT_FN(self, "closed", signal, currentPrice, currentTime, closeReason);
|
|
9023
9100
|
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
9024
9101
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
9025
9102
|
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
@@ -9061,6 +9138,7 @@ const CLOSE_PENDING_SIGNAL_AS_CLOSED_FN = async (self, signal, currentPrice) =>
|
|
|
9061
9138
|
priceClose: currentPrice,
|
|
9062
9139
|
pnlPercentage: publicSignal.pnl.pnlPercentage,
|
|
9063
9140
|
});
|
|
9141
|
+
await CALL_SIGNAL_EVENT_FN(self, "closed", signal, currentPrice, currentTime, "closed");
|
|
9064
9142
|
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
9065
9143
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
9066
9144
|
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
@@ -9231,6 +9309,7 @@ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePr
|
|
|
9231
9309
|
});
|
|
9232
9310
|
await self.setScheduledSignal(null);
|
|
9233
9311
|
const publicSignal = TO_PUBLIC_SIGNAL("scheduled", scheduled, averagePrice);
|
|
9312
|
+
await CALL_SCHEDULE_EVENT_FN$1(self, "cancelled", scheduled, averagePrice, closeTimestamp, reason);
|
|
9234
9313
|
if (reason === "user") {
|
|
9235
9314
|
await CALL_COMMIT_FN(self, {
|
|
9236
9315
|
action: "cancel-scheduled",
|
|
@@ -9343,6 +9422,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, activat
|
|
|
9343
9422
|
await self.setScheduledSignal(null);
|
|
9344
9423
|
await self.setPendingSignal(activatedSignal, activatedSignal.priceOpen);
|
|
9345
9424
|
await CALL_RISK_ADD_SIGNAL_FN(self, self.params.execution.context.symbol, activatedSignal, activationTime, self.params.execution.context.backtest);
|
|
9425
|
+
await CALL_SIGNAL_EVENT_FN(self, "opened", activatedSignal, activatedSignal.priceOpen, activationTime);
|
|
9346
9426
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, activatedSignal, activatedSignal.priceOpen, activationTime, self.params.execution.context.backtest);
|
|
9347
9427
|
await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, activatedSignal, activationTime, self.params.execution.context.backtest);
|
|
9348
9428
|
return true;
|
|
@@ -9373,6 +9453,7 @@ const CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, signal, averagePrice, c
|
|
|
9373
9453
|
if (closeReason === "time_expired" && publicSignal.pnl.pnlPercentage < 0) {
|
|
9374
9454
|
self.params.logger.warn(`ClientStrategy backtest: Signal closed with loss (time_expired), PNL: ${publicSignal.pnl.pnlPercentage.toFixed(2)}%`);
|
|
9375
9455
|
}
|
|
9456
|
+
await CALL_SIGNAL_EVENT_FN(self, "closed", signal, averagePrice, closeTimestamp, closeReason);
|
|
9376
9457
|
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
9377
9458
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
9378
9459
|
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, signal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
@@ -9431,6 +9512,7 @@ const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, aver
|
|
|
9431
9512
|
signal: publicSignal,
|
|
9432
9513
|
note: closedSignal.closeNote ?? closedSignal.note,
|
|
9433
9514
|
});
|
|
9515
|
+
await CALL_SIGNAL_EVENT_FN(self, "closed", closedSignal, averagePrice, closeTimestamp, "closed");
|
|
9434
9516
|
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
9435
9517
|
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
9436
9518
|
await CALL_BREAKEVEN_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
@@ -9453,6 +9535,79 @@ const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, aver
|
|
|
9453
9535
|
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, closeTimestamp, self.params.execution.context.backtest);
|
|
9454
9536
|
return result;
|
|
9455
9537
|
};
|
|
9538
|
+
/**
|
|
9539
|
+
* Closes a deferred broker-confirmed TP/SL fill (createTakeProfit / createStopLoss).
|
|
9540
|
+
*
|
|
9541
|
+
* The exchange and the strategy are parallel states: ClientStrategy evaluates TP/SL against VWAP,
|
|
9542
|
+
* but the real order may fill on candle high/low. When the broker confirms such a fill out of
|
|
9543
|
+
* context, the pending signal is snapshotted into _takeProfitSignal / _stopLossSignal and the next
|
|
9544
|
+
* tick()/backtest() drains it here, closing with the matching closeReason at the effective TP/SL
|
|
9545
|
+
* level (trailing override if set) — bypassing the VWAP completion check.
|
|
9546
|
+
*
|
|
9547
|
+
* Shared by live tick and backtest: the caller passes the close timestamp explicitly (execution
|
|
9548
|
+
* context when in live, candle timestamp in backtest). Mirrors CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN
|
|
9549
|
+
* (commit action "close-pending", carrying closeId/note) but with closeReason take_profit / stop_loss.
|
|
9550
|
+
*
|
|
9551
|
+
* Like the deferred close, the fill snapshot is already established by the broker, so it does NOT
|
|
9552
|
+
* re-confirm via onSignalSync. _takeProfitSignal / _stopLossSignal is cleared and re-persisted by
|
|
9553
|
+
* the caller after draining.
|
|
9554
|
+
*/
|
|
9555
|
+
const CLOSE_PENDING_SIGNAL_AS_FILL_FN = async (self, filledSignal, closeReason, closeTimestamp) => {
|
|
9556
|
+
const closePrice = closeReason === "take_profit"
|
|
9557
|
+
? (filledSignal._trailingPriceTakeProfit ?? filledSignal.priceTakeProfit)
|
|
9558
|
+
: (filledSignal._trailingPriceStopLoss ?? filledSignal.priceStopLoss);
|
|
9559
|
+
const publicSignal = TO_PUBLIC_SIGNAL("pending", filledSignal, closePrice);
|
|
9560
|
+
self.params.logger.info(`ClientStrategy signal ${closeReason} by broker-confirmed fill (createTakeProfit/createStopLoss)`, {
|
|
9561
|
+
symbol: self.params.execution.context.symbol,
|
|
9562
|
+
signalId: filledSignal.id,
|
|
9563
|
+
closeReason,
|
|
9564
|
+
priceClose: closePrice,
|
|
9565
|
+
pnlPercentage: publicSignal.pnl.pnlPercentage,
|
|
9566
|
+
});
|
|
9567
|
+
await CALL_COMMIT_FN(self, {
|
|
9568
|
+
action: "close-pending",
|
|
9569
|
+
symbol: self.params.execution.context.symbol,
|
|
9570
|
+
strategyName: self.params.strategyName,
|
|
9571
|
+
exchangeName: self.params.exchangeName,
|
|
9572
|
+
frameName: self.params.frameName,
|
|
9573
|
+
signalId: filledSignal.id,
|
|
9574
|
+
backtest: self.params.execution.context.backtest,
|
|
9575
|
+
closeId: filledSignal.closeId,
|
|
9576
|
+
timestamp: closeTimestamp,
|
|
9577
|
+
totalEntries: filledSignal._entry?.length ?? 1,
|
|
9578
|
+
totalPartials: filledSignal._partial?.length ?? 0,
|
|
9579
|
+
originalPriceOpen: filledSignal.priceOpen,
|
|
9580
|
+
pnl: publicSignal.pnl,
|
|
9581
|
+
maxDrawdown: publicSignal.maxDrawdown,
|
|
9582
|
+
peakProfit: publicSignal.peakProfit,
|
|
9583
|
+
signal: publicSignal,
|
|
9584
|
+
note: filledSignal.closeNote ?? filledSignal.note,
|
|
9585
|
+
});
|
|
9586
|
+
await CALL_SIGNAL_EVENT_FN(self, "closed", filledSignal, closePrice, closeTimestamp, closeReason);
|
|
9587
|
+
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, filledSignal, closePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
9588
|
+
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
9589
|
+
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, filledSignal, closePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
9590
|
+
// КРИТИЧНО: Очищаем состояние ClientBreakeven при закрытии позиции
|
|
9591
|
+
await CALL_BREAKEVEN_CLEAR_FN(self, self.params.execution.context.symbol, filledSignal, closePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
9592
|
+
await CALL_RISK_REMOVE_SIGNAL_FN(self, self.params.execution.context.symbol, closeTimestamp, self.params.execution.context.backtest);
|
|
9593
|
+
const result = {
|
|
9594
|
+
action: "closed",
|
|
9595
|
+
signal: publicSignal,
|
|
9596
|
+
currentPrice: closePrice,
|
|
9597
|
+
closeReason,
|
|
9598
|
+
closeTimestamp,
|
|
9599
|
+
pnl: publicSignal.pnl,
|
|
9600
|
+
strategyName: self.params.method.context.strategyName,
|
|
9601
|
+
exchangeName: self.params.method.context.exchangeName,
|
|
9602
|
+
frameName: self.params.method.context.frameName,
|
|
9603
|
+
symbol: self.params.execution.context.symbol,
|
|
9604
|
+
backtest: self.params.execution.context.backtest,
|
|
9605
|
+
closeId: filledSignal.closeId,
|
|
9606
|
+
createdAt: closeTimestamp,
|
|
9607
|
+
};
|
|
9608
|
+
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, closeTimestamp, self.params.execution.context.backtest);
|
|
9609
|
+
return result;
|
|
9610
|
+
};
|
|
9456
9611
|
const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, frameEndTime) => {
|
|
9457
9612
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
9458
9613
|
const maxTimeToWait = GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES * 60 * 1000;
|
|
@@ -9575,6 +9730,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
|
|
|
9575
9730
|
totalPartials: publicSignalForCommit.totalPartials,
|
|
9576
9731
|
note: activatedSignal.activateNote ?? publicSignalForCommit.note,
|
|
9577
9732
|
});
|
|
9733
|
+
await CALL_SIGNAL_EVENT_FN(self, "opened", pendingSignal, pendingSignal.priceOpen, candle.timestamp);
|
|
9578
9734
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
|
|
9579
9735
|
await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
|
|
9580
9736
|
return { outcome: "activated", activationIndex: i };
|
|
@@ -9670,6 +9826,20 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles, frameEnd
|
|
|
9670
9826
|
}
|
|
9671
9827
|
continue;
|
|
9672
9828
|
}
|
|
9829
|
+
// КРИТИЧНО: Проверяем broker-confirmed TP fill через createTakeProfit() (напр. из onActivePing).
|
|
9830
|
+
// Закрываем по эффективному уровню TP, минуя VWAP-проверку. Sync не пере-подтверждается.
|
|
9831
|
+
if (self._takeProfitSignal) {
|
|
9832
|
+
const filledSignal = self._takeProfitSignal;
|
|
9833
|
+
self._takeProfitSignal = null;
|
|
9834
|
+
return await CLOSE_PENDING_SIGNAL_AS_FILL_FN(self, filledSignal, "take_profit", currentCandleTimestamp);
|
|
9835
|
+
}
|
|
9836
|
+
// КРИТИЧНО: Проверяем broker-confirmed SL fill через createStopLoss() (напр. из onActivePing).
|
|
9837
|
+
// Закрываем по эффективному уровню SL, минуя VWAP-проверку. Sync не пере-подтверждается.
|
|
9838
|
+
if (self._stopLossSignal) {
|
|
9839
|
+
const filledSignal = self._stopLossSignal;
|
|
9840
|
+
self._stopLossSignal = null;
|
|
9841
|
+
return await CLOSE_PENDING_SIGNAL_AS_FILL_FN(self, filledSignal, "stop_loss", currentCandleTimestamp);
|
|
9842
|
+
}
|
|
9673
9843
|
let shouldClose = false;
|
|
9674
9844
|
let closeReason;
|
|
9675
9845
|
// Check time expiration FIRST (КРИТИЧНО!)
|
|
@@ -9905,6 +10075,20 @@ class ClientStrategy {
|
|
|
9905
10075
|
this._cancelledSignal = null;
|
|
9906
10076
|
this._closedSignal = null;
|
|
9907
10077
|
this._activatedSignal = null;
|
|
10078
|
+
/**
|
|
10079
|
+
* Deferred broker-confirmed take-profit fill (set via createTakeProfit). When non-null, the
|
|
10080
|
+
* exchange reported the TP order was actually filled (e.g. by candle high/low) — the next
|
|
10081
|
+
* tick()/backtest() drains it and closes the pending position with closeReason "take_profit"
|
|
10082
|
+
* at the effective take-profit level, bypassing the VWAP-based TP check.
|
|
10083
|
+
*/
|
|
10084
|
+
this._takeProfitSignal = null;
|
|
10085
|
+
/**
|
|
10086
|
+
* Deferred broker-confirmed stop-loss fill (set via createStopLoss). When non-null, the
|
|
10087
|
+
* exchange reported the SL order was actually filled (e.g. by candle high/low) — the next
|
|
10088
|
+
* tick()/backtest() drains it and closes the pending position with closeReason "stop_loss"
|
|
10089
|
+
* at the effective stop-loss level, bypassing the VWAP-based SL check.
|
|
10090
|
+
*/
|
|
10091
|
+
this._stopLossSignal = null;
|
|
9908
10092
|
/**
|
|
9909
10093
|
* User-supplied signal DTO to be consumed by the next GET_SIGNAL_FN tick instead of
|
|
9910
10094
|
* params.getSignal. Set via createSignal. When non-null, params.getSignal is NOT called
|
|
@@ -9978,6 +10162,10 @@ class ClientStrategy {
|
|
|
9978
10162
|
// - при null: сигнал закрыт по TP/SL/timeout, флаг больше не нужен
|
|
9979
10163
|
// - при новом сигнале: флаг от предыдущего сигнала не должен влиять на новый
|
|
9980
10164
|
this._closedSignal = null;
|
|
10165
|
+
// КРИТИЧНО: Так же сбрасываем отложенные broker-confirmed TP/SL fills — закрытие позиции
|
|
10166
|
+
// любым путём делает их неактуальными, а новая позиция не должна унаследовать чужой fill.
|
|
10167
|
+
this._takeProfitSignal = null;
|
|
10168
|
+
this._stopLossSignal = null;
|
|
9981
10169
|
// ЗАЩИТА ИНВАРИАНТА: При установке нового pending сигнала очищаем scheduled
|
|
9982
10170
|
// Не может быть одновременно pending И scheduled (взаимоисключающие состояния)
|
|
9983
10171
|
// При null: scheduled может существовать (новый сигнал после закрытия позиции)
|
|
@@ -10871,6 +11059,7 @@ class ClientStrategy {
|
|
|
10871
11059
|
signal: publicSignal,
|
|
10872
11060
|
note: cancelledSignal.cancelNote ?? cancelledSignal.note,
|
|
10873
11061
|
});
|
|
11062
|
+
await CALL_SCHEDULE_EVENT_FN$1(this, "cancelled", cancelledSignal, currentPrice, currentTime, "user");
|
|
10874
11063
|
// Call onCancel callback
|
|
10875
11064
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
10876
11065
|
const result = {
|
|
@@ -10931,6 +11120,7 @@ class ClientStrategy {
|
|
|
10931
11120
|
signal: publicSignal,
|
|
10932
11121
|
note: closedSignal.closeNote ?? closedSignal.note,
|
|
10933
11122
|
});
|
|
11123
|
+
await CALL_SIGNAL_EVENT_FN(this, "closed", closedSignal, currentPrice, currentTime, "closed");
|
|
10934
11124
|
// Call onClose callback
|
|
10935
11125
|
await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
10936
11126
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
@@ -10956,6 +11146,32 @@ class ClientStrategy {
|
|
|
10956
11146
|
await CALL_TICK_CALLBACKS_FN(this, this.params.execution.context.symbol, result, currentTime, this.params.execution.context.backtest);
|
|
10957
11147
|
return result;
|
|
10958
11148
|
}
|
|
11149
|
+
// Check if a broker-confirmed take-profit fill is awaiting (createTakeProfit) - close once.
|
|
11150
|
+
// The exchange filled the TP order (e.g. by high/low); close bypassing the VWAP TP check.
|
|
11151
|
+
if (this._takeProfitSignal) {
|
|
11152
|
+
const filledSignal = this._takeProfitSignal;
|
|
11153
|
+
this._takeProfitSignal = null; // Clear after draining
|
|
11154
|
+
// Persist the cleared deferred state so the drained flag is not replayed on restart
|
|
11155
|
+
await PERSIST_STRATEGY_FN(this);
|
|
11156
|
+
this.params.logger.info("ClientStrategy tick: pending signal closed by broker-confirmed take-profit fill", {
|
|
11157
|
+
symbol: this.params.execution.context.symbol,
|
|
11158
|
+
signalId: filledSignal.id,
|
|
11159
|
+
});
|
|
11160
|
+
return await CLOSE_PENDING_SIGNAL_AS_FILL_FN(this, filledSignal, "take_profit", currentTime);
|
|
11161
|
+
}
|
|
11162
|
+
// Check if a broker-confirmed stop-loss fill is awaiting (createStopLoss) - close once.
|
|
11163
|
+
// The exchange filled the SL order (e.g. by high/low); close bypassing the VWAP SL check.
|
|
11164
|
+
if (this._stopLossSignal) {
|
|
11165
|
+
const filledSignal = this._stopLossSignal;
|
|
11166
|
+
this._stopLossSignal = null; // Clear after draining
|
|
11167
|
+
// Persist the cleared deferred state so the drained flag is not replayed on restart
|
|
11168
|
+
await PERSIST_STRATEGY_FN(this);
|
|
11169
|
+
this.params.logger.info("ClientStrategy tick: pending signal closed by broker-confirmed stop-loss fill", {
|
|
11170
|
+
symbol: this.params.execution.context.symbol,
|
|
11171
|
+
signalId: filledSignal.id,
|
|
11172
|
+
});
|
|
11173
|
+
return await CLOSE_PENDING_SIGNAL_AS_FILL_FN(this, filledSignal, "stop_loss", currentTime);
|
|
11174
|
+
}
|
|
10959
11175
|
// Check if scheduled signal was activated - emit opened event once
|
|
10960
11176
|
if (this._activatedSignal) {
|
|
10961
11177
|
const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
|
|
@@ -11056,6 +11272,7 @@ class ClientStrategy {
|
|
|
11056
11272
|
totalPartials: publicSignalForCommit.totalPartials,
|
|
11057
11273
|
note: activatedSignal.activateNote ?? publicSignalForCommit.note,
|
|
11058
11274
|
});
|
|
11275
|
+
await CALL_SIGNAL_EVENT_FN(this, "opened", pendingSignal, currentPrice, currentTime);
|
|
11059
11276
|
// Call onOpen callback
|
|
11060
11277
|
await CALL_OPEN_CALLBACKS_FN(this, this.params.execution.context.symbol, pendingSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
11061
11278
|
const result = {
|
|
@@ -11206,6 +11423,7 @@ class ClientStrategy {
|
|
|
11206
11423
|
signal: publicSignal,
|
|
11207
11424
|
note: cancelledSignal.cancelNote ?? cancelledSignal.note,
|
|
11208
11425
|
});
|
|
11426
|
+
await CALL_SCHEDULE_EVENT_FN$1(this, "cancelled", cancelledSignal, currentPrice, closeTimestamp, "user");
|
|
11209
11427
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
|
|
11210
11428
|
const cancelledResult = {
|
|
11211
11429
|
action: "cancelled",
|
|
@@ -11291,6 +11509,24 @@ class ClientStrategy {
|
|
|
11291
11509
|
await CALL_TICK_CALLBACKS_FN(this, this.params.execution.context.symbol, closedResult, closeTimestamp, this.params.execution.context.backtest);
|
|
11292
11510
|
return closedResult;
|
|
11293
11511
|
}
|
|
11512
|
+
// If a broker-confirmed take-profit fill is awaiting (createTakeProfit) - close once.
|
|
11513
|
+
// The exchange filled the TP order (e.g. by high/low); close bypassing the VWAP TP check.
|
|
11514
|
+
if (this._takeProfitSignal) {
|
|
11515
|
+
this.params.logger.debug("ClientStrategy backtest: pending signal closed by broker-confirmed take-profit fill");
|
|
11516
|
+
const filledSignal = this._takeProfitSignal;
|
|
11517
|
+
this._takeProfitSignal = null; // Clear after draining
|
|
11518
|
+
const closeTimestamp = this.params.execution.context.when.getTime();
|
|
11519
|
+
return await CLOSE_PENDING_SIGNAL_AS_FILL_FN(this, filledSignal, "take_profit", closeTimestamp);
|
|
11520
|
+
}
|
|
11521
|
+
// If a broker-confirmed stop-loss fill is awaiting (createStopLoss) - close once.
|
|
11522
|
+
// The exchange filled the SL order (e.g. by high/low); close bypassing the VWAP SL check.
|
|
11523
|
+
if (this._stopLossSignal) {
|
|
11524
|
+
this.params.logger.debug("ClientStrategy backtest: pending signal closed by broker-confirmed stop-loss fill");
|
|
11525
|
+
const filledSignal = this._stopLossSignal;
|
|
11526
|
+
this._stopLossSignal = null; // Clear after draining
|
|
11527
|
+
const closeTimestamp = this.params.execution.context.when.getTime();
|
|
11528
|
+
return await CLOSE_PENDING_SIGNAL_AS_FILL_FN(this, filledSignal, "stop_loss", closeTimestamp);
|
|
11529
|
+
}
|
|
11294
11530
|
if (!this._pendingSignal && !this._scheduledSignal) {
|
|
11295
11531
|
throw new Error("ClientStrategy backtest: no pending or scheduled signal");
|
|
11296
11532
|
}
|
|
@@ -11408,12 +11644,17 @@ class ClientStrategy {
|
|
|
11408
11644
|
hasActivatedSignal: this._activatedSignal !== null,
|
|
11409
11645
|
hasCancelledSignal: this._cancelledSignal !== null,
|
|
11410
11646
|
hasClosedSignal: this._closedSignal !== null,
|
|
11647
|
+
hasTakeProfitSignal: this._takeProfitSignal !== null,
|
|
11648
|
+
hasStopLossSignal: this._stopLossSignal !== null,
|
|
11411
11649
|
});
|
|
11412
11650
|
this._isStopped = true;
|
|
11413
11651
|
// Clear pending flags to start from clean state
|
|
11414
11652
|
// NOTE: _isStopped blocks NEW position opening, but allows:
|
|
11415
11653
|
// - cancelScheduled() / closePending() for graceful shutdown
|
|
11416
11654
|
// - Monitoring existing _pendingSignal until TP/SL/timeout
|
|
11655
|
+
// NOTE: _takeProfitSignal / _stopLossSignal are NOT cleared — a broker-confirmed fill
|
|
11656
|
+
// reflects a real exchange close that must still drain to emit the closed event, the
|
|
11657
|
+
// same way an existing _pendingSignal keeps being monitored until its natural close.
|
|
11417
11658
|
this._activatedSignal = null;
|
|
11418
11659
|
this._cancelledSignal = null;
|
|
11419
11660
|
this._closedSignal = null;
|
|
@@ -11631,9 +11872,97 @@ class ClientStrategy {
|
|
|
11631
11872
|
if (this._cancelledSignal) {
|
|
11632
11873
|
throw new Error(`ClientStrategy createSignal: a scheduled cancel is awaiting for symbol=${symbol}`);
|
|
11633
11874
|
}
|
|
11875
|
+
if (this._takeProfitSignal) {
|
|
11876
|
+
throw new Error(`ClientStrategy createSignal: a take-profit fill is awaiting for symbol=${symbol}`);
|
|
11877
|
+
}
|
|
11878
|
+
if (this._stopLossSignal) {
|
|
11879
|
+
throw new Error(`ClientStrategy createSignal: a stop-loss fill is awaiting for symbol=${symbol}`);
|
|
11880
|
+
}
|
|
11634
11881
|
this._userSignal = dto;
|
|
11635
11882
|
await PERSIST_STRATEGY_FN(this);
|
|
11636
11883
|
}
|
|
11884
|
+
/**
|
|
11885
|
+
* Reports that the pending position's take-profit order was actually filled on the exchange
|
|
11886
|
+
* (e.g. by candle high/low), forcing a close that does not wait for the VWAP-based TP check.
|
|
11887
|
+
*
|
|
11888
|
+
* The exchange and the strategy are parallel states: ClientStrategy evaluates TP/SL against
|
|
11889
|
+
* VWAP, but the real order may close on high/low. This bridges that gap — the broker confirms
|
|
11890
|
+
* the fill OUT of the async-hooks execution context, the current pending signal is snapshotted
|
|
11891
|
+
* into _takeProfitSignal and cleared, and the next tick()/backtest() drains it, closing the
|
|
11892
|
+
* position with closeReason "take_profit" at the effective take-profit level.
|
|
11893
|
+
*
|
|
11894
|
+
* No-op if no pending signal exists. Persisted (live mode only) so a crash before the next
|
|
11895
|
+
* tick does not lose the deferred close.
|
|
11896
|
+
*
|
|
11897
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
11898
|
+
* @param backtest - Whether running in backtest mode
|
|
11899
|
+
* @param payload - Optional commit id/note attached to the close
|
|
11900
|
+
* @returns Promise that resolves when the take-profit fill is queued
|
|
11901
|
+
*/
|
|
11902
|
+
async createTakeProfit(symbol, backtest, payload) {
|
|
11903
|
+
const closeId = payload.id;
|
|
11904
|
+
this.params.logger.debug("ClientStrategy createTakeProfit", {
|
|
11905
|
+
symbol,
|
|
11906
|
+
hasPendingSignal: this._pendingSignal !== null,
|
|
11907
|
+
closeId,
|
|
11908
|
+
});
|
|
11909
|
+
// Snapshot the pending signal for the next tick/backtest to close with reason "take_profit".
|
|
11910
|
+
if (this._pendingSignal) {
|
|
11911
|
+
this._takeProfitSignal = Object.assign({}, this._pendingSignal, {
|
|
11912
|
+
closeId,
|
|
11913
|
+
closeNote: payload.note,
|
|
11914
|
+
});
|
|
11915
|
+
this._pendingSignal = null;
|
|
11916
|
+
}
|
|
11917
|
+
if (backtest) {
|
|
11918
|
+
// Drained in backtest() with correct candle timestamp; no live persistence.
|
|
11919
|
+
return;
|
|
11920
|
+
}
|
|
11921
|
+
await PersistSignalAdapter.writeSignalData(this._pendingSignal, symbol, this.params.strategyName, this.params.exchangeName);
|
|
11922
|
+
// Persist deferred _takeProfitSignal so a crash before the next tick does not lose it
|
|
11923
|
+
await PERSIST_STRATEGY_FN(this);
|
|
11924
|
+
}
|
|
11925
|
+
/**
|
|
11926
|
+
* Reports that the pending position's stop-loss order was actually filled on the exchange
|
|
11927
|
+
* (e.g. by candle high/low), forcing a close that does not wait for the VWAP-based SL check.
|
|
11928
|
+
*
|
|
11929
|
+
* The exchange and the strategy are parallel states: ClientStrategy evaluates TP/SL against
|
|
11930
|
+
* VWAP, but the real order may close on high/low. This bridges that gap — the broker confirms
|
|
11931
|
+
* the fill OUT of the async-hooks execution context, the current pending signal is snapshotted
|
|
11932
|
+
* into _stopLossSignal and cleared, and the next tick()/backtest() drains it, closing the
|
|
11933
|
+
* position with closeReason "stop_loss" at the effective stop-loss level.
|
|
11934
|
+
*
|
|
11935
|
+
* No-op if no pending signal exists. Persisted (live mode only) so a crash before the next
|
|
11936
|
+
* tick does not lose the deferred close.
|
|
11937
|
+
*
|
|
11938
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
11939
|
+
* @param backtest - Whether running in backtest mode
|
|
11940
|
+
* @param payload - Optional commit id/note attached to the close
|
|
11941
|
+
* @returns Promise that resolves when the stop-loss fill is queued
|
|
11942
|
+
*/
|
|
11943
|
+
async createStopLoss(symbol, backtest, payload) {
|
|
11944
|
+
const closeId = payload.id;
|
|
11945
|
+
this.params.logger.debug("ClientStrategy createStopLoss", {
|
|
11946
|
+
symbol,
|
|
11947
|
+
hasPendingSignal: this._pendingSignal !== null,
|
|
11948
|
+
closeId,
|
|
11949
|
+
});
|
|
11950
|
+
// Snapshot the pending signal for the next tick/backtest to close with reason "stop_loss".
|
|
11951
|
+
if (this._pendingSignal) {
|
|
11952
|
+
this._stopLossSignal = Object.assign({}, this._pendingSignal, {
|
|
11953
|
+
closeId,
|
|
11954
|
+
closeNote: payload.note,
|
|
11955
|
+
});
|
|
11956
|
+
this._pendingSignal = null;
|
|
11957
|
+
}
|
|
11958
|
+
if (backtest) {
|
|
11959
|
+
// Drained in backtest() with correct candle timestamp; no live persistence.
|
|
11960
|
+
return;
|
|
11961
|
+
}
|
|
11962
|
+
await PersistSignalAdapter.writeSignalData(this._pendingSignal, symbol, this.params.strategyName, this.params.exchangeName);
|
|
11963
|
+
// Persist deferred _stopLossSignal so a crash before the next tick does not lose it
|
|
11964
|
+
await PERSIST_STRATEGY_FN(this);
|
|
11965
|
+
}
|
|
11637
11966
|
/**
|
|
11638
11967
|
* Returns the deferred strategy-state snapshot exactly as it would be written to persist on
|
|
11639
11968
|
* this iteration: the in-memory _userSignal, _commitQueue and deferred user-action flags,
|
|
@@ -11653,6 +11982,8 @@ class ClientStrategy {
|
|
|
11653
11982
|
closedSignal: this._closedSignal,
|
|
11654
11983
|
cancelledSignal: this._cancelledSignal,
|
|
11655
11984
|
activatedSignal: this._activatedSignal,
|
|
11985
|
+
takeProfitSignal: this._takeProfitSignal,
|
|
11986
|
+
stopLossSignal: this._stopLossSignal,
|
|
11656
11987
|
};
|
|
11657
11988
|
}
|
|
11658
11989
|
/**
|
|
@@ -12991,7 +13322,7 @@ const CREATE_SYNC_PENDING_FN = (self, strategyName, exchangeName, frameName, bac
|
|
|
12991
13322
|
return true;
|
|
12992
13323
|
}
|
|
12993
13324
|
await syncPendingSubject.next(event);
|
|
12994
|
-
await self.actionCoreService.
|
|
13325
|
+
await self.actionCoreService.orderCheck(backtest, event, { strategyName, exchangeName, frameName });
|
|
12995
13326
|
return true;
|
|
12996
13327
|
}, {
|
|
12997
13328
|
fallback: (error) => {
|
|
@@ -13145,6 +13476,81 @@ const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => trycatch(async (symbol, strateg
|
|
|
13145
13476
|
},
|
|
13146
13477
|
defaultValue: null,
|
|
13147
13478
|
});
|
|
13479
|
+
/**
|
|
13480
|
+
* Creates a callback function for emitting scheduled signal lifecycle events to scheduleEventSubject.
|
|
13481
|
+
*
|
|
13482
|
+
* Called by ClientStrategy when a scheduled signal is created (action "scheduled") or cancelled
|
|
13483
|
+
* before activation (action "cancelled": timeout / price_reject / user). The scheduled -> active
|
|
13484
|
+
* transition is intentionally NOT reported here.
|
|
13485
|
+
*
|
|
13486
|
+
* @param self - Reference to StrategyConnectionService instance
|
|
13487
|
+
* @returns Callback function for scheduled signal lifecycle events
|
|
13488
|
+
*/
|
|
13489
|
+
const CREATE_COMMIT_SCHEDULE_EVENT_FN = (self) => trycatch(async (action, symbol, strategyName, exchangeName, data, currentPrice, backtest, timestamp, reason) => {
|
|
13490
|
+
const event = {
|
|
13491
|
+
action,
|
|
13492
|
+
symbol,
|
|
13493
|
+
strategyName,
|
|
13494
|
+
exchangeName,
|
|
13495
|
+
frameName: data.frameName,
|
|
13496
|
+
data,
|
|
13497
|
+
reason,
|
|
13498
|
+
currentPrice,
|
|
13499
|
+
backtest,
|
|
13500
|
+
timestamp,
|
|
13501
|
+
};
|
|
13502
|
+
await scheduleEventSubject.next(event);
|
|
13503
|
+
await self.actionCoreService.scheduleEvent(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
|
|
13504
|
+
}, {
|
|
13505
|
+
fallback: (error) => {
|
|
13506
|
+
const message = "StrategyConnectionService CREATE_COMMIT_SCHEDULE_EVENT_FN thrown";
|
|
13507
|
+
const payload = {
|
|
13508
|
+
error: errorData(error),
|
|
13509
|
+
message: getErrorMessage(error),
|
|
13510
|
+
};
|
|
13511
|
+
self.loggerService.warn(message, payload);
|
|
13512
|
+
console.warn(message, payload);
|
|
13513
|
+
errorEmitter.next(error);
|
|
13514
|
+
},
|
|
13515
|
+
defaultValue: null,
|
|
13516
|
+
});
|
|
13517
|
+
/**
|
|
13518
|
+
* Creates a callback function for emitting pending signal lifecycle events to signalEventSubject.
|
|
13519
|
+
*
|
|
13520
|
+
* Called by ClientStrategy when a pending position is opened (action "opened") or closed
|
|
13521
|
+
* (action "closed" with a closeReason).
|
|
13522
|
+
*
|
|
13523
|
+
* @param self - Reference to StrategyConnectionService instance
|
|
13524
|
+
* @returns Callback function for pending signal lifecycle events
|
|
13525
|
+
*/
|
|
13526
|
+
const CREATE_COMMIT_SIGNAL_EVENT_FN = (self) => trycatch(async (action, symbol, strategyName, exchangeName, data, currentPrice, backtest, timestamp, closeReason) => {
|
|
13527
|
+
const event = {
|
|
13528
|
+
action,
|
|
13529
|
+
symbol,
|
|
13530
|
+
strategyName,
|
|
13531
|
+
exchangeName,
|
|
13532
|
+
frameName: data.frameName,
|
|
13533
|
+
data,
|
|
13534
|
+
closeReason,
|
|
13535
|
+
currentPrice,
|
|
13536
|
+
backtest,
|
|
13537
|
+
timestamp,
|
|
13538
|
+
};
|
|
13539
|
+
await signalEventSubject.next(event);
|
|
13540
|
+
await self.actionCoreService.pendingEvent(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
|
|
13541
|
+
}, {
|
|
13542
|
+
fallback: (error) => {
|
|
13543
|
+
const message = "StrategyConnectionService CREATE_COMMIT_SIGNAL_EVENT_FN thrown";
|
|
13544
|
+
const payload = {
|
|
13545
|
+
error: errorData(error),
|
|
13546
|
+
message: getErrorMessage(error),
|
|
13547
|
+
};
|
|
13548
|
+
self.loggerService.warn(message, payload);
|
|
13549
|
+
console.warn(message, payload);
|
|
13550
|
+
errorEmitter.next(error);
|
|
13551
|
+
},
|
|
13552
|
+
defaultValue: null,
|
|
13553
|
+
});
|
|
13148
13554
|
/**
|
|
13149
13555
|
* Creates a callback function for emitting idle ping events.
|
|
13150
13556
|
*
|
|
@@ -13437,6 +13843,8 @@ class StrategyConnectionService {
|
|
|
13437
13843
|
callbacks,
|
|
13438
13844
|
onInit: CREATE_COMMIT_INIT_FN(this),
|
|
13439
13845
|
onSchedulePing: CREATE_COMMIT_SCHEDULE_PING_FN(this),
|
|
13846
|
+
onScheduleEvent: CREATE_COMMIT_SCHEDULE_EVENT_FN(this),
|
|
13847
|
+
onSignalEvent: CREATE_COMMIT_SIGNAL_EVENT_FN(this),
|
|
13440
13848
|
onActivePing: CREATE_COMMIT_ACTIVE_PING_FN(this),
|
|
13441
13849
|
onIdlePing: CREATE_COMMIT_IDLE_PING_FN(this),
|
|
13442
13850
|
onDispose: CREATE_COMMIT_DISPOSE_FN(this),
|
|
@@ -14456,6 +14864,50 @@ class StrategyConnectionService {
|
|
|
14456
14864
|
const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
|
|
14457
14865
|
await strategy.createSignal(symbol, currentPrice, dto);
|
|
14458
14866
|
};
|
|
14867
|
+
/**
|
|
14868
|
+
* Reports that the pending position's take-profit order was actually filled on the exchange
|
|
14869
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based TP check.
|
|
14870
|
+
*
|
|
14871
|
+
* Delegates to ClientStrategy.createTakeProfit(). The close is deferred and emitted with
|
|
14872
|
+
* closeReason "take_profit" on the next tick()/backtest(). Works out of the execution context.
|
|
14873
|
+
*
|
|
14874
|
+
* @param backtest - Whether running in backtest mode
|
|
14875
|
+
* @param symbol - Trading pair symbol
|
|
14876
|
+
* @param context - Context with strategyName, exchangeName, frameName
|
|
14877
|
+
* @param payload - Optional commit payload with id and note
|
|
14878
|
+
* @returns Promise that resolves when the take-profit fill is queued
|
|
14879
|
+
*/
|
|
14880
|
+
this.createTakeProfit = async (backtest, symbol, context, payload = {}) => {
|
|
14881
|
+
this.loggerService.log("strategyConnectionService createTakeProfit", {
|
|
14882
|
+
symbol,
|
|
14883
|
+
context,
|
|
14884
|
+
payload,
|
|
14885
|
+
});
|
|
14886
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
14887
|
+
await strategy.createTakeProfit(symbol, backtest, payload);
|
|
14888
|
+
};
|
|
14889
|
+
/**
|
|
14890
|
+
* Reports that the pending position's stop-loss order was actually filled on the exchange
|
|
14891
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based SL check.
|
|
14892
|
+
*
|
|
14893
|
+
* Delegates to ClientStrategy.createStopLoss(). The close is deferred and emitted with
|
|
14894
|
+
* closeReason "stop_loss" on the next tick()/backtest(). Works out of the execution context.
|
|
14895
|
+
*
|
|
14896
|
+
* @param backtest - Whether running in backtest mode
|
|
14897
|
+
* @param symbol - Trading pair symbol
|
|
14898
|
+
* @param context - Context with strategyName, exchangeName, frameName
|
|
14899
|
+
* @param payload - Optional commit payload with id and note
|
|
14900
|
+
* @returns Promise that resolves when the stop-loss fill is queued
|
|
14901
|
+
*/
|
|
14902
|
+
this.createStopLoss = async (backtest, symbol, context, payload = {}) => {
|
|
14903
|
+
this.loggerService.log("strategyConnectionService createStopLoss", {
|
|
14904
|
+
symbol,
|
|
14905
|
+
context,
|
|
14906
|
+
payload,
|
|
14907
|
+
});
|
|
14908
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
14909
|
+
await strategy.createStopLoss(symbol, backtest, payload);
|
|
14910
|
+
};
|
|
14459
14911
|
/**
|
|
14460
14912
|
* Returns the in-memory deferred strategy-state snapshot for this iteration.
|
|
14461
14913
|
*
|
|
@@ -16099,6 +16551,54 @@ const CALL_PING_IDLE_FN = trycatch(async (event, self) => {
|
|
|
16099
16551
|
},
|
|
16100
16552
|
defaultValue: null,
|
|
16101
16553
|
});
|
|
16554
|
+
/**
|
|
16555
|
+
* Wrapper to call scheduleEvent method with error capture.
|
|
16556
|
+
*
|
|
16557
|
+
* Unlike CALL_PING_SCHEDULED_FN there is no hasScheduledSignal guard: the "cancelled" action fires
|
|
16558
|
+
* exactly while the scheduled signal is being removed, so guarding on its presence would suppress it.
|
|
16559
|
+
*/
|
|
16560
|
+
const CALL_SCHEDULE_EVENT_FN = trycatch(async (event, self) => {
|
|
16561
|
+
if (!self._target.scheduleEvent) {
|
|
16562
|
+
return;
|
|
16563
|
+
}
|
|
16564
|
+
return await self._target.scheduleEvent(event);
|
|
16565
|
+
}, {
|
|
16566
|
+
fallback: (error) => {
|
|
16567
|
+
const message = "ActionProxy.scheduleEvent thrown";
|
|
16568
|
+
const payload = {
|
|
16569
|
+
error: errorData(error),
|
|
16570
|
+
message: getErrorMessage(error),
|
|
16571
|
+
};
|
|
16572
|
+
LOGGER_SERVICE$5.warn(message, payload);
|
|
16573
|
+
console.warn(message, payload);
|
|
16574
|
+
errorEmitter.next(error);
|
|
16575
|
+
},
|
|
16576
|
+
defaultValue: null,
|
|
16577
|
+
});
|
|
16578
|
+
/**
|
|
16579
|
+
* Wrapper to call pendingEvent method with error capture.
|
|
16580
|
+
*
|
|
16581
|
+
* Like CALL_SCHEDULE_EVENT_FN there is no hasPendingSignal guard: the "closed" action fires exactly
|
|
16582
|
+
* while the pending signal is being removed, so guarding on its presence would suppress it.
|
|
16583
|
+
*/
|
|
16584
|
+
const CALL_PENDING_EVENT_FN = trycatch(async (event, self) => {
|
|
16585
|
+
if (!self._target.pendingEvent) {
|
|
16586
|
+
return;
|
|
16587
|
+
}
|
|
16588
|
+
return await self._target.pendingEvent(event);
|
|
16589
|
+
}, {
|
|
16590
|
+
fallback: (error) => {
|
|
16591
|
+
const message = "ActionProxy.pendingEvent thrown";
|
|
16592
|
+
const payload = {
|
|
16593
|
+
error: errorData(error),
|
|
16594
|
+
message: getErrorMessage(error),
|
|
16595
|
+
};
|
|
16596
|
+
LOGGER_SERVICE$5.warn(message, payload);
|
|
16597
|
+
console.warn(message, payload);
|
|
16598
|
+
errorEmitter.next(error);
|
|
16599
|
+
},
|
|
16600
|
+
defaultValue: null,
|
|
16601
|
+
});
|
|
16102
16602
|
/**
|
|
16103
16603
|
* Wrapper to call pingActive method with error capture.
|
|
16104
16604
|
*/
|
|
@@ -16325,6 +16825,31 @@ class ActionProxy {
|
|
|
16325
16825
|
async pingScheduled(event) {
|
|
16326
16826
|
return await CALL_PING_SCHEDULED_FN(event, this);
|
|
16327
16827
|
}
|
|
16828
|
+
/**
|
|
16829
|
+
* Handles scheduled signal lifecycle events with error capture.
|
|
16830
|
+
*
|
|
16831
|
+
* Wraps the user's scheduleEvent() method to catch and log any errors. Called once when a
|
|
16832
|
+
* scheduled signal is created (action "scheduled") and once when it is cancelled before
|
|
16833
|
+
* activation (action "cancelled").
|
|
16834
|
+
*
|
|
16835
|
+
* @param event - Scheduled lifecycle data (action discriminates created vs cancelled)
|
|
16836
|
+
* @returns Promise resolving to user's scheduleEvent() result or null on error
|
|
16837
|
+
*/
|
|
16838
|
+
async scheduleEvent(event) {
|
|
16839
|
+
return await CALL_SCHEDULE_EVENT_FN(event, this);
|
|
16840
|
+
}
|
|
16841
|
+
/**
|
|
16842
|
+
* Handles pending signal lifecycle events with error capture.
|
|
16843
|
+
*
|
|
16844
|
+
* Wraps the user's pendingEvent() method to catch and log any errors. Called once when a
|
|
16845
|
+
* pending position is opened (action "opened") and once when it is closed (action "closed").
|
|
16846
|
+
*
|
|
16847
|
+
* @param event - Pending lifecycle data (action discriminates opened vs closed)
|
|
16848
|
+
* @returns Promise resolving to user's pendingEvent() result or null on error
|
|
16849
|
+
*/
|
|
16850
|
+
async pendingEvent(event) {
|
|
16851
|
+
return await CALL_PENDING_EVENT_FN(event, this);
|
|
16852
|
+
}
|
|
16328
16853
|
/**
|
|
16329
16854
|
* Handles active ping events with error capture.
|
|
16330
16855
|
*
|
|
@@ -16383,14 +16908,14 @@ class ActionProxy {
|
|
|
16383
16908
|
*
|
|
16384
16909
|
* @param event - Pending-ping event with action "signal-ping"
|
|
16385
16910
|
*/
|
|
16386
|
-
async
|
|
16387
|
-
if (this._target.
|
|
16388
|
-
console.error("Action::
|
|
16389
|
-
console.error("If you need to check whether the order is still open on the exchange, please use Broker.useBrokerAdapter with
|
|
16390
|
-
console.error("If Action::
|
|
16911
|
+
async orderCheck(event) {
|
|
16912
|
+
if (this._target.orderCheck) {
|
|
16913
|
+
console.error("Action::orderCheck is unwanted cause exchange integration should be implemented in Broker.useBrokerAdapter as an infrastructure domain layer");
|
|
16914
|
+
console.error("If you need to check whether the order is still open on the exchange, please use Broker.useBrokerAdapter with onOrderCheck");
|
|
16915
|
+
console.error("If Action::orderCheck throws the framework will close the position with closeReason \"closed\"!");
|
|
16391
16916
|
console.error("");
|
|
16392
16917
|
console.error("You have been warned!");
|
|
16393
|
-
await this._target.
|
|
16918
|
+
await this._target.orderCheck(event);
|
|
16394
16919
|
}
|
|
16395
16920
|
}
|
|
16396
16921
|
/**
|
|
@@ -16552,6 +17077,40 @@ const CALL_PING_SCHEDULED_CALLBACK_FN = trycatch(async (self, event, strategyNam
|
|
|
16552
17077
|
errorEmitter.next(error);
|
|
16553
17078
|
},
|
|
16554
17079
|
});
|
|
17080
|
+
/** Wrapper to call scheduled lifecycle (creation / cancellation) callback with error handling */
|
|
17081
|
+
const CALL_SCHEDULE_EVENT_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
17082
|
+
if (self.params.callbacks?.onScheduleEvent) {
|
|
17083
|
+
await self.params.callbacks.onScheduleEvent(event, self.params.actionName, strategyName, frameName, backtest);
|
|
17084
|
+
}
|
|
17085
|
+
}, {
|
|
17086
|
+
fallback: (error, self) => {
|
|
17087
|
+
const message = "ClientAction CALL_SCHEDULE_EVENT_CALLBACK_FN thrown";
|
|
17088
|
+
const payload = {
|
|
17089
|
+
error: errorData(error),
|
|
17090
|
+
message: getErrorMessage(error),
|
|
17091
|
+
};
|
|
17092
|
+
self.params.logger.warn(message, payload);
|
|
17093
|
+
console.warn(message, payload);
|
|
17094
|
+
errorEmitter.next(error);
|
|
17095
|
+
},
|
|
17096
|
+
});
|
|
17097
|
+
/** Wrapper to call pending lifecycle (open / close) callback with error handling */
|
|
17098
|
+
const CALL_PENDING_EVENT_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
17099
|
+
if (self.params.callbacks?.onPendingEvent) {
|
|
17100
|
+
await self.params.callbacks.onPendingEvent(event, self.params.actionName, strategyName, frameName, backtest);
|
|
17101
|
+
}
|
|
17102
|
+
}, {
|
|
17103
|
+
fallback: (error, self) => {
|
|
17104
|
+
const message = "ClientAction CALL_PENDING_EVENT_CALLBACK_FN thrown";
|
|
17105
|
+
const payload = {
|
|
17106
|
+
error: errorData(error),
|
|
17107
|
+
message: getErrorMessage(error),
|
|
17108
|
+
};
|
|
17109
|
+
self.params.logger.warn(message, payload);
|
|
17110
|
+
console.warn(message, payload);
|
|
17111
|
+
errorEmitter.next(error);
|
|
17112
|
+
},
|
|
17113
|
+
});
|
|
16555
17114
|
/** Wrapper to call idle ping callback with error handling */
|
|
16556
17115
|
const CALL_PING_IDLE_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
16557
17116
|
if (self.params.callbacks?.onPingIdle) {
|
|
@@ -16613,12 +17172,12 @@ const CALL_SIGNAL_SYNC_CALLBACK_FN = async (self, event, strategyName, frameName
|
|
|
16613
17172
|
}
|
|
16614
17173
|
};
|
|
16615
17174
|
/**
|
|
16616
|
-
* Calls
|
|
17175
|
+
* Calls onOrderCheck callback WITHOUT trycatch — exceptions must propagate
|
|
16617
17176
|
* up to CREATE_SYNC_PENDING_FN in StrategyConnectionService (which returns false on error).
|
|
16618
17177
|
*/
|
|
16619
17178
|
const CALL_ORDER_PING_CALLBACK_FN = async (self, event, strategyName, frameName, isBacktest) => {
|
|
16620
|
-
if (self.params.callbacks?.
|
|
16621
|
-
await self.params.callbacks.
|
|
17179
|
+
if (self.params.callbacks?.onOrderCheck) {
|
|
17180
|
+
await self.params.callbacks.onOrderCheck(event, self.params.actionName, strategyName, frameName, isBacktest);
|
|
16622
17181
|
}
|
|
16623
17182
|
};
|
|
16624
17183
|
/** Wrapper to call onInit callback with error handling */
|
|
@@ -16926,6 +17485,52 @@ class ClientAction {
|
|
|
16926
17485
|
await CALL_PING_SCHEDULED_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
16927
17486
|
}
|
|
16928
17487
|
;
|
|
17488
|
+
/**
|
|
17489
|
+
* Handles scheduled signal lifecycle events (creation / cancellation).
|
|
17490
|
+
*
|
|
17491
|
+
* Manual wiring — EVENT-BASED: users implement {@link IActionCallbacks.onScheduleEvent} (via `addActionSchema`)
|
|
17492
|
+
* to drive the exchange (`commitActivateScheduled` / `commitCancelScheduled`); see that contract
|
|
17493
|
+
* for the full guidance and example. This internal dispatch forwards to the handler/callback.
|
|
17494
|
+
*/
|
|
17495
|
+
async scheduleEvent(event) {
|
|
17496
|
+
this.params.logger.debug("ClientAction scheduleEvent", {
|
|
17497
|
+
actionName: this.params.actionName,
|
|
17498
|
+
strategyName: this.params.strategyName,
|
|
17499
|
+
frameName: this.params.frameName,
|
|
17500
|
+
action: event.action,
|
|
17501
|
+
});
|
|
17502
|
+
if (!this._handlerInstance) {
|
|
17503
|
+
await this.waitForInit();
|
|
17504
|
+
}
|
|
17505
|
+
// Call handler method if defined
|
|
17506
|
+
await this._handlerInstance?.scheduleEvent(event);
|
|
17507
|
+
// Call callback if defined
|
|
17508
|
+
await CALL_SCHEDULE_EVENT_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
17509
|
+
}
|
|
17510
|
+
;
|
|
17511
|
+
/**
|
|
17512
|
+
* Handles pending signal lifecycle events (open / close).
|
|
17513
|
+
*
|
|
17514
|
+
* Manual wiring — EVENT-BASED: users implement {@link IActionCallbacks.onPendingEvent} (via `addActionSchema`) to
|
|
17515
|
+
* drive the exchange; for per-tick fills use `onPingActive`. See that contract for the full
|
|
17516
|
+
* guidance and example. This internal dispatch forwards to the handler/callback.
|
|
17517
|
+
*/
|
|
17518
|
+
async pendingEvent(event) {
|
|
17519
|
+
this.params.logger.debug("ClientAction pendingEvent", {
|
|
17520
|
+
actionName: this.params.actionName,
|
|
17521
|
+
strategyName: this.params.strategyName,
|
|
17522
|
+
frameName: this.params.frameName,
|
|
17523
|
+
action: event.action,
|
|
17524
|
+
});
|
|
17525
|
+
if (!this._handlerInstance) {
|
|
17526
|
+
await this.waitForInit();
|
|
17527
|
+
}
|
|
17528
|
+
// Call handler method if defined
|
|
17529
|
+
await this._handlerInstance?.pendingEvent(event);
|
|
17530
|
+
// Call callback if defined
|
|
17531
|
+
await CALL_PENDING_EVENT_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
17532
|
+
}
|
|
17533
|
+
;
|
|
16929
17534
|
/**
|
|
16930
17535
|
* Handles active ping events during active pending signal monitoring.
|
|
16931
17536
|
*/
|
|
@@ -17003,8 +17608,8 @@ class ClientAction {
|
|
|
17003
17608
|
* Gate for the pending-order ping (order still open on exchange?).
|
|
17004
17609
|
* NOT wrapped in trycatch — exceptions propagate to CREATE_SYNC_PENDING_FN.
|
|
17005
17610
|
*/
|
|
17006
|
-
async
|
|
17007
|
-
this.params.logger.debug("ClientAction
|
|
17611
|
+
async orderCheck(event) {
|
|
17612
|
+
this.params.logger.debug("ClientAction orderCheck", {
|
|
17008
17613
|
actionName: this.params.actionName,
|
|
17009
17614
|
strategyName: this.params.strategyName,
|
|
17010
17615
|
frameName: this.params.frameName,
|
|
@@ -17013,7 +17618,7 @@ class ClientAction {
|
|
|
17013
17618
|
await this.waitForInit();
|
|
17014
17619
|
}
|
|
17015
17620
|
// Call handler method if defined — exceptions propagate
|
|
17016
|
-
await this._handlerInstance?.
|
|
17621
|
+
await this._handlerInstance?.orderCheck(event);
|
|
17017
17622
|
// Call callback if defined — exceptions propagate
|
|
17018
17623
|
await CALL_ORDER_PING_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
17019
17624
|
}
|
|
@@ -17218,6 +17823,36 @@ class ActionConnectionService {
|
|
|
17218
17823
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
17219
17824
|
await action.pingScheduled(event);
|
|
17220
17825
|
};
|
|
17826
|
+
/**
|
|
17827
|
+
* Routes a scheduled signal lifecycle event (creation / cancellation) to the ClientAction instance.
|
|
17828
|
+
*
|
|
17829
|
+
* @param event - Scheduled lifecycle event data (action discriminates created vs cancelled)
|
|
17830
|
+
* @param backtest - Whether running in backtest mode
|
|
17831
|
+
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
17832
|
+
*/
|
|
17833
|
+
this.scheduleEvent = async (event, backtest, context) => {
|
|
17834
|
+
this.loggerService.log("actionConnectionService scheduleEvent", {
|
|
17835
|
+
backtest,
|
|
17836
|
+
context,
|
|
17837
|
+
});
|
|
17838
|
+
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
17839
|
+
await action.scheduleEvent(event);
|
|
17840
|
+
};
|
|
17841
|
+
/**
|
|
17842
|
+
* Routes a pending signal lifecycle event (open / close) to the ClientAction instance.
|
|
17843
|
+
*
|
|
17844
|
+
* @param event - Pending lifecycle event data (action discriminates opened vs closed)
|
|
17845
|
+
* @param backtest - Whether running in backtest mode
|
|
17846
|
+
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
17847
|
+
*/
|
|
17848
|
+
this.pendingEvent = async (event, backtest, context) => {
|
|
17849
|
+
this.loggerService.log("actionConnectionService pendingEvent", {
|
|
17850
|
+
backtest,
|
|
17851
|
+
context,
|
|
17852
|
+
});
|
|
17853
|
+
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
17854
|
+
await action.pendingEvent(event);
|
|
17855
|
+
};
|
|
17221
17856
|
/**
|
|
17222
17857
|
* Routes active ping event to appropriate ClientAction instance.
|
|
17223
17858
|
*
|
|
@@ -17281,20 +17916,20 @@ class ActionConnectionService {
|
|
|
17281
17916
|
await action.signalSync(event);
|
|
17282
17917
|
};
|
|
17283
17918
|
/**
|
|
17284
|
-
* Routes
|
|
17919
|
+
* Routes orderCheck event to appropriate ClientAction instance.
|
|
17285
17920
|
* NOT wrapped in trycatch — exceptions propagate to CREATE_SYNC_PENDING_FN.
|
|
17286
17921
|
*
|
|
17287
17922
|
* @param event - Pending-ping event with action "signal-ping"
|
|
17288
17923
|
* @param backtest - Whether running in backtest mode
|
|
17289
17924
|
* @param context - Execution context
|
|
17290
17925
|
*/
|
|
17291
|
-
this.
|
|
17292
|
-
this.loggerService.log("actionConnectionService
|
|
17926
|
+
this.orderCheck = async (event, backtest, context) => {
|
|
17927
|
+
this.loggerService.log("actionConnectionService orderCheck", {
|
|
17293
17928
|
backtest,
|
|
17294
17929
|
context,
|
|
17295
17930
|
});
|
|
17296
17931
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
17297
|
-
await action.
|
|
17932
|
+
await action.orderCheck(event);
|
|
17298
17933
|
};
|
|
17299
17934
|
/**
|
|
17300
17935
|
* Disposes the ClientAction instance for the given action name.
|
|
@@ -18162,6 +18797,54 @@ class StrategyCoreService {
|
|
|
18162
18797
|
await this.validate(context);
|
|
18163
18798
|
return await this.strategyConnectionService.createSignal(backtest, symbol, dto, context);
|
|
18164
18799
|
};
|
|
18800
|
+
/**
|
|
18801
|
+
* Reports that the pending position's take-profit order was actually filled on the exchange
|
|
18802
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based TP check.
|
|
18803
|
+
*
|
|
18804
|
+
* Validates the context, then delegates to StrategyConnectionService.createTakeProfit().
|
|
18805
|
+
* The close is deferred and emitted with closeReason "take_profit" on the next tick/backtest.
|
|
18806
|
+
* Does not require execution context.
|
|
18807
|
+
*
|
|
18808
|
+
* @param backtest - Whether running in backtest mode
|
|
18809
|
+
* @param symbol - Trading pair symbol
|
|
18810
|
+
* @param context - Context with strategyName, exchangeName, frameName
|
|
18811
|
+
* @param payload - Optional commit payload with id and note
|
|
18812
|
+
* @returns Promise that resolves when the take-profit fill is queued
|
|
18813
|
+
*/
|
|
18814
|
+
this.createTakeProfit = async (backtest, symbol, context, payload = {}) => {
|
|
18815
|
+
this.loggerService.log("strategyCoreService createTakeProfit", {
|
|
18816
|
+
symbol,
|
|
18817
|
+
context,
|
|
18818
|
+
backtest,
|
|
18819
|
+
payload,
|
|
18820
|
+
});
|
|
18821
|
+
await this.validate(context);
|
|
18822
|
+
return await this.strategyConnectionService.createTakeProfit(backtest, symbol, context, payload);
|
|
18823
|
+
};
|
|
18824
|
+
/**
|
|
18825
|
+
* Reports that the pending position's stop-loss order was actually filled on the exchange
|
|
18826
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based SL check.
|
|
18827
|
+
*
|
|
18828
|
+
* Validates the context, then delegates to StrategyConnectionService.createStopLoss().
|
|
18829
|
+
* The close is deferred and emitted with closeReason "stop_loss" on the next tick/backtest.
|
|
18830
|
+
* Does not require execution context.
|
|
18831
|
+
*
|
|
18832
|
+
* @param backtest - Whether running in backtest mode
|
|
18833
|
+
* @param symbol - Trading pair symbol
|
|
18834
|
+
* @param context - Context with strategyName, exchangeName, frameName
|
|
18835
|
+
* @param payload - Optional commit payload with id and note
|
|
18836
|
+
* @returns Promise that resolves when the stop-loss fill is queued
|
|
18837
|
+
*/
|
|
18838
|
+
this.createStopLoss = async (backtest, symbol, context, payload = {}) => {
|
|
18839
|
+
this.loggerService.log("strategyCoreService createStopLoss", {
|
|
18840
|
+
symbol,
|
|
18841
|
+
context,
|
|
18842
|
+
backtest,
|
|
18843
|
+
payload,
|
|
18844
|
+
});
|
|
18845
|
+
await this.validate(context);
|
|
18846
|
+
return await this.strategyConnectionService.createStopLoss(backtest, symbol, context, payload);
|
|
18847
|
+
};
|
|
18165
18848
|
/**
|
|
18166
18849
|
* Returns the in-memory deferred strategy-state snapshot for this iteration.
|
|
18167
18850
|
*
|
|
@@ -19438,6 +20121,48 @@ class ActionCoreService {
|
|
|
19438
20121
|
await this.actionConnectionService.pingScheduled(event, backtest, { actionName, ...context });
|
|
19439
20122
|
}
|
|
19440
20123
|
};
|
|
20124
|
+
/**
|
|
20125
|
+
* Routes a scheduled signal lifecycle event (creation / cancellation) to all registered actions.
|
|
20126
|
+
*
|
|
20127
|
+
* Retrieves action list from strategy schema (IStrategySchema.actions) and invokes the
|
|
20128
|
+
* scheduleEvent handler on each ClientAction instance sequentially. Called once on creation
|
|
20129
|
+
* (action "scheduled") and once on cancellation before activation (action "cancelled").
|
|
20130
|
+
*
|
|
20131
|
+
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
20132
|
+
* @param event - Scheduled lifecycle event data (action discriminates created vs cancelled)
|
|
20133
|
+
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
20134
|
+
*/
|
|
20135
|
+
this.scheduleEvent = async (backtest, event, context) => {
|
|
20136
|
+
this.loggerService.log("actionCoreService scheduleEvent", {
|
|
20137
|
+
context,
|
|
20138
|
+
});
|
|
20139
|
+
await this.validate(context);
|
|
20140
|
+
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
20141
|
+
for (const actionName of actions) {
|
|
20142
|
+
await this.actionConnectionService.scheduleEvent(event, backtest, { actionName, ...context });
|
|
20143
|
+
}
|
|
20144
|
+
};
|
|
20145
|
+
/**
|
|
20146
|
+
* Routes a pending signal lifecycle event (open / close) to all registered actions.
|
|
20147
|
+
*
|
|
20148
|
+
* Retrieves action list from strategy schema (IStrategySchema.actions) and invokes the
|
|
20149
|
+
* pendingEvent handler on each ClientAction instance sequentially. Called once on open
|
|
20150
|
+
* (action "opened") and once on close (action "closed").
|
|
20151
|
+
*
|
|
20152
|
+
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
20153
|
+
* @param event - Pending lifecycle event data (action discriminates opened vs closed)
|
|
20154
|
+
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
20155
|
+
*/
|
|
20156
|
+
this.pendingEvent = async (backtest, event, context) => {
|
|
20157
|
+
this.loggerService.log("actionCoreService pendingEvent", {
|
|
20158
|
+
context,
|
|
20159
|
+
});
|
|
20160
|
+
await this.validate(context);
|
|
20161
|
+
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
20162
|
+
for (const actionName of actions) {
|
|
20163
|
+
await this.actionConnectionService.pendingEvent(event, backtest, { actionName, ...context });
|
|
20164
|
+
}
|
|
20165
|
+
};
|
|
19441
20166
|
/**
|
|
19442
20167
|
* Routes active ping event to all registered actions for the strategy.
|
|
19443
20168
|
*
|
|
@@ -19528,14 +20253,14 @@ class ActionCoreService {
|
|
|
19528
20253
|
* @param event - Pending-ping event with action "signal-ping"
|
|
19529
20254
|
* @param context - Strategy execution context
|
|
19530
20255
|
*/
|
|
19531
|
-
this.
|
|
19532
|
-
this.loggerService.log("actionCoreService
|
|
20256
|
+
this.orderCheck = async (backtest, event, context) => {
|
|
20257
|
+
this.loggerService.log("actionCoreService orderCheck", {
|
|
19533
20258
|
context,
|
|
19534
20259
|
});
|
|
19535
20260
|
await this.validate(context);
|
|
19536
20261
|
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
19537
20262
|
for (const actionName of actions) {
|
|
19538
|
-
await this.actionConnectionService.
|
|
20263
|
+
await this.actionConnectionService.orderCheck(event, backtest, { actionName, ...context });
|
|
19539
20264
|
}
|
|
19540
20265
|
};
|
|
19541
20266
|
/**
|
|
@@ -20003,6 +20728,8 @@ const VALID_METHOD_NAMES = [
|
|
|
20003
20728
|
"partialLossAvailable",
|
|
20004
20729
|
"pingScheduled",
|
|
20005
20730
|
"pingActive",
|
|
20731
|
+
"scheduleEvent",
|
|
20732
|
+
"pendingEvent",
|
|
20006
20733
|
"riskRejection",
|
|
20007
20734
|
"dispose",
|
|
20008
20735
|
];
|
|
@@ -20013,7 +20740,7 @@ const VALID_METHOD_NAMES = [
|
|
|
20013
20740
|
* method one of these produces a dedicated error redirecting to the Broker adapter instead of the
|
|
20014
20741
|
* generic "invalid method" suggestion.
|
|
20015
20742
|
*/
|
|
20016
|
-
const DISCOURAGED_METHOD_NAMES = ["signalSync", "
|
|
20743
|
+
const DISCOURAGED_METHOD_NAMES = ["signalSync", "orderCheck"];
|
|
20017
20744
|
/**
|
|
20018
20745
|
* Builds the dedicated error message for a discouraged exchange-integration handler method.
|
|
20019
20746
|
*
|
|
@@ -20025,7 +20752,7 @@ const DISCOURAGED_METHOD_MESSAGE = (actionName, methodName) => str.newline([
|
|
|
20025
20752
|
`ActionSchema ${actionName} contains discouraged method "${methodName}". `,
|
|
20026
20753
|
`Exchange integration must be implemented in Broker.useBrokerAdapter as the infrastructure domain layer, not in an action handler.`,
|
|
20027
20754
|
typo.nbsp,
|
|
20028
|
-
`Use Broker.useBrokerAdapter with
|
|
20755
|
+
`Use Broker.useBrokerAdapter with onOrderCheck (order still open?) and onSignalOpenCommit / onSignalCloseCommit (order fill) instead.`,
|
|
20029
20756
|
typo.nbsp,
|
|
20030
20757
|
`If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`,
|
|
20031
20758
|
]);
|
|
@@ -42404,6 +43131,13 @@ const breakevenNewTakeProfitPrice = (priceTakeProfit, trailingPriceTakeProfit) =
|
|
|
42404
43131
|
const BROKER_METHOD_NAME_COMMIT_SIGNAL_OPEN = "BrokerAdapter.commitSignalOpen";
|
|
42405
43132
|
const BROKER_METHOD_NAME_COMMIT_SIGNAL_CLOSE = "BrokerAdapter.commitSignalClose";
|
|
42406
43133
|
const BROKER_METHOD_NAME_COMMIT_SIGNAL_PENDING = "BrokerAdapter.commitSignalPending";
|
|
43134
|
+
const BROKER_METHOD_NAME_COMMIT_ACTIVE_PING = "BrokerAdapter.commitActivePing";
|
|
43135
|
+
const BROKER_METHOD_NAME_COMMIT_SCHEDULE_PING = "BrokerAdapter.commitSchedulePing";
|
|
43136
|
+
const BROKER_METHOD_NAME_COMMIT_IDLE_PING = "BrokerAdapter.commitIdlePing";
|
|
43137
|
+
const BROKER_METHOD_NAME_COMMIT_SCHEDULE_OPEN = "BrokerAdapter.commitScheduleOpen";
|
|
43138
|
+
const BROKER_METHOD_NAME_COMMIT_SCHEDULE_CANCELLED = "BrokerAdapter.commitScheduleCancelled";
|
|
43139
|
+
const BROKER_METHOD_NAME_COMMIT_PENDING_OPEN = "BrokerAdapter.commitPendingOpen";
|
|
43140
|
+
const BROKER_METHOD_NAME_COMMIT_PENDING_CLOSE = "BrokerAdapter.commitPendingClose";
|
|
42407
43141
|
const BROKER_METHOD_NAME_COMMIT_PARTIAL_PROFIT = "BrokerAdapter.commitPartialProfit";
|
|
42408
43142
|
const BROKER_METHOD_NAME_COMMIT_PARTIAL_LOSS = "BrokerAdapter.commitPartialLoss";
|
|
42409
43143
|
const BROKER_METHOD_NAME_COMMIT_TRAILING_STOP = "BrokerAdapter.commitTrailingStop";
|
|
@@ -42417,7 +43151,14 @@ const BROKER_METHOD_NAME_CLEAR = "BrokerAdapter.clear";
|
|
|
42417
43151
|
const BROKER_BASE_METHOD_NAME_WAIT_FOR_INIT = "BrokerBase.waitForInit";
|
|
42418
43152
|
const BROKER_BASE_METHOD_NAME_ON_SIGNAL_OPEN = "BrokerBase.onSignalOpenCommit";
|
|
42419
43153
|
const BROKER_BASE_METHOD_NAME_ON_SIGNAL_CLOSE = "BrokerBase.onSignalCloseCommit";
|
|
42420
|
-
const BROKER_BASE_METHOD_NAME_ON_SIGNAL_PENDING = "BrokerBase.
|
|
43154
|
+
const BROKER_BASE_METHOD_NAME_ON_SIGNAL_PENDING = "BrokerBase.onOrderCheck";
|
|
43155
|
+
const BROKER_BASE_METHOD_NAME_ON_ACTIVE_PING = "BrokerBase.onSignalActivePing";
|
|
43156
|
+
const BROKER_BASE_METHOD_NAME_ON_SCHEDULE_PING = "BrokerBase.onSignalSchedulePing";
|
|
43157
|
+
const BROKER_BASE_METHOD_NAME_ON_IDLE_PING = "BrokerBase.onSignalIdlePing";
|
|
43158
|
+
const BROKER_BASE_METHOD_NAME_ON_SCHEDULE_OPEN = "BrokerBase.onSignalScheduleOpen";
|
|
43159
|
+
const BROKER_BASE_METHOD_NAME_ON_SCHEDULE_CANCELLED = "BrokerBase.onSignalScheduleCancelled";
|
|
43160
|
+
const BROKER_BASE_METHOD_NAME_ON_PENDING_OPEN = "BrokerBase.onSignalPendingOpen";
|
|
43161
|
+
const BROKER_BASE_METHOD_NAME_ON_PENDING_CLOSE = "BrokerBase.onSignalPendingClose";
|
|
42421
43162
|
const BROKER_BASE_METHOD_NAME_ON_PARTIAL_PROFIT = "BrokerBase.onPartialProfitCommit";
|
|
42422
43163
|
const BROKER_BASE_METHOD_NAME_ON_PARTIAL_LOSS = "BrokerBase.onPartialLossCommit";
|
|
42423
43164
|
const BROKER_BASE_METHOD_NAME_ON_TRAILING_STOP = "BrokerBase.onTrailingStopCommit";
|
|
@@ -42469,7 +43210,7 @@ class BrokerProxy {
|
|
|
42469
43210
|
/**
|
|
42470
43211
|
* Forwards a pending-order ping to the underlying adapter.
|
|
42471
43212
|
*
|
|
42472
|
-
* If the adapter does not implement `
|
|
43213
|
+
* If the adapter does not implement `onOrderCheck`, the call is silently skipped
|
|
42473
43214
|
* (the order is assumed still open). When implemented, exceptions propagate — a throw means
|
|
42474
43215
|
* the order was NOT FOUND by `payload.signalId` and the framework closes the position with
|
|
42475
43216
|
* closeReason "closed". The adapter must throw ONLY on a confirmed "order not found by id"
|
|
@@ -42478,10 +43219,101 @@ class BrokerProxy {
|
|
|
42478
43219
|
*
|
|
42479
43220
|
* @param payload - Pending ping details: symbol, signalId, position, prices, pnl, context, backtest flag.
|
|
42480
43221
|
*/
|
|
42481
|
-
async
|
|
42482
|
-
if (this._instance.
|
|
43222
|
+
async onOrderCheck(payload) {
|
|
43223
|
+
if (this._instance.onOrderCheck) {
|
|
43224
|
+
await this.waitForInit();
|
|
43225
|
+
await this._instance.onOrderCheck(payload);
|
|
43226
|
+
return;
|
|
43227
|
+
}
|
|
43228
|
+
}
|
|
43229
|
+
/**
|
|
43230
|
+
* Forwards an active-ping event to the underlying adapter.
|
|
43231
|
+
* Silently skipped when the adapter does not implement `onSignalActivePing`.
|
|
43232
|
+
*
|
|
43233
|
+
* @param payload - Active ping details: symbol, signalId, position, prices, pnl, context, backtest.
|
|
43234
|
+
*/
|
|
43235
|
+
async onSignalActivePing(payload) {
|
|
43236
|
+
if (this._instance.onSignalActivePing) {
|
|
43237
|
+
await this.waitForInit();
|
|
43238
|
+
await this._instance.onSignalActivePing(payload);
|
|
43239
|
+
return;
|
|
43240
|
+
}
|
|
43241
|
+
}
|
|
43242
|
+
/**
|
|
43243
|
+
* Forwards a schedule-ping event to the underlying adapter.
|
|
43244
|
+
* Silently skipped when the adapter does not implement `onSignalSchedulePing`.
|
|
43245
|
+
*
|
|
43246
|
+
* @param payload - Schedule ping details: symbol, signalId, position, prices, context, backtest.
|
|
43247
|
+
*/
|
|
43248
|
+
async onSignalSchedulePing(payload) {
|
|
43249
|
+
if (this._instance.onSignalSchedulePing) {
|
|
43250
|
+
await this.waitForInit();
|
|
43251
|
+
await this._instance.onSignalSchedulePing(payload);
|
|
43252
|
+
return;
|
|
43253
|
+
}
|
|
43254
|
+
}
|
|
43255
|
+
/**
|
|
43256
|
+
* Forwards an idle-ping event to the underlying adapter.
|
|
43257
|
+
* Silently skipped when the adapter does not implement `onSignalIdlePing`.
|
|
43258
|
+
*
|
|
43259
|
+
* @param payload - Idle ping details: symbol, currentPrice, context, backtest.
|
|
43260
|
+
*/
|
|
43261
|
+
async onSignalIdlePing(payload) {
|
|
43262
|
+
if (this._instance.onSignalIdlePing) {
|
|
43263
|
+
await this.waitForInit();
|
|
43264
|
+
await this._instance.onSignalIdlePing(payload);
|
|
43265
|
+
return;
|
|
43266
|
+
}
|
|
43267
|
+
}
|
|
43268
|
+
/**
|
|
43269
|
+
* Forwards a scheduled-signal-open event to the underlying adapter.
|
|
43270
|
+
* Silently skipped when the adapter does not implement `onSignalScheduleOpen`.
|
|
43271
|
+
*
|
|
43272
|
+
* @param payload - Scheduled open details: symbol, signalId, position, prices, context, backtest.
|
|
43273
|
+
*/
|
|
43274
|
+
async onSignalScheduleOpen(payload) {
|
|
43275
|
+
if (this._instance.onSignalScheduleOpen) {
|
|
43276
|
+
await this.waitForInit();
|
|
43277
|
+
await this._instance.onSignalScheduleOpen(payload);
|
|
43278
|
+
return;
|
|
43279
|
+
}
|
|
43280
|
+
}
|
|
43281
|
+
/**
|
|
43282
|
+
* Forwards a scheduled-signal-cancelled event to the underlying adapter.
|
|
43283
|
+
* Silently skipped when the adapter does not implement `onSignalScheduleCancelled`.
|
|
43284
|
+
*
|
|
43285
|
+
* @param payload - Scheduled cancel details: symbol, signalId, position, prices, reason, context, backtest.
|
|
43286
|
+
*/
|
|
43287
|
+
async onSignalScheduleCancelled(payload) {
|
|
43288
|
+
if (this._instance.onSignalScheduleCancelled) {
|
|
43289
|
+
await this.waitForInit();
|
|
43290
|
+
await this._instance.onSignalScheduleCancelled(payload);
|
|
43291
|
+
return;
|
|
43292
|
+
}
|
|
43293
|
+
}
|
|
43294
|
+
/**
|
|
43295
|
+
* Forwards a pending-signal-open event to the underlying adapter.
|
|
43296
|
+
* Silently skipped when the adapter does not implement `onSignalPendingOpen`.
|
|
43297
|
+
*
|
|
43298
|
+
* @param payload - Pending open details: symbol, signalId, position, prices, context, backtest.
|
|
43299
|
+
*/
|
|
43300
|
+
async onSignalPendingOpen(payload) {
|
|
43301
|
+
if (this._instance.onSignalPendingOpen) {
|
|
42483
43302
|
await this.waitForInit();
|
|
42484
|
-
await this._instance.
|
|
43303
|
+
await this._instance.onSignalPendingOpen(payload);
|
|
43304
|
+
return;
|
|
43305
|
+
}
|
|
43306
|
+
}
|
|
43307
|
+
/**
|
|
43308
|
+
* Forwards a pending-signal-close event to the underlying adapter.
|
|
43309
|
+
* Silently skipped when the adapter does not implement `onSignalPendingClose`.
|
|
43310
|
+
*
|
|
43311
|
+
* @param payload - Pending close details: symbol, signalId, position, prices, closeReason, context, backtest.
|
|
43312
|
+
*/
|
|
43313
|
+
async onSignalPendingClose(payload) {
|
|
43314
|
+
if (this._instance.onSignalPendingClose) {
|
|
43315
|
+
await this.waitForInit();
|
|
43316
|
+
await this._instance.onSignalPendingClose(payload);
|
|
42485
43317
|
return;
|
|
42486
43318
|
}
|
|
42487
43319
|
}
|
|
@@ -42737,7 +43569,178 @@ class BrokerAdapter {
|
|
|
42737
43569
|
}
|
|
42738
43570
|
const instance = this.getInstance();
|
|
42739
43571
|
if (instance) {
|
|
42740
|
-
await instance.
|
|
43572
|
+
await instance.onOrderCheck(payload);
|
|
43573
|
+
}
|
|
43574
|
+
};
|
|
43575
|
+
/**
|
|
43576
|
+
* Forwards an active-ping to the registered broker adapter.
|
|
43577
|
+
*
|
|
43578
|
+
* Called automatically via activePingSubject when `enable()` is active, on every live tick while a
|
|
43579
|
+
* pending signal is monitored. Skipped silently in backtest mode or when no adapter is registered.
|
|
43580
|
+
* Purely informational — a throw does NOT close the position.
|
|
43581
|
+
*
|
|
43582
|
+
* @param payload - Active ping details: symbol, signalId, position, prices, pnl, context, backtest
|
|
43583
|
+
*/
|
|
43584
|
+
this.commitActivePing = async (payload) => {
|
|
43585
|
+
bt.loggerService.info(BROKER_METHOD_NAME_COMMIT_ACTIVE_PING, {
|
|
43586
|
+
symbol: payload.symbol,
|
|
43587
|
+
context: payload.context,
|
|
43588
|
+
});
|
|
43589
|
+
if (!this.enable.hasValue()) {
|
|
43590
|
+
return;
|
|
43591
|
+
}
|
|
43592
|
+
if (payload.backtest) {
|
|
43593
|
+
return;
|
|
43594
|
+
}
|
|
43595
|
+
const instance = this.getInstance();
|
|
43596
|
+
if (instance) {
|
|
43597
|
+
await instance.onSignalActivePing(payload);
|
|
43598
|
+
}
|
|
43599
|
+
};
|
|
43600
|
+
/**
|
|
43601
|
+
* Forwards a schedule-ping to the registered broker adapter.
|
|
43602
|
+
*
|
|
43603
|
+
* Called automatically via schedulePingSubject when `enable()` is active, on every live tick while
|
|
43604
|
+
* a scheduled signal is monitored. Skipped silently in backtest mode or when no adapter is
|
|
43605
|
+
* registered. Purely informational.
|
|
43606
|
+
*
|
|
43607
|
+
* @param payload - Schedule ping details: symbol, signalId, position, prices, context, backtest
|
|
43608
|
+
*/
|
|
43609
|
+
this.commitSchedulePing = async (payload) => {
|
|
43610
|
+
bt.loggerService.info(BROKER_METHOD_NAME_COMMIT_SCHEDULE_PING, {
|
|
43611
|
+
symbol: payload.symbol,
|
|
43612
|
+
context: payload.context,
|
|
43613
|
+
});
|
|
43614
|
+
if (!this.enable.hasValue()) {
|
|
43615
|
+
return;
|
|
43616
|
+
}
|
|
43617
|
+
if (payload.backtest) {
|
|
43618
|
+
return;
|
|
43619
|
+
}
|
|
43620
|
+
const instance = this.getInstance();
|
|
43621
|
+
if (instance) {
|
|
43622
|
+
await instance.onSignalSchedulePing(payload);
|
|
43623
|
+
}
|
|
43624
|
+
};
|
|
43625
|
+
/**
|
|
43626
|
+
* Forwards an idle-ping to the registered broker adapter.
|
|
43627
|
+
*
|
|
43628
|
+
* Called automatically via idlePingSubject when `enable()` is active, on every live tick while the
|
|
43629
|
+
* strategy has no pending or scheduled signal. Skipped silently in backtest mode or when no adapter
|
|
43630
|
+
* is registered. Purely informational.
|
|
43631
|
+
*
|
|
43632
|
+
* @param payload - Idle ping details: symbol, currentPrice, context, backtest
|
|
43633
|
+
*/
|
|
43634
|
+
this.commitIdlePing = async (payload) => {
|
|
43635
|
+
bt.loggerService.info(BROKER_METHOD_NAME_COMMIT_IDLE_PING, {
|
|
43636
|
+
symbol: payload.symbol,
|
|
43637
|
+
context: payload.context,
|
|
43638
|
+
});
|
|
43639
|
+
if (!this.enable.hasValue()) {
|
|
43640
|
+
return;
|
|
43641
|
+
}
|
|
43642
|
+
if (payload.backtest) {
|
|
43643
|
+
return;
|
|
43644
|
+
}
|
|
43645
|
+
const instance = this.getInstance();
|
|
43646
|
+
if (instance) {
|
|
43647
|
+
await instance.onSignalIdlePing(payload);
|
|
43648
|
+
}
|
|
43649
|
+
};
|
|
43650
|
+
/**
|
|
43651
|
+
* Forwards a scheduled-signal-open to the registered broker adapter.
|
|
43652
|
+
*
|
|
43653
|
+
* Called automatically via scheduleEventSubject (action "scheduled") when a scheduled signal is
|
|
43654
|
+
* created. Skipped silently in backtest mode or when no adapter is registered.
|
|
43655
|
+
*
|
|
43656
|
+
* @param payload - Scheduled open details: symbol, signalId, position, prices, context, backtest
|
|
43657
|
+
*/
|
|
43658
|
+
this.commitScheduleOpen = async (payload) => {
|
|
43659
|
+
bt.loggerService.info(BROKER_METHOD_NAME_COMMIT_SCHEDULE_OPEN, {
|
|
43660
|
+
symbol: payload.symbol,
|
|
43661
|
+
context: payload.context,
|
|
43662
|
+
});
|
|
43663
|
+
if (!this.enable.hasValue()) {
|
|
43664
|
+
return;
|
|
43665
|
+
}
|
|
43666
|
+
if (payload.backtest) {
|
|
43667
|
+
return;
|
|
43668
|
+
}
|
|
43669
|
+
const instance = this.getInstance();
|
|
43670
|
+
if (instance) {
|
|
43671
|
+
await instance.onSignalScheduleOpen(payload);
|
|
43672
|
+
}
|
|
43673
|
+
};
|
|
43674
|
+
/**
|
|
43675
|
+
* Forwards a scheduled-signal-cancelled to the registered broker adapter.
|
|
43676
|
+
*
|
|
43677
|
+
* Called automatically via scheduleEventSubject (action "cancelled") when a scheduled signal is
|
|
43678
|
+
* removed before activation. Skipped silently in backtest mode or when no adapter is registered.
|
|
43679
|
+
*
|
|
43680
|
+
* @param payload - Scheduled cancel details: symbol, signalId, position, prices, reason, context, backtest
|
|
43681
|
+
*/
|
|
43682
|
+
this.commitScheduleCancelled = async (payload) => {
|
|
43683
|
+
bt.loggerService.info(BROKER_METHOD_NAME_COMMIT_SCHEDULE_CANCELLED, {
|
|
43684
|
+
symbol: payload.symbol,
|
|
43685
|
+
context: payload.context,
|
|
43686
|
+
});
|
|
43687
|
+
if (!this.enable.hasValue()) {
|
|
43688
|
+
return;
|
|
43689
|
+
}
|
|
43690
|
+
if (payload.backtest) {
|
|
43691
|
+
return;
|
|
43692
|
+
}
|
|
43693
|
+
const instance = this.getInstance();
|
|
43694
|
+
if (instance) {
|
|
43695
|
+
await instance.onSignalScheduleCancelled(payload);
|
|
43696
|
+
}
|
|
43697
|
+
};
|
|
43698
|
+
/**
|
|
43699
|
+
* Forwards a pending-signal-open to the registered broker adapter.
|
|
43700
|
+
*
|
|
43701
|
+
* Called automatically via signalEventSubject (action "opened") when a pending position is opened.
|
|
43702
|
+
* Skipped silently in backtest mode or when no adapter is registered.
|
|
43703
|
+
*
|
|
43704
|
+
* @param payload - Pending open details: symbol, signalId, position, prices, context, backtest
|
|
43705
|
+
*/
|
|
43706
|
+
this.commitPendingOpen = async (payload) => {
|
|
43707
|
+
bt.loggerService.info(BROKER_METHOD_NAME_COMMIT_PENDING_OPEN, {
|
|
43708
|
+
symbol: payload.symbol,
|
|
43709
|
+
context: payload.context,
|
|
43710
|
+
});
|
|
43711
|
+
if (!this.enable.hasValue()) {
|
|
43712
|
+
return;
|
|
43713
|
+
}
|
|
43714
|
+
if (payload.backtest) {
|
|
43715
|
+
return;
|
|
43716
|
+
}
|
|
43717
|
+
const instance = this.getInstance();
|
|
43718
|
+
if (instance) {
|
|
43719
|
+
await instance.onSignalPendingOpen(payload);
|
|
43720
|
+
}
|
|
43721
|
+
};
|
|
43722
|
+
/**
|
|
43723
|
+
* Forwards a pending-signal-close to the registered broker adapter.
|
|
43724
|
+
*
|
|
43725
|
+
* Called automatically via signalEventSubject (action "closed") when a pending position is closed.
|
|
43726
|
+
* Skipped silently in backtest mode or when no adapter is registered.
|
|
43727
|
+
*
|
|
43728
|
+
* @param payload - Pending close details: symbol, signalId, position, prices, closeReason, context, backtest
|
|
43729
|
+
*/
|
|
43730
|
+
this.commitPendingClose = async (payload) => {
|
|
43731
|
+
bt.loggerService.info(BROKER_METHOD_NAME_COMMIT_PENDING_CLOSE, {
|
|
43732
|
+
symbol: payload.symbol,
|
|
43733
|
+
context: payload.context,
|
|
43734
|
+
});
|
|
43735
|
+
if (!this.enable.hasValue()) {
|
|
43736
|
+
return;
|
|
43737
|
+
}
|
|
43738
|
+
if (payload.backtest) {
|
|
43739
|
+
return;
|
|
43740
|
+
}
|
|
43741
|
+
const instance = this.getInstance();
|
|
43742
|
+
if (instance) {
|
|
43743
|
+
await instance.onSignalPendingClose(payload);
|
|
42741
43744
|
}
|
|
42742
43745
|
};
|
|
42743
43746
|
/**
|
|
@@ -43105,7 +44108,98 @@ class BrokerAdapter {
|
|
|
43105
44108
|
backtest: event.backtest,
|
|
43106
44109
|
});
|
|
43107
44110
|
});
|
|
43108
|
-
const
|
|
44111
|
+
const unActivePing = activePingSubject.subscribe(async (event) => {
|
|
44112
|
+
await this.commitActivePing({
|
|
44113
|
+
symbol: event.symbol,
|
|
44114
|
+
signalId: event.data.id,
|
|
44115
|
+
position: event.data.position,
|
|
44116
|
+
currentPrice: event.currentPrice,
|
|
44117
|
+
priceOpen: event.data.priceOpen,
|
|
44118
|
+
priceTakeProfit: event.data.priceTakeProfit,
|
|
44119
|
+
priceStopLoss: event.data.priceStopLoss,
|
|
44120
|
+
pnl: event.data.pnl,
|
|
44121
|
+
context: {
|
|
44122
|
+
strategyName: event.strategyName,
|
|
44123
|
+
exchangeName: event.exchangeName,
|
|
44124
|
+
frameName: event.frameName,
|
|
44125
|
+
},
|
|
44126
|
+
backtest: event.backtest,
|
|
44127
|
+
});
|
|
44128
|
+
});
|
|
44129
|
+
const unSchedulePing = schedulePingSubject.subscribe(async (event) => {
|
|
44130
|
+
await this.commitSchedulePing({
|
|
44131
|
+
symbol: event.symbol,
|
|
44132
|
+
signalId: event.data.id,
|
|
44133
|
+
position: event.data.position,
|
|
44134
|
+
currentPrice: event.currentPrice,
|
|
44135
|
+
priceOpen: event.data.priceOpen,
|
|
44136
|
+
priceTakeProfit: event.data.priceTakeProfit,
|
|
44137
|
+
priceStopLoss: event.data.priceStopLoss,
|
|
44138
|
+
context: {
|
|
44139
|
+
strategyName: event.strategyName,
|
|
44140
|
+
exchangeName: event.exchangeName,
|
|
44141
|
+
frameName: event.frameName,
|
|
44142
|
+
},
|
|
44143
|
+
backtest: event.backtest,
|
|
44144
|
+
});
|
|
44145
|
+
});
|
|
44146
|
+
const unIdlePing = idlePingSubject.subscribe(async (event) => {
|
|
44147
|
+
await this.commitIdlePing({
|
|
44148
|
+
symbol: event.symbol,
|
|
44149
|
+
currentPrice: event.currentPrice,
|
|
44150
|
+
context: {
|
|
44151
|
+
strategyName: event.strategyName,
|
|
44152
|
+
exchangeName: event.exchangeName,
|
|
44153
|
+
frameName: event.frameName,
|
|
44154
|
+
},
|
|
44155
|
+
backtest: event.backtest,
|
|
44156
|
+
});
|
|
44157
|
+
});
|
|
44158
|
+
const unScheduleEvent = scheduleEventSubject.subscribe(async (event) => {
|
|
44159
|
+
const payload = {
|
|
44160
|
+
symbol: event.symbol,
|
|
44161
|
+
signalId: event.data.id,
|
|
44162
|
+
position: event.data.position,
|
|
44163
|
+
currentPrice: event.currentPrice,
|
|
44164
|
+
priceOpen: event.data.priceOpen,
|
|
44165
|
+
priceTakeProfit: event.data.priceTakeProfit,
|
|
44166
|
+
priceStopLoss: event.data.priceStopLoss,
|
|
44167
|
+
context: {
|
|
44168
|
+
strategyName: event.strategyName,
|
|
44169
|
+
exchangeName: event.exchangeName,
|
|
44170
|
+
frameName: event.frameName,
|
|
44171
|
+
},
|
|
44172
|
+
backtest: event.backtest,
|
|
44173
|
+
};
|
|
44174
|
+
if (event.action === "scheduled") {
|
|
44175
|
+
await this.commitScheduleOpen(payload);
|
|
44176
|
+
return;
|
|
44177
|
+
}
|
|
44178
|
+
await this.commitScheduleCancelled({ ...payload, reason: event.reason });
|
|
44179
|
+
});
|
|
44180
|
+
const unSignalEvent = signalEventSubject.subscribe(async (event) => {
|
|
44181
|
+
const payload = {
|
|
44182
|
+
symbol: event.symbol,
|
|
44183
|
+
signalId: event.data.id,
|
|
44184
|
+
position: event.data.position,
|
|
44185
|
+
currentPrice: event.currentPrice,
|
|
44186
|
+
priceOpen: event.data.priceOpen,
|
|
44187
|
+
priceTakeProfit: event.data.priceTakeProfit,
|
|
44188
|
+
priceStopLoss: event.data.priceStopLoss,
|
|
44189
|
+
context: {
|
|
44190
|
+
strategyName: event.strategyName,
|
|
44191
|
+
exchangeName: event.exchangeName,
|
|
44192
|
+
frameName: event.frameName,
|
|
44193
|
+
},
|
|
44194
|
+
backtest: event.backtest,
|
|
44195
|
+
};
|
|
44196
|
+
if (event.action === "opened") {
|
|
44197
|
+
await this.commitPendingOpen(payload);
|
|
44198
|
+
return;
|
|
44199
|
+
}
|
|
44200
|
+
await this.commitPendingClose({ ...payload, closeReason: event.closeReason });
|
|
44201
|
+
});
|
|
44202
|
+
const disposeFn = compose(() => unSignalOpen(), () => unSignalClose(), () => unSignalPending(), () => unActivePing(), () => unSchedulePing(), () => unIdlePing(), () => unScheduleEvent(), () => unSignalEvent());
|
|
43109
44203
|
return () => {
|
|
43110
44204
|
this.enable.clear();
|
|
43111
44205
|
disposeFn();
|
|
@@ -43251,24 +44345,32 @@ class BrokerBase {
|
|
|
43251
44345
|
bt.loggerService.info(BROKER_BASE_METHOD_NAME_WAIT_FOR_INIT, {});
|
|
43252
44346
|
}
|
|
43253
44347
|
/**
|
|
43254
|
-
* Called when a
|
|
44348
|
+
* Called when a position is being opened (signal activated).
|
|
43255
44349
|
*
|
|
43256
44350
|
* Triggered automatically via syncSubject when a scheduled signal's priceOpen is hit.
|
|
43257
44351
|
* Use to place the actual entry order on the exchange.
|
|
43258
44352
|
*
|
|
43259
44353
|
* Default implementation: Logs signal-open event.
|
|
43260
44354
|
*
|
|
44355
|
+
* Manual wiring — EXCEPTION-BASED GATE: emitted BEFORE the framework mutates state, so a THROW here
|
|
44356
|
+
* (e.g. limit order rejected) rolls back the open — the pending signal returns to idle and retries
|
|
44357
|
+
* next tick; return normally to let it open. Live-only (backtest short-circuits). See
|
|
44358
|
+
* {@link IBroker.onSignalOpenCommit} for the full semantics.
|
|
44359
|
+
*
|
|
43261
44360
|
* @param payload - Signal open details: symbol, cost, position, priceOpen, priceTakeProfit, priceStopLoss, context, backtest
|
|
43262
44361
|
*
|
|
43263
44362
|
* @example
|
|
43264
44363
|
* ```typescript
|
|
43265
44364
|
* async onSignalOpenCommit(payload: BrokerSignalOpenPayload) {
|
|
43266
44365
|
* super.onSignalOpenCommit(payload); // Keep parent logging
|
|
43267
|
-
* await this.exchange.placeMarketOrder({
|
|
44366
|
+
* const order = await this.exchange.placeMarketOrder({
|
|
43268
44367
|
* symbol: payload.symbol,
|
|
43269
44368
|
* side: payload.position === "long" ? "BUY" : "SELL",
|
|
43270
44369
|
* quantity: payload.cost / payload.priceOpen,
|
|
43271
44370
|
* });
|
|
44371
|
+
* if (!order.filled) {
|
|
44372
|
+
* throw new Error(`Entry not filled for ${payload.symbol}`); // -> roll back the open, retry next tick
|
|
44373
|
+
* }
|
|
43272
44374
|
* }
|
|
43273
44375
|
* ```
|
|
43274
44376
|
*/
|
|
@@ -43290,47 +44392,158 @@ class BrokerBase {
|
|
|
43290
44392
|
* normally instead of throwing. A thrown network error would wrongly close an open position; only
|
|
43291
44393
|
* a confirmed "order not found by id" response is a valid reason to throw.
|
|
43292
44394
|
*
|
|
43293
|
-
*
|
|
44395
|
+
* Manual wiring — EXCEPTION-BASED VARIANT: the throw-driven alternative to the imperative
|
|
44396
|
+
* commit-function wiring in `onSignalActivePing`. See {@link IBroker.onOrderCheck} for the full
|
|
44397
|
+
* comparison and example.
|
|
43294
44398
|
*
|
|
43295
|
-
* @
|
|
43296
|
-
* ```typescript
|
|
43297
|
-
* async onOrderPing(payload: BrokerSignalPendingPayload) {
|
|
43298
|
-
* super.onOrderPing(payload); // Keep parent logging
|
|
43299
|
-
* let order: Order | null;
|
|
43300
|
-
* try {
|
|
43301
|
-
* order = await this.exchange.getOrderById(payload.signalId);
|
|
43302
|
-
* } catch (networkError) {
|
|
43303
|
-
* // Transient/network error — swallow and keep the position open, retry next tick
|
|
43304
|
-
* return;
|
|
43305
|
-
* }
|
|
43306
|
-
* if (!order) {
|
|
43307
|
-
* // Confirmed not found by id — close the position
|
|
43308
|
-
* throw new Error(`Order ${payload.signalId} not found on the exchange`);
|
|
43309
|
-
* }
|
|
43310
|
-
* }
|
|
43311
|
-
* ```
|
|
44399
|
+
* @param payload - Pending ping details: symbol, signalId, position, prices, pnl, context, backtest
|
|
43312
44400
|
*/
|
|
43313
|
-
async
|
|
44401
|
+
async onOrderCheck(payload) {
|
|
43314
44402
|
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_SIGNAL_PENDING, {
|
|
43315
44403
|
symbol: payload.symbol,
|
|
43316
44404
|
context: payload.context,
|
|
43317
44405
|
});
|
|
43318
44406
|
}
|
|
43319
44407
|
/**
|
|
43320
|
-
* Called
|
|
44408
|
+
* Called on every live tick while a pending (open) signal is monitored.
|
|
44409
|
+
*
|
|
44410
|
+
* Purely informational mirror of the active-ping lifecycle — unlike `onOrderCheck`, a throw here
|
|
44411
|
+
* does NOT close the position. Override to mirror live monitoring state into your own systems.
|
|
44412
|
+
* The default implementation logs.
|
|
44413
|
+
*
|
|
44414
|
+
* Manual wiring — EVENT-BASED: this is the primary per-tick hook to drive an open position from real exchange
|
|
44415
|
+
* state (`commitCreateTakeProfit` / `commitCreateStopLoss` / `commitClosePending`). See the
|
|
44416
|
+
* {@link IBroker.onSignalActivePing} contract docs for the full guidance and example.
|
|
44417
|
+
*
|
|
44418
|
+
* @param payload - Active ping details: symbol, signalId, position, prices, pnl, context, backtest
|
|
44419
|
+
*/
|
|
44420
|
+
async onSignalActivePing(payload) {
|
|
44421
|
+
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_ACTIVE_PING, {
|
|
44422
|
+
symbol: payload.symbol,
|
|
44423
|
+
context: payload.context,
|
|
44424
|
+
});
|
|
44425
|
+
}
|
|
44426
|
+
/**
|
|
44427
|
+
* Called on every live tick while a scheduled signal is monitored (waiting for priceOpen).
|
|
44428
|
+
*
|
|
44429
|
+
* Purely informational. Override to mirror scheduled-monitoring state. The default logs.
|
|
44430
|
+
*
|
|
44431
|
+
* Manual wiring — EVENT-BASED: per-tick hook to drive a scheduled (resting) order from real exchange state
|
|
44432
|
+
* (`commitActivateScheduled` / `commitCancelScheduled`). See {@link IBroker.onSignalSchedulePing}
|
|
44433
|
+
* for full guidance and example.
|
|
44434
|
+
*
|
|
44435
|
+
* @param payload - Schedule ping details: symbol, signalId, position, prices, context, backtest
|
|
44436
|
+
*/
|
|
44437
|
+
async onSignalSchedulePing(payload) {
|
|
44438
|
+
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_SCHEDULE_PING, {
|
|
44439
|
+
symbol: payload.symbol,
|
|
44440
|
+
context: payload.context,
|
|
44441
|
+
});
|
|
44442
|
+
}
|
|
44443
|
+
/**
|
|
44444
|
+
* Called on every live tick while the strategy is idle (no pending or scheduled signal).
|
|
44445
|
+
*
|
|
44446
|
+
* Purely informational. Override to track idle heartbeats. The default logs.
|
|
44447
|
+
*
|
|
44448
|
+
* @param payload - Idle ping details: symbol, currentPrice, context, backtest
|
|
44449
|
+
*/
|
|
44450
|
+
async onSignalIdlePing(payload) {
|
|
44451
|
+
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_IDLE_PING, {
|
|
44452
|
+
symbol: payload.symbol,
|
|
44453
|
+
context: payload.context,
|
|
44454
|
+
});
|
|
44455
|
+
}
|
|
44456
|
+
/**
|
|
44457
|
+
* Called when a new scheduled signal is created and starts waiting for priceOpen activation.
|
|
44458
|
+
*
|
|
44459
|
+
* The scheduled -> active transition is reported via `onSignalOpenCommit`, not here. Override to
|
|
44460
|
+
* place a resting/limit order on the exchange. The default logs.
|
|
44461
|
+
*
|
|
44462
|
+
* Manual wiring — EVENT-BASED: fires ONCE at creation — place the real resting order (tag it with
|
|
44463
|
+
* `payload.signalId`) and optionally `commitActivateScheduled` / `commitCancelScheduled`. See
|
|
44464
|
+
* {@link IBroker.onSignalScheduleOpen} for full guidance and example.
|
|
44465
|
+
*
|
|
44466
|
+
* @param payload - Scheduled open details: symbol, signalId, position, prices, context, backtest
|
|
44467
|
+
*/
|
|
44468
|
+
async onSignalScheduleOpen(payload) {
|
|
44469
|
+
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_SCHEDULE_OPEN, {
|
|
44470
|
+
symbol: payload.symbol,
|
|
44471
|
+
context: payload.context,
|
|
44472
|
+
});
|
|
44473
|
+
}
|
|
44474
|
+
/**
|
|
44475
|
+
* Called when a scheduled signal is cancelled before activation (timeout / price_reject / user).
|
|
44476
|
+
*
|
|
44477
|
+
* Override to cancel the resting/limit order on the exchange. The default logs.
|
|
44478
|
+
*
|
|
44479
|
+
* Manual wiring — EVENT-BASED (outbound): the strategy already dropped the scheduled signal — cancel the matching
|
|
44480
|
+
* exchange order by `payload.signalId`. See {@link IBroker.onSignalScheduleCancelled}.
|
|
44481
|
+
*
|
|
44482
|
+
* @param payload - Scheduled cancel details: symbol, signalId, position, prices, reason, context, backtest
|
|
44483
|
+
*/
|
|
44484
|
+
async onSignalScheduleCancelled(payload) {
|
|
44485
|
+
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_SCHEDULE_CANCELLED, {
|
|
44486
|
+
symbol: payload.symbol,
|
|
44487
|
+
context: payload.context,
|
|
44488
|
+
});
|
|
44489
|
+
}
|
|
44490
|
+
/**
|
|
44491
|
+
* Called when a pending position is opened (new signal / immediate / scheduled or user activation).
|
|
44492
|
+
*
|
|
44493
|
+
* Informational lifecycle hook. Override to mirror the open into your own systems. The default logs.
|
|
44494
|
+
*
|
|
44495
|
+
* Manual wiring — EVENT-BASED: fires ONCE at open — place entry + protective TP/SL orders (tag with
|
|
44496
|
+
* `payload.signalId`), then drive per-tick from `onSignalActivePing`. See
|
|
44497
|
+
* {@link IBroker.onSignalPendingOpen}.
|
|
44498
|
+
*
|
|
44499
|
+
* @param payload - Pending open details: symbol, signalId, position, prices, context, backtest
|
|
44500
|
+
*/
|
|
44501
|
+
async onSignalPendingOpen(payload) {
|
|
44502
|
+
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_PENDING_OPEN, {
|
|
44503
|
+
symbol: payload.symbol,
|
|
44504
|
+
context: payload.context,
|
|
44505
|
+
});
|
|
44506
|
+
}
|
|
44507
|
+
/**
|
|
44508
|
+
* Called when a pending position is closed (take_profit / stop_loss / time_expired / closed).
|
|
44509
|
+
*
|
|
44510
|
+
* Informational lifecycle hook. Override to mirror the close into your own systems. The default logs.
|
|
44511
|
+
*
|
|
44512
|
+
* Manual wiring — EVENT-BASED (outbound): the strategy already removed the pending signal — flatten the real
|
|
44513
|
+
* position and cancel leftover TP/SL orders by `payload.signalId`. See
|
|
44514
|
+
* {@link IBroker.onSignalPendingClose}.
|
|
44515
|
+
*
|
|
44516
|
+
* @param payload - Pending close details: symbol, signalId, position, prices, closeReason, context, backtest
|
|
44517
|
+
*/
|
|
44518
|
+
async onSignalPendingClose(payload) {
|
|
44519
|
+
bt.loggerService.info(BROKER_BASE_METHOD_NAME_ON_PENDING_CLOSE, {
|
|
44520
|
+
symbol: payload.symbol,
|
|
44521
|
+
context: payload.context,
|
|
44522
|
+
});
|
|
44523
|
+
}
|
|
44524
|
+
/**
|
|
44525
|
+
* Called when a position is being closed (SL/TP hit or manual close).
|
|
43321
44526
|
*
|
|
43322
44527
|
* Triggered automatically via syncSubject when a pending signal is closed.
|
|
43323
44528
|
* Use to place the exit order and record final PnL.
|
|
43324
44529
|
*
|
|
43325
44530
|
* Default implementation: Logs signal-close event.
|
|
43326
44531
|
*
|
|
44532
|
+
* Manual wiring — EXCEPTION-BASED GATE: emitted BEFORE the framework mutates state, so a THROW here
|
|
44533
|
+
* (e.g. exit order failed) SKIPS the close — the position stays open and the close retries next
|
|
44534
|
+
* tick; return normally to let it close. Live-only (backtest short-circuits). See
|
|
44535
|
+
* {@link IBroker.onSignalCloseCommit} for the full semantics.
|
|
44536
|
+
*
|
|
43327
44537
|
* @param payload - Signal close details: symbol, cost, position, currentPrice, pnl, totalEntries, totalPartials, context, backtest
|
|
43328
44538
|
*
|
|
43329
44539
|
* @example
|
|
43330
44540
|
* ```typescript
|
|
43331
44541
|
* async onSignalCloseCommit(payload: BrokerSignalClosePayload) {
|
|
43332
44542
|
* super.onSignalCloseCommit(payload); // Keep parent logging
|
|
43333
|
-
* await this.exchange.closePosition(payload.symbol);
|
|
44543
|
+
* const ok = await this.exchange.closePosition(payload.symbol);
|
|
44544
|
+
* if (!ok) {
|
|
44545
|
+
* throw new Error(`Exit not filled for ${payload.symbol}`); // -> keep position open, retry next tick
|
|
44546
|
+
* }
|
|
43334
44547
|
* await this.db.recordTrade({ symbol: payload.symbol, pnl: payload.pnl });
|
|
43335
44548
|
* }
|
|
43336
44549
|
* ```
|
|
@@ -43586,6 +44799,8 @@ const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
|
|
|
43586
44799
|
const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
|
|
43587
44800
|
const COMMIT_SIGNAL_NOTIFY_METHOD_NAME = "strategy.commitSignalNotify";
|
|
43588
44801
|
const COMMIT_CREATE_SIGNAL_METHOD_NAME = "strategy.commitCreateSignal";
|
|
44802
|
+
const COMMIT_CREATE_TAKE_PROFIT_METHOD_NAME = "strategy.commitCreateTakeProfit";
|
|
44803
|
+
const COMMIT_CREATE_STOP_LOSS_METHOD_NAME = "strategy.commitCreateStopLoss";
|
|
43589
44804
|
const GET_STRATEGY_STATUS_METHOD_NAME = "strategy.getStrategyStatus";
|
|
43590
44805
|
/**
|
|
43591
44806
|
* Cancels the scheduled signal without stopping the strategy.
|
|
@@ -45695,6 +46910,74 @@ async function commitCreateSignal(symbol, dto) {
|
|
|
45695
46910
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
45696
46911
|
await bt.strategyCoreService.createSignal(isBacktest, symbol, dto, { exchangeName, frameName, strategyName });
|
|
45697
46912
|
}
|
|
46913
|
+
/**
|
|
46914
|
+
* Reports that the pending position's take-profit order was actually filled on the exchange
|
|
46915
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based TP check.
|
|
46916
|
+
*
|
|
46917
|
+
* The exchange and the strategy are parallel states: the framework evaluates TP/SL against VWAP,
|
|
46918
|
+
* but the real order may fill on high/low. The close is deferred and emitted with closeReason
|
|
46919
|
+
* "take_profit" on the next tick. No-op if no pending signal exists.
|
|
46920
|
+
*
|
|
46921
|
+
* Automatically detects backtest/live mode from execution context.
|
|
46922
|
+
*
|
|
46923
|
+
* @param symbol - Trading pair symbol
|
|
46924
|
+
* @param payload - Optional commit payload with id and note
|
|
46925
|
+
* @returns Promise that resolves when the take-profit fill is queued
|
|
46926
|
+
*
|
|
46927
|
+
* @example
|
|
46928
|
+
* ```typescript
|
|
46929
|
+
* import { commitCreateTakeProfit } from "backtest-kit";
|
|
46930
|
+
*
|
|
46931
|
+
* // Report TP fill confirmed on the exchange
|
|
46932
|
+
* await commitCreateTakeProfit("BTCUSDT", { id: "tp-fill-001" });
|
|
46933
|
+
* ```
|
|
46934
|
+
*/
|
|
46935
|
+
async function commitCreateTakeProfit(symbol, payload = {}) {
|
|
46936
|
+
bt.loggerService.info(COMMIT_CREATE_TAKE_PROFIT_METHOD_NAME, { symbol, payload });
|
|
46937
|
+
if (!ExecutionContextService.hasContext()) {
|
|
46938
|
+
throw new Error("commitCreateTakeProfit requires an execution context");
|
|
46939
|
+
}
|
|
46940
|
+
if (!MethodContextService.hasContext()) {
|
|
46941
|
+
throw new Error("commitCreateTakeProfit requires a method context");
|
|
46942
|
+
}
|
|
46943
|
+
const { backtest: isBacktest } = bt.executionContextService.context;
|
|
46944
|
+
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
46945
|
+
await bt.strategyCoreService.createTakeProfit(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
|
|
46946
|
+
}
|
|
46947
|
+
/**
|
|
46948
|
+
* Reports that the pending position's stop-loss order was actually filled on the exchange
|
|
46949
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based SL check.
|
|
46950
|
+
*
|
|
46951
|
+
* The exchange and the strategy are parallel states: the framework evaluates TP/SL against VWAP,
|
|
46952
|
+
* but the real order may fill on high/low. The close is deferred and emitted with closeReason
|
|
46953
|
+
* "stop_loss" on the next tick. No-op if no pending signal exists.
|
|
46954
|
+
*
|
|
46955
|
+
* Automatically detects backtest/live mode from execution context.
|
|
46956
|
+
*
|
|
46957
|
+
* @param symbol - Trading pair symbol
|
|
46958
|
+
* @param payload - Optional commit payload with id and note
|
|
46959
|
+
* @returns Promise that resolves when the stop-loss fill is queued
|
|
46960
|
+
*
|
|
46961
|
+
* @example
|
|
46962
|
+
* ```typescript
|
|
46963
|
+
* import { commitCreateStopLoss } from "backtest-kit";
|
|
46964
|
+
*
|
|
46965
|
+
* // Report SL fill confirmed on the exchange
|
|
46966
|
+
* await commitCreateStopLoss("BTCUSDT", { id: "sl-fill-001" });
|
|
46967
|
+
* ```
|
|
46968
|
+
*/
|
|
46969
|
+
async function commitCreateStopLoss(symbol, payload = {}) {
|
|
46970
|
+
bt.loggerService.info(COMMIT_CREATE_STOP_LOSS_METHOD_NAME, { symbol, payload });
|
|
46971
|
+
if (!ExecutionContextService.hasContext()) {
|
|
46972
|
+
throw new Error("commitCreateStopLoss requires an execution context");
|
|
46973
|
+
}
|
|
46974
|
+
if (!MethodContextService.hasContext()) {
|
|
46975
|
+
throw new Error("commitCreateStopLoss requires a method context");
|
|
46976
|
+
}
|
|
46977
|
+
const { backtest: isBacktest } = bt.executionContextService.context;
|
|
46978
|
+
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
46979
|
+
await bt.strategyCoreService.createStopLoss(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
|
|
46980
|
+
}
|
|
45698
46981
|
/**
|
|
45699
46982
|
* Returns the in-memory deferred strategy-state snapshot for the current iteration: the queued
|
|
45700
46983
|
* createSignal, commit queue and deferred user-action flags, plus the current pending signal id.
|
|
@@ -45797,6 +47080,10 @@ const LISTEN_RISK_METHOD_NAME = "event.listenRisk";
|
|
|
45797
47080
|
const LISTEN_RISK_ONCE_METHOD_NAME = "event.listenRiskOnce";
|
|
45798
47081
|
const LISTEN_SCHEDULE_PING_METHOD_NAME = "event.listenSchedulePing";
|
|
45799
47082
|
const LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME = "event.listenSchedulePingOnce";
|
|
47083
|
+
const LISTEN_SCHEDULE_EVENT_METHOD_NAME = "event.listenScheduleEvent";
|
|
47084
|
+
const LISTEN_SCHEDULE_EVENT_ONCE_METHOD_NAME = "event.listenScheduleEventOnce";
|
|
47085
|
+
const LISTEN_SIGNAL_EVENT_METHOD_NAME = "event.listenSignalEvent";
|
|
47086
|
+
const LISTEN_SIGNAL_EVENT_ONCE_METHOD_NAME = "event.listenSignalEventOnce";
|
|
45800
47087
|
const LISTEN_ACTIVE_PING_METHOD_NAME = "event.listenActivePing";
|
|
45801
47088
|
const LISTEN_ACTIVE_PING_ONCE_METHOD_NAME = "event.listenActivePingOnce";
|
|
45802
47089
|
const LISTEN_IDLE_PING_METHOD_NAME = "event.listenIdlePing";
|
|
@@ -46899,6 +48186,137 @@ function listenSchedulePingOnce(filterFn, fn) {
|
|
|
46899
48186
|
};
|
|
46900
48187
|
return disposeFn = listenSchedulePing(wrappedFn);
|
|
46901
48188
|
}
|
|
48189
|
+
/**
|
|
48190
|
+
* Subscribes to scheduled signal lifecycle events (creation and cancellation) with queued async processing.
|
|
48191
|
+
*
|
|
48192
|
+
* Emitted when a scheduled signal is created (action "scheduled") or cancelled before activation
|
|
48193
|
+
* (action "cancelled" with reason "timeout" / "price_reject" / "user"), in both live and backtest.
|
|
48194
|
+
*
|
|
48195
|
+
* IMPORTANT: The scheduled -> active transition (activation) is NOT reported here. Activation
|
|
48196
|
+
* produces an "opened" event on the regular signal emitters (listenSignal) instead.
|
|
48197
|
+
*
|
|
48198
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
48199
|
+
*
|
|
48200
|
+
* @param fn - Callback function to handle scheduled lifecycle events
|
|
48201
|
+
* @returns Unsubscribe function to stop listening
|
|
48202
|
+
*
|
|
48203
|
+
* @example
|
|
48204
|
+
* ```typescript
|
|
48205
|
+
* import { listenScheduleEvent } from "./function/event";
|
|
48206
|
+
*
|
|
48207
|
+
* const unsubscribe = listenScheduleEvent((event) => {
|
|
48208
|
+
* if (event.action === "scheduled") {
|
|
48209
|
+
* console.log(`Scheduled ${event.symbol} @ ${event.data.priceOpen}`);
|
|
48210
|
+
* } else {
|
|
48211
|
+
* console.log(`Cancelled ${event.symbol} (reason: ${event.reason})`);
|
|
48212
|
+
* }
|
|
48213
|
+
* });
|
|
48214
|
+
*
|
|
48215
|
+
* // Later: stop listening
|
|
48216
|
+
* unsubscribe();
|
|
48217
|
+
* ```
|
|
48218
|
+
*/
|
|
48219
|
+
function listenScheduleEvent(fn) {
|
|
48220
|
+
bt.loggerService.log(LISTEN_SCHEDULE_EVENT_METHOD_NAME);
|
|
48221
|
+
return scheduleEventSubject.subscribe(queued(async (event) => fn(event)));
|
|
48222
|
+
}
|
|
48223
|
+
/**
|
|
48224
|
+
* Subscribes to filtered scheduled lifecycle events with one-time execution.
|
|
48225
|
+
*
|
|
48226
|
+
* Listens for events matching the filter predicate, then executes callback once
|
|
48227
|
+
* and automatically unsubscribes. Useful for waiting for a specific scheduled creation
|
|
48228
|
+
* or cancellation.
|
|
48229
|
+
*
|
|
48230
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
48231
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
48232
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
48233
|
+
*
|
|
48234
|
+
* @example
|
|
48235
|
+
* ```typescript
|
|
48236
|
+
* import { listenScheduleEventOnce } from "./function/event";
|
|
48237
|
+
*
|
|
48238
|
+
* // Wait for the first cancellation on BTCUSDT
|
|
48239
|
+
* listenScheduleEventOnce(
|
|
48240
|
+
* (event) => event.symbol === "BTCUSDT" && event.action === "cancelled",
|
|
48241
|
+
* (event) => console.log("BTCUSDT scheduled cancelled:", event.reason)
|
|
48242
|
+
* );
|
|
48243
|
+
* ```
|
|
48244
|
+
*/
|
|
48245
|
+
function listenScheduleEventOnce(filterFn, fn) {
|
|
48246
|
+
bt.loggerService.log(LISTEN_SCHEDULE_EVENT_ONCE_METHOD_NAME);
|
|
48247
|
+
let disposeFn;
|
|
48248
|
+
const wrappedFn = async (event) => {
|
|
48249
|
+
if (filterFn(event)) {
|
|
48250
|
+
await fn(event);
|
|
48251
|
+
disposeFn && disposeFn();
|
|
48252
|
+
}
|
|
48253
|
+
};
|
|
48254
|
+
return disposeFn = listenScheduleEvent(wrappedFn);
|
|
48255
|
+
}
|
|
48256
|
+
/**
|
|
48257
|
+
* Subscribes to pending signal lifecycle events (open and close) with queued async processing.
|
|
48258
|
+
*
|
|
48259
|
+
* Emitted when a pending position is opened (action "opened": new signal / immediate / scheduled
|
|
48260
|
+
* or user activation) or closed (action "closed" with closeReason "take_profit" / "stop_loss" /
|
|
48261
|
+
* "time_expired" / "closed"), in both live and backtest.
|
|
48262
|
+
*
|
|
48263
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
48264
|
+
*
|
|
48265
|
+
* @param fn - Callback function to handle pending lifecycle events
|
|
48266
|
+
* @returns Unsubscribe function to stop listening
|
|
48267
|
+
*
|
|
48268
|
+
* @example
|
|
48269
|
+
* ```typescript
|
|
48270
|
+
* import { listenSignalEvent } from "./function/event";
|
|
48271
|
+
*
|
|
48272
|
+
* const unsubscribe = listenSignalEvent((event) => {
|
|
48273
|
+
* if (event.action === "opened") {
|
|
48274
|
+
* console.log(`Opened ${event.symbol} @ ${event.data.priceOpen}`);
|
|
48275
|
+
* } else {
|
|
48276
|
+
* console.log(`Closed ${event.symbol} (reason: ${event.closeReason})`);
|
|
48277
|
+
* }
|
|
48278
|
+
* });
|
|
48279
|
+
*
|
|
48280
|
+
* // Later: stop listening
|
|
48281
|
+
* unsubscribe();
|
|
48282
|
+
* ```
|
|
48283
|
+
*/
|
|
48284
|
+
function listenSignalEvent(fn) {
|
|
48285
|
+
bt.loggerService.log(LISTEN_SIGNAL_EVENT_METHOD_NAME);
|
|
48286
|
+
return signalEventSubject.subscribe(queued(async (event) => fn(event)));
|
|
48287
|
+
}
|
|
48288
|
+
/**
|
|
48289
|
+
* Subscribes to filtered pending lifecycle events with one-time execution.
|
|
48290
|
+
*
|
|
48291
|
+
* Listens for events matching the filter predicate, then executes callback once
|
|
48292
|
+
* and automatically unsubscribes. Useful for waiting for a specific open or close.
|
|
48293
|
+
*
|
|
48294
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
48295
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
48296
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
48297
|
+
*
|
|
48298
|
+
* @example
|
|
48299
|
+
* ```typescript
|
|
48300
|
+
* import { listenSignalEventOnce } from "./function/event";
|
|
48301
|
+
*
|
|
48302
|
+
* // Wait for the first close on BTCUSDT
|
|
48303
|
+
* listenSignalEventOnce(
|
|
48304
|
+
* (event) => event.symbol === "BTCUSDT" && event.action === "closed",
|
|
48305
|
+
* (event) => console.log("BTCUSDT closed:", event.closeReason)
|
|
48306
|
+
* );
|
|
48307
|
+
* ```
|
|
48308
|
+
*/
|
|
48309
|
+
function listenSignalEventOnce(filterFn, fn) {
|
|
48310
|
+
bt.loggerService.log(LISTEN_SIGNAL_EVENT_ONCE_METHOD_NAME);
|
|
48311
|
+
let disposeFn;
|
|
48312
|
+
const wrappedFn = async (event) => {
|
|
48313
|
+
if (filterFn(event)) {
|
|
48314
|
+
await fn(event);
|
|
48315
|
+
disposeFn && disposeFn();
|
|
48316
|
+
}
|
|
48317
|
+
};
|
|
48318
|
+
return disposeFn = listenSignalEvent(wrappedFn);
|
|
48319
|
+
}
|
|
46902
48320
|
/**
|
|
46903
48321
|
* Subscribes to active ping events with queued async processing.
|
|
46904
48322
|
*
|
|
@@ -47396,6 +48814,8 @@ const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
|
|
|
47396
48814
|
const BACKTEST_METHOD_NAME_CANCEL_SCHEDULED = "Backtest.commitCancelScheduled";
|
|
47397
48815
|
const BACKTEST_METHOD_NAME_CLOSE_PENDING = "Backtest.commitClosePending";
|
|
47398
48816
|
const BACKTEST_METHOD_NAME_COMMIT_CREATE_SIGNAL = "Backtest.commitCreateSignal";
|
|
48817
|
+
const BACKTEST_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT = "Backtest.commitCreateTakeProfit";
|
|
48818
|
+
const BACKTEST_METHOD_NAME_COMMIT_CREATE_STOP_LOSS = "Backtest.commitCreateStopLoss";
|
|
47399
48819
|
const BACKTEST_METHOD_NAME_GET_STRATEGY_STATUS = "Backtest.getStrategyStatus";
|
|
47400
48820
|
const BACKTEST_METHOD_NAME_PARTIAL_PROFIT = "BacktestUtils.commitPartialProfit";
|
|
47401
48821
|
const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.commitPartialLoss";
|
|
@@ -49132,6 +50552,90 @@ class BacktestUtils {
|
|
|
49132
50552
|
}
|
|
49133
50553
|
await bt.strategyCoreService.createSignal(true, symbol, dto, context);
|
|
49134
50554
|
};
|
|
50555
|
+
/**
|
|
50556
|
+
* Reports that the pending position's take-profit order was actually filled on the exchange
|
|
50557
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based TP check.
|
|
50558
|
+
*
|
|
50559
|
+
* The exchange and the strategy are parallel states: the framework evaluates TP/SL against VWAP,
|
|
50560
|
+
* but the real order may fill on high/low. The close is deferred and emitted with closeReason
|
|
50561
|
+
* "take_profit" on the next backtest tick. No-op if no pending signal exists.
|
|
50562
|
+
*
|
|
50563
|
+
* @param symbol - Trading pair symbol
|
|
50564
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
50565
|
+
* @param payload - Optional commit payload with id and note
|
|
50566
|
+
* @returns Promise that resolves when the take-profit fill is queued
|
|
50567
|
+
*
|
|
50568
|
+
* @example
|
|
50569
|
+
* ```typescript
|
|
50570
|
+
* // Report TP fill confirmed on the exchange
|
|
50571
|
+
* await Backtest.commitCreateTakeProfit("BTCUSDT", {
|
|
50572
|
+
* exchangeName: "binance",
|
|
50573
|
+
* strategyName: "my-strategy",
|
|
50574
|
+
* frameName: "1m"
|
|
50575
|
+
* }, { id: "tp-fill-001" });
|
|
50576
|
+
* ```
|
|
50577
|
+
*/
|
|
50578
|
+
this.commitCreateTakeProfit = async (symbol, context, payload = {}) => {
|
|
50579
|
+
bt.loggerService.info(BACKTEST_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT, {
|
|
50580
|
+
symbol,
|
|
50581
|
+
context,
|
|
50582
|
+
payload,
|
|
50583
|
+
});
|
|
50584
|
+
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT);
|
|
50585
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT);
|
|
50586
|
+
{
|
|
50587
|
+
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
50588
|
+
riskName &&
|
|
50589
|
+
bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT);
|
|
50590
|
+
riskList &&
|
|
50591
|
+
riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT));
|
|
50592
|
+
actions &&
|
|
50593
|
+
actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT));
|
|
50594
|
+
}
|
|
50595
|
+
await bt.strategyCoreService.createTakeProfit(true, symbol, context, payload);
|
|
50596
|
+
};
|
|
50597
|
+
/**
|
|
50598
|
+
* Reports that the pending position's stop-loss order was actually filled on the exchange
|
|
50599
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based SL check.
|
|
50600
|
+
*
|
|
50601
|
+
* The exchange and the strategy are parallel states: the framework evaluates TP/SL against VWAP,
|
|
50602
|
+
* but the real order may fill on high/low. The close is deferred and emitted with closeReason
|
|
50603
|
+
* "stop_loss" on the next backtest tick. No-op if no pending signal exists.
|
|
50604
|
+
*
|
|
50605
|
+
* @param symbol - Trading pair symbol
|
|
50606
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
50607
|
+
* @param payload - Optional commit payload with id and note
|
|
50608
|
+
* @returns Promise that resolves when the stop-loss fill is queued
|
|
50609
|
+
*
|
|
50610
|
+
* @example
|
|
50611
|
+
* ```typescript
|
|
50612
|
+
* // Report SL fill confirmed on the exchange
|
|
50613
|
+
* await Backtest.commitCreateStopLoss("BTCUSDT", {
|
|
50614
|
+
* exchangeName: "binance",
|
|
50615
|
+
* strategyName: "my-strategy",
|
|
50616
|
+
* frameName: "1m"
|
|
50617
|
+
* }, { id: "sl-fill-001" });
|
|
50618
|
+
* ```
|
|
50619
|
+
*/
|
|
50620
|
+
this.commitCreateStopLoss = async (symbol, context, payload = {}) => {
|
|
50621
|
+
bt.loggerService.info(BACKTEST_METHOD_NAME_COMMIT_CREATE_STOP_LOSS, {
|
|
50622
|
+
symbol,
|
|
50623
|
+
context,
|
|
50624
|
+
payload,
|
|
50625
|
+
});
|
|
50626
|
+
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_COMMIT_CREATE_STOP_LOSS);
|
|
50627
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_COMMIT_CREATE_STOP_LOSS);
|
|
50628
|
+
{
|
|
50629
|
+
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
50630
|
+
riskName &&
|
|
50631
|
+
bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_COMMIT_CREATE_STOP_LOSS);
|
|
50632
|
+
riskList &&
|
|
50633
|
+
riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_COMMIT_CREATE_STOP_LOSS));
|
|
50634
|
+
actions &&
|
|
50635
|
+
actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_COMMIT_CREATE_STOP_LOSS));
|
|
50636
|
+
}
|
|
50637
|
+
await bt.strategyCoreService.createStopLoss(true, symbol, context, payload);
|
|
50638
|
+
};
|
|
49135
50639
|
/**
|
|
49136
50640
|
* Returns the in-memory deferred strategy-state snapshot for the current backtest iteration.
|
|
49137
50641
|
*
|
|
@@ -50117,6 +51621,8 @@ const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
|
|
|
50117
51621
|
const LIVE_METHOD_NAME_CANCEL_SCHEDULED = "Live.cancelScheduled";
|
|
50118
51622
|
const LIVE_METHOD_NAME_CLOSE_PENDING = "Live.closePending";
|
|
50119
51623
|
const LIVE_METHOD_NAME_COMMIT_CREATE_SIGNAL = "Live.commitCreateSignal";
|
|
51624
|
+
const LIVE_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT = "Live.commitCreateTakeProfit";
|
|
51625
|
+
const LIVE_METHOD_NAME_COMMIT_CREATE_STOP_LOSS = "Live.commitCreateStopLoss";
|
|
50120
51626
|
const LIVE_METHOD_NAME_GET_STRATEGY_STATUS = "Live.getStrategyStatus";
|
|
50121
51627
|
const LIVE_METHOD_NAME_PARTIAL_PROFIT = "LiveUtils.commitPartialProfit";
|
|
50122
51628
|
const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.commitPartialLoss";
|
|
@@ -52030,6 +53536,78 @@ class LiveUtils {
|
|
|
52030
53536
|
frameName: "",
|
|
52031
53537
|
});
|
|
52032
53538
|
};
|
|
53539
|
+
/**
|
|
53540
|
+
* Reports that the pending position's take-profit order was actually filled on the exchange
|
|
53541
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based TP check.
|
|
53542
|
+
*
|
|
53543
|
+
* The exchange and the strategy are parallel states: the framework evaluates TP/SL against VWAP,
|
|
53544
|
+
* but the real order may fill on high/low. The close is deferred and emitted with closeReason
|
|
53545
|
+
* "take_profit" on the next live tick. No-op if no pending signal exists.
|
|
53546
|
+
*
|
|
53547
|
+
* @param symbol - Trading pair symbol
|
|
53548
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
53549
|
+
* @param payload - Optional commit payload with id and note
|
|
53550
|
+
* @returns Promise that resolves when the take-profit fill is queued
|
|
53551
|
+
*/
|
|
53552
|
+
this.commitCreateTakeProfit = async (symbol, context, payload = {}) => {
|
|
53553
|
+
bt.loggerService.info(LIVE_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT, {
|
|
53554
|
+
symbol,
|
|
53555
|
+
context,
|
|
53556
|
+
payload,
|
|
53557
|
+
});
|
|
53558
|
+
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT);
|
|
53559
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT);
|
|
53560
|
+
{
|
|
53561
|
+
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
53562
|
+
riskName &&
|
|
53563
|
+
bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT);
|
|
53564
|
+
riskList &&
|
|
53565
|
+
riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT));
|
|
53566
|
+
actions &&
|
|
53567
|
+
actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_COMMIT_CREATE_TAKE_PROFIT));
|
|
53568
|
+
}
|
|
53569
|
+
await bt.strategyCoreService.createTakeProfit(false, symbol, {
|
|
53570
|
+
strategyName: context.strategyName,
|
|
53571
|
+
exchangeName: context.exchangeName,
|
|
53572
|
+
frameName: "",
|
|
53573
|
+
}, payload);
|
|
53574
|
+
};
|
|
53575
|
+
/**
|
|
53576
|
+
* Reports that the pending position's stop-loss order was actually filled on the exchange
|
|
53577
|
+
* (e.g. by candle high/low), forcing a close that bypasses the VWAP-based SL check.
|
|
53578
|
+
*
|
|
53579
|
+
* The exchange and the strategy are parallel states: the framework evaluates TP/SL against VWAP,
|
|
53580
|
+
* but the real order may fill on high/low. The close is deferred and emitted with closeReason
|
|
53581
|
+
* "stop_loss" on the next live tick. No-op if no pending signal exists.
|
|
53582
|
+
*
|
|
53583
|
+
* @param symbol - Trading pair symbol
|
|
53584
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
53585
|
+
* @param payload - Optional commit payload with id and note
|
|
53586
|
+
* @returns Promise that resolves when the stop-loss fill is queued
|
|
53587
|
+
*/
|
|
53588
|
+
this.commitCreateStopLoss = async (symbol, context, payload = {}) => {
|
|
53589
|
+
bt.loggerService.info(LIVE_METHOD_NAME_COMMIT_CREATE_STOP_LOSS, {
|
|
53590
|
+
symbol,
|
|
53591
|
+
context,
|
|
53592
|
+
payload,
|
|
53593
|
+
});
|
|
53594
|
+
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_COMMIT_CREATE_STOP_LOSS);
|
|
53595
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_COMMIT_CREATE_STOP_LOSS);
|
|
53596
|
+
{
|
|
53597
|
+
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
53598
|
+
riskName &&
|
|
53599
|
+
bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_COMMIT_CREATE_STOP_LOSS);
|
|
53600
|
+
riskList &&
|
|
53601
|
+
riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_COMMIT_CREATE_STOP_LOSS));
|
|
53602
|
+
actions &&
|
|
53603
|
+
actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_COMMIT_CREATE_STOP_LOSS));
|
|
53604
|
+
}
|
|
53605
|
+
await bt.strategyCoreService.createStopLoss(false, symbol, {
|
|
53606
|
+
strategyName: context.strategyName,
|
|
53607
|
+
exchangeName: context.exchangeName,
|
|
53608
|
+
frameName: "",
|
|
53609
|
+
}, payload);
|
|
53610
|
+
};
|
|
52033
53611
|
/**
|
|
52034
53612
|
* Returns the in-memory deferred strategy-state snapshot for the current live iteration.
|
|
52035
53613
|
*
|
|
@@ -60369,7 +61947,9 @@ const SUBJECT_ISOLATION_LIST = [
|
|
|
60369
61947
|
validationSubject,
|
|
60370
61948
|
signalNotifySubject,
|
|
60371
61949
|
beforeStartSubject,
|
|
60372
|
-
afterEndSubject
|
|
61950
|
+
afterEndSubject,
|
|
61951
|
+
scheduleEventSubject,
|
|
61952
|
+
signalEventSubject,
|
|
60373
61953
|
];
|
|
60374
61954
|
/**
|
|
60375
61955
|
* Creates a snapshot function for a given subject by clearing its internal
|
|
@@ -68707,4 +70287,4 @@ const percentValue = (yesterdayValue, todayValue) => {
|
|
|
68707
70287
|
return yesterdayValue / todayValue - 1;
|
|
68708
70288
|
};
|
|
68709
70289
|
|
|
68710
|
-
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Cron, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Lookup, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, PersistStrategyAdapter, PersistStrategyInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, beginContext, beginTime, cacheCandles, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitCreateSignal, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getPriceScale, getRawCandles, getRiskSchema, getRuntimeInfo, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getStrategyStatus, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, intervalStepMs, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenAfterEnd, listenAfterEndOnce, listenBacktestProgress, listenBeforeStart, listenBeforeStartOnce, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toPlainString, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCandles, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, waitForReady, warmCandles, writeMemory };
|
|
70290
|
+
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Cron, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Lookup, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, PersistStrategyAdapter, PersistStrategyInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, beginContext, beginTime, cacheCandles, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitCreateSignal, commitCreateStopLoss, commitCreateTakeProfit, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getPriceScale, getRawCandles, getRiskSchema, getRuntimeInfo, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getStrategyStatus, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, intervalStepMs, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenAfterEnd, listenAfterEndOnce, listenBacktestProgress, listenBeforeStart, listenBeforeStartOnce, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenScheduleEvent, listenScheduleEventOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalEvent, listenSignalEventOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toPlainString, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCandles, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, waitForReady, warmCandles, writeMemory };
|