在 GitHub 上查看

MACD Stochastic 2 策略

该策略使用 StockSharp 高级 API 复刻 MetaTrader 中的“MACD Stochastic 2”专家逻辑。通过 MACD 主线的三根 K 线形态与随机指标 Stochastic 结合,识别接近超卖或超买区域的动量反转。同时为多空方向分别设置止损、止盈,并提供可选的点(pip)单位追踪止损。

概览

  • 通过参数 CandleType 指定任意品种和时间框架。
  • 仅依据 MACD 主线判断局部高低点,信号线和柱状图可用于图表展示。
  • 入场时要求 Stochastic %K 低于 20(做多)或高于 80(做空)。
  • 点值计算遵循原版 EA:取合约的 PriceStep,若价格精度为 3 或 5 位小数则额外乘以 10。

交易逻辑

多头入场

  1. 当前及前两根已完成 K 线的 MACD 主线值全部小于 0。
  2. 当前 MACD 值大于上一根,上一根小于前两根(形成局部底部)。
  3. Stochastic %K 低于 20(超卖)。
  4. 若存在空头仓位则先平仓,随后在 Position <= 0 时开多。

空头入场

  1. 当前及前两根已完成 K 线的 MACD 主线值全部大于 0。
  2. 当前 MACD 值小于上一根,上一根大于前两根(形成局部顶部)。
  3. Stochastic %K 高于 80(超买)。
  4. 若存在多头仓位则先平仓,随后在 Position >= 0 时开空。

仓位管理

  • 固定止损 / 止盈: 多空方向分别配置点数距离,设置为 0 可关闭对应防护。
  • 追踪止损: 启用后,当价格推进超过设定距离时生效;只有当盈利超过追踪步长时才上调/下调止损,减少频繁修改。
  • 反向信号: 出现反向条件时先平掉当前仓位,再用设定手数开立新的反向仓位。

参数

参数 默认值 说明
TradeVolume 1 新仓下单量。
StopLossBuyPips 50 多头止损点数(0 表示关闭)。
StopLossSellPips 50 空头止损点数(0 表示关闭)。
TakeProfitBuyPips 50 多头止盈点数(0 表示关闭)。
TakeProfitSellPips 50 空头止盈点数(0 表示关闭)。
TrailingStopPips 0 追踪止损距离(点)。0 表示禁用。
TrailingStepPips 5 更新追踪止损所需的最小盈利点数;启用追踪时必须为正。
MacdFastPeriod 12 MACD 快速 EMA 长度。
MacdSlowPeriod 26 MACD 慢速 EMA 长度。
MacdSignalPeriod 9 MACD 信号线平滑周期。
StochasticKPeriod 5 Stochastic %K 回溯周期。
StochasticDPeriod 3 Stochastic %D 平滑周期。
StochasticSlowing 3 Stochastic %K 额外平滑长度。
CandleType 1 小时周期 指标计算所用的 K 线类型(时间框架)。

说明

  • Pip 计算方式:pip = PriceStep,若报价精度为 3 或 5 位小数则乘以 10,与原始脚本保持一致。
  • Stochastic 阈值 20/80 在代码中写成常量,需要自定义时可直接修改源代码。
  • 策略仅在完整收盘的 K 线上做出决策,行为与 MetaTrader 的收盘执行一致。

使用步骤

  1. 启动前配置交易标的、CandleType 和下单量。
  2. 根据波动率调整止损、止盈和追踪止损参数。
  3. 需要优化时,可借助 StockSharp 优化器搜索 MACD 与 Stochastic 参数组合。
  4. 若界面存在图表区域,策略会自动绘制 K 线、MACD、Stochastic 以及成交标记,便于监控。
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>
/// MACD and stochastic based swing strategy.
/// Buys when MACD turns up in negative zone + stochastic is oversold.
/// Sells when MACD turns down in positive zone + stochastic is overbought.
/// </summary>
public class MacdStochastic2Strategy : Strategy
{
	private readonly StrategyParam<decimal> _oversoldThreshold;
	private readonly StrategyParam<decimal> _overboughtThreshold;
	private readonly StrategyParam<int> _macdFastPeriod;
	private readonly StrategyParam<int> _macdSlowPeriod;
	private readonly StrategyParam<int> _stochasticKPeriod;
	private readonly StrategyParam<int> _stochasticDPeriod;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _macdPrev1;
	private decimal _macdPrev2;
	private int _macdCount;

