-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
132 lines (106 loc) · 5.51 KB
/
main.py
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
import pandas as pd
import numpy as np
import quantstats as qs
import streamlit as st
import matplotlib.pyplot as plt
from datetime import timedelta
def simulate_portfolios(start_date, end_date, num_securities, num_days, initial_cash, outliers_percentage):
trade_dates = pd.to_datetime(np.sort(np.random.choice(pd.date_range(start=start_date, end=end_date, periods=num_days + 1), num_days, replace=False)))
df = pd.DataFrame({'datetime' : trade_dates})
df['datetime' ] = pd.to_datetime(df['datetime'])
df['datetime_'] = df['datetime']
df = df.set_index('datetime')
outliers_percentage = outliers_percentage/100.0 # percentage of all returns are outliers
outliers_count = int(num_days*outliers_percentage)
allocated_cash = initial_cash/num_securities
for idx in range(0, num_securities):
percentage_changes = np.random.uniform(-0.05, 0.05, num_days).astype(float)
extreme_returns = np.random.uniform(-0.09, 0.1, outliers_count).astype(float)
outliers_date = df['datetime_'].sample(n=outliers_count).to_list()
df[f"pct_change_{idx}"] = percentage_changes
for outlier in list(zip(outliers_date, extreme_returns)):
outlier_dt = outlier[0]
outlier_ret = outlier[1]
df.loc[outlier_dt, f"pct_change_{idx}"] = outlier_ret
df[f"ret_path_{idx}" ] = df[f"pct_change_{idx}"].cumsum()
df[f"cash_path_{idx}" ] = (1+df[f"pct_change_{idx}"]).cumprod()*(allocated_cash)
sim_cols = [col_name for col_name in df.columns if col_name.startswith("cash")]
fig, ax = plt.subplots()
for col_name in sim_cols:
ax.plot(df[col_name])
fig.autofmt_xdate()
st.pyplot(fig)
df['raw_portfolio_cash_path'] = df[sim_cols].sum(axis=1)
col11, col12 = st.columns(2)
rebalancing_options = {
'3D': "3 Day rebalance",
'4D': "4 Day rebalance",
'5D': "5 Day rebalance",
'W': "Weekly rebalance",
'M': "Monthly rebalance",
}
with col11:
rebalancing_frequency = st.selectbox('Rebalancing period:', list(rebalancing_options.keys()),
format_func=lambda option: rebalancing_options[option],
index=list(rebalancing_options.keys()).index("4D"))
# for simplicity let's do equally weighted allocation
rebalanced_portfolio_values = []
rebalanced_dates = []
current_portfolio_value = initial_cash
for date, group in df.groupby(pd.Grouper(freq=rebalancing_frequency)):
group_df = group.copy()
allocated_cash = current_portfolio_value/num_securities # equally weighted
for idx in range(0, num_securities):
group_df[f"rebalanced_cash_path_{idx}" ] = (1+group_df[f"pct_change_{idx}"]).cumprod()*(allocated_cash)
rebalanced_cash_cols = [col_name for col_name in group_df.columns if col_name.startswith("rebalanced_cash_path")]
current_portfolio_value = group_df.iloc[-1][rebalanced_cash_cols].sum()
rebalanced_portfolio_values.append(current_portfolio_value)
rebalanced_dates.append(date)
pass
col21, col22 = st.columns(2)
with col21:
st.markdown(f"##### Portfolio with rebalancing")
rebalanced_df = pd.DataFrame(index=rebalanced_dates)
df.index = pd.to_datetime(df.index)
rebalanced_df['value'] = rebalanced_portfolio_values
rebalanced_df['rebalanced_pct_change'] = rebalanced_df['value'].pct_change()
rebalanced_sr = round(qs.stats.sharpe(returns=rebalanced_df['rebalanced_pct_change']),2)
max_dd = round(qs.stats.max_drawdown(rebalanced_df['rebalanced_pct_change']), 2)
fig, ax = plt.subplots()
ax.plot(rebalanced_df['value'])
fig.autofmt_xdate()
st.pyplot(fig)
st.text(f"Sharpe Ratio : {rebalanced_sr}")
st.text(f"Max DD : {max_dd}")
pass
with col22:
st.markdown("##### Portfolio with no rebalance")
fig, ax = plt.subplots()
ax.plot(df['raw_portfolio_cash_path'])
fig.autofmt_xdate()
st.pyplot(fig)
df['raw_portfolio_pct_change'] = df['raw_portfolio_cash_path'].pct_change()
raw_sr = round(qs.stats.sharpe(returns=df['raw_portfolio_pct_change']),2)
max_dd = round(qs.stats.max_drawdown(df['raw_portfolio_pct_change']), 2)
st.text(f"Sharpe Ratio : {raw_sr}")
st.text(f"Max DD : {max_dd}")
pass
pass
def main():
#st.set_page_config(layout="wide")
st.markdown("### Demonstration of Shannon's demon")
col01, col02, col03, col04, col05 = st.columns(5)
with col01:
start_date = st.date_input('Start Date', min_value=None, max_value=None, key=None)
with col02:
num_securities = st.number_input('Number of securities', min_value=2, max_value=50, step=1, value=50)
with col03:
num_days = st.number_input("Days", min_value=120, max_value=1500, step=30, value=1000)
with col04:
outliers_percentage = st.number_input("Outliers percentage", min_value=1, max_value=100, step=1, value=10)
with col05:
initial_cash = st.number_input("Initial cash $", min_value=10000, step=100, value=10000)
end_date = (start_date + timedelta(days=num_days)) if start_date else None
simulate_portfolios(start_date=start_date, end_date=end_date, num_securities=num_securities, num_days=num_days, initial_cash=initial_cash, outliers_percentage=outliers_percentage)
if __name__ == '__main__':
main()