From 5af754f8c117db1aab7ca6975f16233a4658d119 Mon Sep 17 00:00:00 2001 From: Al4ise Date: Mon, 9 Dec 2024 17:11:49 +0200 Subject: [PATCH 1/2] Fix for Alpaca order tracking --- lumibot/brokers/alpaca.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lumibot/brokers/alpaca.py b/lumibot/brokers/alpaca.py index b09825085..4f093bd8e 100644 --- a/lumibot/brokers/alpaca.py +++ b/lumibot/brokers/alpaca.py @@ -447,6 +447,7 @@ def _submit_order(self, order): order.set_identifier(response.id) order.status = response.status order.update_raw(response) + self._unprocessed_orders.append(order) except Exception as e: order.set_error(e) From 912eb7b2a6ed764fe654e692241a3775d304e0d0 Mon Sep 17 00:00:00 2001 From: Al4ise Date: Tue, 10 Dec 2024 12:04:42 +0200 Subject: [PATCH 2/2] fixes for alpaca and IB --- lumibot/brokers/alpaca.py | 3 +-- lumibot/brokers/broker.py | 10 +++++----- .../data_sources/interactive_brokers_rest_data.py | 4 ++++ lumibot/entities/order.py | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lumibot/brokers/alpaca.py b/lumibot/brokers/alpaca.py index 4f093bd8e..bfac05f40 100644 --- a/lumibot/brokers/alpaca.py +++ b/lumibot/brokers/alpaca.py @@ -548,14 +548,13 @@ def _run_stream(self): """ async def _trade_update(trade_update): - self._orders_queue.join() try: logged_order = trade_update.order type_event = trade_update.event identifier = logged_order.id stored_order = self.get_tracked_order(identifier) if stored_order is None: - logging.info(f"Untracked order {identifier} was logged by broker {self.name}") + logging.debug(f"Untracked order {identifier} was logged by broker {self.name}") return False price = trade_update.price diff --git a/lumibot/brokers/broker.py b/lumibot/brokers/broker.py index 41a393035..7a1730b4a 100644 --- a/lumibot/brokers/broker.py +++ b/lumibot/brokers/broker.py @@ -1204,21 +1204,21 @@ def _process_trade_event(self, stored_order, type_event, price=None, filled_quan except ValueError: raise error - if type_event == self.NEW_ORDER: + if Order.is_equivalent_status(type_event, self.NEW_ORDER): stored_order = self._process_new_order(stored_order) self._on_new_order(stored_order) - elif type_event == self.CANCELED_ORDER: + elif Order.is_equivalent_status(type_event, self.CANCELED_ORDER): # Do not cancel or re-cancel already completed orders if stored_order.is_active(): stored_order = self._process_canceled_order(stored_order) self._on_canceled_order(stored_order) - elif type_event == self.PARTIALLY_FILLED_ORDER: + elif Order.is_equivalent_status(type_event, self.PARTIALLY_FILLED_ORDER): stored_order, position = self._process_partially_filled_order(stored_order, price, filled_quantity) self._on_partially_filled_order(position, stored_order, price, filled_quantity, multiplier) - elif type_event == self.FILLED_ORDER: + elif Order.is_equivalent_status(type_event, self.FILLED_ORDER): position = self._process_filled_order(stored_order, price, filled_quantity) self._on_filled_order(position, stored_order, price, filled_quantity, multiplier) - elif type_event == self.CASH_SETTLED: + elif Order.is_equivalent_status(type_event, self.CASH_SETTLED): self._process_cash_settlement(stored_order, price, filled_quantity) stored_order.type = self.CASH_SETTLED else: diff --git a/lumibot/data_sources/interactive_brokers_rest_data.py b/lumibot/data_sources/interactive_brokers_rest_data.py index 9edfcc4ad..bb89b3217 100644 --- a/lumibot/data_sources/interactive_brokers_rest_data.py +++ b/lumibot/data_sources/interactive_brokers_rest_data.py @@ -307,6 +307,10 @@ def show_error(retries, allow_fail): self.ping_iserver() retrying = True re_msg = "Lumibot got Deauthenticated" + + elif 'There was an error processing the request. Please try again.' in error_message: + retrying = True + re_msg = "Something went wrong." elif "no bridge" in error_message.lower() or "not authenticated" in error_message.lower(): retrying = True diff --git a/lumibot/entities/order.py b/lumibot/entities/order.py index d0f879bf8..0cea98bac 100644 --- a/lumibot/entities/order.py +++ b/lumibot/entities/order.py @@ -817,7 +817,21 @@ def equivalent_status(self, status) -> bool: return True else: return False + + @classmethod + def is_equivalent_status(cls, status1, status2) -> bool: + """Returns if the 2 statuses passed are equivalent.""" + if not status1 or not status2: + return False + elif status1.lower() in [status2.lower(), STATUS_ALIAS_MAP.get(status2.lower(), "")]: + return True + # open/new status is equivalent + elif {status1.lower(), status2.lower()}.issubset({"open", "new"}): + return True + else: + return False + def set_error(self, error): self.status = "error" self._error = error