Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor · · Updated 2026-08-21

Como calcular o Value at Risk: 3 métodos

Veja como calcular o VaR histórico, paramétrico e de Monte Carlo na mesma série de retornos do SPY, além do expected shortfall que o VaR não captura.

O value at risk, ou VaR, é calculado definindo um horizonte, fixando um nível de confiança e, em seguida, obtendo um percentil de uma série de retornos. Um VaR de 2% para um dia e com nível de confiança de 99% significa que a perda fica abaixo de 2% em 99 de cada 100 sessões e supera esse valor na centésima. Três métodos padrão respondem a essa pergunta com os mesmos dados de entrada, mas divergem mais do que a maioria das pessoas espera.

O que o value at risk realmente mede

O VaR é um quantil de uma distribuição de perdas. Ordene todos os retornos diários de uma amostra, do pior ao melhor. Avance 1% a partir da extremidade negativa. O retorno encontrado, expresso como uma perda positiva, é o VaR histórico de 1 dia a 99%. Nada nessa construção promete um pior caso. Ela apenas marca o limite da região que a estimativa deixa de descrever.

Essa é a propriedade que os leitores mais costumam interpretar mal. Um VaR de 99% indica que os piores 1% dos dias estão além do limite. Ele não informa até onde chegam. Drawdown máximo responde a uma pergunta diferente: qual foi a perda do pico ao vale que uma carteira efetivamente sofreu. As duas métricas podem ordenar o mesmo par de carteiras em posições opostas.

Todos os números abaixo usam uma única série: os fechamentos diários do SPY do início de 2010 ao fim de 2025, convertidos em variações percentuais de fechamento a fechamento. A janela é fixa, não móvel. Cada painel reconstrói a série com essas mesmas datas. Assim, os números não mudam entre as execuções.

QuerySérie de retornos fixada: retornos diários do SPY por ano-calendário, de 2010 a 2025
O SQL exato por trás de cada número
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-12-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
rets AS
(
    SELECT date, 100 * (close_px / prev_close - 1) AS ret_pct
    FROM
    (
        SELECT date, close_px,
               lagInFrame(close_px, 1) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
        FROM px
    )
    WHERE date >= '2010-01-01' AND prev_close > 0
)
SELECT
    toString(toYear(date))        AS year,
    count()                       AS sessions,
    round(avg(ret_pct), 3)        AS mean_return_pct,
    round(stddevSamp(ret_pct), 2) AS daily_sigma_pct
FROM rets
GROUP BY year
ORDER BY year
Run this yourself

A série abrange 16 anos-calendário e 252 sessões apenas em 2010. Duas características são relevantes para tudo o que vem a seguir. A média diária é quase irrelevante neste horizonte: 0.054% em 2010, contra um desvio-padrão diário, sigma, de 1.13%. E sigma não é constante. 2020 registrou 2.11% por dia, enquanto 2017 registrou 0.43%. Um único sigma não consegue descrever os dois períodos.

Como é calculado o value at risk, de três formas

VaR histórico: ler o percentil com base no que aconteceu

Ordene os retornos observados e obtenha o percentil. Nenhuma distribuição é pressuposta, o que é a principal vantagem do método. A premissa é que a amostra já contenha o tipo de dia que a estimativa pretende cobrir. Quando o nível de confiança se afasta o suficiente, apenas as poucas sessões mais negativas de toda a janela determinam o resultado.

VaR paramétrico: média menos z vezes sigma

Resuma a série pela média e pelo sigma e, em seguida, pressuponha que os retornos seguem uma distribuição normal. O VaR é z vezes sigma menos a média, em que z é o quantil da distribuição normal padrão: 1,645 a 95%, 2,326 a 99% e 3,090 a 99,9%. O cálculo é imediato, mas a premissa falha em uma direção específica. Os retornos diários de ações concentram-se mais no centro do que uma curva normal e alcançam extremos muito mais distantes. Aqui, sigma é o denominador que também compõe o índice de Sharpe e traz o mesmo ponto cego para dados de cauda pesada.

VaR de Monte Carlo: simular com a seed fixada

Gere uma grande amostra sintética a partir de um processo pressuposto e obtenha o percentil dos resultados simulados. O painel abaixo usa 40.000 observações normais padrão geradas pela transformação de Box-Muller a partir de uma sequência uniforme inicializada por hash, redimensionada para a média e o sigma da série. A seed está no SQL, portanto as observações são idênticas em todas as execuções. A simulação oferece flexibilidade, dependência do caminho e correlação entre múltiplos ativos, mas não oferece realismo: se receber uma distribuição normal, devolverá a resposta paramétrica acrescida do ruído da amostragem. Reamostrar os retornos observados, a técnica por trás de intervalos de confiança bootstrap, mantém a cauda real em consideração.

