Brownian Motion

Un poco de historia

Robert Brown
Robert Brown

En 1827 el botánico escocés Robert Brown observó microscópicamente diminutos granos de polen suspendidos en un fluido y notó incrementos que eran altamente irregulares se cuestionó la posibilidad de que tuvieran vida.Se descubrió que las partículas más finas se movían más rápidamente, y que el movimiento era estimulado por el calor y por una disminución en la viscosidad del líquido. Sus investigaciones fueron publicadas como A Brief Account of Microscopical Observations Made in the Months of June, July and August 1827. Más tarde, en ese siglo, se postuló que el movimiento irregular era causado por un gran número de colisiones entre el polen y las moléculas del líquido (que son microscópicamente pequeñas en comparación con el polen). Se supone que los impactos ocurren con mucha frecuencia en cualquier pequeño intervalo de tiempo, independientemente unos de otros; y que el efecto de un impacto particular es pequeño en comparación con el efecto total.

Alrededor de 1900, Louis Bachelier, un estudiante de doctorado en matemáticas en la Sorbona, estaba estudiando el comportamiento de los precios de las acciones en la Bolsa de París y observó incrementos altamente irregulares. Desarrolló la primera especificación matemática del incremento reportado por Brown y lo utilizó como modelo para el incremento de los precios de las acciones.

Albert Einstein, en 1905, publicó un artículo sobre el movimiento Browniano “Über die von der molekularkinetischen Theorie der Wärme geforderte Bewegung von in ruhenden Flüssigkeiten suspendierten Teilchen” (“On the Motion of Small Particles Suspended in a Stationary Liquid, as Required by the Molecular Kinetic Theory of Heat”), publicado en Annalen der Physik, en el cual explicó el movimiento Browniano como resultado de las colisiones aleatorias entre partículas en suspensión y moléculas del fluido.

En la década de 1920, Norbert Wiener, un físico-matemático en el MIT, desarrolló el marco probabilístico completamente riguroso para este modelo. Este tipo de incremento se conoce ahora como movimiento browniano o proceso de Wiener. La posición del proceso se denota comúnmente por \(𝐵\) o \(𝑊\). El movimiento browniano se utiliza ampliamente para modelar la aleatoriedad en economía y en las ciencias físicas. Es fundamental en la modelización de las opciones financieras.

Fuente: UbboFWiersema (2008). Brownian Motion Calculus. John Wiley % Sons, Ltd. 

Movimiento Browniano

Definición

Un proceso estocástico \(\{B(t), t\geq 0\}\) es un movimiento Browniano si cumple las siguientes propiedades:

1.- \(B(0) = 0\)

2.- \(\{B(t), t\geq 0\}\) tiene incrementos independientes y estacionarios.

3.- Para \(t>0\), B(t) tiene distribución normal con media cero y varianza \(t\).

La función de autocovarianza de \(B\) para \(s < t\) está dada por:

\[ \begin{align*} \mathbb{C}ov(B(s), B(t)) &= \mathbb{E}\big[(B(s) - B(0))(B(t) - B(0))\big] \\ &= \mathbb{E}\big[(B(s) - B(0))^2 + (B(s) - B(0))(B(t) - B(s))\big] \\ &= \mathbb{E}\big[(B(s) - B(0))^2\big] + \underbrace{\mathbb{E}\big[(B(s) - B(0))(B(t) - B(s))\big]}_{=0 \text{ por independencia}} \\ &= s \end{align*} \]

Se puede verificar que para \(t < s\), \(\text{Cov}(B(s), B(t)) = t\), entonces

\[ \mathbb{C}ov(B(s), B(t)) = \min(s, t), \quad \text{para todo } s, t \ge 0. \]

Autocorrelación

Sea \(B(t)\) un movimiento browniano estándar, para \(s > 0\) y \(t > 0\) La autocorrelación entre \(B(s)\) y \(B(t)\) se define como:

