In partnership with

You Can't Automate Good Judgement

AI promises speed and efficiency, but it’s leaving many leaders feeling more overwhelmed than ever.

The real problem isn’t technology.

It’s the pressure to do more with less — without losing what makes your leadership effective.

BELAY created the free resource 5 Traits AI Can’t Replace & Why They Matter More Than Ever to help leaders pinpoint where AI can help and where human judgment is still essential.

At BELAY, we help leaders accomplish more by matching them with top-tier, U.S.-based Executive Assistants who bring the discernment, foresight, and relational intelligence that AI can’t replicate.

That way, you can focus on vision. Not systems.

Elite Quant Plan – 7-Day Free Trial (This Week Only)

No card needed. Cancel anytime. Zero risk.

You get immediate access to:

  • Full code from every article (including today’s HMM notebook)

  • Private GitHub repos & templates

  • All premium deep dives (3–5 per month)

  • 2 × 1-on-1 calls with me

  • One custom bot built/fixed for you

Try the entire Elite experience for 7 days — completely free.

→ Start your free trial now 👇

(Doors close in 7 days or when the post goes out of the spotlight — whichever comes first.)

See you on the inside.

👉 Upgrade Now

🔔 Limited-Time Holiday Deal: 20% Off Our Complete 2026 Playbook! 🔔

Level up before the year ends!

AlgoEdge Insights: 30+ Python-Powered Trading Strategies – The Complete 2026 Playbook

30+ battle-tested algorithmic trading strategies from the AlgoEdge Insights newsletter – fully coded in Python, backtested, and ready to deploy. Your full arsenal for dominating 2026 markets.

Special Promo: Use code SPRING2026 for 20% off

Valid only until March 20, 2026 — act fast!

👇 Buy Now & Save 👇

Instant access to every strategy we've shared, plus exclusive extras.

— AlgoEdge Insights Team

Premium Members – Your Full Notebook Is Ready

The complete Google Colab notebook from today’s article (with live data, full Hidden Markov Model, interactive charts, statistics, and one-click CSV export) is waiting for you.

Preview of what you’ll get:

Inside:

  • Automatic data download for 8 European/US stocks (2010→2025 via yfinance)

  • 8 publication-ready risk management charts from the Medium article

  • Historical Volatility with Volatility-of-Volatility (AFX.DE)

  • Rolling Sharpe Ratio vs stock price (ASML.AS)

  • Rolling Treynor Ratio vs S&P 500 benchmark (MSFT)

  • Rolling Beta comparison RANSAC vs OLS regression (UNA.AS)

  • Jensen's Alpha vs CAPM expected returns (UNA.AS)

  • Multi-level VaR (90%/95%/97%) with risk threshold highlighting (VOW.DE)

  • Bonus: Rolling Statistics (Kurtosis, Skewness, Quantile, Autocorrelation - ASML.AS)

Free readers – you already got the full breakdown and visuals in the article. Paid members – you get the actual tool.

Not upgraded yet? Fix that in 10 seconds here👇

Google Collab Notebook With Full Code Is Available In the End Of The Article Behind The Paywall 👇 (For Paid Subs Only)

1. Introduction

Risk management, at its core, is the identification, assessment, and prioritization of risks followed by coordinated and economical application of resources to minimize, monitor, and control the probability or impact of unforeseen events. In the financial world, risk management plays an indispensable role in ensuring that investment strategies are sound, companies remain solvent, and stakeholders are shielded from potential financial hazards.

Traditionally, risk management strategies were formulated based on historical data and set models which, although effective to a degree, couldn’t adapt swiftly to the rapidly changing landscapes of modern financial markets. This is where dynamic risk management have stepped in. Across global boardrooms, in the algorithms of hedge funds, and among individual investors, dynamic risk management is now crucial.

