工行 USD/CNY 换汇时机分析助手
我把工行人民币兑美元即期牌价的数据抓取下来,覆盖现汇以及现钞两类报价,并在本地维护缓存与完整性校验。脚本会自动补齐缺失数据,并借助分位数、标准分 Z-Score 以及短期趋势等指标输出“今日是否适合购汇”的量化建议。

MISSION BRIEFING
数据分析项目,工行 USD/CNY 换汇时机助手
数据源
ICBC 官方牌价
抓取工行即期牌价数据,覆盖现汇以及现钞,并把发布时间一起保存下来。
缓存策略
增量缓存 + 自动回填
历史只抓一次并维护在 icbc_fx_cache 目录下的 usd_cny_store.json。每次运行会先做完整性校验,再把缺失与异常日期补齐。
核心指标
分位以及 Z-Score
计算 180 日分位,7 日、30 日、90 日、180 日均值,以及标准分 Z-Score,用来刻画相对位置与偏离程度。
趋势判断
14 日斜率
对最近 14 个交易日计算线性斜率,并把它换算成每天的百分比变化,从而更直观地体现短期方向。
输出形态
结论以及信号
输出结论与综合评分,并把触发结论的信号逐条打印出来,方便复核。
抓取策略
限频并加入抖动
随机 UA 与请求头,Warm-up 预热,重试与抖动间隔,并限制每次回填上限,降低请求压力。
SCREEN TOUR
项目展示
点击图片可放大查看
页面列表
点击缩略图切换;点击右侧大图可放大查看
当前预览
点击图片可放大查看;放大后支持:滚轮缩放 · 拖拽平移 · Esc 关闭
终端输出会包含历史回填、指标计算以及判断结论
项目概述
我做这个脚本的主要原因是,我在换汇时不想只看“当前报价”,而是希望把它放到历史序列里去做对比。于是我用 Python 写了一个脚本,它会抓取工行人民币兑美元的即期牌价数据,并把结果落到本地缓存中,避免每次运行都重复拉取历史。
在每次运行时,脚本会先对缓存做完整性校验,并把缺失或异常日期加入回填队列,然后再计算分位数、标准分 Z-Score、趋势斜率等指标。最后它会把“今日是否适合购汇”的结论打印出来,并且同步输出触发结论的信号说明,便于我自己复核判断依据。
本项目仅用于数据分析与工程实践展示,不构成任何投资建议或购汇建议。
解决的核心问题
指标体系
脚本默认以境外卖出价作为“购汇成本”的主要参考,这个字段对应现汇卖出价。为了把“当前价位处在什么位置”讲清楚,脚本会计算多维度指标,并把它们一起输出。
我会先算一个 180 日百分位,它用过去 180 个交易日的数据告诉我当前价格更接近历史的低位还是高位。然后我会把 7 日、30 日、90 日、180 日这几个窗口的均值以及标准差一起算出来,并用标准分 Z-Score 来表达当前价相对均值的偏离程度,这样我能更直观地知道它是“偏便宜”还是“偏贵”。
为了判断短期方向,我会对最近 14 个交易日做线性拟合,得到一个趋势斜率,并把它换算成每天的百分比变化。在此基础上,我也会把当前价和上一交易日做差,得到日度变化及其百分比,这一项主要用来捕捉突然的波动。最后我会把当前价相对 30 日均值的偏离统一到百分比口径,避免不同时间段的对比口径不一致。
最终脚本会输出三档结论,分别是 今日购汇偏有利、今日购汇不划算,建议观望 以及 信号中性,可继续观察盘中价格。每个结论后面都会跟着一组信号说明,用来解释它为什么会得到这个判断。
缓存与数据自动回填
脚本会在与脚本同级的 icbc_fx_cache 目录里维护 usd_cny_store.json。这个缓存文件带有 version 字段,并且把数据分成 daily 与 intraday 两部分。
我在脚本里把缓存校验做成固定流程,所以每次运行时都会先检查 daily 记录的字段是否齐全,并且对 foreign_buy 以及 foreign_sell 做合理区间校验,比如把百美元报价限制在 400 到 1200 之间,避免把明显异常的数值当成真实数据。
另外我会对最近几天标记为 no_data 的日期做复核,这样就算遇到延迟发布造成的缺口,也能在后续运行里再次确认并补齐。为了不让一次运行产生过多请求,我给历史回填设置了上限,每次最多回填 30 天。与此同时,脚本也会保存同一天的盘中快照,并把每个交易日的快照数量限制为 12 条,用来控制缓存体积。
使用方式
# 直接运行,会增量回填缺失历史并更新当天最新牌价,同时输出分析结论
python usd_cny_scientific_analysis.py
# 强制刷新,会忽略缓存并重新抓取设定历史窗口的数据
python usd_cny_scientific_analysis.py --force-refresh
# 调整历史窗口,例如只维护 1 年
python usd_cny_scientific_analysis.py --history-years 1
工程细节
为了让脚本在长期运行时更稳定,我在工程细节上做了这些处理。
我复用 requests.Session,并且对 429 以及 5xx 这类请求失败情况加入重试与退避。请求 API 之前我还会先做 Warm-up 预热,让会话更接近真实访问,然后再去拉取数据。
在请求行为上,我会随机 UA 以及请求头,并且在请求之间加入抖动间隔,用来降低请求尖峰和固定指纹带来的风险。指标层面我把分位阈值、Z-Score 阈值以及斜率阈值集中成常量,后续要是想基于数据表现去做校准,也更容易下手。