Skip to content

Commit

Permalink
Merge pull request #643 from Lumiwealth/ib-hang-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
grzesir authored Dec 3, 2024
2 parents b295196 + 977d58c commit d16d093
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 57 deletions.
13 changes: 5 additions & 8 deletions lumibot/brokers/interactive_brokers_rest.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import logging
from termcolor import colored
from lumibot.brokers import Broker
from lumibot.entities import Order, Asset, Position
from lumibot.data_sources import InteractiveBrokersRESTData
from ..brokers import Broker
from ..entities import Order, Asset, Position
from ..data_sources import InteractiveBrokersRESTData
import datetime
from decimal import Decimal
from math import gcd
import re
import ssl
import time
import json
import traceback
from lumibot.trading_builtins import PollingStream
from ..trading_builtins import PollingStream

TYPE_MAP = dict(
stock="STK",
Expand Down Expand Up @@ -1193,4 +1190,4 @@ def _get_broker_id_from_raw_orders(self, raw_orders):
for leg in o["leg"]:
if "orderId" in leg:
ids.append(str(leg["orderId"]))
return ids
return ids
100 changes: 51 additions & 49 deletions lumibot/data_sources/interactive_brokers_rest_data.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import logging
from termcolor import colored
from lumibot.entities import Asset, Bars
from ..entities import Asset, Bars

from .data_source import DataSource
import subprocess
import os
import time
import requests
import urllib3
from datetime import datetime, timedelta
from datetime import datetime
import pandas as pd

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
Expand Down Expand Up @@ -101,6 +101,8 @@ def start(self, ib_username, ib_password):
"-d",
"--name",
"lumibot-client-portal",
"--restart",
"always",
*env_args,
"-p",
f"{self.port}:{self.port}",
Expand Down Expand Up @@ -246,7 +248,7 @@ def get_account_balances(self):

return response

def handle_http_errors(self, response, description):
def handle_http_errors(self, response, silent, retries, description):
to_return = None
re_msg = None
is_error = False
Expand Down Expand Up @@ -288,29 +290,29 @@ def handle_http_errors(self, response, description):
status_code = 200
response_json = orders

if 'xcredserv comm failed during getEvents due to Connection refused':
if 'xcredserv comm failed during getEvents due to Connection refused' in error_message:
retrying = True
re_msg = f"Task {description} failed: The server is undergoing maintenance. Should fix itself soon"
re_msg = "The server is undergoing maintenance. Should fix itself soon"

elif 'Please query /accounts first' in error_message:
self.ping_iserver()
retrying = True
re_msg = f"Task {description} failed: Lumibot got Deauthenticated"
re_msg = "Lumibot got Deauthenticated"

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

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

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

elif status_code == 503:
re_msg = f"Task {description} failed: Internal server error. Should fix itself soon"
re_msg = "Internal server error. Should fix itself soon"
retrying = True

elif status_code == 500:
Expand All @@ -320,7 +322,7 @@ def handle_http_errors(self, response, description):

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

elif 400 <= status_code < 500:
to_return = response_json
Expand All @@ -329,9 +331,27 @@ def handle_http_errors(self, response, description):

else:
retrying = False


return (retrying, re_msg, is_error, to_return)
if re_msg is not None:
if not silent and retries == 0:
logging.warning(colored(f"Task {description} failed: {re_msg}. Retrying...", "yellow"))
elif retries >= 20:
logging.info(colored(f"Task {description} failed: {re_msg}. Retrying...", "yellow"))
else:
logging.debug(colored(f"Task {description} failed: {re_msg}. Retrying...", "yellow"))

elif is_error:
if not silent and retries == 0:
logging.error(colored(f"Task {description} failed: {to_return}", "red"))
else:
logging.debug(colored(f"Task {description} failed: {to_return}", "red"))

if re_msg is not None:
time.sleep(1)

return (retrying, re_msg, is_error, to_return)

def get_from_endpoint(self, url, description="", silent=False, allow_fail=True):
to_return = None
retries = 0
Expand All @@ -340,25 +360,19 @@ 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, description)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response, silent, retries, description)

if re_msg is not None:
if not silent and retries == 0:
logging.warning(colored(f'{re_msg}. Retrying...', "yellow"))

elif is_error:
if not silent and retries == 0:
logging.error(colored(f"Task {description} failed: {to_return}", "red"))
if re_msg is None and not is_error:
break

else:
allow_fail = True

retries+=1

except requests.exceptions.RequestException as e:
message = f"Error: {description}. Exception: {e}"
if not silent:
logging.error(colored(message, "red"))
else:
logging.debug(colored(message), "red")
to_return = {"error": message}

return to_return
Expand All @@ -371,25 +385,19 @@ 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, description)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response, silent, retries, description)

if re_msg is not None:
if not silent and retries == 0:
logging.warning(colored(f'{re_msg}. Retrying...', "yellow"))

elif is_error:
if not silent and retries == 0:
logging.error(colored(f"Task {description} failed: {to_return}", "red"))

else:
allow_fail = True

retries += 1
if re_msg is None and not is_error:
break

retries+=1

except requests.exceptions.RequestException as e:
message = f"Error: {description}. Exception: {e}"
if not silent:
logging.error(colored(message, "red"))
else:
logging.debug(colored(message), "red")
to_return = {"error": message}

return to_return
Expand All @@ -402,25 +410,19 @@ 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, description)
retrying, re_msg, is_error, to_return = self.handle_http_errors(response, silent, retries, description)

if re_msg is not None:
if not silent and retries == 0:
logging.warning(colored(f'{re_msg}. Retrying...', "yellow"))

elif is_error:
if not silent and retries == 0:
logging.error(colored(f"Task {description} failed: {to_return}", "red"))

else:
allow_fail = True

retries += 1
if re_msg is None and not is_error:
break

retries+=1

except requests.exceptions.RequestException as e:
message = f"Error: {description}. Exception: {e}"
if not silent:
logging.error(colored(message, "red"))
else:
logging.debug(colored(message), "red")
to_return = {"error": message}

return to_return
Expand Down Expand Up @@ -1069,4 +1071,4 @@ def get_quote(self, asset, quote=None, exchange=None):
else:
result["ask"] = None

return result
return result

0 comments on commit d16d093

Please sign in to comment.