In a two-part series, we implement, interpret, and customize 15 dynamic risk management techniques in Python crucial for today’s Financial Analysts and Data Scientists alike:

  1. Historical Volatility

  2. Sharpe Ratio

  3. Treynor Ratio

  4. Rolling Beta

  5. Jensen’s Alpha

  6. Value at Risk

  7. Conditional Value at Risk

  8. Tail Ratio

  9. Omega Ratio

  10. Sortino Ratio

  11. Calmar Ratio

  12. Stability of Returns

  13. Maximum Drawdown

  14. Upside Capture and Downside Capture

  15. Pain Index

This article covers the first seven techniques of dynamic risk management. For a complete exploration, ensure you also read the second part which will be released tomorrow.

88% resolved. 22% loyal. Your stack has a problem.

Those numbers aren't a CX issue — they're a design issue. Gladly's 2026 Customer Expectations Report breaks down exactly where AI-powered service loses customers, and what the architecture of loyalty-driven CX actually looks like.

2. Dynamic Risk Management in Python

2.1 Historical Volatility

Historical volatility (or realized volatility) quantifies the extent of price fluctuations over a specified period. It provides insights into the unpredictability of an asset. The most common approach to calculating historical volatility is to determine the standard deviation of daily returns over a rolling window, typically scaled to annualize the value for comparison purposes.

Formula illustrating the computation of daily historical volatility. The calculation is based on returns of stock prices and provides a measure of how much the stock has fluctuated on a day-to-day basis in the past.

Where ˉRˉ is Average daily return over the N days. Daily volatitility can be converted to annual volatility by using the following formula

Formula for converting daily historical volatility to annualized volatility. The transformation incorporates the square root of the number of trading days in a year (typically 252), offering a more long-term perspective on stock price fluctuations.

Higher historical volatility indicates greater price unpredictability and potentially higher investment risk. The rolling nature of this metric ensures we get a dynamic view, letting investors understand how volatility is changing over time.

import yfinance as yf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Define the ticker
ticker = "AFX.DE"

# Download the data
data = yf.download(ticker, start='2010-01-01', end='2023-12-21')

# Calculate the daily returns
data['Return'] = data['Close'].pct_change()

# Calculate the rolling volatility (21-day window)
data['Rolling_Volatility'] = data['Return'].rolling(window=21).std() * np.sqrt(252)

# Calculate the volatility of volatility
data['Vol_of_Vol'] = data['Rolling_Volatility'].rolling(window=21).std()

# Create a new figure with a specific size
plt.figure(figsize=(20, 10))

# Create primary axis
ax1 = data['Rolling_Volatility'].plot(label='Rolling Volatility', color='red', linewidth=2)
ax1.set_ylabel('Rolling Volatility', color='red')
ax1.set_title(f'{ticker} - Rolling Volatility vs. Volatility of Volatility', fontsize=16)
ax1.grid(True, which='both', linestyle='--', linewidth=0.5)

# Create secondary axis for Vol of Vol
ax2 = ax1.twinx()
data['Vol_of_Vol'].plot(ax=ax2, label='Volatility of Volatility', color='blue', linestyle='--', linewidth=0.5)
ax2.set_ylabel('Volatility of Volatility', color='blue')
ax2.set_ylim(0)

# Create third axis for stock price
ax3 = ax1.twinx()
ax3.spines['right'].set_position(('outward', 60))  # Move the last y-axis spine further to the right
data['Close'].plot(ax=ax3, label='Close Price', color='grey', alpha=0.5)
ax3.set_ylabel('Stock Price', color='grey')
ax3.set_ylim(0)

# Legends
ax1.legend(loc='upper left')
ax2.legend(loc='upper right')
ax3.legend(loc='center right')

plt.tight_layout()
plt.show()

Figure 1. The graph presents a multi-dimensional view of “AFX.DE” stock dynamics over time. The red solid line depicts the 21-day Rolling Volatility, highlighting periods of significant price fluctuations. In blue dashed lines, the Volatility of Volatility (VoV) offers a meta-view of the stability of the rolling volatility itself. The translucent grey line traces the stock’s closing price throughout the period. Together, these metrics provide a comprehensive insight into the stock’s risk profile and price evolution.

2.2 Sharpe Ratio

