在 GitHub 上查看

ColorXvaMA Digit StDev 策略

概述

该策略基于价格相对于指数移动平均线 (EMA) 的偏离程度进行交易。两个偏差倍数(K1 和 K2)定义了由价格标准差计算的内外带。

当价格高于 EMA 且超过 K2 倍标准差时,策略做多;当价格低于 EMA 且超过 K2 倍标准差时,策略做空。持仓在价格回到由 K1 定义的内带内时平仓。

参数

  • EMA Length – EMA 的周期。
  • StdDev Length – 标准差的计算周期。
  • Deviation K1 – 用于平仓的内带倍数。
  • Deviation K2 – 用于开仓的外带倍数。
  • Candle Type – 蜡烛图时间周期。

指标

  • Exponential Moving Average
  • StandardDeviation

工作原理

  1. 订阅所选时间周期的蜡烛图。
  2. 计算价格的 EMA 和标准差。
  3. 计算价格与 EMA 的偏差。
  4. 当偏差超过 ±K2×StdDev 时开仓。
  5. 当偏差回到 ±K1×StdDev 以内时平仓。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that enters positions based on price deviation from EMA and standard deviation.
/// Opens long when price is above EMA by K2*StdDev and short when below by K2*StdDev.
/// Closes positions when deviation returns within K1*StdDev.
/// </summary>
public class ColorXvaMaDigitStDevStrategy : Strategy
{
	private readonly StrategyParam<int> _maLength;
	private readonly StrategyParam<int> _stdLength;
	private readonly StrategyParam<decimal> _k1;
	private readonly StrategyParam<decimal> _k2;
	private readonly StrategyParam<DataType> _candleType;

	/// <summary>
	/// EMA period length.
	/// </summary>
	public int MaLength { get => _maLength.Value; set => _maLength.Value = value; }

	/// <summary>
	/// Standard deviation period.
	/// </summary>
	public int StdLength { get => _stdLength.Value; set => _stdLength.Value = value; }

	/// <summary>
	/// Inner deviation multiplier.
	/// </summary>
	public decimal K1 { get => _k1.Value; set => _k1.Value = value; }

	/// <summary>
	/// Outer deviation multiplier.
	/// </summary>
	public decimal K2 { get => _k2.Value; set => _k2.Value = value; }

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

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public ColorXvaMaDigitStDevStrategy()
	{
		_maLength = Param(nameof(MaLength), 15)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "Period for the exponential moving average", "Parameters");

		_stdLength = Param(nameof(StdLength), 9)
			.SetGreaterThanZero()
			.SetDisplay("StdDev Length", "Period for standard deviation", "Parameters");

		_k1 = Param(nameof(K1), 1.5m)
			.SetDisplay("Deviation K1", "Inner band multiplier", "Parameters");

		_k2 = Param(nameof(K2), 2.5m)
			.SetDisplay("Deviation K2", "Outer band multiplier", "Parameters");

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

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

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

		var ema = new ExponentialMovingAverage { Length = MaLength };
		var std = new StandardDeviation { Length = StdLength };

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, std, ProcessCandle).Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (stdValue == 0m)
			return;

		var deviation = candle.ClosePrice - emaValue;
		var filter1 = K1 * stdValue;
		var filter2 = K2 * stdValue;

		// Open long when price exceeds the upper band
		if (Position <= 0 && deviation > filter2)
		{
			BuyMarket();
		}
		// Open short when price falls below the lower band
		else if (Position >= 0 && deviation < -filter2)
		{
			SellMarket();
		}
		// Close long when price returns inside inner band
		else if (Position > 0 && deviation < filter1)
		{
			SellMarket();
		}
		// Close short when price returns inside inner band
		else if (Position < 0 && deviation > -filter1)
		{
			BuyMarket();
		}
	}
}