在 GitHub 上查看

MACD模式交易者

该策略在MACD的剧烈反转时开仓。它寻找在极小中间值周围出现的两个巨大峰值。当前一根MACD为正且当前值大幅跌入负区时做空;当条件相反时做多。止损和止盈基于最近的高点和低点。

该算法适用于动量快速反转的高波动市场。仅使用市价单,并依据历史K线计算风险水平。

详情

  • 入场条件: 基于 RatioThreshold 的MACD峰值比例。
  • 多空方向: 双向。
  • 退出条件: 在最近极值加偏移处止损或相反峰值出现。
  • 止损: 是。
  • 默认值:
    • FastEmaPeriod = 24
    • SlowEmaPeriod = 13
    • StopLossBars = 22
    • TakeProfitBars = 32
    • OffsetPoints = 40
    • RatioThreshold = 5m
    • CandleType = TimeSpan.FromMinutes(5)
  • 过滤器:
    • 类型: 模式
    • 方向: 双向
    • 指标: MACD
    • 止损: 是
    • 复杂度: 中等
    • 时间框架: 日内 (5m)
    • 季节性: 否
    • 神经网络: 否
    • 背离: 否
    • 风险等级: 中等
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 trades sharp MACD reversals using recent highs and lows for risk.
/// </summary>
public class MacdPatternTraderAllStrategy : Strategy
{
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _stopLossBars;
	private readonly StrategyParam<int> _takeProfitBars;
	private readonly StrategyParam<int> _offsetPoints;
	private readonly StrategyParam<decimal> _ratioThreshold;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _macdPrev;
	private decimal _macdPrev2;
	private decimal _stopLossPrice;
	private decimal _takeProfitPrice;

	/// <summary>
	/// Fast EMA period for MACD.
	/// </summary>
	public int FastEmaPeriod { get => _fastEmaPeriod.Value; set => _fastEmaPeriod.Value = value; }

	/// <summary>
	/// Slow EMA period for MACD.
	/// </summary>
	public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }

	/// <summary>
	/// Number of bars used to calculate stop loss.
	/// </summary>
	public int StopLossBars { get => _stopLossBars.Value; set => _stopLossBars.Value = value; }

	/// <summary>
	/// Number of bars used to calculate take profit.
	/// </summary>
	public int TakeProfitBars { get => _takeProfitBars.Value; set => _takeProfitBars.Value = value; }

	/// <summary>
	/// Offset in points added to stop loss.
	/// </summary>
	public int OffsetPoints { get => _offsetPoints.Value; set => _offsetPoints.Value = value; }

	/// <summary>
	/// Minimal ratio of MACD spikes to previous value.
	/// </summary>
	public decimal RatioThreshold { get => _ratioThreshold.Value; set => _ratioThreshold.Value = value; }

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

	/// <summary>
	/// Initializes a new instance of the <see cref="MacdPatternTraderAllStrategy"/> class.
	/// </summary>
	public MacdPatternTraderAllStrategy()
	{
		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 24)
			.SetDisplay("Fast EMA Period", "Period for fast EMA in MACD", "Indicators")
			
			.SetOptimize(12, 40, 2);

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 13)
			.SetDisplay("Slow EMA Period", "Period for slow EMA in MACD", "Indicators")
			
			.SetOptimize(7, 26, 1);

		_stopLossBars = Param(nameof(StopLossBars), 22)
			.SetDisplay("Stop Loss Bars", "Bars to look back for stop loss", "Risk management")
			
			.SetOptimize(10, 40, 1);

		_takeProfitBars = Param(nameof(TakeProfitBars), 32)
			.SetDisplay("Take Profit Bars", "Bars to look back for take profit", "Risk management")
			
			.SetOptimize(10, 60, 2);

		_offsetPoints = Param(nameof(OffsetPoints), 40)
			.SetDisplay("Offset Points", "Point offset added to stop loss", "Risk management")
			
			.SetOptimize(10, 60, 5);

		_ratioThreshold = Param(nameof(RatioThreshold), 8m)
			.SetDisplay("MACD Ratio", "Minimal ratio of surrounding MACD values", "Signals")
			
			.SetOptimize(3m, 7m, 1m);

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

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

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

		_macdPrev = 0m;
		_macdPrev2 = 0m;
		_stopLossPrice = 0m;
		_takeProfitPrice = 0m;
	}

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

		var macd = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = FastEmaPeriod },
			LongMa = { Length = SlowEmaPeriod }
		};

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

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

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

		// Manage existing position by fixed stop and target
		if (Position > 0)
		{
			if (candle.LowPrice <= _stopLossPrice || candle.HighPrice >= _takeProfitPrice)
			{
				SellMarket(Position);
				_stopLossPrice = 0m;
				_takeProfitPrice = 0m;
			}
		}
		else if (Position < 0)
		{
			if (candle.HighPrice >= _stopLossPrice || candle.LowPrice <= _takeProfitPrice)
			{
				BuyMarket(Math.Abs(Position));
				_stopLossPrice = 0m;
				_takeProfitPrice = 0m;
			}
		}

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_macdPrev2 = _macdPrev;
			_macdPrev = macdValue;
			return;
		}

		var priceStep = Security?.PriceStep ?? 1m;
		var offset = OffsetPoints * priceStep;
		var stopDistance = offset * 2m;
		var takeDistance = offset * 4m;

		var macdCurr = macdValue;
		var macdLast = _macdPrev;
		var macdLast3 = _macdPrev2;

		if (macdLast != 0m)
		{
			var ratio1 = Math.Abs(macdLast3 / macdLast);
			var ratio2 = Math.Abs(macdCurr / macdLast);

			if ((macdLast3 > 0m || macdCurr < 0m) && ratio1 >= RatioThreshold && ratio2 >= RatioThreshold && Position >= 0)
			{
				var sl = candle.ClosePrice + stopDistance;
				var tp = candle.ClosePrice - takeDistance;
				var volume = Volume + Math.Abs(Position);
				SellMarket(volume);
				_stopLossPrice = sl;
				_takeProfitPrice = tp;
			}
			else if ((macdLast3 < 0m || macdCurr > 0m) && ratio1 >= RatioThreshold && ratio2 >= RatioThreshold && Position <= 0)
			{
				var sl = candle.ClosePrice - stopDistance;
				var tp = candle.ClosePrice + takeDistance;
				var volume = Volume + Math.Abs(Position);
				BuyMarket(volume);
				_stopLossPrice = sl;
				_takeProfitPrice = tp;
			}
		}

		_macdPrev2 = _macdPrev;
		_macdPrev = macdCurr;
	}
}