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 | |
### 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') | |
# """ | |
# CAPM model measure systematic risks, however it has unrealistic assumptions and relies heavily on a linear interpretation | |
# of the risks vs. returns relationship. It is better to use CAPM model in conjunction with the Efficient Frontier to better | |
# graphically depict volatility (a measure of investment risk) for the defined rate of return. <br> | |
# Each circle depicted above is a variation of the portfolio with the same input assest, only different weights. | |
# Portfolios with higher volatilities has a yellower shade of hue, while portfolios with a higher return has a bigger radius. <br> | |
# As you input different porfolio assets, take note of how diversification can improve a portfolio's risk versus reward profile. | |
# """ | |
symbols, weights, investment, 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 = 10000 | |
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) | |
#st.write(port_ef_df[tickers].T) | |
## 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()] | |
## CREATE CAPM LINE #https://www.youtube.com/watch?v=JWx2wcrSGkk | |
cal_x = [] | |
cal_y = [] | |
utl = [] | |
for er in np.linspace(rf, max(data['Returns']),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 | |
# fig = go.Figure() | |
# fig.add_trace(go.Scatter(x=port_ef_df['Volatility'], y=port_ef_df['Returns'], \ | |
# color=port_ef_df['Volatility'], mode='markers', name='markers')) | |
fig = px.scatter(port_ef_df, 'Volatility', 'Returns', size='Returns', size_max=20, color='Volatility', \ | |
custom_data=tickers) | |
fig.update_traces(hovertemplate="<br>".join([ | |
"%{tickers[0]}:%{custom_data[0]}", | |
"%{tickers[1]}:%{custom_data[1]}", | |
"%{tickers[2]}:%{custom_data[2]}", | |
"%{tickers[3]}:%{custom_data[3]}" | |
]) | |
) | |
fig1 = px.line(utl_df, x="cal_x", y="cal_y") | |
fig1.update_traces(line=dict(color = 'rgba(11,156,49,1)')) | |
#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 | |
fig3 = go.Figure(data=fig.data + fig1.data) | |
st.plotly_chart(fig3, use_container_width=True) | |
#st.write(stock_df) |