Spaces:
Sleeping
Sleeping
Create app.py
Browse files
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)
|