在 GitHub 上查看

Color HMA StDev

基于 Hull 移动平均线并结合标准差过滤的策略。

系统监控价格偏离 HMA 的程度。当收盘价突破平均线并超过设定的标准差倍数时开多仓,反之开空仓。 较大的倍数定义退出区间,因此只有在价格明显回到区间内后才会平仓。

这种方法旨在捕捉快速的动量爆发,同时避免噪音。Hull 移动平均线对趋势变化反应迅速,标准差则根据 波动性自动扩展阈值,使策略能在动荡市场中适应变化。策略可双向交易,并不使用固定止损,依靠价格回归 到 HMA 作为退出依据。

细节

  • 入场条件:收盘价突破 HMA ± K1 * StdDev。
  • 多空方向:双向交易。
  • 出场条件:收盘价突破相反方向的 HMA ± K2 * StdDev。
  • 止损:无固定止损或止盈。
  • 默认参数
    • HmaPeriod = 13
    • StdPeriod = 9
    • K1 = 1.5m
    • K2 = 2.5m
    • CandleType = TimeSpan.FromHours(4)
  • 过滤器
    • 分类:趋势,波动性
    • 方向:双向
    • 指标:HMA,标准差
    • 止损:无
    • 复杂度:中等
    • 时间框架:4 小时
    • 季节性:否
    • 神经网络:否
    • 背离:否
    • 风险等级:中等
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on Hull Moving Average with standard deviation filter.
/// Opens positions when price deviates from the HMA by a defined multiplier.
/// </summary>
public class ColorHmaStDevStrategy : Strategy
{
	private readonly StrategyParam<int> _hmaPeriod;
	private readonly StrategyParam<int> _stdPeriod;
	private readonly StrategyParam<decimal> _k1;
	private readonly StrategyParam<DataType> _candleType;

	public int HmaPeriod { get => _hmaPeriod.Value; set => _hmaPeriod.Value = value; }
	public int StdPeriod { get => _stdPeriod.Value; set => _stdPeriod.Value = value; }
	public decimal K1 { get => _k1.Value; set => _k1.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ColorHmaStDevStrategy()
	{
		_hmaPeriod = Param(nameof(HmaPeriod), 13)
			.SetDisplay("HMA Period", "Hull Moving Average period", "Indicators")
			.SetOptimize(5, 30, 2);

		_stdPeriod = Param(nameof(StdPeriod), 9)
			.SetDisplay("StdDev Period", "Standard deviation period", "Indicators")
			.SetOptimize(5, 20, 1);

		_k1 = Param(nameof(K1), 0.5m)
			.SetDisplay("Entry Multiplier", "Deviation multiplier for entry", "Parameters")
			.SetOptimize(0.5m, 3m, 0.5m);

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

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

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

		var hma = new HullMovingAverage { Length = HmaPeriod };
		var std = new StandardDeviation { Length = StdPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (stdValue == 0)
			return;

		var upperEntry = hmaValue + K1 * stdValue;
		var lowerEntry = hmaValue - K1 * stdValue;

		if (candle.ClosePrice > upperEntry && Position <= 0)
			BuyMarket();
		else if (candle.ClosePrice < lowerEntry && Position >= 0)
			SellMarket();
	}
}