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

IB Fixes for order objects #638

Merged
merged 5 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 9 additions & 2 deletions lumibot/brokers/interactive_brokers_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def _parse_broker_order(self, response, strategy_name, strategy_object=None):
order.quantity = totalQuantity
order.asset = Asset(symbol=response['ticker'], asset_type="multileg")
order.side = response['side']
order.identifier = response['orderId']

order.child_orders = []

Expand All @@ -205,6 +206,7 @@ def _parse_broker_order(self, response, strategy_name, strategy_object=None):
response=response,
quantity=float(ratio) * totalQuantity,
conId=leg,
parent_identifier=order.identifier
)
order.child_orders.append(child_order)

Expand All @@ -224,7 +226,7 @@ def _parse_broker_order(self, response, strategy_name, strategy_object=None):
order.update_raw(response)
return order

def _parse_order_object(self, strategy_name, response, quantity, conId):
def _parse_order_object(self, strategy_name, response, quantity, conId, parent_identifier=None):
if quantity < 0:
side = "SELL"
quantity = -quantity
Expand Down Expand Up @@ -294,6 +296,9 @@ def _parse_order_object(self, strategy_name, response, quantity, conId):
avg_fill_price=response["avgPrice"] if "avgPrice" in response else None
)

if parent_identifier is not None:
order.parent_identifier=parent_identifier

return order

def _pull_broker_all_orders(self):
Expand Down Expand Up @@ -705,8 +710,10 @@ def submit_orders(

order = Order(orders[0].strategy)
order.order_class = Order.OrderClass.MULTILEG
order.child_orders = orders
order.identifier = response[0]["order_id"]
order.child_orders = orders
for child_order in order.child_orders:
child_order.parent_identifier = order.identifier

self._unprocessed_orders.append(order)
self.stream.dispatch(self.NEW_ORDER, order=order)
Expand Down
31 changes: 17 additions & 14 deletions lumibot/data_sources/interactive_brokers_rest_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def suppress_warnings(self):
url = f"{self.base_url}/iserver/questions/suppress"
json = {"messageIds": ["o451", "o383", "o354", "o163"]}

self.post_to_endpoint(url, json=json, allow_fail=False)
self.post_to_endpoint(url, json=json, description="Suppressing server warnings", allow_fail=False)

def fetch_account_id(self):
if self.account_id is not None:
Expand Down Expand Up @@ -246,7 +246,7 @@ def get_account_balances(self):

return response

def handle_http_errors(self, response):
def handle_http_errors(self, response, description):
to_return = None
re_msg = None
is_error = False
Expand Down Expand Up @@ -279,7 +279,7 @@ def handle_http_errors(self, response):
confirm_response = self.post_to_endpoint(
confirm_url,
{"confirmed": True},
"Confirming order",
description="Confirming Order",
silent=True,
allow_fail=True
)
Expand All @@ -290,22 +290,22 @@ def handle_http_errors(self, response):

if "no bridge" in error_message.lower() or "not authenticated" in error_message.lower():
retrying = True
re_msg = "Not Authenticated"
re_msg = f"Task {description} failed: Not Authenticated"

elif 200 <= status_code < 300:
to_return = response_json
retrying = False

elif status_code == 429:
retrying = True
re_msg = "You got rate limited"
re_msg = f"Task {description} failed: You got rate limited"

elif status_code == 503:
if any("Please query /accounts first" in str(value) for value in response_json.values()):
self.ping_iserver()
re_msg = "Lumibot got Deauthenticated"
re_msg = f"Task {description} failed: Lumibot got Deauthenticated"
else:
re_msg = "Internal server error, should fix itself soon"
re_msg = f"Task {description} failed: Internal server error, should fix itself soon"

retrying = True

Expand All @@ -316,7 +316,7 @@ def handle_http_errors(self, response):

elif status_code == 410:
retrying = True
re_msg = "The bridge blew up"
re_msg = f"Task {description} failed: The bridge blew up"

elif 400 <= status_code < 500:
to_return = response_json
Expand All @@ -336,7 +336,7 @@ def get_from_endpoint(self, url, description="", silent=False, allow_fail=True):
try:
while retrying or not allow_fail:
response = requests.get(url, verify=False)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response, description)

if re_msg is not None:
if not silent and retries == 0:
Expand Down Expand Up @@ -367,7 +367,7 @@ def post_to_endpoint(self, url, json: dict, description="", silent=False, allow_
try:
while retrying or not allow_fail:
response = requests.post(url, json=json, verify=False)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response, description)

if re_msg is not None:
if not silent and retries == 0:
Expand Down Expand Up @@ -398,7 +398,7 @@ def delete_to_endpoint(self, url, description="", silent=False, allow_fail=True)
try:
while retrying or not allow_fail:
response = requests.delete(url, verify=False)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response, description)

if re_msg is not None:
if not silent and retries == 0:
Expand Down Expand Up @@ -463,7 +463,10 @@ def get_broker_all_orders(self):
url, "Getting open orders", allow_fail=False
)

return [order for order in response['orders'] if order.get('totalSize', 0) != 0]
if 'orders' in response and isinstance(response['orders'], list):
return [order for order in response['orders'] if order.get('totalSize', 0) != 0]

return []

def get_order_info(self, orderid):
self.ping_iserver()
Expand All @@ -480,7 +483,7 @@ def execute_order(self, order_data):
self.ping_iserver()

url = f"{self.base_url}/iserver/account/{self.account_id}/orders"
response = self.post_to_endpoint(url, order_data)
response = self.post_to_endpoint(url, order_data, description="Executing order")

if isinstance(response, list) and "order_id" in response[0]:
# success
Expand All @@ -505,7 +508,7 @@ def delete_order(self, order):
self.ping_iserver()
orderId = order.identifier
url = f"{self.base_url}/iserver/account/{self.account_id}/order/{orderId}"
status = self.delete_to_endpoint(url)
status = self.delete_to_endpoint(url, description=f"Deleting order {orderId}")
if status:
logging.info(
colored(f"Order with ID {orderId} canceled successfully.", "green")
Expand Down
Loading