kwess 0.0.7__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.
@@ -1,17 +1,14 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: kwess
3
- Version: 0.0.7
3
+ Version: 0.1.0
4
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
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
 
@@ -99,7 +101,7 @@ print(accs)
99
101
  sim = qs.search_symbols("vfv", verbose="88")
100
102
  print(sim)
101
103
 
102
- sim = qs.get_symbols_by_names("xdiv.to,xuu.to,cve.to", verbose="**")
104
+ sim = qs.get_symbols_by_names("xdiv.to,xuu.to,qqc.to", verbose="**")
103
105
  print(sim)
104
106
 
105
107
  sim = qs.get_symbols_by_names("hom.un.to")
@@ -128,6 +130,50 @@ pprint(ops)
128
130
 
129
131
  ```
130
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
+ ```
131
177
 
132
178
  # API Class And Methods
133
179
 
@@ -501,5 +547,32 @@ Returns:
501
547
  If gmt is True, the returned time will be considered as gmt time.
502
548
  ```
503
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
+
504
577
 
505
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.7
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
 
@@ -99,7 +81,7 @@ print(accs)
99
81
  sim = qs.search_symbols("vfv", verbose="88")
100
82
  print(sim)
101
83
 
102
- sim = qs.get_symbols_by_names("xdiv.to,xuu.to,cve.to", verbose="**")
84
+ sim = qs.get_symbols_by_names("xdiv.to,xuu.to,qqc.to", verbose="**")
103
85
  print(sim)
104
86
 
105
87
  sim = qs.get_symbols_by_names("hom.un.to")
@@ -128,6 +110,50 @@ pprint(ops)
128
110
 
129
111
  ```
130
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
+ ```
131
157
 
132
158
  # API Class And Methods
133
159
 
@@ -501,5 +527,32 @@ Returns:
501
527
  If gmt is True, the returned time will be considered as gmt time.
502
528
  ```
503
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
+
504
557
 
505
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
@@ -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
- pprint(account)
182
+ pp(account)
183
183
  yield account
184
184
 
185
185
 
@@ -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
- pprint(resp.json())
265
+ pp(resp.json())
266
266
  return resp.json()
267
267
 
268
268
 
@@ -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
- pprint(resp.json())
416
+ pp(resp.json())
417
417
  return resp.json()
418
418
 
419
419
 
@@ -449,7 +449,7 @@ 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
- pprint(resp.json())
452
+ pp(resp.json())
453
453
  return resp.json()
454
454
 
455
455
 
@@ -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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
559
+ pp(resp.json())
560
560
  return resp.json()
561
561
 
562
562
 
@@ -583,7 +583,7 @@ class Trader:
583
583
 
584
584
  rd = resp.json()
585
585
  if verbosity > 0:
586
- pprint(dt.strptime(rd["time"][:-13], '%Y-%m-%dT%X'))
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
 
@@ -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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
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
- pprint(resp.json())
892
+ pp(resp.json())
893
893
  return resp.json()
894
894
 
@@ -0,0 +1,7 @@
1
+
2
+ from kwess.kwess_acb import main
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
7
+
@@ -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
 
@@ -77,7 +101,7 @@ print(accs)
77
101
  sim = qs.search_symbols("vfv", verbose="88")
78
102
  print(sim)
79
103
 
80
- sim = qs.get_symbols_by_names("xdiv.to,xuu.to,cve.to", verbose="**")
104
+ sim = qs.get_symbols_by_names("xdiv.to,xuu.to,qqc.to", verbose="**")
81
105
  print(sim)
82
106
 
83
107
  sim = qs.get_symbols_by_names("hom.un.to")
@@ -106,6 +130,50 @@ pprint(ops)
106
130
 
107
131
  ```
108
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
+ ```
109
177
 
110
178
  # API Class And Methods
111
179
 
@@ -479,5 +547,32 @@ Returns:
479
547
  If gmt is True, the returned time will be considered as gmt time.
480
548
  ```
481
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
+
482
577
 
483
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,2 @@
1
+ [console_scripts]
2
+ kwess_acb = kwess.kwess_acb:main
@@ -1 +1,2 @@
1
+ dist
1
2
  kwess
@@ -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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=42"]
3
- build-backend = "setuptools.build_meta"
kwess-0.0.7/setup.cfg DELETED
@@ -1,39 +0,0 @@
1
- [metadata]
2
- name = kwess
3
- version = 0.0.7
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