在 GitHub 上查看

Plan X 突破策略

Plan X 突破策略复刻了 Peter Ingram 编写的 MetaTrader 智能交易系统“plan x”。策略专注于欧洲上午时段,跟踪 15 分钟 K 线。当会话开始时记录一根参考蜡烛的收盘价,随后等待价格向上或向下突破指定的点数,并只持有一个净头寸。止损与跟踪止损均按点数计算,以控制风险。

交易逻辑

  1. 会话锚点

    • 使用 15 分钟蜡烛。
    • 在会话起始小时(默认 11:00)记录该蜡烛的收盘价,作为当天剩余时间的锚点价格。
    • 只有在至少再收出一根蜡烛之后且会话结束之前(默认 15:00)才会寻找信号。
  2. 入场条件

    • 做多:最新一根完成的蜡烛收盘价高于锚点价格 LongTargetPips(默认 25 点)以上,且当前没有持仓。
    • 做空:最新一根完成的蜡烛收盘价低于锚点价格 ShortTargetPips(默认 20 点)以下,且当前没有持仓。
    • 所有比较都基于按合约最小变动和小数位数推算出的点值。
  3. 仓位管理

    • 开仓后立即设置 InitialStopPips(默认 25 点)的固定止损。
    • 当浮动盈利达到 TrailTriggerPips(默认 10 点)时,止损开始跟踪价格。
    • 之后每当价格再前进 TrailTriggerPips,止损就按照 TrailStepPips(默认 5 点)向盈利方向移动。
    • 一旦价格触发止损,立即以市价单平仓。
  4. 下单手数

    • 市价单的手数由 TradeVolume(默认 0.1 手)参数决定,可根据交易标的调整。

参数

名称 说明 默认值
TradeVolume 入场与平仓使用的市价单手数。 0.1
LongTargetPips 做多信号相对锚点需要突破的点数。 25
ShortTargetPips 做空信号相对锚点需要突破的点数。 20
InitialStopPips 入场价到保护性止损的距离。 25
TrailTriggerPips 触发或推动跟踪止损所需的盈利点数。 10
TrailStepPips 跟踪止损每次移动的点数。 5
SessionStartHour 会话开始时间,使用十进制小时(例如 11.5 = 11:30)。 11.0
SessionEndHour 会话结束时间,必须大于 SessionStartHour 15.0
CandleType 用于评估的蜡烛类型,默认 15 分钟。 15 分钟

说明

  • 点值根据标的的 PriceStep 与小数位自动计算(3 或 5 位小数会乘以 10)。
  • 每个交易日都会重新确定锚点价格;若标的存在跳空,请注意价差变化。
  • StockSharp 使用净持仓模式,因此策略一次只会持有一个方向,与原始策略在未启用对冲时的行为一致。

文件

  • CS/PlanXBreakoutStrategy.cs – StockSharp 平台上 Plan X 策略的 C# 实现。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Plan X Breakout strategy using highest high / lowest low channel breakout.
/// Buy when price breaks above the highest high of the lookback period.
/// Sell when price breaks below the lowest low of the lookback period.
/// </summary>
public class PlanXBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _lookback;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevHigh;
	private decimal _prevLow;
	private bool _hasPrev;

	public int Lookback { get => _lookback.Value; set => _lookback.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public PlanXBreakoutStrategy()
	{
		_lookback = Param(nameof(Lookback), 20)
			.SetDisplay("Lookback", "Channel lookback period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevHigh = 0m;
		_prevLow = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var highestHigh = new Highest { Length = Lookback };
		var lowestLow = new Lowest { Length = Lookback };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highestHigh, lowestLow, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevHigh = highest;
			_prevLow = lowest;
			_hasPrev = true;
			return;
		}

		// Breakout above previous highest high
		if (Position <= 0 && candle.ClosePrice > _prevHigh)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Breakout below previous lowest low
		else if (Position >= 0 && candle.ClosePrice < _prevLow)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevHigh = highest;
		_prevLow = lowest;
	}
}