在 GitHub 上查看

Crossover 2 EMA 策略

该策略复刻 MetaTrader 中的 “Crossover_2EMA” 专家顾问,通过比较快慢两条指数移动平均线(EMA)的收盘价来做出交易决策。当快线 EMA 上穿慢线 EMA 时建立多头仓位;当快线重新跌破慢线时立即反向建立空头仓位,从而始终保持与当前趋势方向一致。

交易逻辑

  1. 订阅设定的 K 线类型,并根据用户配置的周期计算两条 EMA。
  2. 在每根收盘的 K 线上计算快慢 EMA 之间的差值。
  3. 当差值由非正变为正值时,表示出现向上金叉:平掉所有空头仓位并按设置的交易量开多。
  4. 当差值由非负变为负值时,表示出现向下死叉:平掉所有多头仓位并开空。
  5. 通过市价单完成仓位切换,反手时会自动扩大下单量以一次性平仓并建立新的反向头寸。

风险控制

  • 启动时调用 StartProtection(),可结合 StockSharp 的防护功能(例如最大回撤、交易时段控制或紧急停机)。
  • 反手交易通过单个市价单完成,避免先平仓再开仓造成的额外延迟。

参数

  • K 线类型 – 用于计算 EMA 的数据序列。
  • 快 EMA 周期 – 快线 EMA 的周期,必须小于慢线周期。
  • 慢 EMA 周期 – 慢线 EMA 的周期,必须大于快线周期。

其他说明

  • 只有当两条 EMA 均已形成后才开始交易,避免初始阶段的噪声信号。
  • 默认参数(1 分钟 K 线,12/24 周期)与原始 MQL 专家顾问保持一致。
  • 参数均支持优化,可在 StockSharp 中进行批量回测和寻优。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Crossover 2 EMA strategy: Fast/slow EMA crossover.
/// Buys when fast EMA crosses above slow EMA.
/// Sells when fast EMA crosses below slow EMA.
/// </summary>
public class Crossover2EmaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public Crossover2EmaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		decimal? prevFast = null;
		decimal? prevSlow = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, (candle, fastVal, slowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevFast.HasValue && prevSlow.HasValue)
				{
					var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
					var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;

					if (crossUp && Position <= 0)
						BuyMarket();
					else if (crossDown && Position >= 0)
						SellMarket();
				}

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

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