5 Nonparametric volatility and co-volatility

  • Alternative to GARCH modelling is nonparametric approach which uses high frequency returns to estimate the volatility at lower frequency

Two nonparametric types of daily volatility measures are common in empirical finance:

  1. OHLC measures (use only up to \(4\) price information open, high, low and close)

  2. Realized measures (use all available intraday prices, so called high–frequency data or tick–by–tick data)

  • There are several OHLC measures of volatility, each with different characteristics

  • The most simple range–based estimator uses only closing prices (so called close–to–close estimator)

\[\begin{equation} \sigma^2_{CC} = \left( \ln \dfrac{C_t}{C_{t-1}} \right)^2 \tag{5.1} \end{equation}\]

  • Instead of relying solely on closing prices, Parkinson was first who proposed using the highest and lowest prices for volatility calculation

\[\begin{equation} \sigma^2_{PA} = \dfrac{1}{4 \ln 2} \left( \ln \dfrac{H_t}{L_t} \right)^2 \tag{5.2} \end{equation}\]

  • The advantages of Parkinson estimator lie in the availability of data, the simplicity of the calculation, and a more efficient volatility estimate compared to the close–to–close volatility (it is \(2.5\) to \(5\) times more efficient)

  • However, estimator \(\sigma^2_{PA}\) also has some drawbacks, i.e. it assumes continuous trading, meaning that there are no overnight returns, and consequently, the opening price is equal to the previous day’s closing price

  • Building on Parkinson’s work, Garman and Klass expanded the data range by using, in addition to the highest and lowest prices, the opening and closing prices, thus incorporating all four available information

\[\begin{equation} \sigma^2_{GK} = \dfrac{1}{2}\left( \ln \dfrac{H_t}{L_t} \right)^2 - (2 \ln 2- 1) \left( \dfrac{C_t}{O_t} \right)^2 \tag{5.3} \end{equation}\]

  • It has been shown that the Garman–Klass estimator is most suitable for stocks prices that follow Brownian motion with zero drift and no jumps

  • Rogers and Satchell have modified the Garman–Klass estimator, i.e. they also use the OHLC range, but their estimator is not sensitive to zero drift, which means that the estimator can accurately estimate volatility even in the presence of a trend in the stock prices

\[\begin{equation} \sigma^2_{RS} = \left(\ln \dfrac{H_t}{O_t} \right) \left( \ln \dfrac{H_t}{C_t} \right) + \left( \ln \dfrac{L_t}{O_t} \right) \left( \ln \dfrac{L_t}{C_t} \right) \tag{5.4} \end{equation}\]

  • The duo after whom the estimator was named, Yang and Zhang, constructed a superior estimator that is insensitive both to zero drift and to overnight jumps

\[\begin{equation} \sigma^2_{YZ} = \left(\ln \dfrac{O_t}{C_t-1}\right)^2 + k \left(\ln \dfrac{C_t}{O_t}\right)^2 +(1-k) \sigma^2_{RS} \tag{5.5} \end{equation}\]

where \(k\) is a weighting factor \(k = 0.34 / (1.34 + (n+1)/(n-1))\), and \(n\) is the number of period for which volatility is being calculated

TABLE 5.1: Comparison of OHLC estimators
Estimator Zero drift Overnight jump Efficiency
Close–to–close 1.0
Parkinson 5.2
Garman–Klass 7.4
Roger–Satchell 8.0
Yang–Zhang 14.0
  • By comparison of above estimator we can also conclude: (a), there’s likely meaningful overnight return when Yang–Zhang > Close–to–close, and (b) there’s evidence of trending behavior when Rogers–Satchell > Garman–Klass

  • All mentioned OHLC estimators can be easily computed in R using volatility() command from the package TTR (Technical Trading Rules), which supports volatility estimates over multiple periods – it does not natively support single–day estimates

Example 16. Install TTR package and load it from the library. Based on Tesla OHLC prices compute all five volatility estimate on weekly basis (over \(5\) days period) and monthly basis (over \(22\) days period). Weekly OHLC volatility estimates compare on a single plot. Do the same for monthly OHLC volatility estimates.
Solution Copy the code lines below to the clipboard, paste them into an R Script file, and run them.
In this example there is no evidence of volatility trending behvior, whereas Yang–Zhang OHLC captures the overnight return quite effectively, as it explicitly accounts for the close–to–open (overnight) return component, making it more accurate for assets with significant after–hours price movements (e.g. earnings reports released after the close can cause such gaps).
Note: by default R calculates the square root of OHLC volatility, and that’s why standard deviation is on the vertical axis scale.
# Install and load necessary packages (only install once)
install.packages("TTR")      
library(TTR)      # for volatility estimators
library(quantmod) # for financial data
library(ggplot2)  # for plotting

