GithubHelp home page GithubHelp logo

questrade_api's Introduction

questrade_api

Python3 Questrade API Wrapper

Installation

  • Use pip/pip3:

    pip3 install questrade-api

Getting Started

  1. Familiarise yourself with the Security documentation for the Questrade API.

  2. Generate a manual refresh token for your application.

  3. Init the API Wrapper with the refresh token:

    from questrade_api import Questrade
    q = Questrade(refresh_token='XYz1dBlop33lLLuys4Bd')
    

    Important: A token will be created at ~/.questrade.json and used for future API calls

    • If the token is valid future initiations will not require a refresh token
    from questrade_api import Questrade
    q = Questrade()
    

Using the API

q.time
=> {'time': '2018-11-16T09:22:27.090000-05:00'}
q.accounts
=> {'accounts': [{'type': 'Margin', 'number': '123456', 'status': 'Active' ...}]}

Accepts: <account_id>

q.account_positions(123456)
=> {'positions': [{'symbol': 'RY.TO', 'symbolId': 34658, 'openQuantity': ...}]}

Accepts: <account_id>

q.account_balances(123456)
=> {'perCurrencyBalances': [{'currency': 'CAD', 'cash': 1000, 'marketValue': 0, ...}]}

Accepts: <account_id>, startTime=, endTime=

q.account_executions(123456)
=> {'executions': []}
q.account_executions(123456,startTime='2018-08-01T00:00:00-0')
=> {'executions': [{'symbol': 'RY.TO', 'symbolId': 34658, 'quantity': 100, ...}]}

Accepts: <account_id>, startTime=, endTime=, stateFilter=

q.account_orders(123456)
=> {'orders': []}
q.account_orders(123456, startTime='2018-08-01T00:00:00-0')
=> {'orders': [{'id': 444444, 'symbol': 'RY.TO', 'symbolId': 34658, ...}]}

Accepts: <account_id>, <order_id>

q.account_order(123456, 444444)
=> {'orders': [{'id': 444444, 'symbol': 'RY.TO', 'symbolId': 34658, 'totalQuantity': 100, ...}]}

Accepts: <account_id>, startTime=, endTime=

q.account_activities(123456)
=> {'activities': []}
q.account_activities(123456, startTime='2018-11-01T00:00:00-0')
=> {'activities': []}

Accepts: <symbol_id>

