financegy 2.0__py3-none-any.whl → 3.0__py3-none-any.whl

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.
financegy/__init__.py CHANGED
@@ -23,6 +23,13 @@ from financegy.modules.securities import (
23
23
  get_historical_trades,
24
24
  )
25
25
 
26
+ from financegy.modules.portfolio import (
27
+ calculate_position_value,
28
+ calculate_position_return,
29
+ calculate_position_return_percent,
30
+ calculate_portfolio_summary,
31
+ )
32
+
26
33
  __all__ = [
27
34
  "get_securities",
28
35
  "get_security_by_symbol",
@@ -41,6 +48,10 @@ __all__ = [
41
48
  "search_securities",
42
49
  "get_trades_for_year",
43
50
  "get_historical_trades",
51
+ "calculate_position_value",
52
+ "calculate_position_return",
53
+ "calculate_position_return_percent",
54
+ "calculate_portfolio_summary",
44
55
  "clear_cache",
45
56
  "save_to_csv",
46
57
  "to_dataframe",
financegy/config.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "2.0"
1
+ __version__ = "3.0"
2
2
 
3
3
  BASE_URL = "https://guyanastockexchangeinc.com"
4
4
  HEADERS = {
@@ -1,6 +1,3 @@
1
- # Function to convert GSE price string to float type
2
-
3
-
4
1
  def to_float(value):
5
2
  if value is None:
6
3
  return None
@@ -0,0 +1,157 @@
1
+ from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
2
+ from financegy.modules import securities
3
+
4
+
5
+ def _to_decimal(value, field_name="value"):
6
+ """
7
+ Convert strings/numbers to Decimal safely.
8
+ Handles commas like "3,000.0".
9
+ """
10
+ try:
11
+ clean_value = str(value).replace(",", "")
12
+ return Decimal(clean_value)
13
+ except (InvalidOperation, TypeError) as e:
14
+ raise ValueError(f"Invalid {field_name}: {value}") from e
15
+
16
+
17
+ def calculate_position_value(symbol: str, shares):
18
+ try:
19
+ shares = _to_decimal(shares, "shares")
20
+ if shares <= 0:
21
+ raise ValueError("Shares must be greater than zero")
22
+
23
+ last_trade = securities.get_recent_trade(symbol)
24
+ last_trade_price = _to_decimal(
25
+ last_trade.get("last_trade_price"), "last_trade_price"
26
+ )
27
+
28
+ position_value = last_trade_price * shares
29
+
30
+ return {
31
+ "last_trade": last_trade,
32
+ "position_value": str(position_value),
33
+ }
34
+
35
+ except (ValueError, KeyError, TypeError) as e:
36
+ raise ValueError(f"[calculate_position_value] Invalid input or data: {e}")
37
+
38
+
39
+ def calculate_position_return(symbol: str, shares, purchase_price):
40
+ try:
41
+ shares = _to_decimal(shares, "shares")
42
+ purchase_price = _to_decimal(purchase_price, "purchase_price")
43
+
44
+ if shares <= 0:
45
+ raise ValueError("Shares must be greater than zero")
46
+ if purchase_price <= 0:
47
+ raise ValueError("Purchase price must be greater than zero")
48
+
49
+ last_trade = securities.get_recent_trade(symbol)
50
+ last_trade_price = _to_decimal(
51
+ last_trade.get("last_trade_price"), "last_trade_price"
52
+ )
53
+
54
+ position_return = (last_trade_price - purchase_price) * shares
55
+
56
+ return {
57
+ "last_trade": last_trade,
58
+ "position_return": str(position_return),
59
+ }
60
+
61
+ except (ValueError, KeyError, TypeError) as e:
62
+ raise ValueError(f"[calculate_position_return] Invalid input or data: {e}")
63
+
64
+
65
+ def calculate_position_return_percent(symbol, shares, purchase_price):
66
+ try:
67
+ shares = _to_decimal(shares, "shares")
68
+ purchase_price = _to_decimal(purchase_price, "purchase_price")
69
+
70
+ if shares <= 0:
71
+ raise ValueError("Shares must be greater than zero")
72
+ if purchase_price <= 0:
73
+ raise ValueError("Purchase price must be greater than zero")
74
+
75
+ last_trade = securities.get_recent_trade(symbol)
76
+ last_trade_price = _to_decimal(
77
+ last_trade.get("last_trade_price"), "last_trade_price"
78
+ )
79
+
80
+ return_percent = (
81
+ (last_trade_price - purchase_price) / purchase_price
82
+ ) * Decimal("100")
83
+
84
+ return {
85
+ "last_trade": last_trade,
86
+ "position_return_percent": str(round(return_percent, 2)),
87
+ }
88
+
89
+ except (ValueError, KeyError, TypeError) as e:
90
+ raise ValueError(
91
+ f"[calculate_position_return_percent] Invalid input or data: {e}"
92
+ )
93
+
94
+
95
+ def calculate_portfolio_summary(positions: list[dict]):
96
+ try:
97
+ total_invested = Decimal("0")
98
+ total_value = Decimal("0")
99
+ detailed_positions = []
100
+
101
+ for pos in positions:
102
+ symbol = pos["symbol"]
103
+ shares = _to_decimal(pos.get("shares"), f"shares for {symbol}")
104
+ purchase_price = _to_decimal(
105
+ pos.get("purchase_price"), f"purchase_price for {symbol}"
106
+ )
107
+
108
+ if shares <= 0:
109
+ raise ValueError(f"Invalid shares for {symbol}")
110
+ if purchase_price <= 0:
111
+ raise ValueError(f"Invalid purchase price for {symbol}")
112
+
113
+ last_trade = securities.get_recent_trade(symbol)
114
+ last_trade_price = _to_decimal(
115
+ last_trade.get("last_trade_price"), f"last_trade_price for {symbol}"
116
+ )
117
+
118
+ invested = shares * purchase_price
119
+ current_value = shares * last_trade_price
120
+ gain_loss = current_value - invested
121
+
122
+ total_invested += invested
123
+ total_value += current_value
124
+
125
+ detailed_positions.append(
126
+ {
127
+ "symbol": symbol,
128
+ "shares": str(shares),
129
+ "purchase_price": str(purchase_price),
130
+ "last_trade_price": str(last_trade_price),
131
+ "invested": str(invested),
132
+ "current_value": str(current_value),
133
+ "gain_loss": str(gain_loss),
134
+ }
135
+ )
136
+
137
+ total_gain_loss = total_value - total_invested
138
+
139
+ if total_invested != 0:
140
+ return_percent = (
141
+ total_gain_loss / total_invested * Decimal("100")
142
+ ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
143
+ else:
144
+ return_percent = Decimal("0.00")
145
+
146
+ return {
147
+ "summary": {
148
+ "total_invested": str(total_invested),
149
+ "current_value": str(total_value),
150
+ "total_gain_loss": str(total_gain_loss),
151
+ "return_percent": str(return_percent),
152
+ },
153
+ "positions": detailed_positions,
154
+ }
155
+
156
+ except (ValueError, KeyError, TypeError) as e:
157
+ raise ValueError(f"[calculate_portfolio_summary] Invalid portfolio data: {e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: financegy
3
- Version: 2.0
3
+ Version: 3.0
4
4
  Summary: Unofficial Python library for accessing GSE (Guyana Stock Exchange) financial data
5
5
  Author-email: Ezra Minty <ezranminty@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,15 @@
1
+ financegy/__init__.py,sha256=SY0WDJGydD-9fU9i3Yff2WI8mrreGfFy22XSlSEPlJI,1664
2
+ financegy/config.py,sha256=lAuUzlFaa7qQ4CN2LVHRQ93LQ7jbhaC95Voz6PpGh_k,193
3
+ financegy/cache/cache_manager.py,sha256=c-VW3tk2A-Z-hMrTN4d_OOB6Zse9r-hrAcNhpBY6Sjo,1840
4
+ financegy/core/parser.py,sha256=tU6bFDVTW6ztKeDWQBL7RuxJGTIjeGuMk3SUF_lTWKY,18181
5
+ financegy/core/request_handler.py,sha256=g0C0R-nvIvicAhxnCEAtTU8buVtzUn8725EERvBA6ZU,276
6
+ financegy/helpers/safe_text.py,sha256=pskFh-ejjVAR8f_NBL5x3EwuBtd9OocAKN6ozqNDPhs,141
7
+ financegy/helpers/to_float.py,sha256=VVE3fkHzIE2un6zD50au_U2b_Luh13p3-T84fxEE6Xo,191
8
+ financegy/modules/portfolio.py,sha256=HFWw1FenJ_pABQhF7OlpP6OXuLCiyQBmwyXLrj-g6WQ,5483
9
+ financegy/modules/securities.py,sha256=_1f0-aZI7xWH5ndk5KSRVkqage3OpMbxaIod4W1xBy8,14687
10
+ financegy/utils/utils.py,sha256=mqbjZ4em_a7rZyDgCjFswspEIL_WUPOXa8siAuxZuoY,1434
11
+ financegy-3.0.dist-info/licenses/LICENSE,sha256=HGLhx0fI215whUzIvTFdFivB447d_IdIIIRncUCQaEs,1088
12
+ financegy-3.0.dist-info/METADATA,sha256=XwV5KpXYnrTusYQKWzHnFwgD4KFNWWggmFwBD1kaKzM,9674
13
+ financegy-3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
14
+ financegy-3.0.dist-info/top_level.txt,sha256=TpdYDxtK61m5xnvvzbqnDVJ82gphEqxnXN_Ur8rjvxQ,10
15
+ financegy-3.0.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- financegy/__init__.py,sha256=d9oZzZVzAazdSqhHX3IdHV9bOChYMreOhmXaG64z8hE,1334
2
- financegy/config.py,sha256=0kiLrY4FI07_wvpQcBlgzeMNLmDwaBa2dHV9rKk9rVM,193
3
- financegy/cache/cache_manager.py,sha256=c-VW3tk2A-Z-hMrTN4d_OOB6Zse9r-hrAcNhpBY6Sjo,1840
4
- financegy/core/parser.py,sha256=tU6bFDVTW6ztKeDWQBL7RuxJGTIjeGuMk3SUF_lTWKY,18181
5
- financegy/core/request_handler.py,sha256=g0C0R-nvIvicAhxnCEAtTU8buVtzUn8725EERvBA6ZU,276
6
- financegy/helpers/safe_text.py,sha256=pskFh-ejjVAR8f_NBL5x3EwuBtd9OocAKN6ozqNDPhs,141
7
- financegy/helpers/to_float.py,sha256=PV0VCFE6KxWXa_5KBQv8eRDJuJ8UrmMtgzlSWDZSkL8,249
8
- financegy/modules/securities.py,sha256=_1f0-aZI7xWH5ndk5KSRVkqage3OpMbxaIod4W1xBy8,14687
9
- financegy/utils/utils.py,sha256=mqbjZ4em_a7rZyDgCjFswspEIL_WUPOXa8siAuxZuoY,1434
10
- financegy-2.0.dist-info/licenses/LICENSE,sha256=HGLhx0fI215whUzIvTFdFivB447d_IdIIIRncUCQaEs,1088
11
- financegy-2.0.dist-info/METADATA,sha256=x6PofU0FCiuMlzfUSdL58B7h11n-2_KVGt2uhvzsb4o,9674
12
- financegy-2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
13
- financegy-2.0.dist-info/top_level.txt,sha256=TpdYDxtK61m5xnvvzbqnDVJ82gphEqxnXN_Ur8rjvxQ,10
14
- financegy-2.0.dist-info/RECORD,,