aigroup-econ-mcp 0.1.6__py3-none-any.whl → 0.2.1__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.
Potentially problematic release.
This version of aigroup-econ-mcp might be problematic. Click here for more details.
- aigroup_econ_mcp/config.py +561 -0
- aigroup_econ_mcp/server.py +2506 -18
- aigroup_econ_mcp/tools/__init__.py +17 -7
- aigroup_econ_mcp/tools/base.py +271 -0
- aigroup_econ_mcp/tools/cache.py +533 -0
- aigroup_econ_mcp/tools/machine_learning.py +673 -0
- aigroup_econ_mcp/tools/monitoring.py +555 -0
- aigroup_econ_mcp/tools/optimized_example.py +229 -0
- aigroup_econ_mcp/tools/panel_data.py +527 -0
- aigroup_econ_mcp/tools/time_series.py +838 -260
- aigroup_econ_mcp/tools/validation.py +482 -0
- {aigroup_econ_mcp-0.1.6.dist-info → aigroup_econ_mcp-0.2.1.dist-info}/METADATA +5 -1
- aigroup_econ_mcp-0.2.1.dist-info/RECORD +20 -0
- aigroup_econ_mcp-0.1.6.dist-info/RECORD +0 -12
- {aigroup_econ_mcp-0.1.6.dist-info → aigroup_econ_mcp-0.2.1.dist-info}/WHEEL +0 -0
- {aigroup_econ_mcp-0.1.6.dist-info → aigroup_econ_mcp-0.2.1.dist-info}/entry_points.txt +0 -0
- {aigroup_econ_mcp-0.1.6.dist-info → aigroup_econ_mcp-0.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,260 +1,838 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from statsmodels.tsa.
|
|
12
|
-
from statsmodels.tsa.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
alpha: float
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
series =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
时间序列分析工具
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from typing import List, Dict, Any, Optional, Tuple
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
import statsmodels.api as sm
|
|
11
|
+
from statsmodels.tsa.stattools import adfuller, kpss, acf, pacf
|
|
12
|
+
from statsmodels.tsa.arima.model import ARIMA
|
|
13
|
+
from statsmodels.tsa.statespace.sarimax import SARIMAX
|
|
14
|
+
from statsmodels.tsa.vector_ar.var_model import VAR
|
|
15
|
+
from statsmodels.tsa.vector_ar.vecm import VECM
|
|
16
|
+
from statsmodels.tsa.statespace.varmax import VARMAX
|
|
17
|
+
from statsmodels.tsa.api import VAR as VAR2
|
|
18
|
+
from statsmodels.tsa.statespace.kalman_filter import KalmanFilter
|
|
19
|
+
from statsmodels.tsa.statespace.tools import (
|
|
20
|
+
constrain_stationary_univariate,
|
|
21
|
+
unconstrain_stationary_univariate
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StationarityTest(BaseModel):
|
|
26
|
+
"""平稳性检验结果"""
|
|
27
|
+
adf_statistic: float
|
|
28
|
+
adf_pvalue: float
|
|
29
|
+
adf_critical_values: Dict[str, float]
|
|
30
|
+
kpss_statistic: float
|
|
31
|
+
kpss_pvalue: float
|
|
32
|
+
is_stationary: bool
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ACFPACFResult(BaseModel):
|
|
36
|
+
"""自相关分析结果"""
|
|
37
|
+
acf_values: List[float]
|
|
38
|
+
pacf_values: List[float]
|
|
39
|
+
acf_confidence: List[Tuple[float, float]]
|
|
40
|
+
pacf_confidence: List[Tuple[float, float]]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ARIMAResult(BaseModel):
|
|
44
|
+
"""ARIMA模型结果"""
|
|
45
|
+
order: Tuple[int, int, int]
|
|
46
|
+
aic: float
|
|
47
|
+
bic: float
|
|
48
|
+
coefficients: Dict[str, float]
|
|
49
|
+
fitted_values: List[float]
|
|
50
|
+
residuals: List[float]
|
|
51
|
+
forecast: Optional[List[float]] = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class VARModelResult(BaseModel):
|
|
55
|
+
"""VAR模型结果"""
|
|
56
|
+
order: int
|
|
57
|
+
aic: float
|
|
58
|
+
bic: float
|
|
59
|
+
hqic: float
|
|
60
|
+
coefficients: Dict[str, Dict[str, float]]
|
|
61
|
+
fitted_values: Dict[str, List[float]]
|
|
62
|
+
residuals: Dict[str, List[float]]
|
|
63
|
+
forecast: Optional[Dict[str, List[float]]] = None
|
|
64
|
+
granger_causality: Dict[str, Dict[str, float]]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class VECMModelResult(BaseModel):
|
|
68
|
+
"""VECM模型结果"""
|
|
69
|
+
coint_rank: int
|
|
70
|
+
aic: float
|
|
71
|
+
bic: float
|
|
72
|
+
hqic: float
|
|
73
|
+
alpha: Dict[str, List[float]]
|
|
74
|
+
beta: List[List[float]]
|
|
75
|
+
gamma: Dict[str, Dict[str, float]]
|
|
76
|
+
cointegration_relations: List[List[float]]
|
|
77
|
+
adjustment_speed: Dict[str, float]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class GARCHModelResult(BaseModel):
|
|
81
|
+
"""GARCH模型结果"""
|
|
82
|
+
order: Tuple[int, int]
|
|
83
|
+
aic: float
|
|
84
|
+
bic: float
|
|
85
|
+
coefficients: Dict[str, float]
|
|
86
|
+
conditional_volatility: List[float]
|
|
87
|
+
standardized_residuals: List[float]
|
|
88
|
+
persistence: float
|
|
89
|
+
unconditional_variance: float
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class StateSpaceModelResult(BaseModel):
|
|
93
|
+
"""状态空间模型结果"""
|
|
94
|
+
state_names: List[str]
|
|
95
|
+
observation_names: List[str]
|
|
96
|
+
log_likelihood: float
|
|
97
|
+
aic: float
|
|
98
|
+
bic: float
|
|
99
|
+
filtered_state: Dict[str, List[float]]
|
|
100
|
+
smoothed_state: Dict[str, List[float]]
|
|
101
|
+
forecast: Optional[Dict[str, List[float]]] = None
|
|
102
|
+
kalman_gain: Optional[List[List[float]]] = None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def check_stationarity(data: List[float], max_lags: int = None) -> StationarityTest:
|
|
106
|
+
"""平稳性检验(ADF和KPSS)"""
|
|
107
|
+
series = pd.Series(data)
|
|
108
|
+
|
|
109
|
+
# ADF检验
|
|
110
|
+
adf_result = adfuller(series, maxlag=max_lags, autolag='AIC')
|
|
111
|
+
adf_stat, adf_pvalue = adf_result[0], adf_result[1]
|
|
112
|
+
adf_critical = adf_result[4]
|
|
113
|
+
|
|
114
|
+
# KPSS检验
|
|
115
|
+
kpss_result = kpss(series, regression='c', nlags='auto')
|
|
116
|
+
kpss_stat, kpss_pvalue = kpss_result[0], kpss_result[1]
|
|
117
|
+
|
|
118
|
+
# 综合判断平稳性
|
|
119
|
+
is_stationary = (adf_pvalue < 0.05) and (kpss_pvalue > 0.05)
|
|
120
|
+
|
|
121
|
+
return StationarityTest(
|
|
122
|
+
adf_statistic=adf_stat,
|
|
123
|
+
adf_pvalue=adf_pvalue,
|
|
124
|
+
adf_critical_values=adf_critical,
|
|
125
|
+
kpss_statistic=kpss_stat,
|
|
126
|
+
kpss_pvalue=kpss_pvalue,
|
|
127
|
+
is_stationary=is_stationary
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def calculate_acf_pacf(
|
|
132
|
+
data: List[float],
|
|
133
|
+
nlags: int = 20,
|
|
134
|
+
alpha: float = 0.05
|
|
135
|
+
) -> ACFPACFResult:
|
|
136
|
+
"""计算自相关和偏自相关函数"""
|
|
137
|
+
series = pd.Series(data)
|
|
138
|
+
|
|
139
|
+
# 计算ACF和PACF
|
|
140
|
+
acf_values = acf(series, nlags=nlags, alpha=alpha)
|
|
141
|
+
pacf_values = pacf(series, nlags=nlags, alpha=alpha)
|
|
142
|
+
|
|
143
|
+
# 构建置信区间
|
|
144
|
+
acf_conf = []
|
|
145
|
+
pacf_conf = []
|
|
146
|
+
|
|
147
|
+
for i in range(len(acf_values[1])):
|
|
148
|
+
acf_conf.append((acf_values[1][i][0], acf_values[1][i][1]))
|
|
149
|
+
pacf_conf.append((pacf_values[1][i][0], pacf_values[1][i][1]))
|
|
150
|
+
|
|
151
|
+
return ACFPACFResult(
|
|
152
|
+
acf_values=acf_values[0].tolist(),
|
|
153
|
+
pacf_values=pacf_values[0].tolist(),
|
|
154
|
+
acf_confidence=acf_conf,
|
|
155
|
+
pacf_confidence=pacf_conf
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def fit_arima_model(
|
|
160
|
+
data: List[float],
|
|
161
|
+
order: Tuple[int, int, int] = (1, 1, 1),
|
|
162
|
+
seasonal_order: Tuple[int, int, int, int] = (0, 0, 0, 0)
|
|
163
|
+
) -> ARIMAResult:
|
|
164
|
+
"""拟合ARIMA模型"""
|
|
165
|
+
series = pd.Series(data)
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
if seasonal_order != (0, 0, 0, 0):
|
|
169
|
+
# 季节性ARIMA
|
|
170
|
+
model = SARIMAX(series, order=order, seasonal_order=seasonal_order)
|
|
171
|
+
else:
|
|
172
|
+
# 普通ARIMA
|
|
173
|
+
model = ARIMA(series, order=order)
|
|
174
|
+
|
|
175
|
+
fitted_model = model.fit()
|
|
176
|
+
|
|
177
|
+
return ARIMAResult(
|
|
178
|
+
order=order,
|
|
179
|
+
aic=fitted_model.aic,
|
|
180
|
+
bic=fitted_model.bic,
|
|
181
|
+
coefficients=fitted_model.params.to_dict(),
|
|
182
|
+
fitted_values=fitted_model.fittedvalues.tolist(),
|
|
183
|
+
residuals=fitted_model.resid.tolist()
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
raise ValueError(f"ARIMA模型拟合失败: {str(e)}")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def find_best_arima_order(
|
|
191
|
+
data: List[float],
|
|
192
|
+
max_p: int = 3,
|
|
193
|
+
max_d: int = 2,
|
|
194
|
+
max_q: int = 3,
|
|
195
|
+
seasonal: bool = False,
|
|
196
|
+
max_P: int = 1,
|
|
197
|
+
max_D: int = 1,
|
|
198
|
+
max_Q: int = 1,
|
|
199
|
+
m: int = 12
|
|
200
|
+
) -> Dict[str, Any]:
|
|
201
|
+
"""自动寻找最佳ARIMA模型阶数"""
|
|
202
|
+
series = pd.Series(data)
|
|
203
|
+
best_aic = float('inf')
|
|
204
|
+
best_order = (0, 0, 0)
|
|
205
|
+
best_seasonal_order = (0, 0, 0, 0)
|
|
206
|
+
best_model = None
|
|
207
|
+
|
|
208
|
+
# 非季节性ARIMA
|
|
209
|
+
if not seasonal:
|
|
210
|
+
for p in range(max_p + 1):
|
|
211
|
+
for d in range(max_d + 1):
|
|
212
|
+
for q in range(max_q + 1):
|
|
213
|
+
try:
|
|
214
|
+
model = ARIMA(series, order=(p, d, q))
|
|
215
|
+
fitted_model = model.fit()
|
|
216
|
+
if fitted_model.aic < best_aic:
|
|
217
|
+
best_aic = fitted_model.aic
|
|
218
|
+
best_order = (p, d, q)
|
|
219
|
+
best_model = fitted_model
|
|
220
|
+
except:
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
# 季节性ARIMA
|
|
224
|
+
else:
|
|
225
|
+
for p in range(max_p + 1):
|
|
226
|
+
for d in range(max_d + 1):
|
|
227
|
+
for q in range(max_q + 1):
|
|
228
|
+
for P in range(max_P + 1):
|
|
229
|
+
for D in range(max_D + 1):
|
|
230
|
+
for Q in range(max_Q + 1):
|
|
231
|
+
try:
|
|
232
|
+
seasonal_order = (P, D, Q, m)
|
|
233
|
+
model = SARIMAX(series, order=(p, d, q), seasonal_order=seasonal_order)
|
|
234
|
+
fitted_model = model.fit()
|
|
235
|
+
if fitted_model.aic < best_aic:
|
|
236
|
+
best_aic = fitted_model.aic
|
|
237
|
+
best_order = (p, d, q)
|
|
238
|
+
best_seasonal_order = seasonal_order
|
|
239
|
+
best_model = fitted_model
|
|
240
|
+
except:
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
if best_model is None:
|
|
244
|
+
raise ValueError("无法找到合适的ARIMA模型")
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
"best_order": best_order,
|
|
248
|
+
"best_seasonal_order": best_seasonal_order if seasonal else None,
|
|
249
|
+
"best_aic": best_aic,
|
|
250
|
+
"best_bic": best_model.bic,
|
|
251
|
+
"coefficients": best_model.params.to_dict(),
|
|
252
|
+
"model_summary": str(best_model.summary())
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def decompose_time_series(
|
|
257
|
+
data: List[float],
|
|
258
|
+
model: str = "additive",
|
|
259
|
+
period: Optional[int] = None
|
|
260
|
+
) -> Dict[str, List[float]]:
|
|
261
|
+
"""时间序列分解"""
|
|
262
|
+
series = pd.Series(data)
|
|
263
|
+
|
|
264
|
+
if period is None:
|
|
265
|
+
# 自动检测周期(简单方法)
|
|
266
|
+
from statsmodels.tsa.seasonal import seasonal_decompose
|
|
267
|
+
decomposition = seasonal_decompose(series, model=model, extrapolate_trend='freq')
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
"trend": decomposition.trend.fillna(0).tolist(),
|
|
271
|
+
"seasonal": decomposition.seasonal.fillna(0).tolist(),
|
|
272
|
+
"residual": decomposition.resid.fillna(0).tolist(),
|
|
273
|
+
"observed": decomposition.observed.tolist()
|
|
274
|
+
}
|
|
275
|
+
else:
|
|
276
|
+
# 指定周期的分解
|
|
277
|
+
decomposition = seasonal_decompose(series, model=model, period=period)
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
"trend": decomposition.trend.fillna(0).tolist(),
|
|
281
|
+
"seasonal": decomposition.seasonal.fillna(0).tolist(),
|
|
282
|
+
"residual": decomposition.resid.fillna(0).tolist(),
|
|
283
|
+
"observed": decomposition.observed.tolist()
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def forecast_arima(
|
|
288
|
+
data: List[float],
|
|
289
|
+
order: Tuple[int, int, int] = (1, 1, 1),
|
|
290
|
+
steps: int = 10,
|
|
291
|
+
seasonal_order: Tuple[int, int, int, int] = (0, 0, 0, 0)
|
|
292
|
+
) -> Dict[str, Any]:
|
|
293
|
+
"""ARIMA模型预测"""
|
|
294
|
+
series = pd.Series(data)
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
if seasonal_order != (0, 0, 0, 0):
|
|
298
|
+
model = SARIMAX(series, order=order, seasonal_order=seasonal_order)
|
|
299
|
+
else:
|
|
300
|
+
model = ARIMA(series, order=order)
|
|
301
|
+
|
|
302
|
+
fitted_model = model.fit()
|
|
303
|
+
|
|
304
|
+
# 生成预测
|
|
305
|
+
forecast_result = fitted_model.forecast(steps=steps)
|
|
306
|
+
forecast_values = forecast_result.tolist()
|
|
307
|
+
|
|
308
|
+
# 预测置信区间
|
|
309
|
+
pred_conf = fitted_model.get_forecast(steps=steps)
|
|
310
|
+
conf_int = pred_conf.conf_int()
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
"forecast": forecast_values,
|
|
314
|
+
"conf_int_lower": conf_int.iloc[:, 0].tolist(),
|
|
315
|
+
"conf_int_upper": conf_int.iloc[:, 1].tolist(),
|
|
316
|
+
"model_aic": fitted_model.aic,
|
|
317
|
+
"model_bic": fitted_model.bic
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
except Exception as e:
|
|
321
|
+
raise ValueError(f"ARIMA预测失败: {str(e)}")
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def var_model(
|
|
325
|
+
data: Dict[str, List[float]],
|
|
326
|
+
max_lags: int = 5,
|
|
327
|
+
ic: str = 'aic'
|
|
328
|
+
) -> VARModelResult:
|
|
329
|
+
"""
|
|
330
|
+
VAR模型 - 向量自回归模型
|
|
331
|
+
|
|
332
|
+
📊 功能说明:
|
|
333
|
+
向量自回归模型用于分析多个时间序列变量之间的动态关系。
|
|
334
|
+
每个变量的当前值都依赖于所有变量的滞后值。
|
|
335
|
+
|
|
336
|
+
📈 模型形式:
|
|
337
|
+
Y_t = A_1 Y_{t-1} + A_2 Y_{t-2} + ... + A_p Y_{t-p} + ε_t
|
|
338
|
+
|
|
339
|
+
💡 使用场景:
|
|
340
|
+
- 宏观经济变量间的相互影响分析
|
|
341
|
+
- 金融市场联动性研究
|
|
342
|
+
- 脉冲响应函数和方差分解
|
|
343
|
+
- 格兰杰因果关系检验
|
|
344
|
+
|
|
345
|
+
⚠️ 注意事项:
|
|
346
|
+
- 所有变量都应该是平稳的
|
|
347
|
+
- 滞后阶数选择很重要
|
|
348
|
+
- 变量数量不宜过多(避免维度灾难)
|
|
349
|
+
- 样本量应足够大
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
data: 多变量时间序列数据字典
|
|
353
|
+
max_lags: 最大滞后阶数
|
|
354
|
+
ic: 信息准则 ('aic', 'bic', 'hqic')
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
VARModelResult: VAR模型结果
|
|
358
|
+
"""
|
|
359
|
+
try:
|
|
360
|
+
# 数据验证
|
|
361
|
+
if not data:
|
|
362
|
+
raise ValueError("数据不能为空")
|
|
363
|
+
|
|
364
|
+
if len(data) < 2:
|
|
365
|
+
raise ValueError("VAR模型至少需要2个变量")
|
|
366
|
+
|
|
367
|
+
# 转换为DataFrame
|
|
368
|
+
df = pd.DataFrame(data)
|
|
369
|
+
|
|
370
|
+
# 检查数据长度
|
|
371
|
+
if len(df) < max_lags + 10:
|
|
372
|
+
raise ValueError(f"数据长度({len(df)})不足,至少需要{max_lags + 10}个观测点")
|
|
373
|
+
|
|
374
|
+
# 拟合VAR模型
|
|
375
|
+
model = VAR(df)
|
|
376
|
+
|
|
377
|
+
# 选择最优滞后阶数
|
|
378
|
+
lag_order = model.select_order(maxlags=max_lags)
|
|
379
|
+
best_lag = getattr(lag_order, ic)
|
|
380
|
+
|
|
381
|
+
# 使用最优滞后阶数拟合模型
|
|
382
|
+
fitted_model = model.fit(best_lag)
|
|
383
|
+
|
|
384
|
+
# 提取系数
|
|
385
|
+
coefficients = {}
|
|
386
|
+
for i, col in enumerate(df.columns):
|
|
387
|
+
coefficients[col] = {}
|
|
388
|
+
# 提取常数项
|
|
389
|
+
if hasattr(fitted_model, 'intercept'):
|
|
390
|
+
coefficients[col]['const'] = float(fitted_model.intercept[i]) if i < len(fitted_model.intercept) else 0.0
|
|
391
|
+
# 提取滞后项系数
|
|
392
|
+
for lag in range(1, best_lag + 1):
|
|
393
|
+
for j, lag_col in enumerate(df.columns):
|
|
394
|
+
coef_name = f"{lag_col}.L{lag}"
|
|
395
|
+
if hasattr(fitted_model, 'coefs'):
|
|
396
|
+
coefficients[col][coef_name] = float(fitted_model.coefs[lag-1][i, j]) if fitted_model.coefs.shape[0] >= lag else 0.0
|
|
397
|
+
else:
|
|
398
|
+
coefficients[col][coef_name] = 0.0
|
|
399
|
+
|
|
400
|
+
# 拟合值和残差
|
|
401
|
+
fitted_values = {}
|
|
402
|
+
residuals = {}
|
|
403
|
+
for i, col in enumerate(df.columns):
|
|
404
|
+
fitted_values[col] = fitted_model.fittedvalues[col].tolist() if col in fitted_model.fittedvalues else []
|
|
405
|
+
residuals[col] = fitted_model.resid[col].tolist() if col in fitted_model.resid else []
|
|
406
|
+
|
|
407
|
+
# 格兰杰因果关系检验
|
|
408
|
+
granger_causality = {}
|
|
409
|
+
for cause in df.columns:
|
|
410
|
+
granger_causality[cause] = {}
|
|
411
|
+
for effect in df.columns:
|
|
412
|
+
if cause != effect:
|
|
413
|
+
try:
|
|
414
|
+
test_result = fitted_model.test_causality(effect, cause, kind='f')
|
|
415
|
+
granger_causality[cause][effect] = test_result.pvalue
|
|
416
|
+
except:
|
|
417
|
+
granger_causality[cause][effect] = 1.0
|
|
418
|
+
|
|
419
|
+
return VARModelResult(
|
|
420
|
+
order=best_lag,
|
|
421
|
+
aic=fitted_model.aic,
|
|
422
|
+
bic=fitted_model.bic,
|
|
423
|
+
hqic=fitted_model.hqic,
|
|
424
|
+
coefficients=coefficients,
|
|
425
|
+
fitted_values=fitted_values,
|
|
426
|
+
residuals=residuals,
|
|
427
|
+
granger_causality=granger_causality
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
except Exception as e:
|
|
431
|
+
raise ValueError(f"VAR模型拟合失败: {str(e)}")
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def vecm_model(
|
|
435
|
+
data: Dict[str, List[float]],
|
|
436
|
+
coint_rank: int = 1,
|
|
437
|
+
deterministic: str = 'co',
|
|
438
|
+
max_lags: int = 5
|
|
439
|
+
) -> VECMModelResult:
|
|
440
|
+
"""
|
|
441
|
+
VECM模型 - 向量误差修正模型
|
|
442
|
+
|
|
443
|
+
📊 功能说明:
|
|
444
|
+
用于分析非平稳时间序列之间的长期均衡关系和短期动态调整。
|
|
445
|
+
适用于存在协整关系的多变量系统。
|
|
446
|
+
|
|
447
|
+
📈 模型形式:
|
|
448
|
+
ΔY_t = αβ' Y_{t-1} + Γ_1 ΔY_{t-1} + ... + Γ_{p-1} ΔY_{t-p+1} + ε_t
|
|
449
|
+
|
|
450
|
+
💡 使用场景:
|
|
451
|
+
- 存在长期均衡关系的经济变量分析
|
|
452
|
+
- 误差修正机制研究
|
|
453
|
+
- 协整关系检验
|
|
454
|
+
- 短期动态调整分析
|
|
455
|
+
|
|
456
|
+
⚠️ 注意事项:
|
|
457
|
+
- 所有变量应该是一阶单整的I(1)
|
|
458
|
+
- 协整秩的选择很重要
|
|
459
|
+
- 需要较大的样本量
|
|
460
|
+
- 对模型设定敏感
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
data: 多变量时间序列数据字典
|
|
464
|
+
coint_rank: 协整秩
|
|
465
|
+
deterministic: 确定性项 ('co', 'ci', 'lo', 'li')
|
|
466
|
+
max_lags: 最大滞后阶数
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
VECMModelResult: VECM模型结果
|
|
470
|
+
"""
|
|
471
|
+
try:
|
|
472
|
+
# 数据验证
|
|
473
|
+
if not data:
|
|
474
|
+
raise ValueError("数据不能为空")
|
|
475
|
+
|
|
476
|
+
if len(data) < 2:
|
|
477
|
+
raise ValueError("VECM模型至少需要2个变量")
|
|
478
|
+
|
|
479
|
+
# 转换为DataFrame
|
|
480
|
+
df = pd.DataFrame(data)
|
|
481
|
+
|
|
482
|
+
# 检查数据长度
|
|
483
|
+
if len(df) < max_lags + 10:
|
|
484
|
+
raise ValueError(f"数据长度({len(df)})不足,至少需要{max_lags + 10}个观测点")
|
|
485
|
+
|
|
486
|
+
# 拟合VECM模型
|
|
487
|
+
model = VECM(df, k_ar_diff=max_lags, coint_rank=coint_rank, deterministic=deterministic)
|
|
488
|
+
fitted_model = model.fit()
|
|
489
|
+
|
|
490
|
+
# 提取系数
|
|
491
|
+
alpha = {}
|
|
492
|
+
beta = fitted_model.beta.tolist() if hasattr(fitted_model, 'beta') else []
|
|
493
|
+
gamma = {}
|
|
494
|
+
|
|
495
|
+
# 提取调整系数alpha
|
|
496
|
+
if hasattr(fitted_model, 'alpha'):
|
|
497
|
+
for i, col in enumerate(df.columns):
|
|
498
|
+
alpha[col] = fitted_model.alpha[i].tolist() if i < len(fitted_model.alpha) else []
|
|
499
|
+
|
|
500
|
+
# 提取短期系数gamma
|
|
501
|
+
if hasattr(fitted_model, 'gamma'):
|
|
502
|
+
for i, col in enumerate(df.columns):
|
|
503
|
+
gamma[col] = {}
|
|
504
|
+
for j, lag_col in enumerate(df.columns):
|
|
505
|
+
if j < len(fitted_model.gamma[i]):
|
|
506
|
+
gamma[col][lag_col] = float(fitted_model.gamma[i][j])
|
|
507
|
+
|
|
508
|
+
# 计算协整关系
|
|
509
|
+
cointegration_relations = []
|
|
510
|
+
if hasattr(fitted_model, 'beta') and fitted_model.beta is not None:
|
|
511
|
+
for i in range(min(coint_rank, len(fitted_model.beta))):
|
|
512
|
+
cointegration_relations.append(fitted_model.beta[i].tolist())
|
|
513
|
+
|
|
514
|
+
# 计算调整速度
|
|
515
|
+
adjustment_speed = {}
|
|
516
|
+
if hasattr(fitted_model, 'alpha') and fitted_model.alpha is not None:
|
|
517
|
+
for i, col in enumerate(df.columns):
|
|
518
|
+
if i < len(fitted_model.alpha):
|
|
519
|
+
adjustment_speed[col] = float(np.mean(np.abs(fitted_model.alpha[i])))
|
|
520
|
+
|
|
521
|
+
return VECMModelResult(
|
|
522
|
+
coint_rank=coint_rank,
|
|
523
|
+
aic=fitted_model.aic if hasattr(fitted_model, 'aic') else 0.0,
|
|
524
|
+
bic=fitted_model.bic if hasattr(fitted_model, 'bic') else 0.0,
|
|
525
|
+
hqic=fitted_model.hqic if hasattr(fitted_model, 'hqic') else 0.0,
|
|
526
|
+
alpha=alpha,
|
|
527
|
+
beta=beta,
|
|
528
|
+
gamma=gamma,
|
|
529
|
+
cointegration_relations=cointegration_relations,
|
|
530
|
+
adjustment_speed=adjustment_speed
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
except Exception as e:
|
|
534
|
+
raise ValueError(f"VECM模型拟合失败: {str(e)}")
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def garch_model(
|
|
538
|
+
data: List[float],
|
|
539
|
+
order: Tuple[int, int] = (1, 1),
|
|
540
|
+
dist: str = 'normal'
|
|
541
|
+
) -> GARCHModelResult:
|
|
542
|
+
"""
|
|
543
|
+
GARCH模型 - 广义自回归条件异方差模型
|
|
544
|
+
|
|
545
|
+
📊 功能说明:
|
|
546
|
+
用于建模金融时间序列的波动率聚类现象,捕捉条件方差的时变特征。
|
|
547
|
+
|
|
548
|
+
📈 模型形式:
|
|
549
|
+
r_t = μ + ε_t, ε_t = σ_t z_t
|
|
550
|
+
σ_t² = ω + α ε_{t-1}² + β σ_{t-1}²
|
|
551
|
+
|
|
552
|
+
💡 使用场景:
|
|
553
|
+
- 金融资产波动率建模
|
|
554
|
+
- 风险管理和VaR计算
|
|
555
|
+
- 期权定价
|
|
556
|
+
- 波动率预测
|
|
557
|
+
|
|
558
|
+
⚠️ 注意事项:
|
|
559
|
+
- 数据应具有波动率聚类特征
|
|
560
|
+
- 需要较大的样本量
|
|
561
|
+
- 对分布假设敏感
|
|
562
|
+
- 高阶GARCH可能不稳定
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
data: 时间序列数据(通常是收益率)
|
|
566
|
+
order: GARCH阶数 (p, q)
|
|
567
|
+
dist: 误差分布 ('normal', 't', 'skewt')
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
GARCHModelResult: GARCH模型结果
|
|
571
|
+
"""
|
|
572
|
+
try:
|
|
573
|
+
# 数据验证
|
|
574
|
+
if not data:
|
|
575
|
+
raise ValueError("数据不能为空")
|
|
576
|
+
|
|
577
|
+
if len(data) < 50:
|
|
578
|
+
raise ValueError("GARCH模型至少需要50个观测点")
|
|
579
|
+
|
|
580
|
+
# 转换为收益率序列(如果数据不是收益率)
|
|
581
|
+
series = pd.Series(data)
|
|
582
|
+
|
|
583
|
+
# 使用arch包进行GARCH建模
|
|
584
|
+
try:
|
|
585
|
+
from arch import arch_model
|
|
586
|
+
except ImportError:
|
|
587
|
+
raise ImportError("请安装arch包: pip install arch")
|
|
588
|
+
|
|
589
|
+
# 拟合GARCH模型
|
|
590
|
+
model = arch_model(series, vol='Garch', p=order[0], q=order[1], dist=dist)
|
|
591
|
+
fitted_model = model.fit(disp='off')
|
|
592
|
+
|
|
593
|
+
# 提取系数
|
|
594
|
+
coefficients = {}
|
|
595
|
+
for param, value in fitted_model.params.items():
|
|
596
|
+
coefficients[param] = float(value)
|
|
597
|
+
|
|
598
|
+
# 计算条件波动率
|
|
599
|
+
conditional_volatility = fitted_model.conditional_volatility.tolist()
|
|
600
|
+
|
|
601
|
+
# 标准化残差
|
|
602
|
+
standardized_residuals = fitted_model.resid / fitted_model.conditional_volatility
|
|
603
|
+
standardized_residuals = standardized_residuals.tolist()
|
|
604
|
+
|
|
605
|
+
# 计算持久性
|
|
606
|
+
alpha_sum = sum([fitted_model.params.get(f'alpha[{i}]', 0) for i in range(1, order[0]+1)])
|
|
607
|
+
beta_sum = sum([fitted_model.params.get(f'beta[{i}]', 0) for i in range(1, order[1]+1)])
|
|
608
|
+
persistence = alpha_sum + beta_sum
|
|
609
|
+
|
|
610
|
+
# 无条件方差
|
|
611
|
+
omega = fitted_model.params.get('omega', 0)
|
|
612
|
+
unconditional_variance = omega / (1 - persistence) if persistence < 1 else float('inf')
|
|
613
|
+
|
|
614
|
+
return GARCHModelResult(
|
|
615
|
+
order=order,
|
|
616
|
+
aic=fitted_model.aic,
|
|
617
|
+
bic=fitted_model.bic,
|
|
618
|
+
coefficients=coefficients,
|
|
619
|
+
conditional_volatility=conditional_volatility,
|
|
620
|
+
standardized_residuals=standardized_residuals,
|
|
621
|
+
persistence=persistence,
|
|
622
|
+
unconditional_variance=unconditional_variance
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
except Exception as e:
|
|
626
|
+
raise ValueError(f"GARCH模型拟合失败: {str(e)}")
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def state_space_model(
|
|
630
|
+
data: List[float],
|
|
631
|
+
state_dim: int = 1,
|
|
632
|
+
observation_dim: int = 1,
|
|
633
|
+
trend: bool = True,
|
|
634
|
+
seasonal: bool = False,
|
|
635
|
+
period: int = 12
|
|
636
|
+
) -> StateSpaceModelResult:
|
|
637
|
+
"""
|
|
638
|
+
状态空间模型 - 卡尔曼滤波
|
|
639
|
+
|
|
640
|
+
📊 功能说明:
|
|
641
|
+
使用状态空间表示和卡尔曼滤波进行时间序列建模,可以处理不可观测的状态变量。
|
|
642
|
+
|
|
643
|
+
📈 模型形式:
|
|
644
|
+
状态方程: α_t = T α_{t-1} + R η_t
|
|
645
|
+
观测方程: y_t = Z α_t + ε_t
|
|
646
|
+
|
|
647
|
+
💡 使用场景:
|
|
648
|
+
- 不可观测状态变量的估计
|
|
649
|
+
- 结构时间序列建模
|
|
650
|
+
- 实时滤波和平滑
|
|
651
|
+
- 缺失数据处理
|
|
652
|
+
|
|
653
|
+
⚠️ 注意事项:
|
|
654
|
+
- 模型设定复杂
|
|
655
|
+
- 需要先验知识
|
|
656
|
+
- 计算量较大
|
|
657
|
+
- 对初始值敏感
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
data: 时间序列数据
|
|
661
|
+
state_dim: 状态维度
|
|
662
|
+
observation_dim: 观测维度
|
|
663
|
+
trend: 是否包含趋势项
|
|
664
|
+
seasonal: 是否包含季节项
|
|
665
|
+
period: 季节周期
|
|
666
|
+
|
|
667
|
+
Returns:
|
|
668
|
+
StateSpaceModelResult: 状态空间模型结果
|
|
669
|
+
"""
|
|
670
|
+
try:
|
|
671
|
+
# 数据验证
|
|
672
|
+
if not data:
|
|
673
|
+
raise ValueError("数据不能为空")
|
|
674
|
+
|
|
675
|
+
if len(data) < 20:
|
|
676
|
+
raise ValueError("状态空间模型至少需要20个观测点")
|
|
677
|
+
|
|
678
|
+
series = pd.Series(data)
|
|
679
|
+
|
|
680
|
+
# 构建状态空间模型
|
|
681
|
+
from statsmodels.tsa.statespace.structural import UnobservedComponents
|
|
682
|
+
|
|
683
|
+
# 模型设定
|
|
684
|
+
if trend and seasonal:
|
|
685
|
+
model_spec = 'trend' if not seasonal else 'trend seasonal'
|
|
686
|
+
seasonal_period = period
|
|
687
|
+
elif trend:
|
|
688
|
+
model_spec = 'trend'
|
|
689
|
+
seasonal_period = None
|
|
690
|
+
elif seasonal:
|
|
691
|
+
model_spec = 'seasonal'
|
|
692
|
+
seasonal_period = period
|
|
693
|
+
else:
|
|
694
|
+
model_spec = 'irregular'
|
|
695
|
+
seasonal_period = None
|
|
696
|
+
|
|
697
|
+
# 拟合模型
|
|
698
|
+
model = UnobservedComponents(series, level=trend, seasonal=seasonal_period)
|
|
699
|
+
fitted_model = model.fit(disp=False)
|
|
700
|
+
|
|
701
|
+
# 状态名称
|
|
702
|
+
state_names = []
|
|
703
|
+
if trend:
|
|
704
|
+
state_names.append('level')
|
|
705
|
+
if seasonal:
|
|
706
|
+
for i in range(period-1):
|
|
707
|
+
state_names.append(f'seasonal_{i+1}')
|
|
708
|
+
|
|
709
|
+
# 观测名称
|
|
710
|
+
observation_names = ['observed']
|
|
711
|
+
|
|
712
|
+
# 滤波状态
|
|
713
|
+
filtered_state = {}
|
|
714
|
+
for i, name in enumerate(state_names):
|
|
715
|
+
if i < fitted_model.filtered_state.shape[0]:
|
|
716
|
+
filtered_state[name] = fitted_model.filtered_state[i].tolist()
|
|
717
|
+
|
|
718
|
+
# 平滑状态
|
|
719
|
+
smoothed_state = {}
|
|
720
|
+
for i, name in enumerate(state_names):
|
|
721
|
+
if i < fitted_model.smoothed_state.shape[0]:
|
|
722
|
+
smoothed_state[name] = fitted_model.smoothed_state[i].tolist()
|
|
723
|
+
|
|
724
|
+
return StateSpaceModelResult(
|
|
725
|
+
state_names=state_names,
|
|
726
|
+
observation_names=observation_names,
|
|
727
|
+
log_likelihood=fitted_model.llf,
|
|
728
|
+
aic=fitted_model.aic,
|
|
729
|
+
bic=fitted_model.bic,
|
|
730
|
+
filtered_state=filtered_state,
|
|
731
|
+
smoothed_state=smoothed_state
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
except Exception as e:
|
|
735
|
+
raise ValueError(f"状态空间模型拟合失败: {str(e)}")
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
def forecast_var(
|
|
739
|
+
data: Dict[str, List[float]],
|
|
740
|
+
steps: int = 10,
|
|
741
|
+
max_lags: int = 5
|
|
742
|
+
) -> Dict[str, Any]:
|
|
743
|
+
"""VAR模型预测"""
|
|
744
|
+
try:
|
|
745
|
+
# 使用VAR模型进行预测
|
|
746
|
+
var_result = var_model(data, max_lags=max_lags)
|
|
747
|
+
|
|
748
|
+
# 转换为DataFrame进行预测
|
|
749
|
+
df = pd.DataFrame(data)
|
|
750
|
+
model = VAR(df)
|
|
751
|
+
fitted_model = model.fit(var_result.order)
|
|
752
|
+
|
|
753
|
+
# 生成预测
|
|
754
|
+
forecast = fitted_model.forecast(df.values[-var_result.order:], steps=steps)
|
|
755
|
+
|
|
756
|
+
# 构建预测结果
|
|
757
|
+
forecast_dict = {}
|
|
758
|
+
for i, col in enumerate(df.columns):
|
|
759
|
+
forecast_dict[col] = forecast[:, i].tolist()
|
|
760
|
+
|
|
761
|
+
return {
|
|
762
|
+
"forecast": forecast_dict,
|
|
763
|
+
"model_order": var_result.order,
|
|
764
|
+
"model_aic": var_result.aic,
|
|
765
|
+
"model_bic": var_result.bic
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
except Exception as e:
|
|
769
|
+
raise ValueError(f"VAR预测失败: {str(e)}")
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def impulse_response_analysis(
|
|
773
|
+
data: Dict[str, List[float]],
|
|
774
|
+
periods: int = 10,
|
|
775
|
+
max_lags: int = 5
|
|
776
|
+
) -> Dict[str, Any]:
|
|
777
|
+
"""脉冲响应分析"""
|
|
778
|
+
try:
|
|
779
|
+
# 拟合VAR模型
|
|
780
|
+
var_result = var_model(data, max_lags=max_lags)
|
|
781
|
+
|
|
782
|
+
# 转换为DataFrame
|
|
783
|
+
df = pd.DataFrame(data)
|
|
784
|
+
model = VAR(df)
|
|
785
|
+
fitted_model = model.fit(var_result.order)
|
|
786
|
+
|
|
787
|
+
# 计算脉冲响应
|
|
788
|
+
irf = fitted_model.irf(periods=periods)
|
|
789
|
+
|
|
790
|
+
# 构建脉冲响应结果
|
|
791
|
+
impulse_responses = {}
|
|
792
|
+
for i, shock_var in enumerate(df.columns):
|
|
793
|
+
impulse_responses[shock_var] = {}
|
|
794
|
+
for j, response_var in enumerate(df.columns):
|
|
795
|
+
impulse_responses[shock_var][response_var] = irf.irfs[:, j, i].tolist()
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
"impulse_responses": impulse_responses,
|
|
799
|
+
"orthogonalized": irf.orth_irfs.tolist() if hasattr(irf, 'orth_irfs') else None,
|
|
800
|
+
"cumulative_effects": irf.cum_effects.tolist() if hasattr(irf, 'cum_effects') else None
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
except Exception as e:
|
|
804
|
+
raise ValueError(f"脉冲响应分析失败: {str(e)}")
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def variance_decomposition(
|
|
808
|
+
data: Dict[str, List[float]],
|
|
809
|
+
periods: int = 10,
|
|
810
|
+
max_lags: int = 5
|
|
811
|
+
) -> Dict[str, Any]:
|
|
812
|
+
"""方差分解"""
|
|
813
|
+
try:
|
|
814
|
+
# 拟合VAR模型
|
|
815
|
+
var_result = var_model(data, max_lags=max_lags)
|
|
816
|
+
|
|
817
|
+
# 转换为DataFrame
|
|
818
|
+
df = pd.DataFrame(data)
|
|
819
|
+
model = VAR(df)
|
|
820
|
+
fitted_model = model.fit(var_result.order)
|
|
821
|
+
|
|
822
|
+
# 计算方差分解
|
|
823
|
+
vd = fitted_model.fevd(periods=periods)
|
|
824
|
+
|
|
825
|
+
# 构建方差分解结果
|
|
826
|
+
variance_decomp = {}
|
|
827
|
+
for i, var_name in enumerate(df.columns):
|
|
828
|
+
variance_decomp[var_name] = {}
|
|
829
|
+
for j, shock_name in enumerate(df.columns):
|
|
830
|
+
variance_decomp[var_name][shock_name] = vd.decomposition[var_name][shock_name].tolist()
|
|
831
|
+
|
|
832
|
+
return {
|
|
833
|
+
"variance_decomposition": variance_decomp,
|
|
834
|
+
"horizon": periods
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
except Exception as e:
|
|
838
|
+
raise ValueError(f"方差分解失败: {str(e)}")
|