Support asynchronous non-market orders (#8946)
API Tests / build (push) Has been cancelled
Benchmarks / build (push) Has been cancelled
Build & Test Lean / build (push) Has been cancelled
Regression Tests / build (push) Has been cancelled
Report Generator Tests / build (push) Has been cancelled
Research Regression Tests / build (push) Has been cancelled
Syntax Tests / build (push) Has been cancelled
Python Virtual Environments / build (push) Has been cancelled

* Add 'asynchronous' parameter to trading api

* Add regression algorithms

* More fixes for asynchronous orders

* Fix failing unit tests

* Fix failing unit tests

* Add multiple orders requests in transaction handler

Make each transaction thread handle requests corresponding to the same order

* Refactor order state machine in BrokerageTransactionHandler

Now using a single dictionary to hold orders and their state, either pending for submission, open or closed.

* Revert: remove complete orders from new state machine in BTH

* Add order ticket to new BTH open orders state machine

* Run syn in Backtesting transaction handler for new orders submission

* Remove OpenOrderState.SubmissionPending property

* Add Security to OpenOrderState

* Minor fix

* Cleanup

* Some cleanup in BTH

* Cleanup

* Minor fix

* Minor unit test fix

* Minor fix

* Make Brokerage.ConcurrencyEnable property virtual

* Minor change

* Cleanup

* Add 'asynchronous' parameter to SetHoldings api

* Improve regression algorithms
This commit is contained in:
Jhonathan Abreu
2025-09-10 08:29:50 -04:00
committed by GitHub
parent 23afff13fc
commit d82d70dacf
49 changed files with 1226 additions and 242 deletions
@@ -24,6 +24,7 @@ class TrailingStopOrderRegressionAlgorithm(QCAlgorithm):
buy_trailing_amount = 2
sell_trailing_amount = 0.5
asynchronous_orders = False
def initialize(self):
@@ -42,7 +43,7 @@ class TrailingStopOrderRegressionAlgorithm(QCAlgorithm):
return
if self._buy_order_ticket is None:
self._buy_order_ticket = self.trailing_stop_order(self._symbol, 100, trailing_amount=self.buy_trailing_amount, trailing_as_percentage=False)
self._buy_order_ticket = self.trailing_stop_order(self._symbol, 100, trailing_amount=self.buy_trailing_amount, trailing_as_percentage=False, asynchronous=self.asynchronous_orders)
elif self._buy_order_ticket.status != OrderStatus.FILLED:
stop_price = self._buy_order_ticket.get(OrderField.STOP_PRICE)
@@ -57,7 +58,7 @@ class TrailingStopOrderRegressionAlgorithm(QCAlgorithm):
if self._sell_order_ticket is None:
if self.portfolio.invested:
self._sell_order_ticket = self.trailing_stop_order(self._symbol, -100, trailing_amount=self.sell_trailing_amount, trailing_as_percentage=False)
self._sell_order_ticket = self.trailing_stop_order(self._symbol, -100, trailing_amount=self.sell_trailing_amount, trailing_as_percentage=False, asynchronous=self.asynchronous_orders)
elif self._sell_order_ticket.status != OrderStatus.FILLED:
stop_price = self._sell_order_ticket.get(OrderField.STOP_PRICE)
@@ -83,3 +84,8 @@ class TrailingStopOrderRegressionAlgorithm(QCAlgorithm):
if orderEvent.fill_price > stop_price:
raise AssertionError(f"Sell trailing stop order should have filled with price less than or equal to the stop price {stop_price}. "
f"Fill price: {orderEvent.fill_price}")
def on_end_of_algorithm(self):
for ticket in self.transactions.get_order_tickets():
if ticket.submit_request.asynchronous != self.asynchronous_orders:
raise AssertionError("Expected all orders to have the same asynchronous flag as the algorithm.")