q.symbol(34659)
=> {'symbols': [{'symbol': 'RY.TO 'symbolId': 34658, 'prevDayClosePrice': ...}]}

Accepts: ids='<symbol_id_1>,<symbol_id_2>', names='<symbol_1>,<symbol_2>'

q.symbols(ids='34658,9339')
=> {'symbols': [{'symbol': 'RY.TO', 'symbolId': 34658, 'prevDayClosePrice': ..}]}
q.symbols(names='RY.TO,BNS.TO')
=> {'symbols': [{'symbol': 'RY.TO', 'symbolId': 34658, 'prevDayClosePrice': ..}]}

Accepts: prefix='<symbol_1>', offset=

q.symbols_search(prefix='RY.TO')
=> {'symbols': [{'symbol': 'RY.TO', 'symbolId': 34658, 'description': ...}]}
q.symbols_search(prefix='RY', offset=5)
{'symbols': [{'symbol': 'RY.PRE.TO', 'symbolId': 34700, 'description': ...}]}

Accepts: <symbol_id>

q.symbol_options(34658)
=> {'optionChain': [{'expiryDate': '2018-11-16T00:00:00.000000-05:00', 'description': ... }]}
q.markets
=> {'markets': [{'name': 'TSX', 'tradingVenues': ['TSX', 'ALPH', 'CXC', ... }]}

Accepts: <symbol_id>

q.markets_quote(34658)
=> {'quotes': [{'symbol': 'RY.TO', 'symbolId': 34658, 'tier': ... }]}

Accepts: ids='<symbol_id_1>,<symbol_id_2>'

q.markets_quotes(ids='34658,9339')
=> {'quotes': [{'symbol': 'RY.TO', 'symbolId': 34658, 'tier': ... }]}

Accepts: optionIds=, filters=

q.markets_options(optionIds=[
    23615873,
    23615874
])
=> {'optionQuotes': [{'underlying': 'RY.TO', 'underlyingId': 34658, 'symbol': 'RY30Nov18 ..}]}
q.markets_options(filters=[
    {
        "optionType": "Call",
        "underlyingId": 34658,
        "expiryDate": "2018-11-30T00:00:00.000000-05:00",
        "minstrikePrice": 90,
        "maxstrikePrice": 100
    }
])
=> {'optionQuotes': [{'underlying': 'RY.TO', 'underlyingId': 34658, 'symbol': 'RY30Nov18 ..}]}

Accepts: variants=

q.markets_strategies(variants=[
    {
        "variantId": 1,
        "strategy": "Custom",
        "legs": [
            {
                    "symbolId": 23545340,
                    "ratio": 10,
                    "action": "Buy"
            },
            {
                "symbolId": 23008592,
                "ratio": 10,
                "action": "Sell"
            }
        ]
    }
])
=> {'strategyQuotes': [{'variantId': 1, 'bidPrice': None, 'askPrice': None, 'underlying': 'RY.TO' ...}]}

Accepts: <symbol_id>, startTime=, endTime=, interval=

q.markets_candles(34658, interval='FiveMinutes')
=> {'candles': [{'start': '2018-11-16T09:30:00.583000-05:00', 'end': '2018-11-16T ..}]}

questrade_api's People

Contributors

mgostick avatar tgardiner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

questrade_api's Issues

Failed to initiate Questrade - __init__() takes 1 positional argument but 2 were given

`Python 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 18:10:19)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

from questrade_api import Questrade
q = Questrade('WOSrhAAL1L1TysWNITnqUoAYIeDfEeV99')
Traceback (most recent call last):
File "", line 1, in
TypeError: init() takes 1 positional argument but 2 were given

`

account_orders time input

trying to use the account_orders method but it keeps returning {'code': 1002, 'message': 'Invalid or malformed argument: start_time'}

I can't am using ISO format like the example, I can't even get the example request to work with the same datetime

vague 500 error... Using Colab

Get a vague 500 error when trying to init a questrade class.

  1 t = 'MYLONGTOKENSTRINGHERE'

----> 2 q = Questrade(refresh_token=t)
3 print(q)

8 frames
/usr/local/lib/python3.6/dist-packages/questrade_api/questrade.py in init(self, **kwargs)
20 ['token_path', 'refresh_token']}
21
---> 22 self.auth = Auth(**auth_kwargs, config=self.config)
23
24 def __read_config(self, fpath):

/usr/local/lib/python3.6/dist-packages/questrade_api/auth.py in init(self, **kwargs)
18 self.token_path = TOKEN_PATH
19 if 'refresh_token' in kwargs:
---> 20 self.__refresh_token(kwargs['refresh_token'])
21
22 def __read_token(self):

/usr/local/lib/python3.6/dist-packages/questrade_api/auth.py in __refresh_token(self, token)
35 def __refresh_token(self, token):
36 req_time = int(time.time())
---> 37 r = request.urlopen(self.config['Auth']['RefreshURL'].format(token))
38 if r.getcode() == 200:
39 token = json.loads(r.read().decode('utf-8'))

/usr/lib/python3.6/urllib/request.py in urlopen(url, data, timeout, cafile, capath, cadefault, context)
221 else:
222 opener = _opener
--> 223 return opener.open(url, data, timeout)
224
225 def install_opener(opener):

/usr/lib/python3.6/urllib/request.py in open(self, fullurl, data, timeout)
530 for processor in self.process_response.get(protocol, []):
531 meth = getattr(processor, meth_name)
--> 532 response = meth(req, response)
533
534 return response

/usr/lib/python3.6/urllib/request.py in http_response(self, request, response)
640 if not (200 <= code < 300):
641 response = self.parent.error(
--> 642 'http', request, response, code, msg, hdrs)
643
644 return response

/usr/lib/python3.6/urllib/request.py in error(self, proto, *args)
568 if http_err:
569 args = (dict, 'default', 'http_error_default') + orig_args
--> 570 return self._call_chain(*args)
571
572 # XXX probably also want an abstract factory that knows when it makes

/usr/lib/python3.6/urllib/request.py in _call_chain(self, chain, kind, meth_name, *args)
502 for handler in handlers:
503 func = getattr(handler, meth_name)
--> 504 result = func(*args)
505 if result is not None:
506 return result

/usr/lib/python3.6/urllib/request.py in http_error_default(self, req, fp, code, msg, hdrs)
648 class HTTPDefaultErrorHandler(BaseHandler):
649 def http_error_default(self, req, fp, code, msg, hdrs):
--> 650 raise HTTPError(req.full_url, code, msg, hdrs, fp)
651
652 class HTTPRedirectHandler(BaseHandler):

HTTPError: HTTP Error 500: Internal Server Error

json.decoder.JSONDecodeError: Expecting value: line 1 column 2 (char 1)

*** 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

QT API timeout/disconnect issue

Hello team,

I have been running the API for a couple weeks now and it works fine; however, it seems to time out after a couple days or only after 1 or 2 days.

There is no exact interval of disconnect/timeout period. Please see the error below. I have to go into APP HUB and refresh the token and input into code then it starts working again. Is there a way to avoid this timeout issue?

Thank you.

Traceback (most recent call last):
File "/home/compadmin/Documents/PYTHON/QT_APP_01/API.py", line 6, in
print(q.time)
File "/home/compadmin/.local/lib/python3.10/site-packages/questrade_api/questrade.py", line 81, in time
return self.__get(self.config['API']['time'])
File "/home/compadmin/.local/lib/python3.10/site-packages/questrade_api/questrade.py", line 49, in __get
r = urllib.request.urlopen(req)
File "/usr/lib/python3.10/urllib/request.py", line 216, in urlopen
return opener.open(url, data, timeout)
File "/usr/lib/python3.10/urllib/request.py", line 519, in open
response = self._open(req, data)
File "/usr/lib/python3.10/urllib/request.py", line 536, in _open
result = self._call_chain(self.handle_open, protocol, protocol +
File "/usr/lib/python3.10/urllib/request.py", line 496, in _call_chain
result = func(*args)
File "/usr/lib/python3.10/urllib/request.py", line 1391, in https_open
return self.do_open(http.client.HTTPSConnection, req,
File "/usr/lib/python3.10/urllib/request.py", line 1352, in do_open
r = h.getresponse()

File "/usr/lib/python3.10/http/client.py", line 1375, in getresponse
response.begin()
File "/usr/lib/python3.10/http/client.py", line 318, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.10/http/client.py", line 287, in _read_status
raise RemoteDisconnected("Remote end closed connection without"
http.client.RemoteDisconnected: Remote end closed connection without response
compadmin@VM01:~/Documents/PYTHON/QT_APP_01$ /bin/python3 /home/compadmin/Documents/PYTHON/QT_APP_01/API.py
Traceback (most recent call last):
File "/home/compadmin/Documents/PYTHON/QT_APP_01/API.py", line 6, in
print(q.time)
File "/home/compadmin/.local/lib/python3.10/site-packages/questrade_api/questrade.py", line 81, in time
return self.__get(self.config['API']['time'])
File "/home/compadmin/.local/lib/python3.10/site-packages/questrade_api/questrade.py", line 49, in __get
r = urllib.request.urlopen(req)
File "/usr/lib/python3.10/urllib/request.py", line 216, in urlopen
return opener.open(url, data, timeout)
File "/usr/lib/python3.10/urllib/request.py", line 519, in open
response = self._open(req, data)
File "/usr/lib/python3.10/urllib/request.py", line 536, in _open
result = self._call_chain(self.handle_open, protocol, protocol +
File "/usr/lib/python3.10/urllib/request.py", line 496, in _call_chain
result = func(*args)
File "/usr/lib/python3.10/urllib/request.py", line 1391, in https_open
return self.do_open(http.client.HTTPSConnection, req,
File "/usr/lib/python3.10/urllib/request.py", line 1352, in do_open
r = h.getresponse()
File "/usr/lib/python3.10/http/client.py", line 1375, in getresponse
response.begin()
File "/usr/lib/python3.10/http/client.py", line 318, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.10/http/client.py", line 287, in _read_status
raise RemoteDisconnected("Remote end closed connection without"
http.client.RemoteDisconnected: Remote end closed connection without response

Subsequent Auth Fails with Token

Hi Tom,

Neat project. I noticed something weird. I am using a FastAPI app, in my app.py I always make a connection and instantiate the Q object (= Questrade(refresh_token='XX') ).
The first time I run the code it works fine, and a restart causes 400 bad requests. It only works if I change the refresh token from the dashboard or have Q = Questrade() (without the refresh token).

I figure this might be an issue with the API maybe.

{'code': 1016, 'message': 'Request is out of allowed OAuth scopes'}

When attempting to call quote data I receive the above error. I was under the impression the QT api allowed level 1 quotes without paid data subscription, or do I require a paid subscription for this to work.

Code:

q.markets_options(filters=[
{
"optionType": "Call",
"underlyingId": 34658,
"expiryDate": "2019-11-30T00:00:00.000000-05:00",
"minstrikePrice": 90,
"maxstrikePrice": 100
}
])

Error:

{'code': 1016, 'message': 'Request is out of allowed OAuth scopes'}

Questrade API Error Codes

Hello,
I often get the RemoteDisconnected error from the API (traceback after the message), but how can I get the Questrade API Error Codes that they have listed in their documentation?

https://www.questrade.com/api/documentation/rate-limiting
https://www.questrade.com/api/documentation/error-handling

I am especially interested to know if I am getting their error 429 'Rate limit exceeded'. Not sure how to capture that from your library.

Thanks!
Mike


RemoteDisconnected Traceback (most recent call last)
in
2 #q = Questrade(refresh_token='MkEKOubWvAyHugGYpE81nInoKB5RimNP0')
3 q=Questrade()
----> 4 q.time
5 #q.symbols_search(prefix='ARKF')['symbols'][0]['symbolId']
6 #oids=",".join([str(x['symbolId']) for x in q.symbols(names='LMND,TSLA,AAPL')['symbols']])

~/opt/anaconda3/lib/python3.7/site-packages/questrade_api/questrade.py in time(self)
79 @Property
80 def time(self):
---> 81 return self.__get(self.config['API']['time'])
82
83 @Property

~/opt/anaconda3/lib/python3.7/site-packages/questrade_api/questrade.py in __get(self, url, params)
47 )
48 try:
---> 49 r = urllib.request.urlopen(req)
50 return json.loads(r.read().decode('utf-8'))
51 except urllib.error.HTTPError as e:

~/opt/anaconda3/lib/python3.7/urllib/request.py in urlopen(url, data, timeout, cafile, capath, cadefault, context)
220 else:
221 opener = _opener
--> 222 return opener.open(url, data, timeout)
223
224 def install_opener(opener):

~/opt/anaconda3/lib/python3.7/urllib/request.py in open(self, fullurl, data, timeout)
523 req = meth(req)
524
--> 525 response = self._open(req, data)
526
527 # post-process response

~/opt/anaconda3/lib/python3.7/urllib/request.py in _open(self, req, data)
541 protocol = req.type
542 result = self._call_chain(self.handle_open, protocol, protocol +
--> 543 '_open', req)
544 if result:
545 return result

~/opt/anaconda3/lib/python3.7/urllib/request.py in _call_chain(self, chain, kind, meth_name, *args)
501 for handler in handlers:
502 func = getattr(handler, meth_name)
--> 503 result = func(*args)
504 if result is not None:
505 return result

~/opt/anaconda3/lib/python3.7/urllib/request.py in https_open(self, req)
1360 def https_open(self, req):
1361 return self.do_open(http.client.HTTPSConnection, req,
-> 1362 context=self._context, check_hostname=self.check_hostname)
1363
1364 https_request = AbstractHTTPHandler.do_request

~/opt/anaconda3/lib/python3.7/urllib/request.py in do_open(self, http_class, req, **http_conn_args)
1320 except OSError as err: # timeout error
1321 raise URLError(err)
-> 1322 r = h.getresponse()
1323 except:
1324 h.close()

~/opt/anaconda3/lib/python3.7/http/client.py in getresponse(self)
1342 try:
1343 try:
-> 1344 response.begin()
1345 except ConnectionError:
1346 self.close()

~/opt/anaconda3/lib/python3.7/http/client.py in begin(self)
304 # read until we get a non-100 response
305 while True:
--> 306 version, status, reason = self._read_status()
307 if status != CONTINUE:
308 break

~/opt/anaconda3/lib/python3.7/http/client.py in _read_status(self)
273 # Presumably, the server closed the connection before
274 # sending a valid response.
--> 275 raise RemoteDisconnected("Remote end closed connection without"
276 " response")
277 try:

RemoteDisconnected: Remote end closed connection without response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.