在 GitHub 上查看

Triple SMA Crossover 策略

概述

Triple SMA Crossover 策略复刻了原始 MQL 专家顾问 3sma.mq4。策略基于收盘价计算三条简单移动平均线(SMA),当短期趋势与中长期趋势一致时进行交易。本次转换保留了原有规则,并使用 StockSharp 的高级策略 API 实现。

交易逻辑

  1. 计算三个可配置周期的 SMA。
  2. 当快线 SMA 下穿中线 SMA 时平掉多头仓位。
  3. 当快线 SMA 上穿中线 SMA 时平掉空头仓位。
  4. 当满足以下条件时开多:
    • 快线 SMA 至少高于中线 SMA 指定的价差步数。
    • 中线 SMA 至少高于慢线 SMA 指定的价差步数。
    • 当前没有持有多头仓位。
  5. 当满足以下条件时开空:
    • 快线 SMA 至少低于中线 SMA 指定的价差步数。
    • 中线 SMA 至少低于慢线 SMA 指定的价差步数。
    • 当前没有持有空头仓位。

参数

  • Candle Type – 计算移动平均的主要K线周期。
  • Fast SMA Length – 快速 SMA 的周期(MQL 参数 SMA1)。
  • Medium SMA Length – 中期 SMA 的周期(MQL 参数 SMA2)。
  • Slow SMA Length – 慢速 SMA 的周期(MQL 参数 SMA3)。
  • SMA Spread Steps – 要求 SMA 之间至少相差的价差步数(MQL 参数 SMAspread)。
  • Trade Volume – 开仓时使用的下单量(MQL 参数 lots)。

说明

  • 原脚本中的止损功能被注释,因此在本实现中也没有启用。
  • 所有平仓均使用市价单,以贴合原策略的简单行为。
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>
/// Triple SMA crossover strategy.
/// Goes long when fast > medium > slow, short when fast less than medium less than slow.
/// Exits when fast crosses medium in opposite direction.
/// </summary>
public class TripleSmaCrossoverStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _mediumPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevMed;
	private bool _hasPrev;

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

	public TripleSmaCrossoverStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_mediumPeriod = Param(nameof(MediumPeriod), 10)
			.SetDisplay("Medium SMA", "Medium SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 20)
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevMed = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var medium = new SimpleMovingAverage { Length = MediumPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };

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

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

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevMed = med;
			_hasPrev = true;
			return;
		}

		// Bullish alignment: fast > medium > slow
		if (fast > med && med > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Bearish alignment: fast < medium < slow
		else if (fast < med && med < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
		// Exit long when fast crosses below medium
		else if (Position > 0 && _prevFast >= _prevMed && fast < med)
		{
			SellMarket();
		}
		// Exit short when fast crosses above medium
		else if (Position < 0 && _prevFast <= _prevMed && fast > med)
		{
			BuyMarket();
		}

		_prevFast = fast;
		_prevMed = med;
	}
}