凌晨 2 点,你刚把策略回测跑完。收益率 47%,夏普比率 3.2。准备睡觉前看了一眼资金曲线——漂亮。

第二天早上醒来:收益率 12%,夏普 1.4。

你以为 BUG 在代码里。实际 BUG 在你的金融知识里。

这是程序员转量化最常踩的坑:代码写得对,数据理解错了。 前复权?后复权?不复权?为什么回测里"精准"买入的位置,实盘根本成交不了?为什么"分红送股"会让历史价格突然打折?

金融概念不需要你读完一个金融学位。但有 5 个概念,如果你跳过它们,回测系统从第一天起就在骗你。


程序员转量化的金融知识速成指南:你只需要懂这 5 个概念

一、先说清楚:为什么程序员最容易在这里翻车

程序员写代码,习惯"非黑即白"。一个函数要么返回 200,要么返回 404。

金融数据不讲这个道理。

同一只股票,历史价格可能同时有三个数字:前复权价、后复权价、不复权价。三个都是对的,但三个适合的场景完全不同。你选错了,回测结果就是空中楼阁。

这不是智商问题,是认知框架的错位。程序员不熟悉金融数据的"测量误差",就像金融人不理解为什么代码要写单元测试。

下面这 5 个概念,每个都对应一个程序员式的"理解为什么":


二、概念一:复权——历史价格的"汇率换算"

2.1 问题是怎么来的

假设苹果公司(AAPL)五年前股价是 $50,今天是 $220。如果你直接画一张 K 线图,会看到一条陡峭的上涨曲线——好像买了就赚。

但这个图漏了一个关键信息:AAPL 在这五年里进行了 2 次拆股(1 拆 4),还分了 12 次股息。

不处理这些事件,直接用"原始价格"画图,就相当于把人民币和美元混在一起画折线图——数字上去了,但实际上没有可比性。

复权,就是给历史价格做一个"汇率换算",让不同时间点的价格可以放在同一个坐标系里比较。

2.2 前复权 vs 后复权:两种换算方向

类型 换算方向 视觉表现 适用场景
前复权 把历史价格往"今天"靠 历史价格变小 最常用,技术分析、策略回测
后复权 把今天价格往"历史"靠 今天价格变大 长期价值投资、股息再投入分析

前复权的逻辑:以现在的价格为准,向下调整历史价格。 就像把所有年份的 GDP 都换算成今天美元计价,让你一眼看出增长曲线。

# 模拟复权的原理(简化演示,不含完整除权因子计算)
def forward_adjust_price(raw_prices, split_ratio_history):
    """
    前复权:将历史价格向下调整
    原理:除权因子 = 拆分后股价 / 拆分前股价
    前复权价 = 原始价 × 除权因子累积值
    """
    adjusted = []
    cumulative_factor = 1.0
    
    # 逆序处理,从最近到最远
    for price, split_ratio in zip(reversed(raw_prices), reversed(split_ratio_history)):
        # 每遇到一次拆股,累积因子乘以拆股比例
        cumulative_factor *= split_ratio
        # 前复权价 = 当前价 / 当时累积因子(简化)
        adjusted.append(price * cumulative_factor)
    
    return list(reversed(adjusted))


# 真实场景中,TickDB 的 kline 接口直接返回已复权数据
import os
import requests

# TickDB 获取前复权 K 线数据
# interval: 1d=日线, 1h=小时线; adjust=forward 前复权, backward 后复权
response = requests.get(
    "https://api.tickdb.ai/v1/market/kline",
    headers={"X-API-Key": os.environ.get("TICKDB_API_KEY")},
    params={
        "symbol": "AAPL.US",
        "interval": "1d",
        "limit": 500,
        "adjust": "forward"  # 前复权,这是回测的标准选择
    },
    timeout=(3.05, 10)
)

data = response.json()
if data.get("code") == 0:
    klines = data["data"]
    print(f"最近收盘价(前复权): {klines[-1]['close']}")

实战要点:回测用前复权。这是业界标准。选错了,你的收益曲线会被人为放大或缩小。

2.3 为什么不复权也有用

不复权的 K 线反映的是真实市场报价,包括跳空缺口。在分析某些事件(财报、收购)造成的真实价格冲击时,不复权数据反而更准确——它告诉你市场实际看到了什么价格。

但回到策略回测:永远用复权数据


三、概念二:除息——你"免费得到"的东西,其实有代价

3.1 除息是什么

公司分红或派息时,会从股价里扣除相应金额。这天叫除息日(Ex-Dividend Date)

