QuantumLearner commited on
Commit
a3cffeb
·
verified ·
1 Parent(s): dea061c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +500 -612
app.py CHANGED
@@ -1,16 +1,20 @@
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, window: int) -> go.Figure:
@@ -50,11 +54,12 @@ def plot_rolling_treynor(data: pd.DataFrame, market_data: pd.DataFrame, window:
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,
@@ -69,8 +74,8 @@ def plot_rolling_treynor(data: pd.DataFrame, market_data: pd.DataFrame, window:
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
 
75
  # Align dates and remove rows with missing data
76
  aligned_data = pd.concat([data['Return'], market_data['Market_Return']], axis=1).dropna()
@@ -80,7 +85,7 @@ def plot_rolling_beta(data: pd.DataFrame, market_data: pd.DataFrame, window: int
80
  rolling_beta_ransac = []
81
  rolling_beta_ols = []
82
 
83
- for i in range(len(aligned_data) - window):
84
  X = aligned_data['Market_Return'].iloc[i:i+window].values.reshape(-1, 1)
85
  y = aligned_data['Return'].iloc[i:i+window].values
86
 
@@ -93,8 +98,8 @@ def plot_rolling_beta(data: pd.DataFrame, market_data: pd.DataFrame, window: int
93
  rolling_beta_ols.append(beta_ols)
94
 
95
  # Convert lists to series with appropriate index
96
- rolling_beta_ransac = pd.Series(rolling_beta_ransac, index=aligned_data.index[window:])
97
- rolling_beta_ols = pd.Series(rolling_beta_ols, index=aligned_data.index[window:])
98
 
99
  fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
100
  subplot_titles=('Close Price', 'Benchmark Price', 'Rolling Beta (RANSAC & OLS)'))
@@ -112,17 +117,20 @@ def plot_rolling_alpha(data: pd.DataFrame, market_data: pd.DataFrame, window: in
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'))
@@ -156,8 +164,8 @@ 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()
@@ -177,7 +185,7 @@ def plot_rolling_cvar(data: pd.DataFrame, window: int) -> go.Figure:
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'))
@@ -193,7 +201,7 @@ def plot_rolling_tail_ratio(data: pd.DataFrame, window: int) -> go.Figure:
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'))
@@ -208,7 +216,7 @@ def plot_rolling_omega(data: pd.DataFrame, window: int) -> go.Figure:
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'))
@@ -222,7 +230,7 @@ def plot_rolling_sortino(data: pd.DataFrame, window: int, MAR: float) -> go.Figu
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'))
@@ -236,7 +244,7 @@ def plot_rolling_calmar(data: pd.DataFrame, window: int) -> go.Figure:
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'))
@@ -270,6 +278,7 @@ def plot_rolling_capture(data: pd.DataFrame, market_data: pd.DataFrame, window:
270
 
271
  # Align indices
272
  market_data = market_data.reindex(data.index, method='ffill')
 
273
 
274
  def calculate_capture(stock_returns, market_returns, is_upside=True):
275
  if is_upside:
@@ -278,7 +287,7 @@ def plot_rolling_capture(data: pd.DataFrame, market_data: pd.DataFrame, window:
278
  else:
279
  relevant_returns = stock_returns[market_returns < 0]
280
  relevant_market_returns = market_returns[market_returns < 0]
281
- return relevant_returns.sum() / relevant_market_returns.sum()
282
 
283
  def compute_rolling_captures(stock_returns, market_returns, window):
284
  upside_captures = []
@@ -298,15 +307,15 @@ def plot_rolling_capture(data: pd.DataFrame, market_data: pd.DataFrame, window:
298
  upside_captures.append(upside)
299
  downside_captures.append(downside)
300
 
301
- # Padding the initial values with NaNs to make the length equal to original data
302
  nan_padding = [np.nan] * (window - 1)
303
- upside_captures = nan_padding + upside_captures
304
- downside_captures = nan_padding + downside_captures
305
 
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,
@@ -325,7 +334,7 @@ def plot_rolling_pain_index(data: pd.DataFrame, window: int) -> go.Figure:
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'))
@@ -342,9 +351,6 @@ st.title('Dynamic Risk Management Indicators')
342
 
343
  st.sidebar.title("Input Parameters")
344
 
345
- # Sidebar for method selection
346
- import datetime
347
-
348
  # Setting today's date plus one day
349
  today_plus_one = pd.to_datetime(datetime.datetime.now().date() + pd.Timedelta(days=1))
350
 
@@ -367,588 +373,470 @@ with st.sidebar.expander("Select Method", expanded=True):
367
  window_size = st.sidebar.number_input('Rolling Window Size (Days)', min_value=1, value=252,
368
  help="Enter the number of days to use for the rolling window in the selected risk indicator calculation.")
369
 
370
-
371
  # Fetch data
372
  if 'data' not in st.session_state or st.sidebar.button('Fetch Data'):
373
- st.session_state.data = fetch_stock_data(ticker, start_date, end_date)
374
-
375
- data = st.session_state.data
376
-
377
- # Additional input for methods requiring benchmark or risk-free rate
378
- if selected in ["Rolling Treynor Ratio", "Rolling Beta", "Rolling Jensen's Alpha", "Rolling Capture Ratios"]:
379
- benchmark_ticker = st.sidebar.text_input('Enter Benchmark Ticker', '^GSPC',
380
- help="Enter the ticker symbol for the benchmark index (e.g., ^GSPC for S&P 500).")
381
- if 'market_data' not in st.session_state or st.sidebar.button('Fetch Market Data'):
382
- st.session_state.market_data = fetch_stock_data(benchmark_ticker, start_date, end_date)
383
- market_data = st.session_state.market_data
384
-
385
- if selected in ["Rolling Sharpe Ratio", "Rolling Treynor Ratio", "Rolling Jensen's Alpha", "Rolling Sortino Ratio"]:
386
- risk_free_rate = st.sidebar.number_input('Risk-Free Rate (as a decimal)', min_value=0.0, value=0.0,
387
- help="Enter the risk-free rate as a decimal (e.g., 0.01 for 1%).")
388
-
389
- if selected == "Rolling Sortino Ratio":
390
- MAR = st.sidebar.number_input('Minimum Acceptable Return (MAR, as a decimal)', min_value=0.0, value=0.0,
391
- help="Enter the Minimum Acceptable Return (MAR) as a decimal (e.g., 0.02 for 2%).")
392
-
393
- # Display results based on the selected method
394
- if selected == "Rolling Volatility":
395
- st.markdown("""
396
- ### Rolling Volatility
397
-
398
- This method calculates the rolling volatility and the volatility of volatility.
399
- """)
400
-
401
- with st.expander("Methodology", expanded=False):
402
- st.markdown("""
403
- 1. **Calculate Returns:**
404
- - Compute the daily returns of the stock:
405
- """)
406
- st.latex(r'''
407
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
408
- ''')
409
- st.markdown("""
410
- where `(P_t)` is the closing price at time `(t)`.
411
-
412
- 2. **Calculate Rolling Volatility:**
413
- - Compute the rolling standard deviation of the returns over a specified window and annualize it:
414
- """)
415
- st.latex(r'''
416
- \text{Rolling Volatility}_t = \sqrt{252} \times \text{std}(\text{Return}_{t-n:t})
417
- ''')
418
- st.markdown("""
419
- where `(n)` is the window size.
420
-
421
- 3. **Calculate Volatility of Volatility:**
422
- - Compute the rolling standard deviation of the rolling volatility over the specified window:
423
- """)
424
- st.latex(r'''
425
- \text{Volatility of Volatility}_t = \text{std}(\text{Rolling Volatility}_{t-n:t})
426
- ''')
427
-
428
-
429
- fig = plot_rolling_volatility(data, window_size)
430
- st.plotly_chart(fig)
431
-
432
- # Display results based on the selected method
433
- if selected == "Rolling Sharpe Ratio":
434
- st.markdown("""
435
- ### Rolling Sharpe Ratio
436
-
437
- This method calculates the rolling Sharpe Ratio.
438
- """)
439
-
440
- with st.expander("Methodology", expanded=False):
441
- st.markdown("""
442
- 1. **Calculate Returns:**
443
- - Compute the daily returns of the stock:
444
- """)
445
- st.latex(r'''
446
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
447
- ''')
448
- st.markdown("""
449
- where `(P_t)` is the closing price at time `(t)`.
450
-
451
- 2. **Calculate Rolling Average Return:**
452
- - Compute the rolling mean of the returns over the specified window:
453
- """)
454
- st.latex(r'''
455
- \text{Rolling Average Return}_t = \text{mean}(\text{Return}_{t-n:t})
456
- ''')
457
- st.markdown("""
458
- where `(n)` is the window size.
459
-
460
- 3. **Calculate Rolling Standard Deviation:**
461
- - Compute the rolling standard deviation of the returns over the specified window:
462
- """)
463
- st.latex(r'''
464
- \text{Rolling Std Dev}_t = \text{std}(\text{Return}_{t-n:t})
465
- ''')
466
- st.markdown("""
467
- where `(n)` is the window size.
468
-
469
- 4. **Calculate Rolling Sharpe Ratio:**
470
- - Annualize the Sharpe Ratio:
471
- """)
472
- st.latex(r'''
473
- \text{Rolling Sharpe Ratio}_t = \frac{\text{Rolling Average Return}_t - R_f}{\text{Rolling Std Dev}_t} \times \sqrt{252}
474
- ''')
475
- st.markdown("""
476
- where `(R_f)` is the risk-free rate.
477
- """)
478
-
479
- fig = plot_rolling_sharpe(data, window_size, risk_free_rate)
480
- st.plotly_chart(fig)
481
-
482
- # Display results based on the selected method
483
- if selected == "Rolling Treynor Ratio":
484
- st.markdown("""
485
- ### Rolling Treynor Ratio
486
-
487
- This method calculates the rolling Treynor Ratio.
488
- """)
489
-
490
- with st.expander("Methodology", expanded=False):
491
- st.markdown("""
492
- 1. **Calculate Returns:**
493
- - Compute the daily returns of the stock and the benchmark:
494
- """)
495
- st.latex(r'''
496
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
497
- ''')
498
- st.markdown("""
499
- where `(P_t)` is the closing price at time `(t)`.
500
-
501
- 2. **Calculate Beta:**
502
- - 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:
503
- """)
504
- st.latex(r'''
505
- \beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
506
- ''')
507
- st.markdown("""
508
- where `(n)` is the window size.
509
-
510
- 3. **Calculate Average Rolling Returns:**
511
- - Compute the rolling mean of the stock returns over the same window:
512
- """)
513
- st.latex(r'''
514
- \text{Average Rolling Return}_t = \frac{1}{n} \sum_{i=t-n+1}^{t} \text{Return}_{\text{stock}, i}
515
- ''')
516
- st.markdown("""
517
- where `(n)` is the window size.
518
-
519
- 4. **Calculate Treynor Ratio:**
520
- - Compute the Treynor Ratio using the risk-free rate `(R_f)`:
521
- """)
522
- st.latex(r'''
523
- \text{Treynor Ratio}_t = \frac{\text{Average Rolling Return}_t - R_f}{\beta_t}
524
- ''')
525
- st.markdown("""
526
- where `(R_f)` is the risk-free rate.
527
- """)
528
-
529
- fig = plot_rolling_treynor(data, market_data, window_size, risk_free_rate)
530
- st.plotly_chart(fig)
531
-
532
- # Display results based on the selected method
533
- if selected == "Rolling Beta":
534
- st.markdown("""
535
- ### Rolling Beta
536
-
537
- This method calculates the rolling beta of a stock's returns against a benchmark using RANSAC and OLS methods.
538
- """)
539
-
540
- with st.expander("Methodology", expanded=False):
541
- st.markdown("""
542
- 1. **Calculate Returns:**
543
- - Compute the daily returns of the stock and the benchmark:
544
- """)
545
- st.latex(r'''
546
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
547
- ''')
548
- st.markdown("""
549
- where `(P_t)` is the closing price at time `(t)`.
550
-
551
- 2. **Calculate Rolling Beta using OLS:**
552
- - Perform a linear regression of the stock returns against the benchmark returns over a specified window:
553
- """)
554
- st.latex(r'''
555
- \beta_{OLS} = \frac{\text{Cov}(\text{Return}_{\text{stock}}, \text{Return}_{\text{benchmark}})}{\text{Var}(\text{Return}_{\text{benchmark}})}
556
- ''')
557
- st.markdown("""
558
- where `(n)` is the window size.
559
-
560
- 3. **Calculate Rolling Beta using RANSAC:**
561
- - Use the RANSAC algorithm to robustly estimate the beta, reducing the influence of outliers:
562
- """)
563
- st.latex(r'''
564
- \beta_{RANSAC} = \text{RANSAC}(\text{Return}_{\text{benchmark}}, \text{Return}_{\text{stock}})
565
- ''')
566
- st.markdown("""
567
- RANSAC algorithm iteratively fits the model and removes outliers.
568
- """)
569
-
570
- fig = plot_rolling_beta(data, market_data, window_size)
571
- st.plotly_chart(fig)
572
-
573
- elif selected == "Rolling Jensen's Alpha":
574
- st.markdown("""
575
- ### Rolling Jensen's Alpha
576
-
577
- This method calculates the rolling Jensen's Alpha.
578
- """)
579
-
580
- with st.expander("Methodology", expanded=False):
581
- st.markdown("""
582
- 1. **Calculate Returns:**
583
- - Compute the daily returns of the stock and the benchmark:
584
- """)
585
- st.latex(r'''
586
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
587
- ''')
588
- st.markdown("""
589
- where `(P_t)` is the closing price at time `(t)`.
590
-
591
- 2. **Calculate Beta:**
592
- - Compute the rolling beta of the stock returns against the benchmark returns over a specified window:
593
- """)
594
- st.latex(r'''
595
- \beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
596
- ''')
597
- st.markdown("""
598
- where `(n)` is the window size.
599
-
600
- 3. **Calculate Expected Return:**
601
- - Compute the expected return of the stock based on the CAPM model:
602
- """)
603
- st.latex(r'''
604
- \text{Expected Return}_t = R_f + \beta_t (\text{Return}_{\text{benchmark}} - R_f)
605
- ''')
606
- st.markdown("""
607
- where `(R_f)` is the risk-free rate.
608
-
609
- 4. **Calculate Jensen's Alpha:**
610
- - Compute the Jensen's Alpha as the difference between the actual return and the expected return:
611
- """)
612
- st.latex(r'''
613
- \alpha_t = \text{Return}_{\text{stock}, t} - \text{Expected Return}_t
614
- ''')
615
- st.markdown("""
616
- where `(α_t)` is Jensen's Alpha at time `(t)`.
617
- """)
618
-
619
- fig = plot_rolling_alpha(data, market_data, window_size, risk_free_rate)
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 Value at Risk (VaR) at different confidence levels.
627
- """)
628
-
629
- with st.expander("Methodology", expanded=False):
630
- st.markdown("""
631
- 1. **Calculate Returns:**
632
- - Compute the daily returns of the stock:
633
- """)
634
- st.latex(r'''
635
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
636
- ''')
637
- st.markdown("""
638
- where `(P_t)` is the closing price at time `(t)`.
639
-
640
- 2. **Calculate Rolling VaR:**
641
- - Compute the rolling quantile of the returns over a specified window for different confidence levels:
642
- """)
643
- st.latex(r'''
644
- \text{VaR}_{\alpha, t} = \text{Quantile}_{\alpha}(\text{Return}_{t-n:t})
645
- ''')
646
- st.markdown("""
647
- where `(α)` is the confidence level and `(n)` is the window size.
648
- """)
649
-
650
- fig = plot_rolling_var(data, window_size)
651
- st.plotly_chart(fig)
652
-
653
- elif selected == "Rolling Conditional VaR":
654
- st.markdown("""
655
- ### Rolling Conditional Value at Risk (CVaR)
656
-
657
- This method calculates the rolling Conditional Value at Risk (CVaR) at different confidence levels.
658
- """)
659
-
660
- with st.expander("Methodology", expanded=False):
661
- st.markdown("""
662
- 1. **Calculate Returns:**
663
- - Compute the daily returns of the stock:
664
- """)
665
- st.latex(r'''
666
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
667
- ''')
668
- st.markdown("""
669
- where `(P_t)` is the closing price at time `(t)`.
670
-
671
- 2. **Calculate Rolling CVaR:**
672
- - Compute the average of the returns that are below the VaR threshold over a specified window:
673
- """)
674
- st.latex(r'''
675
- \text{CVaR}_{\alpha, t} = \frac{1}{n} \sum_{i=1}^{n} \text{Return}_{i} \text{ where } \text{Return}_{i} < \text{VaR}_{\alpha, t}
676
- ''')
677
- st.markdown("""
678
- where `(n)` is the number of returns below the VaR threshold and `(α)` is the confidence level.
679
- """)
680
-
681
- fig = plot_rolling_cvar(data, window_size)
682
- st.plotly_chart(fig)
683
-
684
- elif selected == "Rolling Tail Ratio":
685
- st.markdown("""
686
- ### Rolling Tail Ratio
687
-
688
- This method calculates the rolling Tail Ratio.
689
- """)
690
-
691
- with st.expander("Methodology", expanded=False):
692
- st.markdown("""
693
- 1. **Calculate Returns:**
694
- - Compute the daily returns of the stock:
695
- """)
696
- st.latex(r'''
697
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
698
- ''')
699
- st.markdown("""
700
- where `(P_t)` is the closing price at time `(t)`.
701
-
702
- 2. **Calculate Tail Ratio:**
703
- - Compute the rolling Tail Ratio over a specified window:
704
- """)
705
- st.latex(r'''
706
- \text{Tail Ratio}_t = \frac{|\text{Quantile}_{95}(\text{Return}_{t-n:t})|}{|\text{Quantile}_{5}(\text{Return}_{t-n:t})|}
707
- ''')
708
- st.markdown("""
709
- where `(n)` is the window size.
710
- """)
711
-
712
- fig = plot_rolling_tail_ratio(data, window_size)
713
- st.plotly_chart(fig)
714
-
715
- elif selected == "Rolling Omega Ratio":
716
- st.markdown("""
717
- ### Rolling Omega Ratio
718
-
719
- This method calculates the rolling Omega Ratio.
720
- """)
721
-
722
- with st.expander("Methodology", expanded=False):
723
- st.markdown("""
724
- 1. **Calculate Returns:**
725
- - Compute the daily returns of the stock:
726
- """)
727
- st.latex(r'''
728
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
729
- ''')
730
- st.markdown("""
731
- where `(P_t)` is the closing price at time `(t)`.
732
-
733
- 2. **Calculate Omega Ratio:**
734
- - Compute the rolling Omega Ratio over a specified window:
735
- """)
736
- st.latex(r'''
737
- \text{Omega Ratio}_t = \frac{\sum (\text{Return}_{i} > MAR) (\text{Return}_{i} - MAR)}{\sum (\text{Return}_{i} < MAR) (MAR - \text{Return}_{i})}
738
- ''')
739
- st.markdown("""
740
- where `(MAR)` is the Minimum Acceptable Return and `(n)` is the window size.
741
- """)
742
-
743
- fig = plot_rolling_omega(data, window_size)
744
- st.plotly_chart(fig)
745
-
746
- elif selected == "Rolling Sortino Ratio":
747
- st.markdown("""
748
- ### Rolling Sortino Ratio
749
-
750
- This method calculates the rolling Sortino Ratio.
751
- """)
752
-
753
- with st.expander("Methodology", expanded=False):
754
- st.markdown("""
755
- 1. **Calculate Returns:**
756
- - Compute the daily returns of the stock:
757
- """)
758
- st.latex(r'''
759
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
760
- ''')
761
- st.markdown("""
762
- where `(P_t)` is the closing price at time `(t)`.
763
-
764
- 2. **Calculate Rolling Sortino Ratio:**
765
- - Compute the rolling Sortino Ratio over a specified window:
766
- """)
767
- st.latex(r'''
768
- \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)}}
769
- ''')
770
- st.markdown("""
771
- where `(MAR)` is the Minimum Acceptable Return and `(n)` is the window size.
772
- """)
773
-
774
- fig = plot_rolling_sortino(data, window_size, MAR)
775
- st.plotly_chart(fig)
776
-
777
- elif selected == "Rolling Calmar Ratio":
778
- st.markdown("""
779
- ### Rolling Calmar Ratio
780
-
781
- This method calculates the rolling Calmar Ratio.
782
- """)
783
-
784
- with st.expander("Methodology", expanded=False):
785
- st.markdown("""
786
- 1. **Calculate Returns:**
787
- - Compute the daily returns of the stock:
788
- """)
789
- st.latex(r'''
790
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
791
- ''')
792
- st.markdown("""
793
- where `(P_t)` is the closing price at time `(t)`.
794
-
795
- 2. **Calculate Rolling Calmar Ratio:**
796
- - Compute the rolling Calmar Ratio over a specified window:
797
- """)
798
- st.latex(r'''
799
- \text{Calmar Ratio}_t = \frac{\text{CAGR}_{t-n:t}}{\text{Max Drawdown}_{t-n:t}}
800
- ''')
801
- st.markdown("""
802
- where `CAGR` is the Compound Annual Growth Rate and `Max Drawdown` is the maximum drawdown over the window `(n)`.
803
- """)
804
-
805
- fig = plot_rolling_calmar(data, window_size)
806
- st.plotly_chart(fig)
807
-
808
- elif selected == "Rolling Stability":
809
- st.markdown("""
810
- ### Rolling Stability
811
-
812
- This method calculates the rolling stability of returns.
813
- """)
814
-
815
- with st.expander("Methodology", expanded=False):
816
- st.markdown("""
817
- 1. **Calculate Returns:**
818
- - Compute the daily returns of the stock:
819
- """)
820
- st.latex(r'''
821
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
822
- ''')
823
- st.markdown("""
824
- where `(P_t)` is the closing price at time `(t)`.
825
-
826
- 2. **Calculate Rolling Stability:**
827
- - Compute the rolling stability over a specified window:
828
- """)
829
- st.latex(r'''
830
- \text{Stability}_t = \sqrt{\frac{1}{n-1} \sum_{i=1}^{n} (\log(1 + \text{Return}_{t-i}) - \overline{\log(1 + \text{Return})})^2}
831
- ''')
832
- st.markdown("""
833
- where `(n)` is the window size and `\overline{\log(1 + \text{Return})}` is the mean of the log returns over the window.
834
- """)
835
-
836
- fig = plot_rolling_stability(data, window_size)
837
- st.plotly_chart(fig)
838
-
839
- elif selected == "Rolling Maximum Drawdown":
840
- st.markdown("""
841
- ### Rolling Maximum Drawdown
842
-
843
- This method calculates the rolling maximum drawdown.
844
- """)
845
-
846
- with st.expander("Methodology", expanded=False):
847
- st.markdown("""
848
- 1. **Calculate Returns:**
849
- - Compute the daily returns of the stock:
850
- """)
851
- st.latex(r'''
852
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
853
- ''')
854
- st.markdown("""
855
- where `(P_t)` is the closing price at time `(t)`.
856
-
857
- 2. **Calculate Rolling Maximum Drawdown:**
858
- - Compute the cumulative returns and the maximum drawdown over a specified window:
859
- """)
860
- st.latex(r'''
861
- \text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
862
- ''')
863
- st.latex(r'''
864
- \text{Max Drawdown}_t = \frac{\text{Cumulative Return}_t - \text{Rolling Max Cumulative Return}_t}{\text{Rolling Max Cumulative Return}_t}
865
- ''')
866
- st.markdown("""
867
- where the Rolling Max Cumulative Return is the maximum cumulative return over the window.
868
- """)
869
-
870
- fig = plot_rolling_drawdown(data, window_size)
871
- st.plotly_chart(fig)
872
-
873
- elif selected == "Rolling Capture Ratios":
874
- st.markdown("""
875
- ### Rolling Capture Ratios
876
-
877
- This method calculates the rolling upside and downside capture ratios.
878
- """)
879
-
880
- with st.expander("Methodology", expanded=False):
881
- st.markdown("""
882
- 1. **Calculate Returns:**
883
- - Compute the daily returns of the stock and the benchmark:
884
- """)
885
- st.latex(r'''
886
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
887
- ''')
888
- st.markdown("""
889
- where `(P_t)` is the closing price at time `(t)`.
890
-
891
- 2. **Calculate Upside Capture Ratio:**
892
- - 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:
893
- """)
894
- st.latex(r'''
895
- \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
896
- ''')
897
- st.markdown("""
898
-
899
- 3. **Calculate Downside Capture Ratio:**
900
- - 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:
901
- """)
902
- st.latex(r'''
903
- \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
904
- ''')
905
-
906
- fig = plot_rolling_capture(data, market_data, window_size)
907
- st.plotly_chart(fig)
908
-
909
- elif selected == "Rolling Pain Index":
910
- st.markdown("""
911
- ### Rolling Pain Index
912
-
913
- This method calculates the rolling pain index.
914
- """)
915
-
916
- with st.expander("Methodology", expanded=False):
917
- st.markdown("""
918
- 1. **Calculate Returns:**
919
- - Compute the daily returns of the stock:
920
- """)
921
- st.latex(r'''
922
- \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
923
- ''')
924
- st.markdown("""
925
- where `(P_t)` is the closing price at time `(t)`.
926
-
927
- 2. **Calculate Cumulative Returns:**
928
- - Compute the cumulative returns over time:
929
- """)
930
- st.latex(r'''
931
- \text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
932
- ''')
933
- st.markdown("""
934
-
935
- 3. **Calculate Drawdowns:**
936
- - Determine the drawdowns by comparing the cumulative returns to their running maximum:
937
- """)
938
- st.latex(r'''
939
- \text{Drawdown}_t = \frac{\text{Cumulative Return}_t - \text{Running Max Cumulative Return}_t}{\text{Running Max Cumulative Return}_t}
940
- ''')
941
- st.markdown("""
942
-
943
- 4. **Calculate Rolling Pain Index:**
944
- - Compute the average drawdown over a specified window where the drawdown is negative:
945
- """)
946
- st.latex(r'''
947
- \text{Pain Index} = \frac{1}{n} \sum_{i=1}^{n} \text{Drawdown}_i \quad \text{for } \text{Drawdown}_i < 0
948
- ''')
949
-
950
- fig = plot_rolling_pain_index(data, window_size)
951
- st.plotly_chart(fig)
952
 
953
  hide_streamlit_style = """
954
  <style>
@@ -956,4 +844,4 @@ hide_streamlit_style = """
956
  footer {visibility: hidden;}
957
  </style>
958
  """
959
- st.markdown(hide_streamlit_style, unsafe_allow_html=True)
 
1
  import yfinance as yf
2
  import numpy as np
3
  import pandas as pd
 
4
  import streamlit as st
5
  from sklearn.linear_model import RANSACRegressor, LinearRegression
6
  from scipy.stats import linregress
7
  import plotly.graph_objects as go
8
  from plotly.subplots import make_subplots
9
+ import datetime
10
 
11
  # Helper function to fetch stock data
12
  def fetch_stock_data(ticker: str, start_date: str, end_date: str) -> pd.DataFrame:
13
+ """Fetch stock data from Yahoo Finance."""
14
+ data = yf.download(ticker, start=start_date, end=end_date, auto_adjust=False)
15
+ if isinstance(data.columns, pd.MultiIndex):
16
+ data.columns = data.columns.get_level_values(0)
17
+ return data
18
 
19
  # Helper function to plot rolling volatility and volatility of volatility
20
  def plot_rolling_volatility(data: pd.DataFrame, window: int) -> go.Figure:
 
54
 
55
  # Align indices
56
  market_data = market_data.reindex(data.index, method='ffill')
57
+ aligned_data = pd.concat([data['Return'], market_data['Return']], axis=1).dropna()
58
 
59
+ covariance = aligned_data['Return'].rolling(window=window).cov(aligned_data['Return'].rename('Market_Return'))
60
+ variance = aligned_data['Return'].rolling(window=window).var()
61
  rolling_beta = covariance / variance
62
+ avg_rolling_returns = aligned_data['Return'].rolling(window=window).mean()
63
  data['Rolling_Treynor_Ratio'] = (avg_rolling_returns - risk_free_rate) / rolling_beta
64
 
65
  fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
 
74
 
75
  # Helper function to calculate and plot rolling beta
76
  def plot_rolling_beta(data: pd.DataFrame, market_data: pd.DataFrame, window: int) -> go.Figure:
77
+ data['Return'] = data['Close'].pct_change()
78
+ market_data['Market_Return'] = market_data['Close'].pct_change()
79
 
80
  # Align dates and remove rows with missing data
81
  aligned_data = pd.concat([data['Return'], market_data['Market_Return']], axis=1).dropna()
 
85
  rolling_beta_ransac = []
86
  rolling_beta_ols = []
87
 
88
+ for i in range(len(aligned_data) - window + 1):
89
  X = aligned_data['Market_Return'].iloc[i:i+window].values.reshape(-1, 1)
90
  y = aligned_data['Return'].iloc[i:i+window].values
91
 
 
98
  rolling_beta_ols.append(beta_ols)
99
 
100
  # Convert lists to series with appropriate index
101
+ rolling_beta_ransac = pd.Series(rolling_beta_ransac, index=aligned_data.index[window-1:])
102
+ rolling_beta_ols = pd.Series(rolling_beta_ols, index=aligned_data.index[window-1:])
103
 
104
  fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
105
  subplot_titles=('Close Price', 'Benchmark Price', 'Rolling Beta (RANSAC & OLS)'))
 
117
  data['Return'] = data['Close'].pct_change()
118
  market_data['Return'] = market_data['Close'].pct_change()
119
 
120
+ # Align indices
121
+ market_data = market_data.reindex(data.index, method='ffill')
122
+ aligned_data = pd.concat([data['Return'], market_data['Return']], axis=1).dropna()
123
 
124
+ rolling_alpha = []
125
+ for i in range(len(aligned_data) - window + 1):
126
+ window_stock = aligned_data['Return'].iloc[i:i + window]
127
+ window_market = aligned_data['Return'].iloc[i:i + window]
128
  beta = window_stock.cov(window_market) / window_market.var()
129
  expected_return = risk_free_rate + beta * (window_market.mean() - risk_free_rate)
130
  alpha = window_stock.mean() - expected_return
131
  rolling_alpha.append(alpha)
132
 
133
+ rolling_alpha = pd.Series(rolling_alpha, index=aligned_data.index[window-1:])
134
 
135
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
136
  subplot_titles=('Close Price', 'Rolling Jensen\'s Alpha'))
 
164
  data['Return'] = data['Close'].pct_change()
165
 
166
  def conditional_var(x, alpha=0.05):
167
+ var = np.percentile(x.dropna(), alpha * 100)
168
+ return np.mean(x[x < var]) if len(x[x < var]) > 0 else np.nan
169
 
170
  rolling_cvar_95 = data['Return'].rolling(window).apply(conditional_var, raw=True).dropna()
171
  rolling_cvar_90 = data['Return'].rolling(window).apply(lambda x: conditional_var(x, alpha=0.1), raw=True).dropna()
 
185
  # Helper function to plot rolling Tail Ratio
186
  def plot_rolling_tail_ratio(data: pd.DataFrame, window: int) -> go.Figure:
187
  data['Return'] = data['Close'].pct_change()
188
+ tail_ratio = data['Return'].rolling(window).apply(lambda x: np.abs(np.percentile(x.dropna(), 95)) / np.abs(np.percentile(x.dropna(), 5)) if len(x.dropna()) > 0 else np.nan).dropna()
189
 
190
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
191
  subplot_titles=('Close Price', 'Rolling Tail Ratio'))
 
201
  def plot_rolling_omega(data: pd.DataFrame, window: int) -> go.Figure:
202
  data['Return'] = data['Close'].pct_change()
203
  MAR = 0 # Minimum Acceptable Return
204
+ omega_ratio = data['Return'].rolling(window).apply(lambda x: np.sum(x[x > MAR] - MAR) / np.sum(MAR - x[x < MAR]) if np.sum(x[x < MAR]) != 0 else np.inf).dropna()
205
 
206
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
207
  subplot_titles=('Close Price', 'Rolling Omega Ratio'))
 
216
  # Helper function to plot rolling Sortino Ratio
217
  def plot_rolling_sortino(data: pd.DataFrame, window: int, MAR: float) -> go.Figure:
218
  data['Return'] = data['Close'].pct_change()
219
+ sortino_ratio = data['Return'].rolling(window).apply(lambda x: np.sqrt(252) * np.mean(x - MAR) / np.sqrt(np.mean(np.minimum(0, x - MAR) ** 2)) if np.mean(np.minimum(0, x - MAR) ** 2) > 0 else np.inf).dropna()
220
 
221
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
222
  subplot_titles=('Close Price', 'Rolling Sortino Ratio'))
 
230
  # Helper function to plot rolling Calmar Ratio
231
  def plot_rolling_calmar(data: pd.DataFrame, window: int) -> go.Figure:
232
  data['Return'] = data['Close'].pct_change()
233
+ calmar_ratio = data['Return'].rolling(window).apply(lambda x: (1 + x).prod() ** (252.0 / len(x)) / np.abs(np.min((1 + x).cumprod() / (1 + x).cumprod().cummax()) - 1) if np.min((1 + x).cumprod() / (1 + x).cumprod().cummax()) < 1 else np.inf).dropna()
234
 
235
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
236
  subplot_titles=('Close Price', 'Rolling Calmar Ratio'))
 
244
  # Helper function to plot rolling stability
245
  def plot_rolling_stability(data: pd.DataFrame, window: int) -> go.Figure:
246
  data['Return'] = data['Close'].pct_change()
247
+ stability = data['Return'].rolling(window).apply(lambda x: np.std(np.log1p(x.dropna()).cumsum() - linregress(np.arange(len(x.dropna())), np.log1p(x.dropna()).cumsum()).intercept - linregress(np.arange(len(x.dropna())), np.log1p(x.dropna()).cumsum()).slope * np.arange(len(x.dropna()))) if len(x.dropna()) > 1 else np.nan).dropna()
248
 
249
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
250
  subplot_titles=('Close Price', 'Rolling Stability'))
 
