Skip to content

4.2 交易成本建模

概念详解

交易成本是连接"纸上策略"与"真实收益"的桥梁。许多策略在忽略成本时看起来利润丰厚,一旦加入合理的成本模型就变得一文不值。交易成本建模的目标是尽可能真实地还原市场摩擦,让回测结果更接近实盘表现。

交易成本可以分为显性成本隐性成本两大类:

显性成本(直接可见的支出):

  • 佣金:券商收取的交易手续费,通常按成交金额的固定比例收取(如万三 = 0.03%)
  • 印花税:政府对交易征收的税费(A股卖出时征收成交金额的0.05%)
  • 交易所规费:交易所收取的经手费、证管费等
  • 过户费:股票过户时收取的费用

隐性成本(不易直接观察但对收益有实质影响):

  • 买卖价差:最优买价与最优卖价之间的差额,是市场微观结构的基本成本
  • 冲击成本:大额订单导致的成交价偏离预期价格
  • 延迟成本:从做出决策到订单执行之间的价格变动
  • 机会成本:未能成交导致的潜在收益损失
名词解释:冲击成本

大额订单导致市场价格向不利方向移动的额外成本,与订单大小和市场深度有关。例如买入100万元的股票时,由于你的买盘推高了价格,实际成交均价可能比你最初看到的价格高出0.1%,这0.1%就是冲击成本。

名词解释:滑点

实际成交价格与预期价格之间的差额。滑点的来源包括买卖价差、市场冲击、以及订单提交到成交之间的价格波动。滑点 = 实际成交价 - 信号生成时的参考价。

名词解释:买卖价差

最优卖价(ask)与最优买价(bid)之间的差额。以市价单买入时,你按卖一价成交;以市价单卖出时,你按买一价成交。单边交易的实际成本约等于半个价差。

数学原理

基础成本模型

单次交易的总成本可以表示为:

Cost=Vccommission+Vctax+Vsspread+VλVADV

其中:

  • V 是成交金额
  • ccommission 是佣金费率
  • ctax 是印花税费率
  • sspread 是买卖价差率
  • λ 是冲击系数
  • ADV 是日均成交额

冲击成本模型(Almgren-Chriss框架)

市场冲击成本通常分为永久冲击临时冲击两部分:

Ipermanent=γσXVsign(X)Itemporary=ησ|XVT|β

其中 σ 是波动率,X 是订单量,V 是日均成交量,T 是执行时间,γ,η,β 是需要标定的参数。

简单冲击估算

对于大多数中低频策略,可使用简化公式:

Icost=kσQADV

其中 Q 是订单股数,ADV 是日均成交量(股),k 是一个常数(通常取0.1~1.0,取决于股票流动性)。

Python实战

📌 案例:加入成本

PYTHON4 行 · 143 B
📄此处有展示代码4 行 · 143 B展开 ▼
python
cost_rate = 0.001
trades = df['position'].diff().abs()
cost = trades * df['Close'] * cost_rate
net_returns = strategy_returns - cost / capital
点击展开可浏览运行结果
📘 本段为代码片段(仅展示函数/类定义,未提供运行入口,无需运行)

📌 案例:完整的成本计算器

PYTHON53 行 · 1.9 KB
📄此处有展示代码53 行 · 1.9 KB展开 ▼
python
class TransactionCostModel:
    """
    完整的A股交易成本模型
    """
    def __init__(self,
                 commission_rate=0.0003,   # 佣金万三
                 stamp_tax_rate=0.0005,    # 印花税千0.5(仅卖出)
                 transfer_fee_rate=0.00002,  # 过户费万0.2
                 min_commission=5.0):       # 最低佣金5元
        self.commission_rate = commission_rate
        self.stamp_tax_rate = stamp_tax_rate
        self.transfer_fee_rate = transfer_fee_rate
        self.min_commission = min_commission

    def calculate_cost(self, price, shares, side, avg_daily_volume=None):
        """
        计算单笔交易的总成本

        Parameters:
            price: 成交均价
            shares: 成交股数(正数)
            side: 'buy' 或 'sell'
            avg_daily_volume: 日均成交量(股),用于估算冲击成本
        """
        value = price * shares

        # 显性成本
        commission = max(value * self.commission_rate, self.min_commission)
        stamp_tax = value * self.stamp_tax_rate if side == 'sell' else 0
        transfer_fee = max(value * self.transfer_fee_rate, 1.0)

        # 隐性成本:买卖价差(假设万二)
        spread_cost = value * 0.0002

        # 隐性成本:冲击成本(简化模型)
        if avg_daily_volume and avg_daily_volume > 0:
            participation_rate = shares / avg_daily_volume
            # 参与率越高,冲击成本越大
            impact_cost = value * (0.1 * np.sqrt(participation_rate))
        else:
            impact_cost = 0

        total_cost = commission + stamp_tax + transfer_fee + spread_cost + impact_cost

        return {
            'commission': commission,
            'stamp_tax': stamp_tax,
            'transfer_fee': transfer_fee,
            'spread_cost': spread_cost,
            'impact_cost': impact_cost,
            'total_cost': total_cost,
            'cost_rate': total_cost / value
        }
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:类 `TransactionCostModel`(完整的A股交易成本模型)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

