Spaces:
Runtime error
Runtime error
import streamlit as st | |
import json | |
import plotly.express as px | |
import pandas as pd | |
from datetime import date, timedelta, datetime | |
from risk_metrics import annual_return, absolute_return, annual_vol, max_drawdown | |
import numpy as np | |
from data_creator import create_assets, gen_symbols, create_histories_df, create_unix_dates, create_returns_df, create_rebased_df, date_range | |
from plot_creator import create_rebase_chart | |
from port_creator import uniform, create_port_rtns, markowitz_weights, create_weights_df | |
from risk_metrics import max_drawdown | |
# load start and end dates for investment analysis | |
lookback_years = 5 | |
start_date = date.today() - timedelta(365) | |
end_date = date.today() | |
with st.sidebar: | |
investment_set = st.number_input( | |
"How many coins to would you like in your investment set?", | |
min_value=1, | |
max_value=50, | |
value=10, | |
help="Coins will be added to your investment set in order of largest market cap" | |
#("Top 5 coins", "Top 10 coins") | |
) | |
portfolio_type = st.selectbox( | |
'Select portfolio strategy', | |
('Uniform', 'Markowitz'), | |
help=''' | |
Uniform: An equal propotion of your initial investment is allocated to each | |
asset in the investment set (provided the asset existed at the start date of | |
your investment period). \n | |
Markowitz: Your initial investment is allocated to each asset in the | |
investment set to achieve the "market portfolio" using a risk-variance | |
analysis (provided the asset existed at the start date of your investment | |
period). | |
''' | |
) | |
# Pull down histories from coincap, and create dataframes for historic prices, | |
# returns and rebased cumulative price; histories_df, returns_df, and | |
# rebased_df, respectively. | |
# All fo the functions in the block below have been decorated with st.cache() | |
# and so will only be re-run if their arguments, or their underlying code, are | |
# changed | |
assets_json = create_assets(total_coins=100) | |
symbols, names, coin_ids = gen_symbols(assets_json) | |
start_unix, end_unix = create_unix_dates(today=date.today(), lookback_years=lookback_years) | |
histories_df = create_histories_df(coin_ids, start_unix, end_unix) | |
returns_df = create_returns_df(histories_df) | |
rebased_df = create_rebased_df(returns_df, start_date=start_date, end_date=end_date) | |
if 'rebased_df' not in st.session_state: | |
st.session_state.rebased_df = rebased_df | |
#def adjust_rebased(returns_df, start_date, end_date): | |
def adjust_rebased(): | |
st.session_state.rebased_df = create_rebased_df(returns_df, | |
start_date = st.session_state.myslider[0], | |
end_date=st.session_state.myslider[1]) | |
# Draw rebased graph | |
melt_df = create_rebase_chart(st.session_state.rebased_df, num_coins=investment_set) | |
fig = px.line(melt_df, x=melt_df.index, y='price (USD)', color='coin') | |
with st.expander('Quick explantion', expanded = True): | |
st.subheader("What's this all about then, eh?") | |
st.write(''' | |
The app allows you to construct portfolios of crypto currencies and to | |
backtest their historic performance. | |
You can select how many coins you would like in your investment set using the | |
dropdown box in the sidebar. | |
You can select from two different portfolio constructions | |
strategies using the dropdown box in the sidebar: | |
- Uniform - An equal propotion of your initial investment is allocated to each coin. | |
- Markowitz - Your initial investment is allocated to each coin to achieve the portfolio with the highest sharpe ratio in the 365 day period prior to the investment start date. | |
You can adjust the date range for the portfolio backtest using the slider widget below. | |
If you would like to see the performance of the individual coins in your investment set | |
over the backtest period click the + icon in the Coin view expander. | |
To collapse this expander click the - icon at the top right. | |
''') | |
# Add select slider to allow | |
date_list = date_range(end_date,lookback_years-1) | |
start_port_date, end_port_date = st.select_slider( | |
'Select date range for portolio backtest', | |
key="myslider", | |
options=date_list, | |
value=(date.today() - timedelta(365), date.today()), | |
on_change=adjust_rebased | |
) | |
with st.expander("Coin view", expanded=False): | |
st.subheader('Individual coin performance') | |
st.write(fig) | |
uniform_weights, investment_cols = uniform(returns_df, num_coins=investment_set, | |
start_date=start_port_date, end_date=end_port_date) | |
uniform_weights_dict = {} | |
for i, coin in enumerate(investment_cols): | |
uniform_weights_dict[coin] = uniform_weights[i] | |
markowitz_weights_dict = markowitz_weights(histories_df,start_port_date,investment_cols, analysis_days=365) | |
uniform_returns = create_port_rtns(returns_df, uniform_weights, investment_cols, start_port_date, end_port_date) | |
markotwitz_returns = create_port_rtns(returns_df, list(markowitz_weights_dict.values()), investment_cols, start_port_date, end_port_date) | |
returns_dict = {'Uniform': uniform_returns, 'Markowitz':markotwitz_returns} | |
strategy_dict = {'Uniform': uniform_weights_dict, 'Markowitz':markowitz_weights_dict} | |
port_return = returns_dict[portfolio_type] | |
#calculate max drawdown | |
max_dd, start_idx, end_idx = max_drawdown(port_return) | |
start_dd = port_return.index[start_idx] | |
end_dd = port_return.index[end_idx] | |
port_fig = px.line(port_return, x=port_return.index, y='price (USD)') | |
port_fig.add_vline(x=start_dd, line_width=1, line_color="red") | |
port_fig.add_vline(x=end_dd, line_width=1, line_color="red") | |
port_fig.add_vrect(x0=start_dd, x1=end_dd, line_width=0, fillcolor="red", opacity=0.05, annotation_text="max drawdown ") | |
st.subheader("{} portfolio performance".format(portfolio_type)) | |
weights_df = create_weights_df(strategy_dict[portfolio_type], portfolio_type) | |
bar_fig = px.bar(weights_df, x="strategy", y="weights", color="assets", width=200) | |
bar_fig.update_layout(showlegend=False, xaxis={'visible': False}, ) | |
cols = st.columns([8,1]) | |
cols[0].write(port_fig) | |
cols[1].write(bar_fig) | |
cols = st.columns([1,3]) | |
outlay = cols[0].number_input('Initial $ amount', min_value=0, value=1000, | |
step=1) | |
final_amount = outlay*port_return[-1] | |
max_loss=outlay*max_dd | |
cols[1].write('''For an initial investment of **${:,}**\n | |
You would have ended up with **${:,}** \n | |
You would have suffered a maximum loss of **{:.0f}%** of your portfolio value | |
between **{}** and **{}**'''.format( | |
outlay, int(final_amount), max_dd*100, start_dd, end_dd)) | |