The Sharpe Ratio, named after its founder William F. Sharpe, is a metric used to evaluate the risk-adjusted performance of an investment. Specifically, it measures the excess return for each unit of risk taken by an investment, with the risk typically represented by the standard deviation of returns. A higher Sharpe Ratio indicates that the investment is providing a higher return for its level of risk. When calculated over a rolling window, the Sharpe Ratio provides a dynamic view of how the risk-return trade-off evolves over time.

Formula for the Sharpe Ratio, which measures the performance of an investment relative to the risk-free rate, adjusted for its risk.

  • Rp​ is the expected portfolio return

  • Rf​ is the risk-free rate

  • σp​ is the standard deviation (or volatility) of the portfolio’s excess returns.

The Sharpe Ratio is often used to compare the risk-adjusted returns of different investments. A value greater than 1 suggests that the investment yields a return greater than the risk it entails.

import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt

tickerSymbol = "ASML.AS"
tickerData = yf.Ticker(tickerSymbol)
tickerDf = tickerData.history(period='1d', start='2010-1-1')
tickerDf['returns'] = tickerDf['Close'].pct_change()

# Annualized Sharpe Ratio
rolling_sharpe = np.sqrt(252) * tickerDf['returns'].rolling(252).mean() / tickerDf['returns'].rolling(252).std()

plt.figure(figsize=(15,7))
ax1 = plt.gca()
ax1.plot(rolling_sharpe, label='Rolling Sharpe Ratio', linewidth=1.5)
ax1.axhline(y=1, color='red', linestyle='--', label='Good Sharpe Ratio Threshold: 1')
ax1.set_title('Rolling 1-Year Sharpe Ratio with Stock Price', fontsize=16)
ax1.set_ylabel('Sharpe Ratio', fontsize=14)
ax1.legend()

ax2 = ax1.twinx()
ax2.plot(tickerDf['Close'], color='grey', alpha=0.3, label='Stock Price')
ax2.set_ylabel('Stock Price', fontsize=14)

plt.tight_layout()
plt.show()

Figure 2. Visualizing the 1-Year Rolling Sharpe Ratio for “ASML.AS”: Balancing Risk-Adjusted Returns against Stock Price Movements from 2010–2024. The red dashed line represents the commonly accepted ‘good’ Sharpe Ratio threshold of 1.

Learn how to code faster with AI in 5 mins a day

You're spending 40 hours a week writing code that AI could do in 10.

While you're grinding through pull requests, 200k+ engineers at OpenAI, Google & Meta are using AI to ship faster.

How?

The Code newsletter teaches them exactly which AI tools to use and how to use them.

Here's what you get:

  • AI coding techniques used by top engineers at top companies in just 5 mins a day

  • Tools and workflows that cut your coding time in half

  • Tech insights that keep you 6 months ahead

Sign up and get access to the Ultimate Claude code guide to ship 5X faster.

2.3 Treynor Ratio

The Treynor Ratio, sometimes referred to as the reward-to-volatility ratio, gauges the performance of an investment relative to the amount of market risk it has undertaken. Similar to the Sharpe Ratio, which uses total risk (standard deviation) as its risk measure, the Treynor Ratio specifically uses systematic risk (beta) to evaluate how well an investment has performed above the risk-free rate for its exposure to market risk.

Formula for the Treynor Ratio, which quantifies the excess return earned by a portfolio per unit of systematic risk, as captured by its beta with respect to the market.

The Treynor Ratio helps investors determine how much return they’re receiving for the systematic risk they’ve undertaken. A higher value indicates better performance on a per-unit-of-risk basis.

import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt

# Fetch data
tickerSymbol = "MSFT"
tickerData = yf.Ticker(tickerSymbol)
tickerDf = tickerData.history(period='1d', start='2010-1-1')
tickerDf['returns'] = tickerDf['Close'].pct_change()

# Market data for benchmark (S&P500)
market_data = yf.Ticker("^GSPC").history(period='1d', start='2010-1-1')
market_data['returns'] = market_data['Close'].pct_change()

# Align indices
market_data = market_data.reindex(tickerDf.index, method='ffill')

window_size = 252