\[ \mathbb{C}or(B(s), B(t)) = \frac{\min(s, t)}{\sqrt{s t}}. \]

Simulación:

Sea

\[0 = t_0 < t_1 < t_2 < , ..., < t_{n-1} < t_n = T\]

Consideremos una partición sobre \([0, T]\) dada por \(\Delta t = T/n\).Entonces, para simular trajectorias del movimiento Browniano consideremos los incrementos

\[B(t_1) - B(t_0) \sim N(0, t_1 - t_0) = N(0, \Delta t - 0) = \sqrt{\Delta t} N(0, 1)\] \[ B(t_2) - B(t_1) \sim N(0, t_2 - t_1) = N(0, 2\Delta t-\Delta t) = \sqrt{\Delta t} N(0, 1)\] \[ \vdots \] \[ B(t_n) - B(t_{n-1}) \sim N(0, t_n - t_{n-1}) = N(0, n\Delta t-(n-1)\Delta t) = \sqrt{\Delta t} N(0, 1)\]

Sumando los incrementos se tiene que

\[ B(t_n) - B(t_0) \sim \sum_{i=1}^{n} \sqrt{\Delta t} N(0, 1)\]

dado que \(B(t_0) = 0\) entonces

\[ B(t_n) \sim \sqrt{\Delta t} \sum_{i=1}^{n} N(0, 1)\]

y donde \(\sqrt{\Delta t} = \sqrt{T/n}\)

Movimiento Browniano en una dimensión

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.cm as cm
import matplotlib.patches as patches
sns.set_theme()
# Function para simular una Movimiento Aleatorio 
class StochasticSimulation:
  
    def __init__(self, T=None, steps=100, n_times=1):
        self.steps = steps
        self.n_times = n_times 
        # Si T no se proporciona usar T = steps
        if T is None:
            self.T = steps
        else:
            self.T = T

        self.dt = self.T/ steps
        self.t = np.linspace(0, self.T, steps+1)
             

    def BrownianMotion(self):
        # creamos arreglo
        bm = np.zeros( (self.n_times, self.steps+1))
        # inicializa simulación
        for j in range(bm.shape[1]-1):
            for i in range(bm.shape[0]):
                bm[i][j+1] = bm[i][j] + np.sqrt(self.dt) * np.random.normal(size=1)
    
        df = pd.DataFrame(bm)  
        return df
        

En este ejemplo, primero se definen los parámetros de la simulación: se tomarán \(𝑁=10,000\) pasos, lo que permitirá generar una trayectoria muy detallada del proceso, y se simulará \(𝑀=1\) única trayectoria.

A continuación, se crea una instancia de la clase \(StochasticSimulation\), pasando como argumentos el número de pasos y el número de trayectorias a simular. Esta clase se encarga de manejar los cálculos necesarios para generar procesos estocásticos.

Finalmente, se llama al método \(BrownianMotion()\) de la instancia, el cual genera la trayectoria simulada del movimiento browniano. El resultado se almacena en df, un DataFrame que contiene los valores de la trayectoria a lo largo de los pasos de tiempo definidos.

# Parámetros
N = 10000        # número de pasos
M = 1            # número de trayectorias simuladas

sim = StochasticSimulation(steps=N, n_times=M)
df = sim.BrownianMotion()