# Download TSLA data from Yahoo Finance (from 2021 to end of 2024)
getSymbols("TSLA", src = "yahoo", from = "2021-01-01", to = "2024-12-31")

# Extracting Open, High, Low, Close (OHLC) prices
four_prices <- TSLA[, 1:4]

periods <- 5  # rolling window length approximately 1 trading week

# Calculate different OHLC-based volatility estimators
cc.vol   <- TTR::volatility(four_prices, calc = "close", mean0 = TRUE, n = periods)         
park.vol <- TTR::volatility(four_prices, calc = "parkinson", n = periods)           
gk.vol   <- TTR::volatility(four_prices, calc = "garman.klass", n = periods)        
rs.vol   <- TTR::volatility(four_prices, calc = "rogers.satchell", n = periods)        
yz.vol   <- TTR::volatility(four_prices, calc = "yang.zhang", n = periods)               

# Combine and remove NA values
all.vol.5d <- na.omit(cbind(cc.vol, park.vol, gk.vol, rs.vol, yz.vol))

# Plot 5-day rolling volatilities
ggplot(all.vol.5d, aes(x = Index)) +
  geom_line(aes(y = cc.vol),   color = "Close-to-close") +
  geom_line(aes(y = park.vol), color = "Parkinson") +
  geom_line(aes(y = gk.vol),   color = "Garman-Klass") +
  geom_line(aes(y = rs.vol),   color = "Rogers-Satchell") +
  geom_line(aes(y = yz.vol),   color = "Yang-Zhang") +
  labs(x = "Day", y = "Standard deviation", 
       title = "OHLC volatilities over 5 days period") +
  scale_color_manual(values = c(
    "Close-to-close" = "coral",
    "Parkinson" = "orchid",
    "Garman-Klass" = "steelblue",
    "Rogers-Satchell" = "darkgreen",
    "Yang-Zhang" = "goldenrod"
  )) +
  theme_minimal() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    legend.position = c(0.65, 0.8),
    legend.background = element_blank(),
    legend.key = element_blank(),
    legend.title = element_blank(),
    axis.line = element_line(color = "black"))

# New rolling period - approximately 1 trading month
periods <- 22 

# Recalculate volatility with longer period
cc.vol   <- TTR::volatility(four_prices, calc = "close", mean0 = TRUE, n = periods)         
park.vol <- TTR::volatility(four_prices, calc = "parkinson", n = periods)           
gk.vol   <- TTR::volatility(four_prices, calc = "garman.klass", n = periods)        
rs.vol   <- TTR::volatility(four_prices, calc = "rogers.satchell", n = periods)        
yz.vol   <- TTR::volatility(four_prices, calc = "yang.zhang", n = periods)   

# Combine and clean
all.vol.22d <- na.omit(cbind(cc.vol, park.vol, gk.vol, rs.vol, yz.vol))

# Plot 22-day rolling volatilities
ggplot(all.vol.22d, aes(x = Index)) +
  geom_line(aes(y = cc.vol,   color = "Close-to-close")) +
  geom_line(aes(y = park.vol, color = "Parkinson")) +
  geom_line(aes(y = gk.vol,   color = "Garman-Klass")) +
  geom_line(aes(y = rs.vol,   color = "Rogers-Satchell")) +
  geom_line(aes(y = yz.vol,   color = "Yang-Zhang")) +
  labs(x = "Day", y = "Standard deviation", 
       title = "OHLC volatilities over 22 days period") +
  scale_color_manual(values = c(
    "Close-to-close" = "coral",
    "Parkinson" = "orchid",
    "Garman-Klass" = "steelblue",
    "Rogers-Satchell" = "darkgreen",
    "Yang-Zhang" = "goldenrod"
  )) +
  theme_minimal() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    legend.position = c(0.65, 0.8),
    legend.background = element_blank(),
    legend.key = element_blank(),
    legend.title = element_blank(),
    axis.line = element_line(color = "black"))

\(~~~\)