# Calculate rolling beta
covariance = tickerDf['returns'].rolling(window=window_size).cov(market_data['returns'])
variance = market_data['returns'].rolling(window=window_size).var()
rolling_beta = covariance/variance

# Calculate rolling Treynor Ratio
risk_free_rate = 0
avg_rolling_returns = tickerDf['returns'].rolling(window=window_size).mean()
tickerDf['rolling_treynor_ratio'] = (avg_rolling_returns - risk_free_rate) / rolling_beta

fig, ax1 = plt.subplots(figsize=(14, 7))

color = 'tab:red'
ax1.set_xlabel('Date')
ax1.set_ylabel('Rolling Treynor Ratio', color=color)
ax1.plot(tickerDf['rolling_treynor_ratio'], label="Rolling Treynor Ratio", color=color)
ax1.tick_params(axis='y', labelcolor=color)
ax1.grid(True)
ax1.legend(loc="upper left")

ax2 = ax1.twinx()  
color = 'tab:blue'
ax2.set_ylabel('Stock Price', color=color)  
ax2.plot(tickerDf['Close'], label="MSFT Stock Price", color=color, alpha=0.3)
ax2.tick_params(axis='y', labelcolor=color)

ax3 = ax1.twinx()
color = 'tab:green'
# Offset the twin axis to the right
ax3.spines['right'].set_position(('outward', 60))
ax3.set_ylabel('Benchmark Price', color=color)
ax3.plot(market_data['Close'], label="Benchmark (S&P 500)", linestyle="--", color=color, alpha=0.3)
ax3.tick_params(axis='y', labelcolor=color)

fig.tight_layout()  # otherwise the right y-label is slightly clipped
ax3.legend(loc="upper right")

plt.title('Rolling 1-Year Treynor Ratio with Stock Price and Benchmark')
plt.tight_layout()
plt.show()

Figure 3. Comparative Analysis of MSFT’s Performance (2010–2024): Rolling 1-Year Treynor Ratio juxtaposed against “MSFT” stock price and the S&P 500 benchmark. This visualization aids in evaluating risk-adjusted performance relative to market movements.

2.4 Rolling Beta

Rolling Beta provides a dynamic view of an asset’s risk relative to the broader market, adapting to recent trends and shifts. Beta itself is a critical metric in financial analysis, quantifying the sensitivity of a stock or portfolio’s returns to market fluctuations. A beta value greater than 1 indicates that the asset exhibits higher volatility than the overall market, while a beta less than 1 signifies its returns are less volatile in comparison. It’s defined by the equation:

Formula for Beta, a measure indicating the sensitivity of a portfolio’s or stock’s returns to market movements. A beta greater than 1 suggests the asset is more volatile than the market, while a beta less than 1 implies lower volatility relative to the market.

Where:

  • Rp​ = Return of the portfolio or stock

  • Rm​ = Return of the market

  • Cov(Rp​,Rm​) = Covariance between the portfolio (or stock) and the market

  • Var(Rm​) = Variance of the market

The chart below displays the rolling beta values using RANSAC and OLS methods. RANSAC offers smoother estimates due to its outlier resistance, while OLS can be more volatile. Consistent values between the methods suggest minimal outlier impact; divergences could indicate outliers or non-linear data relationships.

import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import RANSACRegressor, LinearRegression

# Download historical data for ticker and a market index (e.g., ^AEX)
ticker = "UNA.AS"
start_date = "2016-01-01"
end_date = "2025-09-01"
stock = yf.download(ticker, start=start_date, end=end_date)
market = yf.download("^AEX", start=start_date, end=end_date)

# Calculate daily returns
stock_returns = stock["Adj Close"].pct_change().dropna()
market_returns = market["Adj Close"].pct_change().dropna()

# Align dates and remove rows with missing data
aligned_data = pd.concat([stock_returns, market_returns], axis=1).dropna()
aligned_data.columns = [ticker, "Market"]

# Calculate rolling beta using RANSAC
window = 60  # Choose the length of the rolling window
ransac = RANSACRegressor()
ols = LinearRegression()
rolling_beta_ransac = []
rolling_beta_ols = []