# Colormap 
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim.t
std_teorica = np.sqrt(t)
# Media teórica 
mean_B = [0]*len(t)
# Intervalos de confianza 95% 
conf_upper =  mean_B + 1.96 * std_teorica
conf_lower =  mean_B - 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
    plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Movimiento Browniano [{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("B(t)")
plt.grid(True)
plt.legend()
plt.show()

Aproximación teórica mediante simulación

Ahora vamos a simular \(M=1000\) trayectorias y vamos averificar algunas propuedades como el valor esperado, varianza y autocorrelación mediante simulación, para

1.- \(\mathbb{E}[B(15)]\), \(E[B(10)]\)

2.- \(\matbb{V}ar(B(15))\), \(Var(B(10))\)

3.- \(\mathbb{C}or([B(15)], Var[B(10)])\)

# Parámetros
N = 10000      # número de pasos
M = 1000       # número de trayectorias simuladas

sim = StochasticSimulation(steps=N, n_times=M)
df = sim.BrownianMotion()

# Colormap 
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim.t
std_teorica = np.sqrt(t)
# Media teórica 
mean_B = [0]*len(t)
# Intervalos de confianza 95% usando desviación estándar teórica
conf_upper =  mean_B + 1.96 * std_teorica
conf_lower =  mean_B - 1.96 * std_teorica
# Grafica
plt.figure(figsize=(14,6))
for i in range(M):
    plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Movimiento Browniano [{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("B(t)")
plt.grid(True)
plt.legend()
plt.show()

Solución punto 1:

La solución mediante simulación consiste en generar muchas trajectorias, y hacer un corte vertical al tiempo \(t\) extraer los puntos y extraer medidas estadísticas como la media, la varianza, para la covarianza y correlación se seguiría la misma regla pero ahora dos cortes a distintos tiempo, y sobre ellos obtener las medidas. Veamos las distribuciones para los distintos tiempo selecionados en los ejemplos:

# Tiempos a graficar
steps_to_plot = [10, 15]

# Crear figura con subplots
fig, axes = plt.subplots(2, 1, figsize=(10, 6))
axes = axes.flatten()

for i, step in enumerate(steps_to_plot):
    data = df[step]
    mean = data.mean()
    var = data.var()

    sns.histplot(data, bins=30, kde=True, ax=axes[i])
    axes[i].set_title(f"Distribución al tiempo {step}")
    axes[i].set_xlabel('Posición')
    axes[i].set_ylabel('Frecuencia')
    axes[i].legend([f'Media = {mean:.2f}, Varianza = {var:.2f}'])

plt.tight_layout()
plt.show()

# Movimiento Browniano B(15)
B_15 = np.round(df.mean(axis=0).to_list()[15],4)
print(f"Media del movimiento Browniano B(15): {B_15}")
## Media del movimiento Browniano B(15): 0.0646
# Movimiento Browniano B(10)
B_10 = np.round(df.mean(axis=0).to_list()[10],4)
print(f"Media del movimiento Browniano B(10): {B_10}")
## Media del movimiento Browniano B(10): 0.0673

Solución punto 2:

Sabemos que \(Var(B(t)) = t\)

  • \(\mathbb{V}ar(B(15)) = 15\), y
  • \(\mathbb{V}ar(B(10)) = 10\)
# Movimiento Browniano B(5)
var_B15 = np.round(df.var(axis=0).to_list()[15],4)
print(f"Media del movimiento Browniano B(15): {var_B15}")
## Media del movimiento Browniano B(15): 14.5341
# Movimiento Browniano B(6)
var_B10= np.round(df.var(axis=0).to_list()[10],4)
print(f"Media del movimiento Browniano B(10): {var_B10}")
## Media del movimiento Browniano B(10): 9.7265

El valor simulado se acerca mucho al valor teórico, entre más trayectorias se generen el resultado se aproxima más al teórico.

Solución punto 3:

Recordando la formula de la autocorrelación, \(Corr(B(s), B(t)) = \frac{\min(s, t)}{\sqrt{s t}}\) y para \(s=15\), y \(t=10\), entonces, el valor teórico

  • \(Cor(B(15), B(10)) = \frac{min(15,10)}{\sqrt{15*10}} = \frac{10}{12.2474} \sim 0.8164\)
# Movimiento Browniano Corr(B(15), B(10))
B_s = df.iloc[:, 15]
B_t = df.iloc[:, 10]
corr= np.round(np.corrcoef(B_s, B_t)[0,1],4)
print(f"Correlación del movimiento Browniano Cor(B(15),B(10)): {corr}")
## Correlación del movimiento Browniano Cor(B(15),B(10)): 0.8001

Observamos que la simulación aproxima bastente bien el valor teórico.

Caso más complicado

Sean \(\{B_1(t), t \ge 0\}\) y \(\{B_2(t), t \ge 0\}\) dos movimientos Brownianos estándar independientes. Sea

\[ R(t) = \sqrt{B_1(t)^2 + B_2(t)^2}, \quad t \ge 0, \]

donde \(R(t)\) representa la distancia al origen de un movimiento Browniano bidimensional.

Se desea determinar el valor esperado de \(R(t)\), es decir:

\[ \mathbb{E}[R(t)] = \mathbb{E}\Big[\sqrt{B_1(t)^2 + B_2(t)^2}\Big]. \]

Podemos aproximar una solución de $[R(t)] $ para cualquier valor de \(t\), la respuestá es sí. Consideremos la simulación anterior con \(N=10,000\) pasos y \(M=1000\) trayectorias.

# Parámetros
N = 10000      # número de pasos
M = 1000       # número de trayectorias simuladas

# Estavblecemos los parámetros
sim1 = StochasticSimulation(steps=N, n_times=M)
sim2 = StochasticSimulation(steps=N, n_times=M)
# simulación de los dos Movimiento Brownianos
B1 = sim1.BrownianMotion()
B2 = sim2.BrownianMotion()
# Distribución de R(t)
Rt = np.sqrt(B1**2 + B2**2)

# Colormap 
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim.t
std_teorica = np.sqrt(t)
# Media teórica 
mean_B = [0]*len(t)
# Intervalos de confianza 95% usando desviación estándar teórica
#conf_upper =  mean_B + 1.96 * std_teorica
#conf_lower =  mean_B - 1.96 * std_teorica
# Grafica
plt.figure(figsize=(14,6))
for i in range(M):
    plt.plot(t, Rt.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.title(f"Simulación de R(t) [{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("B(t)")
plt.grid(True)
plt.legend()
plt.show()

La trayectorias siguen una caminata aleatoria de Person, y se puede demostrar que para cualquier \(t\) fijo, la distribución es Rayleigh

Simulación de un Movimiento Browniano en dos dimensiones

# Parámetros
N = 10000      # número de pasos
M = 1000       # número de trayectorias simuladas

sim = StochasticSimulation(steps=N, n_times=M)
df = sim.BrownianMotion()

# Colormap (gradiente de gris)
cmap = cm.get_cmap("Greys", M*2)
centro = (0, 0)
circulo = patches.Circle(centro, 400, edgecolor='grey', facecolor='black', alpha=0.8, lw=1, ls='--')

# Crear la figura y los ejes
fig, ax = plt.subplots(figsize=(14, 10))
ax.add_patch(circulo)
# Brownian Motion
for i in range(df.shape[0] - 1):
    if i < 800:
        ax.plot(df.iloc[i][:], df.iloc[i + 1][:], linewidth=0.2, color=cmap(i))  
    else:
        continue
plt.title('Simulación Movimiento Browniano: {} steps and {} paths'.format(N, M))
plt.xlabel('B(t)')
plt.ylabel('B(t)')
plt.xlim(-400, 400) 
## (-400.0, 400.0)
plt.ylim(-400, 400)
## (-400.0, 400.0)
plt.gca().set_xticklabels([])  
plt.gca().set_yticklabels([])  
plt.grid(True)
plt.show()

Referencias

  • Ubbo F Wiersema (2008). Brownian Motion Calculus. John Wiley & Sons Ltd.
  • Sheldon M. Ross(1995). Stochastic Processes. Wiley Series in Probability and Statistics: Probability and Statistics. John Wiley & Sons.
  • LefebvreMario(2007). Applied Stochastics Processes. Springer, New York.