Spaces:
Runtime error
Runtime error
import streamlit as st | |
import plotly.express as px | |
from datetime import date, timedelta | |
from data_creator import create_market_cap_dict, gen_rebased_df, ids2names_dict, names2ids_dict, create_assets, gen_symbols, create_histories_df, create_unix_dates, create_returns_df, create_rebased_df, date_range | |
from plot_creator import get_pre_selected_idx, write_coins, write_coins_custom, write_bespoke_coins, create_comparison_df, load_images, gen_performance_ag_df, add_drawdown | |
from port_creator import gen_all_returns, markowitz_weights_dict, uniform_weights_dict, ids_with_histories, uniform, create_port_rtns, markowitz_weights, create_weights_df | |
from risk_metrics import max_drawdown | |
from st_aggrid import AgGrid, GridOptionsBuilder | |
from persist import persist, load_widget_state | |
load_widget_state() | |
st.markdown( | |
""" | |
<style> | |
.css-1xsoh1l { | |
font-size: 0px; | |
} | |
.css-1xsoh1l{ | |
color: rgb(120 190 33); | |
} | |
.css-jhf39w { | |
color: rgba(120, 190, 33, 1); | |
} | |
.css-jv3mmh { | |
background-color: rgb(120, 190, 33); | |
} | |
</style> | |
""", | |
unsafe_allow_html = True | |
) | |
# load start and end dates for investment analysis | |
lookback_years = 5 # max date range for backtest will be: lookback_years - 1 | |
start_date = date.today() - timedelta(365) | |
end_date = date.today() | |
if 'start_date' not in st.session_state: | |
st.session_state.start_date = start_date | |
st.session_state.end_date = end_date | |
if 'max_coins' not in st.session_state: | |
st.session_state.max_coins = 10 | |
if 'start_id' not in st.session_state: | |
st.session_state.start_id = 1 | |
# Pull down histories from coincap, and create dataframes for historic prices, | |
# returns and rebased cumulative price; histories_df, returns_df, and | |
# rebased_df, respectively. | |
assets_json = create_assets(total_coins=50) | |
symbols, names, coin_ids = gen_symbols(assets_json) | |
ids2symbols = ids2names_dict(coin_ids, symbols) | |
ids2names_dict=ids2names_dict(coin_ids, names) | |
names2ids_dict = names2ids_dict(names, coin_ids) | |
market_cap_dict = create_market_cap_dict(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) | |
# Create list of coin ids with full hisoties over the backtest period | |
ids_with_histories = ids_with_histories(histories_df, | |
st.session_state.start_date, st.session_state.end_date) | |
names_with_histories = list(map(ids2names_dict.get, ids_with_histories)) | |
def change_date_range(): | |
st.session_state.start_date = st.session_state.myslider[0] | |
st.session_state.end_date = st.session_state.myslider[1] | |
# calculate weghts for the uniform and markowitz pfs | |
uniform_weights_dict = uniform_weights_dict(ids_with_histories[:st.session_state.max_coins]) | |
#markowitz_weights_dict = markowitz_weights_dict(histories_df, | |
# st.session_state.start_date ,ids_with_histories[:max_coins], analysis_days=365) | |
strategy_dict = {'Uniform': uniform_weights_dict}#, 'Markowitz':markowitz_weights_dict} | |
if "strategy_dict" not in st.session_state: | |
st.session_state.strategy_dict=strategy_dict | |
if 'selected_assets' not in st.session_state: | |
st.session_state.selected_assets = ["Uniform"] | |
with st.sidebar: | |
st.subheader("Portfolio weights viewer") | |
portfolio_type = st.selectbox( | |
'Select portfolio strategy', | |
['Create your own'] + (list(st.session_state.strategy_dict.keys())), | |
index = st.session_state.start_id | |
) | |
if st.checkbox("Explain this"): | |
st.subheader("What's this all about then, eh?") | |
st.write(''' | |
The app allows you to construct your own portfolios of crypto currencies and view their | |
historic performance alongside the performance of individual crypto | |
currencies over an investment period of your choosing. | |
To view the assets and weights comprising a particular portfolio select the | |
portfolio of interest in the 'Select portfolio strategy' dropdown (a uniform | |
portfolio for the top ten largest coins has been automatically created for you | |
to start with). | |
To create your own portfolio: | |
1. Select 'Create your own' in the 'select portfolio strategy' dropdown; | |
2. Select the maximum number of coins in your portfolio; | |
3. Select the relative weights for each of these assets; | |
4. Choose a name for your portfolio and click add portfolio; | |
5. Click update viewer; | |
You can sort and filter the performance metrics table on each of the columns. | |
To add an asset to the performance chart, select the corresponding select box. | |
''') | |
# Add select slider to allow | |
date_list = date_range(end_date,lookback_years-1) | |
start_port_date, end_port_date = st.select_slider( | |
'Select backtest date range', | |
key="myslider", | |
options=date_list, | |
#value=(date.today() - timedelta(365), date.today()), | |
value = (st.session_state.start_date, st.session_state.end_date), | |
on_change=change_date_range | |
) | |
# Move the definition of strategy_dict to about the potfolio_type selectbox | |
# This will require that you define max_coins in session state,a dn the | |
# have the max_coins number_input update the max coins session state. | |
# = 10 and let it be | |
# calculate returns for the portfolios and add to it the rebased df for assets | |
# with hisories. This is the new returns_df | |
rebased_df = gen_rebased_df(histories_df, ids_with_histories, | |
st.session_state.start_date, st.session_state.end_date) | |
all_returns_df = gen_all_returns(rebased_df, ids_with_histories,st.session_state.strategy_dict) | |
def write_something(): | |
st.write("") | |
def rerun_aggrid(): | |
st.session_state.selected_indexes = selected_indexes | |
st.session_state.performance_ag_df = gen_performance_ag_df(all_returns_df, market_cap_dict, | |
st.session_state.strategy_dict) | |
st.header('ran') | |
if portfolio_type == 'Create your own': | |
with st.sidebar: | |
st.session_state.max_coins = st.number_input( | |
"Maximum number of coins in portfolio", | |
min_value=1, | |
max_value=20, | |
value=10, | |
help=''' | |
Coins will be added to your "investment set" in order of largest market cap. | |
The "investment set" is the group of assets from which your portfolio is | |
constructed. Depending on the portfolio strategy you choose, not all of the | |
assets in your investment set will be included in your portfolio. | |
''' | |
) | |
st.markdown("Bespoke portfolio weights (relative):" , unsafe_allow_html=False) | |
bespoke_weights = write_coins_custom(names_with_histories[:st.session_state.max_coins]) | |
#bespoke_weights = write_bespoke_coins(names_with_histories[:st.session_state.max_coins]) | |
bespoke_cols = st.columns(2) | |
bespoke_cols[0].write(" ") | |
bespoke_cols[0].write(" ") | |
add_bespoke = bespoke_cols[0].button("Add portfolio", key='bespoke_button') | |
bespoke_name = bespoke_cols[1].text_input("Choose portfolio name") | |
if add_bespoke: | |
if bespoke_name=="" or bespoke_name in all_returns_df.columns: | |
st.warning("Please give your portfolio a unique name") | |
else: | |
beskpoke_weights_dict={} | |
for i, wt in enumerate(bespoke_weights): | |
beskpoke_weights_dict[coin_ids[i]] = wt | |
st.session_state.strategy_dict[bespoke_name] = beskpoke_weights_dict | |
st.session_state.start_id = len(st.session_state.strategy_dict) | |
#st.session_state.selected_assets.append(bespoke_name) | |
st.success("Porfolio added, update viewer to see results") | |
#st.button('Update viewer', on_click = change_date_range) | |
st.button('Update viewer', on_click = rerun_aggrid) | |
#st.button('Update viewer', on_click = st.experimental_rerun()) | |
#st.write(st.session_state.strategy_dict) | |
else: | |
non_zero_coins = [key for key in st.session_state.strategy_dict[portfolio_type].keys() if st.session_state.strategy_dict[portfolio_type][key]>0] | |
with st.sidebar: | |
st.markdown(portfolio_type + " portfolio weights (%):" , unsafe_allow_html=False) | |
write_coins(non_zero_coins, st.session_state.strategy_dict[portfolio_type], ids2names_dict) | |
st.session_state.performance_ag_df = gen_performance_ag_df(all_returns_df, market_cap_dict, | |
st.session_state.strategy_dict) | |
if 'selected_assets' not in st.session_state: | |
st.session_state.selected_assets = ["Uniform"] | |
selected_indexes = [] | |
for asset in st.session_state.selected_assets: | |
try: | |
selected_indexes.append(list(st.session_state.performance_ag_df['Asset']).index(asset)) | |
except: | |
pass | |
if 'selected_indexes' not in st.session_state: | |
st.session_state.selected_indexes = selected_indexes | |
gb = GridOptionsBuilder.from_dataframe(st.session_state.performance_ag_df) | |
gb.configure_selection('multiple', use_checkbox=True, | |
pre_selected_rows = st.session_state.selected_indexes) | |
gridOptions = gb.build() | |
st.subheader("Performance metrics") | |
grid_response = AgGrid(st.session_state.performance_ag_df, gridOptions=gridOptions, | |
data_return_mode = 'FILTERED', allow_unsafe_jscode=True, height = 200, | |
update_mode='MODEL_CHANGED', key = persist("aggrid")) # MANUAL SELECTION_CHANGED MODEL_CHANGED, VALUE_CHANGED | |
selected_assets = [] | |
for row in grid_response['selected_rows']: | |
selected_assets.append(row['Asset']) | |
st.session_state.selected_assets = selected_assets | |
#selected_indexes = [] | |
#for asset in st.session_state.selected_assets: | |
# selected_indexes.append(list(performance_ag_df['Asset']).index(asset)) | |
chart_df = create_comparison_df(all_returns_df, st.session_state.selected_assets) | |
fig = px.line(chart_df, x=chart_df.index, y='Value (USD)', color='Asset') | |
st.subheader("Performance chart") | |
st.write(fig) | |