开头不要重复标题,用场景钩子


你以为写代码是量化最难的环节。

直到你第一次跑回测,发现一只股票的价格在某个时间点突然从 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 对量化系统的影响

如果你在计算收益时没有处理除息,你的回测结果会:

  1. 在除息日出现莫名的“亏损”
  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) 打破了这种单向思维:你先借股票卖掉,等股价跌了再买回来还回去,赚的就是这个差价。

流程如下:

  1. 你向券商借了 100 股,当前价格 50 元,卖出,得到 5000 元现金
  2. 股价跌到 40 元,你花 4000 元买回 100 股
  3. 还给券商,赚了 1000 元

做空盈利 = 借出时价格 - 归还时价格(差价为正即盈利)

3.2 为什么做空对量化很重要

很多量化策略是市场中性的:一部分仓位做多,另一部分仓位做空,对冲掉市场整体波动,只赚取“超额收益”(alpha)。

不做空,你就做不了:

  • 配对交易:做多 A 做空 B,赚两者价差收敛的钱
  • 统计套利:做空高估股票,买入低估股票
  • 事件驱动对冲:在财报发布前做多正预期、做空负预期

3.3 做空的风险:不是对称的

程序员要特别记住:做空的风险和做多是不对称的

做多,你最多亏损 100%(股票跌到 0)。

做空,你的亏损理论上是无限的——因为股票可以无限上涨,而你必须买回来还。

举例:你做空 100 股,价格 50 元,股价涨到 200 元,你亏损 150 × 100 = 15,000 元,比你的初始保证金高出很多。

保证金制度:做空需要你提供保证金,券商会在你的亏损接近保证金时发出追缴通知(Margin Call)。如果不能及时补充,券商会强制平仓——即使当时价格对你非常不利。

3.4 数据获取中的做空思维

当你在 TickDB 获取美股数据时,股票代码默认是正向行情。如果你要做空,你需要:

  1. 确认你的券商支持该股票的做空
  2. 在策略逻辑中明确标注“做空仓位”,不要和做多仓位混淆
  3. 收益计算要区分:做多收益率 = (卖出价 - 买入价) / 买入价;做空收益率 = (卖出价 - 买入价) / 卖出价
# 做空仓位收益计算
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 为什么程序员必须理解期权

期权是量化策略的重要组成部分,原因有三个:

  1. 非线性收益结构:股票的收益是线性的(涨多少赚多少),期权的收益是非线性的(可能以小博大,也可能归零)
  2. 波动率交易:期权价格背后隐含着市场对未来波动率的预期,这是很多量化策略的核心信号
  3. 风险对冲:你可以用期权保护你的股票仓位,限制下跌风险

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 个概念的代码跑一遍,确保数据拉取和基本计算逻辑正确,再进入策略开发阶段。

如果你想亲手实现本文提到的数据获取逻辑

  1. 访问 tickdb.ai 注册(免费,无需信用卡)
  2. 在控制台生成 API Key
  3. 设置环境变量 TICKDB_API_KEY,复制本文代码即可运行

如果你习惯用 AI 辅助开发:在 AI 助手中搜索安装 tickdb-market-data SKILL,让 AI 直接帮你写数据获取代码。


本文不构成任何投资建议。市场有风险,投资需谨慎。