假设 AAPL 股价 $180,公司宣布每股分红 $0.25。除息日当天开盘价理论上会是 $179.75。账面上你"得到"了 $0.25,实际上你的股票市值少了 $0.25——总资产没变。

除息不是"发钱",是"把你的钱从左口袋挪到右口袋"。 只不过左口袋(股价)减少了,右口袋(现金)增加了。

3.2 为什么这影响你的回测

如果你的策略是"均线金叉买入,死叉卖出",用不复权数据回测时会看到大量假的"跳空下跌"。这些不是市场真的跌了,而是除息造成的系统显示。

# 演示除息对价格的影响
def simulate_dividend_impact():
    """
    场景:策略在除息日前一天收盘买入
    第二天开盘价会因为除息而"低开"
    """
    pre_dividend_price = 180.00  # 除息日前一天收盘价
    dividend_per_share = 0.25   # 每股分红
    
    # 不复权:第二天开盘直接看到缺口
    raw_open_next_day = pre_dividend_price - dividend_per_share  # 179.75
    apparent_drop = (raw_open_next_day - pre_dividend_price) / pre_dividend_price
    print(f"不复权视角:下跌 {apparent_drop*100:.3f}%")  # -0.139%
    
    # 前复权:价格被向上调整,缺口被消除
    # 此时你看到的"下跌"才是真正的市场波动
    adjusted_open = raw_open_next_day  # 前复权后价格已对齐
    real_drop = (adjusted_open - pre_dividend_price) / pre_dividend_price
    print(f"前复权视角:下跌 {real_drop*100:.3f}%")
    
    return {
        "raw_scenario": apparent_drop * 100,
        "adjusted_scenario": real_drop * 100
    }

simulate_dividend_impact()
# 输出:
# 不复权视角:下跌 -0.139%
# 前复权视角:下跌 0.000%(实际上没有下跌)

3.3 一个更隐蔽的坑:股息再投入

很多量化策略会假设"分红是额外收益"。但如果你不做股息再投入,分红后你的总资产几乎不变——除了税务损耗。

回测系统如果把分红简单加到收益里,会高估策略的收益率。正确的做法是:要么把分红视为现金保留(不计入收益),要么模拟股息再投入并使用前复权价格。


四、概念三:做空——高维世界的"镜像操作"

4.1 从"买入-持有"到"双向操作"

程序员学做空,最容易的类比是指针和反指针

买入(Long):看涨 → 低价买,高价卖 → 利润 = 卖出价 - 买入价

做空(Short):看跌 → 高价借,低价还 → 利润 = 借出价 - 归还价

做空本质是借券卖出。你向券商借股票,当前价格(比如 $100)卖出,等股价跌到 $80 再买回来还回去。利润就是 $20。

4.2 做空的三个"程序员式"理解

① 收益曲线是镜像的,但不是对称的

Long 盈亏曲线:      Short 盈亏曲线:
    /                 \
   /                   \
--o-------           ---o--------
  买入价                 借出价

区别在于:做空的潜在亏损是无限的。股价可以跌到 0(你最多赚 100%),但理论上可以涨到无穷(你需要无限资金来补仓)。

② 保证金机制——内存溢出风险

做空需要保证金,就像程序运行需要内存。股价涨了,你需要追加保证金(Margin Call)。如果保证金不够,券商强制平仓(Force Liquidation)——不管你愿不愿意,系统直接帮你买回来还券。

# 简化的做空保证金计算
class ShortPosition:
    def __init__(self, entry_price, shares, margin_ratio=0.5):
        self.entry_price = entry_price
        self.shares = shares
        self.initial_margin = entry_price * shares * margin_ratio
        
        # 维持保证金率,跌破后触发 Margin Call
        self.maintenance_margin_ratio = 0.25
    
    def current_value(self, current_price):
        """空头持仓市值(负数表示卖出获得现金)"""
        return current_price * self.shares
    
    def margin_ratio(self, current_price):
        """当前保证金率"""
        equity = self.initial_margin + (self.entry_price - current_price) * self.shares
        liability = current_price * self.shares
        return equity / liability if liability > 0 else float('inf')
    
    def check_margin_call(self, current_price):
        ratio = self.margin_ratio(current_price)
        if ratio < self.maintenance_margin_ratio:
            print(f"⚠️ MARGIN CALL! 当前保证金率 {ratio:.2%} < 维持率 {self.maintenance_margin_ratio:.2%}")
            print(f"   需要补足至 ${self.initial_margin * self.maintenance_margin_ratio:.2f}")
        return ratio >= self.maintenance_margin_ratio


