Spaces:
Sleeping
Sleeping
Carsten Stahl
commited on
Commit
•
b7cd1b1
1
Parent(s):
774135c
drastically reduced the complexity of main.py
Browse files
main.py
CHANGED
@@ -1,272 +1,18 @@
|
|
1 |
-
import yfinance as yf
|
2 |
-
import numpy as np
|
3 |
import pandas as pd
|
4 |
|
5 |
-
import streamlit as st
|
6 |
-
|
7 |
from utilities.py.styling import streamlit_style
|
8 |
-
from utilities.py import
|
9 |
-
from utilities.py import summary_tables
|
10 |
-
# from utilities.py import mongodb
|
11 |
-
|
12 |
-
from pypfopt import EfficientFrontier
|
13 |
-
from pypfopt import risk_models
|
14 |
-
from pypfopt import expected_returns
|
15 |
-
from pypfopt import HRPOpt, hierarchical_portfolio
|
16 |
|
17 |
-
import plotly.express as px
|
18 |
-
import plotly.graph_objects as go
|
19 |
|
20 |
streamlit_style()
|
21 |
# data import
|
22 |
company_list_df = pd.read_csv("utilities/data/Company List.csv")
|
23 |
|
|
|
24 |
|
25 |
-
|
26 |
-
company_name = company_list_df["Name"].to_list()
|
27 |
-
company_symbol = (company_list_df["Ticker"] + ".NS").to_list()
|
28 |
-
|
29 |
-
name_to_symbol_dict = dict()
|
30 |
-
symbol_to_name_dict = dict()
|
31 |
-
|
32 |
-
for CSymbol, CName in zip(company_symbol, company_name):
|
33 |
-
name_to_symbol_dict[CName] = CSymbol
|
34 |
-
|
35 |
-
for CSymbol, CName in zip(company_symbol, company_name):
|
36 |
-
symbol_to_name_dict[CSymbol] = CName
|
37 |
-
|
38 |
-
streamlit_company_list_input = st.multiselect(
|
39 |
-
"Select Multiple Companies", company_name, default=None
|
40 |
-
)
|
41 |
-
|
42 |
-
# method selection ---------------------------------------------------------------------
|
43 |
-
|
44 |
-
optimisation_method = st.selectbox(
|
45 |
-
"Choose an optimization method accordingly",
|
46 |
-
(
|
47 |
-
"Efficient Frontier",
|
48 |
-
"Hierarchical Risk Parity",
|
49 |
-
),
|
50 |
-
)
|
51 |
-
|
52 |
-
parameter_for_optimisation = 0
|
53 |
-
if optimisation_method == "Efficient Frontier":
|
54 |
-
parameter_for_optimisation = st.selectbox(
|
55 |
-
"Choose an optimization parameter accordingly",
|
56 |
-
(
|
57 |
-
"Maximum Sharpe Ratio",
|
58 |
-
"Efficient Risk",
|
59 |
-
"Minimum Volatility",
|
60 |
-
"Efficient Return",
|
61 |
-
),
|
62 |
-
)
|
63 |
-
|
64 |
-
# selection of starting date and amount to invest --------------------------------------
|
65 |
-
|
66 |
-
company_name_to_symbol = [name_to_symbol_dict[i] for i in streamlit_company_list_input]
|
67 |
-
|
68 |
-
number_of_symbols = len(company_name_to_symbol)
|
69 |
-
|
70 |
-
start_date = st.date_input(
|
71 |
-
"Start Date",
|
72 |
-
format="YYYY-MM-DD",
|
73 |
-
value=pd.Timestamp("1947-08-15"),
|
74 |
-
max_value=pd.Timestamp.now(),
|
75 |
-
)
|
76 |
-
|
77 |
-
initial_investment = st.number_input("How much would you want to invest?", value=45000)
|
78 |
-
|
79 |
-
# Optimization and summary of results
|
80 |
-
if number_of_symbols > 1:
|
81 |
-
company_data = pd.DataFrame()
|
82 |
-
|
83 |
-
# get the stock data for the companies
|
84 |
-
for cname in company_name_to_symbol:
|
85 |
-
stock_data_temp = yf.download(
|
86 |
-
cname, start=start_date, end=pd.Timestamp.now().strftime("%Y-%m-%d")
|
87 |
-
)["Adj Close"]
|
88 |
-
stock_data_temp.name = cname
|
89 |
-
company_data = pd.merge(
|
90 |
-
company_data,
|
91 |
-
stock_data_temp,
|
92 |
-
how="outer",
|
93 |
-
right_index=True,
|
94 |
-
left_index=True,
|
95 |
-
)
|
96 |
-
|
97 |
-
# cleaning the data
|
98 |
-
company_data.dropna(axis=1, how="all", inplace=True)
|
99 |
-
|
100 |
-
company_data.dropna(inplace=True)
|
101 |
-
|
102 |
-
for i in company_data.columns:
|
103 |
-
company_data[i] = company_data[i].abs()
|
104 |
-
|
105 |
-
st.write(
|
106 |
-
f"Note: Due to unavailability of full data, this Analysis uses data from the date: {company_data.index[0]}"
|
107 |
-
)
|
108 |
-
|
109 |
-
number_of_symbols = len(company_data.columns)
|
110 |
-
|
111 |
-
# showing the stock data in the UI
|
112 |
-
st.dataframe(company_data, use_container_width=True)
|
113 |
-
|
114 |
-
# only continue with sim, if more than one company was fetched
|
115 |
-
if number_of_symbols > 1:
|
116 |
-
company_stock_returns_data = company_data.pct_change().dropna()
|
117 |
-
|
118 |
-
# Config for the simulation
|
119 |
-
mu = 0
|
120 |
-
S = 0
|
121 |
-
ef = 0
|
122 |
-
company_asset_weights = 0
|
123 |
-
|
124 |
-
# Do the portfolio optimization
|
125 |
-
if optimisation_method == "Efficient Frontier":
|
126 |
-
mu = expected_returns.mean_historical_return(company_data)
|
127 |
-
S = risk_models.sample_cov(company_data)
|
128 |
-
|
129 |
-
ef = EfficientFrontier(mu, S)
|
130 |
-
|
131 |
-
if parameter_for_optimisation == "Maximum Sharpe Raio":
|
132 |
-
ef.max_sharpe()
|
133 |
-
elif parameter_for_optimisation == "Minimum Volatility":
|
134 |
-
ef.min_volatility()
|
135 |
-
elif parameter_for_optimisation == "Efficient Risk":
|
136 |
-
ef.efficient_risk(0.5)
|
137 |
-
else:
|
138 |
-
ef.efficient_return(0.05)
|
139 |
-
|
140 |
-
company_asset_weights = pd.DataFrame.from_dict(
|
141 |
-
ef.clean_weights(), orient="index"
|
142 |
-
).reset_index()
|
143 |
-
elif optimisation_method == "Hierarchical Risk Parity":
|
144 |
-
mu = expected_returns.returns_from_prices(company_data)
|
145 |
-
S = risk_models.sample_cov(company_data)
|
146 |
-
|
147 |
-
ef = HRPOpt(mu, S)
|
148 |
-
|
149 |
-
company_asset_weights = ef.optimize()
|
150 |
-
company_asset_weights = pd.DataFrame.from_dict(
|
151 |
-
company_asset_weights, orient="index", columns=["Weight"]
|
152 |
-
).reset_index()
|
153 |
-
|
154 |
-
# cleaning the returned data from the optimization and outputing results
|
155 |
-
company_asset_weights.columns = ["Ticker", "Allocation"]
|
156 |
-
|
157 |
-
company_asset_weights["Name"] = [
|
158 |
-
symbol_to_name_dict[i] for i in company_asset_weights["Ticker"]
|
159 |
-
]
|
160 |
-
|
161 |
-
company_asset_weights = company_asset_weights[["Name", "Ticker", "Allocation"]]
|
162 |
-
|
163 |
-
st.dataframe(company_asset_weights, use_container_width=True)
|
164 |
-
|
165 |
-
|
166 |
-
# get portfolio performance and refactor the data
|
167 |
-
(
|
168 |
-
expected_annual_return,
|
169 |
-
annual_volatility,
|
170 |
-
sharpe_ratio,
|
171 |
-
) = ef.portfolio_performance()
|
172 |
-
|
173 |
-
st_portfolio_performance = pd.DataFrame.from_dict(
|
174 |
-
{
|
175 |
-
"Expected annual return": (expected_annual_return * 100).round(2),
|
176 |
-
"Annual volatility": (annual_volatility * 100).round(2),
|
177 |
-
"Sharpe ratio": sharpe_ratio.round(2),
|
178 |
-
},
|
179 |
-
orient="index",
|
180 |
-
).reset_index()
|
181 |
-
|
182 |
-
st_portfolio_performance.columns = ["Metrics", "Summary"]
|
183 |
-
|
184 |
-
# output the method used above the results
|
185 |
-
if optimisation_method == "Efficient Frontier":
|
186 |
-
st.write(
|
187 |
-
"Optimization Method - ",
|
188 |
-
optimisation_method,
|
189 |
-
"---- Parameter - ",
|
190 |
-
parameter_for_optimisation,
|
191 |
-
)
|
192 |
-
else:
|
193 |
-
st.write("Optimization Method - ", optimisation_method)
|
194 |
-
|
195 |
-
st.dataframe(st_portfolio_performance, use_container_width=True)
|
196 |
-
|
197 |
-
# plot the pie chart with the asset weights
|
198 |
-
plots.pie_chart_company_asset_weights(company_asset_weights)
|
199 |
-
|
200 |
-
# summarizing the asset returns with optimized portfolio
|
201 |
-
portfolio_returns = (
|
202 |
-
company_stock_returns_data * list(ef.clean_weights().values())
|
203 |
-
).sum(axis=1)
|
204 |
-
|
205 |
-
annual_portfolio_returns = portfolio_returns.resample("Y").apply(
|
206 |
-
lambda x: (x + 1).prod() - 1
|
207 |
-
)
|
208 |
-
|
209 |
-
cumulative_returns = (portfolio_returns + 1).cumprod() * initial_investment
|
210 |
-
|
211 |
-
# Output the results in the tab
|
212 |
-
|
213 |
-
tab1, tab2, tab3 = st.tabs(["Plots", "Annual Returns", "Montly Returns"])
|
214 |
-
|
215 |
-
with tab1:
|
216 |
-
plots.plot_annual_returns(annual_portfolio_returns)
|
217 |
-
plots.plot_cummulative_returns(cumulative_returns)
|
218 |
-
|
219 |
-
with tab2:
|
220 |
-
annual_portfolio_returns = summary_tables.annual_returns_dataframe(
|
221 |
-
annual_portfolio_returns
|
222 |
-
)
|
223 |
-
annual_cumulative_returns = (
|
224 |
-
summary_tables.annual_cumulative_returns_dataframe(cumulative_returns)
|
225 |
-
)
|
226 |
-
annual_stock_returns = summary_tables.company_wise_annual_return(
|
227 |
-
company_stock_returns_data, company_asset_weights
|
228 |
-
)
|
229 |
-
|
230 |
-
merged_annual_returns_data = pd.merge(
|
231 |
-
annual_portfolio_returns,
|
232 |
-
annual_cumulative_returns,
|
233 |
-
on="Year",
|
234 |
-
suffixes=("_portfolio", "_cumulative"),
|
235 |
-
)
|
236 |
-
|
237 |
-
merged_annual_returns_data = pd.merge(
|
238 |
-
merged_annual_returns_data, annual_stock_returns, on="Year"
|
239 |
-
)
|
240 |
-
|
241 |
-
st.write("Annual Returns")
|
242 |
-
st.dataframe(merged_annual_returns_data, use_container_width=True)
|
243 |
-
|
244 |
-
with tab3:
|
245 |
-
monthly_portfolio_return = summary_tables.monthly_returns_dataframe(
|
246 |
-
portfolio_returns
|
247 |
-
)
|
248 |
-
monthly_stock_return = summary_tables.company_wise_monthly_return(
|
249 |
-
company_stock_returns_data, company_asset_weights
|
250 |
-
)
|
251 |
-
monthly_cumulative_returns = (
|
252 |
-
summary_tables.monthly_cumulative_returns_dataframe(cumulative_returns)
|
253 |
-
)
|
254 |
-
|
255 |
-
merged_monthly_returns_data = pd.merge(
|
256 |
-
monthly_portfolio_return,
|
257 |
-
monthly_cumulative_returns,
|
258 |
-
on=["Year", "Month"],
|
259 |
-
how="inner",
|
260 |
-
)
|
261 |
-
|
262 |
-
merged_monthly_returns_data = pd.merge(
|
263 |
-
merged_monthly_returns_data,
|
264 |
-
monthly_stock_return,
|
265 |
-
on=["Year", "Month"],
|
266 |
-
how="inner",
|
267 |
-
)
|
268 |
|
269 |
-
|
270 |
-
st.dataframe(merged_monthly_returns_data, use_container_width=True)
|
271 |
|
272 |
-
|
|
|
|
|
|
|
|
1 |
import pandas as pd
|
2 |
|
|
|
|
|
3 |
from utilities.py.styling import streamlit_style
|
4 |
+
from utilities.py.composer import Composer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
|
|
|
|
6 |
|
7 |
streamlit_style()
|
8 |
# data import
|
9 |
company_list_df = pd.read_csv("utilities/data/Company List.csv")
|
10 |
|
11 |
+
composer = Composer(company_list_df)
|
12 |
|
13 |
+
composer.render_user_input()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
+
ready_to_render_results = len(composer.user_input.get_selected_comp_ids()) > 1
|
|
|
16 |
|
17 |
+
if ready_to_render_results:
|
18 |
+
composer.render_results()
|