for i in range(len(aligned_data) - window):
    X = aligned_data["Market"].iloc[i:i+window].values.reshape(-1, 1)
    y = aligned_data[ticker].iloc[i:i+window].values
    
    ransac.fit(X, y)
    beta_ransac = ransac.estimator_.coef_[0]
    rolling_beta_ransac.append(beta_ransac)

    ols.fit(X, y)
    beta_ols = ols.coef_[0]
    rolling_beta_ols.append(beta_ols)

# Plot rolling beta
plt.figure(figsize=(15,7))
plt.plot(aligned_data.index[window:], rolling_beta_ransac, label="RANSAC")
plt.plot(aligned_data.index[window:], rolling_beta_ols, label="OLS")
plt.xlabel("Date")
plt.ylabel("Rolling Beta")
plt.title(f"Rolling Beta of {ticker} using RANSAC and OLS")
plt.legend()
plt.grid()

Figure 4. Assessing Sensitivity to Market Fluctuations: Rolling Beta of “UNA.AS” (2016–2025) derived using both RANSAC and OLS methods. This comparative visualization offers insights into the stock’s responsiveness to broader market movements, highlighting potential anomalies and robustness in beta estimation

2.5 Jensen’s Alpha

Jensen’s Alpha, often just termed Alpha, represents an investment’s average excess return after adjusting for the performance predicted by the Capital Asset Pricing Model (CAPM). Essentially, it quantifies how much an asset either outperforms or underperforms relative to its systematic risk. A positive Alpha suggests that an investment has yielded returns above its expected risk-adjusted returns, while a negative Alpha denotes underperformance.

Formula for Jensen’s Alpha, which represents the abnormal return of a portfolio or security relative to the expected return predicted by the Capital Asset Pricing Model (CAPM). A positive Jensen’s Alpha indicates a strategy that has consistently beaten the market when adjusted for risk.

Where:

  • α = Jensen’s Alpha

  • Rp​ = Portfolio’s realized return

  • Rf​ = Risk-free rate

  • β = Portfolio’s beta with the market

  • Rm​ = Market’s realized return

The rolling Jensen’s Alpha graphically presents how the investment’s excess returns, adjusted for market risk, evolve over time. A period with positive Alpha suggests that the investment yielded a performance superior to the CAPM’s predictions, while a negative Alpha phase indicates that it didn’t meet expectations.

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt

# Download historical data
start_date = "2016-01-01"
end_date = "2025-09-01"
stock = yf.download("UNA.AS", start=start_date, end=end_date)
market = yf.download("^AEX", start=start_date, end=end_date)

# Calculate daily returns
stock_returns = stock["Adj Close"].pct_change().dropna()
market_returns = market["Adj Close"].pct_change().dropna()

# Align data
aligned_data = pd.concat([stock_returns, market_returns], axis=1).dropna()
aligned_data.columns = ["Stock", "Market"]

window = 60  # rolling window
risk_free_rate = 0.01  # example constant rate, adjust as required

rolling_alpha = []

for i in range(len(aligned_data) - window):
    data_window = aligned_data.iloc[i:i+window]
    beta = data_window.cov().iloc[0, 1] / data_window["Market"].var()
    expected_return = risk_free_rate + beta * (data_window["Market"].mean() - risk_free_rate)
    alpha = data_window["Stock"].mean() - expected_return
    rolling_alpha.append(alpha)

# Plotting
plt.figure(figsize=(30, 8))
plt.plot(aligned_data.index[window:], rolling_alpha, label="Rolling Jensen's Alpha")
plt.xlabel("Date")
plt.ylabel("Alpha")
plt.title(f"Rolling Jensen's Alpha for UNA.AS over {window} Days")
plt.grid()
plt.legend()
plt.show()

Figure 5. Measuring Performance against Expected Returns: Rolling Jensen’s Alpha for “UNA.AS” (2016–2025) over a 60-day window. This temporal analysis provides insights into the stock’s consistent ability to outperform or underperform the market’s expected return, adjusted for risk, over the given period.

