Skip to content

Commit

Permalink
Add content to README and some docstrings.
Browse files Browse the repository at this point in the history
  • Loading branch information
LVG77 committed Mar 17, 2024
1 parent 134215f commit dac6836
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 8 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,33 @@ Python wrapper for Questrade API

Install this library using `pip`:
```bash
# not released yet
pip install qt-api
```
## Usage

Usage instructions go here.
Use access code from Questrade API.
Also optionally you can use acct_flag to create account-specific credentials file.

```python
from qt_api.qt import Questrade

qt = Questrade(access_code="xxx", acct_flag="zz")
```

Optionally, you can refresh access token like so:
```python
qt.refresh_access_token()
```

Then you can use any of the provided methods. For example to get a list of all accounts:
```python
accounts = qt.get_account()
```
... or to get a quote for a list of symbols:
```python
quotes = qt.get_symbol_quote(symbols=["AAPL", "MSFT"])
```

## Development

Expand Down
23 changes: 21 additions & 2 deletions qt_api/qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ def validate_dict(input_dict):


class Questrade:
"""A class for interacting with the Questrade API.
This class provides methods for getting account information, positions,
and executions, as well as retrieving quotes for tickers. The class can
also be used to refresh the access token if it has expired.
To use this class, either an access code or an access token must be provided.
If an access code is provided, the class will use it to generate and save
an access token to a file in the user's home directory. If an access token
is provided, it will be used instead of generating one from an access code.
Args:
access_code (str): The access code for generating an access token.
If not provided, the class will try to load an access token from
a file in the user's home directory.
acct_flag (str): A unique identifier for the account. This will be used
when saving the access token to a file. If not provided, the
access token will be saved to a file named "creds.yaml".
"""
def __init__(self, access_code:str = None, acct_flag:str = None):
self.access_code = access_code
self.acct_flag = acct_flag
Expand Down Expand Up @@ -87,7 +106,7 @@ def _get_access_token(self)->None:
self.headers = {"Authorization": self.access_token.token_type
+ " " + self.access_token.access_token}
# save access token to file
save_creds(self.access_token)
save_creds(self.access_token, self.acct_flag)

def refresh_access_token(self)->None:
"Refresh access token before it has expired"
Expand All @@ -103,7 +122,7 @@ def refresh_access_token(self)->None:
self.headers = {"Authorization": self.access_token.token_type
+ " " + self.access_token.access_token}
# save access token to file
save_creds(self.access_token)
save_creds(self.access_token, self.acct_flag)
print("Token refreshed successfully")


Expand Down
22 changes: 20 additions & 2 deletions qt_api/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import time
from datetime import datetime, timedelta
from qt_api.qt import Questrade

Expand Down Expand Up @@ -29,12 +30,29 @@ def generate_date_pairs(n_pairs:int, time_delta:int = 30, start_date:str = None)
return date_pairs


def get_acct_activities(qt: Questrade, acct_no: int, n: int, verbose: bool = True)->list[dict]:
"Get activities for account `acct_no` for `n` consecutive 30-day periods."
def get_acct_activities(qt: Questrade, acct_no: int, n: int, trottle: float = None, verbose: bool = True) -> list[dict]:
"""
Get activities for account `acct_no` for `n` consecutive 30-day periods.
Args:
qt (Questrade): The Questrade API client.
acct_no (int): The account number.
n (int): The number of 30-day periods to get activities for.
trottle (float, optional): The amount of time in seconds to wait between
API requests. Defaults to None.
verbose (bool, optional): Whether to print a message for each period
being retrieved. Defaults to True.
Returns:
list[dict]: A list of activity records.
"""

date_pairs = generate_date_pairs(n_pairs=n)
activities = []
for d2,d1 in date_pairs:
print(f"Getting data for {d1} to {d2} period")
r = qt.get_activities(acct_no, d1, d2)
activities.extend(r)
if trottle:
time.sleep(trottle)
return activities
6 changes: 3 additions & 3 deletions tests/test_questrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def test_get_access_token(mock_validate_dict, mock_save_creds, mock_get, mock_ac
# Assert
mock_get.assert_called_once_with(TOKEN_URL + mock_access_code)
mock_validate_dict.assert_called_once_with(sample_creds_data)
mock_save_creds.assert_called_once_with(QTTokenFile(**sample_creds_data))
mock_save_creds.assert_called_once_with(QTTokenFile(**sample_creds_data), None)
assert questrade.headers == {"Authorization": "some abc"}

@mock.patch('qt_api.qt.load_creds')
Expand Down Expand Up @@ -47,11 +47,11 @@ def test_send_request(mock_client, mock_get_access_token, sample_creds_data):
@mock.patch('qt_api.qt.save_creds')
@mock.patch('qt_api.qt.validate_dict')
def test_refresh_access_token(mock_validate_dict, mock_save_creds, mock_get, mock_get_access_token, sample_creds_data):
qt = Questrade(access_code="xxxv1")
qt = Questrade(access_code="xxxv1", acct_flag="xx")
qt.access_token = QTTokenFile(**sample_creds_data)
mock_get.raise_for_status.return_value = None
mock_get.return_value.json.return_value = sample_creds_data
req = qt.refresh_access_token()
assert mock_get.call_args[0][0] == TOKEN_URL + qt.access_token.refresh_token
mock_validate_dict.assert_called_once_with(sample_creds_data)
mock_save_creds.assert_called_once_with(QTTokenFile(**sample_creds_data))
mock_save_creds.assert_called_once_with(QTTokenFile(**sample_creds_data), "xx")

0 comments on commit dac6836

Please sign in to comment.