配对交易策略

概览

PairsTradingStrategy 是一种基于两种相关工具之间统计套利的配对交易策略。它跟踪两种资产价格之间的价差,并在价差显著偏离均值时开仓,期望价差回归均值。

主要组件

该策略继承自 Strategy 并使用参数进行配置:

public class PairsTradingStrategy : Strategy
{
	private readonly StrategyParam<int> _spreadLength;
	private readonly StrategyParam<decimal> _entryThreshold;
	private readonly StrategyParam<decimal> _exitThreshold;
	private readonly StrategyParam<DataType> _candleType;

	// 每个工具的最新价格
	private decimal? _lastPrice1;
	private decimal? _lastPrice2;
}

策略参数

该策略允许自定义以下参数:

  • SpreadLength - 用于计算点差均值和标准差的周期(默认值20)
  • 入场阈值 - 建仓的 Z 分数阈值(默认值 2.0)
  • 退出阈值 - 用于退出持仓的 Z 分数阈值(默认 0.5)
  • K线类型 - 要使用的K线类型(默认5分钟)

所有参数都可以在指定的取值范围内进行优化。

策略初始化

OnStarted2 方法中,为两个工具创建指标并设置K线订阅:

protected override void OnStarted2(DateTime time)
{
	base.OnStarted2(time);

	// 获取用于配对交易的两个工具
	var securities = GetWorkingSecurities().ToArray();
	if (securities.Length < 2)
		throw new InvalidOperationException("必须指定两个工具。");

	var sec1 = securities[0].sec;
	var sec2 = securities[1].sec;

	// 用于计算价差均值和标准差的指标
	var sma = new SimpleMovingAverage { Length = SpreadLength };
	var stdDev = new StandardDeviation { Length = SpreadLength };

	_lastPrice1 = null;
	_lastPrice2 = null;

	// 订阅第一个工具的K线
	SubscribeCandles(CandleType, security: sec1)
		.Bind(c =>
		{
			if (c.State != CandleStates.Finished)
				return;

			_lastPrice1 = c.ClosePrice;
		})
		.Start();

	// 订阅第二个工具的K线并处理价差
	SubscribeCandles(CandleType, security: sec2)
		.Bind(c =>
		{
			if (c.State != CandleStates.Finished)
				return;

			_lastPrice2 = c.ClosePrice;

			if (_lastPrice1 == null)
				return;

			ProcessSpread(_lastPrice1.Value, _lastPrice2.Value, sma, stdDev);
		})
		.Start();
}

传播处理

ProcessSpread 方法计算利差的 Z 分数并生成交易信号:

private void ProcessSpread(decimal price1, decimal price2,
	SimpleMovingAverage sma, StandardDeviation stdDev)
{
	// 将价差计算为价格差
	var spread = price1 - price2;

	// 处理指标
	var smaValue = sma.Process(new DecimalIndicatorValue(sma, spread));
	var devValue = stdDev.Process(new DecimalIndicatorValue(stdDev, spread));

	if (!sma.IsFormed || !stdDev.IsFormed)
		return;

	if (!IsFormedAndOnlineAndAllowTrading())
		return;

	var mean = smaValue.ToDecimal();
	var dev = devValue.ToDecimal();

	if (dev == 0)
		return;

	// 计算 Z-Score:以标准差为单位的价差偏离均值程度
	var zScore = (spread - mean) / dev;

	// 价差过高:卖出第一个工具,买入第二个工具
	if (zScore > EntryThreshold && Position >= 0)
	{
		SellMarket(Volume + Math.Abs(Position));
	}
	// 价差过低:买入第一个工具,卖出第二个工具
	else if (zScore < -EntryThreshold && Position <= 0)
	{
		BuyMarket(Volume + Math.Abs(Position));
	}
	// 回归均值:平仓
	else if (Math.Abs(zScore) < ExitThreshold && Position != 0)
	{
		ClosePosition();
	}
}

交易逻辑

  • 卖出信号:当没有空头持仓时,利差 Z 分数超过入场阈值(默认 2.0)
  • 买入信号:当没有多头持仓时,价差 Z 分数跌破负入场阈值(默认 -2.0)
  • 平仓:当绝对 Z 分数值降至退出阈值以下(默认 0.5)时,表示价差正在回归均值

特征

  • 该策略适用于通过 GetWorkingSecurities() 方法获得的两种工具
  • 价差是通过两个工具的K线收盘价的差额来计算的
  • Z 分数用于标准化与平均值的偏差分布
  • 该策略实现了经典的均值回归概念
  • 该策略仅适用于已完成的K线
  • 支持参数优化以寻找最佳策略设置