第二章:数据准备——获取纳斯达克主要行业ETF数据

做量化投资,数据就是你的弹药库。没有数据,再牛的策略也是纸上谈兵。

这一章,我们来搞定纳斯达克主要行业ETF的数据获取。说白了,就是要把QQQ、XLK、XLV这些家伙的历史数据拉下来,存好,等着后续分析用。

2.1 为什么选这些ETF?

我刚开始做行业轮动策略时,也纠结过到底选哪些标的。后来发现,纳斯达克这几个行业ETF覆盖面广、流动性好,而且数据源稳定。

你想想看,QQQ代表整个纳斯达克100,XLK是科技,XLV是医疗,XLI是工业,XLP是必需消费,XLY是可选消费,XLF是金融,XLU是公用事业,XLB是材料,XLRE是房地产。这十个基本把美股主要行业都覆盖了。

核心思路:行业轮动的本质就是资金在不同板块间流动。我们跟踪这些ETF,就能捕捉到资金流向的变化。

2.2 数据获取方案

我个人习惯用Python的yfinance库来拉数据。为什么选它?因为免费、简单、数据质量还行。

安装命令很简单:

pip install yfinance pandas

嗯,这里要注意:yfinance的数据源是雅虎财经,国内访问有时候会慢。我建议你配个代理,或者用国内的tushare替代。不过为了教学方便,咱们先用yfinance。

2.3 代码实战:批量获取ETF数据

直接上代码。这段代码我用了好几年,基本没出过问题。

import yfinance as yf
import pandas as pd
from datetime import datetime, timedelta

# 定义ETF列表
etf_list = ['QQQ', 'XLK', 'XLV', 'XLI', 'XLP', 
            'XLY', 'XLF', 'XLU', 'XLB', 'XLRE']

# 设置时间范围:过去5年
end_date = datetime.now()
start_date = end_date - timedelta(days=5*365)

# 批量下载数据
def fetch_etf_data(tickers, start, end):
    data = {}
    for ticker in tickers:
        print(f"正在获取 {ticker} 数据...")
        try:
            df = yf.download(ticker, start=start, end=end, 
                           progress=False)
            data[ticker] = df
        except Exception as e:
            print(f"获取 {ticker} 失败: {e}")
    return data

# 执行下载
etf_data = fetch_etf_data(etf_list, start_date, end_date)

# 查看数据样例
print(etf_data['QQQ'].head())

避坑指南:我曾经一次性下载10个ETF的5年数据,结果网络超时了。后来我加了重试机制,每次下载间隔1秒。代码里加个time.sleep(1)就能解决。

2.4 数据清洗与存储

数据拉下来后,你会发现有些ETF在某些日期没有交易数据。比如节假日、停牌日。这些空值需要处理。

我的做法是:

  1. ffill()向前填充缺失值
  2. 删除仍然为空的行
  3. 统一索引为日期格式
# 数据清洗函数
def clean_etf_data(data_dict):
    cleaned = {}
    for ticker, df in data_dict.items():
        # 只保留收盘价
        df_clean = df[['Close']].copy()
        df_clean.columns = [f'{ticker}_Close']
        
        # 填充缺失值
        df_clean = df_clean.ffill().dropna()
        
        cleaned[ticker] = df_clean
    return cleaned

# 合并所有ETF数据
def merge_etf_data(cleaned_dict):
    merged = pd.DataFrame()
    for ticker, df in cleaned_dict.items():
        if merged.empty:
            merged = df
        else:
            merged = merged.join(df, how='outer')
    return merged.dropna()

# 执行清洗和合并
cleaned_data = clean_etf_data(etf_data)
merged_data = merge_etf_data(cleaned_data)

# 保存到CSV
merged_data.to_csv('nasdaq_etf_data.csv')
print(f"数据保存完成,共 {len(merged_data)} 个交易日")

2.5 数据质量检查

数据拿到手,别急着用。先做个快速检查。

检查项 方法 预期结果
缺失值比例 merged_data.isnull().sum() 应为0
日期连续性 检查日期是否连续 无跳空(节假日除外)
价格合理性 检查是否有负值或异常值 所有价格>0
数据长度 各ETF行数是否一致 应完全一致

注意:XLRE(房地产ETF)是2015年才推出的,如果你拉的数据早于2015年,这部分数据会是空的。我建议统一从2016年开始拉数据,这样所有ETF都有完整记录。

2.6 数据可视化预览

数据准备好了,咱们画个图看看整体走势。这里用matplotlib简单画一下。

import matplotlib.pyplot as plt

# 归一化处理,方便对比
normalized = merged_data / merged_data.iloc[0] * 100

# 绘制走势图
plt.figure(figsize=(14, 8))
for col in normalized.columns:
    plt.plot(normalized.index, normalized[col], 
             label=col.replace('_Close', ''), linewidth=1)

plt.title('纳斯达克行业ETF走势对比(归一化)', fontsize=14)
plt.xlabel('日期')
plt.ylabel('价格(基准=100)')
plt.legend(loc='best')
plt.grid(True, alpha=0.3)
plt.show()

你会发现,不同行业的走势差异很大。比如2020年疫情初期,XLK(科技)跌得少、涨得快,而XLI(工业)跌得惨。这就是行业轮动的机会所在。

2.7 数据更新机制

策略不是做一次就完事的。你需要定期更新数据。

我建议:

  • 每天收盘后自动拉取最新数据
  • 用增量更新的方式,只拉取缺失的日期
  • 保留原始数据,不要覆盖
# 增量更新函数
def update_data(existing_file, tickers):
    # 读取已有数据
    old_data = pd.read_csv(existing_file, index_col=0, parse_dates=True)
    last_date = old_data.index[-1]
    
    # 只拉取新数据
    new_data = fetch_etf_data(tickers, 
                              start=last_date + timedelta(days=1),
                              end=datetime.now())
    
    # 合并新旧数据
    updated = pd.concat([old_data, new_data])
    updated.to_csv(existing_file)
    print(f"数据已更新至 {updated.index[-1].date()}")

核心要点:数据准备是整个策略的基石。数据质量直接决定策略效果。我见过太多人花90%时间调参数,却不愿意花10%时间检查数据。结果策略回测漂亮,实盘一塌糊涂。

好了,数据已经准备好了。接下来我们就可以用这些数据做行业轮动分析了。

数据准备流程 选择ETF标的 QQQ, XLK, XLV等10只 数据获取 yfinance批量下载 数据清洗 填充缺失值、统一格式 数据合并 按日期对齐、合并为DataFrame 质量检查 缺失值、异常值、连续性 数据存储 保存为CSV文件 每日增量更新

数据准备这块,其实没什么高深的技术。关键是要细心、规范。我见过有人因为日期格式不统一,回测时数据对不上,白白浪费了两天时间。所以,从一开始就养成良好的数据管理习惯,后面会省很多事。

个人经验:我习惯在数据文件里加一个版本号,比如nasdaq_etf_data_v1.csv。这样万一数据出问题,可以快速回滚到上一个版本。别问我为什么知道这个坑...

数据准备好了,接下来就可以开始真正的行业轮动分析工作了。

蓝海数据掘金营,专注资料整理