Spaces:
Runtime error
Runtime error
File size: 6,357 Bytes
642c876 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
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))
|