	public decimal OversoldThreshold { get => _oversoldThreshold.Value; set => _oversoldThreshold.Value = value; }
	public decimal OverboughtThreshold { get => _overboughtThreshold.Value; set => _overboughtThreshold.Value = value; }
	public int MacdFastPeriod { get => _macdFastPeriod.Value; set => _macdFastPeriod.Value = value; }
	public int MacdSlowPeriod { get => _macdSlowPeriod.Value; set => _macdSlowPeriod.Value = value; }
	public int StochasticKPeriod { get => _stochasticKPeriod.Value; set => _stochasticKPeriod.Value = value; }
	public int StochasticDPeriod { get => _stochasticDPeriod.Value; set => _stochasticDPeriod.Value = value; }
	public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
	public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MacdStochastic2Strategy()
	{
		_oversoldThreshold = Param(nameof(OversoldThreshold), 20m)
			.SetDisplay("Oversold", "Stochastic oversold threshold", "Stochastic");

		_overboughtThreshold = Param(nameof(OverboughtThreshold), 80m)
			.SetDisplay("Overbought", "Stochastic overbought threshold", "Stochastic");

		_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("MACD Fast", "Fast EMA length for MACD", "MACD");

		_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("MACD Slow", "Slow EMA length for MACD", "MACD");

		_stochasticKPeriod = Param(nameof(StochasticKPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic K", "Lookback for %K", "Stochastic");

		_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic D", "Smoothing for %D", "Stochastic");

		_stopLoss = Param(nameof(StopLoss), 1000m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 2000m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit", "Take profit in price units", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for calculations", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_macdPrev1 = 0m;
		_macdPrev2 = 0m;
		_macdCount = 0;
	}

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

		var macd = new MovingAverageConvergenceDivergence(
			new ExponentialMovingAverage { Length = MacdSlowPeriod },
			new ExponentialMovingAverage { Length = MacdFastPeriod });

		var rsi = new RelativeStrengthIndex { Length = StochasticKPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, (candle, rsiValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				// Process MACD manually
				var macdResult = macd.Process(candle.ClosePrice, candle.CloseTime, true);
				if (!macd.IsFormed)
					return;

				var macdValue = macdResult.ToDecimal();

				_macdCount++;
				if (_macdCount < 3)
				{
					_macdPrev2 = _macdPrev1;
					_macdPrev1 = macdValue;
					return;
				}

				var macd0 = macdValue;
				var macd1 = _macdPrev1;
				var macd2 = _macdPrev2;

				// Buy: MACD in negative zone turning up + stochastic oversold
				var longSignal = macd0 < 0m && macd1 < 0m && macd2 < 0m &&
					macd0 > macd1 && macd1 < macd2 &&
					rsiValue < OversoldThreshold;

				// Sell: MACD in positive zone turning down + stochastic overbought
				var shortSignal = macd0 > 0m && macd1 > 0m && macd2 > 0m &&
					macd0 < macd1 && macd1 > macd2 &&
					rsiValue > OverboughtThreshold;

				if (longSignal && Position <= 0)
					BuyMarket();
				else if (shortSignal && Position >= 0)
					SellMarket();

				_macdPrev2 = _macdPrev1;
				_macdPrev1 = macdValue;
			})
			.Start();

		StartProtection(
			new Unit(TakeProfit, UnitTypes.Absolute),
			new Unit(StopLoss, UnitTypes.Absolute));

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