# 测试场景
pos = ShortPosition(entry_price=100, shares=100, margin_ratio=0.5)
print(f"初始保证金: ${pos.initial_margin:.2f}")

# 股价涨到 150,保证金率危险
ratio = pos.check_margin_call(current_price=150)
print(f"股价 150 时保证金率: {ratio:.2%}\n")
# ⚠️ MARGIN CALL! 当前保证金率 16.67% < 维持率 25.00%

③ 卖空费用(Short Interest Rate)——持续的内存占用费

做空借券不是免费的。券商会收取年化利息,通常在 0.25% - 50% 不等。热门做空标的(比如 GameStop 这种散户逼空股)做空利率可以飙升到三位数。

回测系统如果忽略做空成本,会严重高估做空策略的夏普比率。

4.3 什么时候必须懂做空

  • 统计套利(多空配对交易):同时做多低估标的、做空高估标的
  • 事件驱动(做空财务造假公司):参考浑水(Muddy Waters)模式
  • 对冲(保护多头头寸):下跌市场中减少损失

五、概念四:期权——给股票买"保险",还是给自己买"彩票"

5.1 期权的两个维度

期权(Option)有两个核心维度,每个维度有两种状态:

维度一:方向

  • 看涨期权(Call):有权在约定价格买股票
  • 看跌期权(Put):有权在约定价格卖股票

维度二:角色

  • 买方(Holder):付出权利金,获得权利(理论上收益无限、损失有限)
  • 卖方(Writer):收取权利金,承担义务(理论上收益有限、损失无限)

四个基本组合:

组合 预期方向 风险特征
买入 Call 大涨 损失上限为权利金
买入 Put 大跌 损失上限为权利金
卖出 Call 不涨或小跌 潜在损失无限
卖出 Put 不跌或小涨 潜在损失接近无限

5.2 为什么程序员要理解期权

不是因为你要"炒期权"。而是因为期权数据里藏着市场对未来价格的预期

隐含波动率(IV) 是期权定价反推出来的市场波动率预期。这是量化交易中最重要的情绪指标之一。

# 演示期权链的基本结构(概念层,不含实际定价模型)
def parse_option_chain(ticker, expiry_date, underlying_price=100):
    """
    期权链:同一到期日的所有 Call 和 Put 合约
    Strike Price(行权价):约定的买卖价格
    """
    strikes = [85, 90, 95, 100, 105, 110, 115]  # 行权价序列
    
    option_chain = {
        "symbol": ticker,
        "expiry": expiry_date,
        "underlying_price": underlying_price,
        "calls": {},
        "puts": {}
    }
    
    for strike in strikes:
        # ATM(平值):行权价 ≈ 当前价
        moneyness = "ITM" if strike < underlying_price else ("OTM" if strike > underlying_price else "ATM")
        
        option_chain["calls"][strike] = {
            "moneyness": moneyness,
            "type": "call"
        }
        option_chain["puts"][strike] = {
            "moneyness": moneyness,
            "type": "put"
        }
    
    return option_chain


chain = parse_option_chain("AAPL", "2026-04-18", underlying_price=180.25)
print("=== AAPL 期权链(部分)===")
print(f"标的现价: ${chain['underlying_price']}")
print(f"到期日: {chain['expiry']}\n")

for strike, info in chain["puts"].items():
    print(f"  Put  ${strike} [{info['moneyness']}]")
print("  ───── ATM ─────")
for strike, info in chain["calls"].items():
    print(f"  Call ${strike} [{info['moneyness']}]")

5.3 一个期权数据的实际应用

期权 Skew(波动率偏斜)反映市场对未来下跌的恐惧程度。当 Skew 急剧上升时,往往意味着市场正在定价"黑天鹅"风险——这本身就是量化信号。

理解期权,不需要你会定价,但需要你知道它代表什么。


六、概念五:期货——带杠杆的"未来之约"

6.1 期货的本质

期货(Futures)本质上是一个标准化合约:约定在未来的某个时间、以某个价格、交割某种资产。

你不需要"买"期货,你需要"开仓"——承诺在到期时履行交割义务(或在到期前平仓了结)。

期货的三大核心机制

机制 含义 程序员类比
杠杆(Leverage) 只需缴纳保证金(约 5-10%)就能控制 100% 的合约价值 保证金交易 = 用 1 万本金撬动 10 万仓位
每日结算(Mark to Market) 每天根据结算价计算盈亏,直接从保证金账户扣除 日终刷新账户余额
到期交割/平仓 可以实物交割,也可以到期前对冲平仓 close() 方法释放仓位

