import streamlit as st import pandas as pd import numpy as np import os import sys sys.path.append(os.path.dirname(os.getcwd())) from project_tools import project_utils, project_config, numerapi_utils import warnings import plotly.express as px import json warnings.filterwarnings("ignore") from PIL import Image import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots from streamlit import caching import time import traceback import datetime st.set_page_config(layout='wide') get_benchmark_data = True # get_dailyscore = True def sidebar_data_picker(): st.sidebar.subheader('Model Data Picker') top_lb = st.sidebar.checkbox('top LB by corr', value=True) top_tp3m = st.sidebar.checkbox('most profitable 3 month', value=True) top_tp1y = st.sidebar.checkbox('most profitable 1 year', value=True) special_list = st.sidebar.checkbox('model from specific users', value=True) return top_lb, top_tp3m, top_tp1y, special_list # to be removed def model_data_picker_bak(values = None): if values is None: values = [True, True, True, True, True, True] model_dict = {} st.sidebar.subheader('Model Data Picker') # top_lb = st.sidebar.checkbox('top LB by corr', value=values[0]) # top_tp3m = st.sidebar.checkbox('most profitable 3 month', value=values[1]) top_tp1y = st.sidebar.checkbox('most profitable 1 year', value=values[2]) special_list = st.sidebar.checkbox('model from specific users', value=values[3]) benchmark_list = st.sidebar.checkbox('benchmark models', value=values[4]) default_list = st.sidebar.checkbox('default models', value=values[5]) # if top_lb: # model_dict['top_corr'] = project_config.TOP_LB # if top_tp3m: # model_dict['top_3m'] = project_config.TP3M if top_tp1y: model_dict['top_1y'] = project_config.TP1Y if benchmark_list: model_dict['benchmark'] = project_config.BENCHMARK_MODELS if special_list: model_dict['iaai'] = project_config.IAAI_MODELS # model_dict['arbitrage'] = project_config.ARBITRAGE_MODELS # model_dict['mm'] = project_config.MM_MODELS # model_dict['restrade'] = project_config.RESTRADE_MODELS if default_list: model_dict['yx'] = project_config.MODEL_NAMES + project_config.NEW_MODEL_NAMES model_dict['mcv'] = project_config.MCV_MODELS + project_config.MCV_NEW_MODELS return model_dict # to be removed def model_fast_picker_bak(models): text_content = ''' fast model picker by CSV string. example: "model1, model2, model3" ''' text = st.sidebar.text_area(text_content) result_models = [] if len(text)>0: csv_parts = text.split(',') for s in csv_parts: m = s.strip() if m in models: result_models.append(m) return list(dict.fromkeys(result_models)) def default_model_picker(): picked_models = {} if os.path.isfile('default_models.json'): default_models_dict = project_utils.load_json('default_models.json') for key in default_models_dict.keys(): picked_models[key] = default_models_dict[key] if os.path.isfile('user_models.json'): user_models_dict = project_utils.load_json('user_models.json') for key in user_models_dict.keys(): picked_models[key] = user_models_dict[key] return picked_models def model_fast_picker(model_list): text_content = ''' fast model picker by CSV string. example: "model1, model2, model3" ''' text = st.sidebar.text_area(text_content) result_models = [] if len(text)>0: csv_parts = text.split(',') for s in csv_parts: m = s.strip() if (m in model_list): #and (m not in preselected_models): result_models.append(m) return list(dict.fromkeys(result_models)) def generate_round_table(data, row_cts, c, r, sortcol='corrmmc'): # rounds = data # row_cts[c].write(2*r+c) latest_round = int(data['roundNumber'].max()) earliest_round = int(data['roundNumber'].min()) suggest_round = int(latest_round - (2*r+c)) select_round = row_cts[c].slider('select a round', earliest_round, latest_round, suggest_round, 1) # row_cts[c].write(select_round) round_data = data[data['roundNumber']==select_round].sort_values(by=sortcol, ascending=False).reset_index(drop=True) round_resolved_time = round_data['roundResolveTime'][0] # round_data = round_data[round_data['model'].isin(models)].reset_index(drop=True) # latest_date = round_data['date'].values[0] row_cts[c].write(f'round: {select_round} resolved time: {round_resolved_time}') row_cts[c].dataframe(round_data.drop(['roundNumber', 'roundResolveTime'], axis=1), height=max_table_height-100) def generate_dailyscore_metrics(data, row_cts, c, r): # row_cts[c].write([r, c, 2*r+c]) select_metric = row_cts[c].selectbox("", list(id_metric_opt.keys()), index=2*r+c, format_func=lambda x: id_metric_opt[x]) latest_round = int(data['roundNumber'].max()) earliest_round = int(data['roundNumber'].min()) score = id_metric_score_dic[select_metric] df = project_utils.calculate_rounddailysharpe_dashboard(data, latest_round, earliest_round, score).sort_values(by='sos', ascending=False) row_cts[c].dataframe(df, height=max_table_height-100) pass def get_roundmetric_data(data): numfeats1 = ['corr', 'mmc', 'tc', 'corrmmc', 'corrtc', 'fncV3', 'fncV3_pct'] stat1 = ['sum', 'mean', 'count', {'sharpe': project_utils.get_array_sharpe}] # {'ptp':np.ptp}]#{'sharp':project_utils.get_array_sharpe}] numfeats2 = ['corr_pct', 'mmc_pct', 'tc_pct','corrtc_avg_pct', 'corrmmc_avg_pct'] stat2 = ['mean']#, {'sharp': project_utils.get_array_sharpe}] roundmetric_agg_rcp = [ [['model'], numfeats1, stat1], [['model'], numfeats2, stat2] ] res = project_utils.groupby_agg_execution(roundmetric_agg_rcp, data)['model'] rename_dict = {} for c in res.columns.tolist(): if c != 'model': rename_dict[c] = c[6:] # remove 'model_' in column name res.rename(columns = rename_dict, inplace=True) return res def generate_round_metrics(data, row_cts, c, r): select_metric = row_cts[c].selectbox("", list(roundmetric_opt.keys()), index=2*r+c, format_func=lambda x: roundmetric_opt[x]) cols = ['model'] # st.write(select_metric) # st.write(data.columns.tolist()) for col in data.columns.tolist(): if select_metric =='corrmmc': if (f'{select_metric}_' in col) or ('corrmmc_avg_' in col): cols += [col] elif select_metric =='corrtc': if (f'{select_metric}_' in col) or ('corrtc_avg_' in col): cols += [col] else: # if (f'{select_metric}_' in col) and (not('corrmmc' in col)) and (not('corrtc' in col)): if (f'{select_metric}_' in col): cols+= [col] if select_metric != 'pct': sort_col = select_metric+'_sharpe' else: sort_col = 'corr_pct_mean' view_data = data[cols].sort_values(by=sort_col, ascending=False) row_cts[c].dataframe(view_data) pass def dailyscore_chart(data, row_cts, c, r, select_metric): latest_round = int(data['roundNumber'].max()) earliest_round = int(data['roundNumber'].min()) suggest_round = int(latest_round - (2*r+c)) select_round = row_cts[c].slider('select a round', earliest_round, latest_round, suggest_round, 1) data = data[data['roundNumber']==select_round] if len(data)>0: fig = chart_pxline(data, 'date', y=select_metric, color='model', hover_data=list(histtrend_opt.keys())) row_cts[c].plotly_chart(fig, use_container_width=True) else: row_cts[c].info('no data was found for the selected round') pass def generate_live_round_stake(data, row_cts, c, r): latest_round = int(data['roundNumber'].max()) select_round = int(latest_round - (2*r+c)) select_data = data[data['roundNumber']==select_round].reset_index(drop=True) if len(select_data)>0: payout_sum = select_data['payout'].sum().round(3) stake_sum = select_data['stake'].sum().round(3) if payout_sum >= 0: payout_color = 'green' else: payout_color = 'red' space = ' '*5 content_str = f'#### Round: {select_round}{space}Stake: {stake_sum}{space}Payout: {payout_sum} NMR' row_cts[c].markdown(content_str, unsafe_allow_html=True) select_data = select_data.drop(['roundNumber'], axis=1).sort_values(by='payout', ascending=False) row_cts[c].dataframe(select_data, height=max_table_height-100) def round_view(data, select_perview, select_metric=None): num_cols = 2 num_rows = 2 for r in range(num_rows): row_cts = st.columns(num_cols) for c in range(num_cols): if select_perview=='round_result': generate_round_table(data, row_cts, c, r) if select_perview=='dailyscore_metric': generate_dailyscore_metrics(data, row_cts, c, r) if select_perview=='metric_view': generate_round_metrics(data, row_cts, c, r) if select_perview=='dailyscore_chart': dailyscore_chart(data, row_cts, c, r, select_metric) if select_perview=='live_round_stake': generate_live_round_stake(data, row_cts, c, r) def score_overview(): if 'model_data' in st.session_state: data = st.session_state['model_data'].copy() data = data.drop_duplicates(['model', 'roundNumber'], keep='first') roundview = st.expander('round performance overview', expanded=True) with roundview: round_view(data, 'round_result') else: st.write('model data missing, please go to the Dowanload Score Data section to download model data first') def metric_overview(): if 'model_data' in st.session_state: data = st.session_state['model_data'].copy() st.subheader('Select Round Data') latest_round = int(data['roundNumber'].max()) earliest_round = int(data['roundNumber'].min()) if (latest_round - earliest_round) > 10: # suggest_round = int(latest_round - (latest_round - earliest_round) / 2) suggest_round = 280 else: suggest_round = earliest_round select_rounds = st.slider('select a round', earliest_round, latest_round, (suggest_round, latest_round - 1), 1) data=data.drop_duplicates(['model', 'roundNumber'], keep='first') data = data[(data['roundNumber'] >= select_rounds[0]) & (data['roundNumber'] <= select_rounds[1])].reset_index(drop=True) roundmetrics_data = get_roundmetric_data(data) min_count = int(roundmetrics_data['count'].min()) max_count = int(roundmetrics_data['count'].max()) if min_count < max_count: select_minround = st.sidebar.slider('miminum number of rounds', min_count, max_count, min_count, 1) else: select_minround = min_count roundmetrics_data = roundmetrics_data[roundmetrics_data['count'] >= select_minround].reset_index(drop=True) metricview_exp = st.expander('metric overview', expanded=True) dataview_exp = st.expander('full data view', expanded=False) with metricview_exp: round_view(roundmetrics_data, 'metric_view') with dataview_exp: st.write(roundmetrics_data) else: st.write('model data missing, please go to the Dowanload Score Data section to download model data first') def data_operation(): # top_lb, top_tp3m, top_tp1y, special_list = sidebar_data_picker() full_model_list = st.session_state['models'] latest_round = project_utils.latest_round models = [] benchmark_opt = st.sidebar.checkbox('download default models', value=True) if benchmark_opt: model_dict = default_model_picker() for k in model_dict.keys(): models += model_dict[k] models = models + model_fast_picker(full_model_list) if len(models)>0: model_selection = st.multiselect('select models', st.session_state['models'], default=models) suggest_min_round = 182 #latest_round-50 min_round, max_round = st.slider('select tournament rounds', 200, latest_round, (suggest_min_round, latest_round), 1) roundlist = [i for i in range(max_round, min_round-1, -1)] download = st.button('download data of selected models') st.sidebar.subheader('configuration') show_info=st.sidebar.checkbox('show background data', value=False) # update_numeraiti_data = st.sidebar.checkbox('update numerati data', value=True) # update_model_data = st.sidebar.checkbox('update model data', value=True) # update_model_data = model_df = get_saved_data() if download and len(model_selection)>0: # if update_model_data: with st.spinner('downloading model round results'): model_df = [] model_df = download_model_round_result(model_selection, roundlist, show_info) prjreload = st.sidebar.button('reload config') if prjreload: project_utils.reload_project() if len(model_df)>0: rename_dict = {'corrPercentile': 'corr_pct', 'correlation':'corr', 'corrWMetamodel':'corr_meta', 'mmcPercentile':'mmc_pct', 'tcPercentile':'tc_pct', 'fncV3Percentile':'fncV3_pct'} model_df.rename(columns=rename_dict, inplace=True) model_df['corrmmc'] = model_df['corr'] + model_df['mmc'] model_df['corrmmc_avg_pct'] = (model_df['corr_pct'] + model_df['mmc_pct'])/2 model_df['corrtc'] = model_df['corr'] + model_df['tc'] model_df['corrtc_avg_pct'] = (model_df['corr_pct'] + model_df['tc_pct'])/2 # st.write(model_df.head(5)) # ord_cols = ['model','corr', 'mmc', 'tc', 'corrmmc', 'corrtc', 'corr_pct', 'tc_pct', 'corrtc_avg_pct','corr_meta', 'mmc_pct', 'corrmmc_avg_pct', 'roundNumber', 'roundResolveTime'] ord_cols = ['model','corr', 'tc', 'corrtc', 'corr_pct', 'tc_pct', 'corrtc_avg_pct','corr_meta', 'fncV3', 'fncV3_pct','corrmmc_avg_pct', 'roundNumber', 'roundResolveTime', 'mmc', 'corrmmc','mmc_pct'] model_df = model_df[ord_cols] if project_config.SAVE_LOCAL_COPY: try: project_utils.pickle_data(project_config.MODEL_ROUND_RESULT_FILE, model_df) except: pass st.session_state['model_data'] = model_df if show_info: st.text('list of models being tracked') st.write(model_dict) try: dshape = st.session_state['model_data'].shape st.write(f'downloaded model result data shape is {dshape}') st.write(model_df) except: st.write('model data was not retrieved') if len(model_df)>0: get_performance_data_status(model_df) return None def get_saved_data(): res = [] if os.path.isfile(project_config.MODEL_ROUND_RESULT_FILE): res = project_utils.load_data(project_config.MODEL_ROUND_RESULT_FILE) st.session_state['model_data'] = res return res def get_performance_data_status(df): st.sidebar.subheader('model data summary') # latest_date = df['date'][0].strftime(project_config.DATETIME_FORMAT3) model_num = df['model'].nunique() round_num = df['roundNumber'].nunique() latest_round = df['roundNumber'].max() # st.sidebar.text(f'latest date: {latest_date}') st.sidebar.text(f'number of models: {model_num}') st.sidebar.text(f'number of rounds: {round_num}') st.sidebar.text(f'latest round: {latest_round}') return None def download_model_round_result(models, roundlist, show_info): model_df = [] model_dfs = [] my_bar = st.progress(0.0) my_bar.progress(0.0) percent_complete = 0.0 for i in range(len(models)): message = '' try: model_res = numerapi_utils.daily_submissions_performances_V3(models[i]) if len(model_res) > 0: cols = ['model'] + list(model_res[0].keys()) model_df = pd.DataFrame(model_res) model_df['model'] = models[i] model_df = model_df[cols] model_dfs.append(model_df) else: message = f'no result found for model {models[i]}' except Exception: # if show_info: # st.write(f'error while getting result for {models[i]}') except_msg = traceback.format_exc() message = f'error while getting result for {models[i]}: {except_msg}' if show_info and len(message) > 0: st.info(message) percent_complete += 1 / len(models) if i == len(models) - 1: percent_complete = 1.0 time.sleep(0.1) my_bar.progress(percent_complete) model_df = pd.concat(model_dfs, axis=0).sort_values(by=['roundNumber'], ascending=False).reset_index(drop=True) model_df['roundResolveTime'] = pd.to_datetime(model_df['roundResolveTime']) model_df['roundResolveTime'] = model_df['roundResolveTime'].dt.strftime(project_config.DATETIME_FORMAT3) model_df = model_df[model_df['roundNumber'].isin(roundlist)].reset_index(drop=True) return model_df def chart_pxline(data, x, y, color, hover_data=None, x_range=None): fig = px.line(data, x=x, y=y, color=color, hover_data=hover_data) fig.update_layout(plot_bgcolor='black', paper_bgcolor='black', font_color='white', height = max_height, margin=dict(l=0, r=10, t=20, b=20)) fig.update_xaxes(showgrid=False, range=x_range) fig.update_yaxes(gridcolor='grey') return fig def roundresult_chart(data, model_selection): round_data = data[data['model'].isin(model_selection)].drop_duplicates(['model', 'roundNumber'], keep='first').reset_index(drop=True) min_round = int(round_data['roundNumber'].min()) max_round = int(round_data['roundNumber'].max()) suggest_min_round = max_round - 20 if min_round == max_round: min_round = max_round - 20 min_selectround, max_selectround = st.slider('select plotting round range', min_round, max_round, (suggest_min_round, max_round), 1) select_metric = st.selectbox('Choose a metric', list(histtrend_opt.keys()), index=0, format_func=lambda x: histtrend_opt[x]) round_range = [min_selectround, max_selectround] round_list = [r for r in range(min_selectround, max_selectround + 1)] round_data = round_data[round_data['roundNumber'].isin(round_list)] mean_df = round_data.groupby(['model'])[select_metric].agg('mean').reset_index() mean_df[f'model avg.'] = mean_df['model'] + ': ' + mean_df[select_metric].round(5).astype(str) mean_df['mean'] = mean_df[select_metric] merge_cols = ['model', 'model avg.', 'mean'] round_data = round_data.merge(right=mean_df[merge_cols], on='model', how='left').sort_values(by=['mean','model', 'roundNumber'], ascending=False) fig = chart_pxline(round_data, 'roundNumber', y=select_metric, color='model avg.', hover_data=list(histtrend_opt.keys())+['roundResolveTime'],x_range=round_range) if fig is not None: st.plotly_chart(fig, use_container_width=True) def histtrend(): # default_models = ['yxbot'] # models = default_models.copy() data = st.session_state['model_data'].copy() models = data['model'].unique().tolist() model_selection = [] default_models = model_fast_picker(models) if len(models)>0: if len(default_models)==0: default_models = [models[0]] model_selection = st.sidebar.multiselect('select models for chart', models, default=default_models) if len(model_selection)>0: roundresult_chart(data, model_selection) # fig = px.line(df, x='roundNumber', y='corr', color='model', hover_data=['corr_pct']) # st.write(model_selection) else: if len(model_selection)==0: st.info('please select some models from the dropdown list') else: st.info('model result data file missing, or no model is selected') # st.write(models) def model_evaluation(): data = st.session_state['model_data'].copy() models = data['model'].unique().tolist() model_selection = [] default_models = model_fast_picker(models) mean_scale = [-0.05, 0.1] count_scale = [1, 50] sharpe_scale = [-0.2, 2] pct_scale = [0, 1] radar_scale = [0, 5] if len(models)>0: if len(default_models)==0: default_models = [models[0]] model_selection = st.sidebar.multiselect('select models for chart', models, default=default_models) if len(model_selection)>0: round_data = data[data['model'].isin(model_selection)].drop_duplicates(['model', 'roundNumber'],keep='first').reset_index(drop=True) min_round = int(round_data['roundNumber'].min()) max_round = int(round_data['roundNumber'].max()) suggest_min_round = max_round - 20 if min_round == max_round: min_round = max_round - 20 min_selectround, max_selectround = st.slider('select plotting round range', min_round, max_round, (suggest_min_round, max_round), 1) round_list = [r for r in range(min_selectround, max_selectround+1)] # defaultlist = ['corr_sharpe', 'tc_sharpe', 'corrtc_sharpe','corr_mean', 'tc_mean' 'corrtc_mean', 'corrtc_avg_pct','count'] defaultlist = ['corr_sharpe', 'tc_sharpe', 'corrtc_sharpe', 'corr_mean', 'tc_mean', 'corrtc_mean', 'corrtc_avg_pct_mean'] select_metrics = st.multiselect('Metric Selection', list(model_eval_opt.keys()), format_func=lambda x: model_eval_opt[x], default=defaultlist) round_data = round_data[round_data['roundNumber'].isin(round_list)].reset_index(drop=True) #'need normalised radar chart + tabular view here roundmetric_df = get_roundmetric_data(round_data).sort_values(by='corrtc_sharpe', ascending=False).reset_index(drop=True) radarmetric_df = roundmetric_df.copy(deep=True) for col in select_metrics: if 'mean' in col: use_scale = mean_scale if 'sharpe' in col: use_scale = sharpe_scale if 'pct' in col: use_scale = pct_scale if 'count' in col: use_scale = count_scale radarmetric_df[col] = radarmetric_df[col].apply(lambda x: project_utils.rescale(x, use_scale, radar_scale)) select_metrics_name = [model_eval_opt[i] for i in select_metrics] radarmetric_df.rename(columns=model_eval_opt, inplace=True) roundmetric_df.rename(columns=model_eval_opt, inplace=True) fig = go.Figure() for i in range(len(radarmetric_df)): fig.add_trace(go.Scatterpolar( r=radarmetric_df.loc[i, select_metrics_name].values, theta=select_metrics_name, fill='toself', name=radarmetric_df['model'].values[i] )) fig.update_polars( radialaxis=dict(visible=True, autorange=False, #type='linear', range=[0,5]) ) fig.update_layout(plot_bgcolor='black', paper_bgcolor='black', font_color='aliceblue', height=max_height+100, margin=dict(l=0, r=10, t=20, b=20), showlegend=True) st.plotly_chart(fig, use_container_width=True) st.text('Calculated Metrics') st.dataframe(roundmetric_df[['model'] + select_metrics_name], height=max_table_height) st.text('Rescaled Metrics on Chart') st.dataframe(radarmetric_df[['model'] + select_metrics_name], height=max_table_height) # st.write(select_metrics) def get_portfolio_overview(models, onlylatest=True): res_df = [] my_bar = st.progress(0.0) my_bar.progress(0.0) percent_complete = 0.0 for i in range(len(models)): m = models[i] try: if onlylatest: # mdf = numerapi_utils.get_model_history(m).loc[0:0] mdf = numerapi_utils.get_model_history_v3(m).loc[0:0] else: # mdf = numerapi_utils.get_model_history(m) mdf = numerapi_utils.get_model_history_v3(m) res_df.append(mdf) except: # st.info(f'no information for model {m} is available') pass percent_complete += 1 / len(models) if i == len(models) - 1: percent_complete = 1.0 time.sleep(0.1) my_bar.progress(percent_complete) try: res_df = pd.concat(res_df, axis=0) res_df['profitability'] = res_df['realised_pl']/(res_df['current_stake']-res_df['realised_pl']) cols = ['model', 'date', 'current_stake', 'floating_stake', 'floating_pl', 'realised_pl', 'profitability', 'roundNumber', 'roundResolved', 'payout'] # res_df['date'] = res_df['date'].dt.date if onlylatest: res_df = res_df.sort_values(by='floating_pl', ascending=False).reset_index(drop=True) return res_df[cols] else: return res_df[cols] except: return [] def get_stake_type(corr, mmc): if mmc>0: res = str(int(corr)) + 'xCORR ' + str(int(mmc)) +'xMMC' else: res = '1xCORR' return res @st.cache(suppress_st_warning=True) def get_stake_by_liverounds(models): latest_round_id = int(project_utils.get_latest_round_id()) roundlist = [i for i in range(latest_round_id, latest_round_id - 5, -1)] res = [] my_bar = st.progress(0.0) my_bar.progress(0.0) percent_complete = 0.0 percent_part = 0 for r in roundlist: for m in models: percent_complete += 1 / (len(models)*len(roundlist)) try: data = numerapi_utils.get_round_model_performance(r, m) # print(f'successfuly extract for model {m} in round {r}') res.append(data) except: pass # print(f'no result found for model {m} in round {r}') if percent_part == (len(models)*len(roundlist)) - 1: percent_complete = 1.0 time.sleep(0.1) my_bar.progress(percent_complete) percent_part +=1 res_df = pd.DataFrame.from_dict(res).fillna(0) res_df['payoutPending'] = res_df['payoutPending'].astype(np.float64) res_df['selectedStakeValue'] = res_df['selectedStakeValue'].astype(np.float64) res_df['stake_type'] = res_df.apply(lambda x: get_stake_type(x['corrMultiplier'], x['mmcMultiplier']),axis=1) rename_dict = {'selectedStakeValue': 'stake', 'payoutPending': 'payout', 'correlation':'corr'} res_df = res_df.rename(columns=rename_dict) col_ord = ['model', 'roundNumber', 'stake', 'payout', 'stake_type', 'corr', 'mmc'] return res_df[col_ord] def get_stake_graph(data): numfeats = ['current_stake', 'floating_stake', 'floating_pl', 'realised_pl'] stat1 = ['sum'] agg_rcp = [[['date'], numfeats, stat1]] select_opt = st.selectbox('Select Time Span', list(stakeoverview_plot_opt.keys()), index=1, format_func=lambda x: stakeoverview_plot_opt[x]) res = project_utils.groupby_agg_execution(agg_rcp, data)['date'] w5delta = datetime.timedelta(weeks=5) w13delta = datetime.timedelta(weeks=13) date_w5delta = res['date'].max() - w5delta date_w13delta = res['date'].max() - w13delta y1delta = datetime.timedelta(weeks=52) date_y1delta = res['date'].max() - y1delta rename_dict = {'date_current_stake_sum': 'total_stake', 'date_floating_stake_sum': 'floating_stake', 'date_floating_pl_sum': 'floating_pl', 'date_realised_pl_sum': 'realised_pl'} res = res.rename(columns=rename_dict) if select_opt == '1month': res = res[res['date']>date_w5delta] elif select_opt=='3month': res = res[res['date']>date_w13delta] elif select_opt=='1year': res = res[res['date']>date_y1delta] else: pass fig = make_subplots(specs=[[{"secondary_y": True}]]) fig.add_trace( go.Scatter(x=res['date'], y=res['floating_stake'], name="floating_stake"), secondary_y=False,) fig.add_trace(go.Scatter(x=res['date'], y=res['total_stake'], name="total_stake"),secondary_y=False,) fig.add_trace(go.Scatter(x=res['date'], y=res['realised_pl'], name="realised_pl"),secondary_y=True,) fig.update_layout(plot_bgcolor='black', paper_bgcolor='black', font_color='white') fig.update_xaxes(showgrid=False, range=None, nticks=30) fig.update_yaxes(gridcolor='grey', title_text="total stake/floating stake/realised PL", secondary_y=False) fig.update_yaxes(showgrid=False, title_text="realised PL", zeroline=False,secondary_y=True) st.plotly_chart(fig, use_container_width=True) # # def live_round_stakeview(data): # models = data # latest_round_id = int(project_utils.get_latest_round_id()) # roundlist = [i for i in range(latest_round_id, latest_round_id-4, -1] def check_session_state(key): # st.write(data) if key in st.session_state: return st.session_state[key] else: return None def stake_overview(): # data = st.session_state['models'].copy() models = st.session_state['models'].copy() model_selection = [] baseline_models = [] model_dict = default_model_picker() for k in model_dict.keys(): baseline_models += model_dict[k] default_models = model_fast_picker(models) if len(models)>0: # if len(default_models)==0: # default_models = baseline_models[0] model_selection = st.sidebar.multiselect('select models for chart', models, default=default_models) redownload_data = False # download = st.sidebar.button('download stake data') if len(model_selection) > 0: if 'stake_df' not in st.session_state: redownload_data = True else: if set(model_selection)!=st.session_state['stake_overview_models']: redownload_data = True else: ovdf = st.session_state['stake_df'] if redownload_data: ovdf = get_portfolio_overview(model_selection, onlylatest=False) st.session_state['stake_df'] = ovdf st.session_state['stake_overview_models'] = set(ovdf['model'].unique().tolist()) chartdf = ovdf.copy(deep=True) ovdf = ovdf.drop_duplicates('model', keep='first') ovdf = ovdf.sort_values(by='floating_pl', ascending=False).reset_index(drop=True) if len(ovdf) > 0: overview_cols = ['model', 'current_stake', 'floating_stake', 'floating_pl', 'realised_pl'] date_text = datetime.datetime.now().strftime(project_config.DATETIME_FORMAT3) ovdf.drop(['date'], axis=1, inplace=True) stake_cts = st.columns(2) pl_cts = st.columns(2) date_label = st.empty() get_stake_graph(chartdf) ovdf_exp = st.expander('stake data overview', expanded=True) with ovdf_exp: st.dataframe(ovdf[overview_cols], height=max_table_height) total_current_stake = round(ovdf['current_stake'].sum(), 3) total_floating_stake = round(ovdf['floating_stake'].sum(), 3) rpl = round(ovdf['realised_pl'].sum(), 3) fpl = round(ovdf['floating_pl'].sum(), 3) current_stake_str = f'### Stake Balance: {total_current_stake:0.3f} NMR' float_stake_str = f'### Floating Balance: {total_floating_stake:0.3f} NMR' if rpl >= 0: real_pl_color = 'green' else: real_pl_color = 'red' if fpl >= 0: float_pl_color = 'green' else: float_pl_color = 'red' real_pl_str = f'### Realised P/L: {rpl} NMR' float_pl_str = f'### Floating P/L: {fpl} NMR' stake_cts[0].markdown(current_stake_str, unsafe_allow_html=True) stake_cts[1].markdown(float_stake_str, unsafe_allow_html=True) pl_cts[0].markdown(real_pl_str, unsafe_allow_html=True) pl_cts[1].markdown(float_pl_str, unsafe_allow_html=True) date_label.subheader(f'Date: {date_text}') if st.sidebar.checkbox('show breakdown by live rounds', value=False): liveround_exp = st.expander('show breakdown by live rounds (requires extra data downloading)',expanded=True) with liveround_exp: stake_models = ovdf['model'].tolist() liveround_stake_df = get_stake_by_liverounds(stake_models) round_view(liveround_stake_df,'live_round_stake') if st.sidebar.checkbox('show resolved round summary', value=False): resolvedround_exp = st.expander('show resolved rounds summary for selected model group', expanded=True) with resolvedround_exp: get_roundresolve_history(chartdf) # st.write(chartdf) def get_roundresolve_history(data): resolved_rounds = data[data['roundResolved'] == True]['roundNumber'].unique().tolist() rsdf = data[data['roundResolved'] == True].reset_index(drop=True) rs_date = rsdf[['date', 'roundNumber']].drop_duplicates('roundNumber').reset_index(drop=True) numfeats = ['current_stake', 'payout'] stat1 = ['sum'] agg_rcp = [[['roundNumber'], numfeats, stat1]] res = project_utils.groupby_agg_execution(agg_rcp, rsdf)['roundNumber'].sort_values(by='roundNumber', ascending=False) res = res.merge(right=rs_date, on='roundNumber') rename_dict = {'roundNumber': 'Round', 'roundNumber_current_stake_sum': 'Total Stake', 'roundNumber_payout_sum': 'Round P/L', 'date': 'Resolved Date'} res.rename(columns=rename_dict, inplace=True) st.write(res) def app_setting(): pfm_exp = st.expander('Perormance Data Setting', expanded=True) with pfm_exp: pfm_default_model= st.checkbox('download data for default model', value=True) stake_exp = st.expander('stake overview data setting', expanded=True) if st.button('confirm settiong'): st.session_state['pfm_default_model'] = pfm_default_model def performance_overview(): # st.sidebar.subheader('Choose a Table View') select_app = st.sidebar.selectbox("", list(pfm_opt.keys()), index=0, format_func=lambda x: pfm_opt[x]) if select_app=='data_op': data_operation() if select_app=='liveround_view': score_overview() if select_app=='metric_view': metric_overview() if select_app=='historic_trend': histtrend() if select_app=='model_evaluation': model_evaluation() def show_content(): st.sidebar.header('Dashboard Selection') select_app = st.sidebar.selectbox("", list(app_opt.keys()), index=1, format_func=lambda x: app_opt[x]) if select_app=='performance_overview': performance_overview() if select_app=='stake_overview': stake_overview() if select_app=='app_setting': app_setting() # main body # various configuration setting app_opt = { 'performance_overview' : 'Performance Overview', 'stake_overview': 'Stake Overview', # 'app_setting':'' } pfm_opt = { 'data_op': 'Download Score Data', 'liveround_view': 'Round Overview', 'metric_view':'Metric Overview', 'historic_trend': 'Historic Trend', 'model_evaluation': 'Model Evaluation', } tbl_opt = { 'round_result':'Round Results', 'dailyscore_metric':'Daily Score Metrics', 'round_metric' : 'Round Metrics' } id_metric_opt = { 'id_corr_sharpe':'Daily Score corr sharpe', 'id_mmc_sharpe': 'Daily Score mmc sharpe', 'id_corrmmc_sharpe': 'Daily Score corrmmc sharpe', 'id_corr2mmc_sharpe': 'Daily Score corr2mmc sharpe', 'id_corrmmcpct_sharpe': 'Daily Score corrmmc avg pct sharpe', 'id_corr2mmcpct_sharpe': 'Daily Score corr2mmc avg pct sharpe', 'id_corrpct_sharpe':'Daily Score corr pct sharpe', 'id_mmcpct_sharpe': 'Daily Score mmc pct sharpe', } id_metric_score_dic = { 'id_corr_sharpe':'corr', 'id_mmc_sharpe': 'mmc', 'id_corrmmc_sharpe': 'corrmmc', 'id_corr2mmc_sharpe': 'corr2mmc', 'id_corrmmcpct_sharpe': 'cmavg_pct', 'id_corr2mmcpct_sharpe': 'c2mavg_pct', 'id_corrpct_sharpe':'corr_pct', 'id_mmcpct_sharpe': 'mmc_pct' } roundmetric_opt ={'corr':'Corr metrics', 'tc': 'TC metrics', 'corrtc': 'CorrTC metrics', 'fncV3': 'FNCV3 metrics', 'pct': 'Pecentage metrics', 'corrmmc' : 'CorrMMC metrics', 'mmc': 'MMC metrics' } histtrend_opt = { 'corr':'Correlation', 'mmc': 'MMC', 'tc' : 'TC', 'corr_pct': 'Correlation Percentile', 'tc_pct' : 'TC Percentile', 'mmc_pct':'MMC Percentile', 'corrmmc': 'Correlation+MMC', 'corrtc': 'Correlation+TC', 'corrtc_avg_pct': 'Correlation+TC Average Percentile', 'corrmmc_avg_pct': 'Correlation+MMC Average Percentile', } model_eval_opt = { 'corr_sharpe' : 'Correlation Sharpe', 'mmc_sharpe' : 'MMC Sharpe', 'tc_sharpe' : 'TC Sharpe', 'corrtc_sharpe': 'Correlation+TC Sharpe', 'corrmmc_sharpe' : 'Correlation+MMC Sharpe', 'corr_mean':'Avg. Correlation', 'tc_mean': 'Avg. TC', 'count': 'Number of Rounds', 'mmc_mean':'Avg. MMC', 'corrtc_mean': 'Avg. Correlation+TC', 'corrmmc_mean': 'Avg. Correlation+MMC', 'corr_pct_mean': 'Avg. Correlation Percentile', 'mmc_pct_mean': 'Avg. MMC Percentile', 'corrmmc_avg_pct_mean': 'Avg. Correlation+MMC Percentile', 'corrtc_avg_pct_mean': 'Avg. Correlation+TC Percentile', } stakeoverview_plot_opt = { '1month':'1 Month', '3month':'3 Months', '1year':'1 Year', 'all':'Display all available data' } def show_session_status_info(): # 'raw_performance_data' key1 = 'model_data' key2 = 'models' if check_session_state(key1) is None: st.write(f'{key1} is None') else: st.write(f'{key1} shape is {st.session_state[key1].shape}') if check_session_state(key2) is None: st.write(f'{key2} is None') else: st.write(f'{key2} list has {len(st.session_state[key2])} models') pass project_utils.reload_project() height_exp = st.sidebar.expander('Plots and tables setting', expanded=False) with height_exp: max_height = st.slider('Please choose the height for plots', 100, 1000, 400, 50) max_table_height = st.slider('Please choose the height for tables', 100, 1000, 500, 50) st.title('Numerai Dashboard') # key = 'pfm_default_model' # if check_session_state('pfm_default_model') is None: # st.write('set value') # st.session_state['pfm_default_model'] = True # else: # st.write('use set value') # # st.write(st.session_state) df = get_saved_data() if check_session_state('models') is None: with st.spinner('updating model list'): st.session_state['models'] = numerapi_utils.get_lb_models() # debug purpose only # show_session_status_info() show_content()