Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Alpaca order tracking #660

Merged
merged 2 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lumibot/brokers/alpaca.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

category Functionality severity potentially major

Orders not removed from unprocessed orders list

Tell me more

What is the issue?
Orders are added to _unprocessed_orders but never removed, potentially causing memory leaks and tracking inaccuracies.

Why this matters
Orders will accumulate indefinitely in the _unprocessed_orders list, consuming memory and possibly leading to incorrect order tracking since orders that are completed or cancelled remain in the unprocessed list.

Chat with Korbit by mentioning @korbit-ai, and give a 👍 or 👎 to help Korbit improve your reviews.


except Exception as e:
order.set_error(e)
Expand Down Expand Up @@ -547,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
Expand Down
10 changes: 5 additions & 5 deletions lumibot/brokers/broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions lumibot/data_sources/interactive_brokers_rest_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions lumibot/entities/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading