3 votos

Estimación de la volatilidad de un movimiento browniano geométrico a diferentes velocidades de muestreo

Tengo problemas para estimar la volatilidad (= desviación estándar de los rendimientos logarítmicos) cuando los datos se vuelven a muestrear con diferentes frecuencias de muestreo.

Problema

He generado una serie temporal de datos utilizando un movimiento browniano geométrico. Los datos originales de la serie temporal se generan en un 1 hora intervalo de medio año:

$$ \begin{align} \mathrm{days} & = 365 / 2 = 182.5\,, \\ n & =182.5 \cdot 24 = 4380\,, \\ \Delta t & = \mathrm{days} / 365 / n=0.000114155\,. \end{align} $$

Supuestos:

  • El comercio es continuo a lo largo del año, por lo que el número de días de negociación es de 365.
  • $t=1$ significa 1 año (=365 días hábiles).

La serie temporal original del GBM $\{x_1 , \ldots , x_n\}$ se genera utilizando los siguientes parámetros:

$$ \begin{align} \mu & = 0.5\,, \\ \sigma_\mathrm{1h} & = 0.8\,, \\ x_0 & = 1000\,, \\ \Delta t & = 0.000114155\,. \end{align} $$

Utilizando estos parámetros, las volatilidades diarias y anuales son:

$$ \begin{align} \sigma_\mathrm{daily} & = \sigma_\mathrm{1h} \sqrt{24} = 3.92\,,\\ \sigma_\mathrm{annual} & = \sigma_\mathrm{daily} \sqrt{365} = 74.88\,. \\ \end{align} $$

Estimación de parámetros

Ahora estimo la volatilidad utilizando las siguientes fórmulas:

$$ \begin{align} r_i & = \log{\frac{x_i}{x_{i-1}}} \qquad \textrm{log returns}\\ \hat{\mu} & = \frac{1}{n}\cdot\sum_{i=1}^n{r_i} \qquad \textrm{mean return}\\ S_\mu^2 & = \frac{1}{n-1}\sum_{i=1}^n{(r_i-\hat{\mu})^2} \qquad \textrm{sample variance of returns}\\ \hat{\sigma} & = \frac{S_\mu}{\sqrt{\Delta t}} \qquad \textrm{estimated volatility} \end{align} $$

Aplicándolo a los datos de la serie temporal original (intervalo de 1h), obtengo resultados correctos:

$$ \begin{align} \hat{\sigma}_\mathrm{1h} & = 0.7970 \\ \hat{\sigma}_\mathrm{daily} & = 3.871 \\ \hat{\sigma}_\mathrm{annual} & = 73.959 \\ \end{align} $$

Estimación de parámetros a partir de datos remuestreados

Sin embargo, cuando vuelvo a muestrear los datos de la serie temporal en 1 día (o cualquier otro intervalo), la sigma estimada $\hat\sigma$ se mantiene igual (alrededor de 0,8), pero como la frecuencia es diferente, $\Delta t$ es diferente y $\hat{\sigma}_\mathrm{daily}$ y $\hat{\sigma}_\mathrm{annual}$ es incorrecto.

Remuestreo de la serie temporal para Contenedores de 1 día , da

$$ \begin{align} n & = 183 \\ \Delta t & = \textrm{days} / 365 / n = 0.002725 \\ \hat{\sigma}_\mathrm{1d} & = 0.773 \\ \hat{\sigma}_\mathrm{daily} & = 0.775 \\ \hat{\sigma}_\mathrm{annual} & = 14.811 \\ \end{align} $$

Estoy bastante seguro de que el problema radica en algún lugar de la escala de la volatilidad, pero no puedo averiguar qué es exactamente lo que está mal aquí.

Los datos remuestreados (bins de 1 día) tienen un intervalo de tiempo diferente entre cada punto de datos, por lo que $\Delta t$ tiene que ser diferente.

Implementación en Python

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(1018)

mu = 0.5
sigma = 0.8
x0 = 1000

start = pd.to_datetime("2021-01-01")
end = pd.to_datetime("2021-07-02 11:59.9999")
freq = "1H"

date_index=pd.date_range(start, end, freq=freq)
n = len(date_index)
dur = end-start
dur_days = (dur.days + dur.seconds / (3600 * 24))
dt = dur_days / 365 / n

print("n:", n)
print("dur_days: %.1f" % dur_days)
print("dt: %.8f" % dt)

x = np.exp((mu - sigma ** 2 / 2) * dt + sigma * np.random.normal(0, np.sqrt(dt), size=n).T)
x = x0 * x.cumprod(axis=0)

df0 = pd.DataFrame({"values": x}, index=date_index) 
df = df0.copy()

real_daily_vola = sigma * np.sqrt(1 / dt / 365)
real_ann_vola = real_daily_vola * np.sqrt(365)

print("sample vola (=sigma): %.2f for freq %s" % (sigma, freq))
print("real daily vola: %.2f" % real_daily_vola)
print("real ann vola: %.2f" % real_ann_vola)

def estimate_vola(s):
    n = len(s)
    start = s.index[0]
    end = s.index[-1]
    dur = end - start
    dur_days = dur.days + dur.seconds / (3600 * 24)
    dt = dur_days / 365 / n
    r = np.log(s / s.shift(1))
    mu = np.nanmean(r)
    s2 = 1 / (n-1) * ((r - mu)**2).sum() 
    est_sigma = np.sqrt(s2 / dt)
    daily_vola = est_sigma * np.sqrt(1 / dt / 365)
    ann_vola = daily_vola * np.sqrt(365)
    return est_sigma, daily_vola, ann_vola

print("Estimate original vola: sigma=%.2f daily_vola=%.2f ann_vola=%.2f" % estimate_vola(df0["values"]))

# Re-sample to 1d interval
df = pd.DataFrame({"open": x, "high": x, "low": x, "close": x}, index=date_index)
df_1d = df.resample('1D').agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last'})

print("Re-sampled to 1d bins: sigma=%.2f daily_vola=%.2f ann_vola=%.2f" % estimate_vola(df_1d["close"]))

# Re-sample to 2d interval
df = pd.DataFrame({"open": x, "high": x, "low": x, "close": x}, index=date_index)
df_2d = df.resample('2D').agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last'})

print("Re-sampled to 2d bins: sigma=%.2f daily_vola=%.2f ann_vola=%.2f" % estimate_vola(df_2d["close"]))

Salida

n: 4380
dur_days: 182.5
dt: 0.00011416
sample vola (=sigma): 0.80 for freq 1H
real daily vola: 3.92
real ann vola: 74.88
Estimate original vola: sigma=0.79 daily_vola=3.87 ann_vola=73.96
Re-sampled to 1d bins: sigma=0.77 daily_vola=0.78 ann_vola=14.81
Re-sampled to 2d bins: sigma=0.79 daily_vola=0.56 ann_vola=10.70

Nota

El factor de escala 1 / dt / 365 en línea

daily_vola = est_sigma * np.sqrt(1 / dt / 365)

para convertir $\hat\sigma$ en la vola diaria es correcta, creo. Lo es:

Frecuencia de remuestreo

1 / dt / 365

1D

1

2D

0.5

1voto

Bradley Wilson Puntos 265

De la forma en que ha generado las rutas, espera que el anualizado vol a ser 0,8 (ya que se está multiplicando por $\sqrt{dt}$ y $dt$ se expresa en años.

En el cálculo realizado, se puede recuperar en los datos brutos con

np.std(np.diff(np.log(df0['values']),1))*np.sqrt(365*24)

o en los datos diarios remuestreados con, respectivamente

np.std(np.diff(np.log(df_1d["close"]),1))*np.sqrt(365)
np.std(np.diff(np.log(df_2d["close"]),1))*np.sqrt(365/2)

Los 3 dan un resultado cercano pero no exactamente 0,8, como se esperaba.

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X