Robert Castagna commited on
Commit
2633645
·
1 Parent(s): 04c03e6

add portfolio builder

Browse files
pages/2_Portfolio_Builder.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from openbb import obb
3
+ import riskfolio as rp
4
+ import os
5
+ from dotenv import load_dotenv
6
+ import matplotlib.pyplot as plt
7
+ import pandas as pd
8
+ import numpy as np
9
+ import plotly.graph_objs as go
10
+ import plotly.tools as tls
11
+ from plotly.subplots import make_subplots
12
+ import plotly.figure_factory as ff
13
+ import streamlit as st
14
+
15
+ load_dotenv()
16
+
17
+ from openbb import obb
18
+ obb.account.login(pat=os.environ['open_bb_pat'])
19
+
20
+
21
+ # take stock inputs
22
+ tickers = [
23
+ "XLE", "XLF", "XLU", "XLI", "GDX",
24
+ "XLK", "XLV", "XLY", "XLP", "XLB",
25
+ "XOP", "IYR", "XHB", "ITB", "VNQ",
26
+ "GDXJ", "IYE", "OIH", "XME", "XRT",
27
+ "SMH", "IBB", "KBE", "KRE", "XTL",
28
+ ]
29
+
30
+ start_date = '2023-01-01'
31
+ end_date = '2024-01-01'
32
+
33
+ data = (
34
+ obb
35
+ .equity
36
+ .price
37
+ .historical(tickers, start_date=start_date, end_date=end_date, provider="yfinance")
38
+ .to_df()
39
+ .pivot(columns="symbol", values="close")
40
+ )
41
+
42
+ returns = data.pct_change().dropna()
43
+
44
+ # -------------------------- (Efficient Frontier Calculation) -------------------------------- #
45
+ st.title('Efficient Frontier Portfolio')
46
+ st.write("The efficient frontier is a set of optimal portfolios that offer the highest expected return for a defined level of risk or the lowest risk for a given level of expected return. Portfolios that lie below the efficient frontier are sub-optimal because they do not provide enough return for the level of risk. Portfolios that cluster to the right of the efficient frontier are also sub-optimal because they have a higher level of risk for the defined rate of return.")
47
+
48
+ port = rp.Portfolio(returns=returns)
49
+
50
+ # Step 2: Set portfolio optimization model
51
+ port.assets_stats(model='hist') # Using historical data for estimation
52
+
53
+ # Step 3: Configure the optimization model and calculate the efficient frontier
54
+ ef = port.efficient_frontier(model='Classic', rm='MV', points=50, rf=0.0406, hist=True)
55
+ w1 = port.optimization(model='Classic', rm='MV', obj='Sharpe', rf=0.0, l=0, hist=True)
56
+
57
+ mu = port.mu # Expected returns
58
+ cov = port.cov # Covariance matrix
59
+
60
+
61
+
62
+ # ---------------------------- (Portfolio Statistics) -------------------------------- #
63
+
64
+ st.write('**Portfolio Statistics Optimized for Max Sharpe Ratio:**')
65
+ spy_prices = obb.equity.price.historical(symbol = "spy", provider="yfinance", start_date=start_date, end_date=end_date).to_df()
66
+
67
+ # Calculate daily returns
68
+ # Ensure you're using the adjusted close prices for accurate return calculation
69
+ benchmark_returns = spy_prices['close'].pct_change().dropna()
70
+
71
+ port.rf = 0.0406 # Risk-free rate
72
+ portfolio_return = np.dot(w1, mu)
73
+
74
+ # market return
75
+ spy_daily_return = benchmark_returns
76
+ spy_expected_return = spy_daily_return.mean()
77
+
78
+ # portfolio's beta
79
+ covariance = returns.apply(lambda x: x.cov(spy_daily_return))
80
+ spy_variance = spy_daily_return.var()
81
+ beta_values = covariance / spy_variance
82
+ portfolio_beta = np.dot(w1['weights'], beta_values)
83
+ st.write('Portfolio Beta: ', np.round(portfolio_beta,3))
84
+
85
+ # jensens alpha
86
+ expected_return = port.rf + portfolio_beta * (spy_daily_return - port.rf)
87
+ st.write('Jensen\'s Alpha: ', np.round(expected_return.iloc[-1],3))
88
+
89
+ # treynor ratio
90
+ treynor_ratio = (expected_return - port.rf) / portfolio_beta
91
+ st.write('Treynor Ratio: ', np.round(treynor_ratio.iloc[-1],3))
92
+
93
+ # Portfolio volatility
94
+ portfolio_stddev = np.sqrt(np.dot(pd.Series(w1['weights']).T, np.dot(covariance, w1['weights'])))
95
+
96
+ # Sharpe Ratio, adjusted for the risk-free rate
97
+ sharpe_ratio = (expected_return.iloc[-1] - port.rf) / np.mean(portfolio_stddev[portfolio_stddev != 0])
98
+ st.write('Sharpe Ratio: ', np.round(sharpe_ratio, 3))
99
+
100
+ # -------------------------- (Plotting) -------------------------------- #
101
+ # Step 4: Plot the efficient frontier
102
+ fig_ef, ax_ef = plt.subplots()
103
+ ax_ef = rp.plot_frontier(mu=mu, cov=cov, returns=port.returns, w=w1, rm='MV', w_frontier=ef, marker='*', label='Optimal Portfolio - Max. Sharpe' ,s=16)
104
+ st.pyplot(fig_ef)
105
+
106
+ st.write('**Asset Mix of Optimized Portfolio:**')
107
+ st.dataframe(w1.T, use_container_width=True)
108
+
109
+ # corr matrix
110
+ fig, ax = plt.subplots()
111
+ corr = returns.corr()
112
+
113
+ # Create a heatmap
114
+ heatmap = go.Heatmap(z=corr.values, x=corr.columns, y=corr.index, colorscale='RdYlBu')
115
+ layout = go.Layout(title='Correlation Matrix', autosize=True, width=1200, height=1200)
116
+ fig = go.Figure(data=[heatmap], layout=layout)
117
+
118
+ st.plotly_chart(fig)
119
+
120
+
121
+
122
+ # -------------------------- (HRP Portfolio) -------------------------------- #
123
+ st.title('Hierarchical Risk Parity Portfolio')
124
+ st.write("""
125
+ HRP is unlike traditional portfolio optimization methods. It can create an optimized portfolio when the covariance matrix is ill-degenerated or singular. This is impossible for quadratic optimizers.
126
+ Research has shown HRP to deliver lower out-of-sample variance than traditional optimization methods.
127
+ """)
128
+
129
+ fig1, ax1 = plt.subplots()
130
+ ax1 = rp.plot_clusters(returns=returns,
131
+ codependence='pearson',
132
+ linkage='single',
133
+ k=None,
134
+ max_k=10,
135
+ leaf_order=True,
136
+ dendrogram=True,
137
+ ax=None)
138
+
139
+ st.pyplot(fig1)
140
+
141
+ port = rp.HCPortfolio(returns=returns)
142
+ w = port.optimization(
143
+ model="HRP",
144
+ codependence="pearson",
145
+ rm="MV", # minimum variance
146
+ rf=0.05,
147
+ linkage="single",
148
+ max_k=10,
149
+ leaf_order=True,
150
+ )
151
+
152
+ fig2, ax2 = plt.subplots()
153
+ ax2 = rp.plot_pie(
154
+ w=w,
155
+ title="HRP Naive Risk Parity",
156
+ others=0.05,
157
+ nrow=25,
158
+ cmap="tab20",
159
+ height=8,
160
+ width=10,
161
+ ax=None,
162
+ )
163
+ st.pyplot(fig2)
164
+
165
+ fig3, ax3 = plt.subplots()
166
+ ax3 = rp.plot_risk_con(
167
+ w=w,
168
+ cov=returns.cov(),
169
+ returns=returns,
170
+ rm="MV",
171
+ rf=0,
172
+ alpha=0.05,
173
+ color="tab:blue",
174
+ height=6,
175
+ width=10,
176
+ t_factor=252,
177
+ ax=None,
178
+ )
179
+ st.pyplot(fig3)
pages/{2_Sentiment_Data_Input.py → 3_Sentiment_Data_Input.py} RENAMED
File without changes
requirements.txt CHANGED
@@ -20,4 +20,6 @@ streamlit==1.22.0
20
  regex
21
  yfinance==0.2.28
22
  torch
23
- python-dotenv
 
 
 
20
  regex
21
  yfinance==0.2.28
22
  torch
23
+ python-dotenv
24
+ openbb
25
+ riskfolio-lib