File size: 9,424 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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)