financegy 2.0__py3-none-any.whl → 3.1__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.1.dist-info}/METADATA +67 -29
- financegy-3.1.dist-info/RECORD +15 -0
- financegy-2.0.dist-info/RECORD +0 -14
- {financegy-2.0.dist-info → financegy-3.1.dist-info}/WHEEL +0 -0
- {financegy-2.0.dist-info → financegy-3.1.dist-info}/licenses/LICENSE +0 -0
- {financegy-2.0.dist-info → financegy-3.1.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}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: financegy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1
|
|
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
|
|
@@ -70,7 +70,7 @@ search_results = financegy.search_securities("DDL")
|
|
|
70
70
|
# Get all trades for a specific year
|
|
71
71
|
year_trades = financegy.get_trades_for_year("DDL", "2019")
|
|
72
72
|
|
|
73
|
-
# Get historical trades within a date range
|
|
73
|
+
# Get historical trades within a date range - supports: yyyy / mm/yyyy / dd/mm/yyyy
|
|
74
74
|
historical_trades = financegy.get_historical_trades(
|
|
75
75
|
symbol="DDL",
|
|
76
76
|
start_date="01/06/2020",
|
|
@@ -96,6 +96,35 @@ volatility = financegy.get_sessions_volatility("DDL", 30)
|
|
|
96
96
|
# Year-to-date high and low traded prices
|
|
97
97
|
ytd_high_low = financegy.get_ytd_high_low("DDL")
|
|
98
98
|
|
|
99
|
+
# --------------------------
|
|
100
|
+
# Portfolio / Position Calculations
|
|
101
|
+
# --------------------------
|
|
102
|
+
|
|
103
|
+
# Calculate the current market value of a position
|
|
104
|
+
position_value = financegy.calculate_position_value("DDL", shares=50)
|
|
105
|
+
|
|
106
|
+
# Calculate unrealized gain or loss for a position
|
|
107
|
+
position_return = financegy.calculate_position_return(
|
|
108
|
+
symbol="DDL",
|
|
109
|
+
shares=50,
|
|
110
|
+
purchase_price=250
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Calculate percentage return for a position
|
|
114
|
+
position_return_percent = financegy.calculate_position_return_percent(
|
|
115
|
+
symbol="DDL",
|
|
116
|
+
shares=50,
|
|
117
|
+
purchase_price=250
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Portfolio-level summary
|
|
121
|
+
portfolio = [
|
|
122
|
+
{"symbol": "DTC", "shares": 100, "purchase_price": 300},
|
|
123
|
+
{"symbol": "DDL", "shares": 50, "purchase_price": 250},
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
portfolio_summary = financegy.calculate_portfolio_summary(portfolio)
|
|
127
|
+
|
|
99
128
|
# --------------------------
|
|
100
129
|
# Utilities
|
|
101
130
|
# --------------------------
|
|
@@ -117,39 +146,48 @@ financegy.clear_cache(silent=True)
|
|
|
117
146
|
|
|
118
147
|
### Core Data Retrieval
|
|
119
148
|
|
|
120
|
-
| Function | Description
|
|
121
|
-
| ----------------------------------------------------- |
|
|
122
|
-
| `get_securities()` | Returns all currently traded securities on the GSE.
|
|
123
|
-
| `get_security_by_symbol(symbol)` | Returns the full security name for a ticker symbol
|
|
124
|
-
| `get_recent_trade(symbol)` | Returns the most recent trade information for the given security.
|
|
125
|
-
| `get_security_recent_year(symbol)` | Returns all trade data for the most recent year available for the selected security.
|
|
126
|
-
| `get_session_trades(session)` | Returns trade data for
|
|
127
|
-
| `get_security_session_trade(symbol, session)` | Returns trade data for a
|
|
128
|
-
| `search_securities(query)` | Searches securities whose names or ticker symbols match the given query
|
|
129
|
-
| `get_trades_for_year(symbol, year)` | Returns all trade records for a specific security during a given year.
|
|
130
|
-
| `get_historical_trades(symbol, start_date, end_date)` | Returns historical trades within the specified date range
|
|
149
|
+
| Function | Description |
|
|
150
|
+
| ----------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
151
|
+
| `get_securities()` | Returns all currently traded securities on the GSE. |
|
|
152
|
+
| `get_security_by_symbol(symbol)` | Returns the full security name for a ticker symbol. |
|
|
153
|
+
| `get_recent_trade(symbol)` | Returns the most recent trade information for the given security. |
|
|
154
|
+
| `get_security_recent_year(symbol)` | Returns all trade data for the most recent year available for the selected security. |
|
|
155
|
+
| `get_session_trades(session)` | Returns trade data for all securities during a specific trading session. |
|
|
156
|
+
| `get_security_session_trade(symbol, session)` | Returns trade data for a specific security during a specific session. |
|
|
157
|
+
| `search_securities(query)` | Searches securities whose names or ticker symbols match the given query. |
|
|
158
|
+
| `get_trades_for_year(symbol, year)` | Returns all trade records for a specific security during a given year. |
|
|
159
|
+
| `get_historical_trades(symbol, start_date, end_date)` | Returns historical trades within the specified date range. |
|
|
131
160
|
|
|
132
161
|
### Analytics / Calculation Functions
|
|
133
162
|
|
|
134
|
-
| Function | Description
|
|
135
|
-
| ---------------------------------------------------------------- |
|
|
136
|
-
| `get_previous_close(symbol)` | Returns the most recent closing/last trade price
|
|
137
|
-
| `get_price_change(symbol)` | Returns absolute price difference
|
|
138
|
-
| `get_price_change_percent(symbol)` | Returns percent price change
|
|
139
|
-
| `get_latest_session_for_symbol(symbol)` | Returns the latest trade dict for the symbol
|
|
140
|
-
| `get_sessions_average_price(symbol, session_start, session_end)` | Returns the average last traded price over a session range
|
|
141
|
-
| `get_average_price(symbol, session_number)` | Returns the average last traded price over the last
|
|
142
|
-
| `get_sessions_volatility(symbol, session_number)` | Returns volatility over the last
|
|
143
|
-
| `get_ytd_high_low(symbol)` | Returns year-to-date highest and lowest traded prices
|
|
163
|
+
| Function | Description |
|
|
164
|
+
| ---------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
165
|
+
| `get_previous_close(symbol)` | Returns the most recent closing/last trade price. |
|
|
166
|
+
| `get_price_change(symbol)` | Returns absolute price difference vs previous session close. |
|
|
167
|
+
| `get_price_change_percent(symbol)` | Returns percent price change vs previous session close. |
|
|
168
|
+
| `get_latest_session_for_symbol(symbol)` | Returns the latest trade dict for the symbol. |
|
|
169
|
+
| `get_sessions_average_price(symbol, session_start, session_end)` | Returns the average last traded price over a session range. |
|
|
170
|
+
| `get_average_price(symbol, session_number)` | Returns the average last traded price over the last N sessions. |
|
|
171
|
+
| `get_sessions_volatility(symbol, session_number)` | Returns volatility over the last N sessions. |
|
|
172
|
+
| `get_ytd_high_low(symbol)` | Returns year-to-date highest and lowest traded prices. |
|
|
173
|
+
|
|
174
|
+
### Portfolio / Position Functions
|
|
175
|
+
|
|
176
|
+
| Function | Description |
|
|
177
|
+
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
178
|
+
| `calculate_position_value(symbol, shares)` | Calculates the current market value of a position using the latest trade price. |
|
|
179
|
+
| `calculate_position_return(symbol, shares, purchase_price)` | Calculates the unrealized gain or loss for a position. |
|
|
180
|
+
| `calculate_position_return_percent(symbol, shares, purchase_price)` | Calculates the percentage return for a position. |
|
|
181
|
+
| `calculate_portfolio_summary(positions)` | Computes a full portfolio summary including totals and per-position breakdown. |
|
|
144
182
|
|
|
145
183
|
### Utilities
|
|
146
184
|
|
|
147
|
-
| Function | Description
|
|
148
|
-
| ---------------------------------------------------------------------- |
|
|
149
|
-
| `to_dataframe(data)` | Converts FinanceGY list/dict results into a
|
|
150
|
-
| `save_to_csv(data, filename="output.csv", path=None, silent=False)` | Saves data to a CSV file.
|
|
151
|
-
| `save_to_excel(data, filename="output.xlsx", path=None, silent=False)` | Saves data to an Excel file.
|
|
152
|
-
| `clear_cache(silent=False)` | Completely clears the FinanceGY cache directory.
|
|
185
|
+
| Function | Description |
|
|
186
|
+
| ---------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
187
|
+
| `to_dataframe(data)` | Converts FinanceGY list/dict results into a pandas DataFrame. |
|
|
188
|
+
| `save_to_csv(data, filename="output.csv", path=None, silent=False)` | Saves data to a CSV file. |
|
|
189
|
+
| `save_to_excel(data, filename="output.xlsx", path=None, silent=False)` | Saves data to an Excel file. |
|
|
190
|
+
| `clear_cache(silent=False)` | Completely clears the FinanceGY cache directory. |
|
|
153
191
|
|
|
154
192
|
---
|
|
155
193
|
|
|
@@ -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.1.dist-info/licenses/LICENSE,sha256=HGLhx0fI215whUzIvTFdFivB447d_IdIIIRncUCQaEs,1088
|
|
12
|
+
financegy-3.1.dist-info/METADATA,sha256=-qczV0iIjIUiI5Y4ouoRYG--hc6c58w-ELSxEFHWQFM,10931
|
|
13
|
+
financegy-3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
14
|
+
financegy-3.1.dist-info/top_level.txt,sha256=TpdYDxtK61m5xnvvzbqnDVJ82gphEqxnXN_Ur8rjvxQ,10
|
|
15
|
+
financegy-3.1.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
|