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 +11 -0
- financegy/config.py +1 -1
- financegy/helpers/to_float.py +0 -3
- financegy/modules/portfolio.py +157 -0
- {financegy-2.0.dist-info → financegy-3.0.dist-info}/METADATA +1 -1
- financegy-3.0.dist-info/RECORD +15 -0
- financegy-2.0.dist-info/RECORD +0 -14
- {financegy-2.0.dist-info → financegy-3.0.dist-info}/WHEEL +0 -0
- {financegy-2.0.dist-info → financegy-3.0.dist-info}/licenses/LICENSE +0 -0
- {financegy-2.0.dist-info → financegy-3.0.dist-info}/top_level.txt +0 -0
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
financegy/helpers/to_float.py
CHANGED
|
@@ -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}")
|
|
@@ -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,,
|
financegy-2.0.dist-info/RECORD
DELETED
|
@@ -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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|