00 · 数据与环境¶

⚠️ 免责声明:本研究为量化/短期交易方法与可行性的探讨。所有回测均为历史模拟,不代表未来收益,不构成任何投资建议。真实交易受流动性、冲击成本、税费、心理与执行误差影响,结果通常更差。量化与短期交易可致重大本金亏损。读者需自行承担一切决策后果。

数据来源:A股 akshare(新浪后端) / 美股 yfinance,限流时降级腾讯行情;全部缓存于 data/*.csv,每个数字均可在本 notebook 单元复现。


本 notebook 负责:统一数据层验证、缓存、中文绘图、最小示例。 后续所有 notebook 复用这里建立的 qr_data(取数+缓存)与 qr_bt(回测)两个模块,保证全研究的数字同源、可离线复现。

1. 环境与依赖¶

In [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年)。

In [2]:
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):

  1. 命中 data/ 缓存 CSV → 直接用(离线可复现);
  2. 否则尝试任务指定源(A股 akshare / 美股 yfinance);
  3. 失败降级腾讯行情(美股自动回溯拆股复权);
  4. 每条序列过 sanity_check(拒绝单日涨跌>45%/非正价格,挡住复权错误)。

本机实测:Yahoo 周期性 429、eastmoney 后端被封——这正是容错链存在的意义。首次运行会落地 CSV,此后完全离线。

In [3]:
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%涨跌停、美股个股略宽)、数据来源。

In [4]:
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
Out[4]:
行数 起 止 缺失 最大单日 年化 来源
标的
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,直观看十年走势。

In [5]:
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()
No description has been provided for this image

解读:对数坐标下,白酒(茅台/五粮液)与美股科技(AAPL/MSFT/QQQ)是过去十年最强主线;沪深300指数十年仅约翻倍(年化~7%),指数本身并不性感——这恰恰是后面讨论「主动量化要跑赢什么基准」的关键参照。

6. K线最小示例(mplfinance)¶

In [6]:
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.
No description has been provided for this image

解读:K线只是可视化工具。本研究刻意不做「看图选股」——人眼在历史图上总能找到「规律」,这正是后面 03 要实证的过拟合陷阱来源。

7. 小结¶

  • 统一数据层 qr_data 跑通:10 个标的、约11年日线、全部缓存、通过质量体检。
  • 容错链(官方源→腾讯→缓存→sanity_check)保证离线可复现且挡住脏数据。
  • 下一步 01 在这套数据上构造常见信号。