jborchma / qtrade Goto Github PK
View Code? Open in Web Editor NEWSimple Questrade API wrapper for Python
Home Page: https://jborchma.github.io/qtrade/
License: MIT License
Simple Questrade API wrapper for Python
Home Page: https://jborchma.github.io/qtrade/
License: MIT License
I'm assuming most of the usages for this package would be for analytics, do you want to include analytical utilities such as convert_to_pandas
etc. in this package or do you think those should be separate?
Add functionality for submitting orders.
I think it would be great to add type hints and mypy checks.
*** Error Output Below ***
File "/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/decoder.py", line 353, in raw_decode
obj, end = self.scan_once(s, idx)
builtins.StopIteration: 1
During handling of the above exception, another exception occurred:
 File "/dowload_questrade_data/questrade/questrade_play.py", line 4, in <module>
q = Questrade(refresh_token='<my token code>')
File "/usr/local/lib/python3.7/site-packages/questrade_api/questrade.py", line 22, in __init__
self.auth = Auth(**auth_kwargs, config=self.config)
File "/usr/local/lib/python3.7/site-packages/questrade_api/auth.py", line 20, in __init__
self.__refresh_token(kwargs['refresh_token'])
File "/usr/local/lib/python3.7/site-packages/questrade_api/auth.py", line 39, in __refresh_token
token = json.loads(r.read().decode('utf-8'))
File "/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 348, in loads
return _default_decoder.decode(s)
File "/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 2 (char 1)
*** From 'decoder.py' ***
def raw_decode(self, s, idx=0):
"""Decode a JSON document from ``s`` (a ``str`` beginning with
a JSON document) and return a 2-tuple of the Python
representation and the index in ``s`` where the document ended.
This can be used to decode a JSON document from a string that may
have extraneous data at the end.
"""
try:
obj, end = self.scan_once(s, idx)
except StopIteration as err:
raise JSONDecodeError("Expecting value", s, err.value) from None
return obj, end
Issue with line 355: raise JSONDecodeError("Expecting value", s, err.value) from None
I think with all the changes to Travis recently, it might be time to move to Github actions before running out of credits.
Like the title says. The documentation has more info.
Hi Jan,
Recently, I have encountered errors related to SSLCertVerification and MaxRetry below, and I am not sure what went wrong from my side. Specifically, I get these errors when I call get_account_id
method.
SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)
During handling of the above exception, another exception occurred:
MaxRetryError Traceback (most recent call last)
~\anaconda3\lib\site-packages\requests\adapters.py in send(self, request, stream, timeout, verify, cert, proxies)
438 if not chunked:
--> 439 resp = conn.urlopen(
440 method=request.method,
~\anaconda3\lib\site-packages\urllib3\connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
754
--> 755 retries = retries.increment(
756 method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
~\anaconda3\lib\site-packages\urllib3\util\retry.py in increment(self, method, url, response, error, _pool, _stacktrace)
573 if new_retry.is_exhausted():
--> 574 raise MaxRetryError(_pool, url, error or ResponseError(cause))
575
MaxRetryError: HTTPSConnectionPool(host='api05.iq.questrade.com', port=443): Max retries exceeded with url: /v1/accounts (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)')))
During handling of the above exception, another exception occurred:
SSLError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9912/2462350887.py in
1 acctData = {}
----> 2 acctNums = qbot.get_account_id()
3 ACCOUNT_TYPE = ESHINHW_ACCOUNT_TYPE
4 for aNum in acctNums:
5 if aNum in ACCOUNT_TYPE:
~\Desktop\GitHub\questrade-portfolio-manager\utils\qtrade.py in get_account_id(self)
221 """
222 log.info("Getting account ID...")
--> 223 response: Dict[str, List[Dict[str, int]]] = self._send_message("get", "accounts")
224
225 account_id = []
~\Desktop\GitHub\questrade-portfolio-manager\utils\qtrade.py in _send_message(self, method, endpoint, params, data, json)
89 log.error("Access token not set...")
90 raise Exception("Access token not set...")
---> 91 resp = self.session.request(method, url, params=params, data=data, json=json, timeout=30)
92 resp.raise_for_status()
93 return resp.json()
~\anaconda3\lib\site-packages\requests\sessions.py in request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
540 }
541 send_kwargs.update(settings)
--> 542 resp = self.send(prep, **send_kwargs)
543
544 return resp
~\anaconda3\lib\site-packages\requests\sessions.py in send(self, request, **kwargs)
653
654 # Send the request
--> 655 r = adapter.send(request, **kwargs)
656
657 # Total elapsed time of the request (approximately)
~\anaconda3\lib\site-packages\requests\adapters.py in send(self, request, stream, timeout, verify, cert, proxies)
512 if isinstance(e.reason, _SSLError):
513 # This branch is for urllib3 v1.22 and later.
--> 514 raise SSLError(e, request=request)
515
516 raise ConnectionError(e, request=request)
Is there anything I can change on my side to fix those errors, or is it a problem from the Questrade side?
Thanks!
I believe QT made some changes in the last year and things might have been broken. So, these are my steps:
Basically, I have to create a new token for each run.
Am I doing something wrong?
We have different accounts, my partner and myself. The API right now doesn't support different YAML files for the token cache, but what I want to do is to aggregate our positions for further analysis.
Like the title says, add testing.
I should add a disclaimer to the readme.
I think it could be useful to add an option to not necessarily write out the token yaml every time when initiating.
I think I like the RTD theme better.
I hope it's not me; but it worked yesterday and today it never returns. Even though the token was not expired I got a new one; same result.
I'm not sure how I could provide more info about this.
Code:
from qtrade import Questrade
MyQtrade = Questrade(token_yaml="access_token.yml")
MyQtrade.refresh_access_token(from_yaml=True)
print(MyQtrade.ticker_information('IBM'))
I run each line in the interpreter and "refresh_access_token" does not return.
I stopped it with Ctrl-C and I got the following:
`
MyQtrade.refresh_access_token(from_yaml=True)
Traceback (most recent call last):
File "", line 1, in
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\qtrade\questrade.py", line 194, in refresh_access_token
data = requests.get(url)
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\requests\api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\requests\api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\requests\sessions.py", line 529, in request
resp = self.send(prep, **send_kwargs)
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\requests\sessions.py", line 645, in send
r = adapter.send(request, **kwargs)
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\requests\adapters.py", line 440, in send
resp = conn.urlopen(
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\urllib3\connectionpool.py", line 703, in urlopen
httplib_response = self._make_request(
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\urllib3\connectionpool.py", line 449, in _make_request
six.raise_from(e, None)
File "", line 3, in raise_from
File "C:\Users\Cat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\urllib3\connectionpool.py", line 444, in _make_request
httplib_response = conn.getresponse()
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1374, in getresponse
response.begin()
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 318, in begin
version, status, reason = self._read_status()
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 279, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\socket.py", line 705, in readinto
return self._sock.recv_into(b)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\ssl.py", line 1273, in recv_into
return self.read(nbytes, buffer)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\ssl.py", line 1129, in read
return self._sslobj.read(len, buffer)
KeyboardInterrupt
`
Anything I'm doing wrong, or that I can do to fix this, please?
Hi,
Thank you for making this module, it is really helpful. I wonder if there's any other kind of interval, like minute or hour that can be used in this function?
I found this document, and tried like "FourHours", "OneMinute". However, they don't work at all.
https://www.questrade.com/api/documentation/rest-operations/enumerations/enumerations#historical-data-granularity
Hello!
I am just wondering if qtrade can work with multiple accounts from different login IDs.
In my situation, I have no issue with using qtrade, but I want to manage other people's accounts with different API keys.
Based on my understanding, access_token.yml only works with one ID, and I have to delete and recreate it for new ID.
Is this correct and are there ways to access different accounts from different IDs?
Thanks,
As the title says: Add logging functionality.
For code styling, it would be good to setup black and add a pre-commit hook.
I'm getting the following warning when testing the API:
ResourceWarning: unclosed <ssl.SSLSocket fd=1104, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.5.66', 50373), raddr=('204.89.196.131', 443)>
The reason is that the Questrade class is missing a session closing statement. Please add the following:
class Questrade:
...
def __del__(self):
self.session.close()
Thank you,
As the title says, I should rebuild the docs and release 0.5.0
thanks to all contributors for qtrade, which works well for me.
The ability to get the latest execution would be a great addition (unless already available and i am not aware of).
In particular, that info is required to compute intra-day PnL.
https://www.questrade.com/api/documentation/rest-operations/account-calls/accounts-id-executions
Best
Currently, the error messages are not very informative. Add much clearer error messages that actually tell the user what went wrong.
Now that I have version dependent installs and code, I should test multiple versions to make sure all of them work.
Environment: conda python==3.8 environment with qtrade
and ipython
installed
Issue:
'OneDay'
works with get_historical_data
:
uvxy = q.get_historical_data('UVXY', '2019-07-01', '2019-07-02', 'OneDay')
but changing 'OneDay'
to 'OneHour'
causes error:
>>> uvxy = q.get_historical_data('UVXY', '2019-07-01', '2019-07-02', 'OneHour')
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
<ipython-input-22-a0637f02abb7> in <module>
----> 1 uvxy = q.get_historical_data('UVXY', '2019-07-01', '2019-07-02', 'OneHour')
~/Documents/projects/qtrade/qtrade/questrade.py in get_historical_data(self, ticker, start_date, end_date, interval)
448 )
449
--> 450 response = self._send_message(
451 "get", "markets/candles/" + str(ids), params=payload
452 )
~/Documents/projects/qtrade/qtrade/questrade.py in _send_message(self, method, endpoint, params, data, json)
83 method, url, params=params, data=data, json=json, timeout=30
84 )
---> 85 resp.raise_for_status()
86 return resp.json()
87
~/anaconda3/envs/qtrade/lib/python3.8/site-packages/requests/models.py in raise_for_status(self)
939
940 if http_error_msg:
--> 941 raise HTTPError(http_error_msg, response=self)
942
943 def close(self):
HTTPError: 400 Client Error: Bad Request for url: https://api06.iq.questrade.com/v1/markets/candles/22992135?startTime=2019-07-01T00%3A00%3A00-05%3A00&endTime=2019-07-02T00%3A00%3A00-05%3A00&interval=OneHour
I think the current minimum version of pyyaml is way too old and should not be used anymore.
Hi, I've enjoyed using your Questrade wrapper to perform analytic works on my accounts at Questrade. Just one suggestion, I hope there's a method which retrieves account balance data such as cash amount, total equity, and total market value by currencies. I figured in my own way to retrieve my balance data like below, but having this feature officially on your wrapper would be quite useful.
(^ Not sure if this is a safe and right way to get the data from API, but it works fine with me so far)
Thanks!
Maybe a url link needs to be updated in your functions. If I can update it myself I don't mind doing it, if you can tell me where it is in the code.¸
I simply coded:
from qtrade import Questrade
qtrade = Questrade(access_code='abc') #abc is my personnel token
The output is the error code:
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://login.questrade.com/oauth2/token?grant_type=refresh_token&refresh_token=MyqKC7-5ywmcBAPmPzjJfzjgToQYM3P90
Your help would be apprciated
Firstly, thanks @jborchma for this library.
I am able to download data such as ticker information, account info etc, but when i try to download historical data i get the below error:
HTTPError: 403 Client Error: Forbidden for url: https://api02.iq.questrade.com/v1/markets/candles/36261155?startTime=2019-07-01T00%3A00%3A00-05%3A00&endTime=2019-07-02T00%3A00%3A00-05%3A00&interval=OneDay
Does this mean i do not have access to historical market data?
Anyone else get this issue?
Thanks
As the title says, add sphinx documentation and look into automatically deploying it to gh-pages
using travis.
Hi,
When I try to call get_account_balances I get the following error: AttributeError: 'Questrade' object has no attribute 'get_account_balances'
In the questrade.py file I see the function is in there, not sure why Im not able to call it. Sorry If Im missing something, im new to python.
Thanks
The readme should have a couple of more examples beyond just showing the token handling.
Should add function to make sure the full access token was provided.
Hello Jan. First, thanks for creating this package, and I look forward to using it.
I was not able to obtain an access token. Can you please clarify the initialization workflow and where the values are sourced from? I did the following:
I then get the following traceback
Traceback (most recent call last):
File "./options.py", line 11, in <module>
qtrade = Questrade(access_code=access_code)
File "/app/qtrade/qtrade/questrade.py", line 38, in __init__
self.get_access_token()
File "/app/qtrade/qtrade/questrade.py", line 83, in get_access_token
data.raise_for_status()
File "/usr/local/lib/python3.7/site-packages/requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://login.questrade.com/oauth2/token?grant_type=refresh_token&refresh_token=1234567890ABCDEFG
It would be nice to automatically build the sphinx docs from GH actions when I add a new release. Also pushing the new release to pypi would be nice.
Going one step further, a push to master
could automatically create the release (release message could be found from the last PR). But that's probably optional.
Hi Jan, Is it possible to place a new order using code? I only see a provision to retrieve existing orders using the questrade api, it is mentioned in the following Questrade documentation - https://www.questrade.com/api/documentation/rest-operations/account-calls/accounts-id-orders.
But is it also possible to create a new order using questrade api? Looking forward to your response. Thank you.
Add a method to connect to L1 streaming and show and eventually export to csv all the transaction
The token management could definitely be improved. Maybe it would work in conjunction with the requests.Session object. Definitely room for improvement.
the submit_order is not working with me:
the http status code is 400 (bad request). Can you please give us an example ? I am using a partner app (Medved Trader)
The way that method is currently written, is shouldn't actually be used by the user. It is just a method that's used when initializing the class.
I think it would make sense to include things like flake8, isort and the generic whitespace linters from pre-commit in the yaml-file.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.