2.6 Value at Risk (VaR)

Value at Risk (VaR) is a statistical measure that quantifies the potential loss in value of a financial asset or portfolio over a specific time horizon for a given confidence interval. Essentially, it provides a worst-case scenario given a confidence level, telling us that “we should not expect to lose more than this amount X% of the time”. For instance, a 1-Year VaR at the 95% confidence level tells us the maximum loss we can expect over the next year 5% of the time.

Formula for Value at Risk (VaR), quantifying the maximum potential loss in value of a portfolio over a specific time period for a given confidence level. VaR provides a measure of market risk and is widely used in risk management.”

Where:

  • VaRconfidence level​ = Value at Risk at a specific confidence level

  • Quantile = The quantile function returning the value below which a given percentage of observations in a group fall

  • returns = The set of portfolio returns

  • confidence level = The confidence level for the VaR estimation, typically expressed as a decimal (e.g., 0.95 for 95%)

VaR offers a measure of the maximum potential loss an investment might face for a given confidence level and time horizon. When we use a rolling window to compute VaR, it provides a dynamic view of how the risk of maximum potential loss changes over time. For instance, a rising VaR might indicate increasing risk in the asset.

import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt

# Define the ticker symbol
tickerSymbol = "VOW.DE"
tickerData = yf.Ticker(tickerSymbol)
tickerDf = tickerData.history(period='1d', start='2010-1-1')
tickerDf['returns'] = tickerDf['Close'].pct_change()

# Calculate VaR for 90%, 95%, and 97% confidence levels
rolling_var_90 = tickerDf['returns'].rolling(252).quantile(0.10).dropna()
rolling_var_95 = tickerDf['returns'].rolling(252).quantile(0.05).dropna()
rolling_var_97 = tickerDf['returns'].rolling(252).quantile(0.03).dropna()

threshold = -0.04  # change this value as needed

plt.figure(figsize=(15,7))

# Plotting stock price
ax1 = plt.gca()
tickerDf['Close'].plot(ax=ax1, color='gray', linewidth=0.5, label='Stock Price')
ax1.set_ylabel('Stock Price', fontsize=14)
ax1.legend(loc='upper left')

# Plotting VaR on secondary y-axis
ax2 = ax1.twinx()
ax2.plot(rolling_var_90.index, rolling_var_90, label='Rolling VaR 90%', color='cyan', linewidth=1.5)
ax2.plot(rolling_var_95.index, rolling_var_95, label='Rolling VaR 95%', color='blue', linewidth=1.5)
ax2.plot(rolling_var_97.index, rolling_var_97, label='Rolling VaR 97%', color='purple', linewidth=1.5)

# Horizontal line to denote the threshold
ax2.axhline(y=threshold, color='red', linestyle='--', label=f'Threshold at {threshold*100:.2f}%')

# Highlight areas below the threshold for 95% VaR
ax2.fill_between(rolling_var_95.index, rolling_var_95, threshold, where=(rolling_var_95 <= threshold), color='red', alpha=0.5)

# Aesthetics
ax2.set_ylabel('VaR', fontsize=14)
ax2.grid(True, alpha=0.5)
ax2.legend(loc='upper right')
plt.title('Rolling 1-Year VaR with Stock Price', fontsize=16)

plt.tight_layout()
plt.show()

Figure 6. Rolling 1-Year Value at Risk (VaR) for “VOW.DE” at 90%, 95%, and 97% confidence levels juxtaposed with its stock price. Areas shaded in red represent periods where the 95% VaR surpasses the defined threshold, signaling heightened potential risk.

2.7 Conditional Value at Risk (CVaR)

Conditional Value at Risk (CVaR), also known as Expected Shortfall, quantifies the expected value of loss in the worst-case scenarios. It provides a clearer view of potential extreme losses than the Value at Risk (VaR) metric. In essence, while VaR gives us a threshold, CVaR tells us about the expected loss beyond that threshold.

Conditional Value at Risk (CVaR) at confidence level α, representing the average of losses exceeding the Value at Risk (VaR) at the same confidence level.

