
复现 Hamilton(1989) 的时变马尔可夫区制转换是 statsmodels 的 MarkovAutoregression 最经典的练兵场而 TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 能让你把 Codex 顺畅地接进这个复现流程。很多人的第一次尝试都卡在同一个地方k_regimes2、order4、switch_arFalse 三个参数明明都写了滤波概率和平滑概率也能画出来expected durations 却总跟论文对不上。这篇文章我会带着 Codex 走一遍完整流程——先去官网创建 API Key再把 Codex 的 Base URL 指到 https://taotoken.net/api剩下的数据读取、模型拟合、概率绘制、持续时间核对都交给 Codex 按原文步骤重写脚本你在本地执行验证。原文里的 gndata、MarkovRegression、summary、smoothed_marginal_probabilities 这些关键词我们逐一落到可跑的代码上最后在终端看到请求成功、无 401再去官网看一眼这次调用记在用量里整个链路才算闭环。1. Hamilton 1989 复现为什么总卡在参数和概率上1.1 三个参数决定了模型形态原文用MarkovAutoregression复现 Hamilton(1989) 的开创性论文模型的数学形式并不复杂一个4阶自回归过程均值在两个区制之间切换转移概率矩阵固定不变。落到 statsmodels 里三个参数必须一起出现mod sm.tsa.MarkovAutoregression( hamilton, k_regimes2, order4, switch_arFalse, )k_regimes2是区制数量order4是自回归阶数switch_arFalse表示自回归系数不随区制切换。最后一个参数最容易漏因为默认模型会切换自回归系数一旦漏掉估计出的参数含义就和原论文对不上。新手经常在这里遇到第一个坑switch_ar漏写之后模型也能跑但summary()里多出一大堆区制相关的 AR 系数和 Hamilton(1989) 的设定完全不是一回事。这时候去对论文的表格折腾半天也找不出差异在哪。1.2 滤波概率和平滑概率是两回事Hamilton 的滤波器给出的是filtered概率基于截至 t 时刻的数据对 t 时刻处于某个区制的概率估计。平滑器Kim 1994给出的是smoothed概率用全样本信息反过来修正 t 时刻的概率。statsmodels 在fit()之后直接暴露了这两个属性res mod.fit() res.filtered_marginal_probabilities res.smoothed_marginal_probabilities很多复现稿画出来的图只有一条线其实应该画两条滤波概率在转折点附近抖动更明显平滑概率更接近 NBER 标注的衰退区间。如果只画filtered会觉得模型切换得太频繁只画smoothed又会觉得模型反应太慢。两者对照才能理解 Hamilton 的设计意图。Codex 在这里能帮上忙的地方是你不需要记住这些概念的名字只要把「滤波和平滑都要画」写进提示词它生成的代码会自动把两个子图拼在一起。1.3 expected durations 算不对的根源原文最后计算了衰退与扩张的预期持续时间。计算公式本身很简单如果转移概率矩阵是P [[p_00, p_01], [p_10, p_11]]那么区制 0 的预期持续时间为1 / (1 - p_00)区制 1 的预期持续时间为1 / (1 - p_11)。问题在于新手经常读错summary()里的转移矩阵方向statsmodels 输出的矩阵行是当前区制、列是下一期区制还是反过来如果读反预期持续时间可能变成 1.5 个季度和 2 个季度跟原文的「衰退约一年、扩张约两年半」差了十万八千里。所以先放一放代码细节把 Codex 的访问通道配好后面的改写和输出核对才有地方落地。2. 把 Codex 接到 TaoTokenBase URL 和 Key 的一次性配置2.1 去官网创建 API Key准备材料就一样东西API Key。打开 TaoToken注册并创建一个 Key。注意这里拿到的 Key 是一串真实值创建之后只显示一次记得复制下来。官网落地页只做三件事注册账号、创建 Key、看模型广场。创建 Key 之后不要急着关页面去模型广场看一眼当前可用的模型 ID。后面 Codex 的config.toml里model字段填什么是以模型广场显示的为准不是随便猜一个。2.2 编辑 ~/.codex/config.tomlCodex 是一个命令行工具它的配置集中在~/.codex/config.toml。要让 Codex 走 TaoToken 的统一接入通道需要新增一个model_provider并把默认 provider 指过去。完整的配置如下model YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEYbase_url填的是接口地址https://taotoken.net/api末尾不要加/v1。很多人在这一步习惯性补一个/v1结果 Codex 请求发到https://taotoken.net/api/v1返回 404。另外模型 ID 那一项填YOUR_MODEL_ID只是占位符实际值要去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的模型广场复制。然后在终端导出环境变量export TAOTOKEN_API_KEYYOUR_API_KEYenv_key告诉 Codex 去哪个环境变量里读取 Key所以这里的环境变量名必须和config.toml里一致。2.3 先跑一个最小请求确认连通配置好之后不要直接开肝 Hamilton 复现先用一句话让 Codex 回答确认请求真的通了codex exec 用一句话说明你已经准备好复现 Hamilton 1989如果 Codex 正常返回说明 TaoToken 通道没问题。如果返回 401先检查TAOTOKEN_API_KEY是否导出成功如果返回 404检查base_url是不是多了/v1。这个最小验证只需要十秒钟但能省掉后面排查环境变量的时间。3. 让 Codex 按原文步骤重写 Hamilton 1989 脚本3.1 给 Codex 的提示词现在把原文的步骤翻译成一段清晰的提示词。原文的路径是读取gndata→ 用MarkovRegression拟合 → 输出summary()→ 画滤波和平滑概率 → 计算预期持续时间。我把这些要求原样交给 Codex请用 statsmodels 复现 Hamilton(1989) 的马尔可夫区制转换自回归模型。 要求 1. 读取本地数据文件 gndataCSV 或 Excel 均可请先读取并打印前几行确认列名 2. 用 MarkovAutoregression 建模k_regimes2order4switch_arFalse 3. 拟合后输出 res.summary() 4. 分别绘制 filtered_marginal_probabilities 和 smoothed_marginal_probabilities 5. 从转移矩阵计算两个区制的预期持续时间并输出结果 6. 代码要能直接运行数据文件路径请用变量标注注意原文里gndata是从本地读取的Codex 不会替你下载数据也不会替你在生产机器上执行脚本。它只负责生成代码、解释结果、对照输出。数据文件需要你自己放到当前目录脚本也要你在本地终端运行再把输出贴回对话。这一步是 AI 编程工具的基本边界生成、解释、对照是它的事执行和验证始终在你手里。3.2 Codex 生成的代码长什么样按照上面的提示词Codex 会生成类似下面的脚本。注意这不代表原文代码而是符合原文步骤的可运行版本import numpy as np import pandas as pd import statsmodels.api as sm import matplotlib.pyplot as plt # 读取本地数据请确认 gndata 的格式 df pd.read_csv(gndata.csv, index_col0, parse_datesTrue) data df.iloc[1:] # 原文从第二行开始 # Hamilton(1989)k_regimes2, order4, switch_arFalse mod sm.tsa.MarkovAutoregression( data, k_regimes2, order4, switch_arFalse, ) res mod.fit() print(res.summary()) # 滤波概率与平滑概率 filtered res.filtered_marginal_probabilities smoothed res.smoothed_marginal_probabilities fig, axes plt.subplots(2, figsize(10, 7)) axes[0].plot(filtered[0]) axes[0].set_title(Filtered probability of regime 0) axes[1].plot(smoothed[0]) axes[1].set_title(Smoothed probability of regime 0) plt.tight_layout() plt.show() # 预期持续时间 trans res.regime_transition p00 trans[0, 0] p11 trans[1, 1] expected_duration_0 1 / (1 - p00) expected_duration_1 1 / (1 - p11) print(Expected duration of regime 0:, expected_duration_0) print(Expected duration of regime 1:, expected_duration_1)res.regime_transition是拟合后得到的转移概率矩阵。如果 Codex 的版本里属性名不同让它先打印dir(res)再调整。这段代码引用filtered[0]是因为filtered_marginal_probabilities返回的是 DataFrame列名是区制编号取0表示第一个区制。3.3 本地执行把输出贴回去脚本生成后在本地终端运行python hamilton1989.py然后把summary()的输出和两张概率图的结果描述贴回 Codex 对话。Codex 会根据输出判断参数是否合理、概率曲线是否符合 NBER 标注的衰退区间。这个「生成 → 运行 → 回贴 → 修正」的循环就是 Codex 在这条工作流里的真正价值它不是替你跑模型而是陪你调模型。4. 滤波概率、平滑概率与 expected durations 的验证口径4.1 先核对转移矩阵的方向summary()里有几个关键表格系数估计、转移概率矩阵、区制持续期。其中转移概率矩阵是个 2×2 的矩阵行是当前区制列是下一期区制。用这个口径去读res.regime_transitionprint(res.regime_transition)假设输出是[[0.904, 0.096], [0.228, 0.772]]那么区制 0 的预期持续时间是1 / (1 - 0.904)也就是 10.4 个季度左右区制 1 的预期持续时间是1 / (1 - 0.772)也就是 4.4 个季度左右。Hamilton(1989) 的经典结论是衰退持续约一年、扩张约两年半对应过来就是区制 1 约 4 个季度区制 0 约 10 个季度。两组数一对上说明switch_arFalse和转移矩阵的读取方向都没问题。4.2 用代码验证 expected durations原文在模型拟合后直接调用了预期持续时间的计算。Codex 重写的代码里这一步是手算的好处是你能看到每个数字从哪来。如果你想直接读结果对象的属性也可以接着上面的res运行print(res.expected_durations)res.expected_durations会返回一个数组两个区制各给一个持续时间。如果这个数组的值和手算的1/(1-p_ii)不一致说明转移矩阵的读取方向出了问题需要回头检查行列顺序。大多数情况下expected_durations的输出会直接给出[9.8, 4.3]这样的数字对应扩张约两年半、衰退约一年。4.3 平滑概率的图形核对滤波概率在衰退刚开始时会快速跳变平滑概率则会把这种跳变磨平一些。两者之间的差异如果过大通常意味着转移概率的估计对样本区间很敏感。原文把 NBER 的衰退阴影画在图上作对比这一步在 Codex 生成的代码里可以补上import pandas_datareader.data as web from datetime import datetime # NBER 衰退区间这里只是获取方式需要联网 # 如果没有网络可以跳过这一段直接看概率曲线不过要注意pandas_datareader拉 NBER 数据依赖网络有时候会失败。如果只做复现验证不联网也没关系平滑概率在高位持续四个季度左右的区间基本就是对衰退持续时间的直观印证。把smoothed[0]的图像和expected_durations的数字放在一起看整个 Hamilton 模型的结论就完整了。5. Filardo 时变转移概率让 Codex 改 exog_tvtp5.1 从固定概率到时变概率前面 Hamilton(1989) 假设转移概率矩阵不随时间变化。Filardo(1994) 放松了这个假设允许转移概率依赖上一期的外生变量。这时每个时期的转移概率变成p_ij_t logistic(x_{t-1} * beta_ij)。statsmodels 的实现里这个外生变量通过exog_tvtp参数传入。原文用的数据里有一个先行指标标准化之后作为exog_tvtp。对应的建模代码是mod_tvtp sm.tsa.MarkovAutoregression( data[dlip], k_regimes2, order4, exog_tvtpdata[leading], ) res_tvtp mod_tvtp.fit(search20)注意这里仍然用MarkovAutoregression因为 Filardo 模型保留了 Hamilton 的自回归结构。真正改用MarkovRegression的是原文中间演示的 KNS 三状态方差转换模型那个模型没有自回归成分需要指定k_regimes3、switching_varianceTrue、trendnc。两条路径不要混。data[leading]是范文里的先行指标列名实际数据里叫dmdlleading就改成dmdlleading。5.2 fit(search20) 解决局部最优马尔可夫区制转换模型的似然函数有很多局部极大值直接用 BFGS 从默认参数出发经常收敛到奇怪的地方。fit(search20)会让 statsmodels 对起始参数向量做 20 次随机扰动选最优的一组作为实际起始参数。这是 Filardo 复现里最关键的一步不写search的话时变转移概率的估计结果会很不稳定。Codex 生成的对应代码大致是res_tvtp mod_tvtp.fit(search20) print(res_tvtp.summary()) smoothed_tvtp res_tvtp.smoothed_marginal_probabilities # 时变模型的预期持续时间随 x_{t-1} 变化 expected_durations_tvtp res_tvtp.expected_durations plt.plot(expected_durations_tvtp[0])expected_durations在时变模型里不再是一个常数而是一条随x_{t-1}变化的曲线。把这条曲线画出来能看到经济衰退期间低生产状态的预期持续时间明显抬升这就是 Filardo 模型比 Hamilton 固定概率模型多出来的信息量。5.3 对比两种模型的结果固定概率模型给出的是一个平均意义上的持续期衰退约 4 个季度扩张约 10 个季度。时变模型则告诉你这个持续时间不是均匀的——如果上一期的先行指标开得很差那么当期进入低生产状态后预期持续时间可能从 4 个季度拉长到 8 个甚至更久。把 Hamilton 模型的平滑概率和 Filardo 模型的平滑概率画在同一张图上最直观的差异是Filardo 模型的区制切换点往往领先几个季度。这就是标题里「时变」两个字的实际价值也是原文最后一张图想表达的东西。Codex 在这里的用法是把两张图的数据都输出然后让它用文字描述两个模型在衰退起点附近的差异比你盯着图猜要快。6. 在终端确认调用成功并回到官网核对用量6.1 怎么确认请求真的走通了前面所有 Codex 交互都是通过 TaoToken 通道发给大模型的。验证用量视角下你需要确认请求确实成功而不是本地缓存或错误回退。Codex 正常工作的标志是每次codex exec都有完整返回没有任何 401 或 404。如果 Codex 在运行中报了 401先去检查TAOTOKEN_API_KEY这个环境变量是不是真的设上了echo $TAOTOKEN_API_KEY如果输出为空回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 检查 Key 是否复制完整。如果报 404大概率是base_url里多了/v1把https://taotoken.net/api/v1改回https://taotoken.net/api就好。模型 ID 写错时的报错信息通常不是 401 也不是 404而是一段「model not found」之类的 JSON回去对一下模型广场的列表就行。6.2 顺手记下来的三个注意点第一模型 ID 一定要从模型广场复制不要凭记忆写。很多人图省事让 Codex 直接猜一个模型名结果请求发出去服务端根本不认识这个 ID报出来的错误既不是 401 也不是 404而是模型不存在。第二base_url永远只填https://taotoken.net/api官网落地页的链接不要填进去。落地页负责注册、看模型、看用量接口地址负责接收 Codex 的请求两者各管各的。第三Key 创建后只显示一次但随时可以在控制台重新生成重新生成后记得同步更新环境变量否则终端里还挂着旧 Key报错信息会误导你往模型方向上查。6.3 打开控制台核对这次调用的用量最后一步验证用量视角打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 进入控制台看这次 Hamilton 和 Filardo 调试过程中发起了多少次请求、消耗了多少 token。这不是让你心疼额度而是确认配置真实生效——你看到的请求数和本地实际操作次数对得上说明 model_provider 指向完全正确。以后遇到summary()读不懂、平滑概率对不上 NBER 衰退区间的情况直接把输出贴给 Codex让它按原文对照就行。Key 和 Base URL 配好一次后面所有的模型访问都走同一条通道。这套「Codex 生成脚本 本地执行验证 控制台核对用量」的流程比每次手动配环境变量再猜模型名要省心得多。