00 · 数据与环境¶
⚠️ 免责声明:本研究为量化/短期交易方法与可行性的探讨。所有回测均为历史模拟,不代表未来收益,不构成任何投资建议。真实交易受流动性、冲击成本、税费、心理与执行误差影响,结果通常更差。量化与短期交易可致重大本金亏损。读者需自行承担一切决策后果。
数据来源:A股 akshare(新浪后端) / 美股 yfinance,限流时降级腾讯行情;全部缓存于
data/*.csv,每个数字均可在本 notebook 单元复现。
本 notebook 负责:统一数据层验证、缓存、中文绘图、最小示例。
后续所有 notebook 复用这里建立的 qr_data(取数+缓存)与 qr_bt(回测)两个模块,保证全研究的数字同源、可离线复现。
1. 环境与依赖¶
import sys, platform
import numpy as np, pandas as pd, matplotlib
import qr_data as q, qr_bt as bt
bt.setup_chinese_font() # 中文字体: Noto Sans CJK SC
print('Python', sys.version.split()[0], '|', platform.system())
print('pandas', pd.__version__, '| numpy', np.__version__, '| matplotlib', matplotlib.__version__)
np.random.seed(42) # 固定随机种子,保证可复现
Python 3.11.6 | Linux pandas 3.0.3 | numpy 2.4.6 | matplotlib 3.11.0
2. 研究标的池¶
A股取沪深300指数 + 5 只高流动性成分股(覆盖白酒/银行/保险/家电);美股取宽基 ETF(SPY/QQQ) + 大盘股(AAPL/MSFT)。区间 2014-01-01 ~ 2024-12-31(约11年)。
print('A股标的:')
for k,v in q.A_UNIVERSE.items(): print(f' {k:8s} {v["name"]:8s} ({v["tx"]})')
print('美股标的:')
for k,v in q.US_UNIVERSE.items(): print(f' {k:6s} {v["name"]:12s} ({v["tx"]})')
A股标的: CSI300 沪深300指数 (sh000300) 600519 贵州茅台 (sh600519) 000858 五粮液 (sz000858) 600036 招商银行 (sh600036) 601318 中国平安 (sh601318) 000333 美的集团 (sz000333) 美股标的: SPY 标普500 ETF (usSPY.AM) QQQ 纳指100 ETF (usQQQ.OQ) AAPL 苹果 (usAAPL.OQ) MSFT 微软 (usMSFT.OQ)
3. 取数 + 缓存(容错降级)¶
取数逻辑(qr_data.get_daily):
- 命中
data/缓存 CSV → 直接用(离线可复现); - 否则尝试任务指定源(A股 akshare / 美股 yfinance);
- 失败降级腾讯行情(美股自动回溯拆股复权);
- 每条序列过
sanity_check(拒绝单日涨跌>45%/非正价格,挡住复权错误)。
本机实测:Yahoo 周期性 429、eastmoney 后端被封——这正是容错链存在的意义。首次运行会落地 CSV,此后完全离线。
data = q.load_all(start='2014-01-01', end='2024-12-31', verbose=True)
print('\n标的数:', len(data))
[cache] A/CSI300 沪深300指数: 2676 行 [cache] A/600519 贵州茅台: 2676 行 [cache] A/000858 五粮液: 2616 行 [cache] A/600036 招商银行: 2671 行 [cache] A/601318 中国平安: 2675 行 [cache] A/000333 美的集团: 2614 行 [cache] US/SPY 标普500 ETF: 2767 行 [cache] US/QQQ 纳指100 ETF: 2767 行 [cache] US/AAPL 苹果: 2767 行 [cache] US/MSFT 微软: 2767 行 标的数: 10
4. 数据质量体检¶
逐标的核对:行数、起止日、缺失值、最大单日涨跌(A股应≈±10%涨跌停、美股个股略宽)、数据来源。
rows=[]
for k,df in data.items():
r=df['close'].pct_change()
rows.append(dict(标的=k, 行数=len(df), 起=str(df.index.min().date()), 止=str(df.index.max().date()),
缺失=int(df[['open','high','low','close']].isna().sum().sum()),
最大单日=f'{r.abs().max():.1%}', 年化=f'{r.mean()*252:.1%}', 来源=df.attrs['source']))
qa=pd.DataFrame(rows).set_index('标的')
qa
| 行数 | 起 | 止 | 缺失 | 最大单日 | 年化 | 来源 | |
|---|---|---|---|---|---|---|---|
| 标的 | |||||||
| CSI300 | 2676 | 2014-01-02 | 2024-12-31 | 0 | 8.7% | 7.4% | cache |
| 600519 | 2676 | 2014-01-02 | 2024-12-31 | 0 | 10.0% | 32.1% | cache |
| 000858 | 2616 | 2014-01-02 | 2024-12-31 | 0 | 10.0% | 30.6% | cache |
| 600036 | 2671 | 2014-01-02 | 2024-12-31 | 0 | 10.0% | 20.7% | cache |
| 601318 | 2675 | 2014-01-02 | 2024-12-31 | 0 | 10.0% | 16.6% | cache |
| 000333 | 2614 | 2014-01-02 | 2024-12-31 | 0 | 10.0% | 26.3% | cache |
| SPY | 2767 | 2014-01-02 | 2024-12-31 | 0 | 10.9% | 12.1% | cache |
| QQQ | 2767 | 2014-01-02 | 2024-12-30 | 0 | 12.0% | 19.3% | cache |
| AAPL | 2767 | 2014-01-02 | 2024-12-30 | 0 | 12.9% | 28.4% | cache |
| MSFT | 2767 | 2014-01-02 | 2024-12-30 | 0 | 14.7% | 27.4% | cache |
解读:A股个股最大单日严格卡在 ±10%(涨跌停制度,复权正确的旁证);指数与美股无涨跌停故略宽但均 <15%,无缺失值。数据干净、可用。
5. 归一化净值对比:A股 vs 美股¶
把各标的首日净值归一为 1,直观看十年走势。
import matplotlib.pyplot as plt
fig,axes=plt.subplots(1,2,figsize=(13,4.5))
for k in q.A_UNIVERSE:
s=data[k]['close']; axes[0].plot(s.index, s/s.iloc[0], label=q.A_UNIVERSE[k]['name'], lw=1)
axes[0].set_title('A股标的归一化净值 (2014=1)'); axes[0].legend(fontsize=8); axes[0].set_yscale('log')
for k in q.US_UNIVERSE:
s=data[k]['close']; axes[1].plot(s.index, s/s.iloc[0], label=q.US_UNIVERSE[k]['name'], lw=1)
axes[1].set_title('美股标的归一化净值 (2014=1)'); axes[1].legend(fontsize=8); axes[1].set_yscale('log')
plt.tight_layout(); plt.show()
解读:对数坐标下,白酒(茅台/五粮液)与美股科技(AAPL/MSFT/QQQ)是过去十年最强主线;沪深300指数十年仅约翻倍(年化~7%),指数本身并不性感——这恰恰是后面讨论「主动量化要跑赢什么基准」的关键参照。
6. K线最小示例(mplfinance)¶
import mplfinance as mpf
# mplfinance 的 style 会重置字体,需把 CJK 字体塞回 rc
cjk_style=mpf.make_mpf_style(base_mpf_style='charles',
rc={'font.sans-serif':['Noto Sans CJK SC'],'axes.unicode_minus':False})
sub=data['600519'].loc['2024-01-01':'2024-06-30'][['open','high','low','close','volume']]
mpf.plot(sub, type='candle', volume=True, style=cjk_style,
title='贵州茅台 2024H1 日K', figsize=(11,5), warn_too_much_data=10**6)
findfont: Failed to find font weight semibold, now using 500.
findfont: Failed to find font weight semibold, now using 500.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight semibold, now using 500.
解读:K线只是可视化工具。本研究刻意不做「看图选股」——人眼在历史图上总能找到「规律」,这正是后面 03 要实证的过拟合陷阱来源。
7. 小结¶
- 统一数据层
qr_data跑通:10 个标的、约11年日线、全部缓存、通过质量体检。 - 容错链(官方源→腾讯→缓存→sanity_check)保证离线可复现且挡住脏数据。
- 下一步
01在这套数据上构造常见信号。