开头不要重复标题,用场景钩子
你以为写代码是量化最难的环节。
直到你第一次跑回测,发现一只股票的价格在某个时间点突然从 150 元变成了 90 元——不是分红,不是拆股,只是“前复权”还是“不复权”的区别。
然后你研究期权数据,发现希腊字母字母表都认识,但 delta、gamma 混在一起分不清到底该用哪个。
你开始怀疑人生:当年学编译原理、操作系统、分布式系统都没这么费劲,怎么几个金融概念就把自己卡住了?
这不是你一个人的问题。
程序员转量化,最大的坑不是代码能力,而是金融常识的盲区。这些常识不是难,而是乱——网上的解释要么太浅,要么太学术,要么互相矛盾。
本文做了第一层筛选:从几十个金融概念里,挑出你必须现在懂的 5 个,和可以边做边学的其他一切。
这 5 个概念,是写代码之前必须搞明白的地基。它们分别是:
- 复权:让你的价格数据不出现断层
- 除息与除权:让你知道利润去了哪
- 做空:让你理解双向盈利的可能
- 期权基础:让你知道什么叫非线性收益
- 期货基础:让你理解杠杆和交割机制
为什么先搞清楚这 5 个
量化的世界里,有两种知识:
第一种:搞错了会导致系统级错误。 比如你不知道复权,跑出来的回测结果会系统性偏差 20% 以上;你不知道除息,模拟交易的持仓成本会永远对不上。这种知识不搞定,后续所有工作都是白搭。
第二种:搞错了只会让你多走弯路。 比如期权定价模型的推导过程、期货价差的期限结构——你可以先会用,再慢慢理解原理。
程序员最擅长的就是先跑起来再优化。所以这篇文章只解决第一类:那些“搞错了一定会出事”的概念。
一、复权:为什么你的价格数据会“断裂”
1.1 问题场景
你在回测系统里拉了一只股票两年的日线数据,从 2019 年到 2021 年。在 2020 年 6 月 1 日,价格从 120 元突然跳到了 45 元——但实际上那段时间没有任何分红、拆股,股票走势看起来很正常。
这不是数据错误,而是你拉的是不复权的价格。
1.2 什么是复权
上市公司会分红、送股、拆股。这些操作发生时,股票的名义价格会发生变化,但实际上持有者的总市值是不变的。
举例:
- 分红:你持有 100 股,每股 100 元,公司决定每股分红 5 元。分红后,股价通常会从 100 元跌到 95 元——你的账户里多了 500 元现金,但股票市值少了 500 元,总资产不变。
- 送股:你持有 100 股,每股 50 元,公司决定每 10 股送 10 股。送股后,你持有 200 股,股价调整为 25 元——总市值不变。
- 拆股(比如 10 拆 1):你持有 100 股,拆股后变成 1000 股,股价除以 10——总市值不变。
不复权的价格会记录这种名义上的跳变。复权后的价格会把历史价格按照当前股本调整,让你看到的是“真实收益率”的连续曲线。
复权分为两种:
- 前复权:以当前价格为基准,往回调整历史价格。历史价格会变低。
- 后复权:以历史价格为基准,往后调整当前价格。当前价格会变高。
大部分量化系统默认使用前复权,因为它保证了你看到的 K 线是连续的,方便计算收益率。
1.3 代码示例:拉取复权数据
import os
import requests
# TickDB 获取前复权日线数据
API_KEY = os.environ.get("TICKDB_API_KEY")
headers = {"X-API-Key": API_KEY}
params = {
"symbol": "AAPL.US", # 苹果股票
"interval": "1d",
"adjust": "qfq", # 前复权 (qfq = 前复权; hfq = 后复权; none = 不复权)
"limit": 500,
"start_time": "2019-01-01",
"end_time": "2021-12-31"
}
response = requests.get(
"https://api.tickdb.ai/v1/market/kline",
headers=headers,
params=params,
timeout=(3.05, 10)
)
if response.status_code == 200:
data = response.json()
if data.get("code") == 0:
klines = data["data"]
print(f"获取到 {len(klines)} 条 K 线数据(前复权)")
for k in klines[:3]:
print(f"时间: {k['timestamp']}, 开盘: {k['open']}, 收盘: {k['close']}")
工程要点:
adjust参数决定是否复权。TickDB 支持qfq(前复权)、hfq(后复权)、none(不复权)- 回测系统必须使用复权数据,否则你的夏普比率会系统性失真
- 前复权和后复权的区别在于基准点,选择时要和实盘成交价对应
二、除息与除权:利润去了哪里
2.1 常见误解
很多程序员以为,分红就是公司白送钱给你——“我持有 1000 股,每股分红 1 元,账户里多了 1000 元,有什么不好?”
这个理解没有完全错,但它忽略了一个关键:分红后,股价会相应下跌。这个下跌叫做除息。
如果不懂这个机制,你会在回测中看到这样的假象:你买了股票,然后它“莫名其妙”跌了 3%,你以为是市场出了问题——其实是除息了,你的账户里同时多了 3% 的现金。
2.2 除息、除权的本质
| 操作 | 发生条件 | 价格调整 | 你的账户变化 |
|---|---|---|---|
| 除息 (XD, Ex-Dividend) | 分红 | 股价扣除每股分红金额 | 现金增加,股价减少,总资产不变 |
| 除权 (XR, Ex-Right) | 送股、配股 | 股价按送股/配股比例调整 | 股份数增加,股价降低,总资产不变 |
关键理解:除息/除权不是损失,是“拆钱”——公司把一部分市值从股票里转移到了现金(或把一股拆成了两股)。
2.3 对量化系统的影响
如果你在计算收益时没有处理除息,你的回测结果会:
- 在除息日出现莫名的“亏损”
- 长期收益率计算偏高(因为你只看股价变化,没算现金分红)
- 与实盘结果产生永久性偏差
正确的收益计算方式:
# 计算真实收益率需要考虑分红再投资
def calculate_total_return(prices, dividends):
"""
prices: 每日收盘价(前复权)
dividends: 每日分红(每股)
"""
total_value = 0
shares = 1.0
cash = 0.0
for i in range(len(prices)):
# 分红再投入
if dividends[i] > 0:
cash += shares * dividends[i]
shares_bought = cash / prices[i]
shares += shares_bought
cash = 0.0
# 当日资产价值
total_value = shares * prices[i] + cash
return total_value / initial_value - 1
实用建议:
- 使用前复权价格可以自动消除名义价格的跳变,但分红记录需要单独处理
- 如果你的回测系统不考虑分红再投资,至少要确认你使用的收益指标是“价格收益”还是“总收益”
三、做空:理解双向盈利的可能性
3.1 什么是做空
程序员习惯的思维是:买低卖高,股票涨了才能赚钱。
做空(Short Selling) 打破了这种单向思维:你先借股票卖掉,等股价跌了再买回来还回去,赚的就是这个差价。
流程如下:
- 你向券商借了 100 股,当前价格 50 元,卖出,得到 5000 元现金
- 股价跌到 40 元,你花 4000 元买回 100 股
- 还给券商,赚了 1000 元
做空盈利 = 借出时价格 - 归还时价格(差价为正即盈利)
3.2 为什么做空对量化很重要
很多量化策略是市场中性的:一部分仓位做多,另一部分仓位做空,对冲掉市场整体波动,只赚取“超额收益”(alpha)。
不做空,你就做不了:
- 配对交易:做多 A 做空 B,赚两者价差收敛的钱
- 统计套利:做空高估股票,买入低估股票
- 事件驱动对冲:在财报发布前做多正预期、做空负预期
3.3 做空的风险:不是对称的
程序员要特别记住:做空的风险和做多是不对称的。
做多,你最多亏损 100%(股票跌到 0)。
做空,你的亏损理论上是无限的——因为股票可以无限上涨,而你必须买回来还。
举例:你做空 100 股,价格 50 元,股价涨到 200 元,你亏损 150 × 100 = 15,000 元,比你的初始保证金高出很多。
保证金制度:做空需要你提供保证金,券商会在你的亏损接近保证金时发出追缴通知(Margin Call)。如果不能及时补充,券商会强制平仓——即使当时价格对你非常不利。
3.4 数据获取中的做空思维
当你在 TickDB 获取美股数据时,股票代码默认是正向行情。如果你要做空,你需要:
- 确认你的券商支持该股票的做空
- 在策略逻辑中明确标注“做空仓位”,不要和做多仓位混淆
- 收益计算要区分:做多收益率 = (卖出价 - 买入价) / 买入价;做空收益率 = (卖出价 - 买入价) / 卖出价
# 做空仓位收益计算
def short_pnl(entry_price, exit_price, position_size):
"""
entry_price: 做空入场价格(借出时的价格)
exit_price: 平仓价格(买回时的价格)
position_size: 股数
"""
return (entry_price - exit_price) * position_size
# 做空收益率(相对于借入时的价值)
def short_return(entry_price, exit_price):
return (entry_price - exit_price) / entry_price
四、期权基础:从线性收益到非线性收益
4.1 期权的本质
期权(Option)是一种权利:你有权在约定的时间,以约定的价格,买入或卖出标的资产。
期权分两种:
- 看涨期权(Call):有权以行权价买入标的
- 看跌期权(Put):有权以行权价卖出标的
期权的购买者付出权利金,获得权利;期权的出售者收取权利金,承担义务。
4.2 为什么程序员必须理解期权
期权是量化策略的重要组成部分,原因有三个:
- 非线性收益结构:股票的收益是线性的(涨多少赚多少),期权的收益是非线性的(可能以小博大,也可能归零)
- 波动率交易:期权价格背后隐含着市场对未来波动率的预期,这是很多量化策略的核心信号
- 风险对冲:你可以用期权保护你的股票仓位,限制下跌风险
4.3 核心概念:行权价、到期日、内在价值、时间价值
对于程序员,理解期权需要抓住四个核心维度:
| 概念 | 定义 | 程序员的类比 |
|---|---|---|
| 行权价(Strike Price) | 约定买卖标的的价格 | 函数签名中的参数值 |
| 到期日(Expiration) | 期权失效的日期 | 程序的 timeout |
| 内在价值(Intrinsic Value) | 如果现在行权,能赚多少 | Call 的内在价值 = max(标的价格 - 行权价, 0) |
| 时间价值(Time Value) | 未来价格波动带来的可能性价值 | 离到期越远,时间价值越高 |
期权价格 = 内在价值 + 时间价值
4.4 简单期权策略示例:获取波动率数据
import os
import requests
# 获取期权链数据(某个标的的所有期权合约)
API_KEY = os.environ.get("TICKDB_API_KEY")
headers = {"X-API-Key": API_KEY}
params = {
"symbol": "AAPL.US",
"expiry": "2025-06-20" # 到期日
}
response = requests.get(
"https://api.tickdb.ai/v1/options/chain",
headers=headers,
params=params,
timeout=(3.05, 10)
)
if response.status_code == 200:
data = response.json()
if data.get("code") == 0:
contracts = data["data"]["contracts"]
# 筛选 Call 和 Put,提取不同行权价的权利金
calls = [c for c in contracts if c["type"] == "call"]
puts = [c for c in contracts if c["type"] == "put"]
print(f"到期日 2025-06-20 的期权链:共 {len(contracts)} 个合约")
print(f"看涨期权(Call)数量: {len(calls)}")
print(f"看跌期权(Put)数量: {len(puts)}")
# 从 ATM(At The Money)附近的 Call 提取隐含波动率
spot_price = data["data"]["spot_price"]
atm_strike = round(spot_price / 5) * 5 # 找到最近的行权价
for c in calls:
if abs(c["strike"] - atm_strike) < 5:
print(f"行权价 {c['strike']} | 权利金 {c['bid']:.2f} - {c['ask']:.2f} | 隐含波动率 {c.get('iv', 'N/A')}")
4.5 需要边做边学的进阶概念
期权世界里还有大量概念你现在不需要精通,但需要在实践中逐步理解:
- 希腊字母(Delta、Gamma、Theta、Vega):衡量期权价格对不同变量的敏感性
- 波动率微笑(Volatility Smile):不同行权价的期权隐含波动率呈现 U 形
- Black-Scholes 模型:期权定价的经典模型,理解它就能理解期权市场的定价逻辑
- 期权组合的希腊字母对冲:如何用多个期权组合消除某些风险
这些概念可以先用起来,再慢慢理解原理——就像你当年学编程时,先会用 Git,再慢慢理解分支模型。
五、期货基础:杠杆与交割机制
5.1 什么是期货
期货(Futures)是一种约定在未来某个时间、以某个价格交割标的资产的合约。
与期权不同,期货的买卖双方都有义务——到期必须交割(或者在到期前平仓)。
期货主要用来:
- 对冲风险:比如航空公司用原油期货锁定燃油成本
- 投机:赚取价格波动的收益
- 套利:利用不同到期日或不同市场间的价差
5.2 程序员最需要理解的两个概念
5.2.1 保证金与杠杆
期货不需要你全额支付标的资产价值。你只需要支付保证金——通常是合约价值的一个比例(比如 10%)。
这意味着你有杠杆——10% 保证金对应 10 倍杠杆。
举例:
- 原油期货当前价格 80 美元/桶,一份合约是 1000 桶,价值 80,000 美元
- 保证金要求 10%,即 8,000 美元就能建仓
- 如果油价涨到 88 美元,你赚了 8,000 美元(翻倍)
- 如果油价跌到 72 美元,你亏了 8,000 美元(本金归零)
杠杆是双刃剑:放大收益,也放大亏损。
5.2.2 交割机制
期货分为两类:
- 实物交割:到期时真的交割标的(原油、铜等商品期货)
- 现金交割:到期时按照标的指数的结算价进行现金结算(指数期货、比特币期货)
大部分散户交易者不会等到交割,会在到期前平仓。但你要理解的是:期货价格和现货价格之间存在收敛关系,这个关系就是套利策略的基础。
5.3 期货数据的特殊处理
期货和股票不同,它有到期日,同一标的的不同月份合约是不同的品种。
import os
import requests
API_KEY = os.environ.get("TICKDB_API_KEY")
headers = {"X-API-Key": API_KEY}
# 获取当前可交易的原油期货合约
params = {
"category": "commodities",
"sub_category": "crude_oil"
}
response = requests.get(
"https://api.tickdb.ai/v1/symbols/available",
headers=headers,
params=params,
timeout=(3.05, 10)
)
if response.status_code == 200:
data = response.json()
futures = [s for s in data["data"] if "期货" in s.get("name", "")]
print(f"原油期货可用合约数量: {len(futures)}")
# 筛选近月合约(主力合约)
for f in futures[:5]:
print(f"品种: {f['symbol']} | 名称: {f['name']} | 到期: {f.get('expiry', 'N/A')}")
注意事项:
- 跨月移仓会产生成本(展期收益/损耗),长期持有期货策略需要处理这个问题
- 商品期货的交易日大多在月初,注意非交易日的数据处理
- 加密货币期货(如 BTC/USDT 期货)通常是永续合约,没有到期日,但有资金费率机制
六、这些可以边做边学
以上 5 个概念,是你写代码之前必须搞明白的。搞明白它们,你的量化系统不会有方向性的错误。
还有一些概念,你现在不需要精通,可以先学会用,在实战中逐步理解:
| 概念 | 建议学习方式 |
|---|---|
| 希腊字母 | 先理解 Delta(标的价格变化对期权的影响),其他三个慢慢来 |
| 波动率计算(Garman-Klass、Parkinson) | 先用简单的收益率标准差,后续再引入更复杂的估算方法 |
| 融资利率/做空利率 | 用到市场中性策略时再深入理解,不同券商、不同股票差异很大 |
| 期权定价模型(Black-Scholes) | 先理解“隐含波动率”的概念,模型细节可以在回测中逐步验证 |
| 产业链传导逻辑 | 这个是行业知识积累,需要阅读研究报告和跟踪行业动态 |
底层逻辑是:代码能跑通、数据能拉准、策略框架能跑起来,这三件事优先。其他概念,在你需要用到的时候再深入。
结语
程序员转量化,最大的优势是工程能力,最快的弯路是金融盲区。
复权、除权、做空、期权、期货——这 5 个概念是入场券,不是终点。它们搞定了,你的回测系统不会系统性失真,你的策略逻辑不会出现方向性错误。
剩下的,都是边做边学。
下一步行动
如果你是程序员,刚接触量化开发:先把这 5 个概念的代码跑一遍,确保数据拉取和基本计算逻辑正确,再进入策略开发阶段。
如果你想亲手实现本文提到的数据获取逻辑:
- 访问 tickdb.ai 注册(免费,无需信用卡)
- 在控制台生成 API Key
- 设置环境变量
TICKDB_API_KEY,复制本文代码即可运行
如果你习惯用 AI 辅助开发:在 AI 助手中搜索安装 tickdb-market-data SKILL,让 AI 直接帮你写数据获取代码。
本文不构成任何投资建议。市场有风险,投资需谨慎。