kwess 0.0.6__tar.gz → 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kwess-0.0.6/kwess.egg-info → kwess-0.1.0}/PKG-INFO +93 -19
- kwess-0.0.6/PKG-INFO → kwess-0.1.0/README.md +84 -30
- {kwess-0.0.6 → kwess-0.1.0}/kwess/__init__.py +25 -25
- kwess-0.1.0/kwess/__main__.py +7 -0
- kwess-0.1.0/kwess/kwess_acb.py +176 -0
- kwess-0.0.6/README.md → kwess-0.1.0/kwess.egg-info/PKG-INFO +104 -8
- {kwess-0.0.6 → kwess-0.1.0}/kwess.egg-info/SOURCES.txt +3 -1
- kwess-0.1.0/kwess.egg-info/entry_points.txt +2 -0
- {kwess-0.0.6 → kwess-0.1.0}/kwess.egg-info/top_level.txt +1 -0
- kwess-0.1.0/pyproject.toml +46 -0
- kwess-0.1.0/setup.cfg +4 -0
- kwess-0.0.6/pyproject.toml +0 -3
- kwess-0.0.6/setup.cfg +0 -39
- {kwess-0.0.6 → kwess-0.1.0}/LICENSE.txt +0 -0
- {kwess-0.0.6 → kwess-0.1.0}/kwess.egg-info/dependency_links.txt +0 -0
- {kwess-0.0.6 → kwess-0.1.0}/kwess.egg-info/requires.txt +0 -0
- {kwess-0.0.6 → kwess-0.1.0}/kwess.egg-info/zip-safe +0 -0
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: kwess
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: Questrade API wrapper.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Project-URL: Bug Tracker, https://github.com/kaiyoux/kwess/issues
|
|
12
|
-
Keywords: Questrade,api,REST,wrapper
|
|
5
|
+
Author-email: Issa Lompo <kaiyoux@gmail.com>
|
|
6
|
+
Maintainer-email: Issa Lompo <kaiyoux@gmail.com>
|
|
7
|
+
License-Expression: GPL-3.0-or-later
|
|
8
|
+
Project-URL: Homepage, https://github.com/pypa/kwess
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com
|
|
10
|
+
Keywords: questrade,api,REST,wrapper,ACB,profit,loss,average cost base,adjusted cost base,logs,kwess
|
|
13
11
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
15
12
|
Classifier: Operating System :: OS Independent
|
|
16
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
17
14
|
Classifier: Intended Audience :: Developers
|
|
@@ -19,14 +16,19 @@ Requires-Python: >=3.6
|
|
|
19
16
|
Description-Content-Type: text/markdown
|
|
20
17
|
License-File: LICENSE.txt
|
|
21
18
|
Requires-Dist: requests>=2.28.1
|
|
19
|
+
Dynamic: license-file
|
|
22
20
|
|
|
23
21
|
# Introduction
|
|
24
22
|
|
|
25
23
|
Yet another Questrade API wrapper.
|
|
26
|
-
|
|
27
24
|
For details about the input parameters and output results, please visit
|
|
28
25
|
the [Questrade API documentation](https://www.questrade.com/api/home).
|
|
29
26
|
|
|
27
|
+
Also includes an executable module that determines the ACB
|
|
28
|
+
(Average/Adjusted Cost Base) of buy and sell activities from
|
|
29
|
+
saved logs, and prints the profit/loss amounts of sell transactions
|
|
30
|
+
up till, but not including, a given cut-off year.
|
|
31
|
+
|
|
30
32
|
### To install:
|
|
31
33
|
**python -m pip install kwess**
|
|
32
34
|
|
|
@@ -56,6 +58,7 @@ for acc in accs:
|
|
|
56
58
|
print(acc, "\n")
|
|
57
59
|
|
|
58
60
|
# Get (all types of) TFSA account orders from 17/8/2022 to now
|
|
61
|
+
# Questrade does not seem to keep old account orders - only the most recent
|
|
59
62
|
accs = qs.get_account_orders(startdatetime=dt(year=2022, month=8, day=17), \
|
|
60
63
|
verbose="vv")
|
|
61
64
|
for acc in accs:
|
|
@@ -98,7 +101,7 @@ print(accs)
|
|
|
98
101
|
sim = qs.search_symbols("vfv", verbose="88")
|
|
99
102
|
print(sim)
|
|
100
103
|
|
|
101
|
-
sim = qs.get_symbols_by_names("xdiv.to,xuu.to,
|
|
104
|
+
sim = qs.get_symbols_by_names("xdiv.to,xuu.to,qqc.to", verbose="**")
|
|
102
105
|
print(sim)
|
|
103
106
|
|
|
104
107
|
sim = qs.get_symbols_by_names("hom.un.to")
|
|
@@ -127,6 +130,50 @@ pprint(ops)
|
|
|
127
130
|
|
|
128
131
|
```
|
|
129
132
|
|
|
133
|
+
# Sample Script To Pull Your Questrade Logs
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
from kwess import Trader
|
|
137
|
+
from datetime import datetime as dt
|
|
138
|
+
import json
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_logs(qt, startdate, enddate=None, accounttype="TFSA"):
|
|
142
|
+
"""
|
|
143
|
+
Parameters:
|
|
144
|
+
- qt Trader object.
|
|
145
|
+
- startdate datetime object for beginning period.
|
|
146
|
+
- enddate datetime object for end period. Defaults to None, which will be treated as now.
|
|
147
|
+
- accounttype type of account for which activities will be obtained. Defaults to TFSA.
|
|
148
|
+
"""
|
|
149
|
+
if enddate == None:
|
|
150
|
+
enddate = dt.now()
|
|
151
|
+
for filename in ["activities", "orders", "executions"]:
|
|
152
|
+
filename = accounttype + "_account_" + filename + "_from_" + str(startdate.date()) + "_to_" + str(enddate.date()) + ".json"
|
|
153
|
+
with open(filename, mode="at", encoding="utf-8") as jfp:
|
|
154
|
+
if "activities" in filename:
|
|
155
|
+
accs = qt.get_account_activities(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose="nnn")
|
|
156
|
+
elif "orders" in filename:
|
|
157
|
+
accs = qt.get_account_orders(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose="nnn")
|
|
158
|
+
else:
|
|
159
|
+
accs = qt.get_account_executions(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose='nnn')
|
|
160
|
+
|
|
161
|
+
for acc in accs:
|
|
162
|
+
json.dump(acc, jfp, ensure_ascii=False, indent=2)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
#account_types = ["TFSA", "Cash", "Margin"]
|
|
169
|
+
#account_types = ["Cash"]
|
|
170
|
+
account_types = ["Margin"]
|
|
171
|
+
qt = Trader(verbose='n')
|
|
172
|
+
|
|
173
|
+
for acc_type in account_types:
|
|
174
|
+
get_logs(qt=qt, accounttype=acc_type, startdate=dt(year=2025, month=9, day=17))
|
|
175
|
+
|
|
176
|
+
```
|
|
130
177
|
|
|
131
178
|
# API Class And Methods
|
|
132
179
|
|
|
@@ -159,7 +206,7 @@ Parameters:
|
|
|
159
206
|
- server_type could be 2 possible values: "live" or "test".
|
|
160
207
|
"live" will allow you to interact with your real Questrade account.
|
|
161
208
|
"test" is for interacting with your test account.
|
|
162
|
-
- timeout number of seconds to wait for the
|
|
209
|
+
- timeout number of seconds to wait for the server to respond before
|
|
163
210
|
giving up.
|
|
164
211
|
Defaults to 15 seconds. Set timeout to None if you wish to wait forever
|
|
165
212
|
for a response.
|
|
@@ -173,7 +220,7 @@ build_datetime_string(self, adatetime=None, gmt=False)
|
|
|
173
220
|
Description:
|
|
174
221
|
Higher level helper method used to build a Questrade datetime string.
|
|
175
222
|
Parameters:
|
|
176
|
-
- adatetime a datetime object.
|
|
223
|
+
- adatetime a datetime object. Defaults to now.
|
|
177
224
|
- gmt optional boolean indicating if datetime is Greenwich Mean Time.
|
|
178
225
|
Default value is False.
|
|
179
226
|
Returns:
|
|
@@ -222,8 +269,8 @@ Returns:
|
|
|
222
269
|
Account balances as a Python object representation of the returned json.
|
|
223
270
|
|
|
224
271
|
|
|
225
|
-
get_account_executions(self,
|
|
226
|
-
|
|
272
|
+
get_account_executions(self, startdatetime, enddatetime=None,
|
|
273
|
+
accounttype='TFSA', verbose='')
|
|
227
274
|
Description:
|
|
228
275
|
Generator that provides account executions from the account related to
|
|
229
276
|
account type accounttype, between the range specified by startdatetime and
|
|
@@ -304,7 +351,7 @@ Yields:
|
|
|
304
351
|
json.
|
|
305
352
|
|
|
306
353
|
|
|
307
|
-
get_market_candles(self, sid, interval, startdatetime
|
|
354
|
+
get_market_candles(self, sid, interval, startdatetime, enddatetime=None,
|
|
308
355
|
verbose='')
|
|
309
356
|
Description:
|
|
310
357
|
Provides a list of json formatted market candles.
|
|
@@ -380,7 +427,7 @@ Returns:
|
|
|
380
427
|
|
|
381
428
|
get_new_refresh_token(self, token, server_type, verbose='')
|
|
382
429
|
Description:
|
|
383
|
-
Obtains a new refresh token (and new access token) from the
|
|
430
|
+
Obtains a new refresh token (and new access token) from the Authorization server.
|
|
384
431
|
You generally would not need to call this method, as it is potentially called by
|
|
385
432
|
Trader during initialization.
|
|
386
433
|
Trader will only call this method if the access token has expired.
|
|
@@ -500,5 +547,32 @@ Returns:
|
|
|
500
547
|
If gmt is True, the returned time will be considered as gmt time.
|
|
501
548
|
```
|
|
502
549
|
|
|
550
|
+
# Example of Executable Module
|
|
551
|
+
|
|
552
|
+
Open a command line in the directory containing your logs, and run the module:
|
|
553
|
+
```
|
|
554
|
+
kwess_acb
|
|
555
|
+
(or)
|
|
556
|
+
python -m kwess.kwess_acb
|
|
557
|
+
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### To run the scrpit:
|
|
561
|
+
**kwess_acb [-h] [-s SYMBOL [SYMBOL ...]] [-y CUT_OFF_YEAR] [-p PATH]**
|
|
562
|
+
```
|
|
563
|
+
Computes the ACB (Average/Adjusted Cost Base) from kwess activity logs, and
|
|
564
|
+
prints profit/loss of Sell transactions.
|
|
565
|
+
|
|
566
|
+
options:
|
|
567
|
+
-h, --help show this help message and exit.
|
|
568
|
+
-s SYMBOL [SYMBOL ...], --symbol SYMBOL [SYMBOL ...]
|
|
569
|
+
ticker symbol(s).
|
|
570
|
+
-y CUT_OFF_YEAR, --cut_off_year CUT_OFF_YEAR
|
|
571
|
+
cut off year. defaults to the current year.
|
|
572
|
+
-p PATH, --path PATH path of the directory containing the logs.
|
|
573
|
+
defaults to the current directory.
|
|
574
|
+
|
|
575
|
+
```
|
|
576
|
+
|
|
503
577
|
|
|
504
578
|
Let me know if you have any questions: <kaiyoux@gmail.com>
|
|
@@ -1,32 +1,14 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: kwess
|
|
3
|
-
Version: 0.0.6
|
|
4
|
-
Summary: Questrade API wrapper.
|
|
5
|
-
Home-page: https://github.com/kaiyoux/kwess
|
|
6
|
-
Author: Issa Lompo
|
|
7
|
-
Author-email: kaiyoux@gmail.com
|
|
8
|
-
Maintainer: Issa Lompo
|
|
9
|
-
Maintainer-email: kaiyoux@gmail.com
|
|
10
|
-
License: GNU General Public License v3 (GPLv3)
|
|
11
|
-
Project-URL: Bug Tracker, https://github.com/kaiyoux/kwess/issues
|
|
12
|
-
Keywords: Questrade,api,REST,wrapper
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
15
|
-
Classifier: Operating System :: OS Independent
|
|
16
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
17
|
-
Classifier: Intended Audience :: Developers
|
|
18
|
-
Requires-Python: >=3.6
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE.txt
|
|
21
|
-
Requires-Dist: requests>=2.28.1
|
|
22
|
-
|
|
23
1
|
# Introduction
|
|
24
2
|
|
|
25
3
|
Yet another Questrade API wrapper.
|
|
26
|
-
|
|
27
4
|
For details about the input parameters and output results, please visit
|
|
28
5
|
the [Questrade API documentation](https://www.questrade.com/api/home).
|
|
29
6
|
|
|
7
|
+
Also includes an executable module that determines the ACB
|
|
8
|
+
(Average/Adjusted Cost Base) of buy and sell activities from
|
|
9
|
+
saved logs, and prints the profit/loss amounts of sell transactions
|
|
10
|
+
up till, but not including, a given cut-off year.
|
|
11
|
+
|
|
30
12
|
### To install:
|
|
31
13
|
**python -m pip install kwess**
|
|
32
14
|
|
|
@@ -56,6 +38,7 @@ for acc in accs:
|
|
|
56
38
|
print(acc, "\n")
|
|
57
39
|
|
|
58
40
|
# Get (all types of) TFSA account orders from 17/8/2022 to now
|
|
41
|
+
# Questrade does not seem to keep old account orders - only the most recent
|
|
59
42
|
accs = qs.get_account_orders(startdatetime=dt(year=2022, month=8, day=17), \
|
|
60
43
|
verbose="vv")
|
|
61
44
|
for acc in accs:
|
|
@@ -98,7 +81,7 @@ print(accs)
|
|
|
98
81
|
sim = qs.search_symbols("vfv", verbose="88")
|
|
99
82
|
print(sim)
|
|
100
83
|
|
|
101
|
-
sim = qs.get_symbols_by_names("xdiv.to,xuu.to,
|
|
84
|
+
sim = qs.get_symbols_by_names("xdiv.to,xuu.to,qqc.to", verbose="**")
|
|
102
85
|
print(sim)
|
|
103
86
|
|
|
104
87
|
sim = qs.get_symbols_by_names("hom.un.to")
|
|
@@ -127,6 +110,50 @@ pprint(ops)
|
|
|
127
110
|
|
|
128
111
|
```
|
|
129
112
|
|
|
113
|
+
# Sample Script To Pull Your Questrade Logs
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
from kwess import Trader
|
|
117
|
+
from datetime import datetime as dt
|
|
118
|
+
import json
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_logs(qt, startdate, enddate=None, accounttype="TFSA"):
|
|
122
|
+
"""
|
|
123
|
+
Parameters:
|
|
124
|
+
- qt Trader object.
|
|
125
|
+
- startdate datetime object for beginning period.
|
|
126
|
+
- enddate datetime object for end period. Defaults to None, which will be treated as now.
|
|
127
|
+
- accounttype type of account for which activities will be obtained. Defaults to TFSA.
|
|
128
|
+
"""
|
|
129
|
+
if enddate == None:
|
|
130
|
+
enddate = dt.now()
|
|
131
|
+
for filename in ["activities", "orders", "executions"]:
|
|
132
|
+
filename = accounttype + "_account_" + filename + "_from_" + str(startdate.date()) + "_to_" + str(enddate.date()) + ".json"
|
|
133
|
+
with open(filename, mode="at", encoding="utf-8") as jfp:
|
|
134
|
+
if "activities" in filename:
|
|
135
|
+
accs = qt.get_account_activities(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose="nnn")
|
|
136
|
+
elif "orders" in filename:
|
|
137
|
+
accs = qt.get_account_orders(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose="nnn")
|
|
138
|
+
else:
|
|
139
|
+
accs = qt.get_account_executions(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose='nnn')
|
|
140
|
+
|
|
141
|
+
for acc in accs:
|
|
142
|
+
json.dump(acc, jfp, ensure_ascii=False, indent=2)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == "__main__":
|
|
148
|
+
#account_types = ["TFSA", "Cash", "Margin"]
|
|
149
|
+
#account_types = ["Cash"]
|
|
150
|
+
account_types = ["Margin"]
|
|
151
|
+
qt = Trader(verbose='n')
|
|
152
|
+
|
|
153
|
+
for acc_type in account_types:
|
|
154
|
+
get_logs(qt=qt, accounttype=acc_type, startdate=dt(year=2025, month=9, day=17))
|
|
155
|
+
|
|
156
|
+
```
|
|
130
157
|
|
|
131
158
|
# API Class And Methods
|
|
132
159
|
|
|
@@ -159,7 +186,7 @@ Parameters:
|
|
|
159
186
|
- server_type could be 2 possible values: "live" or "test".
|
|
160
187
|
"live" will allow you to interact with your real Questrade account.
|
|
161
188
|
"test" is for interacting with your test account.
|
|
162
|
-
- timeout number of seconds to wait for the
|
|
189
|
+
- timeout number of seconds to wait for the server to respond before
|
|
163
190
|
giving up.
|
|
164
191
|
Defaults to 15 seconds. Set timeout to None if you wish to wait forever
|
|
165
192
|
for a response.
|
|
@@ -173,7 +200,7 @@ build_datetime_string(self, adatetime=None, gmt=False)
|
|
|
173
200
|
Description:
|
|
174
201
|
Higher level helper method used to build a Questrade datetime string.
|
|
175
202
|
Parameters:
|
|
176
|
-
- adatetime a datetime object.
|
|
203
|
+
- adatetime a datetime object. Defaults to now.
|
|
177
204
|
- gmt optional boolean indicating if datetime is Greenwich Mean Time.
|
|
178
205
|
Default value is False.
|
|
179
206
|
Returns:
|
|
@@ -222,8 +249,8 @@ Returns:
|
|
|
222
249
|
Account balances as a Python object representation of the returned json.
|
|
223
250
|
|
|
224
251
|
|
|
225
|
-
get_account_executions(self,
|
|
226
|
-
|
|
252
|
+
get_account_executions(self, startdatetime, enddatetime=None,
|
|
253
|
+
accounttype='TFSA', verbose='')
|
|
227
254
|
Description:
|
|
228
255
|
Generator that provides account executions from the account related to
|
|
229
256
|
account type accounttype, between the range specified by startdatetime and
|
|
@@ -304,7 +331,7 @@ Yields:
|
|
|
304
331
|
json.
|
|
305
332
|
|
|
306
333
|
|
|
307
|
-
get_market_candles(self, sid, interval, startdatetime
|
|
334
|
+
get_market_candles(self, sid, interval, startdatetime, enddatetime=None,
|
|
308
335
|
verbose='')
|
|
309
336
|
Description:
|
|
310
337
|
Provides a list of json formatted market candles.
|
|
@@ -380,7 +407,7 @@ Returns:
|
|
|
380
407
|
|
|
381
408
|
get_new_refresh_token(self, token, server_type, verbose='')
|
|
382
409
|
Description:
|
|
383
|
-
Obtains a new refresh token (and new access token) from the
|
|
410
|
+
Obtains a new refresh token (and new access token) from the Authorization server.
|
|
384
411
|
You generally would not need to call this method, as it is potentially called by
|
|
385
412
|
Trader during initialization.
|
|
386
413
|
Trader will only call this method if the access token has expired.
|
|
@@ -500,5 +527,32 @@ Returns:
|
|
|
500
527
|
If gmt is True, the returned time will be considered as gmt time.
|
|
501
528
|
```
|
|
502
529
|
|
|
530
|
+
# Example of Executable Module
|
|
531
|
+
|
|
532
|
+
Open a command line in the directory containing your logs, and run the module:
|
|
533
|
+
```
|
|
534
|
+
kwess_acb
|
|
535
|
+
(or)
|
|
536
|
+
python -m kwess.kwess_acb
|
|
537
|
+
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### To run the scrpit:
|
|
541
|
+
**kwess_acb [-h] [-s SYMBOL [SYMBOL ...]] [-y CUT_OFF_YEAR] [-p PATH]**
|
|
542
|
+
```
|
|
543
|
+
Computes the ACB (Average/Adjusted Cost Base) from kwess activity logs, and
|
|
544
|
+
prints profit/loss of Sell transactions.
|
|
545
|
+
|
|
546
|
+
options:
|
|
547
|
+
-h, --help show this help message and exit.
|
|
548
|
+
-s SYMBOL [SYMBOL ...], --symbol SYMBOL [SYMBOL ...]
|
|
549
|
+
ticker symbol(s).
|
|
550
|
+
-y CUT_OFF_YEAR, --cut_off_year CUT_OFF_YEAR
|
|
551
|
+
cut off year. defaults to the current year.
|
|
552
|
+
-p PATH, --path PATH path of the directory containing the logs.
|
|
553
|
+
defaults to the current directory.
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
|
|
503
557
|
|
|
504
558
|
Let me know if you have any questions: <kaiyoux@gmail.com>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import requests
|
|
3
3
|
import sys
|
|
4
|
-
from pprint import pprint
|
|
4
|
+
from pprint import pprint as pp
|
|
5
5
|
from datetime import datetime as dt, timezone as tz, timedelta as td
|
|
6
6
|
import time
|
|
7
7
|
import json
|
|
@@ -35,7 +35,7 @@ class Trader:
|
|
|
35
35
|
- server_type could be 2 possible values: "live" or "test". "live" will allow you to
|
|
36
36
|
interact with your real Questrade account. "test" is for interacting with your test
|
|
37
37
|
account.
|
|
38
|
-
- timeout number of seconds to wait for the
|
|
38
|
+
- timeout number of seconds to wait for the server to respond before giving up.
|
|
39
39
|
Defaults to 15 seconds. Set timeout to None if you wish to wait forever for a response.
|
|
40
40
|
- verbose level of verbosity represented by the number of characters in a string.
|
|
41
41
|
Defaults to empty string. Maximum verbosity is 1 or "v".
|
|
@@ -88,7 +88,7 @@ class Trader:
|
|
|
88
88
|
def get_new_refresh_token(self, token, verbose=''):
|
|
89
89
|
"""
|
|
90
90
|
Description:
|
|
91
|
-
Obtains a new refresh token (and new access token) from the
|
|
91
|
+
Obtains a new refresh token (and new access token) from the Authorization server.
|
|
92
92
|
You generally would not need to call this method, as it is potentially called by Trader
|
|
93
93
|
during initialization.
|
|
94
94
|
Trader will only call this method if the access token has expired.
|
|
@@ -179,7 +179,7 @@ class Trader:
|
|
|
179
179
|
print(f"Accounts for user id {self.userid}:")
|
|
180
180
|
for account in self.accounts:
|
|
181
181
|
if verbose:
|
|
182
|
-
|
|
182
|
+
pp(account)
|
|
183
183
|
yield account
|
|
184
184
|
|
|
185
185
|
|
|
@@ -231,8 +231,8 @@ class Trader:
|
|
|
231
231
|
datetime objects.
|
|
232
232
|
Parameters:
|
|
233
233
|
- startdatetime datetime object specifying the start of a range.
|
|
234
|
-
- enddatetime optional datetime object specifying the end of a range.
|
|
235
|
-
now (datetime.datetime.now()) if not specified.
|
|
234
|
+
- enddatetime optional datetime object specifying the end of a range.
|
|
235
|
+
Defaults to now (datetime.datetime.now()) if not specified.
|
|
236
236
|
- accounttype type of Questrade account. Defaults to "tfsa".
|
|
237
237
|
- verbose level of verbosity represented by the number of characters in a string.
|
|
238
238
|
Defaults to empty string. Maximum verbosity is 3 or "vvv".
|
|
@@ -262,7 +262,7 @@ class Trader:
|
|
|
262
262
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for account activities.", f"{self.server_type} server returned {resp.status_code} on get_account_activities().", ex)
|
|
263
263
|
|
|
264
264
|
if verbosity > 1:
|
|
265
|
-
|
|
265
|
+
pp(resp.json())
|
|
266
266
|
return resp.json()
|
|
267
267
|
|
|
268
268
|
|
|
@@ -334,7 +334,7 @@ class Trader:
|
|
|
334
334
|
Description:
|
|
335
335
|
Higher level helper method used to build a Questrade datetime string.
|
|
336
336
|
Parameters:
|
|
337
|
-
- adatetime a datetime object.
|
|
337
|
+
- adatetime a datetime object. Defaults to now.
|
|
338
338
|
- gmt optional boolean indicating if datetime is Greenwich Mean Time.
|
|
339
339
|
Default value is False.
|
|
340
340
|
Returns:
|
|
@@ -413,7 +413,7 @@ class Trader:
|
|
|
413
413
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for account orders.", f"{self.server_type} server returned {resp.status_code} on get_account_orders().", ex)
|
|
414
414
|
|
|
415
415
|
if verbosity > 1:
|
|
416
|
-
|
|
416
|
+
pp(resp.json())
|
|
417
417
|
return resp.json()
|
|
418
418
|
|
|
419
419
|
|
|
@@ -449,12 +449,12 @@ class Trader:
|
|
|
449
449
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for account orders by ids.", f"{self.server_type} server returned {resp.status_code} on get_account_orders_by_ids().", ex)
|
|
450
450
|
|
|
451
451
|
if verbosity > 0:
|
|
452
|
-
|
|
452
|
+
pp(resp.json())
|
|
453
453
|
return resp.json()
|
|
454
454
|
|
|
455
455
|
|
|
456
456
|
@get_all
|
|
457
|
-
def get_account_executions(self,
|
|
457
|
+
def get_account_executions(self, startdatetime, enddatetime=None, accounttype="TFSA", verbose=''):
|
|
458
458
|
"""
|
|
459
459
|
Description:
|
|
460
460
|
Generator that provides account executions from the account related to account type
|
|
@@ -494,7 +494,7 @@ class Trader:
|
|
|
494
494
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for account executions.", f"{self.server_type} server returned {resp.status_code} on get_account_executions().", ex)
|
|
495
495
|
|
|
496
496
|
if verbosity > 1:
|
|
497
|
-
|
|
497
|
+
pp(resp.json())
|
|
498
498
|
return resp.json()
|
|
499
499
|
|
|
500
500
|
|
|
@@ -525,7 +525,7 @@ class Trader:
|
|
|
525
525
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for account balances.", f"{self.server_type} server returned {resp.status_code} on get_account_balances().", ex)
|
|
526
526
|
|
|
527
527
|
if verbosity > 0:
|
|
528
|
-
|
|
528
|
+
pp(resp.json())
|
|
529
529
|
return resp.json()
|
|
530
530
|
|
|
531
531
|
|
|
@@ -556,7 +556,7 @@ class Trader:
|
|
|
556
556
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for account positions.", f"{self.server_type} server returned {resp.status_code} on get_account_positions().", ex)
|
|
557
557
|
|
|
558
558
|
if verbosity > 0:
|
|
559
|
-
|
|
559
|
+
pp(resp.json())
|
|
560
560
|
return resp.json()
|
|
561
561
|
|
|
562
562
|
|
|
@@ -583,12 +583,12 @@ class Trader:
|
|
|
583
583
|
|
|
584
584
|
rd = resp.json()
|
|
585
585
|
if verbosity > 0:
|
|
586
|
-
|
|
586
|
+
pp(dt.strptime(rd["time"][:-13], '%Y-%m-%dT%X'))
|
|
587
587
|
return dt.strptime(rd["time"][:-13], '%Y-%m-%dT%X'), rd
|
|
588
588
|
|
|
589
589
|
|
|
590
590
|
@get_all
|
|
591
|
-
def get_market_candles(self, sid, interval, startdatetime
|
|
591
|
+
def get_market_candles(self, sid, interval, startdatetime, enddatetime=None, verbose=''):
|
|
592
592
|
"""
|
|
593
593
|
Description:
|
|
594
594
|
Provides a list of json formatted market candles.
|
|
@@ -623,7 +623,7 @@ class Trader:
|
|
|
623
623
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for market candles.", f"{self.server_type} server returned {resp.status_code} on get_market_candles().", ex)
|
|
624
624
|
|
|
625
625
|
if verbosity > 0:
|
|
626
|
-
|
|
626
|
+
pp(resp.json())
|
|
627
627
|
return resp.json()
|
|
628
628
|
|
|
629
629
|
|
|
@@ -652,7 +652,7 @@ class Trader:
|
|
|
652
652
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for market quote strategies.", f"{self.server_type} server returned {resp.status_code} on get_market_quotes_strategies().", ex)
|
|
653
653
|
|
|
654
654
|
if verbosity > 0:
|
|
655
|
-
|
|
655
|
+
pp(resp.json())
|
|
656
656
|
return resp.json()
|
|
657
657
|
|
|
658
658
|
|
|
@@ -684,7 +684,7 @@ class Trader:
|
|
|
684
684
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for market quote options.", f"{self.server_type} server returned {resp.status_code} on get_market_quotes_options().", ex)
|
|
685
685
|
|
|
686
686
|
if verbosity > 0:
|
|
687
|
-
|
|
687
|
+
pp(resp.json())
|
|
688
688
|
return resp.json()
|
|
689
689
|
|
|
690
690
|
|
|
@@ -729,7 +729,7 @@ class Trader:
|
|
|
729
729
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for market quotes.", f"{self.server_type} server returned {resp.status_code} on get_market_quotes().", ex)
|
|
730
730
|
|
|
731
731
|
if verbosity > 0:
|
|
732
|
-
|
|
732
|
+
pp(resp.json())
|
|
733
733
|
return resp.json()
|
|
734
734
|
|
|
735
735
|
|
|
@@ -756,7 +756,7 @@ class Trader:
|
|
|
756
756
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for markets.", f"{self.server_type} server returned {resp.status_code} on get_markets().", ex)
|
|
757
757
|
|
|
758
758
|
if verbosity > 0:
|
|
759
|
-
|
|
759
|
+
pp(resp.json())
|
|
760
760
|
return resp.json()
|
|
761
761
|
|
|
762
762
|
|
|
@@ -784,7 +784,7 @@ class Trader:
|
|
|
784
784
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for symbol options.", f"{self.server_type} server returned {resp.status_code} on get_symbol_options().", ex)
|
|
785
785
|
|
|
786
786
|
if verbosity > 0:
|
|
787
|
-
|
|
787
|
+
pp(resp.json())
|
|
788
788
|
return resp.json()
|
|
789
789
|
|
|
790
790
|
|
|
@@ -814,7 +814,7 @@ class Trader:
|
|
|
814
814
|
self._report_and_exit(resp.request.url, resp.text, "Failed to search server for symbols.", f"{self.server_type} server returned {resp.status_code} on search_symbols().", ex)
|
|
815
815
|
|
|
816
816
|
if verbosity > 0:
|
|
817
|
-
|
|
817
|
+
pp(resp.json())
|
|
818
818
|
return resp.json()
|
|
819
819
|
|
|
820
820
|
|
|
@@ -855,7 +855,7 @@ class Trader:
|
|
|
855
855
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for symbols by ids.", f"{self.server_type} server returned {resp.status_code} on get_symbols_by_ids().", ex)
|
|
856
856
|
|
|
857
857
|
if verbosity > 0:
|
|
858
|
-
|
|
858
|
+
pp(resp.json())
|
|
859
859
|
return resp.json()
|
|
860
860
|
|
|
861
861
|
|
|
@@ -889,6 +889,6 @@ class Trader:
|
|
|
889
889
|
self._report_and_exit(resp.request.url, resp.text, "Failed to query server for symbols by names.", f"{self.server_type} server returned {resp.status_code} on get_symbols_by_names().", ex)
|
|
890
890
|
|
|
891
891
|
if verbosity > 0:
|
|
892
|
-
|
|
892
|
+
pp(resp.json())
|
|
893
893
|
return resp.json()
|
|
894
894
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# kwess_acb.py
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from pprint import pprint as pp
|
|
6
|
+
from datetime import datetime as dt
|
|
7
|
+
import argparse
|
|
8
|
+
import math
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Activities:
|
|
13
|
+
def __init__(self, cut_off_year=0, path="."):
|
|
14
|
+
"""
|
|
15
|
+
Description:
|
|
16
|
+
Initializer of an Activities object.
|
|
17
|
+
Parameters:
|
|
18
|
+
- cut_off_year the ACB will be calculated up until (but not including) the cut_off_year.
|
|
19
|
+
- path directory path in which your Questrade investment activities logs are located.
|
|
20
|
+
Returns:
|
|
21
|
+
Activities object.
|
|
22
|
+
"""
|
|
23
|
+
self.data = []
|
|
24
|
+
self.all_symbols = set()
|
|
25
|
+
self.path = path
|
|
26
|
+
self.symbol = ""
|
|
27
|
+
if cut_off_year == 0:
|
|
28
|
+
self.cut_off_year = dt.today().year
|
|
29
|
+
else:
|
|
30
|
+
self.cut_off_year = cut_off_year
|
|
31
|
+
self.load_from_json_files()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_from_json_files(self):
|
|
35
|
+
"""
|
|
36
|
+
Description:
|
|
37
|
+
Loads activities logs. It is called during Activities object initialization.
|
|
38
|
+
"""
|
|
39
|
+
p = Path(self.path)
|
|
40
|
+
if not p.exists():
|
|
41
|
+
print(f"Invalid path: {self.path}")
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
data = []
|
|
44
|
+
for e in p.glob("*activities*.json"):
|
|
45
|
+
with e.open(mode="rt") as f:
|
|
46
|
+
t = f.read()
|
|
47
|
+
t = t.replace("}{", "},{")
|
|
48
|
+
s = "[" + t + "]"
|
|
49
|
+
l = json.loads(s)
|
|
50
|
+
for d in l:
|
|
51
|
+
for v in d.values():
|
|
52
|
+
for a in v:
|
|
53
|
+
#pp(a)
|
|
54
|
+
if (dt.fromisoformat(a["settlementDate"]).year < self.cut_off_year) and (a["action"] in ("Buy", "Sell")):
|
|
55
|
+
data.append(a)
|
|
56
|
+
self.all_symbols.add(a["symbol"])
|
|
57
|
+
self.data = sorted(data, key=lambda x: dt.fromisoformat(x["settlementDate"]))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def generate_adjusted_cost_base(self, symbol, include_fees=True):
|
|
61
|
+
"""
|
|
62
|
+
Description:
|
|
63
|
+
Calculates the ACB and prints profit/loss of Sell transactions for the given symbol.
|
|
64
|
+
Parameters:
|
|
65
|
+
- symbol the ticker symbol.
|
|
66
|
+
- include_fees whether or not to include the commission fees in the calculation. Defaults to True.
|
|
67
|
+
"""
|
|
68
|
+
prev_shares = 0
|
|
69
|
+
prev_cost_base = 0
|
|
70
|
+
prev_cost_base_per_share = 1
|
|
71
|
+
add_newline = False
|
|
72
|
+
for e in self.data:
|
|
73
|
+
if e["symbol"] == symbol.upper():
|
|
74
|
+
if e["action"] == "Buy":
|
|
75
|
+
shares = e["quantity"] + prev_shares
|
|
76
|
+
if include_fees:
|
|
77
|
+
cost_base = math.fabs(e["netAmount"]) + prev_cost_base # includes commission and fees
|
|
78
|
+
else:
|
|
79
|
+
cost_base = math.fabs(e["grossAmount"]) + prev_cost_base # does not include commission and fees
|
|
80
|
+
e["total_shares"] = shares
|
|
81
|
+
e["total_cost_base"] = cost_base
|
|
82
|
+
e["cost_base_per_share"] = cost_base / shares
|
|
83
|
+
prev_shares = shares
|
|
84
|
+
prev_cost_base = cost_base
|
|
85
|
+
prev_cost_base_per_share = e["cost_base_per_share"]
|
|
86
|
+
elif e["action"] == "Sell":
|
|
87
|
+
cost_base_per_share = prev_cost_base_per_share
|
|
88
|
+
if include_fees:
|
|
89
|
+
#print(f"e[grossAmount] {e["grossAmount"]}\ne[commission] {e["commission"]}\ne[quantity] {e["quantity"]}\nprev_cost_base {prev_cost_base}\n prev_shares {prev_shares}")
|
|
90
|
+
capital_gain_loss = e["grossAmount"] + e["commission"] + e["quantity"] * cost_base_per_share
|
|
91
|
+
else:
|
|
92
|
+
capital_gain_loss = e["grossAmount"] + e["quantity"] * cost_base_per_share
|
|
93
|
+
prev_cost_base += e["quantity"] * cost_base_per_share
|
|
94
|
+
prev_shares += e["quantity"]
|
|
95
|
+
e["capital_gain_loss"] = capital_gain_loss
|
|
96
|
+
e["total_shares"] = prev_shares
|
|
97
|
+
e["total_cost_base"] = prev_cost_base # for debugging
|
|
98
|
+
e["cost_base_per_share"] = cost_base_per_share # for debugging
|
|
99
|
+
if capital_gain_loss > 0:
|
|
100
|
+
print(f'{symbol} capital gain of {capital_gain_loss} on {e["settlementDate"]}')
|
|
101
|
+
add_newline = True
|
|
102
|
+
elif capital_gain_loss < 0:
|
|
103
|
+
print(f'{symbol} capital loss of {capital_gain_loss} on {e["settlementDate"]}')
|
|
104
|
+
add_newline = True
|
|
105
|
+
if add_newline:
|
|
106
|
+
print()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def generate_all_ACBs(self, include_fees=True):
|
|
110
|
+
"""
|
|
111
|
+
Description:
|
|
112
|
+
Calculates the ACB and prints profit/loss of Sell transactions for all the symbols present in the activities logs.
|
|
113
|
+
Parameters:
|
|
114
|
+
- include_fees whether or not to include the commission fees in the calculation. Defaults to True.
|
|
115
|
+
"""
|
|
116
|
+
for symbol in self.all_symbols:
|
|
117
|
+
self.generate_adjusted_cost_base(symbol, include_fees)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def print_by_symbol(self, symbol):
|
|
121
|
+
"""
|
|
122
|
+
Description:
|
|
123
|
+
Prints the activities logs relating to symbol.
|
|
124
|
+
Parameters:
|
|
125
|
+
- symbol the ticker symbol.
|
|
126
|
+
"""
|
|
127
|
+
for e in self.data:
|
|
128
|
+
if e["symbol"] == symbol.upper():
|
|
129
|
+
pp(e)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def __str__(self):
|
|
133
|
+
"""
|
|
134
|
+
Description:
|
|
135
|
+
Used for printing the activities logs.
|
|
136
|
+
Returns:
|
|
137
|
+
String representation of the activities logs.
|
|
138
|
+
"""
|
|
139
|
+
s = ""
|
|
140
|
+
for e in self.data:
|
|
141
|
+
s = "\n".join([s, str(e)])
|
|
142
|
+
return s
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def __repr__(self):
|
|
146
|
+
"""
|
|
147
|
+
Description:
|
|
148
|
+
Used for printing the activities object.
|
|
149
|
+
Returns:
|
|
150
|
+
String representation of the Activities object.
|
|
151
|
+
"""
|
|
152
|
+
return f"Activities(data: {self.data}\nsymbol: {self.symbol}\nall_symbols: {self.all_symbols}\npath: {self.path}\ncut_off_year: {self.cut_off_year})"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def main():
|
|
156
|
+
"""
|
|
157
|
+
Description:
|
|
158
|
+
Calculates the ACB and prints the profit/loss of Sell transactions for the symbol(s) present in the activities logs.
|
|
159
|
+
"""
|
|
160
|
+
parser = argparse.ArgumentParser(prog="kwess_acb.py", description="Computes the ACB (Average/Adjusted Cost Base) from kwess activity logs, and prints profit/loss of Sell transactions.")
|
|
161
|
+
parser.add_argument("-s", "--symbol", nargs="+", help="ticker symbol(s).")
|
|
162
|
+
parser.add_argument("-y", "--cut_off_year", default=0, help="cut off year. defaults to the current year.")
|
|
163
|
+
parser.add_argument("-p", "--path", default=".", help="path of the directory containing the logs. defaults to the current directory.")
|
|
164
|
+
args = parser.parse_args()
|
|
165
|
+
|
|
166
|
+
acts = Activities(cut_off_year=int(args.cut_off_year), path=args.path)
|
|
167
|
+
if args.symbol:
|
|
168
|
+
for symbol in args.symbol:
|
|
169
|
+
acts.generate_adjusted_cost_base(symbol)
|
|
170
|
+
else:
|
|
171
|
+
acts.generate_all_ACBs()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
main()
|
|
@@ -1,10 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kwess
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Questrade API wrapper.
|
|
5
|
+
Author-email: Issa Lompo <kaiyoux@gmail.com>
|
|
6
|
+
Maintainer-email: Issa Lompo <kaiyoux@gmail.com>
|
|
7
|
+
License-Expression: GPL-3.0-or-later
|
|
8
|
+
Project-URL: Homepage, https://github.com/pypa/kwess
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com
|
|
10
|
+
Keywords: questrade,api,REST,wrapper,ACB,profit,loss,average cost base,adjusted cost base,logs,kwess
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Requires-Python: >=3.6
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.txt
|
|
18
|
+
Requires-Dist: requests>=2.28.1
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
1
21
|
# Introduction
|
|
2
22
|
|
|
3
23
|
Yet another Questrade API wrapper.
|
|
4
|
-
|
|
5
24
|
For details about the input parameters and output results, please visit
|
|
6
25
|
the [Questrade API documentation](https://www.questrade.com/api/home).
|
|
7
26
|
|
|
27
|
+
Also includes an executable module that determines the ACB
|
|
28
|
+
(Average/Adjusted Cost Base) of buy and sell activities from
|
|
29
|
+
saved logs, and prints the profit/loss amounts of sell transactions
|
|
30
|
+
up till, but not including, a given cut-off year.
|
|
31
|
+
|
|
8
32
|
### To install:
|
|
9
33
|
**python -m pip install kwess**
|
|
10
34
|
|
|
@@ -34,6 +58,7 @@ for acc in accs:
|
|
|
34
58
|
print(acc, "\n")
|
|
35
59
|
|
|
36
60
|
# Get (all types of) TFSA account orders from 17/8/2022 to now
|
|
61
|
+
# Questrade does not seem to keep old account orders - only the most recent
|
|
37
62
|
accs = qs.get_account_orders(startdatetime=dt(year=2022, month=8, day=17), \
|
|
38
63
|
verbose="vv")
|
|
39
64
|
for acc in accs:
|
|
@@ -76,7 +101,7 @@ print(accs)
|
|
|
76
101
|
sim = qs.search_symbols("vfv", verbose="88")
|
|
77
102
|
print(sim)
|
|
78
103
|
|
|
79
|
-
sim = qs.get_symbols_by_names("xdiv.to,xuu.to,
|
|
104
|
+
sim = qs.get_symbols_by_names("xdiv.to,xuu.to,qqc.to", verbose="**")
|
|
80
105
|
print(sim)
|
|
81
106
|
|
|
82
107
|
sim = qs.get_symbols_by_names("hom.un.to")
|
|
@@ -105,6 +130,50 @@ pprint(ops)
|
|
|
105
130
|
|
|
106
131
|
```
|
|
107
132
|
|
|
133
|
+
# Sample Script To Pull Your Questrade Logs
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
from kwess import Trader
|
|
137
|
+
from datetime import datetime as dt
|
|
138
|
+
import json
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_logs(qt, startdate, enddate=None, accounttype="TFSA"):
|
|
142
|
+
"""
|
|
143
|
+
Parameters:
|
|
144
|
+
- qt Trader object.
|
|
145
|
+
- startdate datetime object for beginning period.
|
|
146
|
+
- enddate datetime object for end period. Defaults to None, which will be treated as now.
|
|
147
|
+
- accounttype type of account for which activities will be obtained. Defaults to TFSA.
|
|
148
|
+
"""
|
|
149
|
+
if enddate == None:
|
|
150
|
+
enddate = dt.now()
|
|
151
|
+
for filename in ["activities", "orders", "executions"]:
|
|
152
|
+
filename = accounttype + "_account_" + filename + "_from_" + str(startdate.date()) + "_to_" + str(enddate.date()) + ".json"
|
|
153
|
+
with open(filename, mode="at", encoding="utf-8") as jfp:
|
|
154
|
+
if "activities" in filename:
|
|
155
|
+
accs = qt.get_account_activities(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose="nnn")
|
|
156
|
+
elif "orders" in filename:
|
|
157
|
+
accs = qt.get_account_orders(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose="nnn")
|
|
158
|
+
else:
|
|
159
|
+
accs = qt.get_account_executions(accounttype=accounttype, startdatetime=startdate, enddatetime=enddate, verbose='nnn')
|
|
160
|
+
|
|
161
|
+
for acc in accs:
|
|
162
|
+
json.dump(acc, jfp, ensure_ascii=False, indent=2)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
#account_types = ["TFSA", "Cash", "Margin"]
|
|
169
|
+
#account_types = ["Cash"]
|
|
170
|
+
account_types = ["Margin"]
|
|
171
|
+
qt = Trader(verbose='n')
|
|
172
|
+
|
|
173
|
+
for acc_type in account_types:
|
|
174
|
+
get_logs(qt=qt, accounttype=acc_type, startdate=dt(year=2025, month=9, day=17))
|
|
175
|
+
|
|
176
|
+
```
|
|
108
177
|
|
|
109
178
|
# API Class And Methods
|
|
110
179
|
|
|
@@ -137,7 +206,7 @@ Parameters:
|
|
|
137
206
|
- server_type could be 2 possible values: "live" or "test".
|
|
138
207
|
"live" will allow you to interact with your real Questrade account.
|
|
139
208
|
"test" is for interacting with your test account.
|
|
140
|
-
- timeout number of seconds to wait for the
|
|
209
|
+
- timeout number of seconds to wait for the server to respond before
|
|
141
210
|
giving up.
|
|
142
211
|
Defaults to 15 seconds. Set timeout to None if you wish to wait forever
|
|
143
212
|
for a response.
|
|
@@ -151,7 +220,7 @@ build_datetime_string(self, adatetime=None, gmt=False)
|
|
|
151
220
|
Description:
|
|
152
221
|
Higher level helper method used to build a Questrade datetime string.
|
|
153
222
|
Parameters:
|
|
154
|
-
- adatetime a datetime object.
|
|
223
|
+
- adatetime a datetime object. Defaults to now.
|
|
155
224
|
- gmt optional boolean indicating if datetime is Greenwich Mean Time.
|
|
156
225
|
Default value is False.
|
|
157
226
|
Returns:
|
|
@@ -200,8 +269,8 @@ Returns:
|
|
|
200
269
|
Account balances as a Python object representation of the returned json.
|
|
201
270
|
|
|
202
271
|
|
|
203
|
-
get_account_executions(self,
|
|
204
|
-
|
|
272
|
+
get_account_executions(self, startdatetime, enddatetime=None,
|
|
273
|
+
accounttype='TFSA', verbose='')
|
|
205
274
|
Description:
|
|
206
275
|
Generator that provides account executions from the account related to
|
|
207
276
|
account type accounttype, between the range specified by startdatetime and
|
|
@@ -282,7 +351,7 @@ Yields:
|
|
|
282
351
|
json.
|
|
283
352
|
|
|
284
353
|
|
|
285
|
-
get_market_candles(self, sid, interval, startdatetime
|
|
354
|
+
get_market_candles(self, sid, interval, startdatetime, enddatetime=None,
|
|
286
355
|
verbose='')
|
|
287
356
|
Description:
|
|
288
357
|
Provides a list of json formatted market candles.
|
|
@@ -358,7 +427,7 @@ Returns:
|
|
|
358
427
|
|
|
359
428
|
get_new_refresh_token(self, token, server_type, verbose='')
|
|
360
429
|
Description:
|
|
361
|
-
Obtains a new refresh token (and new access token) from the
|
|
430
|
+
Obtains a new refresh token (and new access token) from the Authorization server.
|
|
362
431
|
You generally would not need to call this method, as it is potentially called by
|
|
363
432
|
Trader during initialization.
|
|
364
433
|
Trader will only call this method if the access token has expired.
|
|
@@ -478,5 +547,32 @@ Returns:
|
|
|
478
547
|
If gmt is True, the returned time will be considered as gmt time.
|
|
479
548
|
```
|
|
480
549
|
|
|
550
|
+
# Example of Executable Module
|
|
551
|
+
|
|
552
|
+
Open a command line in the directory containing your logs, and run the module:
|
|
553
|
+
```
|
|
554
|
+
kwess_acb
|
|
555
|
+
(or)
|
|
556
|
+
python -m kwess.kwess_acb
|
|
557
|
+
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### To run the scrpit:
|
|
561
|
+
**kwess_acb [-h] [-s SYMBOL [SYMBOL ...]] [-y CUT_OFF_YEAR] [-p PATH]**
|
|
562
|
+
```
|
|
563
|
+
Computes the ACB (Average/Adjusted Cost Base) from kwess activity logs, and
|
|
564
|
+
prints profit/loss of Sell transactions.
|
|
565
|
+
|
|
566
|
+
options:
|
|
567
|
+
-h, --help show this help message and exit.
|
|
568
|
+
-s SYMBOL [SYMBOL ...], --symbol SYMBOL [SYMBOL ...]
|
|
569
|
+
ticker symbol(s).
|
|
570
|
+
-y CUT_OFF_YEAR, --cut_off_year CUT_OFF_YEAR
|
|
571
|
+
cut off year. defaults to the current year.
|
|
572
|
+
-p PATH, --path PATH path of the directory containing the logs.
|
|
573
|
+
defaults to the current directory.
|
|
574
|
+
|
|
575
|
+
```
|
|
576
|
+
|
|
481
577
|
|
|
482
578
|
Let me know if you have any questions: <kaiyoux@gmail.com>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
LICENSE.txt
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
|
-
setup.cfg
|
|
5
4
|
kwess/__init__.py
|
|
5
|
+
kwess/__main__.py
|
|
6
|
+
kwess/kwess_acb.py
|
|
6
7
|
kwess.egg-info/PKG-INFO
|
|
7
8
|
kwess.egg-info/SOURCES.txt
|
|
8
9
|
kwess.egg-info/dependency_links.txt
|
|
10
|
+
kwess.egg-info/entry_points.txt
|
|
9
11
|
kwess.egg-info/requires.txt
|
|
10
12
|
kwess.egg-info/top_level.txt
|
|
11
13
|
kwess.egg-info/zip-safe
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kwess"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Questrade API wrapper."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.6"
|
|
11
|
+
keywords = ["questrade", "api", "REST", "wrapper", "ACB", "profit", "loss", "average cost base", "adjusted cost base", "logs", "kwess"]
|
|
12
|
+
license = "GPL-3.0-or-later"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
# Authors and Maintainers are now arrays of tables
|
|
21
|
+
authors = [
|
|
22
|
+
{name = "Issa Lompo", email = "kaiyoux@gmail.com"}
|
|
23
|
+
]
|
|
24
|
+
maintainers = [
|
|
25
|
+
{name = "Issa Lompo", email = "kaiyoux@gmail.com"}
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
dependencies = ["requests>=2.28.1"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/pypa/kwess"
|
|
32
|
+
"Bug Tracker" = "https://github.com"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
kwess_acb = "kwess.kwess_acb:main"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools]
|
|
38
|
+
include-package-data = true
|
|
39
|
+
zip-safe = true
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.packages.find]
|
|
42
|
+
where = ["."] # Standard if your code is in the root; use ["src"] if using src layout
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.package-data]
|
|
45
|
+
sample_configs = ["*.txt"]
|
|
46
|
+
"*" = ["*.txt", "*.md"]
|
kwess-0.1.0/setup.cfg
ADDED
kwess-0.0.6/pyproject.toml
DELETED
kwess-0.0.6/setup.cfg
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
[metadata]
|
|
2
|
-
name = kwess
|
|
3
|
-
version = 0.0.6
|
|
4
|
-
author = Issa Lompo
|
|
5
|
-
author_email = kaiyoux@gmail.com
|
|
6
|
-
maintainer = Issa Lompo
|
|
7
|
-
maintainer_email = kaiyoux@gmail.com
|
|
8
|
-
description = Questrade API wrapper.
|
|
9
|
-
long_description = file: README.md
|
|
10
|
-
long_description_content_type = text/markdown
|
|
11
|
-
url = https://github.com/kaiyoux/kwess
|
|
12
|
-
keywords = Questrade, api, REST, wrapper
|
|
13
|
-
license = GNU General Public License v3 (GPLv3)
|
|
14
|
-
license_files = LICENSE.txt
|
|
15
|
-
project_urls =
|
|
16
|
-
Bug Tracker = https://github.com/kaiyoux/kwess/issues
|
|
17
|
-
classifiers =
|
|
18
|
-
Programming Language :: Python :: 3
|
|
19
|
-
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
20
|
-
Operating System :: OS Independent
|
|
21
|
-
Development Status :: 5 - Production/Stable
|
|
22
|
-
Intended Audience :: Developers
|
|
23
|
-
|
|
24
|
-
[options]
|
|
25
|
-
packages = find:
|
|
26
|
-
python_requires = >=3.6
|
|
27
|
-
zip_safe = True
|
|
28
|
-
include_package_data = True
|
|
29
|
-
install_requires =
|
|
30
|
-
requests>=2.28.1
|
|
31
|
-
|
|
32
|
-
[options.package_data]
|
|
33
|
-
sample_configs = *.txt
|
|
34
|
-
* = *.txt, *.md
|
|
35
|
-
|
|
36
|
-
[egg_info]
|
|
37
|
-
tag_build =
|
|
38
|
-
tag_date = 0
|
|
39
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|