Portfolio-Optimization / Portfolio.py
arif97's picture
updated
8443aa0
Raw History Blame Contribute Delete
7.43 kB
import streamlit as st
from streamlit_lottie import st_lottie
import pandas as pd
import plotly.express as px
import numpy as np
from scipy.optimize import minimize
from fredapi import Fred
import warnings
import time
import json
import plotly.graph_objects as go
warnings.filterwarnings('ignore')
np.set_printoptions(suppress=True)
from PortfolioHelperFuncs import navigationBar
from PortfolioHelperFuncs import commodities
from PortfolioHelperFuncs import Data
from PortfolioHelperFuncs import visualize
from PortfolioHelperFuncs import retrieve_tickers
from PortfolioHelperFuncs import KeyMetrics
from PortfolioHelperFuncs import plot_optimal_weights
st.set_page_config(layout="wide",
page_title="Portfolio Optimization For Commodities",
page_icon="Portfolio_Icon.jpg")
if "dtVariable" not in st.session_state:
st.session_state.dtVariable = Data(collectedData=pd.DataFrame(),
commTickers={},
years=5,
upperBoundLimit=0.5)
if 'loadOnce' not in st.session_state:
st.session_state.loadOnce = True
if st.session_state.loadOnce:
with open(file='Portfolio_Anim.json', mode = "r") as f:
anim = json.load(f)
with st.lottie_spinner(anim, height = 700):
time.sleep(2)
st.session_state.loadOnce = False
#------------------------------------------------------------------------------ Navigation Bar
selected_option = navigationBar()
if selected_option == 'Sharpe Ratio Technique':
_, c, _ = st.columns((3,5,3))
c.title('Portfolio Optimization with Commodities')
with c.form(key='Parameters Button'):
selected_commodities = st.multiselect('Select Commodities', list(commodities.keys()), default=list())
years_of_data = st.slider('Select Number of Years of Data', 1, 10, 5)
upper_bound_limit = st.slider('Set Upper Bound Limit for Asset Allocation', 0.0, 1.0, 0.3)
risk_free_rate_source = st.radio('Select Risk-Free Rate Source', ['Manual', 'FRED API'])
disabledTF = True if risk_free_rate_source == "FRED API" else False
risk_free_rate_manual = st.number_input('Enter Risk-Free Rate (%)', min_value=0.0, max_value=100.0, value=3.0,)
log_normal_returns = st.checkbox('Use Log Normal Returns (Will use Simple Returns if not selected)', value=False)
submitted = st.form_submit_button("Get me my Optimal Investment Strategy")
if submitted:
st.write('<br>', unsafe_allow_html=True)
st.divider()
st.write('<br>', unsafe_allow_html=True)
st.session_state.dtVariable.collectedData = pd.DataFrame()
st.session_state.dtVariable.commTickers = {k: commodities[k] for k in selected_commodities}
st.session_state.dtVariable.years = years_of_data
st.session_state.dtVariable.upperBoundLimit = upper_bound_limit
retrieve_tickers()
st.session_state.dtVariable.collectedData = st.session_state.dtVariable.collectedData[selected_commodities]
f = visualize(st.session_state.dtVariable.collectedData, 'Historical Prices', {'value': 'Price', 'variable': 'Commodity'}, 'Date', 'Price', 'Commodities')
_, cnt, _ = st.columns((2,5,2))
cnt.plotly_chart(f, use_container_width= True)
if risk_free_rate_source == 'FRED API':
f = Fred(api_key='385c2ddfc6d3c369a67ad83c63f6fd7d')
treasuryRate = f.get_series_latest_release('GS10')
risk_free_rate = treasuryRate.iloc[-1] / 100
else:
risk_free_rate = risk_free_rate_manual / 100
constraints = {'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1}
bounds = [(0, st.session_state.dtVariable.upperBoundLimit) for _ in range(len(st.session_state.dtVariable.collectedData.columns))]
metrics = KeyMetrics(data=st.session_state.dtVariable.collectedData, logNormal=log_normal_returns)
metrics.calculate_daily_returns()
metrics.calculate_covariance()
def objective(weights):
metrics.initialWeights = weights
metrics.calculate_standard_deviation()
metrics.calculate_expected_return()
metrics.calculate_sharpe_ratio(riskFreeRate=risk_free_rate)
return -metrics.sharpeRatio
result = minimize(fun=objective, x0=metrics.initialWeights, constraints=constraints, bounds=bounds, method='SLSQP')
if result.success:
optimal_weights = result.x
optimal_weights = np.round(optimal_weights, 3)
if np.isclose(np.sum(optimal_weights), 1):
metrics.initialWeights = optimal_weights
metrics.calculate_covariance()
metrics.calculate_standard_deviation()
metrics.calculate_expected_return()
metrics.calculate_sharpe_ratio(riskFreeRate=risk_free_rate)
# optimal_weights_df = pd.DataFrame(data=optimal_weights.reshape(-1, 1), index=st.session_state.dtVariable.collectedData.columns)
optimal_weights_df = pd.DataFrame({ 'Tickers': np.array(st.session_state.dtVariable.collectedData.columns), 'Weights': optimal_weights })
st.write('<br>', unsafe_allow_html=True)
st.divider()
st.write('<br>', unsafe_allow_html=True)
riskCol, sharpeRatioCol, returnsCol = st.columns((2,2,2))
riskCol.header("Investment Risk Associated with your Portfolio and the Weights Allocation")
fig_gauge = go.Figure(go.Indicator(
mode="gauge+number",
value=metrics.standardDeviation * 100,
title={'text': ""},
gauge={'axis': {'range': [0, 100]}}
))
riskCol.plotly_chart(fig_gauge)
# Metric Card
sharpeRatioCol.header("Sharpe Ratio")
sharpeRatioCol.metric(label="", value=np.round(metrics.sharpeRatio,4))
returnsCol.header("Expected Returns on your Portfolio & its Optimal Weights")
fig_gauge = go.Figure(go.Indicator(
mode="gauge+number",
value=metrics.expectedReturns * 100,
title={'text': ""},
gauge={'axis': {'range': [0, 100]}}
))
returnsCol.plotly_chart(fig_gauge)
st.write('<br>', unsafe_allow_html=True)
st.divider()
st.write('<br>', unsafe_allow_html=True)
_, weightsDepictionColumns1, _, weightsDepictionColumns2, _ = st.columns((1,4,1,2,1))
weightsDepictionColumns1.plotly_chart(plot_optimal_weights(optimal_weights_df), use_container_width=True)
weightsDepictionColumns2.header('Weights Allocation Pie Chart')
fig_pie = px.pie(optimal_weights_df, values='Weights', names='Tickers', title='', height=400)
weightsDepictionColumns2.plotly_chart(fig_pie)
else:
cnt.error("The sum of the optimal weights is not equal to 1.")
else:
cnt.error(f"Optimization failed: {result.message}")
cnt.write("Try Different Combinations, add more tickers or adjusting Upper Bound Values or Something other")