QuantumLearner commited on
Commit
541b21f
·
verified ·
1 Parent(s): b741940

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1039 -0
app.py ADDED
@@ -0,0 +1,1039 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import yfinance as yf
2
+ import numpy as np
3
+ import pandas as pd
4
+ import matplotlib.pyplot as plt
5
+ import streamlit as st
6
+ from sklearn.linear_model import RANSACRegressor, LinearRegression
7
+ from scipy.stats import linregress
8
+ import plotly.graph_objects as go
9
+ from plotly.subplots import make_subplots
10
+
11
+ # Helper function to fetch stock data
12
+ def fetch_stock_data(ticker: str, start_date: str, end_date: str) -> pd.DataFrame:
13
+ return yf.download(ticker, start=start_date, end=end_date)
14
+
15
+ # Helper function to plot rolling volatility and volatility of volatility
16
+ def plot_rolling_volatility(data: pd.DataFrame) -> go.Figure:
17
+ data['Return'] = data['Close'].pct_change()
18
+ data['Rolling_Volatility'] = data['Return'].rolling(window=21).std() * np.sqrt(252)
19
+ data['Vol_of_Vol'] = data['Rolling_Volatility'].rolling(window=21).std()
20
+
21
+ fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
22
+ subplot_titles=('Close Price', 'Rolling Volatility', 'Volatility of Volatility'))
23
+
24
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1)
25
+ fig.add_trace(go.Scatter(x=data.index, y=data['Rolling_Volatility'], mode='lines', name='Rolling Volatility', line=dict(color='red')), row=2, col=1)
26
+ fig.add_trace(go.Scatter(x=data.index, y=data['Vol_of_Vol'], mode='lines', name='Volatility of Volatility', line=dict(color='blue')), row=3, col=1)
27
+
28
+ fig.update_layout(title='Rolling Volatility and Volatility of Volatility', xaxis_title='Date')
29
+ return fig
30
+
31
+ # Helper function to plot rolling Sharpe ratio
32
+ def plot_rolling_sharpe(data: pd.DataFrame) -> go.Figure:
33
+ data['Return'] = data['Close'].pct_change()
34
+ rolling_sharpe = np.sqrt(252) * data['Return'].rolling(252).mean() / data['Return'].rolling(252).std()
35
+
36
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
37
+ subplot_titles=('Close Price', 'Rolling Sharpe Ratio'))
38
+
39
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1)
40
+ fig.add_trace(go.Scatter(x=data.index, y=rolling_sharpe, mode='lines', name='Rolling Sharpe Ratio', line=dict(color='green')), row=2, col=1)
41
+ fig.add_hline(y=1, line=dict(color='red', dash='dash'), row=2, col=1)
42
+
43
+ fig.update_layout(title='Rolling 1-Year Sharpe Ratio with Stock Price', xaxis_title='Date')
44
+ return fig
45
+
46
+ # Helper function to plot rolling Treynor ratio
47
+ # Helper function to calculate and plot rolling Treynor Ratio
48
+ def plot_rolling_treynor(data: pd.DataFrame, market_data: pd.DataFrame, window_size: int = 252) -> go.Figure:
49
+ data['Return'] = data['Close'].pct_change()
50
+ market_data['Return'] = market_data['Close'].pct_change()
51
+
52
+ # Align indices
53
+ market_data = market_data.reindex(data.index, method='ffill')
54
+
55
+ covariance = data['Return'].rolling(window=window_size).cov(market_data['Return'])
56
+ variance = market_data['Return'].rolling(window=window_size).var()
57
+ rolling_beta = covariance / variance
58
+ avg_rolling_returns = data['Return'].rolling(window=window_size).mean()
59
+ risk_free_rate = 0
60
+ data['Rolling_Treynor_Ratio'] = (avg_rolling_returns - risk_free_rate) / rolling_beta
61
+
62
+ fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
63
+ subplot_titles=('Close Price', 'Benchmark Price', 'Rolling Treynor Ratio'))
64
+
65
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
66
+ fig.add_trace(go.Scatter(x=market_data.index, y=market_data['Close'], mode='lines', name='Benchmark Price', line=dict(color='blue')), row=2, col=1)
67
+ fig.add_trace(go.Scatter(x=data.index, y=data['Rolling_Treynor_Ratio'], mode='lines', name='Rolling Treynor Ratio', line=dict(color='red')), row=3, col=1)
68
+
69
+ fig.update_layout(title='Rolling 1-Year Treynor Ratio with Stock Price and Benchmark', xaxis_title='Date')
70
+ return fig
71
+
72
+ # Helper function to calculate and plot rolling beta
73
+ def plot_rolling_beta(data: pd.DataFrame, market_data: pd.DataFrame, window: int = 60) -> go.Figure:
74
+ data['Return'] = data['Close'].pct_change().dropna()
75
+ market_data['Market_Return'] = market_data['Close'].pct_change().dropna()
76
+
77
+ # Align dates and remove rows with missing data
78
+ aligned_data = pd.concat([data['Return'], market_data['Market_Return']], axis=1).dropna()
79
+
80
+ ransac = RANSACRegressor()
81
+ ols = LinearRegression()
82
+ rolling_beta_ransac = []
83
+ rolling_beta_ols = []
84
+
85
+ for i in range(len(aligned_data) - window):
86
+ X = aligned_data['Market_Return'].iloc[i:i+window].values.reshape(-1, 1)
87
+ y = aligned_data['Return'].iloc[i:i+window].values
88
+
89
+ ransac.fit(X, y)
90
+ beta_ransac = ransac.estimator_.coef_[0]
91
+ rolling_beta_ransac.append(beta_ransac)
92
+
93
+ ols.fit(X, y)
94
+ beta_ols = ols.coef_[0]
95
+ rolling_beta_ols.append(beta_ols)
96
+
97
+ # Convert lists to series with appropriate index
98
+ rolling_beta_ransac = pd.Series(rolling_beta_ransac, index=aligned_data.index[window:])
99
+ rolling_beta_ols = pd.Series(rolling_beta_ols, index=aligned_data.index[window:])
100
+
101
+ fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
102
+ subplot_titles=('Close Price', 'Benchmark Price', 'Rolling Beta (RANSAC & OLS)'))
103
+
104
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
105
+ fig.add_trace(go.Scatter(x=market_data.index, y=market_data['Close'], mode='lines', name='Benchmark Price', line=dict(color='blue')), row=2, col=1)
106
+ fig.add_trace(go.Scatter(x=rolling_beta_ransac.index, y=rolling_beta_ransac, mode='lines', name='Rolling Beta (RANSAC)', line=dict(color='red')), row=3, col=1)
107
+ fig.add_trace(go.Scatter(x=rolling_beta_ols.index, y=rolling_beta_ols, mode='lines', name='Rolling Beta (OLS)', line=dict(color='green')), row=3, col=1)
108
+
109
+ fig.update_layout(title='Rolling 1-Year Beta with Stock Price and Benchmark', xaxis_title='Date')
110
+ return fig
111
+
112
+ # Helper function to plot rolling Jensen's alpha
113
+ def plot_rolling_alpha(data: pd.DataFrame, market_data: pd.DataFrame) -> go.Figure:
114
+ data['Return'] = data['Close'].pct_change()
115
+ market_data['Return'] = market_data['Close'].pct_change()
116
+
117
+ window_size = 252
118
+ rolling_alpha = []
119
+
120
+ for i in range(len(data) - window_size):
121
+ window = data['Return'].iloc[i:i + window_size]
122
+ market_window = market_data['Return'].iloc[i:i + window_size]
123
+ beta = window.cov(market_window) / market_window.var()
124
+ expected_return = beta * market_window.mean()
125
+ alpha = window.mean() - expected_return
126
+ rolling_alpha.append(alpha)
127
+
128
+ rolling_alpha = pd.Series(rolling_alpha, index=data.index[window_size:])
129
+
130
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
131
+ subplot_titles=('Close Price', 'Rolling Jensen\'s Alpha'))
132
+
133
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
134
+ fig.add_trace(go.Scatter(x=rolling_alpha.index, y=rolling_alpha, mode='lines', name='Rolling Jensen\'s Alpha', line=dict(color='green')), row=2, col=1)
135
+
136
+ fig.update_layout(title='Rolling 1-Year Jensen\'s Alpha with Stock Price', xaxis_title='Date')
137
+ return fig
138
+
139
+ # Helper function to plot rolling Value at Risk (VaR)
140
+ def plot_rolling_var(data: pd.DataFrame) -> go.Figure:
141
+ data['Return'] = data['Close'].pct_change()
142
+ rolling_var_90 = data['Return'].rolling(252).quantile(0.10).dropna()
143
+ rolling_var_95 = data['Return'].rolling(252).quantile(0.05).dropna()
144
+ rolling_var_97 = data['Return'].rolling(252).quantile(0.03).dropna()
145
+
146
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
147
+ subplot_titles=('Close Price', 'Rolling VaR'))
148
+
149
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
150
+ fig.add_trace(go.Scatter(x=rolling_var_90.index, y=rolling_var_90, mode='lines', name='Rolling VaR 90%', line=dict(color='cyan')), row=2, col=1)
151
+ fig.add_trace(go.Scatter(x=rolling_var_95.index, y=rolling_var_95, mode='lines', name='Rolling VaR 95%', line=dict(color='blue')), row=2, col=1)
152
+ fig.add_trace(go.Scatter(x=rolling_var_97.index, y=rolling_var_97, mode='lines', name='Rolling VaR 97%', line=dict(color='purple')), row=2, col=1)
153
+
154
+ fig.update_layout(title='Rolling 1-Year VaR with Stock Price', xaxis_title='Date')
155
+ return fig
156
+
157
+ # Helper function to plot rolling Conditional VaR (CVaR)
158
+ def plot_rolling_cvar(data: pd.DataFrame) -> go.Figure:
159
+ data['Return'] = data['Close'].pct_change()
160
+
161
+ def conditional_var(x, alpha=0.05):
162
+ var = np.percentile(x, alpha * 100)
163
+ return np.mean(x[x < var])
164
+
165
+ rolling_cvar_95 = data['Return'].rolling(252).apply(conditional_var, raw=True).dropna()
166
+ rolling_cvar_90 = data['Return'].rolling(252).apply(lambda x: conditional_var(x, alpha=0.1), raw=True).dropna()
167
+ rolling_cvar_97 = data['Return'].rolling(252).apply(lambda x: conditional_var(x, alpha=0.03), raw=True).dropna()
168
+
169
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
170
+ subplot_titles=('Close Price', 'Rolling CVaR'))
171
+
172
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
173
+ fig.add_trace(go.Scatter(x=rolling_cvar_95.index, y=rolling_cvar_95, mode='lines', name='Rolling CVaR 95%', line=dict(color='blue')), row=2, col=1)
174
+ fig.add_trace(go.Scatter(x=rolling_cvar_90.index, y=rolling_cvar_90, mode='lines', name='Rolling CVaR 90%', line=dict(color='green')), row=2, col=1)
175
+ fig.add_trace(go.Scatter(x=rolling_cvar_97.index, y=rolling_cvar_97, mode='lines', name='Rolling CVaR 97%', line=dict(color='orange')), row=2, col=1)
176
+
177
+ fig.update_layout(title='Rolling 1-Year CVaR with Stock Price', xaxis_title='Date')
178
+ return fig
179
+
180
+ # Helper function to plot rolling Tail Ratio
181
+ def plot_rolling_tail_ratio(data: pd.DataFrame) -> go.Figure:
182
+ data['Return'] = data['Close'].pct_change()
183
+ tail_ratio = data['Return'].rolling(252).apply(lambda x: np.abs(np.percentile(x, 95)) / np.abs(np.percentile(x, 5))).dropna()
184
+
185
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
186
+ subplot_titles=('Close Price', 'Rolling Tail Ratio'))
187
+
188
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
189
+ fig.add_trace(go.Scatter(x=tail_ratio.index, y=tail_ratio, mode='lines', name='Tail Ratio', line=dict(color='purple')), row=2, col=1)
190
+ fig.add_hline(y=1, line=dict(color='red', dash='dash'), row=2, col=1)
191
+
192
+ fig.update_layout(title='Rolling 1-Year Tail Ratio with Stock Price', xaxis_title='Date')
193
+ return fig
194
+
195
+ # Helper function to plot rolling Omega Ratio
196
+ def plot_rolling_omega(data: pd.DataFrame) -> go.Figure:
197
+ data['Return'] = data['Close'].pct_change()
198
+ MAR = 0 # Minimum Acceptable Return
199
+ omega_ratio = data['Return'].rolling(252).apply(lambda x: np.sum(x[x > MAR] - MAR) / np.sum(MAR - x[x < MAR])).dropna()
200
+
201
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
202
+ subplot_titles=('Close Price', 'Rolling Omega Ratio'))
203
+
204
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
205
+ fig.add_trace(go.Scatter(x=omega_ratio.index, y=omega_ratio, mode='lines', name='Omega Ratio', line=dict(color='green')), row=2, col=1)
206
+ fig.add_hline(y=1, line=dict(color='red', dash='dash'), row=2, col=1)
207
+
208
+ fig.update_layout(title='Rolling 1-Year Omega Ratio with Stock Price', xaxis_title='Date')
209
+ return fig
210
+
211
+ # Helper function to plot rolling Sortino Ratio
212
+ def plot_rolling_sortino(data: pd.DataFrame) -> go.Figure:
213
+ data['Return'] = data['Close'].pct_change()
214
+ MAR = 0 # Minimum Acceptable Return
215
+ sortino_ratio = data['Return'].rolling(252).apply(lambda x: np.mean(x - MAR) / np.sqrt(np.mean(np.minimum(0, x - MAR) ** 2))).dropna()
216
+
217
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
218
+ subplot_titles=('Close Price', 'Rolling Sortino Ratio'))
219
+
220
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
221
+ fig.add_trace(go.Scatter(x=sortino_ratio.index, y=sortino_ratio, mode='lines', name='Sortino Ratio', line=dict(color='green')), row=2, col=1)
222
+
223
+ fig.update_layout(title='Rolling 1-Year Sortino Ratio with Stock Price', xaxis_title='Date')
224
+ return fig
225
+
226
+ # Helper function to plot rolling Calmar Ratio
227
+ def plot_rolling_calmar(data: pd.DataFrame) -> go.Figure:
228
+ data['Return'] = data['Close'].pct_change()
229
+ calmar_ratio = data['Return'].rolling(252).apply(lambda x: (1 + x).cumprod()[-1] ** (252.0 / len(x)) / np.abs(np.min((1 + x).cumprod() / (1 + x).cumprod().cummax()) - 1)).dropna()
230
+
231
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
232
+ subplot_titles=('Close Price', 'Rolling Calmar Ratio'))
233
+
234
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
235
+ fig.add_trace(go.Scatter(x=calmar_ratio.index, y=calmar_ratio, mode='lines', name='Calmar Ratio', line=dict(color='green')), row=2, col=1)
236
+
237
+ fig.update_layout(title='Rolling 1-Year Calmar Ratio with Stock Price', xaxis_title='Date')
238
+ return fig
239
+
240
+ # Helper function to plot rolling stability
241
+ def plot_rolling_stability(data: pd.DataFrame) -> go.Figure:
242
+ data['Return'] = data['Close'].pct_change()
243
+ stability = data['Return'].rolling(252).apply(lambda x: np.std(np.log1p(x).cumsum() - linregress(np.arange(len(x)), np.log1p(x).cumsum()).intercept - linregress(np.arange(len(x)), np.log1p(x).cumsum()).slope * np.arange(len(x)))).dropna()
244
+
245
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
246
+ subplot_titles=('Close Price', 'Rolling Stability'))
247
+
248
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
249
+ fig.add_trace(go.Scatter(x=stability.index, y=stability, mode='lines', name='Stability', line=dict(color='purple')), row=2, col=1)
250
+
251
+ fig.update_layout(title='Rolling 1-Year Stability of Returns with Stock Price', xaxis_title='Date')
252
+ return fig
253
+
254
+ # Helper function to plot rolling maximum drawdown
255
+ def plot_rolling_drawdown(data: pd.DataFrame) -> go.Figure:
256
+ data['Return'] = data['Close'].pct_change()
257
+ rolling_cumulative = (1 + data['Return']).cumprod()
258
+ rolling_max = rolling_cumulative.rolling(252, min_periods=1).max()
259
+ rolling_drawdown = (rolling_cumulative - rolling_max) / rolling_max
260
+
261
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
262
+ subplot_titles=('Close Price', 'Rolling Maximum Drawdown'))
263
+
264
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
265
+ fig.add_trace(go.Scatter(x=rolling_drawdown.index, y=rolling_drawdown, mode='lines', name='Maximum Drawdown', line=dict(color='red')), row=2, col=1)
266
+
267
+ fig.update_layout(title='Rolling 1-Year Maximum Drawdown with Stock Price', xaxis_title='Date')
268
+ return fig
269
+
270
+ # Helper function to calculate and plot rolling capture ratios
271
+ def plot_rolling_capture(data: pd.DataFrame, market_data: pd.DataFrame, window_size: int = 252) -> go.Figure:
272
+ data['Return'] = data['Close'].pct_change()
273
+ market_data['Return'] = market_data['Close'].pct_change()
274
+
275
+ # Align indices
276
+ market_data = market_data.reindex(data.index, method='ffill')
277
+
278
+ def calculate_capture(stock_returns, market_returns, is_upside=True):
279
+ if is_upside:
280
+ relevant_returns = stock_returns[market_returns > 0]
281
+ relevant_market_returns = market_returns[market_returns > 0]
282
+ else:
283
+ relevant_returns = stock_returns[market_returns < 0]
284
+ relevant_market_returns = market_returns[market_returns < 0]
285
+ return relevant_returns.sum() / relevant_market_returns.sum()
286
+
287
+ def compute_rolling_captures(stock_returns, market_returns, window):
288
+ upside_captures = []
289
+ downside_captures = []
290
+
291
+ for idx in range(len(stock_returns) - window + 1):
292
+ current_window_stock = stock_returns.iloc[idx: idx + window]
293
+ current_window_market = market_returns.iloc[idx: idx + window]
294
+
295
+ if len(current_window_market[current_window_market > 0]) == 0 or len(current_window_market[current_window_market < 0]) == 0:
296
+ upside = np.nan
297
+ downside = np.nan
298
+ else:
299
+ upside = calculate_capture(current_window_stock, current_window_market, is_upside=True)
300
+ downside = calculate_capture(current_window_stock, current_window_market, is_upside=False)
301
+
302
+ upside_captures.append(upside)
303
+ downside_captures.append(downside)
304
+
305
+ # Padding the initial values with NaNs to make the length equal to original data
306
+ nan_padding = [np.nan] * (window - 1)
307
+ upside_captures = nan_padding + upside_captures
308
+ downside_captures = nan_padding + downside_captures
309
+
310
+ return upside_captures, downside_captures
311
+
312
+ data['rolling_upside_capture'], data['rolling_downside_capture'] = compute_rolling_captures(
313
+ data['Return'], market_data['Return'], window_size
314
+ )
315
+
316
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
317
+ subplot_titles=('Close Price', 'Rolling Capture Ratios'))
318
+
319
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
320
+ fig.add_trace(go.Scatter(x=data.index, y=data['rolling_upside_capture'], mode='lines', name='Rolling Upside Capture', line=dict(color='green')), row=2, col=1)
321
+ fig.add_trace(go.Scatter(x=data.index, y=data['rolling_downside_capture'], mode='lines', name='Rolling Downside Capture', line=dict(color='red')), row=2, col=1)
322
+
323
+ fig.update_layout(title='Rolling 1-Year Capture Ratios with Stock Price', xaxis_title='Date')
324
+ return fig
325
+
326
+
327
+ # Helper function to plot rolling Pain Index
328
+ def plot_rolling_pain_index(data: pd.DataFrame) -> go.Figure:
329
+ data['Return'] = data['Close'].pct_change()
330
+ cumulative_return = (1 + data['Return']).cumprod()
331
+ running_max = cumulative_return.cummax()
332
+ drawdown = (cumulative_return - running_max) / running_max
333
+ pain_index = drawdown.rolling(252).apply(lambda x: np.mean(x[x < 0])).dropna()
334
+
335
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
336
+ subplot_titles=('Close Price', 'Rolling Pain Index'))
337
+
338
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
339
+ fig.add_trace(go.Scatter(x=pain_index.index, y=pain_index, mode='lines', name='Pain Index', line=dict(color='red')), row=2, col=1)
340
+
341
+ fig.update_layout(title='Rolling 1-Year Pain Index with Stock Price', xaxis_title='Date')
342
+ return fig
343
+
344
+ # Streamlit app
345
+ st.set_page_config(page_title="Technical Analysis", layout="wide")
346
+ st.title('Technical Analysis Indicators')
347
+
348
+ # Sidebar for method selection
349
+ selected = st.sidebar.radio("Select Indicator", ["Rolling Volatility", "Rolling Sharpe Ratio", "Rolling Treynor Ratio", "Rolling Beta", "Rolling Jensen's Alpha",
350
+ "Rolling Value at Risk", "Rolling Conditional VaR", "Rolling Tail Ratio", "Rolling Omega Ratio",
351
+ "Rolling Sortino Ratio", "Rolling Calmar Ratio", "Rolling Stability", "Rolling Maximum Drawdown",
352
+ "Rolling Capture Ratios", "Rolling Pain Index"])
353
+
354
+ # Sidebar for input parameters
355
+ st.sidebar.header("Input Parameters")
356
+ ticker = st.sidebar.text_input('Enter Stock Ticker', 'VOW.DE')
357
+ start_date = st.sidebar.date_input('Start Date', pd.to_datetime('2010-01-01'))
358
+ end_date = st.sidebar.date_input('End Date', pd.to_datetime('2024-12-31'))
359
+
360
+ # Fetch data
361
+ if 'data' not in st.session_state or st.sidebar.button('Fetch Data'):
362
+ st.session_state.data = fetch_stock_data(ticker, start_date, end_date)
363
+
364
+ data = st.session_state.data
365
+
366
+ # Additional input for Rolling Treynor Ratio, Rolling Beta, Rolling Jensen's Alpha, Rolling Capture Ratios
367
+ if selected in ["Rolling Treynor Ratio", "Rolling Beta", "Rolling Jensen's Alpha", "Rolling Capture Ratios"]:
368
+ benchmark_ticker = st.sidebar.text_input('Enter Benchmark Ticker', '^GSPC')
369
+ if 'market_data' not in st.session_state or st.sidebar.button('Fetch Market Data'):
370
+ st.session_state.market_data = fetch_stock_data(benchmark_ticker, start_date, end_date)
371
+
372
+ market_data = st.session_state.market_data
373
+
374
+ # Display results based on the selected method
375
+ if selected == "Rolling Volatility":
376
+ st.markdown("""
377
+ ### Rolling Volatility
378
+
379
+ This method calculates the rolling volatility and the volatility of volatility.
380
+
381
+ **Methodology:**
382
+
383
+ 1. **Calculate Returns:**
384
+ - Compute the daily returns of the stock:
385
+ """)
386
+ st.latex(r'''
387
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
388
+ ''')
389
+ st.markdown("""
390
+ where \( P_t \) is the closing price at time \( t \).
391
+
392
+ 2. **Calculate Rolling Volatility:**
393
+ - Compute the rolling standard deviation of the returns over a specified window (e.g., 21 days) and annualize it:
394
+ """)
395
+ st.latex(r'''
396
+ \text{Rolling Volatility}_t = \sqrt{252} \times \text{std}(\text{Return}_{t-n:t})
397
+ ''')
398
+ st.markdown("""
399
+ where \( n \) is the window size (e.g., 21 days).
400
+
401
+ 3. **Calculate Volatility of Volatility:**
402
+ - Compute the rolling standard deviation of the rolling volatility over a specified window (e.g., 21 days):
403
+ """)
404
+ st.latex(r'''
405
+ \text{Volatility of Volatility}_t = \text{std}(\text{Rolling Volatility}_{t-n:t})
406
+ ''')
407
+
408
+ st.markdown("""
409
+ **How to use:**
410
+ 1. Enter the stock ticker, start date, and end date.
411
+ 2. Click 'Fetch Data' to load the stock data.
412
+ 3. The chart will display the rolling volatility and the volatility of volatility.
413
+ """)
414
+
415
+ fig = plot_rolling_volatility(data)
416
+ st.plotly_chart(fig)
417
+
418
+ elif selected == "Rolling Sharpe Ratio":
419
+ st.markdown("""
420
+ ### Rolling Sharpe Ratio
421
+
422
+ This method calculates the rolling 1-year Sharpe Ratio.
423
+
424
+ **Methodology:**
425
+
426
+ 1. **Calculate Returns:**
427
+ - Compute the daily returns of the stock:
428
+ """)
429
+ st.latex(r'''
430
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
431
+ ''')
432
+ st.markdown("""
433
+ where \( P_t \) is the closing price at time \( t \).
434
+
435
+ 2. **Calculate Rolling Average Return:**
436
+ - Compute the rolling mean of the returns over a 1-year period (252 trading days):
437
+ """)
438
+ st.latex(r'''
439
+ \text{Rolling Average Return}_t = \text{mean}(\text{Return}_{t-252:t})
440
+ ''')
441
+ st.markdown("""
442
+
443
+ 3. **Calculate Rolling Standard Deviation:**
444
+ - Compute the rolling standard deviation of the returns over a 1-year period:
445
+ """)
446
+ st.latex(r'''
447
+ \text{Rolling Std Dev}_t = \text{std}(\text{Return}_{t-252:t})
448
+ ''')
449
+ st.markdown("""
450
+
451
+ 4. **Calculate Rolling Sharpe Ratio:**
452
+ - Annualize the Sharpe Ratio by multiplying by the square root of 252:
453
+ """)
454
+ st.latex(r'''
455
+ \text{Rolling Sharpe Ratio}_t = \frac{\text{Rolling Average Return}_t}{\text{Rolling Std Dev}_t} \times \sqrt{252}
456
+ ''')
457
+ st.markdown("""
458
+
459
+ **How to use:**
460
+ 1. Enter the stock ticker, start date, and end date.
461
+ 2. Click 'Fetch Data' to load the stock data.
462
+ 3. The chart will display the rolling Sharpe Ratio.
463
+ """)
464
+
465
+ fig = plot_rolling_sharpe(data)
466
+ st.plotly_chart(fig)
467
+
468
+
469
+ elif selected == "Rolling Treynor Ratio":
470
+ st.markdown("""
471
+ ### Rolling Treynor Ratio
472
+
473
+ This method calculates the rolling 1-year Treynor Ratio.
474
+
475
+ **Methodology:**
476
+
477
+ 1. **Calculate Returns:**
478
+ - Compute the daily returns of the stock and the benchmark:
479
+ """)
480
+ st.latex(r'''
481
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
482
+ ''')
483
+ st.markdown("""
484
+ where \( P_t \) is the closing price at time \( t \).
485
+
486
+ 2. **Calculate Beta:**
487
+ - Compute the rolling covariance between the stock and benchmark returns, and divide by the rolling variance of the benchmark returns to get the rolling beta:
488
+ """)
489
+ st.latex(r'''
490
+ \beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
491
+ ''')
492
+ st.markdown("""
493
+ where the rolling window size is typically 252 days for 1-year calculations.
494
+
495
+ 3. **Calculate Average Rolling Returns:**
496
+ - Compute the rolling mean of the stock returns over the same window:
497
+ """)
498
+ st.latex(r'''
499
+ \text{Average Rolling Return}_t = \frac{1}{n} \sum_{i=t-n+1}^{t} \text{Return}_{\text{stock}, i}
500
+ ''')
501
+ st.markdown("""
502
+ where \( n \) is the window size (e.g., 252 days).
503
+
504
+ 4. **Calculate Treynor Ratio:**
505
+ - Compute the Treynor Ratio using the risk-free rate \( R_f \) (assumed to be 0 here for simplicity):
506
+ """)
507
+ st.latex(r'''
508
+ \text{Treynor Ratio}_t = \frac{\text{Average Rolling Return}_t - R_f}{\beta_t}
509
+ ''')
510
+ st.markdown("""
511
+ where \( R_f \) is the risk-free rate.
512
+
513
+ **How to use:**
514
+ 1. Enter the stock ticker, start date, and end date.
515
+ 2. Enter the benchmark ticker.
516
+ 3. Click 'Fetch Data' to load the stock and benchmark data.
517
+ 4. The chart will display the rolling Treynor Ratio.
518
+ """)
519
+
520
+ fig = plot_rolling_treynor(data, market_data)
521
+ st.plotly_chart(fig)
522
+
523
+ elif selected == "Rolling Beta":
524
+ st.markdown("""
525
+ ### Rolling Beta
526
+
527
+ This method calculates the rolling beta of a stock's returns against a benchmark using RANSAC and OLS methods.
528
+
529
+ **Methodology:**
530
+
531
+ 1. **Calculate Returns:**
532
+ - Compute the daily returns of the stock and the benchmark:
533
+ """)
534
+ st.latex(r'''
535
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
536
+ ''')
537
+ st.markdown("""
538
+ where \( P_t \) is the closing price at time \( t \).
539
+
540
+ 2. **Calculate Rolling Beta using OLS:**
541
+ - Perform a linear regression of the stock returns against the benchmark returns over a specified window (e.g., 60 days):
542
+ """)
543
+ st.latex(r'''
544
+ \beta_{OLS} = \frac{\text{Cov}(\text{Return}_{\text{stock}}, \text{Return}_{\text{benchmark}})}{\text{Var}(\text{Return}_{\text{benchmark}})}
545
+ ''')
546
+ st.markdown("""
547
+ where the rolling window size is typically 60 days.
548
+
549
+ 3. **Calculate Rolling Beta using RANSAC:**
550
+ - Use the RANSAC algorithm to robustly estimate the beta, reducing the influence of outliers:
551
+ """)
552
+ st.latex(r'''
553
+ \beta_{RANSAC} = \text{RANSAC}(\text{Return}_{\text{benchmark}}, \text{Return}_{\text{stock}})
554
+ ''')
555
+ st.markdown("""
556
+ where the RANSAC algorithm iteratively fits the model and removes outliers.
557
+
558
+ **How to use:**
559
+ 1. Enter the stock ticker, start date, and end date.
560
+ 2. Enter the benchmark ticker.
561
+ 3. Click 'Fetch Data' to load the stock and benchmark data.
562
+ 4. The chart will display the rolling beta calculated using both OLS and RANSAC methods.
563
+ """)
564
+
565
+ fig = plot_rolling_beta(data, market_data)
566
+ st.plotly_chart(fig)
567
+
568
+ elif selected == "Rolling Jensen's Alpha":
569
+ st.markdown("""
570
+ ### Rolling Jensen's Alpha
571
+
572
+ This method calculates the rolling 1-year Jensen's Alpha.
573
+
574
+ **Methodology:**
575
+
576
+ 1. **Calculate Returns:**
577
+ - Compute the daily returns of the stock and the benchmark:
578
+ """)
579
+ st.latex(r'''
580
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
581
+ ''')
582
+ st.markdown("""
583
+ where \( P_t \) is the closing price at time \( t \).
584
+
585
+ 2. **Calculate Beta:**
586
+ - Compute the rolling beta of the stock returns against the benchmark returns over a specified window (e.g., 252 days):
587
+ """)
588
+ st.latex(r'''
589
+ \beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
590
+ ''')
591
+ st.markdown("""
592
+ where the rolling window size is typically 252 days for 1-year calculations.
593
+
594
+ 3. **Calculate Expected Return:**
595
+ - Compute the expected return of the stock based on the CAPM model:
596
+ """)
597
+ st.latex(r'''
598
+ \text{Expected Return}_t = R_f + \beta_t (\text{Return}_{\text{benchmark}} - R_f)
599
+ ''')
600
+ st.markdown("""
601
+ where \( R_f \) is the risk-free rate (assumed to be 0 here for simplicity).
602
+
603
+ 4. **Calculate Jensen's Alpha:**
604
+ - Compute the Jensen's Alpha as the difference between the actual return and the expected return:
605
+ """)
606
+ st.latex(r'''
607
+ \alpha_t = \text{Return}_{\text{stock}, t} - \text{Expected Return}_t
608
+ ''')
609
+ st.markdown("""
610
+ where \( \alpha_t \) is Jensen's Alpha at time \( t \).
611
+
612
+ **How to use:**
613
+ 1. Enter the stock ticker, start date, and end date.
614
+ 2. Enter the benchmark ticker.
615
+ 3. Click 'Fetch Data' to load the stock and benchmark data.
616
+ 4. The chart will display the rolling Jensen's Alpha.
617
+ """)
618
+
619
+ fig = plot_rolling_alpha(data, market_data)
620
+ st.plotly_chart(fig)
621
+
622
+ elif selected == "Rolling Value at Risk":
623
+ st.markdown("""
624
+ ### Rolling Value at Risk (VaR)
625
+
626
+ This method calculates the rolling 1-year Value at Risk (VaR) at different confidence levels.
627
+
628
+ **Methodology:**
629
+
630
+ 1. **Calculate Returns:**
631
+ - Compute the daily returns of the stock:
632
+ """)
633
+ st.latex(r'''
634
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
635
+ ''')
636
+ st.markdown("""
637
+ where \( P_t \) is the closing price at time \( t \).
638
+
639
+ 2. **Calculate Rolling VaR:**
640
+ - Compute the rolling quantile of the returns over a specified window (e.g., 252 days) for different confidence levels:
641
+ """)
642
+ st.latex(r'''
643
+ \text{VaR}_{\alpha, t} = \text{Quantile}_{\alpha}(\text{Return}_{t-n:t})
644
+ ''')
645
+ st.markdown("""
646
+ where \( \alpha \) is the confidence level (e.g., 0.05 for 95% VaR) and \( n \) is the window size (e.g., 252 days).
647
+
648
+ **How to use:**
649
+ 1. Enter the stock ticker, start date, and end date.
650
+ 2. Click 'Fetch Data' to load the stock data.
651
+ 3. The chart will display the rolling VaR at different confidence levels (e.g., 90%, 95%, 97%).
652
+
653
+ **Results:**
654
+ The chart shows the close prices along with the rolling VaR at different confidence levels over time.
655
+ """)
656
+
657
+ fig = plot_rolling_var(data)
658
+ st.plotly_chart(fig)
659
+
660
+ elif selected == "Rolling Conditional VaR":
661
+ st.markdown("""
662
+ ### Rolling Conditional Value at Risk (CVaR)
663
+
664
+ This method calculates the rolling 1-year Conditional Value at Risk (CVaR) at different confidence levels.
665
+
666
+ **Methodology:**
667
+
668
+ 1. **Calculate Returns:**
669
+ - Compute the daily returns of the stock:
670
+ """)
671
+ st.latex(r'''
672
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
673
+ ''')
674
+ st.markdown("""
675
+ where \( P_t \) is the closing price at time \( t \).
676
+
677
+ 2. **Calculate Rolling VaR:**
678
+ - Compute the rolling quantile of the returns over a specified window (e.g., 252 days) for different confidence levels:
679
+ """)
680
+ st.latex(r'''
681
+ \text{VaR}_{\alpha, t} = \text{Quantile}_{\alpha}(\text{Return}_{t-n:t})
682
+ ''')
683
+ st.markdown("""
684
+ where \( \alpha \) is the confidence level (e.g., 0.05 for 95% VaR) and \( n \) is the window size (e.g., 252 days).
685
+
686
+ 3. **Calculate Rolling CVaR:**
687
+ - Compute the average of the returns that are below the VaR threshold:
688
+ """)
689
+ st.latex(r'''
690
+ \text{CVaR}_{\alpha, t} = \frac{1}{n} \sum_{i=1}^{n} \text{Return}_{i} \text{ where } \text{Return}_{i} < \text{VaR}_{\alpha, t}
691
+ ''')
692
+ st.markdown("""
693
+ where \( n \) is the number of returns below the VaR threshold.
694
+
695
+ **How to use:**
696
+ 1. Enter the stock ticker, start date, and end date.
697
+ 2. Click 'Fetch Data' to load the stock data.
698
+ 3. The chart will display the rolling CVaR at different confidence levels (e.g., 90%, 95%, 97%).
699
+
700
+ **Results:**
701
+ The chart shows the close prices along with the rolling CVaR at different confidence levels over time.
702
+ """)
703
+
704
+ fig = plot_rolling_cvar(data)
705
+ st.plotly_chart(fig)
706
+
707
+ elif selected == "Rolling Tail Ratio":
708
+ st.markdown("""
709
+ ### Rolling Tail Ratio
710
+
711
+ This method calculates the rolling 1-year Tail Ratio.
712
+
713
+ **Methodology:**
714
+
715
+ 1. **Calculate Returns:**
716
+ - Compute the daily returns of the stock:
717
+ """)
718
+ st.latex(r'''
719
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
720
+ ''')
721
+ st.markdown("""
722
+ where \( P_t \) is the closing price at time \( t \).
723
+
724
+ 2. **Calculate Tail Ratio:**
725
+ - Compute the rolling Tail Ratio over a specified window (e.g., 252 days):
726
+ """)
727
+ st.latex(r'''
728
+ \text{Tail Ratio}_t = \frac{|\text{Quantile}_{95}(\text{Return}_{t-n:t})|}{|\text{Quantile}_{5}(\text{Return}_{t-n:t})|}
729
+ ''')
730
+ st.markdown("""
731
+ where \( n \) is the window size (e.g., 252 days).
732
+
733
+ **How to use:**
734
+ 1. Enter the stock ticker, start date, and end date.
735
+ 2. Click 'Fetch Data' to load the stock data.
736
+ 3. The chart will display the rolling Tail Ratio.
737
+
738
+ **Results:**
739
+ The chart shows the close prices along with the rolling Tail Ratio over time, indicating the balance between extreme positive and negative returns.
740
+ """)
741
+
742
+ fig = plot_rolling_tail_ratio(data)
743
+ st.plotly_chart(fig)
744
+
745
+ elif selected == "Rolling Omega Ratio":
746
+ st.markdown("""
747
+ ### Rolling Omega Ratio
748
+
749
+ This method calculates the rolling 1-year Omega Ratio.
750
+
751
+ **Methodology:**
752
+
753
+ 1. **Calculate Returns:**
754
+ - Compute the daily returns of the stock:
755
+ """)
756
+ st.latex(r'''
757
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
758
+ ''')
759
+ st.markdown("""
760
+ where \( P_t \) is the closing price at time \( t \).
761
+
762
+ 2. **Calculate Omega Ratio:**
763
+ - Compute the rolling Omega Ratio over a specified window (e.g., 252 days):
764
+ """)
765
+ st.latex(r'''
766
+ \text{Omega Ratio}_t = \frac{\sum (\text{Return}_{i} > MAR) (\text{Return}_{i} - MAR)}{\sum (\text{Return}_{i} < MAR) (MAR - \text{Return}_{i})}
767
+ ''')
768
+ st.markdown("""
769
+ where \( MAR \) is the Minimum Acceptable Return (assumed to be 0 here for simplicity) and \( n \) is the window size (e.g., 252 days).
770
+
771
+ **How to use:**
772
+ 1. Enter the stock ticker, start date, and end date.
773
+ 2. Click 'Fetch Data' to load the stock data.
774
+ 3. The chart will display the rolling Omega Ratio.
775
+
776
+ **Results:**
777
+ The chart shows the close prices along with the rolling Omega Ratio over time, indicating the risk-return trade-off.
778
+ """)
779
+
780
+ fig = plot_rolling_omega(data)
781
+ st.plotly_chart(fig)
782
+
783
+ elif selected == "Rolling Sortino Ratio":
784
+ st.markdown("""
785
+ ### Rolling Sortino Ratio
786
+
787
+ This method calculates the rolling 1-year Sortino Ratio.
788
+
789
+ **Methodology:**
790
+
791
+ 1. **Calculate Returns:**
792
+ - Compute the daily returns of the stock:
793
+ """)
794
+ st.latex(r'''
795
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
796
+ ''')
797
+ st.markdown("""
798
+ where \( P_t \) is the closing price at time \( t \).
799
+
800
+ 2. **Calculate Rolling Sortino Ratio:**
801
+ - Compute the rolling Sortino Ratio over a specified window (e.g., 252 days):
802
+ """)
803
+ st.latex(r'''
804
+ \text{Sortino Ratio}_t = \frac{\sqrt{252} \cdot \text{Mean}(\text{Return}_{t-n:t} - MAR)}{\sqrt{\text{Mean}(\min(0, \text{Return}_{t-n:t} - MAR)^2)}}
805
+ ''')
806
+ st.markdown("""
807
+ where \( MAR \) is the Minimum Acceptable Return (assumed to be 0 here for simplicity) and \( n \) is the window size (e.g., 252 days).
808
+
809
+ **How to use:**
810
+ 1. Enter the stock ticker, start date, and end date.
811
+ 2. Click 'Fetch Data' to load the stock data.
812
+ 3. The chart will display the rolling Sortino Ratio.
813
+
814
+ **Results:**
815
+ The chart shows the close prices along with the rolling Sortino Ratio over time, indicating the risk-adjusted return considering downside risk.
816
+ """)
817
+
818
+ fig = plot_rolling_sortino(data)
819
+ st.plotly_chart(fig)
820
+
821
+ elif selected == "Rolling Calmar Ratio":
822
+ st.markdown("""
823
+ ### Rolling Calmar Ratio
824
+
825
+ This method calculates the rolling 1-year Calmar Ratio.
826
+
827
+ **Methodology:**
828
+
829
+ 1. **Calculate Returns:**
830
+ - Compute the daily returns of the stock:
831
+ """)
832
+ st.latex(r'''
833
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
834
+ ''')
835
+ st.markdown("""
836
+ where \( P_t \) is the closing price at time \( t \).
837
+
838
+ 2. **Calculate Rolling Calmar Ratio:**
839
+ - Compute the rolling Calmar Ratio over a specified window (e.g., 252 days):
840
+ """)
841
+ st.latex(r'''
842
+ \text{Calmar Ratio}_t = \frac{\text{CAGR}_{t-n:t}}{\text{Max Drawdown}_{t-n:t}}
843
+ ''')
844
+ st.markdown("""
845
+ where \( \text{CAGR} \) is the Compound Annual Growth Rate and \( \text{Max Drawdown} \) is the maximum drawdown over the window \( n \) (e.g., 252 days).
846
+
847
+ **How to use:**
848
+ 1. Enter the stock ticker, start date, and end date.
849
+ 2. Click 'Fetch Data' to load the stock data.
850
+ 3. The chart will display the rolling Calmar Ratio.
851
+
852
+ **Results:**
853
+ The chart shows the close prices along with the rolling Calmar Ratio over time, indicating the performance of the stock relative to its maximum drawdown.
854
+ """)
855
+
856
+ fig = plot_rolling_calmar(data)
857
+ st.plotly_chart(fig)
858
+
859
+
860
+ elif selected == "Rolling Stability":
861
+ st.markdown("""
862
+ ### Rolling Stability
863
+
864
+ This method calculates the rolling 1-year stability of returns.
865
+
866
+ **Methodology:**
867
+
868
+ 1. **Calculate Returns:**
869
+ - Compute the daily returns of the stock:
870
+ """)
871
+ st.latex(r'''
872
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
873
+ ''')
874
+ st.markdown("""
875
+ where \( P_t \) is the closing price at time \( t \).
876
+
877
+ 2. **Calculate Rolling Stability:**
878
+ - Compute the rolling stability over a specified window (e.g., 252 days):
879
+ """)
880
+ st.latex(r'''
881
+ \text{Stability}_t = \sqrt{\frac{1}{n-1} \sum_{i=1}^{n} (\log(1 + \text{Return}_{t-i}) - \overline{\log(1 + \text{Return})})^2}
882
+ ''')
883
+ st.markdown("""
884
+ where \( n \) is the window size (e.g., 252 days) and \( \overline{\log(1 + \text{Return})} \) is the mean of the log returns over the window.
885
+
886
+ **How to use:**
887
+ 1. Enter the stock ticker, start date, and end date.
888
+ 2. Click 'Fetch Data' to load the stock data.
889
+ 3. The chart will display the rolling stability.
890
+
891
+ **Results:**
892
+ The chart shows the close prices along with the rolling stability over time, indicating the consistency of returns.
893
+ """)
894
+
895
+ fig = plot_rolling_stability(data)
896
+ st.plotly_chart(fig)
897
+
898
+ elif selected == "Rolling Maximum Drawdown":
899
+ st.markdown("""
900
+ ### Rolling Maximum Drawdown
901
+
902
+ This method calculates the rolling 1-year maximum drawdown.
903
+
904
+ **Methodology:**
905
+
906
+ 1. **Calculate Returns:**
907
+ - Compute the daily returns of the stock:
908
+ """)
909
+ st.latex(r'''
910
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
911
+ ''')
912
+ st.markdown("""
913
+ where \( P_t \) is the closing price at time \( t \).
914
+
915
+ 2. **Calculate Rolling Maximum Drawdown:**
916
+ - Compute the cumulative returns and the maximum drawdown over a specified window (e.g., 252 days):
917
+ """)
918
+ st.latex(r'''
919
+ \text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
920
+ ''')
921
+ st.latex(r'''
922
+ \text{Max Drawdown}_t = \frac{\text{Cumulative Return}_t - \text{Rolling Max Cumulative Return}_t}{\text{Rolling Max Cumulative Return}_t}
923
+ ''')
924
+ st.markdown("""
925
+ where the Rolling Max Cumulative Return is the maximum cumulative return over the window.
926
+
927
+ **How to use:**
928
+ 1. Enter the stock ticker, start date, and end date.
929
+ 2. Click 'Fetch Data' to load the stock data.
930
+ 3. The chart will display the rolling maximum drawdown.
931
+
932
+ **Results:**
933
+ The chart shows the close prices along with the rolling maximum drawdown over time, indicating the peak-to-trough decline during a specified period.
934
+ """)
935
+
936
+ fig = plot_rolling_drawdown(data)
937
+ st.plotly_chart(fig)
938
+
939
+ elif selected == "Rolling Capture Ratios":
940
+ st.markdown("""
941
+ ### Rolling Capture Ratios
942
+
943
+ This method calculates the rolling 1-year upside and downside capture ratios.
944
+
945
+ **Methodology:**
946
+
947
+ 1. **Calculate Returns:**
948
+ - Compute the daily returns of the stock and the benchmark:
949
+ """)
950
+ st.latex(r'''
951
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
952
+ ''')
953
+ st.markdown("""
954
+ where \( P_t \) is the closing price at time \( t \).
955
+
956
+ 2. **Calculate Upside Capture Ratio:**
957
+ - Compute the ratio of the sum of the stock's returns to the sum of the benchmark's returns during periods when the benchmark's returns are positive:
958
+ """)
959
+ st.latex(r'''
960
+ \text{Upside Capture Ratio}_t = \frac{\sum_{i=1}^{t} \text{Return}_{\text{stock}, i}}{\sum_{i=1}^{t} \text{Return}_{\text{benchmark}, i}}, \quad \text{if } \text{Return}_{\text{benchmark}, i} > 0
961
+ ''')
962
+ st.markdown("""
963
+ 3. **Calculate Downside Capture Ratio:**
964
+ - Compute the ratio of the sum of the stock's returns to the sum of the benchmark's returns during periods when the benchmark's returns are negative:
965
+ """)
966
+ st.latex(r'''
967
+ \text{Downside Capture Ratio}_t = \frac{\sum_{i=1}^{t} \text{Return}_{\text{stock}, i}}{\sum_{i=1}^{t} \text{Return}_{\text{benchmark}, i}}, \quad \text{if } \text{Return}_{\text{benchmark}, i} < 0
968
+ ''')
969
+ st.markdown("""
970
+ **How to use:**
971
+ 1. Enter the stock ticker, start date, and end date.
972
+ 2. Enter the benchmark ticker.
973
+ 3. Click 'Fetch Data' to load the stock and benchmark data.
974
+ 4. The chart will display the rolling capture ratios.
975
+
976
+ **Results:**
977
+ The chart shows the close prices along with the rolling upside and downside capture ratios over time, indicating the stock's performance relative to the benchmark during up and down market conditions.
978
+ """)
979
+
980
+ fig = plot_rolling_capture(data, market_data)
981
+ st.plotly_chart(fig)
982
+
983
+ elif selected == "Rolling Pain Index":
984
+ st.markdown("""
985
+ ### Rolling Pain Index
986
+
987
+ This method calculates the rolling 1-year pain index.
988
+
989
+ **Methodology:**
990
+
991
+ 1. **Calculate Returns:**
992
+ - Compute the daily returns of the stock:
993
+ """)
994
+ st.latex(r'''
995
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
996
+ ''')
997
+ st.markdown("""
998
+ where \( P_t \) is the closing price at time \( t \).
999
+
1000
+ 2. **Calculate Cumulative Returns:**
1001
+ - Compute the cumulative returns over time:
1002
+ """)
1003
+ st.latex(r'''
1004
+ \text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
1005
+ ''')
1006
+ st.markdown("""
1007
+ 3. **Calculate Drawdowns:**
1008
+ - Determine the drawdowns by comparing the cumulative returns to their running maximum:
1009
+ """)
1010
+ st.latex(r'''
1011
+ \text{Drawdown}_t = \frac{\text{Cumulative Return}_t - \text{Running Max Cumulative Return}_t}{\text{Running Max Cumulative Return}_t}
1012
+ ''')
1013
+ st.markdown("""
1014
+ 4. **Calculate Rolling Pain Index:**
1015
+ - Compute the average drawdown over a specified window (e.g., 252 days) where the drawdown is negative:
1016
+ """)
1017
+ st.latex(r'''
1018
+ \text{Pain Index} = \frac{1}{n} \sum_{i=1}^{n} \text{Drawdown}_i \quad \text{for } \text{Drawdown}_i < 0
1019
+ ''')
1020
+ st.markdown("""
1021
+ **How to use:**
1022
+ 1. Enter the stock ticker, start date, and end date.
1023
+ 2. Click 'Fetch Data' to load the stock data.
1024
+ 3. The chart will display the rolling pain index.
1025
+
1026
+ **Results:**
1027
+ The chart shows the close prices along with the rolling pain index over time, indicating the average drawdown experienced over a specified period.
1028
+ """)
1029
+
1030
+ fig = plot_rolling_pain_index(data)
1031
+ st.plotly_chart(fig)
1032
+
1033
+ hide_streamlit_style = """
1034
+ <style>
1035
+ #MainMenu {visibility: hidden;}
1036
+ footer {visibility: hidden;}
1037
+ </style>
1038
+ """
1039
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)