By comparing the CVaR at different confidence levels, investors can assess the risk of extreme losses at various levels of likelihood. For instance, a sudden spike in the 97% CVaR line (orange) suggests an increased expectation of severe losses in the worst 3% of cases, indicating heightened tail risk during that period.

import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
plt.style.use('seaborn-darkgrid')

tickerSymbol = "MSFT"
tickerData = yf.Ticker(tickerSymbol)
tickerDf = tickerData.history(period='1d', start='2010-1-1')
tickerDf['returns'] = tickerDf['Close'].pct_change()

def conditional_var(x, alpha=0.05):
    var = np.percentile(x, alpha * 100)
    return np.mean(x[x < var])

rolling_cvar_95 = tickerDf['returns'].rolling(252).apply(conditional_var, raw=True).dropna()
rolling_cvar_90 = tickerDf['returns'].rolling(252).apply(lambda x: conditional_var(x, alpha=0.1), raw=True).dropna()
rolling_cvar_97 = tickerDf['returns'].rolling(252).apply(lambda x: conditional_var(x, alpha=0.03), raw=True).dropna()

mean_cvar_95 = rolling_cvar_95.mean()
mean_cvar_90 = rolling_cvar_90.mean()
mean_cvar_97 = rolling_cvar_97.mean()

plt.figure(figsize=(15,7))
ax1 = plt.gca()
ax1.plot(rolling_cvar_95, label='Rolling CVaR 95%', linewidth=1.5, color='blue')
ax1.plot(rolling_cvar_90, label='Rolling CVaR 90%', linewidth=1.5, color='green')
ax1.plot(rolling_cvar_97, label='Rolling CVaR 97%', linewidth=1.5, color='orange')
ax1.axhline(y=mean_cvar_95, color='red', linestyle='--', label=f'Mean CVaR 95%: {mean_cvar_95:.5f}')
ax1.axhline(y=mean_cvar_90, color='green', linestyle='--', label=f'Mean CVaR 90%: {mean_cvar_90:.5f}')
ax1.axhline(y=mean_cvar_97, color='orange', linestyle='--', label=f'Mean CVaR 97%: {mean_cvar_97:.5f}')
ax1.set_title('Rolling 1-Year CVaR with Stock Price for Different Confidence Levels', fontsize=16)
ax1.set_ylabel('CVaR', fontsize=14)
ax1.legend()

ax2 = ax1.twinx()
ax2.plot(tickerDf['Close'], color='grey', alpha=0.3, label='Stock Price')
ax2.set_ylabel('Stock Price', fontsize=14)

plt.tight_layout()
plt.show()

Figure 7. Rolling 1-Year Conditional Value at Risk (CVaR) for “MSFT” at Confidence Levels of 90%, 95%, and 97%. The graph juxtaposes the expected average loss during the worst-case scenarios over a year against the stock’s actual price trajectory.

2.8 Bonus — Rolling Statistics

Dynamic risk management is also about understanding the intricacies of statistical measures that can signify impending volatility or anomalies. Rolling statistics , computed over short windows, offer a real-time snapshot of these intricacies, allowing for more responsive and informed decision-making. For our illustration, we delved into a 20-day rolling window analysis of several statistical properties of th eprice returns:

  • Rolling Kurtosis: Measures the tail risk. High kurtosis indicates the potential for extreme negative or positive returns.

  • Rolling Skewness: Provides a lens into the asymmetry of returns. Positive skewness hints at frequent small losses and occasional large gains, while a negative skewness implies the opposite.

  • Rolling Quantile (Median in this case): Offers a continuous view of the median price, aiding in spotting unusual price movements that might divert from its typical pattern.

  • Rolling Autocorrelation: Measures the similarity between the stock’s performance and a lagged version of itself, vital for identifying repeating patterns in price movement.

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import kurtosis, skew

# Load the data
ticker = "ASML.AS"
data = yf.download(ticker, start='2020-01-01', end='2024-09-17')

# Compute daily returns
data['Returns'] = data['Close'].pct_change()