278
 
279
  # Align indices
280
  market_data = market_data.reindex(data.index, method='ffill')
281
+ aligned_data = pd.concat([data['Return'], market_data['Return']], axis=1).dropna()
282
 
283
  def calculate_capture(stock_returns, market_returns, is_upside=True):
284
  if is_upside:
 
287
  else:
288
  relevant_returns = stock_returns[market_returns < 0]
289
  relevant_market_returns = market_returns[market_returns < 0]
290
+ return relevant_returns.sum() / relevant_market_returns.sum() if len(relevant_market_returns) > 0 else np.nan
291
 
292
  def compute_rolling_captures(stock_returns, market_returns, window):
293
  upside_captures = []
 
307
  upside_captures.append(upside)
308
  downside_captures.append(downside)
309
 
310
+ # Padding the initial values with NaNs to match index length
311
  nan_padding = [np.nan] * (window - 1)
312
+ upside_captures = pd.Series(nan_padding + upside_captures, index=stock_returns.index)
313
+ downside_captures = pd.Series(nan_padding + downside_captures, index=stock_returns.index)
314
 
315
  return upside_captures, downside_captures
316
 
317
  data['rolling_upside_capture'], data['rolling_downside_capture'] = compute_rolling_captures(
318
+ aligned_data['Return'], aligned_data['Return'].rename('Market_Return'), window
319
  )
