macroshock 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- macroshock/__init__.py +1 -0
- macroshock/bootstrap.py +259 -0
- macroshock/data.py +111 -0
- macroshock/fc.py +68 -0
- macroshock/identification.py +315 -0
- macroshock/irf.py +104 -0
- macroshock/macroshock.py +788 -0
- macroshock/plotting.py +275 -0
- macroshock/stats.py +78 -0
- macroshock/var.py +109 -0
- macroshock-0.1.0.dist-info/METADATA +37 -0
- macroshock-0.1.0.dist-info/RECORD +15 -0
- macroshock-0.1.0.dist-info/WHEEL +5 -0
- macroshock-0.1.0.dist-info/licenses/LICENSE +21 -0
- macroshock-0.1.0.dist-info/top_level.txt +1 -0
macroshock/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .macroshock import SVAR
|
macroshock/bootstrap.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# bootstrap.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from scipy.linalg import solve_triangular
|
|
7
|
+
from .var import estimate_var
|
|
8
|
+
from .fc import compute_forecast
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def bootstrap_svar(model, typ: str, shock: int = 1):
|
|
12
|
+
"""
|
|
13
|
+
Replica la lógica original de SVAR.Bootstrap, pero fuera de la clase.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
model : objeto tipo SVAR
|
|
18
|
+
Debe tener los mismos atributos que usaba el método Bootstrap original.
|
|
19
|
+
typ : {'IR', 'FC'}
|
|
20
|
+
Tipo de bootstrap: IRFs o Forecasts.
|
|
21
|
+
shock : int
|
|
22
|
+
Índice (1-based) del shock para IRFs.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
draws : dict[str, np.ndarray]
|
|
27
|
+
Diccionario {variable: draws} con shape:
|
|
28
|
+
- IR: (reps, steps)
|
|
29
|
+
- FC: (reps, past + horizon)
|
|
30
|
+
"""
|
|
31
|
+
# BEGIN BOOTSTRAP
|
|
32
|
+
b = 0
|
|
33
|
+
|
|
34
|
+
if typ == "IR":
|
|
35
|
+
draws = {var: np.zeros((model.reps, model.steps)) for var in model.variables}
|
|
36
|
+
elif typ == "FC":
|
|
37
|
+
draws = {
|
|
38
|
+
var: np.zeros((model.reps, model.past + model.horizon))
|
|
39
|
+
for var in model.variables
|
|
40
|
+
}
|
|
41
|
+
else:
|
|
42
|
+
raise ValueError("typ must be 'IR' or 'FC'.")
|
|
43
|
+
|
|
44
|
+
while True:
|
|
45
|
+
# progreso
|
|
46
|
+
if b in list(
|
|
47
|
+
range(int(model.reps / 10) - 1, model.reps, int(model.reps / 10))
|
|
48
|
+
):
|
|
49
|
+
print(str(b + 1) + "/" + str(model.reps))
|
|
50
|
+
|
|
51
|
+
# ---------- tipo de remuestreo ----------
|
|
52
|
+
if model.resampling == 1: # normal
|
|
53
|
+
resampled_resid = model.resid.copy()
|
|
54
|
+
for i in range(model.n_obs):
|
|
55
|
+
rnd = np.random.randint(0, model.n_obs)
|
|
56
|
+
resampled_resid[i] = model.resid[rnd]
|
|
57
|
+
elif model.resampling == 2: # wild
|
|
58
|
+
rsigns = np.random.choice([-1, 1], size=model.resid.shape[0])
|
|
59
|
+
# Nota: igual que el código original, esto modifica model.resid en sitio
|
|
60
|
+
resampled_resid = model.resid
|
|
61
|
+
for i in range(model.resid.shape[0]):
|
|
62
|
+
resampled_resid[i, :] = model.resid[i, :] * rsigns[i]
|
|
63
|
+
else:
|
|
64
|
+
raise ValueError("resampling must be 1 (normal) or 2 (wild).")
|
|
65
|
+
|
|
66
|
+
# ---------- condición inicial para recursión hacia adelante ----------
|
|
67
|
+
Y_sim = model.Y.copy()
|
|
68
|
+
Y_0 = np.zeros((model.n_vars * model.lags,))
|
|
69
|
+
|
|
70
|
+
for i in range(model.lags):
|
|
71
|
+
Y_0[i * model.n_vars : (i + 1) * model.n_vars] = model.data[
|
|
72
|
+
model.lags - i - 1, :
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
# ---------- generar datos simulados ----------
|
|
76
|
+
for i in range(model.n_obs):
|
|
77
|
+
u = np.zeros((model.n_vars * model.lags,))
|
|
78
|
+
u[0 : model.n_vars] = resampled_resid[i]
|
|
79
|
+
|
|
80
|
+
if model.const == 0:
|
|
81
|
+
FY_0 = model.F @ Y_0
|
|
82
|
+
Y_0 = FY_0 + u
|
|
83
|
+
elif model.const == 1:
|
|
84
|
+
beta_const = np.zeros((model.n_vars * model.lags,))
|
|
85
|
+
beta_const[0 : model.n_vars] = model.beta[:, 0]
|
|
86
|
+
FY_0 = model.F @ Y_0
|
|
87
|
+
Y_0 = beta_const + FY_0 + u
|
|
88
|
+
elif model.const == 2:
|
|
89
|
+
beta_const = np.zeros((model.n_vars * model.lags,))
|
|
90
|
+
beta_const[0 : model.n_vars] = model.beta[:, 0]
|
|
91
|
+
beta_trend = np.zeros((model.n_vars * model.lags,))
|
|
92
|
+
beta_trend[0 : model.n_vars] = model.beta[:, 1]
|
|
93
|
+
FY_0 = model.F @ Y_0
|
|
94
|
+
Y_0 = beta_const + ((i + model.lags) * beta_trend) + FY_0 + u
|
|
95
|
+
elif model.const == 3:
|
|
96
|
+
beta_const = np.zeros((model.n_vars * model.lags,))
|
|
97
|
+
beta_const[0 : model.n_vars] = model.beta[:, 0]
|
|
98
|
+
beta_trend = np.zeros((model.n_vars * model.lags,))
|
|
99
|
+
beta_trend[0 : model.n_vars] = model.beta[:, 1]
|
|
100
|
+
beta_sqrd = np.zeros((model.n_vars * model.lags,))
|
|
101
|
+
beta_sqrd[0 : model.n_vars] = model.beta[:, 2]
|
|
102
|
+
FY_0 = model.F @ Y_0
|
|
103
|
+
Y_0 = (
|
|
104
|
+
beta_const
|
|
105
|
+
+ (i * beta_trend)
|
|
106
|
+
+ ((i**2) * beta_sqrd)
|
|
107
|
+
+ FY_0
|
|
108
|
+
+ u
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if model.DUM is not None:
|
|
112
|
+
Y_0[0 : model.n_vars] = Y_0[0 : model.n_vars] + model.beta[
|
|
113
|
+
:,
|
|
114
|
+
model.const
|
|
115
|
+
+ (model.n_vars * model.lags)
|
|
116
|
+
+ model.n_ex : model.const
|
|
117
|
+
+ (model.n_vars * model.lags)
|
|
118
|
+
+ model.n_ex
|
|
119
|
+
+ model.n_dum,
|
|
120
|
+
] @ model.D[i, :]
|
|
121
|
+
|
|
122
|
+
Y_sim[i] = Y_0[0 : model.n_vars]
|
|
123
|
+
|
|
124
|
+
Y_sim = np.vstack([model.data[0 : model.lags, :], Y_sim])
|
|
125
|
+
|
|
126
|
+
# ---------- remuestreo del instrumento (método IV) ----------
|
|
127
|
+
if model.method == "IV":
|
|
128
|
+
iv = model.z
|
|
129
|
+
if model.resampling == 2 and model.iv is not None:
|
|
130
|
+
for i in range(model.n_iv):
|
|
131
|
+
for j in range(len(iv[i])):
|
|
132
|
+
iv[i][j] = iv[i][j] * rsigns[-len(iv[i]) :][j]
|
|
133
|
+
|
|
134
|
+
# ---------- construir Ysim y Xsim ----------
|
|
135
|
+
Ysim = Y_sim[model.lags :]
|
|
136
|
+
Xsim_list = []
|
|
137
|
+
|
|
138
|
+
for i in range(model.lags):
|
|
139
|
+
Xsim_list.append(
|
|
140
|
+
Y_sim[(model.lags - i - 1) : model.n_obs + model.lags - i - 1]
|
|
141
|
+
)
|
|
142
|
+
Xsim = np.hstack(Xsim_list)
|
|
143
|
+
|
|
144
|
+
if model.const == 1:
|
|
145
|
+
Xsim = np.hstack([np.ones((model.n_obs, 1)), Xsim])
|
|
146
|
+
elif model.const == 2:
|
|
147
|
+
trend = np.arange(1, model.n_obs + 1).reshape(-1, 1)
|
|
148
|
+
Xsim = np.hstack([np.ones((model.n_obs - model.lags, 1)), trend, Xsim])
|
|
149
|
+
elif model.const == 3:
|
|
150
|
+
trend = np.arange(1, model.n_obs + 1).reshape(-1, 1)
|
|
151
|
+
Xsim = np.hstack(
|
|
152
|
+
[np.ones((model.n_obs, 1)), trend, trend**2, Xsim]
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if model.EXOGdata is not None:
|
|
156
|
+
if len(model.EXOG) == model.n_obs:
|
|
157
|
+
Xsim_exog = model.EXOGdata
|
|
158
|
+
else:
|
|
159
|
+
Xsim_exog = model.EXOGdata[-model.n_obs :]
|
|
160
|
+
if len(Xsim_exog) != model.n_obs:
|
|
161
|
+
print("Exogenous variables in different domain")
|
|
162
|
+
Xsim = np.hstack([Xsim, Xsim_exog])
|
|
163
|
+
|
|
164
|
+
if model.DUM is not None:
|
|
165
|
+
Xsim = np.hstack([Xsim, model.D[: model.n_obs, :]])
|
|
166
|
+
|
|
167
|
+
# ---------- asegurar shapes ----------
|
|
168
|
+
if isinstance(Ysim, pd.Series):
|
|
169
|
+
Ysim = Ysim.values.reshape(-1, 1)
|
|
170
|
+
if isinstance(Xsim, pd.Series):
|
|
171
|
+
Xsim = Xsim.values.reshape(-1, 1)
|
|
172
|
+
|
|
173
|
+
Ysim = Ysim.reshape(-1, 1) if Ysim.ndim == 1 else Ysim
|
|
174
|
+
Xsim = Xsim.reshape(-1, 1) if Xsim.ndim == 1 else Xsim
|
|
175
|
+
|
|
176
|
+
Xsim = np.array(Xsim, dtype=float)
|
|
177
|
+
Ysim = np.array(Ysim, dtype=float)
|
|
178
|
+
|
|
179
|
+
# ---------- estimar VAR simulado ----------
|
|
180
|
+
simres=estimate_var(Ysim, Xsim, model.R)
|
|
181
|
+
|
|
182
|
+
XXsim = Xsim.T @ Xsim
|
|
183
|
+
XXsim_inv = np.linalg.inv(XXsim)
|
|
184
|
+
betasim = simres.beta
|
|
185
|
+
sigma_usim = simres.sigma_u
|
|
186
|
+
residsim = simres.resid
|
|
187
|
+
|
|
188
|
+
beta_stdsim = np.zeros((model.n_vars, model.n_cols))
|
|
189
|
+
for i in range(model.n_vars):
|
|
190
|
+
beta_stdsim[i, :] = np.sqrt(
|
|
191
|
+
np.diagonal(sigma_usim[i, i] * XXsim_inv)
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# ---------- construir Fsim ----------
|
|
195
|
+
topsim = betasim[:, -(model.n_vars * model.lags) :]
|
|
196
|
+
bottomsim = np.zeros(
|
|
197
|
+
((model.lags - 1) * model.n_vars, model.lags * model.n_vars)
|
|
198
|
+
)
|
|
199
|
+
for i in range((model.lags - 1) * model.n_vars):
|
|
200
|
+
bottomsim[i, i] = 1
|
|
201
|
+
Fsim = np.vstack([topsim, bottomsim])
|
|
202
|
+
|
|
203
|
+
# ---------- identificar Bsim ----------
|
|
204
|
+
if model.method == "short":
|
|
205
|
+
Bsim = np.linalg.cholesky(sigma_usim)
|
|
206
|
+
|
|
207
|
+
elif model.method == "long":
|
|
208
|
+
M1 = np.eye(model.n_vars)
|
|
209
|
+
for k in range(model.lags):
|
|
210
|
+
M1 = M1 - Fsim[
|
|
211
|
+
0 : model.n_vars, k * model.n_vars : (k + 1) * model.n_vars
|
|
212
|
+
]
|
|
213
|
+
M1 = np.linalg.inv(M1)
|
|
214
|
+
D = np.linalg.cholesky(M1 @ sigma_usim @ M1.T)
|
|
215
|
+
Bsim = np.linalg.solve(M1, D)
|
|
216
|
+
|
|
217
|
+
elif model.method == "IV":
|
|
218
|
+
usim = residsim
|
|
219
|
+
for j in range(model.n_iv):
|
|
220
|
+
XX_invfssim = (np.var(iv[j])) ** (-1)
|
|
221
|
+
betafssim = XX_invfssim * (iv[j].T @ usim[-len(iv[j]) :, j])
|
|
222
|
+
ujhatsim = iv[j] * betafssim
|
|
223
|
+
Bsim = np.zeros((model.n_vars, model.n_vars))
|
|
224
|
+
Bsim[j, j] = 1
|
|
225
|
+
for i in range(model.n_vars):
|
|
226
|
+
XX_invsssim = (np.var(ujhatsim)) ** (-1)
|
|
227
|
+
if i != j:
|
|
228
|
+
betasssim = XX_invsssim * (
|
|
229
|
+
ujhatsim.T.T @ usim[-len(iv[j]) :, i]
|
|
230
|
+
)
|
|
231
|
+
Bsim[i, j] = betasssim
|
|
232
|
+
Csim = np.linalg.cholesky(sigma_usim)
|
|
233
|
+
q = solve_triangular(Csim, Bsim[:, j], lower=True)
|
|
234
|
+
vsim = np.linalg.norm(q)
|
|
235
|
+
# vsim = np.linalg.norm(Csim[:,j])
|
|
236
|
+
Bsim[:, j] = Bsim[:, j] / vsim
|
|
237
|
+
|
|
238
|
+
# ---------- almacenar draws ----------
|
|
239
|
+
if typ == "IR":
|
|
240
|
+
ir_b = model.IR(Bsim, Fsim, shock)
|
|
241
|
+
|
|
242
|
+
if np.any(Bsim != 0):
|
|
243
|
+
if model.method in ("short", "long", "IV"):
|
|
244
|
+
for k in range(model.n_vars):
|
|
245
|
+
draws[model.variables[k]][b, :] = ir_b[:, k]
|
|
246
|
+
b += 1
|
|
247
|
+
|
|
248
|
+
if typ == "FC":
|
|
249
|
+
fc_b = compute_forecast(model, Fsim)
|
|
250
|
+
if np.any(Bsim != 0):
|
|
251
|
+
if model.method in ("short", "long", "IV"):
|
|
252
|
+
for k in range(model.n_vars):
|
|
253
|
+
draws[model.variables[k]][b, :] = fc_b[:, k]
|
|
254
|
+
b += 1
|
|
255
|
+
|
|
256
|
+
if b >= model.reps:
|
|
257
|
+
break
|
|
258
|
+
|
|
259
|
+
return draws
|
macroshock/data.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#data.py
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class YXR:
|
|
9
|
+
Y: np.ndarray
|
|
10
|
+
X: np.ndarray
|
|
11
|
+
R: np.ndarray
|
|
12
|
+
|
|
13
|
+
def YX(model) -> YXR:
|
|
14
|
+
|
|
15
|
+
model.n_obs, _ = model.data.shape
|
|
16
|
+
|
|
17
|
+
Y = model.data[model.lags:,:]
|
|
18
|
+
X = []
|
|
19
|
+
|
|
20
|
+
for i in range(model.lags):
|
|
21
|
+
X.append(model.data[(model.lags-i-1) : model.n_obs - model.lags + (model.lags-i-1),:])
|
|
22
|
+
X = np.hstack(X)
|
|
23
|
+
|
|
24
|
+
if model.const == 1:
|
|
25
|
+
X = np.hstack([np.ones((model.n_obs - model.lags, 1)), X])
|
|
26
|
+
elif model.const == 2:
|
|
27
|
+
trend = np.arange(1, model.n_obs - model.lags + 1).reshape(-1, 1)
|
|
28
|
+
X = np.hstack([np.ones((model.n_obs - model.lags, 1)), trend, X])
|
|
29
|
+
elif model.const == 3:
|
|
30
|
+
trend = np.arange(1, model.n_obs - model.lags + 1).reshape(-1, 1)
|
|
31
|
+
X = np.hstack([np.ones((model.n_obs - model.lags, 1)), trend, trend**2, X])
|
|
32
|
+
|
|
33
|
+
model.n_obs, _ = X.shape
|
|
34
|
+
|
|
35
|
+
if model.EXOGdata is not None:
|
|
36
|
+
if model.n_obsex==model.n_obs:
|
|
37
|
+
X_exog=model.EXOGdata
|
|
38
|
+
else:
|
|
39
|
+
X_exog=model.EXOGdata[-model.n_obs:]
|
|
40
|
+
if len(X_exog)!=model.n_obs:
|
|
41
|
+
print('Exogenous variables in different domain')
|
|
42
|
+
X=np.hstack([X,X_exog])
|
|
43
|
+
|
|
44
|
+
if model.DUM is not None:
|
|
45
|
+
if model.DUM=='Q':
|
|
46
|
+
model.dummies=['Q'+f"{i+1}" for i in range(4)]
|
|
47
|
+
model.n_dum=3
|
|
48
|
+
model.D=np.zeros((model.n_obs+model.horizon,4))
|
|
49
|
+
for j in range(model.n_obs+model.horizon):
|
|
50
|
+
for x in range(4):
|
|
51
|
+
if j in range(x,model.n_obs+model.horizon,4):
|
|
52
|
+
model.D[j,x]=1
|
|
53
|
+
if model.DUM=='M':
|
|
54
|
+
model.dummies=['M'+f"{i+1}" for i in range(12)]
|
|
55
|
+
model.n_dum=11
|
|
56
|
+
model.D=np.zeros((model.n_obs+model.horizon,12))
|
|
57
|
+
for j in range(model.n_obs+model.horizon):
|
|
58
|
+
for x in range(12):
|
|
59
|
+
if j in range(x,model.n_obs+model.horizon,12):
|
|
60
|
+
model.D[j,x]=1
|
|
61
|
+
model.D=model.D[:,:model.n_dum]
|
|
62
|
+
model.Dhorizon=model.D[model.n_obs:model.n_obs+model.horizon,:]
|
|
63
|
+
X=np.hstack([X,model.D[0:model.n_obs,:]])
|
|
64
|
+
else:
|
|
65
|
+
model.n_dum=0
|
|
66
|
+
|
|
67
|
+
model.Y=Y
|
|
68
|
+
model.X=X
|
|
69
|
+
|
|
70
|
+
if isinstance(model.Y, pd.Series):
|
|
71
|
+
model.Y=model.Y.values.reshape(-1, 1)
|
|
72
|
+
if isinstance(model.X, pd.Series):
|
|
73
|
+
model.X=model.X.values.reshape(-1, 1)
|
|
74
|
+
|
|
75
|
+
model.Y = model.Y.reshape(-1, 1) if model.Y.ndim == 1 else model.Y
|
|
76
|
+
model.X = model.X.reshape(-1, 1) if model.X.ndim == 1 else model.X
|
|
77
|
+
|
|
78
|
+
_ , model.n_vars=model.Y.shape
|
|
79
|
+
model.n_obs, model.n_cols= model.X.shape
|
|
80
|
+
|
|
81
|
+
model.X = np.array(model.X, dtype=float)
|
|
82
|
+
model.Y = np.array(model.Y, dtype=float)
|
|
83
|
+
|
|
84
|
+
if model.restrictions is None:
|
|
85
|
+
model.R=np.eye(model.n_cols*model.n_vars)
|
|
86
|
+
|
|
87
|
+
elif model.restrictions=='zeros':
|
|
88
|
+
RR=np.eye(model.n_cols*model.n_vars)
|
|
89
|
+
drop=[]
|
|
90
|
+
|
|
91
|
+
for i in range(model.n_vars):
|
|
92
|
+
for j in range(model.n_vars):
|
|
93
|
+
if model.R[i][j]==0:
|
|
94
|
+
for k in range(model.lags):
|
|
95
|
+
drop.append(model.const*model.n_vars+i+(model.n_vars*j)+(k*model.n_vars*model.n_vars))
|
|
96
|
+
|
|
97
|
+
model.R=np.delete(RR, drop, axis=1)
|
|
98
|
+
|
|
99
|
+
elif model.restrictions=='custom zeros':
|
|
100
|
+
RR=model.R.ravel(order='F')
|
|
101
|
+
RRR=np.eye(len(RR))
|
|
102
|
+
drop=[]
|
|
103
|
+
|
|
104
|
+
for i in range(len(RR)):
|
|
105
|
+
if RR[i]==0:
|
|
106
|
+
drop.append(i)
|
|
107
|
+
|
|
108
|
+
model.R=np.delete(RRR, drop, axis=1)
|
|
109
|
+
|
|
110
|
+
return YXR(Y=model.Y, X=model.X, R=model.R)
|
|
111
|
+
|
macroshock/fc.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#fc.py
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
def compute_forecast(modelo, F):
|
|
5
|
+
|
|
6
|
+
# 1. Initialize State Vector (start)
|
|
7
|
+
start = np.zeros((modelo.n_vars * modelo.lags, 1))
|
|
8
|
+
for i in range(modelo.lags):
|
|
9
|
+
# Stacking lags: y_t, y_{t-1}, ...
|
|
10
|
+
start[i*modelo.n_vars : (i+1)*modelo.n_vars, :] = \
|
|
11
|
+
modelo.Y[modelo.n_obs - i - 1, :].reshape(-1, 1)
|
|
12
|
+
|
|
13
|
+
# 2. Initialize Forecast Array
|
|
14
|
+
Yfc = np.zeros((modelo.horizon + modelo.past, modelo.n_vars))
|
|
15
|
+
Yfc[0:modelo.past, :] = modelo.Y[-modelo.past:, :]
|
|
16
|
+
|
|
17
|
+
# 3. Pre-compute Deterministic Vectors (Optimization)
|
|
18
|
+
# Moving this outside the loop prevents creating new arrays 120 times
|
|
19
|
+
beta_const = np.zeros((modelo.n_vars * modelo.lags, 1))
|
|
20
|
+
beta_trend = np.zeros((modelo.n_vars * modelo.lags, 1))
|
|
21
|
+
beta_sqrd = np.zeros((modelo.n_vars * modelo.lags, 1))
|
|
22
|
+
|
|
23
|
+
if modelo.const >= 1:
|
|
24
|
+
beta_const[0:modelo.n_vars, 0] = modelo.beta[:, 0]
|
|
25
|
+
if modelo.const >= 2:
|
|
26
|
+
beta_trend[0:modelo.n_vars, 0] = modelo.beta[:, 1]
|
|
27
|
+
if modelo.const == 3:
|
|
28
|
+
beta_sqrd[0:modelo.n_vars, 0] = modelo.beta[:, 2]
|
|
29
|
+
|
|
30
|
+
# 4. Forecast Loop
|
|
31
|
+
for i in range(modelo.horizon):
|
|
32
|
+
|
|
33
|
+
# A. Transition Step
|
|
34
|
+
FYs = F @ start
|
|
35
|
+
|
|
36
|
+
# B. Add Deterministic Terms (Const, Trend, etc)
|
|
37
|
+
# Note: Ensure your trend logic (i + lags) matches your estimation definition
|
|
38
|
+
if modelo.const == 1:
|
|
39
|
+
FYs += beta_const
|
|
40
|
+
elif modelo.const == 2:
|
|
41
|
+
FYs += beta_const + ((i + modelo.lags) * beta_trend)
|
|
42
|
+
elif modelo.const == 3:
|
|
43
|
+
FYs += beta_const + (i * beta_trend) + ((i**2) * beta_sqrd)
|
|
44
|
+
|
|
45
|
+
# C. Add Dummies (if present)
|
|
46
|
+
if modelo.DUM is not None:
|
|
47
|
+
# Calculate dummy index offset for readability
|
|
48
|
+
idx_start = modelo.const + (modelo.lags * modelo.n_vars) + modelo.n_ex
|
|
49
|
+
|
|
50
|
+
if modelo.DUM == 'Q':
|
|
51
|
+
dummy_coefs = modelo.beta[:, idx_start : idx_start+3]
|
|
52
|
+
dummy_vals = modelo.Dhorizon[i, :].reshape(-1, 1)
|
|
53
|
+
FYs[0:modelo.n_vars, :] += dummy_coefs @ dummy_vals
|
|
54
|
+
|
|
55
|
+
elif modelo.DUM == 'M':
|
|
56
|
+
dummy_coefs = modelo.beta[:, idx_start : idx_start+11]
|
|
57
|
+
dummy_vals = modelo.Dhorizon[i, :].reshape(-1, 1)
|
|
58
|
+
FYs[0:modelo.n_vars, :] += dummy_coefs @ dummy_vals
|
|
59
|
+
|
|
60
|
+
# D. Store Forecast (THE FIX)
|
|
61
|
+
# We take the first 'n_vars' rows from the 'FYs' column vector
|
|
62
|
+
# and flatten them to fit into the Yfc row.
|
|
63
|
+
Yfc[modelo.past + i, :] = FYs[0:modelo.n_vars, 0]
|
|
64
|
+
|
|
65
|
+
# E. Update state for next iteration
|
|
66
|
+
start = FYs
|
|
67
|
+
|
|
68
|
+
return Yfc
|