QueryUma série, três métodos: VaR de 1 dia em seis níveis de confiança
O SQL exato por trás de cada número
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-12-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
rets AS
(
    SELECT date, 100 * (close_px / prev_close - 1) AS ret_pct
    FROM
    (
        SELECT date, close_px,
               lagInFrame(close_px, 1) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
        FROM px
    )
    WHERE date >= '2010-01-01' AND prev_close > 0
),
emp AS
(
    SELECT
        avg(ret_pct)                  AS mu,
        stddevSamp(ret_pct)           AS sd,
        quantileExact(0.100)(ret_pct) AS h90,
        quantileExact(0.050)(ret_pct) AS h95,
        quantileExact(0.025)(ret_pct) AS h975,
        quantileExact(0.010)(ret_pct) AS h99,
        quantileExact(0.005)(ret_pct) AS h995,
        quantileExact(0.001)(ret_pct) AS h999
    FROM rets
),
draws AS
(
    SELECT
        quantileExact(0.100)(z) AS z90,
        quantileExact(0.050)(z) AS z95,
        quantileExact(0.025)(z) AS z975,
        quantileExact(0.010)(z) AS z99,
        quantileExact(0.005)(z) AS z995,
        quantileExact(0.001)(z) AS z999
    FROM
    (
        SELECT sqrt(-2 * log(u1)) * cos(2 * pi() * u2) AS z
        FROM
        (
            SELECT
                (cityHash64('var-seed-u1', i) % 999999937 + 1) / 999999938.0 AS u1,
                (cityHash64('var-seed-u2', i) % 999999937 + 1) / 999999938.0 AS u2
            FROM (SELECT arrayJoin(range(40000)) AS i)
        )
    )
)
SELECT
    tupleElement(lvl, 1)                                                  AS confidence,
    round(-1 * tupleElement(lvl, 2), 2)                                   AS historical_var_pct,
    round(tupleElement(lvl, 3) * sd - mu, 2)                              AS parametric_var_pct,
    round(-1 * (mu + sd * tupleElement(lvl, 4)), 2)                       AS monte_carlo_var_pct,
    round(-1 * tupleElement(lvl, 2) - (tupleElement(lvl, 3) * sd - mu), 2) AS method_spread
FROM
(
    SELECT
        mu,
        sd,
        arrayJoin([
            ('90.0%', h90,  1.281552, z90,  1),
            ('95.0%', h95,  1.644854, z95,  2),
            ('97.5%', h975, 1.959964, z975, 3),
            ('99.0%', h99,  2.326348, z99,  4),
            ('99.5%', h995, 2.575829, z995, 5),
            ('99.9%', h999, 3.090232, z999, 6)
        ]) AS lvl
    FROM emp
    CROSS JOIN draws
)
ORDER BY tupleElement(lvl, 5)
Run this yourself

No nível 95.0%, os três métodos ficam a um ponto de distância entre si: 1.66% histórico, 1.74% paramétrico e 1.71% simulado. Em 99.9%, eles se separam: 5.85% histórico contra 3.31% paramétrico, uma diferença de 2.54 pontos percentuais com dados idênticos. A coluna simulada fica ao lado da coluna paramétrica em todos os níveis, e essa é a conclusão, não uma falha. A simulação reproduz qualquer distribuição que lhe seja fornecida.

Leia o painel de cima para baixo em uma coluna, e cada aumento da confiança amplia o limite. Leia-o na horizontal, por linha, e a escolha do método quase não importa no centro da distribuição, mas domina na cauda. Um limite de VaR informado sem o método e a janela utilizados não é um número que outra pessoa consiga reproduzir.

O que o VaR não mostra: expected shortfall

O expected shortfall, também chamado de VaR condicional, calcula a média das perdas nos dias que ultrapassam o VaR. O VaR indica onde começa a cauda da distribuição. O expected shortfall mede o que ocorre dentro dela.

