Equipes de tesouraria, investidores e gestores de risco raramente precisam saber o preço de um ativo no ano que vem. Precisam conhecer a faixa: quanto ele poderia subir, quanto poderia cair e qual a probabilidade de cada resultado. A simulação de Monte Carlo responde a essa pergunta gerando milhares de trajetórias de preço possíveis e lendo a distribuição dos resultados.
O modelo mais comum por trás dessas trajetórias é o movimento browniano geométrico (GBM). Ele supõe que os preços se movem aleatoriamente em torno de uma tendência, descrita por dois parâmetros: a deriva, a taxa média com que o preço muda, e a volatilidade, o quanto ele oscila em torno dessa tendência. Também supõe que os retornos logarítmicos seguem uma distribuição normal.
Para mostrar como funciona, simulamos o preço da ação da Apple de 2021 a 2024 e comparamos as trajetórias simuladas com o que de fato aconteceu.
Primeiro método: a fórmula de Black-Scholes
O framework de Black-Scholes supõe retornos log-normais, o que dá a expressão padrão do preço no instante t:
S(t) = S(0) * exp((μ - 0.5 * σ²) * t + σ * W(t))
Aqui μ é a deriva, σ a volatilidade e W(t) um movimento browniano padrão. O termo -0.5 * σ² corrige o arrasto da volatilidade, e faz parte da fórmula.
O código abaixo estima a deriva e a volatilidade a partir dos retornos diários da Apple e simula 1.000 trajetórias.
import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
apple = yf.download("AAPL", start="2021-01-01", end="2024-01-01")
apple.columns = apple.columns.levels[0]
price = apple.Close
returns = price.pct_change().dropna()
t = len(returns) # number of daily steps
dt = 1 # one trading day per step
mu = returns.mean() # drift
sigma = returns.std() # volatility
simulations = 1000
S0 = price.iloc[0]
def gbm_paths(mu, sigma, W):
return np.exp((mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * W)
Ws = np.random.standard_normal(size=(simulations, t))
gbm = gbm_paths(mu, sigma, Ws)
gbm = np.insert(gbm, 0, S0, axis=1).cumprod(axis=1)
x_axis = range(0, t + 1)
fig, ax = plt.subplots(figsize=(12, 5.5))
for path in gbm:
ax.plot(x_axis, path, color="grey", linewidth=0.1)
ax.plot(x_axis, price, color="red", linewidth=1)
Segundo método: modelar diretamente os retornos logarítmicos
Se os retornos são log-normais, seus logaritmos são normais, e podem ser simulados com um movimento browniano padrão e depois convertidos de volta em preços. É o mesmo modelo visto de outro ângulo, e muitas vezes é mais fácil de explicar.
log_returns = np.log(returns + 1)
mu_log = log_returns.mean()
sigma_log = log_returns.std()
def bm_paths(mu, sigma, W):
return mu + sigma * W
bm = bm_paths(mu_log, sigma_log, Ws)
bm = S0 * np.exp(bm.cumsum(axis=1))
bm = np.insert(bm, 0, S0, axis=1)
fig, ax = plt.subplots(figsize=(12, 5.5))
for path in bm:
ax.plot(x_axis, path, color="grey", linewidth=0.1)
ax.plot(x_axis, price, color="red", linewidth=1)
O que a comparação mostra
Os dois métodos produzem distribuições praticamente idênticas, o que confirma que são dois caminhos válidos para o mesmo modelo. Para quem decide, o valor não está nas trajetórias individuais, mas na faixa que elas descrevem: a banda em que o preço provavelmente vai se mover e a probabilidade de chegar a um nível que prejudicaria o negócio.