📌 案例:在回测中集成成本模型

PYTHON53 行 · 1.8 KB
📄此处有展示代码53 行 · 1.8 KB展开 ▼
python
def backtest_with_costs(strategy, data, cost_model):
    """
    带完整成本模型的回测循环
    """
    capital = 1_000_000
    positions = {}
    equity_curve = []
    cost_log = []

    for date, row in data.iterrows():
        # 策略生成目标持仓
        target_positions = strategy.predict(row, positions)

        # 计算所需交易
        current_symbols = set(positions.keys()) | set(target_positions.keys())
        daily_cost = 0

        for sym in current_symbols:
            target_shares = target_positions.get(sym, 0)
            current_shares = positions.get(sym, 0)
            diff = target_shares - current_shares

            if diff != 0:
                side = 'buy' if diff > 0 else 'sell'
                cost_info = cost_model.calculate_cost(
                    price=row['Close', sym],
                    shares=abs(diff),
                    side=side,
                    avg_daily_volume=row.get(('Volume', sym), None)
                )
                daily_cost += cost_info['total_cost']
                cost_log.append({**cost_info, 'date': date, 'symbol': sym})

        # 执行交易并扣除成本
        for sym in current_symbols:
            target_shares = target_positions.get(sym, 0)
            current_shares = positions.get(sym, 0)
            diff = target_shares - current_shares

            if diff != 0:
                capital -= diff * row['Close', sym]  # 支付买入/收入卖出

        capital -= daily_cost  # 扣除交易成本
        positions = {k: v for k, v in target_positions.items() if v != 0}

        # 记录净值
        total_value = capital + sum(
            positions.get(sym, 0) * row['Close', sym]
            for sym in positions
        )
        equity_curve.append({'date': date, 'equity': total_value})

    return pd.DataFrame(equity_curve).set_index('date'), pd.DataFrame(cost_log)
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `backtest_with_costs`(带完整成本模型的回测循环)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

常见误区

  1. 成本比例选择过于乐观:很多回测只用万1或万2的费率,但实际交易中加上冲击成本和滑点,小票的交易成本可能达到千3甚至更高。

  2. 忽略印花税:A股卖出时征收0.05%的印花税。如果你是高换手率策略,这笔费用累积起来非常可观。

  3. 用成交均价代替实际成交价:以收盘价作为成交价的假设非常强。如果你的策略信号需要在盘中确认(如突破信号),收盘价可能与实际入场价相差甚远。

  4. 不考虑流动性约束:小市值股票的日均成交额可能只有几百万,你的策略如果分配了几十万的权重,在实际中根本无法以下单时的价格成交。

  5. 忽略最低佣金:很多券商有最低5元的佣金要求。对于小额交易(如不足1.7万元),实际佣金费率远高于名义费率。

实战练习

  1. 用你的回测框架分别计算以下场景的年化收益差异:无成本 vs 仅佣金(万三) vs 完整成本(佣金+印花税+买卖价差万二)。对于月换手率100%的策略,成本侵蚀了多少收益?

  2. 编写一个流动性过滤器:对于日均成交额小于1000万元的股票,自动从可交易池中剔除。观察过滤前后策略表现的变化。

  3. 模拟冲击成本的非线性特征:假设冲击成本 I=0.1σQ/ADV,其中 σ 取日波动率的2%。计算该策略中每笔交易的冲击成本,找出冲击成本最高的10笔交易。

延伸阅读

  • Almgren, R. & Chriss, N. (2001). "Optimal Execution of Portfolio Transactions." Journal of Risk.
  • Kissell, R. (2014). The Science of Algorithmic Trading and Portfolio Management. Academic Press. 全面讨论交易成本和执行算法。
  • Bouchaud, J. P. et al. (2009). "How Markets Slowly Digest Changes in Supply and Demand." 深入分析冲击成本的微观结构基础。
  • Frazzini, A., Israel, R., & Moskowitz, T. J. (2018). "Trading Costs." 实证分析了实际交易成本的大小。

本章要点

  • 交易成本分为显性成本(佣金、印花税、规费)和隐性成本(买卖价差、冲击成本、延迟成本)。
  • 冲击成本与订单大小非线性相关:订单越大,单位冲击成本越高。核心公式为 I=kσQ/ADV
  • A股市场的交易成本结构:卖出时征收印花税(0.05%),买入不征收;佣金通常在万2~万3之间;还有过户费和规费。
  • 完整的成本建模应该包含最低佣金约束和流动性过滤。
  • 在回测中考虑成本后,许多高换手率策略的预期收益会大幅缩水甚至转负——这是成本建模的价值所在。