|
import pandas as pd |
|
import numpy as np |
|
from datetime import datetime as dt |
|
from pypfopt.efficient_frontier import EfficientFrontier |
|
import streamlit as st |
|
import plotly.graph_objects as go |
|
import plotly.express as px |
|
from PIL import Image |
|
|
|
|
|
|
|
|
|
def ef_viz(stock_df,choices): |
|
|
|
|
|
|
|
symbols, weights, investment, rf, A_coef = choices.values() |
|
tickers = symbols |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stock_dff = stock_df.copy() |
|
stock_dff['Date'] = pd.to_datetime(stock_dff['Date']) |
|
|
|
|
|
|
|
|
|
ind_er_df = stock_dff.resample('Y', on = 'Date').last().pct_change().mean() |
|
ind_er = ind_er_df[tickers] |
|
|
|
ann_sd = stock_df[tickers].pct_change().apply(lambda x: np.log(1+x)).std().apply(lambda x: x*np.sqrt(250)) |
|
assets = pd.concat([ind_er, ann_sd], axis=1) |
|
assets.columns = ['Returns', 'Volatility'] |
|
assets |
|
|
|
ln_pct_change = stock_df[tickers].pct_change().apply(lambda x: np.log(1+x))[1:] |
|
|
|
cov_matrix =ln_pct_change.cov() |
|
|
|
|
|
p_ret = [] |
|
p_vol = [] |
|
p_weights = [] |
|
|
|
num_assets = len(tickers) |
|
num_portfolios = 1000 |
|
|
|
for portfolio in range(num_portfolios): |
|
weights = np.random.random(num_assets) |
|
weights = weights/np.sum(weights) |
|
p_weights.append(weights) |
|
returns = np.dot(weights, ind_er) |
|
|
|
p_ret.append(returns) |
|
var = cov_matrix.mul(weights, axis=0).mul(weights, axis=1).sum().sum() |
|
sd = np.sqrt(var) |
|
ann_sd = sd*np.sqrt(250) |
|
p_vol.append(ann_sd) |
|
|
|
data = {'Returns':p_ret, 'Volatility':p_vol} |
|
|
|
for counter, symbol in enumerate(stock_df[tickers].columns.tolist()): |
|
|
|
data[symbol] = [w[counter] for w in p_weights] |
|
|
|
port_ef_df = pd.DataFrame(data) |
|
port_ef_df['Vol'] = port_ef_df['Volatility'] |
|
|
|
|
|
|
|
|
|
|
|
min_vol_port = port_ef_df.iloc[port_ef_df['Volatility'].idxmin()] |
|
optimal_risky_port = port_ef_df.iloc[((port_ef_df['Returns']-rf)/port_ef_df['Volatility']).idxmax()] |
|
|
|
|
|
def make_op_df(df, tickers): |
|
new = {} |
|
op_str = str() |
|
new['Returns'] = df[0] |
|
new['Volatility'] = df[1] |
|
|
|
for i in range(0,len(tickers)): |
|
new[tickers[i]]= df[i+2] |
|
op_str += str(tickers[i]) + ': ' + str(round(df[i+2],4)) + '<br>' |
|
|
|
return pd.DataFrame(new, index=[0]), op_str |
|
|
|
op_df, op_str = make_op_df(optimal_risky_port, tickers) |
|
|
|
def make_port_str(df, tickers): |
|
port_str_lst = [] |
|
for i in range(0,len(df)): |
|
temp = str() |
|
for u in range(0,len(tickers)): |
|
temp += str(tickers[u])+ ': ' + str(round(df[tickers[u]][i],4)) + '<br>' |
|
port_str_lst.append(temp) |
|
|
|
return port_str_lst |
|
|
|
port_str_lst = make_port_str(port_ef_df, tickers) |
|
|
|
|
|
cal_x = [] |
|
cal_y = [] |
|
utl = [] |
|
|
|
|
|
|
|
for er in np.linspace(rf, max(data['Returns'])+rf,20): |
|
sd = (er - rf)/ ((optimal_risky_port[0] - rf)/ optimal_risky_port[1]) |
|
u = er - 0.5*A_coef*(sd**2) |
|
cal_x.append(sd) |
|
cal_y.append(er) |
|
utl.append(u) |
|
|
|
data2 = {'Utility':utl, 'cal_x':cal_x, 'cal_y':cal_y} |
|
|
|
utl_df = pd.DataFrame(data2) |
|
|
|
|
|
fig3 = go.Figure() |
|
|
|
|
|
fig3.add_trace(go.Scatter(x=port_ef_df['Volatility'], y=port_ef_df['Returns'], hovertemplate='Volatility: %{x} <br>Returns: %{y} <br>%{text}',\ |
|
text= port_str_lst, mode='markers', \ |
|
marker=dict(color=port_ef_df['Volatility'], colorbar=dict(title="Volatility"), \ |
|
size=port_ef_df['Returns']*50, cmax=max(port_ef_df['Volatility']),\ |
|
cmin=min(port_ef_df['Volatility'])),name='Portfolio')) |
|
|
|
|
|
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',\ |
|
hovertemplate='Volatility: %{x} <br>Returns: %{y}')) |
|
|
|
fig3.add_trace(go.Scatter(x=op_df['Volatility'], y=op_df['Returns'], mode='markers', \ |
|
marker=dict(color= 'rgba(11,156,49,1)', size=30),\ |
|
hovertemplate='Volatility: %{x} <br>Returns: %{y} <br>%{text}',\ |
|
text=[op_str])) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fig3.update_layout(showlegend=False) |
|
fig3.update_xaxes(title_text="Volatility") |
|
fig3.update_yaxes(title_text="Portfolio Return Rates") |
|
|
|
st.plotly_chart(fig3, use_container_width=True) |
|
|
|
|
|
op_df = op_df.style.set_properties(**{'color':'green'}) |
|
st.subheader('Optimal Returns vs Volatility and Portfolio weights') |
|
st.write(op_df) |
|
im = Image.open('EFvsMinvar.png') |
|
st.subheader('Understand the Efficient Frontier') |
|
st.image(im, caption='Elements of the Efficient Frontier',use_column_width='auto') |