Big update to ef.py include centering of visual objects
Browse files
ef.py
CHANGED
@@ -5,7 +5,7 @@ from pypfopt.efficient_frontier import EfficientFrontier
|
|
5 |
import streamlit as st
|
6 |
import plotly.graph_objects as go
|
7 |
import plotly.express as px
|
8 |
-
|
9 |
|
10 |
### START AND RUN STREAMLIT
|
11 |
#https://docs.streamlit.io/library/get-started/installation
|
@@ -13,14 +13,7 @@ import plotly.express as px
|
|
13 |
def ef_viz(stock_df,choices):
|
14 |
#st.write("EF Visualization KOI EDITS")
|
15 |
# st.header('CAPM Model and the Efficient Frontier')
|
16 |
-
|
17 |
-
# CAPM model measure systematic risks, however it has unrealistic assumptions and relies heavily on a linear interpretation
|
18 |
-
# of the risks vs. returns relationship. It is better to use CAPM model in conjunction with the Efficient Frontier to better
|
19 |
-
# graphically depict volatility (a measure of investment risk) for the defined rate of return. <br>
|
20 |
-
# Each circle depicted above is a variation of the portfolio with the same input assest, only different weights.
|
21 |
-
# Portfolios with higher volatilities has a yellower shade of hue, while portfolios with a higher return has a bigger radius. <br>
|
22 |
-
# As you input different porfolio assets, take note of how diversification can improve a portfolio's risk versus reward profile.
|
23 |
-
# """
|
24 |
symbols, weights, investment, rf, A_coef = choices.values()
|
25 |
tickers = symbols
|
26 |
|
@@ -54,7 +47,7 @@ def ef_viz(stock_df,choices):
|
|
54 |
p_weights = [] # Define an empty array for asset weights
|
55 |
|
56 |
num_assets = len(tickers)
|
57 |
-
num_portfolios =
|
58 |
|
59 |
for portfolio in range(num_portfolios):
|
60 |
weights = np.random.random(num_assets)
|
@@ -75,14 +68,41 @@ def ef_viz(stock_df,choices):
|
|
75 |
data[symbol] = [w[counter] for w in p_weights]
|
76 |
|
77 |
port_ef_df = pd.DataFrame(data)
|
78 |
-
|
|
|
79 |
## NEEDS INPUT INSTEAD OF HARD CODE
|
80 |
#a = 5 #the coefficient of risk aversion is A. If an invest is less risk averse A is small. We assume 25 < A < 35.
|
81 |
#rf = 0.041
|
82 |
|
83 |
min_vol_port = port_ef_df.iloc[port_ef_df['Volatility'].idxmin()]
|
84 |
optimal_risky_port = port_ef_df.iloc[((port_ef_df['Returns']-rf)/port_ef_df['Volatility']).idxmax()]
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
## CREATE CAPM LINE #https://www.youtube.com/watch?v=JWx2wcrSGkk
|
88 |
cal_x = []
|
@@ -91,7 +111,7 @@ def ef_viz(stock_df,choices):
|
|
91 |
|
92 |
|
93 |
|
94 |
-
for er in np.linspace(rf, max(data['Returns']),20):
|
95 |
sd = (er - rf)/ ((optimal_risky_port[0] - rf)/ optimal_risky_port[1])
|
96 |
u = er - 0.5*A_coef*(sd**2)
|
97 |
cal_x.append(sd)
|
@@ -103,28 +123,71 @@ def ef_viz(stock_df,choices):
|
|
103 |
utl_df = pd.DataFrame(data2)
|
104 |
|
105 |
## Create Figure
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
-
|
123 |
-
|
124 |
-
#https://stackoverflow.com/questions/59057881/python-plotly-how-to-customize-hover-template-on-with-what-information-to-show
|
125 |
-
#https://stackoverflow.com/questions/65124833/plotly-how-to-combine-scatter-and-line-plots-using-plotly-express
|
126 |
|
127 |
-
|
|
|
128 |
|
129 |
-
|
130 |
-
|
|
|
|
5 |
import streamlit as st
|
6 |
import plotly.graph_objects as go
|
7 |
import plotly.express as px
|
8 |
+
from PIL import Image
|
9 |
|
10 |
### START AND RUN STREAMLIT
|
11 |
#https://docs.streamlit.io/library/get-started/installation
|
|
|
13 |
def ef_viz(stock_df,choices):
|
14 |
#st.write("EF Visualization KOI EDITS")
|
15 |
# st.header('CAPM Model and the Efficient Frontier')
|
16 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
symbols, weights, investment, rf, A_coef = choices.values()
|
18 |
tickers = symbols
|
19 |
|
|
|
47 |
p_weights = [] # Define an empty array for asset weights
|
48 |
|
49 |
num_assets = len(tickers)
|
50 |
+
num_portfolios = 1000
|
51 |
|
52 |
for portfolio in range(num_portfolios):
|
53 |
weights = np.random.random(num_assets)
|
|
|
68 |
data[symbol] = [w[counter] for w in p_weights]
|
69 |
|
70 |
port_ef_df = pd.DataFrame(data)
|
71 |
+
port_ef_df['Vol'] = port_ef_df['Volatility']
|
72 |
+
|
73 |
## NEEDS INPUT INSTEAD OF HARD CODE
|
74 |
#a = 5 #the coefficient of risk aversion is A. If an invest is less risk averse A is small. We assume 25 < A < 35.
|
75 |
#rf = 0.041
|
76 |
|
77 |
min_vol_port = port_ef_df.iloc[port_ef_df['Volatility'].idxmin()]
|
78 |
optimal_risky_port = port_ef_df.iloc[((port_ef_df['Returns']-rf)/port_ef_df['Volatility']).idxmax()]
|
79 |
+
|
80 |
+
### Make DF and data string for when hover over data points
|
81 |
+
def make_op_df(df, tickers):
|
82 |
+
new = {}
|
83 |
+
op_str = str()
|
84 |
+
new['Returns'] = df[0]
|
85 |
+
new['Volatility'] = df[1]
|
86 |
+
|
87 |
+
for i in range(0,len(tickers)):
|
88 |
+
new[tickers[i]]= df[i+2]
|
89 |
+
op_str += str(tickers[i]) + ': ' + str(round(df[i+2],4)) + '<br>'
|
90 |
+
|
91 |
+
return pd.DataFrame(new, index=[0]), op_str
|
92 |
+
|
93 |
+
op_df, op_str = make_op_df(optimal_risky_port, tickers)
|
94 |
+
|
95 |
+
def make_port_str(df, tickers):
|
96 |
+
port_str_lst = []
|
97 |
+
for i in range(0,len(df)):
|
98 |
+
temp = str()
|
99 |
+
for u in range(0,len(tickers)):
|
100 |
+
temp += str(tickers[u])+ ': ' + str(round(df[tickers[u]][i],4)) + '<br>'
|
101 |
+
port_str_lst.append(temp)
|
102 |
+
|
103 |
+
return port_str_lst
|
104 |
+
|
105 |
+
port_str_lst = make_port_str(port_ef_df, tickers)
|
106 |
|
107 |
## CREATE CAPM LINE #https://www.youtube.com/watch?v=JWx2wcrSGkk
|
108 |
cal_x = []
|
|
|
111 |
|
112 |
|
113 |
|
114 |
+
for er in np.linspace(rf, max(data['Returns'])+rf,20):
|
115 |
sd = (er - rf)/ ((optimal_risky_port[0] - rf)/ optimal_risky_port[1])
|
116 |
u = er - 0.5*A_coef*(sd**2)
|
117 |
cal_x.append(sd)
|
|
|
123 |
utl_df = pd.DataFrame(data2)
|
124 |
|
125 |
## Create Figure
|
126 |
+
fig3 = go.Figure()
|
127 |
+
|
128 |
+
#https://plotly.com/python/colorscales/
|
129 |
+
fig3.add_trace(go.Scatter(x=port_ef_df['Volatility'], y=port_ef_df['Returns'], hovertemplate='Volatility: %{x} <br>Returns: %{y} <br>%{text}',\
|
130 |
+
text= port_str_lst, mode='markers', \
|
131 |
+
marker=dict(color=port_ef_df['Volatility'], colorbar=dict(title="Volatility"), \
|
132 |
+
size=port_ef_df['Returns']*50, cmax=max(port_ef_df['Volatility']),\
|
133 |
+
cmin=min(port_ef_df['Volatility'])),name='Portfolio'))
|
134 |
+
#, mode='markers', size=port_ef_df['Returns'], \
|
135 |
+
#size_max=30, color=port_ef_df['Vol']))
|
136 |
+
fig3.add_trace(go.Scatter(x=utl_df['cal_x'], y=utl_df['cal_y'], mode='lines', line = dict(color='rgba(11,156,49,1)'),name='Ultility Function',\
|
137 |
+
hovertemplate='Volatility: %{x} <br>Returns: %{y}')) #))
|
138 |
+
|
139 |
+
fig3.add_trace(go.Scatter(x=op_df['Volatility'], y=op_df['Returns'], mode='markers', \
|
140 |
+
marker=dict(color= 'rgba(11,156,49,1)', size=30),\
|
141 |
+
hovertemplate='Volatility: %{x} <br>Returns: %{y} <br>%{text}',\
|
142 |
+
text=[op_str]))
|
143 |
+
### HOVER TEMPLATE # https://plotly.com/python/hover-text-and-formatting/
|
144 |
+
|
145 |
+
|
146 |
+
# ### SAVE IN CASE CANNOT FIGURE OUT THE HOVER TEMPLATE
|
147 |
+
# fig2 = px.scatter(op_df, 'Volatility', 'Returns')
|
148 |
+
# fig2.update_traces(marker=dict(color= 'rgba(11,156,49,1)', size=35))
|
149 |
+
|
150 |
+
# fig1 = px.line(utl_df, x="cal_x", y="cal_y")
|
151 |
+
# #fig1.update_traces(line=dict(color = 'rgba(11,156,49,1)'))
|
152 |
+
|
153 |
+
# fig = px.scatter(port_ef_df, 'Volatility', 'Returns', size='Returns', size_max=30, color='Vol')
|
154 |
+
# #https://stackoverflow.com/questions/59057881/python-plotly-how-to-customize-hover-template-on-with-what-information-to-show
|
155 |
+
# #https://stackoverflow.com/questions/65124833/plotly-how-to-combine-scatter-and-line-plots-using-plotly-express
|
156 |
|
157 |
+
# #data3 =
|
158 |
+
# fig3.data = [fig2.data,fig1.data,fig.data]
|
159 |
+
# #fig3.update_traces(line=dict(color = 'rgba(11,156,49,1)'))
|
160 |
+
# ####
|
161 |
+
|
162 |
+
fig3.update_layout(showlegend=False)#, legend_title_text = "Contestant")
|
163 |
+
fig3.update_xaxes(title_text="Volatility")
|
164 |
+
fig3.update_yaxes(title_text="Portfolio Return Rates")
|
165 |
+
|
166 |
+
st.plotly_chart(fig3, use_container_width=True)
|
167 |
+
|
168 |
+
#st.write(op_str)
|
169 |
+
op_df = op_df.style.set_properties(**{'color':'green'})
|
170 |
+
st.subheader('Optimal Returns vs Volatility and Portfolio weights')
|
171 |
+
col1, col2, col3 = st.columns([1,6,1])
|
172 |
+
with col1:
|
173 |
+
st.write("")
|
174 |
+
|
175 |
+
with col2:
|
176 |
+
st.write(op_df)
|
177 |
+
|
178 |
+
with col3:
|
179 |
+
st.write("")
|
180 |
+
|
181 |
+
im = Image.open('EFvsMinvar.png')
|
182 |
+
st.subheader('Understand the Efficient Frontier')
|
183 |
+
col1, col2, col3 = st.columns([1,6,1])
|
184 |
|
185 |
+
with col1:
|
186 |
+
st.write("")
|
|
|
|
|
187 |
|
188 |
+
with col2:
|
189 |
+
st.image(im, caption='Elements of the Efficient Frontier',use_column_width='auto')
|
190 |
|
191 |
+
with col3:
|
192 |
+
st.write("")
|
193 |
+
|