QueryVaR versus expected shortfall, a perda média além do limite
O SQL exato por trás de cada número
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-12-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
rets AS
(
    SELECT date, 100 * (close_px / prev_close - 1) AS ret_pct
    FROM
    (
        SELECT date, close_px,
               lagInFrame(close_px, 1) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
        FROM px
    )
    WHERE date >= '2010-01-01' AND prev_close > 0
),
qs AS
(
    SELECT
        quantileExact(0.100)(ret_pct) AS q90,
        quantileExact(0.050)(ret_pct) AS q95,
        quantileExact(0.025)(ret_pct) AS q975,
        quantileExact(0.010)(ret_pct) AS q99,
        quantileExact(0.005)(ret_pct) AS q995,
        quantileExact(0.001)(ret_pct) AS q999
    FROM rets
),
tails AS
(
    SELECT
        any(q90)                        AS var90,
        any(q95)                        AS var95,
        any(q975)                       AS var975,
        any(q99)                        AS var99,
        any(q995)                       AS var995,
        any(q999)                       AS var999,
        avgIf(ret_pct, ret_pct <= q90)  AS es90,
        avgIf(ret_pct, ret_pct <= q95)  AS es95,
        avgIf(ret_pct, ret_pct <= q975) AS es975,
        avgIf(ret_pct, ret_pct <= q99)  AS es99,
        avgIf(ret_pct, ret_pct <= q995) AS es995,
        avgIf(ret_pct, ret_pct <= q999) AS es999
    FROM rets
    CROSS JOIN qs
    HAVING countIf(ret_pct <= q999) > 0
)
SELECT
    tupleElement(lvl, 1)                                  AS confidence,
    round(-1 * tupleElement(lvl, 2), 2)                   AS historical_var_pct,
    round(-1 * tupleElement(lvl, 3), 2)                   AS expected_shortfall_pct,
    round(tupleElement(lvl, 3) / tupleElement(lvl, 2), 2) AS es_to_var_ratio
FROM
(
    SELECT
        arrayJoin([
            ('90.0%', var90,  es90,  1),
            ('95.0%', var95,  es95,  2),
            ('97.5%', var975, es975, 3),
            ('99.0%', var99,  es99,  4),
            ('99.5%', var995, es995, 5),
            ('99.9%', var999, es999, 6)
        ]) AS lvl
    FROM tails
)
ORDER BY tupleElement(lvl, 4)
Run this yourself

Em 99.0%, o VaR desta série é 3.09% e o expected shortfall é 4.43%, ou 1.44 vezes o limite. Em uma distribuição normal, essa razão ficaria perto de 1,15 no mesmo nível. Mesmo em 99.9%, quando o limite já chegou a 5.85%, a média dos dias que ultrapassam o limite é 8.14%. Um limite baseado apenas no VaR trata todos os excessos como se fossem o mesmo evento. A coluna da razão mostra como essa suposição está distante da realidade.

O VaR de 1 dia a 99% significa a mesma coisa para uma instituição e para um trader?

Não. A diferença está sobretudo no horizonte. Um trader que encerra o dia sem posição mantém risco durante algumas horas. Por isso, um limite de uma sessão corresponde ao período de manutenção da posição, embora a trajetória intradiária possa ultrapassar bastante o resultado entre fechamentos. Uma instituição que financia passivos de longo prazo mantém posições durante anos. A sua exposição estende-se por trimestres. Nesse caso, o VaR de 1 dia a 99% funciona como instrumento de capital e monitorização, e não como descrição do risco efetivamente assumido. Durante anos, as regras de capital bancário basearam-se num VaR de 1 dia a 99%. Mais tarde, o enquadramento de risco de mercado de Basileia passou a usar um expected shortfall de 97,5%.

A forma padrão de ligar horizontes é escalar pela raiz quadrada do tempo: multiplicar o valor de uma sessão pela raiz quadrada do número de sessões no horizonte. Esse procedimento pressupõe retornos independentes e um sigma constante. A coluna de sigma ano a ano acima já mostra a segunda metade desse pressuposto a falhar.

QueryEscalonamento pela raiz do tempo versus perdas medidas em vários pregões, nível de 99%
O SQL exato por trás de cada número
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-11-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
multi AS
(
    SELECT
        date,
        100 * (close_px / p1  - 1) AS r1,
        100 * (close_px / p5  - 1) AS r5,
        100 * (close_px / p10 - 1) AS r10,
        100 * (close_px / p20 - 1) AS r20
    FROM
    (
        SELECT
            date,
            close_px,
            lagInFrame(close_px, 1)  OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p1,
            lagInFrame(close_px, 5)  OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p5,
            lagInFrame(close_px, 10) OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p10,
            lagInFrame(close_px, 20) OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p20
        FROM px
    )
    WHERE date >= '2010-01-01' AND p20 > 0
),
q AS
(
    SELECT
        quantileExact(0.01)(r1)  AS q1,
        quantileExact(0.01)(r5)  AS q5,
        quantileExact(0.01)(r10) AS q10,
        quantileExact(0.01)(r20) AS q20
    FROM multi
)
SELECT
    tupleElement(h, 1)                                             AS horizon,
    round(-1 * tupleElement(h, 3), 2)                              AS actual_var_pct,
    round(-1 * q1 * sqrt(tupleElement(h, 2)), 2)                   AS sqrt_scaled_var_pct,
    round(tupleElement(h, 3) / (q1 * sqrt(tupleElement(h, 2))), 2) AS actual_to_scaled_ratio
