拓冰建站拓冰建站
首页 / 资讯中心 / 正文

用Python从零搭建美元指数、美债与黄金行情联动分析工具

在财经资讯里“美元指数下跌”“10年期美债收益率跳水”“黄金价格拉升”常常在同一篇文章中出现。作为开发者看到这类行情标题时与其只关心涨跌结果不如把它拆成一个数据工程问题能不能写一套脚本自动拉取三类市场数据做清洗对齐再用相关性和图表把联动关系量化出来。本文就以“美元指数、10年期美债收益率、黄金价格”这组经典宏观品种为例从零搭建一个行情联动分析工具。整个项目适合 Python 基础读者读完可以掌握免费行情源的使用、时间序列对齐、滚动相关性计算、Matplotlib 可视化和定时报告生成也能迁移到其他金融数据分析场景。在实际动手之前要先把目标说清楚本文不预测行情不评价政策方向只做“从公开数据到统计指标”的工程实现。行情标题给出的是一瞬间的市场情绪而脚本给出的是一段时间的数据关系两者用途不同。代码里出现的任何数字都只是当前读取到的历史数据不代表对未来的判断。1. 这个题目拆成技术任务是什么1.1 三个行情品种的常见数据口径美元指数、美国国债收益率、黄金价格是宏观行情分析中经常被放在一起讨论的三个变量。从数据角度看它们分属不同资产类别数据源和字段含义也完全不同。品种常见代码数据口径更新频率数据特点美元指数DX-Y.NYB美元对一篮子货币的汇率指数分钟/日线开盘收盘连续几乎每个交易日都有数据10年期美债收益率^TNX美国10年期国债的到期收益率分钟/日线数值为百分比行情软件常显示为“4.20”代表4.20%国际黄金GCF纽约商品交易所黄金期货主力合约价格分钟/日线受交易时段影响存在节假日缺失在金融数据接口里这三个品种通常都能按“美元指数期货”“国债收益率指数”“黄金期货”三类合约找到。要注意的是不同数据商对同一品种的命名可能不同比如美债收益率有的返回小数形式有的返回百分数形式落地前要先查看文档确认。1.2 从新闻标题到数据工程问题当一篇行情报道说“10年期收益率跳水”时它背后是一整根收益率曲线的变化当说“黄金直指某个价位”时背后是期货市场多空力量的快速切换。开发者的工作不是重复描述涨跌而是把这种描述转换成可验证的代码逻辑。这里要解决的问题有三个如何稳定地获取三份日线行情数据。如何把不同交易日的序列对齐到同一时间轴上。如何量化三个品种之间的联动程度。第二个问题最容易忽略。美元指数、美债、黄金的交易时间并不完全一致美国债券市场和商品期货市场可能有不同的节假日休市安排。直接合并三个 DataFrame很容易出现 NaN 堆积或者前后错位必须在计算前做统一处理。2. 技术选型Python 生态与免费行情源2.1 行情数据源对比搭建个人分析工具时优先考虑免费、无需注册、接口稳定的数据源。如果后续要把脚本做成团队服务再切换到付费或专业数据源。数据源获取方式优点限制适用场景yfinancePython 第三方库免费、免注册、支持全球主流品种数据延迟较高极端行情时可能限流个人研究、原型验证AkSharePython 第三方库覆盖国内股票、期货、宏观数据依赖目标网站页面结构偶尔需要更新中国金融市场数据分析Tushare ProHTTP API数据结构规范、字段完整注册需要积分部分接口有权限要求中等规模量化项目本地 CSV手工导出完全可控、无网络依赖需要人工维护、更新滞后离线学习和测试Bloomberg / Wind终端 API实时、准确、覆盖全成本高、申请门槛高机构生产环境本文示例使用 yfinance原因是它安装方便、不需要申请 token适合作为最小闭环的起点。生产环境如果对数据时效和准确性有更高要求需要评估替换成本。2.2 OHLCV 字段语义行情接口返回的数据一般包含几个标准字段理解它们才能正确计算涨跌幅和技术指标。字段英文含义说明Open开盘价当前交易周期第一笔成交价High最高价当前周期内最高成交价Low最低价当前周期内最低成交价Close收盘价当前周期最后一笔成交价也是计算涨幅最常用的字段Volume成交量当前周期累计成交数量债券收益率序列通常没有成交量Adj Close复权收盘价经过除权除息调整后的收盘价指数和期货品种一般与 Close 相同对于美元指数和黄金期货计算日涨跌幅使用 Close 字段。对于债券收益率不能简单地把数值变化看作百分比涨跌因为收益率本身就是一个利率数值直接相减得到的是“基点变化”而不是收益率变化的百分比。2.3 版本环境确认本文示例基于以下环境实际项目用到不同版本时要调整对应的 API 参数。Python 3.10 及以上yfinance 0.2.x 版本pandas 2.x 版本matplotlib 3.x 版本注意yfinance 的接口版本更新较快如果代码运行报错先看官方仓库的 README不要盲目改参数。3. 环境准备与最小工程结构3.1 安装依赖创建虚拟环境后执行以下命令安装依赖。python -m venv .venv source .venv/bin/activate # Windows 使用 .venv\Scripts\activate然后安装三个核心库。pip install yfinance pandas matplotlib如果网络下载较慢可以使用国内镜像源。pip install yfinance pandas matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后用一段代码验证环境是否可用。import yfinance as yf import pandas as pd import matplotlib.pyplot as plt print(yfinance version:, yf.__version__) print(pandas version:, pd.__version__) print(matplotlib version:, plt.matplotlib.__version__)能正常打印版本号说明环境搭建完成。下面开始设计项目目录。3.2 项目目录结构这个项目按照“数据获取—清洗—计算—可视化—报告”五层组织。market_analysis/ ├── .venv/ ├── data/ │ ├── raw/ │ └── processed/ ├── output/ │ └── charts/ ├── scripts/ │ ├── download_data.py │ ├── clean_data.py │ ├── correlation_report.py │ └── make_charts.py └── main.pydata/raw保存从网络下载的原始 CSV 文件。data/processed保存清洗对齐后的宽表。output/charts保存生成的图片。scripts存放各阶段业务代码。main.py是串联入口。这种分层方式不是必须的但它能把“下载”“清洗”“计算”三个阶段分开方便单独调试和定时执行。3.3 环境检查清单在写正式代码前建议先按下面清单检查一遍Python 虚拟环境是否激活。三个依赖库是否安装成功。当前目录是否有读写权限。网络能否访问数据源域名。时间戳时区是否设置为 Asia/Shanghai。跳过这里的一两个检查不会影响代码运行但会在后续排错时增加干扰。4. 获取三类行情数据4.1 编写行情下载脚本第一个脚本scripts/download_data.py负责从 yfinance 下载数据并保存到本地。import yfinance as yf import pandas as pd from pathlib import Path RAW_DIR Path(__file__).parent.parent / data / raw RAW_DIR.mkdir(parentsTrue, exist_okTrue) TICKERS { DXY: DX-Y.NYB, # 美元指数 US10Y: ^TNX, # 10年期美债收益率 GOLD: GCF, # 黄金期货 } START_DATE 2023-01-01 END_DATE 2024-12-31 def download_one(ticker: str, start: str, end: str) - pd.DataFrame: 下载单个品种的日线数据保存为 CSV 文件。 df yf.download( ticker, startstart, endend, auto_adjustFalse, progressFalse, ) if df is None or df.empty: raise RuntimeError(fdownload failed: {ticker}) df.to_csv(RAW_DIR / f{ticker}.csv) print(fdownloaded {ticker}: {len(df)} rows) return df def main(): for name, ticker in TICKERS.items(): try: download_one(ticker, START_DATE, END_DATE) except Exception as exc: print(f[ERROR] {name} - {ticker} - {exc}) if __name__ __main__: main()这段代码的作用是按照字典里的品种代码逐个下载并把原始数据落到data/raw目录。把数据先落盘再处理比每次都从网络读取更稳因为后续调试清洗逻辑时不需要反复请求数据源。4.2 yfinance 返回的数据结构运行脚本后下载到的 CSV 一般长这样。Date,Open,High,Low,Close,Adj Close,Volume 2023-01-03,103.52,104.12,103.19,103.39,103.39,0 2023-01-04,103.25,103.76,102.27,102.52,102.52,0如果同一个文件里出现Price前缀比如Price.Open这是 yfinance 多级列名造成的说明写入 CSV 时没有把列名展平。解决方法是下载后手动做一层列名处理。if isinstance(df.columns, pd.MultiIndex): df.columns df.columns.get_level_values(0)4.3 常见网络与命名问题下载阶段最常见的错误有两种。第一种是YFRateLimitError表示短时间请求太频繁解决方式是增加间隔时间或者把progressFalse改为progressFalse的同时在循环里加time.sleep(1)。第二种是No data found for symbol通常表示 ticker 代码写错或者该品种在当前时间范围内没有数据。注意不同数据源对“美元指数”的代码定义不同DX-Y.NYB是洲际交易所的美元指数不是单纯的美汇指数。代码一旦选定在整篇文章里都要保持同一个口径。5. 数据清洗与指标计算5.1 时间对齐交易日历问题三个品种的交易日并不完全一致。美国债券市场休市日可能和期货市场不同因此在合并之前先统一把 Date 字符串转成DatetimeIndex再使用reindex对齐时间轴。import pandas as pd from pathlib import Path RAW_DIR Path(data/raw) PROCESSED_DIR Path(data/processed) PROCESSED_DIR.mkdir(parentsTrue, exist_okTrue) def load_series(ticker: str, field: str Close) - pd.Series: df pd.read_csv(RAW_DIR / f{ticker}.csv, index_col0, parse_datesTrue) if Adj Close in df.columns: df df.rename(columns{Adj Close: AdjClose}) return df[field] close_dxy load_series(DX-Y.NYB) close_us10y load_series(^TNX) close_gold load_series(GCF) panel pd.concat( { DXY: close_dxy, US10Y: close_us10y, GOLD: close_gold, }, axis1, ).dropna(howall) panel panel.sort_index() panel.to_csv(PROCESSED_DIR / panel.csv)这里使用concat而不是merge是因为三个序列都落在同一个日期索引上直接横向合并即可。dropna(howall)删除的是三列全为空的天数比如某一天三个市场都休市。5.2 缺失值处理策略对齐之后还需要处理部分品种单独缺失的情况。可以选择删除缺失行、向前填充、向后填充或插值不同策略适用场景不同。策略做法适用场景dropna删除包含 NaN 的行三个品种都强制要求同日数据ffill用前一个有效值填充非交易日数据可以沿用上一交易日bfill用后一个有效值填充适合补全第一条记录前的少量缺失interpolate线性插值只适合少量缺失且序列应较平滑在本项目里建议使用“先 concat再 ffill 限制填充天数”的方式。panel panel.ffill(limit5).dropna()limit5表示最多向前填充五个工作日这样能避免在某品种长期停牌时用旧数据填充过多。5.3 计算日涨跌幅与累计涨跌幅数据对齐后计算日涨跌幅是联动分析的基础。daily_ret panel.pct_change().dropna() daily_ret.to_csv(PROCESSED_DIR / daily_returns.csv) cumulative_ret (1 daily_ret).cumprod() - 1对于美元指数和黄金pct_change()得到的是资产价格的日收益率。对于美债收益率pct_change()计算的是收益率的相对变化率。这个指标可以用来观察三者在同一交易日内的涨跌方向是否一致但不能直接解释为资产的资本利得。5.4 计算均线过滤短期噪声直接看日收益率数据噪声比较大。加入 20 日均线后能过滤掉一些随机波动让趋势更直观。panel[DXY_MA20] panel[DXY].rolling(20).mean() panel[US10Y_MA20] panel[US10Y].rolling(20).mean() panel[GOLD_MA20] panel[GOLD].rolling(20).mean()均线的目的是辅助观察不是交易信号。在行情联动分析里它更多用于判断当前价格处于中期均线上方还是下方从而对“同步性”的观察增加一个维度。6. 联动关系量化用相关系数和滚动窗口说话6.1 全样本相关系数最简单的联动分析是计算三者的相关系数矩阵。corr daily_ret.corr() print(corr)输出会是一个三行三列的矩阵。例如DXY US10Y GOLD DXY 1.0000 -0.3200 -0.6800 US10Y -0.3200 1.0000 0.1200 GOLD -0.6800 0.1200 1.0000这不是固定值不同时间窗口结果差别会很大。它的含义是在样本区间内两个序列的日收益率在统计上是否朝同一个方向变动。数值接近 1 表示同向接近 -1 表示反向接近 0 表示无明显线性关系。6.2 滚动相关性观察关系是否稳定全局相关系数的问题在于它会掩盖时间段内的变化。比如前半段美元和黄金负相关明显后半段可能因为市场结构变化而变成弱相关。用滚动窗口可以观察这种变化。rolling_corr daily_ret[DXY].rolling(60).corr(daily_ret[GOLD]) rolling_corr.tail(10)这里rolling(60)表示使用过去 60 个交易日作为窗口。窗口越小数值越敏感窗口越大曲线越平滑。一般个人分析使用 20 到 90 天窗口。窗口大小适用分析问题20 天短期联动容易上下剧烈波动60 天常规月度观察平衡性和平滑性较好90 天以上中长期趋势对突发事件反应慢6.3 用最小二乘做简单的弹性系数除了相关系数还可以计算两个变量日收益率的回归系数用来观察“当美元指数变化1%时黄金收益率平均变化多少”。import numpy as np dxy_ret daily_ret[DXY].values gold_ret daily_ret[GOLD].values mask ~(np.isnan(dxy_ret) | np.isnan(gold_ret)) beta, alpha np.polyfit(dxy_ret[mask], gold_ret[mask], 1) print(alpha:, alpha) print(beta:, beta)这个回归模型很简单只适合作为分析参考。它没有处理异方差、序列自相关和多重共线性不能用于严谨的计量建模也不能用于实际交易决策。6.4 量化分析的边界这里算出的相关系数和回归系数只是统计描述。金融变量之间的关系是非平稳的过去的高相关不代表未来继续保持。把滚动相关系数理解为“当前市场状态的一种快照”比理解为“未来规律”要稳妥得多。7. 可视化与自动报告7.1 三图联动展示绘制三张子图分别展示美元指数、10年期美债收益率和黄金价格在同一个时间轴上观察峰值和底部的位置关系。import matplotlib.pyplot as plt fig, axes plt.subplots(3, 1, figsize(12, 10), sharexTrue) axes[0].plot(panel.index, panel[DXY], labelDXY, colortab:blue) axes[0].set_ylabel(USD Index) axes[0].legend() axes[1].plot(panel.index, panel[US10Y], label10Y Yield, colortab:orange) axes[1].set_ylabel(Yield (%)) axes[1].legend() axes[2].plot(panel.index, panel[GOLD], labelGold, colortab:red) axes[2].set_ylabel(Gold Price (USD)) axes[2].legend() fig.suptitle(DXY / US10Y / GOLD Daily Close) fig.tight_layout() plt.savefig(output/charts/panel_close.png, dpi150) plt.close(fig)这里使用sharexTrue让三个子图共享横轴时间方便观察同一日期附近多个品种的拐点。7.2 绘制滚动相关系数图把滚动相关系数单独画出来比只看一张全局相关矩阵要直观。fig, ax plt.subplots(figsize(12, 5)) ax.plot(rolling_corr.index, rolling_corr.values, labelDXY-GOLD 60d corr, colorpurple) ax.axhline(0, colorgray, linewidth0.8, linestyle--) ax.set_ylabel(Correlation) ax.set_title(Rolling 60-Day Correlation) ax.legend() fig.tight_layout() plt.savefig(output/charts/rolling_corr_dxy_gold.png, dpi150) plt.close(fig)当曲线在 0 轴上方时说明窗口内美元和黄金同向波动在 0 轴下方时则是反向波动。这条曲线本身不会告诉你未来会怎样但它能清晰指出“关系在哪一段发生了变化”。7.3 输出汇总报告脚本最终应该生成一份简单的 Markdown 或 CSV 汇总方便定时任务发送或归档。summary { last_date: str(panel.index[-1].date()), dxy_last: round(panel[DXY].iloc[-1], 2), us10y_last: round(panel[US10Y].iloc[-1], 2), gold_last: round(panel[GOLD].iloc[-1], 2), dxy_gold_corr_60d: round(rolling_corr.iloc[-1], 4), } print(summary)如果后续要发送邮件可以把这份字典转成报告文案。7.4 用定时任务每天自动运行在 Linux 系统里可以用 cron 设置每天收盘后运行一次主脚本。0 8 * * 1-5 cd /path/to/market_analysis /path/to/.venv/bin/python main.py logs/run.log 21上面这行表示周一到周五早上 8 点运行。注意服务器时区如果是 UTC北京时间 8 点对应 UTC 0 点需要自行调整。在 Windows 环境中可以使用“任务计划程序”指向.venv\Scripts\python.exe和main.py路径。定时任务跑起来后要观察日志文件不能只验证脚本能跑一次。8. 常见问题排查从行情脚本到图表输出的完整排错表问题现象常见原因检查方式处理建议yfinance 下载返回空数据ticker 代码错误或数据源限流先单独打印df.head()检查 ticker 拼写增加重试逻辑等待后重试CSV 出现Price.Open多级列名MultiIndex 列未被展平打印df.columns使用get_level_values(0)展平列名concat 后大量 NaN三品种交易日不一致检查各序列的长度和缺失比例使用ffill(limit5).dropna()处理收益率序列出现 0 或异常值复权字段选择错误或停牌填充影响检查原始 Close 是否有连续相同值复核auto_adjust参数确保数据字段正确图表中文乱码matplotlib 默认字体不支持中文查看系统字体列表设置plt.rcParams[font.sans-serif]为指定中文字体定时任务没有输出环境变量或工作目录不对手动执行python main.py看是否报错在 cron 中使用绝对路径并把日志写入文件滚动相关性计算报错参与计算的两个 Series 索引不对齐检查daily_ret是否仍有 NaN先dropna()再计算相关性排错顺序建议从“输入数据是否正确”开始再检查字段选择、缺失值处理、时区问题和字体显示。多数脚本问题都出在数据对齐阶段而不是最后的计算公式。9. 生产环境还需要补哪些能力9.1 从脚本升级为服务个人脚本跑通之后如果要在团队内使用至少要补充以下能力配置外置化数据源代码、开始日期、时间窗口不要写死在脚本里放入 YAML 或环境变量。日志记录每次运行开始时间、结束时间、行数变化和异常堆栈。数据持久化把每日数据增量写入 SQLite 或 PostgreSQL而不是每次全量下载。重试机制网络请求失败时按指数退避重试。监控告警当数据缺失比例超过阈值或脚本运行失败时通过企业微信机器人、邮件等渠道通知负责人。9.2 数据库表结构示例如果使用 SQLite可以设计两张表一张原始日线表一张指标计算表。CREATE TABLE daily_price ( trade_date DATE NOT NULL, symbol TEXT NOT NULL, open REAL, high REAL, low REAL, close REAL, volume REAL, PRIMARY KEY (trade_date, symbol) );CREATE TABLE daily_indicator ( trade_date DATE NOT NULL, dxy_ret REAL, us10y_ret REAL, gold_ret REAL, dxy_gold_corr_60d REAL, PRIMARY KEY (trade_date) );把原始数据和衍生指标分开存储后续重算指标时不需要重新下载行情。9.3 合规与数据说明金融数据虽然是公开数据但不同数据源的授权范围不一样。使用 yfinance 做个人研究和学习没有问题如果要在商用系统中收费提供服务需要重新评估数据源的许可条款。输出报告中应保留“数据来源”和“统计区间”字段方便追溯。9.4 扩展方向从行情到文本对标题型分析感兴趣的人可以把这套数据管道进一步扩展为“新闻情绪 行情联动”系统。先抓取每日新闻标题用情感分析模型给出情绪分数再与美元指数、黄金价格的日收益率做相关性统计。这是一个典型的跨领域工程练习可以把爬虫、NLP、时间序列分析串在一起。9.5 项目验收清单一份可直接使用的检查清单三个品种原始数据都能成功下载并保存。清洗后的宽表不存在未解释的 NaN。日收益率计算正确首行为 NaN 正常。相关矩阵输出格式完整。60 日滚动相关性曲线正常生成。图表能保存到指定目录。主脚本能从命令重复执行且结果一致。日志能看到每次运行的记录。说明文档里有数据源和版本信息。这个项目本身不复杂但它把行情获取、数据清洗、统计计算、可视化和自动化串成了一条完整链路。下次再看到“某个品种暴涨暴跌”的标题时可以先问一句我能不能用数据把这个关系验证一遍。能把这个问题问出来就已经开始进入金融数据工程的门了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门