6.2 期货价格的"预期陷阱"

期货价格 ≠ 现货价格。

期货价格 = 现货价格 + 持有成本(Carry Cost)- 持有收益(Convenience Yield)

这叫持有成本模型(Cost of Carry Model)

def futures_price(spot_price, risk_free_rate, time_to_expiry, dividend_yield=0):
    """
    期货定价基准公式
    F = S × e^((r - q) × T)
    
    其中:
    S = 现货价格
    r = 无风险利率
    q = 股息收益率(持有股票时相当于"减分")
    T = 到期时间(年)
    """
    import math
    F = spot_price * math.exp((risk_free_rate - dividend_yield) * time_to_expiry)
    return F


# 示例:原油期货
# WTI 原油现货 $80/桶,3 个月期货
spot = 80.0
rate = 0.0525    # 当前美国国债利率约 5.25%
expiry = 0.25    # 3 个月 = 0.25 年

fair_futures = futures_price(spot, rate, expiry)
print(f"原油现货: ${spot}")
print(f"3 个月期货理论价: ${fair_futures:.2f}")
print(f"基差: ${fair_futures - spot:.2f}")
# 输出:期货升水约 $1.04,反映资金成本

6.3 期货在量化中的应用

  • 商品期货:农产品、金属、能源——策略层面的"CTA"策略主战场
  • 指数期货:IF(沪深 300)、IC(中证 500)——对冲工具
  • 加密货币期货:永续合约——24/7 运行,高杠杆,需要特别注意资金费率(Funding Rate)

七、这 5 个概念怎么用:程序员实用地图

理解了概念之后,更重要的是知道在代码的哪个位置遇到它们

量化系统开发阶段
│
├── 数据获取 ───────────→ 复权(adjust 参数选 forward/backward)
│
├── 因子计算 ───────────→ 除息(分红数据要从事件表里对齐)
│
├── 策略信号 ───────────→ 做空(双向仓位的保证金计算)
│                          期权 IV(作为市场情绪因子输入)
│
├── 持仓管理 ───────────→ 期货保证金(维持保证金率、强制平仓线)
│
└── 业绩归因 ───────────→ 全流程(所有概念综合影响最终收益)

7.1 这 5 个概念的掌握优先级

优先级 概念 理由
⭐⭐⭐ 必懂 复权 不懂这个,你的回测数字全是错的
⭐⭐⭐ 必懂 除息 与复权配合,影响价格数据的准确性
⭐⭐ 建议懂 做空 不做空策略可以先跳过,但理解逻辑有助于看懂市场结构
⭐⭐ 建议懂 期权 理解 IV 和 Skew 即可,不需要会定价
⭐ 了解 期货 有相关策略需求时再深入

优先级判断标准:如果这个概念你没懂,你的回测结果会不会系统性错误?

  • 复权和除息:。这两个直接决定你用的价格数据对不对。
  • 做空:看策略。纯多策略不需要,统计套利必须懂。
  • 期权和期货:看方向。商品 CTA 策略绕不开,股票多因子可以先跳过。

八、结语

回到开头那个场景:你回测跑出来 47% 收益,早上醒来变成 12%。

问题大概率不在代码里,而在数据里。用了不复权的价格,除息没对齐,期货保证金算错了——任何一条都足够让你的"高夏普"变成"高风险"。

程序员最大的优势是调试能力。代码有 BUG,trace 一下;回测有 BUG,先从这 5 个概念查起。

你懂怎么写没有 BUG 的代码。现在,学会读没有 BUG 的数据。


下一步行动

如果你是刚转量化的程序员,建议按这个顺序建立你的金融知识体系:

  1. 复权和除息(两天内搞定,数据层面立刻受益)
  2. 用 TickDB 的前复权 K 线数据重新跑一遍你的回测——你可能会发现结果大变
  3. 理解做空的保证金机制(避免爆仓的第一课)
  4. 了解期权 IV 作为市场情绪信号的含义

如果你已经有策略在跑,用 TickDB 的 adjust=forward 参数重新回测一次,比你优化任何因子参数的收益都大。

获取 TickDB API Key:访问 tickdb.ai,注册后可在控制台一键生成。Python 示例代码参考上方代码块,直接复制运行即可。


风险提示:本文不构成任何投资建议。量化策略存在市场风险,过往回测结果不代表未来收益。期货、期权等衍生品具有高杠杆特性,可能导致本金损失。投资有风险,决策需谨慎。