FROM
(
    SELECT
        q1,
        arrayJoin([
            ('1 session',   1.0,  q1,  1),
            ('5 sessions',  5.0,  q5,  2),
            ('10 sessions', 10.0, q10, 3),
            ('20 sessions', 20.0, q20, 4)
        ]) AS h
    FROM q
)
ORDER BY tupleElement(h, 4)
Run this yourself

A primeira linha é uma verificação de identidade: em 1 session, o valor escalado é igual ao valor medido, com um rácio de 1. Em 20 sessions, os dois valores divergem, e não na direção normalmente apontada como risco do método simplificado: escalar o valor de uma sessão produz 13.8%, enquanto os retornos medidos nesse horizonte registam 10.7%, com um rácio de 0.78. Nesta janela, o valor escalado fica acima da cauda medida para várias sessões. Dois padrões atuam em sentidos opostos. Uma distribuição de uma sessão com caudas pesadas torna-se menos extrema quando os retornos são agregados. Assim, o quantil de 99% agregado aumenta mais lentamente do que a raiz quadrada do horizonte. O agrupamento da volatilidade atua no sentido contrário, concentrando sessões de forte oscilação dentro da mesma janela. Nesta série, o primeiro padrão é mais forte. Nenhuma das duas direções é garantida noutra janela ou noutro ativo. Esse é o ponto: o multiplicador é uma hipótese, não uma medição. Esse agrupamento também é a propriedade em que o volatility targeting se apoia para redimensionar posições.

Há ainda uma limitação, que vale enunciar de forma direta. Tudo acima é um VaR de ativo único sobre um fundo de índice amplo. Uma carteira com dez ativos correlacionados apresenta risco de concentração, que um VaR de carteira trata por meio de uma estimativa de correlação. As correlações, porém, variam mais precisamente quando a estimativa é mais importante.

Notas metodológicas e convenções
  • Os percentis vêm de um quantil exato, e as simulações de Monte Carlo usam uma sequência uniforme inicializada por hash e escrita no SQL. Assim, todos os valores podem ser recalculados e reproduzem o mesmo resultado.
  • Os retornos são variações de preço entre fechamentos, sem reinvestimento de dividendos, que é a convenção usual para um VaR de uma sessão.
  • O painel de horizontes usa janelas sobrepostas: retornos consecutivos de 20 sessões partilham 19 dias. Por isso, a cauda baseia-se em muito menos observações independentes do que o número de linhas sugere.

Perguntas frequentes

O que é um VaR de 1 dia a 99%?

É o nível de perda que é ultrapassado pelo pior 1 em cada 100 dias de negociação, medido ao longo de uma única sessão. Na série acima, a estimativa histórica é 3.09%. O valor identifica um limiar, mas não informa o tamanho das perdas que o ultrapassam.

Qual método de cálculo do VaR é mais preciso?

Nenhum dos três é preciso em termos absolutos, pois cada um responde à pergunta sob premissas diferentes. O VaR histórico é fiel à amostra recebida, mas não diz nada sobre eventos que ficaram fora dela. O VaR paramétrico é barato e subestima as caudas das ações. O método de Monte Carlo só é tão bom quanto a distribuição usada como entrada.

Qual é a diferença entre VaR e expected shortfall?

O VaR é o limiar correspondente a um determinado nível de confiança. O expected shortfall calcula a média das perdas nos dias que ultrapassam esse limiar. Nesta série, o expected shortfall ao nível de 99% equivale a 1.44 vezes o VaR, contra aproximadamente 1,15 vez sob uma distribuição normal.

É possível converter um VaR de 1 dia em um VaR de 10 dias?

Multiplicar pela raiz quadrada de 10 é o atalho padrão. Essa abordagem pressupõe que os retornos são independentes e que a volatilidade é constante. O painel de horizontes mede essa diferença: ao longo de 20 sessões, o valor escalado foi 13.8%, contra 10.7% no valor medido.


Cada painel acima contém o SQL que gerou os dados. Para executar os mesmos três cálculos com outro ticker ou outra janela, faça o pedido em inglês simples no terminal Strasmore.

#risk#value at risk#expected shortfall#quant#position sizing