在 GitHub 上查看

XDPO蜡烛策略

该策略源自 MQL5 专家 Exp_XDPOCandle 的移植。它对开盘价和收盘价进行两次指数移动平均平滑,构建出合成蜡烛。合成蜡烛的颜色(多头、空头或中性)用于驱动交易决策。

策略逻辑

  1. 对每根市场蜡烛执行两次平滑:
    • 第一次使用长度为 FastLength 的 EMA;
    • 第二次对第一次的结果使用长度为 SlowLength 的 EMA。
  2. 若平滑后的收盘价高于开盘价,蜡烛视为多头
  3. 若平滑后的收盘价低于开盘价,蜡烛视为空头
  4. 当出现多头蜡烛且前一根蜡烛非多头时,开多仓;当出现空头蜡烛且前一根蜡烛非空头时,开空仓;
  5. 通过反向市价单自动平掉相反方向的持仓。

参数

名称 描述
FastLength 第一次EMA的长度
SlowLength 第二次EMA的长度
CandleType 使用的蜡烛类型和时间框架

使用方法

  1. 在 StockSharp 环境中将策略附加到某个证券;
  2. 按需调整参数,默认值与原专家设置一致;
  3. 启动策略,它会订阅指定的蜡烛并在颜色变化时进行交易。

说明

  • 风险管理通过默认的 StartProtection() 完成,可根据需要调整 Volume 和其他保护参数;
  • 当前仅提供 C# 版本,尚无 Python 版本。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;



/// <summary>
/// XDPO candle strategy that trades on color changes of double smoothed candles.
/// </summary>
public class XdpoCandleStrategy : Strategy
{
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _emaOpen1;
	private decimal? _emaOpen2;
	private decimal? _emaClose1;
	private decimal? _emaClose2;
	private int? _previousColor;

	/// <summary>
	/// Length of the first exponential moving average.
	/// </summary>
	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	/// <summary>
	/// Length of the second exponential moving average.
	/// </summary>
	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="XdpoCandleStrategy"/>.
	/// </summary>
	public XdpoCandleStrategy()
	{
		_fastLength = Param(nameof(FastLength), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast Length", "Length of the first EMA", "Parameters")
			;

		_slowLength = Param(nameof(SlowLength), 5)
			.SetGreaterThanZero()
			.SetDisplay("Slow Length", "Length of the second EMA", "Parameters")
			;

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

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

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

		_emaOpen1 = _emaOpen2 = _emaClose1 = _emaClose2 = null;
		_previousColor = null;
	}

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

		var warmup = new StockSharp.Algo.Indicators.ExponentialMovingAverage { Length = FastLength };
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(warmup, ProcessCandle)
			.Start();

	}

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

		var open1 = CalcEma(candle.OpenPrice, ref _emaOpen1, FastLength);
		var open2 = CalcEma(open1, ref _emaOpen2, SlowLength);
		var close1 = CalcEma(candle.ClosePrice, ref _emaClose1, FastLength);
		var close2 = CalcEma(close1, ref _emaClose2, SlowLength);

		var color = open2 < close2 ? 2 : open2 > close2 ? 0 : 1;
		var goLong = color == 2 && _previousColor != 2;
		var goShort = color == 0 && _previousColor != 0;

		if (IsFormedAndOnlineAndAllowTrading())
		{
			if (goLong && Position <= 0)
				BuyMarket();
			else if (goShort && Position >= 0)
				SellMarket();
		}

		_previousColor = color;
	}

	private static decimal CalcEma(decimal price, ref decimal? prev, int length)
	{
		var k = 2m / (length + 1m);
		var result = prev.HasValue ? price * k + prev.Value * (1m - k) : price;
		prev = result;
		return result;
	}
}