Python 量化金融市场基础
代码之外,量化交易者必须理解市场本身的规则:有什么资产可以交易、什么时候能交易、价格为什么会跳空、每笔交易要付出多少成本。
这些规则直接决定策略的写法——不考虑 T+1 和涨跌停的回测,结论是不可信的。
金融资产大类
量化交易的对象是各种金融资产,常见的有以下几类:
| 资产 | 是什么 | 风险水平 |
|---|---|---|
| 股票 | 公司所有权凭证,收益来自价差与分红 | 中高 |
| 债券 | 债务凭证,到期还本付息,价格与利率反向 | 低 |
| 公募基金 / ETF | 一篮子资产的集合投资,ETF 可像股票一样场内交易 | 视底层资产而定 |
| 期货 | 约定未来某时点以某价格买卖的标准化合约,自带杠杆、可双向交易 | 高 |
| 期权 | 买入"在未来以约定价格买卖的权利",买方最大损失为权利金 | 高(卖方风险极大) |
| 外汇 | 不同国家货币之间的兑换交易,全球最大交易市场 | 中高 |
| 加密货币 | 7×24 小时交易、无涨跌停限制、波动极大 | 极高 |
本教程的实例以股票为主,这也是个人量化入门最合适的品种。
A 股交易规则
写策略前先记住 A 股的几条硬规则:
| 规则 | 内容 | 对策略的影响 |
|---|---|---|
| 交易时段 | 工作日 9:30-11:30、13:00-15:00 | 日线策略以"交易日"为单位 |
| T+1 | 当天买入的股票次日才能卖出 | 无法当天买当天卖,日内回转受限 |
| 涨跌停 | 主板 ±10%,创业板/科创板 ±20%,ST 股 ±5% | 涨停买不进、跌停卖不出,回测要模拟 |
| 最小单位 | 买入须为 100 股(1 手)整数倍,卖出可不足 100 股 | 仓位计算要按手取整 |
| 价格最小变动 | 0.01 元 | 限价单价格只能落在 0.01 的网格上 |
其中涨跌停对回测影响最大:跌停日股票"卖不出去",策略的实际执行可能与信号不一致,严谨的回测框架会专门处理这一点。
全球主要市场规则对比
| 规则 | A 股 | 港股 | 美股 | 加密货币 |
|---|---|---|---|---|
| 结算制度 | T+1 | T+2 交易,可日内回转 | T+1 结算,可日内回转 | 实时 |
| 涨跌幅限制 | ±10% / ±20% | 无(有市调机制) | 无(有熔断机制) | 无 |
| 最小单位 | 100 股 | 1 手(各股不同) | 1 股(支持碎股) | 任意数量 |
| 做空 | 融券(受限) | 可卖空 | 可卖空 | 合约可做空 |
| 交易时间 | 4.5 小时/日 | 5.5 小时/日 | 6.5 小时/日 | 7×24 |
提示:市场差异意味着策略不能直接照搬。美股上验证的日内策略拿到 A 股会受 T+1 限制;A 股的涨跌停规则在加密货币市场则完全不存在。跨市场移植策略时,先核对规则表。
复权:回测前的必修课
第 6 篇介绍数据获取时提过复权,这里把它彻底讲清楚。
上市公司分红、送股后,股价会在除权除息日出现人为的"跳空下跌":比如每股分红 25 元,除权日开盘价会直接比前一日收盘价低 25 元左右。
这个跳空不是市场下跌,如果直接用未复权价格计算收益率,每次分红都会被误判成一次暴跌。
复权就是消除这个人为跳空的处理方式,常用两种口径:
| 口径 | 做法 | 用途 |
|---|---|---|
| 前复权 | 以当前价格为基准,向前调整历史价格 | 看图、计算收益率,最常用 |
| 后复权 | 以上市首日为基准,向后调整 | 计算长期真实总回报 |
| 不复权 | 原始价格 | 查看历史真实成交价 |
动手验证:茅台的分红跳空
贵州茅台在 2023 年 6 月 30 日除权除息,每股派现约 25.9 元。对比除权日前后的未复权价与复权价:
实例
# auto_adjust=False 同时返回未复权 Close 与复权 Adj Close
df = yf.download("600519.SS", start="2023-06-01", end="2023-07-15",
auto_adjust=False, progress=False, multi_level_index=False)
print(df[["Close", "Adj Close"]].round(2).to_string())
# 查看分红记录
print(yf.Ticker("600519.SS").dividends.loc["2023"].to_string())
输出结果如下所示(节选除权日前后):
Close Adj Close
Date
2023-06-27 1711.05 1513.83
2023-06-28 1728.38 1529.16
2023-06-29 1713.71 1516.19
2023-06-30 1691.00 1519.06
2023-07-03 1724.10 1548.80
2023-07-04 1730.60 1554.63
2023-06-30 25.911
未复权 Close 在 6 月 30 日从 1713.71 跌到 1691.00,看似下跌 1.3%;而复权价 Adj Close 实际从 1516.19 涨到 1519.06——除权日持有者真实是赚钱的,分红已经落袋。
把 2019 至 2024 年的两条价格曲线画在一起,除权日的大量"假下跌"清晰可见:
实例
import matplotlib.pyplot as plt
df = yf.download("600519.SS", start="2019-01-01", end="2024-12-31",
auto_adjust=False, progress=False, multi_level_index=False)
plt.figure(figsize=(12, 6))
plt.plot(df.index, df["Close"], label="Close (Unadjusted)",
color="#1f2933", linewidth=1.2)
plt.plot(df.index, df["Adj Close"], label="Adj Close (Adjusted)",
color="#14805e", linewidth=1.2)
plt.title("Maotai (600519.SS): Close vs Adj Close 2019-2024")
plt.xlabel("Date")
plt.ylabel("Price (CNY)")
plt.legend()
plt.grid(alpha=0.25)
plt.show()
执行以上代码,输出的图表如下:

两条曲线的裂口都出现在每年 6 月前后的分红除权日。未复权曲线越往后"跌得越多",纯属分红累积的假象。
结论:计算收益率、均线、回测一律使用复权价格;只有查看"历史上真实成交了多少块钱"时才看未复权价。yfinance 用 auto_adjust 参数控制,AKShare 用 adjust="qfq"(前复权)/"hfq"(后复权)控制,详见获取金融数据。
交易成本:策略的隐形对手
回测里每一笔交易都不是免费的,A 股交易的主要成本如下:
| 费用 | 费率 | 收取方向 |
|---|---|---|
| 佣金 | 常见万 2.5 至万 3,多数券商有 5 元起收门槛 | 买卖双边 |
| 印花税 | 0.05% | 仅卖出单边 |
| 过户费 | 0.001% | 买卖双边 |
| 滑点 | 无固定值,取决于流动性 | 隐性成本 |
双边合计约 0.06% 至 0.1%,单边卖出再加 0.05% 印花税。
换个角度看:一次完整的买卖循环大约损耗 0.13% 到 0.16%。年换手 100 次的策略,光成本就要吃掉 13% 到 16% 的收益。
注意:以上费率以券商与交易所最新公告为准。滑点指实际成交价与信号价格的偏差,流动性差的股票或快速波动的行情中滑点可能远超手续费。回测时务必设置与实际一致的费率。
点我分享笔记