在 GitHub 上查看

带标准差的 Color XdinMA 策略

该策略是 MQL5 专家 Exp_ColorXdinMA_StDev 的 StockSharp 移植版。 它将两个移动平均线组合成名为 XdinMA 的单一曲线,并跟踪其随时间的变化。 当前与前一个 XdinMA 值之差与其近期标准差的倍数进行比较。 当该差值超过正阈值时开多仓,低于负阈值时开空仓。

工作原理

  1. 计算两个简单移动平均线:
    • Main MA – 由 MainLength 定义的周期。
    • Plus MA – 由 PlusLength 定义的周期。
  2. 构建自定义线 XdinMA = 2 * MainMA - PlusMA
  3. 将连续两根 K 线之间的 XdinMA 变化传递给周期为 StdPeriod 的标准差指标。
  4. 当变化值大于 K1 * StdDev 时下达买入订单;当变化值小于 -K1 * StdDev 时下达卖出订单,开仓前会关闭已有的反向仓位。

参数

参数 说明
MainLength 主移动平均线的周期
PlusLength 次移动平均线的周期
StdPeriod 计算标准差所使用的柱数
K1 偏差阈值的倍数
K2 预留,用于未来扩展第二个过滤器

所有参数均通过 StrategyParam 暴露,可在界面中修改或用于优化。

注意事项

  • 仅处理已完成的 K 线。
  • 策略使用市价单,不包含止损或止盈逻辑。
  • 图表会显示移动平均线和成交记录,方便视觉分析。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on difference of two moving averages with standard deviation filter.
/// Buys when xdin change exceeds K1*StdDev, sells when below -K1*StdDev.
/// </summary>
public class ColorXdinMAStDevStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _mainLength;
	private readonly StrategyParam<int> _plusLength;
	private readonly StrategyParam<int> _stdPeriod;
	private readonly StrategyParam<decimal> _k1;

	private StandardDeviation _stdDev;
	private decimal? _prevXdin;

	public ColorXdinMAStDevStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle type", "Type of candles", "General");

		_mainLength = Param(nameof(MainLength), 10)
			.SetDisplay("Main MA Length", "Length of primary moving average", "Parameters");

		_plusLength = Param(nameof(PlusLength), 20)
			.SetDisplay("Plus MA Length", "Length of secondary moving average", "Parameters");

		_stdPeriod = Param(nameof(StdPeriod), 9)
			.SetDisplay("StdDev Period", "Period for standard deviation of MA changes", "Parameters");

		_k1 = Param(nameof(K1), 0.5m)
			.SetDisplay("Filter K1", "Multiplier for standard deviation filter", "Parameters");
	}

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

	public int MainLength
	{
		get => _mainLength.Value;
		set => _mainLength.Value = value;
	}

	public int PlusLength
	{
		get => _plusLength.Value;
		set => _plusLength.Value = value;
	}

	public int StdPeriod
	{
		get => _stdPeriod.Value;
		set => _stdPeriod.Value = value;
	}

	public decimal K1
	{
		get => _k1.Value;
		set => _k1.Value = value;
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevXdin = null;
		_stdDev = null;
	}

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

		_prevXdin = null;
		_stdDev = new StandardDeviation { Length = StdPeriod };
		Indicators.Add(_stdDev);

		var mainMa = new ExponentialMovingAverage { Length = MainLength };
		var plusMa = new ExponentialMovingAverage { Length = PlusLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(mainMa, plusMa, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, mainMa);
			DrawIndicator(area, plusMa);
			DrawOwnTrades(area);
		}
	}

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

		// xdin = extrapolated price using MA difference
		var xdin = mainValue * 2m - plusValue;

		if (_prevXdin is null)
		{
			_prevXdin = xdin;
			return;
		}

		var change = xdin - _prevXdin.Value;
		_prevXdin = xdin;

		var stdResult = _stdDev.Process(new DecimalIndicatorValue(_stdDev, change, candle.ServerTime) { IsFinal = true });
		if (!_stdDev.IsFormed)
			return;

		var stDev = stdResult.ToDecimal();
		if (stDev == 0)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var filter = K1 * stDev;

		if (change > filter && Position <= 0)
			BuyMarket();
		else if (change < -filter && Position >= 0)
			SellMarket();
	}
}