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
### START AND RUN STREAMLIT
#https://docs.streamlit.io/library/get-started/installation
def ef_viz(stock_df,choices):
#st.write("EF Visualization KOI EDITS")
# st.header('CAPM Model and the Efficient Frontier')
symbols, weights, benchmark, investing_style, rf, A_coef = choices.values()
tickers = symbols
#tickers.append('sp500')
#st.write(tickers)
#st.write(stock_df)
# Yearly returns for individual companies
#https://stackoverflow.com/questions/69284773/unable-to-resample-the-pandas-with-date-column-typeerror-only-valid-with-dateti
stock_dff = stock_df.copy()
stock_dff['Date'] = pd.to_datetime(stock_dff['Date'])
# ind_er_df = stock_dff.set_index('Date')
#st.write(stock_dff.columns)
ind_er_df = stock_dff.resample('Y', on = 'Date').last().pct_change().mean()
ind_er = ind_er_df[tickers]
#st.write(ind_er)
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) # Creating a table for visualising returns and volatility of assets
assets.columns = ['Returns', 'Volatility']
assets
#st.write(assets)
ln_pct_change = stock_df[tickers].pct_change().apply(lambda x: np.log(1+x))[1:]
#Cov Matrix
cov_matrix =ln_pct_change.cov()
## CREATE PORFOLIOS WEIGHTS
p_ret = [] # Define an empty array for portfolio returns
p_vol = [] # Define an empty array for portfolio volatility
p_weights = [] # Define an empty array for asset 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) # Returns are the product of individual expected returns of asset and its
# weights
p_ret.append(returns)
var = cov_matrix.mul(weights, axis=0).mul(weights, axis=1).sum().sum()# Portfolio Variance
sd = np.sqrt(var) # Daily standard deviation
ann_sd = sd*np.sqrt(250) # Annual standard deviation = volatility
p_vol.append(ann_sd)
data = {'Returns':p_ret, 'Volatility':p_vol}
for counter, symbol in enumerate(stock_df[tickers].columns.tolist()):
#print(counter, symbol)
data[symbol] = [w[counter] for w in p_weights]
port_ef_df = pd.DataFrame(data)
port_ef_df['Vol'] = port_ef_df['Volatility']
## NEEDS INPUT INSTEAD OF HARD CODE
#a = 5 #the coefficient of risk aversion is A. If an invest is less risk averse A is small. We assume 25 < A < 35.
#rf = 0.041
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()]
### Make DF and data string for when hover over data points
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)) + '
'
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)) + '
'
port_str_lst.append(temp)
return port_str_lst
port_str_lst = make_port_str(port_ef_df, tickers)
## CREATE CAPM LINE #https://www.youtube.com/watch?v=JWx2wcrSGkk
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)
## Create Figure
fig3 = go.Figure()
#https://plotly.com/python/colorscales/
fig3.add_trace(go.Scatter(x=port_ef_df['Volatility'], y=port_ef_df['Returns'], hovertemplate='Volatility: %{x}
Returns: %{y}
%{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'))
#, mode='markers', size=port_ef_df['Returns'], \
#size_max=30, color=port_ef_df['Vol']))
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}
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}
Returns: %{y}
%{text}',\
text=[op_str]))
### HOVER TEMPLATE # https://plotly.com/python/hover-text-and-formatting/
# ### SAVE IN CASE CANNOT FIGURE OUT THE HOVER TEMPLATE
# fig2 = px.scatter(op_df, 'Volatility', 'Returns')
# fig2.update_traces(marker=dict(color= 'rgba(11,156,49,1)', size=35))
# fig1 = px.line(utl_df, x="cal_x", y="cal_y")
# #fig1.update_traces(line=dict(color = 'rgba(11,156,49,1)'))
# fig = px.scatter(port_ef_df, 'Volatility', 'Returns', size='Returns', size_max=30, color='Vol')
# #https://stackoverflow.com/questions/59057881/python-plotly-how-to-customize-hover-template-on-with-what-information-to-show
# #https://stackoverflow.com/questions/65124833/plotly-how-to-combine-scatter-and-line-plots-using-plotly-express
# #data3 =
# fig3.data = [fig2.data,fig1.data,fig.data]
# #fig3.update_traces(line=dict(color = 'rgba(11,156,49,1)'))
# ####
fig3.update_layout(showlegend=False)#, legend_title_text = "Contestant")
fig3.update_xaxes(title_text="Volatility")
fig3.update_yaxes(title_text="Portfolio Return Rates")
st.plotly_chart(fig3, use_container_width=True)
#st.write(op_str)
op_df = op_df.style.set_properties(**{'color':'green'})
st.subheader('Optimal Returns vs Volatility and Portfolio weights')
col1, col2, col3 = st.columns([1,6,1])
with col1:
st.write("")
with col2:
st.write(op_df)
with col3:
st.write("")
im = Image.open('EFvsMinvar.png')
st.subheader('Understand the Efficient Frontier')
col1, col2, col3 = st.columns([1,6,1])
with col1:
st.write("")
with col2:
st.image(im, caption='Elements of the Efficient Frontier',use_column_width='auto')
with col3:
st.write("")