# Calculate rolling window size
window_size = 20

# Calculate rolling kurtosis
data['Rolling_Kurtosis'] = data['Returns'].rolling(window_size).apply(kurtosis)

# Calculate rolling skewness
data['Rolling_Skewness'] = data['Returns'].rolling(window_size).apply(skew)

# Calculate rolling quantiles
quantile_value = 0.5  # adjust this for the quantile you want to calculate
data['Rolling_Quantile'] = data['Returns'].rolling(window_size).quantile(quantile_value)

# Calculate rolling autocorrelation
data['Rolling_Autocorrelation'] = data['Returns'].rolling(window_size).apply(lambda x: x.autocorr())

# Plotting
fig, axes = plt.subplots(5, 1, figsize=(30, 20))

axes[0].plot(data['Close'], label='Close')
axes[0].set_title(f'{ticker} Close Price')
axes[0].legend()

axes[1].plot(data['Rolling_Kurtosis'], label='Rolling_Kurtosis', color='orange')
axes[1].set_title('Rolling Kurtosis of Returns')
axes[1].legend()

axes[2].plot(data['Rolling_Skewness'], label='Rolling_Skewness', color='green')
axes[2].set_title('Rolling Skewness of Returns')
axes[2].legend()

axes[3].plot(data['Rolling_Quantile'], label=f'Rolling_Quantile ({quantile_value})', color='red')
axes[3].set_title('Rolling Quantile of Returns')
axes[3].legend()

axes[4].plot(data['Rolling_Autocorrelation'], label='Rolling_Autocorrelation', color='purple')
axes[4].set_title('Rolling Autocorrelation of Returns')
axes[4].legend()

plt.tight_layout()
plt.show()

Figure 8. Visual Depiction of “ASML.AS” Price and Associated Rolling Statistics on the Price Returns from 2020–2024: Inspecting Statistical Patterns and Risk Characteristics through Rolling Kurtosis, Skewness, Median Quantile, and Autocorrelation.

3. Applications of Dynamic Risk Management

  • Adapting to Market Volatility: In our rapidly evolving financial markets, the ability to react swiftly is paramount. The dynamic risk management techniques discussed here, especially methods like Historical Volatility and Value at Risk, provide the dexterity needed to navigate volatile market environments. They constantly adjust to new data, ensuring a current and pertinent understanding of risk levels.

  • Portfolio Optimization: Effective portfolio management isn’t just about maximizing returns; it’s about achieving desired returns within an acceptable risk framework. With dynamic risk measures such as the Sharpe and Treynor ratios, investors can better tailor their portfolios, ensuring assets are always in line with risk tolerance levels.

  • Improved Decision Making: The regular updating of risk measures, as seen with Rolling Beta and Jensen’s Alpha, empowers investors. When market dynamics shift, these metrics ensure that investors aren’t relying on outdated data, facilitating more timely and informed decisions.

  • Enhanced Reporting: In our digital age, stakeholders expect real-time insights. Dynamic metrics offer an accurate, up-to-the-minute snapshot of investment risks and returns, strengthening both trust and communication channels.

  • Proactive Risk Mitigation: Tools like Conditional Value at Risk enable early trend or anomaly detection. By recognizing potential threats sooner, investors can proactively adjust positions, mitigating adverse impacts.

4. Concluding Thoughts

In this article, we delved deep into the realm of dynamic risk management, exploring its pertinence in today’s complex financial landscape. We’ve covered seven key techniques, each offering unique insights and benefits. As we navigate the future, these dynamic tools will undoubtedly form an essential part of any financial analyst’s toolkit. Stay tuned for our next article, where we’ll uncover eight more cutting-edge techniques in this arena.

This concludes our discussion on the initial seven techniques. For insights on the remaining eight methods, please proceed to the second part of this series for a much deeper understanding of dynamic risk management. lease consider clapping if you have found this content beneficial.

logo

Subscribe to our premium content to read the rest.

Become a paying subscriber to get access to this post and other subscriber-only content.

Upgrade

Keep Reading