320
 
321
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
 
334
  cumulative_return = (1 + data['Return']).cumprod()
335
  running_max = cumulative_return.cummax()
336
  drawdown = (cumulative_return - running_max) / running_max
337
+ pain_index = drawdown.rolling(window).apply(lambda x: np.mean(x[x < 0]) if len(x[x < 0]) > 0 else 0).dropna()
338
 
339
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
340
  subplot_titles=('Close Price', 'Rolling Pain Index'))
 
351
 
352
  st.sidebar.title("Input Parameters")
353
 
 
 
 
354
  # Setting today's date plus one day
355
  today_plus_one = pd.to_datetime(datetime.datetime.now().date() + pd.Timedelta(days=1))
356
 
 
373
  window_size = st.sidebar.number_input('Rolling Window Size (Days)', min_value=1, value=252,
374
  help="Enter the number of days to use for the rolling window in the selected risk indicator calculation.")
375
 
 
376
  # Fetch data
377
  if 'data' not in st.session_state or st.sidebar.button('Fetch Data'):
378
+ data = fetch_stock_data(ticker, start_date, end_date)
379
+ if data.empty:
380
+ st.error(f"No data returned for {ticker} from {start_date} to {end_date}")
381
+ else:
382
+ st.session_state.data = data
383
+
384
+ if 'data' in st.session_state and not st.session_state.data.empty:
385
+ data = st.session_state.data
386
+
387
+ # Additional input for methods requiring benchmark or risk-free rate
388
+ if selected in ["Rolling Treynor Ratio", "Rolling Beta", "Rolling Jensen's Alpha", "Rolling Capture Ratios"]:
389
+ benchmark_ticker = st.sidebar.text_input('Enter Benchmark Ticker', '^GSPC',
390
+ help="Enter the ticker symbol for the benchmark index (e.g., ^GSPC for S&P 500).")
391
+ if 'market_data' not in st.session_state or st.sidebar.button('Fetch Market Data'):
392
+ market_data = fetch_stock_data(benchmark_ticker, start_date, end_date)
393
+ if market_data.empty:
394
+ st.error(f"No data returned for {benchmark_ticker} from {start_date} to {end_date}")
395
+ else:
396
+ st.session_state.market_data = market_data
397
+ if 'market_data' in st.session_state and not st.session_state.market_data.empty:
398
+ market_data = st.session_state.market_data
399
+
400
+ if selected in ["Rolling Sharpe Ratio", "Rolling Treynor Ratio", "Rolling Jensen's Alpha", "Rolling Sortino Ratio"]:
401
+ risk_free_rate = st.sidebar.number_input('Risk-Free Rate (as a decimal)', min_value=0.0, value=0.0,
402
+ help="Enter the risk-free rate as a decimal (e.g., 0.01 for 1%).")
403
+
404
+ if selected == "Rolling Sortino Ratio":
405
+ MAR = st.sidebar.number_input('Minimum Acceptable Return (MAR, as a decimal)', min_value=0.0, value=0.0,
406
+ help="Enter the Minimum Acceptable Return (MAR) as a decimal (e.g., 0.02 for 2%).")
407
+
408
+ # Display results based on the selected method
409
+ if selected == "Rolling Volatility":
410
+ st.markdown("""
411
+ ### Rolling Volatility
412
+ This method calculates the rolling volatility and the volatility of volatility.
413
+ """)
414
+ with st.expander("Methodology", expanded=False):
415
+ st.markdown("""
416
+ 1. **Calculate Returns:**
417
+ - Compute the daily returns of the stock:
418
+ """)
419
+ st.latex(r'''
420
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
421
+ ''')
422
+ st.markdown("""
423
+ where `(P_t)` is the closing price at time `(t)`.
424
+ 2. **Calculate Rolling Volatility:**
425
+ - Compute the rolling standard deviation of the returns over a specified window and annualize it:
426
+ """)
427
+ st.latex(r'''
428
+ \text{Rolling Volatility}_t = \sqrt{252} \times \text{std}(\text{Return}_{t-n:t})
429
+ ''')
430
+ st.markdown("""
431
+ where `(n)` is the window size.
432
+ 3. **Calculate Volatility of Volatility:**
433
+ - Compute the rolling standard deviation of the rolling volatility over the specified window:
434
+ """)
435
+ st.latex(r'''
436
+ \text{Volatility of Volatility}_t = \text{std}(\text{Rolling Volatility}_{t-n:t})
437
+ ''')
438
+ fig = plot_rolling_volatility(data, window_size)
439
+ st.plotly_chart(fig)
440
+
441
+ elif selected == "Rolling Sharpe Ratio":
442
+ st.markdown("""
443
+ ### Rolling Sharpe Ratio
444
+ This method calculates the rolling Sharpe Ratio.
445
+ """)
446
+ with st.expander("Methodology", expanded=False):
447
+ st.markdown("""
448
+ 1. **Calculate Returns:**
449
+ - Compute the daily returns of the stock:
450
+ """)
451
+ st.latex(r'''
452
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
453
+ ''')
454
+ st.markdown("""
455
+ where `(P_t)` is the closing price at time `(t)`.
456
+ 2. **Calculate Rolling Average Return:**
457
+ - Compute the rolling mean of the returns over the specified window:
458
+ """)
459
+ st.latex(r'''
460
+ \text{Rolling Average Return}_t = \text{mean}(\text{Return}_{t-n:t})
461
+ ''')
462
+ st.markdown("""
463
+ where `(n)` is the window size.
464
+ 3. **Calculate Rolling Standard Deviation:**
465
+ - Compute the rolling standard deviation of the returns over the specified window:
466
+ """)
467
+ st.latex(r'''
468
+ \text{Rolling Std Dev}_t = \text{std}(\text{Return}_{t-n:t})
469
+ ''')
470
+ st.markdown("""
471
+ 4. **Calculate Rolling Sharpe Ratio:**
472
+ - Annualize the Sharpe Ratio:
473
+ """)
474
+ st.latex(r'''
475
+ \text{Rolling Sharpe Ratio}_t = \frac{\text{Rolling Average Return}_t - R_f}{\text{Rolling Std Dev}_t} \times \sqrt{252}
476
+ ''')
477
+ st.markdown("""
478
+ where `(R_f)` is the risk-free rate.
479
+ """)
480
+ fig = plot_rolling_sharpe(data, window_size, risk_free_rate)
481
+ st.plotly_chart(fig)
482
+
483
+ elif selected == "Rolling Treynor Ratio" and 'market_data' in st.session_state:
484
+ st.markdown("""
485
+ ### Rolling Treynor Ratio
486
+ This method calculates the rolling Treynor Ratio.
487
+ """)
488
+ with st.expander("Methodology", expanded=False):
489
+ st.markdown("""
490
+ 1. **Calculate Returns:**
491
+ - Compute the daily returns of the stock and the benchmark:
492
+ """)
493
+ st.latex(r'''
494
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
495
+ ''')
496
+ st.markdown("""
497
+ 2. **Calculate Beta:**
498
+ - 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:
499
+ """)
500
+ st.latex(r'''
501
+ \beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
502
+ ''')
503
+ st.markdown("""
504
+ 3. **Calculate Average Rolling Returns:**
505
+ - Compute the rolling mean of the stock returns over the same window:
506
+ """)
507
+ st.latex(r'''
508
+ \text{Average Rolling Return}_t = \frac{1}{n} \sum_{i=t-n+1}^{t} \text{Return}_{\text{stock}, i}
509
+ ''')
510
+ st.markdown("""
511
+ 4. **Calculate Treynor Ratio:**
512
+ - Compute the Treynor Ratio using the risk-free rate `(R_f)`:
513
+ """)
514
+ st.latex(r'''
515
+ \text{Treynor Ratio}_t = \frac{\text{Average Rolling Return}_t - R_f}{\beta_t}
516
+ ''')
517
+ fig = plot_rolling_treynor(data, market_data, window_size, risk_free_rate)
518
+ st.plotly_chart(fig)
519
+
520
+ elif selected == "Rolling Beta" and 'market_data' in st.session_state:
521
+ st.markdown("""
522
+ ### Rolling Beta
523
+ This method calculates the rolling beta of a stock's returns against a benchmark using RANSAC and OLS methods.
524
+ """)
525
+ with st.expander("Methodology", expanded=False):
526
+ st.markdown("""
527
+ 1. **Calculate Returns:**
528
+ - Compute the daily returns of the stock and the benchmark:
529
+ """)
530
+ st.latex(r'''
531
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
532
+ ''')
533
+ st.markdown("""
534
+ 2. **Calculate Rolling Beta using OLS:**
535
+ - Perform a linear regression of the stock returns against the benchmark returns over a specified window:
536
+ """)
537
+ st.latex(r'''
538
+ \beta_{OLS} = \frac{\text{Cov}(\text{Return}_{\text{stock}}, \text{Return}_{\text{benchmark}})}{\text{Var}(\text{Return}_{\text{benchmark}})}
539
+ ''')
540
+ st.markdown("""
541
+ 3. **Calculate Rolling Beta using RANSAC:**
542
+ - Use the RANSAC algorithm to robustly estimate the beta, reducing the influence of outliers:
543
+ """)
544
+ st.latex(r'''
545
+ \beta_{RANSAC} = \text{RANSAC}(\text{Return}_{\text{benchmark}}, \text{Return}_{\text{stock}})
546
+ ''')
547
+ fig = plot_rolling_beta(data, market_data, window_size)
548
+ st.plotly_chart(fig)
549
+
550
+ elif selected == "Rolling Jensen's Alpha" and 'market_data' in st.session_state:
551
+ st.markdown("""
552
+ ### Rolling Jensen's Alpha
553
+ This method calculates the rolling Jensen's Alpha.
554
+ """)
555
+ with st.expander("Methodology", expanded=False):
556
+ st.markdown("""
557
+ 1. **Calculate Returns:**
558
+ - Compute the daily returns of the stock and the benchmark:
559
+ """)
560
+ st.latex(r'''
561
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
562
+ ''')
563
+ st.markdown("""
564
+ 2. **Calculate Beta:**
565
+ - Compute the rolling beta of the stock returns against the benchmark returns over a specified window:
566
+ """)
567
+ st.latex(r'''
568
+ \beta_t = \frac{\text{Cov}(\text{Return}_{\text{stock}, t}, \text{Return}_{\text{benchmark}, t})}{\text{Var}(\text{Return}_{\text{benchmark}, t})}
569
+ ''')
570
+ st.markdown("""
571
+ 3. **Calculate Expected Return:**
572
+ - Compute the expected return of the stock based on the CAPM model:
573
+ """)
574
+ st.latex(r'''
575
+ \text{Expected Return}_t = R_f + \beta_t (\text{Return}_{\text{benchmark}} - R_f)
576
+ ''')
577
+ st.markdown("""
578
+ 4. **Calculate Jensen's Alpha:**
579
+ - Compute the Jensen's Alpha as the difference between the actual return and the expected return:
580
+ """)
581
+ st.latex(r'''
582
+ \alpha_t = \text{Return}_{\text{stock}, t} - \text{Expected Return}_t
583
+ ''')
584
+ fig = plot_rolling_alpha(data, market_data, window_size, risk_free_rate)
585
+ st.plotly_chart(fig)
586
+
587
+ elif selected == "Rolling Value at Risk":
588
+ st.markdown("""
589
+ ### Rolling Value at Risk (VaR)
590
+ This method calculates the rolling Value at Risk (VaR) at different confidence levels.
591
+ """)
592
+ with st.expander("Methodology", expanded=False):
593
+ st.markdown("""
594
+ 1. **Calculate Returns:**
595
+ - Compute the daily returns of the stock:
596
+ """)
597
+ st.latex(r'''
598
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
599
+ ''')
600
+ st.markdown("""
601
+ 2. **Calculate Rolling VaR:**
602
+ - Compute the rolling quantile of the returns over a specified window for different confidence levels:
603
+ """)
604
+ st.latex(r'''
605
+ \text{VaR}_{\alpha, t} = \text{Quantile}_{\alpha}(\text{Return}_{t-n:t})
606
+ ''')
607
+ fig = plot_rolling_var(data, window_size)
608
+ st.plotly_chart(fig)
609
+
610
+ elif selected == "Rolling Conditional VaR":
611
+ st.markdown("""
612
+ ### Rolling Conditional Value at Risk (CVaR)
613
+ This method calculates the rolling Conditional Value at Risk (CVaR) at different confidence levels.
614
+ """)
615
+ with st.expander("Methodology", expanded=False):
616
+ st.markdown("""
617
+ 1. **Calculate Returns:**
618
+ - Compute the daily returns of the stock:
619
+ """)
620
+ st.latex(r'''
621
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
622
+ ''')
623
+ st.markdown("""
624
+ 2. **Calculate Rolling CVaR:**
625
+ - Compute the average of the returns that are below the VaR threshold over a specified window:
626
+ """)
627
+ st.latex(r'''
628
+ \text{CVaR}_{\alpha, t} = \frac{1}{n} \sum_{i=1}^{n} \text{Return}_{i} \text{ where } \text{Return}_{i} < \text{VaR}_{\alpha, t}
629
+ ''')
630
+ fig = plot_rolling_cvar(data, window_size)
631
+ st.plotly_chart(fig)
632
+
633
+ elif selected == "Rolling Tail Ratio":
634
+ st.markdown("""
635
+ ### Rolling Tail Ratio
636
+ This method calculates the rolling Tail Ratio.
637
+ """)
638
+ with st.expander("Methodology", expanded=False):
639
+ st.markdown("""
640
+ 1. **Calculate Returns:**
641
+ - Compute the daily returns of the stock:
642
+ """)
643
+ st.latex(r'''
644
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
645
+ ''')
646
+ st.markdown("""
647
+ 2. **Calculate Tail Ratio:**
648
+ - Compute the rolling Tail Ratio over a specified window:
649
+ """)
650
+ st.latex(r'''
651
+ \text{Tail Ratio}_t = \frac{|\text{Quantile}_{95}(\text{Return}_{t-n:t})|}{|\text{Quantile}_{5}(\text{Return}_{t-n:t})|}
652
+ ''')
653
+ fig = plot_rolling_tail_ratio(data, window_size)
654
+ st.plotly_chart(fig)
655
+
656
+ elif selected == "Rolling Omega Ratio":
657
+ st.markdown("""
658
+ ### Rolling Omega Ratio
659
+ This method calculates the rolling Omega Ratio.
660
+ """)
661
+ with st.expander("Methodology", expanded=False):
662
+ st.markdown("""
663
+ 1. **Calculate Returns:**
664
+ - Compute the daily returns of the stock:
665
+ """)
666
+ st.latex(r'''
667
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
668
+ ''')
669
+ st.markdown("""
670
+ 2. **Calculate Omega Ratio:**
671
+ - Compute the rolling Omega Ratio over a specified window:
672
+ """)
673
+ st.latex(r'''
674
+ \text{Omega Ratio}_t = \frac{\sum (\text{Return}_{i} > MAR) (\text{Return}_{i} - MAR)}{\sum (\text{Return}_{i} < MAR) (MAR - \text{Return}_{i})}
675
+ ''')
676
+ fig = plot_rolling_omega(data, window_size)
677
+ st.plotly_chart(fig)
678
+
679
+ elif selected == "Rolling Sortino Ratio":
680
+ st.markdown("""
681
+ ### Rolling Sortino Ratio
682
+ This method calculates the rolling Sortino Ratio.
683
+ """)
684
+ with st.expander("Methodology", expanded=False):
685
+ st.markdown("""
686
+ 1. **Calculate Returns:**
687
+ - Compute the daily returns of the stock:
688
+ """)
689
+ st.latex(r'''
690
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
691
+ ''')
692
+ st.markdown("""
693
+ 2. **Calculate Rolling Sortino Ratio:**
694
+ - Compute the rolling Sortino Ratio over a specified window:
695
+ """)
696
+ st.latex(r'''
697
+ \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)}}
698
+ ''')
699
+ fig = plot_rolling_sortino(data, window_size, MAR)
700
+ st.plotly_chart(fig)
701
+
702
+ elif selected == "Rolling Calmar Ratio":
703
+ st.markdown("""
704
+ ### Rolling Calmar Ratio
705
+ This method calculates the rolling Calmar Ratio.
706
+ """)
707
+ with st.expander("Methodology", expanded=False):
708
+ st.markdown("""
709
+ 1. **Calculate Returns:**
710
+ - Compute the daily returns of the stock:
711
+ """)
712
+ st.latex(r'''
713
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
714
+ ''')
715
+ st.markdown("""
716
+ 2. **Calculate Rolling Calmar Ratio:**
717
+ - Compute the rolling Calmar Ratio over a specified window:
718
+ """)
719
+ st.latex(r'''
720
+ \text{Calmar Ratio}_t = \frac{\text{CAGR}_{t-n:t}}{\text{Max Drawdown}_{t-n:t}}
721
+ ''')
722
+ fig = plot_rolling_calmar(data, window_size)
723
+ st.plotly_chart(fig)
724
+
725
+ elif selected == "Rolling Stability":
726
+ st.markdown("""
727
+ ### Rolling Stability
728
+ This method calculates the rolling stability of returns.
729
+ """)
730
+ with st.expander("Methodology", expanded=False):
731
+ st.markdown("""
732
+ 1. **Calculate Returns:**
733
+ - Compute the daily returns of the stock:
734
+ """)
735
+ st.latex(r'''
736
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
737
+ ''')
738
+ st.markdown("""
739
+ 2. **Calculate Rolling Stability:**
740
+ - Compute the rolling stability over a specified window:
741
+ """)
742
+ st.latex(r'''
743
+ \text{Stability}_t = \sqrt{\frac{1}{n-1} \sum_{i=1}^{n} (\log(1 + \text{Return}_{t-i}) - \overline{\log(1 + \text{Return})})^2}
744
+ ''')
745
+ fig = plot_rolling_stability(data, window_size)
746
+ st.plotly_chart(fig)
747
+
748
+ elif selected == "Rolling Maximum Drawdown":
749
+ st.markdown("""
750
+ ### Rolling Maximum Drawdown
751
+ This method calculates the rolling maximum drawdown.
752
+ """)
753
+ with st.expander("Methodology", expanded=False):
754
+ st.markdown("""
755
+ 1. **Calculate Returns:**
756
+ - Compute the daily returns of the stock:
757
+ """)
758
+ st.latex(r'''
759
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
760
+ ''')
761
+ st.markdown("""
762
+ 2. **Calculate Rolling Maximum Drawdown:**
763
+ - Compute the cumulative returns and the maximum drawdown over a specified window:
764
+ """)
765
+ st.latex(r'''
766
+ \text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
767
+ ''')
768
+ st.latex(r'''
769
+ \text{Max Drawdown}_t = \frac{\text{Cumulative Return}_t - \text{Rolling Max Cumulative Return}_t}{\text{Rolling Max Cumulative Return}_t}
770
+ ''')
771
+ fig = plot_rolling_drawdown(data, window_size)
772
+ st.plotly_chart(fig)
773
+
774
+ elif selected == "Rolling Capture Ratios" and 'market_data' in st.session_state:
775
+ st.markdown("""
776
+ ### Rolling Capture Ratios
777
+ This method calculates the rolling upside and downside capture ratios.
778
+ """)
779
+ with st.expander("Methodology", expanded=False):
780
+ st.markdown("""
781
+ 1. **Calculate Returns:**
782
+ - Compute the daily returns of the stock and the benchmark:
783
+ """)
784
+ st.latex(r'''
785
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
786
+ ''')
787
+ st.markdown("""
788
+ 2. **Calculate Upside Capture Ratio:**
789
+ - 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:
790
+ """)
791
+ st.latex(r'''
792
+ \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
793
+ ''')
794
+ st.markdown("""
795
+ 3. **Calculate Downside Capture Ratio:**
796
+ - 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:
797
+ """)
798
+ st.latex(r'''
799
+ \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
800
+ ''')
801
+ fig = plot_rolling_capture(data, market_data, window_size)
802
+ st.plotly_chart(fig)
803
+
804
+ elif selected == "Rolling Pain Index":
805
+ st.markdown("""
806
+ ### Rolling Pain Index
807
+ This method calculates the rolling pain index.
808
+ """)
809
+ with st.expander("Methodology", expanded=False):
810
+ st.markdown("""
811
+ 1. **Calculate Returns:**
812
+ - Compute the daily returns of the stock:
813
+ """)
814
+ st.latex(r'''
815
+ \text{Return}_t = \frac{P_t - P_{t-1}}{P_{t-1}}
816
+ ''')
817
+ st.markdown("""
818
+ 2. **Calculate Cumulative Returns:**
819
+ - Compute the cumulative returns over time:
820
+ """)
821
+ st.latex(r'''
822
+ \text{Cumulative Return}_t = \prod_{i=1}^{t} (1 + \text{Return}_i)
823
+ ''')
824
+ st.markdown("""
825
+ 3. **Calculate Drawdowns:**
826
+ - Determine the drawdowns by comparing the cumulative returns to their running maximum:
827
+ """)
828
+ st.latex(r'''
829
+ \text{Drawdown}_t = \frac{\text{Cumulative Return}_t - \text{Running Max Cumulative Return}_t}{\text{Running Max Cumulative Return}_t}
830
+ ''')
831
+ st.markdown("""
832
+ 4. **Calculate Rolling Pain Index:**
833
+ - Compute the average drawdown over a specified window where the drawdown is negative:
834
+ """)
835
+ st.latex(r'''
836
+ \text{Pain Index} = \frac{1}{n} \sum_{i=1}^{n} \text{Drawdown}_i \quad \text{for } \text{Drawdown}_i < 0
837
+ ''')
838
+ fig = plot_rolling_pain_index(data, window_size)
839
+ st.plotly_chart(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
 
841
  hide_streamlit_style = """
842
  <style>
 
844
  footer {visibility: hidden;}
845
  </style>
846
  """
847
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)