import streamlit as st import requests import json import plotly.express as px import pandas as pd import datetime as dt from risk_metrics import annual_return, absolute_return, annual_vol, max_drawdown try: from PIL import Image except ImportError: import Image import numpy as np st.markdown( """ """, unsafe_allow_html = True ) def load_data(limit='10'): ''' Returns a dictionary with data for each of the top 'limit' cypto currencies ranked by market cap. The data is generated by querying the coincap API /assets endpoint. See coincap documentation for more info: https://docs.coincap.io/ Parameters: limit (str): The number of crypto coins that you want to return data for. Ranked in order of market cap. Returns: (dict): A dictionary object of data. ''' url = "https://api.coincap.io/v2/assets" # N.B. here adampt the params dict to only request what you need payload={'limit': limit} headers = {} return requests.request("GET", url, params=payload, headers=headers).json() def load_histories(ids_list): url = "http://api.coincap.io/v2/assets/{}/history?interval=d1" payload={} headers = {} histories_dict = {} for id in ids_list: response_histories = requests.request("GET", url.format(id), headers=headers, data=payload) histories_json = response_histories.json() histories_dict[id] = histories_json['data'] return histories_dict def gen_symbols(assets_json): symbols_list = [] names_list = [] ids_list =[] for dict in assets_json['data']: symbols_list.append(dict['symbol']) names_list.append(dict['name']) ids_list.append(dict['id']) return symbols_list, names_list, ids_list def write_symbols(symbols_list): cols = st.columns(len(symbols_list)) for i, symbol in enumerate(symbols_list): col = cols[i] col.image(f'logos/{symbol}.png',width=40) globals()[st.session_state.names[i]] = col.checkbox(symbol, value = 0) #col.checkbox(symbol, st.image(f'logos/{symbol}.png',width=40)) if "assets_json" not in st.session_state: st.session_state.assets_json = load_data() symbols, names, ids = gen_symbols(st.session_state.assets_json) st.session_state.symbols = symbols st.session_state.names = names st.session_state.ids = ids st.session_state.histories = load_histories(ids) id_symbol_map = {} for i, id in enumerate(ids): id_symbol_map[id]=symbols[i] st.session_state.id_symbol_map = id_symbol_map #write_symbols(st.session_state.symbols) symbols_list = st.session_state.symbols names_list = st.session_state.names ids_list = st.session_state.ids asset_json = st.session_state.assets_json histories_dict = st.session_state.histories id_symbol_map = st.session_state.id_symbol_map def date_conv(date): return dt.datetime.strptime(date, '%Y-%m-%d') price_histories_df = pd.DataFrame(columns=['coin','date','price']) return_histories_df = pd.DataFrame(columns=['coin','date','price']) for id in ids_list: price=[] date=[] for observation in histories_dict[id]: date.append(date_conv(observation['date'][0:10])) #date.append(observation['time']) price.append(float(observation['priceUsd'])) price_df = pd.DataFrame({"coin": id, "date":date, "price": price}) price_histories_df = pd.concat([price_histories_df, price_df]) returns = [float(b) / float(a) for b,a in zip(price[1:], price[:-1])] returns_df = pd.DataFrame({"coin": id, "date":date[1:], "price": returns}) return_histories_df = pd.concat([return_histories_df, returns_df]) start_date = dt.date.today()-dt.timedelta(360) rebased_prices_df = pd.DataFrame(columns=['coin','date','price','rebased_price']) for id in ids_list: temp_rebase_df = return_histories_df[(return_histories_df['date']>=pd.Timestamp(start_date)) & (return_histories_df['coin']==id)] rebased_price=[1] for i in range(1,len(temp_rebase_df)): rebased_price.append(temp_rebase_df['price'].iloc[i]*rebased_price[i-1]) temp_rebase_df['rebased_price']=rebased_price rebased_prices_df = pd.concat([rebased_prices_df, temp_rebase_df]) fig2 = px.line(rebased_prices_df, x="date", y="rebased_price", color="coin") st.write(fig2) cols = st.columns(len(symbols_list)) checkboxes=[] def write_coins(id_symbol_map, n_cols=5): n_coins = len(id_symbol_map) n_rows = 1 + n_coins // int(n_cols) rows = [st.container() for _ in range(n_rows)] cols_per_row = [r.columns(n_cols) for r in rows] cols = [column for row in cols_per_row for column in row] #cols = st.columns(n_coins) #checkboxes=[] for i, id in enumerate(id_symbol_map): cols[i].image('logos/{}.png'.format(id_symbol_map[id]),width=40) globals()[st.session_state.names[i]] = cols[i].checkbox("include", value = 1, key=id) globals()["slider_"+ids_list[i]] = cols[i].slider(id, min_value=0, max_value=100, value=50, key=id) checkboxes.append(globals()[st.session_state.names[i]]) write_coins(id_symbol_map) #for i, symbol in enumerate(symbols_list): # col = cols[i] # col.image(f'logos/{symbol}.png',width=40) # globals()[st.session_state.names[i]] = col.checkbox(symbol, value = 1) # checkboxes.append(globals()[st.session_state.names[i]]) #if any(checkboxes): # checked_ids=[] # cols2 = st.columns(sum(checkboxes)) # j=0 # for i, value in enumerate(checkboxes): # if value==1: # checked_ids.append(ids_list[i]) # col2=cols2[j] # col2.image(f'logos/{symbols_list[i]}.png',width=20) # j+=1 def create_grid(top_left, bottom_right): num_rows=3 num_cols=7 col_positions = np.linspace(top_left[0], bottom_right[0], num=num_cols) row_positions = np.linspace(top_left[1], bottom_right[1], num=num_rows) return [(int(col_positions[i]),int(row_positions[j])) for j in range(num_rows) for i in range(num_cols)] # These are the coordinates of the top left and bottom right of the cart image # given it's curent size. You need to change these if you change the size of the # cart top_left=[300,300] bottom_right=[650, 450] grid = create_grid(top_left, bottom_right) def add_logo(background, symbol, position, size=(70,70)): bg = Image.open(background) fg = Image.open("logos/{}.png".format(symbol)) bg = bg.convert("RGBA") fg = fg.convert("RGBA") # Resize logo fg_resized = fg.resize(size) # Overlay logo onto background at position bg.paste(fg_resized,box=position,mask=fg_resized) # Save result bg.save(background) cart_cols = st.columns([3,2]) if any(checkboxes): checked_ids=[] for i, value in enumerate(checkboxes): if value==1: checked_ids.append(ids_list[i]) #cart_cols[1].image(f'logos/{symbols_list[i]}.png',width=20) #cart_cols[2].slider(ids_list[i],min_value=0, max_value=100, value=50) # change the below to make it run only if checked_ids ecists - i.e. wrap it up oin a function original = Image.open("images/cart.png") original.save('images/background.png') position_ids = [round(x) for x in np.linspace(0, len(grid)-1, num=len(checked_ids))] for i, id in enumerate(checked_ids): size = tuple([int(num * globals()["slider_"+id]/50) for num in (70,70)]) add_logo('images/background.png', id_symbol_map[id], grid[position_ids[i]], size=size) weights=[] for id in checked_ids: weights.append(globals()["slider_"+id]) sum_weights = sum(weights) weights = [weight/sum_weights for weight in weights] weights_df = pd.DataFrame({'ids':checked_ids, 'weights': weights, 'portfolio': 'port_1'}) pie_fig = px.pie(weights_df, values='weights', names='ids') pie_fig.update_layout(showlegend=False) bar_fig = px.bar(weights_df, x="portfolio", y="weights", color="ids", width=200) bar_fig.update_layout(showlegend=False) cart_cols[0].image('images/background.png', width=400) cart_cols[1].write(bar_fig) gen_port = st.button('Generate portfolio return') metrics_dict= {'annual_return' : "Return (annualised)", 'absolute_return': "Return over period", 'annual_vol': 'Annual volatility', 'max_drawdown': 'Max loss'} def write_metrics(prices, *metrics): for metric in metrics: cols = st.columns(2) if metric.__name__ == 'max_drawdown': cols[0].write(metrics_dict[metric.__name__] +': ') cols[1].write('{:.2%}'.format(metric(prices)[0])) else: cols[0].write(metrics_dict[metric.__name__] +': ') cols[1].write('{:.2%}'.format(metric(prices))) if gen_port: # adjust weight calculation to read in from globals()["slider_"+ids_list[i]] #weights = [1/len(checked_ids)]*len(checked_ids) portfolio_dict={checked_ids[i]:weights[i] for i in range(len(checked_ids))} start_date = dt.date.today()-dt.timedelta(360) weighted_prices_df = pd.DataFrame(columns=['coin','date','price','weighted_price']) for id in checked_ids: temp_weight_df = return_histories_df[(return_histories_df['date']>=pd.Timestamp(start_date)) & (return_histories_df['coin']==id)] weighted_price=[portfolio_dict[id]] for i in range(1,len(temp_weight_df)): weighted_price.append(temp_weight_df['price'].iloc[i]*weighted_price[i-1]) temp_weight_df['weighted_price']=weighted_price weighted_prices_df = pd.concat([weighted_prices_df, temp_weight_df]) date_list = [start_date + dt.timedelta(days=x) for x in range(360)] port_returns=[] for date in date_list: port_returns.append(weighted_prices_df['weighted_price'][weighted_prices_df['date']==pd.Timestamp(date)].sum()) port_returns_df = pd.DataFrame({'date':date_list, 'price': port_returns}) prices = port_returns_df['price'] max_dd, start_idx, end_idx = max_drawdown(prices) start_dt = port_returns_df['date'].iloc[start_idx] end_dt = port_returns_df['date'].iloc[end_idx] fig3 = px.line(port_returns_df, x="date", y="price") fig3.add_vline(x=start_dt, line_width=1, line_color="red") fig3.add_vline(x=end_dt, line_width=1, line_color="red") fig3.add_vrect(x0=start_dt, x1=end_dt, line_width=0, fillcolor="red", opacity=0.05, annotation_text="max loss ") st.write(fig3) st.title("Risk metrics") write_metrics(prices, absolute_return, annual_return, annual_vol, max_drawdown) #for i, symbol in enumerate(symbols_list): # col2 = cols2[i] # col.image(f'logos/{symbol}.png',width=40) #price_subset_df = price_histories_df[price_histories_df['coin'].isin(checked_ids)] #rebased_subset_df = rebased_prices_df[rebased_prices_df['coin'].isin(checked_ids)] #fig1 = px.line(price_subset_df, x="date", y="price", color="coin") #st.write(fig1) #fig2 = px.line(rebased_subset_df, x="date", y="rebased_price", color="coin") #st.write(fig2)