在 GitHub 上查看

修正均线突破策略

该策略使用修正均线指标。该指标基于移动平均并结合价格标准差来调整平滑程度,从而减少价格异常波动的影响。

当价格收盘价突破修正均线上方一定点数并回落到突破水平时,策略做多;当价格跌破修正均线下方并回抽到突破水平时,策略做空。止损和止盈以绝对价格点数设置。

参数

  • Candle Type – 用于计算的K线周期。
  • Length – 移动平均和标准差的周期长度。
  • MA Type – 移动平均类型(SMA、EMA、SMMA、LWMA)。
  • Level Points – 与修正均线之间的突破距离(价格步长)。
  • Stop Loss Points – 距离入场价的止损点数。
  • Take Profit Points – 距离入场价的止盈点数。
  • Enable Long – 允许开多单。
  • Enable Short – 允许开空单。

交易逻辑

  1. 计算移动平均和标准差。
  2. 使用前一数值和方差比例构建修正均线。
  3. 当前一根K线收盘价超出修正均线并超过设定阈值时识别突破。
  4. 突破后等待下一根K线回到突破水平并按突破方向入场。
  5. 当出现新的突破信号时平掉相反方向的仓位。
  6. 应用止损和止盈保护。

说明

本策略由MQL脚本 Exp_CorrectedAverage.mq5 转换而来,仅供学习使用,在实盘使用前需进行充分测试。

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 the Corrected Average breakout.
/// Monitors price relative to a corrected moving average and trades on breakouts.
/// </summary>
public class CorrectedAverageBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _length;
	private readonly StrategyParam<int> _levelPoints;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private decimal _prevCorrected;
	private decimal _prevPrevCorrected;
	private decimal _prevClose;
	private decimal _prevPrevClose;
	private bool _isInitialized;
	private decimal _level;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int Length { get => _length.Value; set => _length.Value = value; }
	public int LevelPoints { get => _levelPoints.Value; set => _levelPoints.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public CorrectedAverageBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for calculations", "General");

		_length = Param(nameof(Length), 12)
			.SetDisplay("Length", "Period of moving average", "Indicator");

		_levelPoints = Param(nameof(LevelPoints), 300)
			.SetDisplay("Level Points", "Breakout distance in price steps", "Trading");

		_stopLossPoints = Param(nameof(StopLossPoints), 1000)
			.SetDisplay("Stop Loss Points", "Stop loss in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
			.SetDisplay("Take Profit Points", "Take profit in price steps", "Risk");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCorrected = default;
		_prevPrevCorrected = default;
		_prevClose = default;
		_prevPrevClose = default;
		_isInitialized = default;
		_level = default;
	}

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

		var step = Security.PriceStep ?? 1m;
		_level = LevelPoints * step;

		var ma = new ExponentialMovingAverage { Length = Length };
		var std = new StandardDeviation { Length = Length };

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

		StartProtection(
			new Unit(StopLossPoints * step, UnitTypes.Absolute),
			new Unit(TakeProfitPoints * step, UnitTypes.Absolute));
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		decimal corrected;

		if (!_isInitialized)
		{
			corrected = maValue;
			_isInitialized = true;
		}
		else
		{
			var v1 = stdValue * stdValue;
			var v2 = (_prevCorrected - maValue) * (_prevCorrected - maValue);
			var k = (v2 < v1 || v2 == 0m) ? 0m : 1m - (v1 / v2);
			corrected = _prevCorrected + k * (maValue - _prevCorrected);
		}

		var buySignal = _prevPrevClose > _prevPrevCorrected + _level && _prevClose <= _prevCorrected + _level;
		var sellSignal = _prevPrevClose < _prevPrevCorrected - _level && _prevClose >= _prevCorrected - _level;

		if (buySignal && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (sellSignal && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevPrevCorrected = _prevCorrected;
		_prevPrevClose = _prevClose;
		_prevCorrected = corrected;
		_prevClose = candle.ClosePrice;
	}
}