Skip to content
Closed
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
42 changes: 36 additions & 6 deletions ib_async/flexreport.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
"""Access to account statement webservice."""

import logging
import os
import time
import xml.etree.ElementTree as et
from contextlib import suppress
from typing import Final
from urllib.parse import urlparse
from urllib.request import urlopen

from ib_async import util
from ib_async.objects import DynamicObject

_logger = logging.getLogger("ib_async.flexreport")

FLEXREPORT_URL: Final = (
"https://ndcdyn.interactivebrokers.com/AccountManagement/"
"FlexWebService/SendRequest?"
)
"""
https://www.interactivebrokers.com/campus/ibkr-api-page/flex-web-service/#flex-generate-report
"""


class FlexError(Exception):
pass
Expand All @@ -33,6 +44,8 @@ def __init__(self, token=None, queryId=None, path=None):
"""
Download a report by giving a valid ``token`` and ``queryId``,
or load from file by giving a valid ``path``.

To overwrite default URL, set env variable ``IB_FLEXREPORT_URL``.
"""
if token and queryId:
self.download(token, queryId)
Expand Down Expand Up @@ -65,13 +78,30 @@ def df(self, topic: str, parseNumbers=True):
"""Same as extract but return the result as a pandas DataFrame."""
return util.df(self.extract(topic, parseNumbers))

def get_url(self):
"""Generate flexreport URL."""

def is_valid_url(url: str) -> bool:
try:
result = urlparse(url)
# Must have scheme (http/https) and netloc (domain)
return all([result.scheme, result.netloc])
except Exception:
return False

_url = os.getenv("IB_FLEXREPORT_URL", FLEXREPORT_URL)
if is_valid_url(_url):
return _url
raise FlexError(
"Invalid URL, please check that env variable IB_FLEXREPORT_URL is set correctly."
)

def download(self, token, queryId):
"""Download report for the given ``token`` and ``queryId``."""
url = (
"https://gdcdyn.interactivebrokers.com"
f"/Universal/servlet/FlexStatementService.SendRequest?"
f"t={token}&q={queryId}&v=3"
)
base_url = self.get_url()
query = f"t={token}&q={queryId}&v=3"
url = base_url + query

resp = urlopen(url)
data = resp.read()

Expand All @@ -94,7 +124,7 @@ def download(self, token, queryId):

while True:
time.sleep(1)
url = f"{baseUrl}?q={code}&t={token}"
url = f"{baseUrl}?q={code}&t={token}&v=3"
resp = urlopen(url)
self.data = resp.read()
self.root = et.fromstring(self.data)
Expand Down
Loading