在 GitHub 上查看

EMA 6/12 均线交叉策略

该策略复刻了 MetaTrader 平台的 EMA_6_12 专家顾问:在默认的一小时 K 线中计算 EMA(6) 与 EMA(12),并在蜡烛收盘后确认均线交叉,再决定开仓或平仓。

交易逻辑

  • 进场
    • 当 EMA(6) 上穿 EMA(12) 时触发做多信号,若当前没有持仓,则开多单。
    • 当 EMA(6) 下穿 EMA(12) 时触发做空信号,若当前没有持仓,则开空单。
  • 离场
    • UseCloseSignals(默认启用)为真时,一旦出现相反方向的均线交叉,策略先行平掉当前持仓,并等待下一次交叉后再入场,保持与原始 EA 的节奏一致。
    • 可选的止盈与跟踪止损通过 StockSharp 自带的 StartProtection 方法托管。
  • 仓位管理
    • 下单数量由 OrderVolume 参数控制,默认 1 手,在发单前会根据标的交易规则自动对齐。

风险控制

  • 跟踪止损:将原 EA 的“点”参数转换为价格最小变动单位,只要参数大于零便会在持仓获利时自动跟随。
  • 止盈:同样以价格步长表示,设置为 0 即可关闭。
  • 策略不会加仓或网格交易,每个品种同时只持有一个方向的仓位。

参数说明

参数 说明
CandleType 构建 K 线与均线所用的时间框架,默认 1 小时。
OrderVolume 下单手数。
ShortEmaLength 快速 EMA 的周期(默认 6)。
LongEmaLength 慢速 EMA 的周期(默认 12)。
UseCloseSignals 是否在出现反向交叉时平仓(默认开启)。
TrailingStopSteps 以价格步长表示的跟踪止损距离,0 表示关闭。
TakeProfitSteps 以价格步长表示的止盈距离,0 表示关闭。

其他说明

  • 仅在蜡烛收盘后处理信号,避免盘中噪声。
  • 每当仓位回到 0 时会重置上一笔 EMA 值,确保下一次交叉判断干净。
  • 代码注释为英文,并且缩进使用制表符,符合仓库要求。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EMA 6/12 crossover strategy.
/// Buys when EMA(6) crosses above EMA(12).
/// Sells when EMA(6) crosses below EMA(12).
/// </summary>
public class Ema612Strategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public Ema612Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 6)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

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

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }

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

		_hasPrev = false;

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

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}