🇬🇧 English · 🇪🇸 Español
Restricciones narrativas de signo en modelos SVAR
El módulo puremacro.var.identify.narrative_sign implementa la metodología de identificación estructural para vectores autorregresivos (SVAR) desarrollada por Antolín-Díaz y Rubio-Ramírez (2018, American Economic Review), junto con las extensiones de cotas de magnitud de Ludvigson, Ma y Ng (2021, Journal of Political Economy).
En los modelos SVAR identificados mediante restricciones de signo convencionales (Faust 1998; Uhlig 2005; Rubio-Ramírez, Waggoner y Zha 2010), las regiones identificadas por conjuntos (set identification) suelen ser excesivamente amplias, arrojando intervalos de credibilidad que abarcan respuestas cualitativamente contradictorias. Las restricciones narrativas resuelven esta limitación al condicionar las matrices de rotación estructural sobre evidencia histórica concreta —tales como anuncios documentados de la banca central, perturbaciones geopolíticas en el mercado petrolero o episodios específicos de crisis financiera—, descartando aquellas extracciones ortogonales que contradicen el registro histórico establecido.
1. Fundamentos metodológicos
1.1 La forma reducida del VAR y las rotaciones estructurales
Considérese un modelo \(\text{VAR}(p)\) en forma reducida con \(n\) variables endógenas:
donde \(\Sigma\) es la matriz simétrica y definida positiva de covarianzas residuales. Sea \(P = \text{chol}(\Sigma)\) el factor triangular inferior de Cholesky tal que \(P P' = \Sigma\).
Las innovaciones de forma reducida \(u_t\) se vinculan con las perturbaciones estructurales ortonormales \(\varepsilon_t \sim \mathcal{N}(0, I_n)\) mediante:
donde \(Q \in \mathcal{O}(n)\) es una matriz ortogonal de rotación que satisface \(Q Q' = Q' Q = I_n\). Las respuestas al impulso estructurales en el horizonte \(h\) se obtienen como:
siendo \(\Phi_h\) la matriz de coeficientes de media móvil de la forma reducida en el horizonte \(h\) (\(\Phi_0 = I_n\)).
1.2 Tipología de restricciones narrativas
Antolín-Díaz y Rubio-Ramírez (2018) formalizan las restricciones narrativas sobre las realizaciones de las perturbaciones estructurales \(\varepsilon_t = B^{-1} u_t = Q' P^{-1} u_t\) y su descomposición histórica en cuatro categorías fundamentales:
-
Tipo I: Restricción sobre el signo de la perturbación (
'shock_sign')
Fija el signo de la perturbación estructural \(j\) en una fecha histórica precisa \(t^*\): $\(\text{sign}(\varepsilon_{j, t^*}) = s, \quad s \in \{-1, +1\}\)$ Ejemplo: «La perturbación de política monetaria en el cuarto trimestre de 1979 (endurecimiento de Paul Volcker) fue estrictamente positiva (contractiva)». El signo es un campo obligatorio de una restricción de Tipo I (las tuplas(fecha, choque, signo)son la forma abreviada). -
Tipo II: Dominancia en la descomposición histórica (
'hd_dominance',dominance='most')
Exige que la perturbación estructural \(j\) sea la principal contribuyente individual al cambio inesperado acumulado en la variable \(i\) a lo largo de una ventana de \(L\) períodos finalizada en \(t_1^*\): $\(|H_{i, j}(t_1^*, L)| \ge \max_{k \neq j} |H_{i, k}(t_1^*, L)|\)$ donde la contribución histórica de la perturbación \(k\) se define como: $\(H_{i, k}(t_1^*, L) = \sum_{l=0}^L (\Phi_l B)_{i, k} \, \varepsilon_{k, t_1^* - l}\)$ -
Tipo III: Dominancia abrumadora en la descomposición histórica (
'hd_dominance',dominance='overwhelming')
Exige que la contribución absoluta de la perturbación \(j\) supere la suma de las contribuciones de todas las demás perturbaciones estructurales combinadas: $\(|H_{i, j}(t_1^*, L)| \ge \sum_{k \neq j} |H_{i, k}(t_1^*, L)|\)$ Ejemplo: «La perturbación monetaria fue la causa abrumadora del incremento imprevisto de la tasa de fondos federales en 1979Q4». -
Tipo IV: Cotas de magnitud de la perturbación (
'shock_bound')
Impone desigualdades sobre la magnitud absoluta de la perturbación estructural conforme a Ludvigson, Ma y Ng (2021): $\(\underline{m} \le |\varepsilon_{j, t^*}| \le \bar{m}\)$ Ejemplo: «La perturbación monetaria durante el cambio de régimen de Volcker tuvo una magnitud de al menos 2 desviaciones estándar (\(|\varepsilon_{j, t^*}| \ge 2.0\))». La cota es sin signo por defecto (sign=None); indiquesign=+1osign=-1para restringir además el signo de la perturbación.
1.3 Fechas de las restricciones
date puede ser un entero o una fecha de calendario:
- Un entero es siempre el índice de fila (base 0) en
Y(debe ser \(\ge p\) para que exista un residuo), se pase o nodates. - Un
pd.Timestamp, una cadena ISO ("1979-10-06"), undatetime.dateo unnp.datetime64se localiza endates. UnDataFrameindexado por unDatetimeIndexoPeriodIndexproporcionadatesautomáticamente (undates=explícito tiene prioridad). Si hay una coincidencia exacta con una marca temporal, se usa; en caso contrario la fecha se asigna a la observación cuyo período la contiene, con la longitud del período inferida del espaciado del índice: mismo trimestre en un índice trimestral (de modo que el anuncio del 1979-10-06 cae en 1979Q4 tanto si el índice está marcado en 1979-10-01 como en 1979-12-31), mismo mes en un índice mensual, coincidencia exacta en índices más finos. Una fecha cuyo período no figura en el índice produce unValueErroren lugar de reasignarse a un período vecino.
1.4 Muestreo y ponderación por importancia (Algoritmo 1 de AD-RR)
- Generación de rotaciones de Haar: Se muestrean matrices aleatorias gaussianas \(Z \sim \mathcal{N}(0, I_n)\) y se calcula \(Q\) mediante descomposición QR con normalización de signos positivos en la diagonal (\(R_{ii} > 0\)). Con
bayes_draws=Truese extrae un nuevo \((A, c, \Sigma)\) de la distribución a posteriori conjugada Normal-Inversa-Wishart para cada rotación (se vuelve a muestrear hasta obtener un VAR estable, con un máximo de 50 intentos; una extracción sin candidato estable se omite y se contabiliza enn_unstable_draws). - Filtrado por restricciones de signo tradicionales: Se verifica si las respuestas al impulso \(\Psi_h = \Phi_h P Q\) satisfacen las restricciones de signo especificadas en
sign_matrix. - Validación narrativa: Se calculan las perturbaciones estructurales históricas \(\varepsilon = u (B^{-1})'\) y se comprueba el cumplimiento simultáneo de todas las restricciones narrativas en sus fechas correspondientes.
- Ponderación por importancia: A fin de corregir el sesgo que favorecería a parámetros bajo los cuales los eventos narrativos fuesen fortuitamente probables, cada extracción superviviente se pondera por: $\(w = \frac{1}{\omega}\)$ donde \(\omega\) representa la probabilidad de que las restricciones narrativas se verifiquen cuando las perturbaciones en las fechas restringidas se extraen de forma independiente e idénticamente distribuida \(\mathcal{N}(0, I_n)\):
- Para restricciones puras de Tipo I (
'shock_sign') sobre \(m\) pares distintos (fecha, perturbación), \(\omega = 0.5^m\) se conoce de forma analítica cerrada (\(w = 2^m\)). - Para restricciones de Tipo II, III o IV, \(\omega\) se estima mediante simulación de Monte Carlo con \(S = \text{n\_weight\_sims}\) extracciones. Una estimación \(\hat\omega = 0\) se acota inferiormente en \(1/S\) (el ponderador queda limitado a \(S\)); el número de extracciones en las que se activa esa cota se reporta como
n_weight_floor. - Inferencia ponderada: Las bandas de credibilidad y la mediana se obtienen mediante cuantiles ponderados puntuales sobre las extracciones aceptadas. La matriz de impacto de cada extracción superviviente (y, en modo bayesiano, sus coeficientes autorregresivos) se conserva en el resultado, de modo que
.irf(h)y.fevd(h)para horizontes más allá del \(H\) estimado son medianas ponderadas de las extracciones extendidas, nunca una única extracción representativa.
2. Diagnósticos de aceptación y métricas
El objeto NarrativeSignResult generado proporciona métricas diagnósticas exhaustivas:
| Atributo | Tipo | Significado analítico |
|---|---|---|
n_draws |
int |
Total de rotaciones ortogonales de Haar evaluadas. |
n_traditional_accepted |
int |
Número de extracciones que satisfacen las restricciones de signo tradicionales. |
n_narrative_accepted |
int |
Extracciones que satisfacen tanto las restricciones tradicionales como las narrativas. |
acceptance_rate |
float |
Tasa de aceptación global (n_narrative_accepted / n_draws). |
traditional_acceptance_rate |
float |
Fracción de extracciones válidas tradicionales (n_traditional_accepted / n_draws). |
narrative_acceptance_rate |
float |
Tasa condicional de aceptación narrativa (n_narrative_accepted / n_traditional_accepted). |
weights |
np.ndarray |
Ponderadores de importancia en bruto \(1/\hat{\omega}\) para las extracciones supervivientes. |
ess / effective_draws |
float |
Tamaño muestral efectivo de Kish: \((\sum w_i)^2 / \sum w_i^2\). |
n_weight_floor |
int |
Extracciones supervivientes cuya \(\hat\omega\) fue 0 y se acotó en \(1/\text{n\_weight\_sims}\) (sus ponderadores están limitados, por lo que ess sobreestima la eficiencia). |
n_unstable_draws |
int |
Solo en modo bayesiano: extracciones a posteriori omitidas por no hallarse un VAR estable en 50 intentos. |
restriction_labels |
tuple[str] |
Etiquetas legibles que identifican cada restricción impuesta. |
restriction_fail_counts |
tuple[int] |
Diagnóstico de rigidez: extracciones tradicionales rechazadas por cada restricción individual. |
accepted_B, accepted_A |
np.ndarray |
Matrices de impacto \((m, n, n)\) de las \(m\) extracciones supervivientes y, en modo bayesiano, sus matrices autorregresivas \((m, p, n, n)\) (None en modo MCO). |
A_list, intercept, residuals, Sigma, B |
— | Objetos de forma reducida y matriz de impacto de la extracción representativa (la más cercana a la mediana); B B' = Sigma en ambos modos. |
init_y |
np.ndarray |
Las primeras \(p\) observaciones, condición inicial por defecto de la descomposición histórica. |
Avisos
identify_narrative_sign emite un RuntimeWarning (nunca degrada en silencio) cuando:
- sobreviven demasiado pocas extracciones para resolver las bandas solicitadas — menos de \(\max(10, \lceil 2/(1-\text{ci}) \rceil)\) extracciones aceptadas, en cuyo caso las bandas puntuales pueden colapsar sobre la mediana;
- los ponderadores de importancia están concentrados —
essde Kish por debajo del 10 % den_narrative_accepted, de modo que unas pocas extracciones dominan las bandas ponderadas; - se activa la cota inferior de \(\omega\) en al menos una extracción superviviente (
n_weight_floor > 0; aumenten_weight_sims); - se omitieron extracciones a posteriori inestables en modo bayesiano (
n_unstable_draws > 0). Si todas las extracciones a posteriori son inestables, se lanza unRuntimeError.
summary() muestra el modo de forma reducida, el número de extracciones inestables y el número de cotas activadas junto a los recuentos de aceptación.
3. Aplicación: Choque de política monetaria de Volcker (1979Q4)
En octubre de 1979, la Reserva Federal presidida por Paul Volcker adoptó un nuevo esquema operativo centrado en objetivos cuantitativos de reservas no prestadas, provocando un aumento histórico sin precedentes en la tasa de fondos federales con el propósito de erradicar la persistente inflación estadounidense.
Antolín-Díaz y Rubio-Ramírez (2018) retoman el SVAR monetario identificado por signos de Uhlig (2005) —un VAR mensual de seis variables para EE. UU. sobre los datos de Uhlig— y lo afinan con restricciones narrativas sobre el episodio de Volcker, principalmente:
- Restricción narrativa 1 (Tipo I): la perturbación de política monetaria de octubre de 1979 es positiva (contractiva).
- Restricción narrativa 2 (Tipo III): la perturbación monetaria es la contribuyente abrumadora al movimiento inesperado de la tasa de fondos federales en octubre de 1979.
Su resultado principal es que estas restricciones estrechan sustancialmente el conjunto identificado por signos y eliminan las respuestas contraintuitivas que las restricciones de signo tradicionales admiten por sí solas.
El ejemplo ejecutable siguiente aplica las mismas dos restricciones de Volcker a un pequeño VAR trimestral sintético de 3 variables (TFF, inflación, crecimiento del producto) con un choque de +3.5 desviaciones estándar inyectado en 1979Q4. Ilustra el mecanismo y la API; no reproduce las cifras del artículo (los datos de Uhlig no se descargan sin conexión y AD-RR presentan sus FIR gráficamente). puremacro.examples.narrative_sign_adrr.run_demo() es una versión más completa del mismo ejercicio sintético, y tests/test_replication_adrr2018.py fija una instantánea de regresión de la propia salida de esa demostración: es una prueba de regresión, no una comprobación frente a valores publicados.
Ejemplo ejecutable
import numpy as np
import pandas as pd
from puremacro.var.identify import (
NarrativeRestriction,
identify_narrative_sign,
)
# 1. Panel trimestral sintético (TFF, Inflación, Crecimiento del PIB), 1965Q1-2007Q4
rng = np.random.default_rng(42)
T = 172
fechas = pd.date_range("1965-01-01", periods=T, freq="QS") # marcas de inicio de trimestre
volcker_idx = fechas.get_loc("1979-10-01") # fila de 1979Q4
# Simulación de un VAR estacionario calibrado
Y = np.zeros((T, 3))
for t in range(1, T):
Y[t] = 0.5 * Y[t-1] + rng.standard_normal(3)
# Inyección del episodio de ajuste de Volcker en 1979Q4
Y[volcker_idx, 0] += 3.5
# 2. Restricciones de signo tradicionales en h = 0
# Choque 0 = Política monetaria contractiva (+TFF, -Inflación, -PIB)
sign_matrix = {0: np.array([+1, -1, -1])}
# 3. Restricciones narrativas. Las fechas de calendario se resuelven mediante
# `dates` (el anuncio del FOMC del 1979-10-06 cae en la observación de
# 1979Q4); un entero como `volcker_idx` se leería como índice de fila.
restricciones = [
# Tipo I: perturbación monetaria positiva en 1979Q4
("1979-10-06", 0, +1),
# Tipo III: choque monetario como causa abrumadora del repunte de la TFF
NarrativeRestriction(
kind="hd_dominance",
date="1979-10-06",
shock=0,
variable=0, # TFF
window=0,
dominance="overwhelming",
),
]
# 4. Estimación del SVAR con restricciones narrativas
res_narr = identify_narrative_sign(
Y,
restricciones,
p=2,
horizon=16,
sign_matrix=sign_matrix,
dates=fechas,
n_draws=3000,
n_weight_sims=300,
ci=0.68,
seed=123,
)
# 5. Diagnósticos e informe resumen
print(res_narr.summary())
print(f"Aceptación tradicional: {res_narr.traditional_acceptance_rate:.2%}")
print(f"Aceptación narrativa : {res_narr.narrative_acceptance_rate:.2%}")
print(f"Tamaño efectivo (ESS) : {res_narr.effective_draws:.1f}")
# 6. Figuras, tablas y objetos derivados
fig = res_narr.plot(shock_idx=0, target_idx=None) # un panel por variable
tabla_md = res_narr.to_markdown(target_idx=0, shock_idx=0)
tabla_tex = res_narr.to_latex(target_idx=0, shock_idx=0)
fevd_20 = res_narr.fevd(horizon=20) # mediana ponderada más allá de H=16
dh_tff = res_narr.historical_decomposition(variable=0)
4. Especificación completa de la API
identify_narrative_sign (alias narrative_sign_svar)
identify_narrative_sign(
Y: np.ndarray | pd.DataFrame | VarEstimateResult,
restrictions: list | None = None,
*,
p: int | None = None,
lags: int | None = None, # alias de p
horizon: int | None = None, # por defecto 20
horizons: int | None = None, # alias de horizon
sign_matrix: dict | np.ndarray | None = None,
dates: Sequence[datetime-like] | None = None,
bayes_draws: bool = False,
n_draws: int = 2000,
n_weight_sims: int = 500,
ci: float = 0.9,
seed: int | None = 0,
) -> NarrativeSignResult
Parámetros:
Y: Matriz temporal \((T, n)\),DataFrameo resultado previoVarEstimateResult. UnDataFrameconDatetimeIndex/PeriodIndexproporcionadatesautomáticamente y sus nombres de columna pasan aresult.names.restrictions: Lista de restricciones narrativas. Admite instancias deNarrativeRestriction, tuplas breves(fecha, indice_choque, signo)(Tipo I) o eventospuremacro.narrative.NarrativeEvent(asignados automáticamente a Tipo I sobre el choque 0, usando la fecha de anuncio y el signo).p/lags: Orden autorregresivo del VAR (inferido automáticamente siYes unVarEstimateResult; obligatorio en otro caso). Pasar ambos con valores distintos produceValueError.horizon/horizons: Horizonte de proyección \(H\) para las respuestas al impulso (por defecto20). Pasar ambos con valores distintos produceValueError.sign_matrix: Diccionario{h: S}con matrices de signo tradicionales \(S \in \{-1, 0, 1\}\) de forma \((n, n)\), o \((n,)\) aplicado a la columna del choque 0; un array suelto equivale a{0: S}.None(por defecto) no impone restricciones de signo tradicionales.dates: Índices o etiquetas temporales de longitud \(T\) para resolver fechas de calendario (véase §1.3). Tiene prioridad sobre el índice delDataFrame.bayes_draws: Si esTrue, muestrea coeficientes \((A, \Sigma)\) de la distribución a posteriori Normal-Inversa-Wishart para cada extracción (véase §1.4).n_draws: Cantidad total de rotaciones de Haar candidatas (por defecto2000, debe ser \(\ge 1\)).n_weight_sims: Muestras de Monte Carlo empleadas en el cómputo de \(\omega\) para restricciones de Tipo II, III o IV (por defecto500, debe ser \(\ge 1\)).ci: Nivel de cobertura para las bandas de credibilidad (por defecto0.90); debe estar estrictamente entre 0 y 1.seed: Semilla del generador de rotaciones y del simulador de ponderadores (por defecto0;Noneusa entropía nueva).
Los argumentos con nombre desconocidos producen TypeError: una errata como n_draw= nunca se ejecuta en silencio con el valor por defecto.
5. Interfaz de resultados y capacidades analíticas
La clase NarrativeSignResult implementa las herramientas del protocolo de presentación de puremacro:
.plot(*, target_idx=0, shock_idx=0, title="", ylabel="Response", scale=1.0, ax=None): con índices enteros, un único panel (mediana de la respuesta y banda de credibilidad sombreada) para ese par (variable, choque), opcionalmente dibujado sobreax.target_idx=Nonedibuja un panel por variable de respuesta para el choque dado;shock_idx=Noneun panel por choque; ambosNonela rejilla completa \(n \times n\). Devuelve laFigurede matplotlib..summary(): Informe en texto plano con el modo de forma reducida, las tasas de aceptación, el tamaño muestral efectivo, los recuentos de cotas/extracciones inestables y las frecuencias de rechazo de cada restricción..to_frame(),.to_markdown(),.to_latex(),.to_typst(): Tablas ordenadas de la mediana de respuesta y sus bandas (target_idx/shock_idxseleccionan el par)..irf(horizon=None): Respuestas al impulso de mediana ponderada con forma \((h+1, n, n)\). Para \(h \le H\), un corte deirf_median; para \(h > H\), la mediana ponderada de la FIR de cada extracción aceptada extendida hasta \(h\) (las primeras \(H+1\) filas coinciden conirf_median)..fevd(horizon=None): Descomposición de la varianza del error de pronóstico de mediana ponderada, con filas renormalizadas para sumar 1; extendida más allá de \(H\) del mismo modo..historical_decomposition(variable=None, shock=None, init_y=None): Descomposición histórica que usa la \(B\) de la extracción representativa junto con los objetos de forma reducida de esa misma extracción (MCO en modo MCO; los propios \((A, c)\) y residuos de la extracción a posteriori en modo bayesiano).init_ytoma por defecto las primeras \(p\) observaciones almacenadas, de modo quedeterministic + shocks.sum(axis=2)reproduce \(y_t\) exactamente para \(t \ge p\). Devuelve un diccionario ('shocks'de forma \((T-p, n, n)\),'deterministic'de forma \((T-p, n)\)) o unDataFrameordenado cuando se indicanvariabley/oshock.