Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -13,10 +13,10 @@ def fetch_stock_data(ticker: str, start_date: str, end_date: str) -> pd.DataFram
|
|
| 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=
|
| 19 |
-
data['Vol_of_Vol'] = data['Rolling_Volatility'].rolling(window=
|
| 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'))
|
|
@@ -29,9 +29,9 @@ def plot_rolling_volatility(data: pd.DataFrame) -> go.Figure:
|
|
| 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(
|
| 35 |
|
| 36 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 37 |
subplot_titles=('Close Price', 'Rolling Sharpe Ratio'))
|
|
@@ -40,23 +40,21 @@ def plot_rolling_sharpe(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 44 |
return fig
|
| 45 |
|
| 46 |
# Helper function to plot rolling Treynor ratio
|
| 47 |
-
|
| 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=
|
| 56 |
-
variance = market_data['Return'].rolling(window=
|
| 57 |
rolling_beta = covariance / variance
|
| 58 |
-
avg_rolling_returns = data['Return'].rolling(window=
|
| 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,
|
|
@@ -66,11 +64,11 @@ def plot_rolling_treynor(data: pd.DataFrame, market_data: pd.DataFrame, window_s
|
|
| 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
|
| 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
|
| 74 |
data['Return'] = data['Close'].pct_change().dropna()
|
| 75 |
market_data['Market_Return'] = market_data['Close'].pct_change().dropna()
|
| 76 |
|
|
@@ -106,26 +104,25 @@ def plot_rolling_beta(data: pd.DataFrame, market_data: pd.DataFrame, window: int
|
|
| 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
|
| 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) -
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
beta =
|
| 124 |
-
expected_return = beta *
|
| 125 |
-
alpha =
|
| 126 |
rolling_alpha.append(alpha)
|
| 127 |
|
| 128 |
-
rolling_alpha = pd.Series(rolling_alpha, index=data.index[
|
| 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'))
|
|
@@ -133,15 +130,15 @@ def plot_rolling_alpha(data: pd.DataFrame, market_data: pd.DataFrame) -> go.Figu
|
|
| 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
|
| 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(
|
| 143 |
-
rolling_var_95 = data['Return'].rolling(
|
| 144 |
-
rolling_var_97 = data['Return'].rolling(
|
| 145 |
|
| 146 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 147 |
subplot_titles=('Close Price', 'Rolling VaR'))
|
|
@@ -151,20 +148,20 @@ def plot_rolling_var(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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(
|
| 166 |
-
rolling_cvar_90 = data['Return'].rolling(
|
| 167 |
-
rolling_cvar_97 = data['Return'].rolling(
|
| 168 |
|
| 169 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 170 |
subplot_titles=('Close Price', 'Rolling CVaR'))
|
|
@@ -174,13 +171,13 @@ def plot_rolling_cvar(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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(
|
| 184 |
|
| 185 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 186 |
subplot_titles=('Close Price', 'Rolling Tail Ratio'))
|
|
@@ -189,14 +186,14 @@ def plot_rolling_tail_ratio(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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(
|
| 200 |
|
| 201 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 202 |
subplot_titles=('Close Price', 'Rolling Omega Ratio'))
|
|
@@ -205,14 +202,13 @@ def plot_rolling_omega(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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 |
-
|
| 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'))
|
|
@@ -220,13 +216,13 @@ def plot_rolling_sortino(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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(
|
| 230 |
|
| 231 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 232 |
subplot_titles=('Close Price', 'Rolling Calmar Ratio'))
|
|
@@ -234,13 +230,13 @@ def plot_rolling_calmar(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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(
|
| 244 |
|
| 245 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 246 |
subplot_titles=('Close Price', 'Rolling Stability'))
|
|
@@ -248,14 +244,14 @@ def plot_rolling_stability(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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(
|
| 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,
|
|
@@ -264,11 +260,11 @@ def plot_rolling_drawdown(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 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,
|
| 272 |
data['Return'] = data['Close'].pct_change()
|
| 273 |
market_data['Return'] = market_data['Close'].pct_change()
|
| 274 |
|
|
@@ -310,7 +306,7 @@ def plot_rolling_capture(data: pd.DataFrame, market_data: pd.DataFrame, window_s
|
|
| 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'],
|
| 314 |
)
|
| 315 |
|
| 316 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
|
@@ -320,17 +316,16 @@ def plot_rolling_capture(data: pd.DataFrame, market_data: pd.DataFrame, window_s
|
|
| 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
|
| 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(
|
| 334 |
|
| 335 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 336 |
subplot_titles=('Close Price', 'Rolling Pain Index'))
|
|
@@ -338,7 +333,7 @@ def plot_rolling_pain_index(data: pd.DataFrame) -> go.Figure:
|
|
| 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
|
| 342 |
return fig
|
| 343 |
|
| 344 |
# Streamlit app
|
|
@@ -356,6 +351,7 @@ 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'):
|
|
@@ -363,14 +359,19 @@ if 'data' not in st.session_state or st.sidebar.button('Fetch Data'):
|
|
| 363 |
|
| 364 |
data = st.session_state.data
|
| 365 |
|
| 366 |
-
# Additional input for
|
| 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("""
|
|
@@ -390,16 +391,16 @@ if selected == "Rolling Volatility":
|
|
| 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
|
| 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
|
| 400 |
|
| 401 |
3. **Calculate Volatility of Volatility:**
|
| 402 |
-
- Compute the rolling standard deviation of the rolling volatility over
|
| 403 |
""")
|
| 404 |
st.latex(r'''
|
| 405 |
\text{Volatility of Volatility}_t = \text{std}(\text{Rolling Volatility}_{t-n:t})
|
|
@@ -408,18 +409,19 @@ if selected == "Rolling Volatility":
|
|
| 408 |
st.markdown("""
|
| 409 |
**How to use:**
|
| 410 |
1. Enter the stock ticker, start date, and end date.
|
| 411 |
-
2.
|
| 412 |
-
3.
|
|
|
|
| 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
|
| 423 |
|
| 424 |
**Methodology:**
|
| 425 |
|
|
@@ -433,44 +435,44 @@ elif selected == "Rolling Sharpe Ratio":
|
|
| 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
|
| 437 |
""")
|
| 438 |
st.latex(r'''
|
| 439 |
-
\text{Rolling Average Return}_t = \text{mean}(\text{Return}_{t-
|
| 440 |
''')
|
| 441 |
st.markdown("""
|
| 442 |
|
| 443 |
3. **Calculate Rolling Standard Deviation:**
|
| 444 |
-
- Compute the rolling standard deviation of the returns over
|
| 445 |
""")
|
| 446 |
st.latex(r'''
|
| 447 |
-
\text{Rolling Std Dev}_t = \text{std}(\text{Return}_{t-
|
| 448 |
''')
|
| 449 |
st.markdown("""
|
| 450 |
|
| 451 |
4. **Calculate Rolling Sharpe Ratio:**
|
| 452 |
-
- Annualize the Sharpe Ratio
|
| 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.
|
| 462 |
-
3.
|
|
|
|
| 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
|
| 474 |
|
| 475 |
**Methodology:**
|
| 476 |
|
|
@@ -490,7 +492,7 @@ elif selected == "Rolling Treynor Ratio":
|
|
| 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
|
| 494 |
|
| 495 |
3. **Calculate Average Rolling Returns:**
|
| 496 |
- Compute the rolling mean of the stock returns over the same window:
|
|
@@ -499,10 +501,10 @@ elif selected == "Rolling Treynor Ratio":
|
|
| 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
|
| 503 |
|
| 504 |
4. **Calculate Treynor Ratio:**
|
| 505 |
-
- Compute the Treynor Ratio using the risk-free rate \( R_f \)
|
| 506 |
""")
|
| 507 |
st.latex(r'''
|
| 508 |
\text{Treynor Ratio}_t = \frac{\text{Average Rolling Return}_t - R_f}{\beta_t}
|
|
@@ -512,12 +514,12 @@ elif selected == "Rolling Treynor Ratio":
|
|
| 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":
|
|
@@ -538,13 +540,13 @@ elif selected == "Rolling Beta":
|
|
| 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
|
| 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
|
| 548 |
|
| 549 |
3. **Calculate Rolling Beta using RANSAC:**
|
| 550 |
- Use the RANSAC algorithm to robustly estimate the beta, reducing the influence of outliers:
|
|
@@ -557,19 +559,19 @@ elif selected == "Rolling Beta":
|
|
| 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
|
| 573 |
|
| 574 |
**Methodology:**
|
| 575 |
|
|
@@ -583,13 +585,13 @@ elif selected == "Rolling Jensen's Alpha":
|
|
| 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
|
| 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
|
| 593 |
|
| 594 |
3. **Calculate Expected Return:**
|
| 595 |
- Compute the expected return of the stock based on the CAPM model:
|
|
@@ -598,7 +600,7 @@ elif selected == "Rolling Jensen's Alpha":
|
|
| 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
|
| 602 |
|
| 603 |
4. **Calculate Jensen's Alpha:**
|
| 604 |
- Compute the Jensen's Alpha as the difference between the actual return and the expected return:
|
|
@@ -611,19 +613,19 @@ elif selected == "Rolling Jensen's Alpha":
|
|
| 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
|
| 627 |
|
| 628 |
**Methodology:**
|
| 629 |
|
|
@@ -637,31 +639,32 @@ elif selected == "Rolling Value at Risk":
|
|
| 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
|
| 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
|
| 647 |
|
| 648 |
**How to use:**
|
| 649 |
1. Enter the stock ticker, start date, and end date.
|
| 650 |
-
2.
|
| 651 |
-
3.
|
|
|
|
| 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
|
| 665 |
|
| 666 |
**Methodology:**
|
| 667 |
|
|
@@ -674,41 +677,33 @@ elif selected == "Rolling Conditional VaR":
|
|
| 674 |
st.markdown("""
|
| 675 |
where \( P_t \) is the closing price at time \( t \).
|
| 676 |
|
| 677 |
-
2. **Calculate Rolling
|
| 678 |
-
- Compute the
|
| 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.
|
| 698 |
-
3.
|
|
|
|
| 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
|
| 712 |
|
| 713 |
**Methodology:**
|
| 714 |
|
|
@@ -722,31 +717,32 @@ elif selected == "Rolling Tail Ratio":
|
|
| 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
|
| 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
|
| 732 |
|
| 733 |
**How to use:**
|
| 734 |
1. Enter the stock ticker, start date, and end date.
|
| 735 |
-
2.
|
| 736 |
-
3.
|
|
|
|
| 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
|
| 750 |
|
| 751 |
**Methodology:**
|
| 752 |
|
|
@@ -760,31 +756,32 @@ elif selected == "Rolling Omega Ratio":
|
|
| 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
|
| 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
|
| 770 |
|
| 771 |
**How to use:**
|
| 772 |
1. Enter the stock ticker, start date, and end date.
|
| 773 |
-
2.
|
| 774 |
-
3.
|
|
|
|
| 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
|
| 788 |
|
| 789 |
**Methodology:**
|
| 790 |
|
|
@@ -798,31 +795,32 @@ elif selected == "Rolling Sortino Ratio":
|
|
| 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
|
| 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
|
| 808 |
|
| 809 |
**How to use:**
|
| 810 |
1. Enter the stock ticker, start date, and end date.
|
| 811 |
-
2.
|
| 812 |
-
3.
|
|
|
|
| 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
|
| 826 |
|
| 827 |
**Methodology:**
|
| 828 |
|
|
@@ -836,32 +834,32 @@ elif selected == "Rolling Calmar Ratio":
|
|
| 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
|
| 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 \)
|
| 846 |
|
| 847 |
**How to use:**
|
| 848 |
1. Enter the stock ticker, start date, and end date.
|
| 849 |
-
2.
|
| 850 |
-
3.
|
|
|
|
| 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
|
| 865 |
|
| 866 |
**Methodology:**
|
| 867 |
|
|
@@ -875,31 +873,32 @@ elif selected == "Rolling Stability":
|
|
| 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
|
| 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
|
| 885 |
|
| 886 |
**How to use:**
|
| 887 |
1. Enter the stock ticker, start date, and end date.
|
| 888 |
-
2.
|
| 889 |
-
3.
|
|
|
|
| 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
|
| 903 |
|
| 904 |
**Methodology:**
|
| 905 |
|
|
@@ -913,7 +912,7 @@ elif selected == "Rolling Maximum Drawdown":
|
|
| 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
|
| 917 |
""")
|
| 918 |
st.latex(r'''
|
| 919 |
\text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
|
|
@@ -926,21 +925,22 @@ elif selected == "Rolling Maximum Drawdown":
|
|
| 926 |
|
| 927 |
**How to use:**
|
| 928 |
1. Enter the stock ticker, start date, and end date.
|
| 929 |
-
2.
|
| 930 |
-
3.
|
|
|
|
| 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
|
| 944 |
|
| 945 |
**Methodology:**
|
| 946 |
|
|
@@ -969,7 +969,7 @@ elif selected == "Rolling Capture Ratios":
|
|
| 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 |
|
|
@@ -977,14 +977,14 @@ elif selected == "Rolling Capture Ratios":
|
|
| 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
|
| 988 |
|
| 989 |
**Methodology:**
|
| 990 |
|
|
@@ -1012,7 +1012,7 @@ elif selected == "Rolling Pain Index":
|
|
| 1012 |
''')
|
| 1013 |
st.markdown("""
|
| 1014 |
4. **Calculate Rolling Pain Index:**
|
| 1015 |
-
- Compute the average drawdown over a specified window
|
| 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
|
|
@@ -1020,14 +1020,15 @@ elif selected == "Rolling Pain Index":
|
|
| 1020 |
st.markdown("""
|
| 1021 |
**How to use:**
|
| 1022 |
1. Enter the stock ticker, start date, and end date.
|
| 1023 |
-
2.
|
| 1024 |
-
3.
|
|
|
|
| 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 = """
|
|
|
|
| 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, window: int) -> go.Figure:
|
| 17 |
data['Return'] = data['Close'].pct_change()
|
| 18 |
+
data['Rolling_Volatility'] = data['Return'].rolling(window=window).std() * np.sqrt(252)
|
| 19 |
+
data['Vol_of_Vol'] = data['Rolling_Volatility'].rolling(window=window).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'))
|
|
|
|
| 29 |
return fig
|
| 30 |
|
| 31 |
# Helper function to plot rolling Sharpe ratio
|
| 32 |
+
def plot_rolling_sharpe(data: pd.DataFrame, window: int, risk_free_rate: float) -> go.Figure:
|
| 33 |
data['Return'] = data['Close'].pct_change()
|
| 34 |
+
rolling_sharpe = np.sqrt(252) * (data['Return'].rolling(window).mean() - risk_free_rate) / data['Return'].rolling(window).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'))
|
|
|
|
| 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 Sharpe Ratio with Stock Price', xaxis_title='Date')
|
| 44 |
return fig
|
| 45 |
|
| 46 |
# Helper function to plot rolling Treynor ratio
|
| 47 |
+
def plot_rolling_treynor(data: pd.DataFrame, market_data: pd.DataFrame, window: int, risk_free_rate: float) -> go.Figure:
|
|
|
|
| 48 |
data['Return'] = data['Close'].pct_change()
|
| 49 |
market_data['Return'] = market_data['Close'].pct_change()
|
| 50 |
|
| 51 |
# Align indices
|
| 52 |
market_data = market_data.reindex(data.index, method='ffill')
|
| 53 |
|
| 54 |
+
covariance = data['Return'].rolling(window=window).cov(market_data['Return'])
|
| 55 |
+
variance = market_data['Return'].rolling(window=window).var()
|
| 56 |
rolling_beta = covariance / variance
|
| 57 |
+
avg_rolling_returns = data['Return'].rolling(window=window).mean()
|
|
|
|
| 58 |
data['Rolling_Treynor_Ratio'] = (avg_rolling_returns - risk_free_rate) / rolling_beta
|
| 59 |
|
| 60 |
fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
|
|
|
| 64 |
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)
|
| 65 |
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)
|
| 66 |
|
| 67 |
+
fig.update_layout(title='Rolling Treynor Ratio with Stock Price and Benchmark', xaxis_title='Date')
|
| 68 |
return fig
|
| 69 |
+
|
| 70 |
# Helper function to calculate and plot rolling beta
|
| 71 |
+
def plot_rolling_beta(data: pd.DataFrame, market_data: pd.DataFrame, window: int) -> go.Figure:
|
| 72 |
data['Return'] = data['Close'].pct_change().dropna()
|
| 73 |
market_data['Market_Return'] = market_data['Close'].pct_change().dropna()
|
| 74 |
|
|
|
|
| 104 |
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)
|
| 105 |
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)
|
| 106 |
|
| 107 |
+
fig.update_layout(title='Rolling Beta with Stock Price and Benchmark', xaxis_title='Date')
|
| 108 |
return fig
|
| 109 |
|
| 110 |
# Helper function to plot rolling Jensen's alpha
|
| 111 |
+
def plot_rolling_alpha(data: pd.DataFrame, market_data: pd.DataFrame, window: int, risk_free_rate: float) -> go.Figure:
|
| 112 |
data['Return'] = data['Close'].pct_change()
|
| 113 |
market_data['Return'] = market_data['Close'].pct_change()
|
| 114 |
|
|
|
|
| 115 |
rolling_alpha = []
|
| 116 |
|
| 117 |
+
for i in range(len(data) - window):
|
| 118 |
+
window_stock = data['Return'].iloc[i:i + window]
|
| 119 |
+
window_market = market_data['Return'].iloc[i:i + window]
|
| 120 |
+
beta = window_stock.cov(window_market) / window_market.var()
|
| 121 |
+
expected_return = risk_free_rate + beta * (window_market.mean() - risk_free_rate)
|
| 122 |
+
alpha = window_stock.mean() - expected_return
|
| 123 |
rolling_alpha.append(alpha)
|
| 124 |
|
| 125 |
+
rolling_alpha = pd.Series(rolling_alpha, index=data.index[window:])
|
| 126 |
|
| 127 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 128 |
subplot_titles=('Close Price', 'Rolling Jensen\'s Alpha'))
|
|
|
|
| 130 |
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
|
| 131 |
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)
|
| 132 |
|
| 133 |
+
fig.update_layout(title='Rolling Jensen\'s Alpha with Stock Price', xaxis_title='Date')
|
| 134 |
return fig
|
| 135 |
|
| 136 |
# Helper function to plot rolling Value at Risk (VaR)
|
| 137 |
+
def plot_rolling_var(data: pd.DataFrame, window: int) -> go.Figure:
|
| 138 |
data['Return'] = data['Close'].pct_change()
|
| 139 |
+
rolling_var_90 = data['Return'].rolling(window).quantile(0.10).dropna()
|
| 140 |
+
rolling_var_95 = data['Return'].rolling(window).quantile(0.05).dropna()
|
| 141 |
+
rolling_var_97 = data['Return'].rolling(window).quantile(0.03).dropna()
|
| 142 |
|
| 143 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 144 |
subplot_titles=('Close Price', 'Rolling VaR'))
|
|
|
|
| 148 |
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)
|
| 149 |
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)
|
| 150 |
|
| 151 |
+
fig.update_layout(title='Rolling VaR with Stock Price', xaxis_title='Date')
|
| 152 |
return fig
|
| 153 |
|
| 154 |
# Helper function to plot rolling Conditional VaR (CVaR)
|
| 155 |
+
def plot_rolling_cvar(data: pd.DataFrame, window: int) -> go.Figure:
|
| 156 |
data['Return'] = data['Close'].pct_change()
|
| 157 |
|
| 158 |
def conditional_var(x, alpha=0.05):
|
| 159 |
var = np.percentile(x, alpha * 100)
|
| 160 |
return np.mean(x[x < var])
|
| 161 |
|
| 162 |
+
rolling_cvar_95 = data['Return'].rolling(window).apply(conditional_var, raw=True).dropna()
|
| 163 |
+
rolling_cvar_90 = data['Return'].rolling(window).apply(lambda x: conditional_var(x, alpha=0.1), raw=True).dropna()
|
| 164 |
+
rolling_cvar_97 = data['Return'].rolling(window).apply(lambda x: conditional_var(x, alpha=0.03), raw=True).dropna()
|
| 165 |
|
| 166 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 167 |
subplot_titles=('Close Price', 'Rolling CVaR'))
|
|
|
|
| 171 |
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)
|
| 172 |
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)
|
| 173 |
|
| 174 |
+
fig.update_layout(title='Rolling CVaR with Stock Price', xaxis_title='Date')
|
| 175 |
return fig
|
| 176 |
|
| 177 |
# Helper function to plot rolling Tail Ratio
|
| 178 |
+
def plot_rolling_tail_ratio(data: pd.DataFrame, window: int) -> go.Figure:
|
| 179 |
data['Return'] = data['Close'].pct_change()
|
| 180 |
+
tail_ratio = data['Return'].rolling(window).apply(lambda x: np.abs(np.percentile(x, 95)) / np.abs(np.percentile(x, 5))).dropna()
|
| 181 |
|
| 182 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 183 |
subplot_titles=('Close Price', 'Rolling Tail Ratio'))
|
|
|
|
| 186 |
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)
|
| 187 |
fig.add_hline(y=1, line=dict(color='red', dash='dash'), row=2, col=1)
|
| 188 |
|
| 189 |
+
fig.update_layout(title='Rolling Tail Ratio with Stock Price', xaxis_title='Date')
|
| 190 |
return fig
|
| 191 |
|
| 192 |
# Helper function to plot rolling Omega Ratio
|
| 193 |
+
def plot_rolling_omega(data: pd.DataFrame, window: int) -> go.Figure:
|
| 194 |
data['Return'] = data['Close'].pct_change()
|
| 195 |
MAR = 0 # Minimum Acceptable Return
|
| 196 |
+
omega_ratio = data['Return'].rolling(window).apply(lambda x: np.sum(x[x > MAR] - MAR) / np.sum(MAR - x[x < MAR])).dropna()
|
| 197 |
|
| 198 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 199 |
subplot_titles=('Close Price', 'Rolling Omega Ratio'))
|
|
|
|
| 202 |
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)
|
| 203 |
fig.add_hline(y=1, line=dict(color='red', dash='dash'), row=2, col=1)
|
| 204 |
|
| 205 |
+
fig.update_layout(title='Rolling Omega Ratio with Stock Price', xaxis_title='Date')
|
| 206 |
return fig
|
| 207 |
|
| 208 |
# Helper function to plot rolling Sortino Ratio
|
| 209 |
+
def plot_rolling_sortino(data: pd.DataFrame, window: int, MAR: float) -> go.Figure:
|
| 210 |
data['Return'] = data['Close'].pct_change()
|
| 211 |
+
sortino_ratio = data['Return'].rolling(window).apply(lambda x: np.mean(x - MAR) / np.sqrt(np.mean(np.minimum(0, x - MAR) ** 2))).dropna()
|
|
|
|
| 212 |
|
| 213 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 214 |
subplot_titles=('Close Price', 'Rolling Sortino Ratio'))
|
|
|
|
| 216 |
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
|
| 217 |
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)
|
| 218 |
|
| 219 |
+
fig.update_layout(title='Rolling Sortino Ratio with Stock Price', xaxis_title='Date')
|
| 220 |
return fig
|
| 221 |
|
| 222 |
# Helper function to plot rolling Calmar Ratio
|
| 223 |
+
def plot_rolling_calmar(data: pd.DataFrame, window: int) -> go.Figure:
|
| 224 |
data['Return'] = data['Close'].pct_change()
|
| 225 |
+
calmar_ratio = data['Return'].rolling(window).apply(lambda x: (1 + x).cumprod()[-1] ** (252.0 / len(x)) / np.abs(np.min((1 + x).cumprod() / (1 + x).cumprod().cummax()) - 1)).dropna()
|
| 226 |
|
| 227 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 228 |
subplot_titles=('Close Price', 'Rolling Calmar Ratio'))
|
|
|
|
| 230 |
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
|
| 231 |
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)
|
| 232 |
|
| 233 |
+
fig.update_layout(title='Rolling Calmar Ratio with Stock Price', xaxis_title='Date')
|
| 234 |
return fig
|
| 235 |
|
| 236 |
# Helper function to plot rolling stability
|
| 237 |
+
def plot_rolling_stability(data: pd.DataFrame, window: int) -> go.Figure:
|
| 238 |
data['Return'] = data['Close'].pct_change()
|
| 239 |
+
stability = data['Return'].rolling(window).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()
|
| 240 |
|
| 241 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 242 |
subplot_titles=('Close Price', 'Rolling Stability'))
|
|
|
|
| 244 |
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
|
| 245 |
fig.add_trace(go.Scatter(x=stability.index, y=stability, mode='lines', name='Stability', line=dict(color='purple')), row=2, col=1)
|
| 246 |
|
| 247 |
+
fig.update_layout(title='Rolling Stability of Returns with Stock Price', xaxis_title='Date')
|
| 248 |
return fig
|
| 249 |
|
| 250 |
# Helper function to plot rolling maximum drawdown
|
| 251 |
+
def plot_rolling_drawdown(data: pd.DataFrame, window: int) -> go.Figure:
|
| 252 |
data['Return'] = data['Close'].pct_change()
|
| 253 |
rolling_cumulative = (1 + data['Return']).cumprod()
|
| 254 |
+
rolling_max = rolling_cumulative.rolling(window, min_periods=1).max()
|
| 255 |
rolling_drawdown = (rolling_cumulative - rolling_max) / rolling_max
|
| 256 |
|
| 257 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
|
|
|
| 260 |
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
|
| 261 |
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)
|
| 262 |
|
| 263 |
+
fig.update_layout(title='Rolling Maximum Drawdown with Stock Price', xaxis_title='Date')
|
| 264 |
return fig
|
| 265 |
|
| 266 |
# Helper function to calculate and plot rolling capture ratios
|
| 267 |
+
def plot_rolling_capture(data: pd.DataFrame, market_data: pd.DataFrame, window: int) -> go.Figure:
|
| 268 |
data['Return'] = data['Close'].pct_change()
|
| 269 |
market_data['Return'] = market_data['Close'].pct_change()
|
| 270 |
|
|
|
|
| 306 |
return upside_captures, downside_captures
|
| 307 |
|
| 308 |
data['rolling_upside_capture'], data['rolling_downside_capture'] = compute_rolling_captures(
|
| 309 |
+
data['Return'], market_data['Return'], window
|
| 310 |
)
|
| 311 |
|
| 312 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
|
|
|
| 316 |
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)
|
| 317 |
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)
|
| 318 |
|
| 319 |
+
fig.update_layout(title='Rolling Capture Ratios with Stock Price', xaxis_title='Date')
|
| 320 |
return fig
|
| 321 |
|
|
|
|
| 322 |
# Helper function to plot rolling Pain Index
|
| 323 |
+
def plot_rolling_pain_index(data: pd.DataFrame, window: int) -> go.Figure:
|
| 324 |
data['Return'] = data['Close'].pct_change()
|
| 325 |
cumulative_return = (1 + data['Return']).cumprod()
|
| 326 |
running_max = cumulative_return.cummax()
|
| 327 |
drawdown = (cumulative_return - running_max) / running_max
|
| 328 |
+
pain_index = drawdown.rolling(window).apply(lambda x: np.mean(x[x < 0])).dropna()
|
| 329 |
|
| 330 |
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
|
| 331 |
subplot_titles=('Close Price', 'Rolling Pain Index'))
|
|
|
|
| 333 |
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='grey')), row=1, col=1)
|
| 334 |
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)
|
| 335 |
|
| 336 |
+
fig.update_layout(title='Rolling Pain Index with Stock Price', xaxis_title='Date')
|
| 337 |
return fig
|
| 338 |
|
| 339 |
# Streamlit app
|
|
|
|
| 351 |
ticker = st.sidebar.text_input('Enter Stock Ticker', 'VOW.DE')
|
| 352 |
start_date = st.sidebar.date_input('Start Date', pd.to_datetime('2010-01-01'))
|
| 353 |
end_date = st.sidebar.date_input('End Date', pd.to_datetime('2024-12-31'))
|
| 354 |
+
window_size = st.sidebar.number_input('Rolling Window Size (Days)', min_value=1, value=252)
|
| 355 |
|
| 356 |
# Fetch data
|
| 357 |
if 'data' not in st.session_state or st.sidebar.button('Fetch Data'):
|
|
|
|
| 359 |
|
| 360 |
data = st.session_state.data
|
| 361 |
|
| 362 |
+
# Additional input for methods requiring benchmark or risk-free rate
|
| 363 |
if selected in ["Rolling Treynor Ratio", "Rolling Beta", "Rolling Jensen's Alpha", "Rolling Capture Ratios"]:
|
| 364 |
benchmark_ticker = st.sidebar.text_input('Enter Benchmark Ticker', '^GSPC')
|
| 365 |
if 'market_data' not in st.session_state or st.sidebar.button('Fetch Market Data'):
|
| 366 |
st.session_state.market_data = fetch_stock_data(benchmark_ticker, start_date, end_date)
|
|
|
|
| 367 |
market_data = st.session_state.market_data
|
| 368 |
|
| 369 |
+
if selected in ["Rolling Sharpe Ratio", "Rolling Treynor Ratio", "Rolling Jensen's Alpha", "Rolling Sortino Ratio"]:
|
| 370 |
+
risk_free_rate = st.sidebar.number_input('Risk-Free Rate (as a decimal)', min_value=0.0, value=0.0)
|
| 371 |
+
|
| 372 |
+
if selected == "Rolling Sortino Ratio":
|
| 373 |
+
MAR = st.sidebar.number_input('Minimum Acceptable Return (MAR, as a decimal)', min_value=0.0, value=0.0)
|
| 374 |
+
|
| 375 |
# Display results based on the selected method
|
| 376 |
if selected == "Rolling Volatility":
|
| 377 |
st.markdown("""
|
|
|
|
| 391 |
where \( P_t \) is the closing price at time \( t \).
|
| 392 |
|
| 393 |
2. **Calculate Rolling Volatility:**
|
| 394 |
+
- Compute the rolling standard deviation of the returns over a specified window and annualize it:
|
| 395 |
""")
|
| 396 |
st.latex(r'''
|
| 397 |
\text{Rolling Volatility}_t = \sqrt{252} \times \text{std}(\text{Return}_{t-n:t})
|
| 398 |
''')
|
| 399 |
st.markdown("""
|
| 400 |
+
where \( n \) is the window size.
|
| 401 |
|
| 402 |
3. **Calculate Volatility of Volatility:**
|
| 403 |
+
- Compute the rolling standard deviation of the rolling volatility over the specified window:
|
| 404 |
""")
|
| 405 |
st.latex(r'''
|
| 406 |
\text{Volatility of Volatility}_t = \text{std}(\text{Rolling Volatility}_{t-n:t})
|
|
|
|
| 409 |
st.markdown("""
|
| 410 |
**How to use:**
|
| 411 |
1. Enter the stock ticker, start date, and end date.
|
| 412 |
+
2. Enter the rolling window size.
|
| 413 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 414 |
+
4. The chart will display the rolling volatility and the volatility of volatility.
|
| 415 |
""")
|
| 416 |
|
| 417 |
+
fig = plot_rolling_volatility(data, window_size)
|
| 418 |
st.plotly_chart(fig)
|
| 419 |
|
| 420 |
elif selected == "Rolling Sharpe Ratio":
|
| 421 |
st.markdown("""
|
| 422 |
### Rolling Sharpe Ratio
|
| 423 |
|
| 424 |
+
This method calculates the rolling Sharpe Ratio.
|
| 425 |
|
| 426 |
**Methodology:**
|
| 427 |
|
|
|
|
| 435 |
where \( P_t \) is the closing price at time \( t \).
|
| 436 |
|
| 437 |
2. **Calculate Rolling Average Return:**
|
| 438 |
+
- Compute the rolling mean of the returns over the specified window:
|
| 439 |
""")
|
| 440 |
st.latex(r'''
|
| 441 |
+
\text{Rolling Average Return}_t = \text{mean}(\text{Return}_{t-n:t})
|
| 442 |
''')
|
| 443 |
st.markdown("""
|
| 444 |
|
| 445 |
3. **Calculate Rolling Standard Deviation:**
|
| 446 |
+
- Compute the rolling standard deviation of the returns over the specified window:
|
| 447 |
""")
|
| 448 |
st.latex(r'''
|
| 449 |
+
\text{Rolling Std Dev}_t = \text{std}(\text{Return}_{t-n:t})
|
| 450 |
''')
|
| 451 |
st.markdown("""
|
| 452 |
|
| 453 |
4. **Calculate Rolling Sharpe Ratio:**
|
| 454 |
+
- Annualize the Sharpe Ratio:
|
| 455 |
""")
|
| 456 |
st.latex(r'''
|
| 457 |
+
\text{Rolling Sharpe Ratio}_t = \frac{\text{Rolling Average Return}_t - R_f}{\text{Rolling Std Dev}_t} \times \sqrt{252}
|
| 458 |
''')
|
| 459 |
st.markdown("""
|
| 460 |
|
| 461 |
**How to use:**
|
| 462 |
1. Enter the stock ticker, start date, and end date.
|
| 463 |
+
2. Enter the rolling window size and risk-free rate.
|
| 464 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 465 |
+
4. The chart will display the rolling Sharpe Ratio.
|
| 466 |
""")
|
| 467 |
|
| 468 |
+
fig = plot_rolling_sharpe(data, window_size, risk_free_rate)
|
| 469 |
st.plotly_chart(fig)
|
| 470 |
|
|
|
|
| 471 |
elif selected == "Rolling Treynor Ratio":
|
| 472 |
st.markdown("""
|
| 473 |
### Rolling Treynor Ratio
|
| 474 |
|
| 475 |
+
This method calculates the rolling Treynor Ratio.
|
| 476 |
|
| 477 |
**Methodology:**
|
| 478 |
|
|
|
|
| 492 |
\beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
|
| 493 |
''')
|
| 494 |
st.markdown("""
|
| 495 |
+
where the rolling window size is specified.
|
| 496 |
|
| 497 |
3. **Calculate Average Rolling Returns:**
|
| 498 |
- Compute the rolling mean of the stock returns over the same window:
|
|
|
|
| 501 |
\text{Average Rolling Return}_t = \frac{1}{n} \sum_{i=t-n+1}^{t} \text{Return}_{\text{stock}, i}
|
| 502 |
''')
|
| 503 |
st.markdown("""
|
| 504 |
+
where \( n \) is the window size.
|
| 505 |
|
| 506 |
4. **Calculate Treynor Ratio:**
|
| 507 |
+
- Compute the Treynor Ratio using the risk-free rate \( R_f \):
|
| 508 |
""")
|
| 509 |
st.latex(r'''
|
| 510 |
\text{Treynor Ratio}_t = \frac{\text{Average Rolling Return}_t - R_f}{\beta_t}
|
|
|
|
| 514 |
|
| 515 |
**How to use:**
|
| 516 |
1. Enter the stock ticker, start date, and end date.
|
| 517 |
+
2. Enter the benchmark ticker, rolling window size, and risk-free rate.
|
| 518 |
3. Click 'Fetch Data' to load the stock and benchmark data.
|
| 519 |
4. The chart will display the rolling Treynor Ratio.
|
| 520 |
""")
|
| 521 |
|
| 522 |
+
fig = plot_rolling_treynor(data, market_data, window_size, risk_free_rate)
|
| 523 |
st.plotly_chart(fig)
|
| 524 |
|
| 525 |
elif selected == "Rolling Beta":
|
|
|
|
| 540 |
where \( P_t \) is the closing price at time \( t \).
|
| 541 |
|
| 542 |
2. **Calculate Rolling Beta using OLS:**
|
| 543 |
+
- Perform a linear regression of the stock returns against the benchmark returns over a specified window:
|
| 544 |
""")
|
| 545 |
st.latex(r'''
|
| 546 |
\beta_{OLS} = \frac{\text{Cov}(\text{Return}_{\text{stock}}, \text{Return}_{\text{benchmark}})}{\text{Var}(\text{Return}_{\text{benchmark}})}
|
| 547 |
''')
|
| 548 |
st.markdown("""
|
| 549 |
+
where the rolling window size is specified.
|
| 550 |
|
| 551 |
3. **Calculate Rolling Beta using RANSAC:**
|
| 552 |
- Use the RANSAC algorithm to robustly estimate the beta, reducing the influence of outliers:
|
|
|
|
| 559 |
|
| 560 |
**How to use:**
|
| 561 |
1. Enter the stock ticker, start date, and end date.
|
| 562 |
+
2. Enter the benchmark ticker and rolling window size.
|
| 563 |
3. Click 'Fetch Data' to load the stock and benchmark data.
|
| 564 |
4. The chart will display the rolling beta calculated using both OLS and RANSAC methods.
|
| 565 |
""")
|
| 566 |
|
| 567 |
+
fig = plot_rolling_beta(data, market_data, window_size)
|
| 568 |
st.plotly_chart(fig)
|
| 569 |
|
| 570 |
elif selected == "Rolling Jensen's Alpha":
|
| 571 |
st.markdown("""
|
| 572 |
### Rolling Jensen's Alpha
|
| 573 |
|
| 574 |
+
This method calculates the rolling Jensen's Alpha.
|
| 575 |
|
| 576 |
**Methodology:**
|
| 577 |
|
|
|
|
| 585 |
where \( P_t \) is the closing price at time \( t \).
|
| 586 |
|
| 587 |
2. **Calculate Beta:**
|
| 588 |
+
- Compute the rolling beta of the stock returns against the benchmark returns over a specified window:
|
| 589 |
""")
|
| 590 |
st.latex(r'''
|
| 591 |
\beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
|
| 592 |
''')
|
| 593 |
st.markdown("""
|
| 594 |
+
where the rolling window size is specified.
|
| 595 |
|
| 596 |
3. **Calculate Expected Return:**
|
| 597 |
- Compute the expected return of the stock based on the CAPM model:
|
|
|
|
| 600 |
\text{Expected Return}_t = R_f + \beta_t (\text{Return}_{\text{benchmark}} - R_f)
|
| 601 |
''')
|
| 602 |
st.markdown("""
|
| 603 |
+
where \( R_f \) is the risk-free rate.
|
| 604 |
|
| 605 |
4. **Calculate Jensen's Alpha:**
|
| 606 |
- Compute the Jensen's Alpha as the difference between the actual return and the expected return:
|
|
|
|
| 613 |
|
| 614 |
**How to use:**
|
| 615 |
1. Enter the stock ticker, start date, and end date.
|
| 616 |
+
2. Enter the benchmark ticker, rolling window size, and risk-free rate.
|
| 617 |
3. Click 'Fetch Data' to load the stock and benchmark data.
|
| 618 |
4. The chart will display the rolling Jensen's Alpha.
|
| 619 |
""")
|
| 620 |
|
| 621 |
+
fig = plot_rolling_alpha(data, market_data, window_size, risk_free_rate)
|
| 622 |
st.plotly_chart(fig)
|
| 623 |
|
| 624 |
elif selected == "Rolling Value at Risk":
|
| 625 |
st.markdown("""
|
| 626 |
### Rolling Value at Risk (VaR)
|
| 627 |
|
| 628 |
+
This method calculates the rolling Value at Risk (VaR) at different confidence levels.
|
| 629 |
|
| 630 |
**Methodology:**
|
| 631 |
|
|
|
|
| 639 |
where \( P_t \) is the closing price at time \( t \).
|
| 640 |
|
| 641 |
2. **Calculate Rolling VaR:**
|
| 642 |
+
- Compute the rolling quantile of the returns over a specified window for different confidence levels:
|
| 643 |
""")
|
| 644 |
st.latex(r'''
|
| 645 |
\text{VaR}_{\alpha, t} = \text{Quantile}_{\alpha}(\text{Return}_{t-n:t})
|
| 646 |
''')
|
| 647 |
st.markdown("""
|
| 648 |
+
where \( \alpha \) is the confidence level and \( n \) is the window size.
|
| 649 |
|
| 650 |
**How to use:**
|
| 651 |
1. Enter the stock ticker, start date, and end date.
|
| 652 |
+
2. Enter the rolling window size.
|
| 653 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 654 |
+
4. The chart will display the rolling VaR at different confidence levels.
|
| 655 |
|
| 656 |
**Results:**
|
| 657 |
The chart shows the close prices along with the rolling VaR at different confidence levels over time.
|
| 658 |
""")
|
| 659 |
|
| 660 |
+
fig = plot_rolling_var(data, window_size)
|
| 661 |
st.plotly_chart(fig)
|
| 662 |
|
| 663 |
elif selected == "Rolling Conditional VaR":
|
| 664 |
st.markdown("""
|
| 665 |
### Rolling Conditional Value at Risk (CVaR)
|
| 666 |
|
| 667 |
+
This method calculates the rolling Conditional Value at Risk (CVaR) at different confidence levels.
|
| 668 |
|
| 669 |
**Methodology:**
|
| 670 |
|
|
|
|
| 677 |
st.markdown("""
|
| 678 |
where \( P_t \) is the closing price at time \( t \).
|
| 679 |
|
| 680 |
+
2. **Calculate Rolling CVaR:**
|
| 681 |
+
- Compute the average of the returns that are below the VaR threshold over a specified window:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
""")
|
| 683 |
st.latex(r'''
|
| 684 |
\text{CVaR}_{\alpha, t} = \frac{1}{n} \sum_{i=1}^{n} \text{Return}_{i} \text{ where } \text{Return}_{i} < \text{VaR}_{\alpha, t}
|
| 685 |
''')
|
| 686 |
st.markdown("""
|
| 687 |
+
where \( n \) is the number of returns below the VaR threshold and \( \alpha \) is the confidence level.
|
| 688 |
|
| 689 |
**How to use:**
|
| 690 |
1. Enter the stock ticker, start date, and end date.
|
| 691 |
+
2. Enter the rolling window size.
|
| 692 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 693 |
+
4. The chart will display the rolling CVaR at different confidence levels.
|
| 694 |
|
| 695 |
**Results:**
|
| 696 |
The chart shows the close prices along with the rolling CVaR at different confidence levels over time.
|
| 697 |
""")
|
| 698 |
|
| 699 |
+
fig = plot_rolling_cvar(data, window_size)
|
| 700 |
st.plotly_chart(fig)
|
| 701 |
|
| 702 |
elif selected == "Rolling Tail Ratio":
|
| 703 |
st.markdown("""
|
| 704 |
### Rolling Tail Ratio
|
| 705 |
|
| 706 |
+
This method calculates the rolling Tail Ratio.
|
| 707 |
|
| 708 |
**Methodology:**
|
| 709 |
|
|
|
|
| 717 |
where \( P_t \) is the closing price at time \( t \).
|
| 718 |
|
| 719 |
2. **Calculate Tail Ratio:**
|
| 720 |
+
- Compute the rolling Tail Ratio over a specified window:
|
| 721 |
""")
|
| 722 |
st.latex(r'''
|
| 723 |
\text{Tail Ratio}_t = \frac{|\text{Quantile}_{95}(\text{Return}_{t-n:t})|}{|\text{Quantile}_{5}(\text{Return}_{t-n:t})|}
|
| 724 |
''')
|
| 725 |
st.markdown("""
|
| 726 |
+
where \( n \) is the window size.
|
| 727 |
|
| 728 |
**How to use:**
|
| 729 |
1. Enter the stock ticker, start date, and end date.
|
| 730 |
+
2. Enter the rolling window size.
|
| 731 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 732 |
+
4. The chart will display the rolling Tail Ratio.
|
| 733 |
|
| 734 |
**Results:**
|
| 735 |
The chart shows the close prices along with the rolling Tail Ratio over time, indicating the balance between extreme positive and negative returns.
|
| 736 |
""")
|
| 737 |
|
| 738 |
+
fig = plot_rolling_tail_ratio(data, window_size)
|
| 739 |
st.plotly_chart(fig)
|
| 740 |
|
| 741 |
elif selected == "Rolling Omega Ratio":
|
| 742 |
st.markdown("""
|
| 743 |
### Rolling Omega Ratio
|
| 744 |
|
| 745 |
+
This method calculates the rolling Omega Ratio.
|
| 746 |
|
| 747 |
**Methodology:**
|
| 748 |
|
|
|
|
| 756 |
where \( P_t \) is the closing price at time \( t \).
|
| 757 |
|
| 758 |
2. **Calculate Omega Ratio:**
|
| 759 |
+
- Compute the rolling Omega Ratio over a specified window:
|
| 760 |
""")
|
| 761 |
st.latex(r'''
|
| 762 |
\text{Omega Ratio}_t = \frac{\sum (\text{Return}_{i} > MAR) (\text{Return}_{i} - MAR)}{\sum (\text{Return}_{i} < MAR) (MAR - \text{Return}_{i})}
|
| 763 |
''')
|
| 764 |
st.markdown("""
|
| 765 |
+
where \( MAR \) is the Minimum Acceptable Return and \( n \) is the window size.
|
| 766 |
|
| 767 |
**How to use:**
|
| 768 |
1. Enter the stock ticker, start date, and end date.
|
| 769 |
+
2. Enter the rolling window size and minimum acceptable return.
|
| 770 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 771 |
+
4. The chart will display the rolling Omega Ratio.
|
| 772 |
|
| 773 |
**Results:**
|
| 774 |
The chart shows the close prices along with the rolling Omega Ratio over time, indicating the risk-return trade-off.
|
| 775 |
""")
|
| 776 |
|
| 777 |
+
fig = plot_rolling_omega(data, window_size)
|
| 778 |
st.plotly_chart(fig)
|
| 779 |
|
| 780 |
elif selected == "Rolling Sortino Ratio":
|
| 781 |
st.markdown("""
|
| 782 |
### Rolling Sortino Ratio
|
| 783 |
|
| 784 |
+
This method calculates the rolling Sortino Ratio.
|
| 785 |
|
| 786 |
**Methodology:**
|
| 787 |
|
|
|
|
| 795 |
where \( P_t \) is the closing price at time \( t \).
|
| 796 |
|
| 797 |
2. **Calculate Rolling Sortino Ratio:**
|
| 798 |
+
- Compute the rolling Sortino Ratio over a specified window:
|
| 799 |
""")
|
| 800 |
st.latex(r'''
|
| 801 |
\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)}}
|
| 802 |
''')
|
| 803 |
st.markdown("""
|
| 804 |
+
where \( MAR \) is the Minimum Acceptable Return and \( n \) is the window size.
|
| 805 |
|
| 806 |
**How to use:**
|
| 807 |
1. Enter the stock ticker, start date, and end date.
|
| 808 |
+
2. Enter the rolling window size and minimum acceptable return.
|
| 809 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 810 |
+
4. The chart will display the rolling Sortino Ratio.
|
| 811 |
|
| 812 |
**Results:**
|
| 813 |
The chart shows the close prices along with the rolling Sortino Ratio over time, indicating the risk-adjusted return considering downside risk.
|
| 814 |
""")
|
| 815 |
|
| 816 |
+
fig = plot_rolling_sortino(data, window_size, MAR)
|
| 817 |
st.plotly_chart(fig)
|
| 818 |
|
| 819 |
elif selected == "Rolling Calmar Ratio":
|
| 820 |
st.markdown("""
|
| 821 |
### Rolling Calmar Ratio
|
| 822 |
|
| 823 |
+
This method calculates the rolling Calmar Ratio.
|
| 824 |
|
| 825 |
**Methodology:**
|
| 826 |
|
|
|
|
| 834 |
where \( P_t \) is the closing price at time \( t \).
|
| 835 |
|
| 836 |
2. **Calculate Rolling Calmar Ratio:**
|
| 837 |
+
- Compute the rolling Calmar Ratio over a specified window:
|
| 838 |
""")
|
| 839 |
st.latex(r'''
|
| 840 |
\text{Calmar Ratio}_t = \frac{\text{CAGR}_{t-n:t}}{\text{Max Drawdown}_{t-n:t}}
|
| 841 |
''')
|
| 842 |
st.markdown("""
|
| 843 |
+
where \( \text{CAGR} \) is the Compound Annual Growth Rate and \( \text{Max Drawdown} \) is the maximum drawdown over the window \( n \).
|
| 844 |
|
| 845 |
**How to use:**
|
| 846 |
1. Enter the stock ticker, start date, and end date.
|
| 847 |
+
2. Enter the rolling window size.
|
| 848 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 849 |
+
4. The chart will display the rolling Calmar Ratio.
|
| 850 |
|
| 851 |
**Results:**
|
| 852 |
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.
|
| 853 |
""")
|
| 854 |
|
| 855 |
+
fig = plot_rolling_calmar(data, window_size)
|
| 856 |
st.plotly_chart(fig)
|
| 857 |
|
|
|
|
| 858 |
elif selected == "Rolling Stability":
|
| 859 |
st.markdown("""
|
| 860 |
### Rolling Stability
|
| 861 |
|
| 862 |
+
This method calculates the rolling stability of returns.
|
| 863 |
|
| 864 |
**Methodology:**
|
| 865 |
|
|
|
|
| 873 |
where \( P_t \) is the closing price at time \( t \).
|
| 874 |
|
| 875 |
2. **Calculate Rolling Stability:**
|
| 876 |
+
- Compute the rolling stability over a specified window:
|
| 877 |
""")
|
| 878 |
st.latex(r'''
|
| 879 |
\text{Stability}_t = \sqrt{\frac{1}{n-1} \sum_{i=1}^{n} (\log(1 + \text{Return}_{t-i}) - \overline{\log(1 + \text{Return})})^2}
|
| 880 |
''')
|
| 881 |
st.markdown("""
|
| 882 |
+
where \( n \) is the window size and \( \overline{\log(1 + \text{Return})} \) is the mean of the log returns over the window.
|
| 883 |
|
| 884 |
**How to use:**
|
| 885 |
1. Enter the stock ticker, start date, and end date.
|
| 886 |
+
2. Enter the rolling window size.
|
| 887 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 888 |
+
4. The chart will display the rolling stability.
|
| 889 |
|
| 890 |
**Results:**
|
| 891 |
The chart shows the close prices along with the rolling stability over time, indicating the consistency of returns.
|
| 892 |
""")
|
| 893 |
|
| 894 |
+
fig = plot_rolling_stability(data, window_size)
|
| 895 |
st.plotly_chart(fig)
|
| 896 |
|
| 897 |
elif selected == "Rolling Maximum Drawdown":
|
| 898 |
st.markdown("""
|
| 899 |
### Rolling Maximum Drawdown
|
| 900 |
|
| 901 |
+
This method calculates the rolling maximum drawdown.
|
| 902 |
|
| 903 |
**Methodology:**
|
| 904 |
|
|
|
|
| 912 |
where \( P_t \) is the closing price at time \( t \).
|
| 913 |
|
| 914 |
2. **Calculate Rolling Maximum Drawdown:**
|
| 915 |
+
- Compute the cumulative returns and the maximum drawdown over a specified window:
|
| 916 |
""")
|
| 917 |
st.latex(r'''
|
| 918 |
\text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
|
|
|
|
| 925 |
|
| 926 |
**How to use:**
|
| 927 |
1. Enter the stock ticker, start date, and end date.
|
| 928 |
+
2. Enter the rolling window size.
|
| 929 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 930 |
+
4. 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, window_size)
|
| 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 upside and downside capture ratios.
|
| 944 |
|
| 945 |
**Methodology:**
|
| 946 |
|
|
|
|
| 969 |
st.markdown("""
|
| 970 |
**How to use:**
|
| 971 |
1. Enter the stock ticker, start date, and end date.
|
| 972 |
+
2. Enter the benchmark ticker and rolling window size.
|
| 973 |
3. Click 'Fetch Data' to load the stock and benchmark data.
|
| 974 |
4. The chart will display the rolling capture ratios.
|
| 975 |
|
|
|
|
| 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, window_size)
|
| 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 pain index.
|
| 988 |
|
| 989 |
**Methodology:**
|
| 990 |
|
|
|
|
| 1012 |
''')
|
| 1013 |
st.markdown("""
|
| 1014 |
4. **Calculate Rolling Pain Index:**
|
| 1015 |
+
- Compute the average drawdown over a specified window 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
|
|
|
|
| 1020 |
st.markdown("""
|
| 1021 |
**How to use:**
|
| 1022 |
1. Enter the stock ticker, start date, and end date.
|
| 1023 |
+
2. Enter the rolling window size.
|
| 1024 |
+
3. Click 'Fetch Data' to load the stock data.
|
| 1025 |
+
4. The chart will display the rolling pain index.
|
| 1026 |
|
| 1027 |
**Results:**
|
| 1028 |
The chart shows the close prices along with the rolling pain index over time, indicating the average drawdown experienced over a specified period.
|
| 1029 |
""")
|
| 1030 |
|
| 1031 |
+
fig = plot_rolling_pain_index(data, window_size)
|
| 1032 |
st.plotly_chart(fig)
|
| 1033 |
|
| 1034 |
hide_streamlit_style = """
|