在 GitHub 上查看

Suffic369 策略

概述

Suffic369 是一套趋势突破策略,结合两条短周期移动平均线和宽幅布林带。策略在快速收盘价 SMA 上穿最近高点 SMA 且收盘价落在布林下轨附近时做多;在快速 SMA 下穿最近低点 SMA 且收盘价触及布林上轨时做空。移植到 StockSharp 后沿用了原始 MQL 逻辑,并通过高层蜡烛订阅与指标绑定实现。

指标

  • 快速 SMA(收盘价,周期 3):衡量收盘价的短期动量。
  • 高点 SMA(最高价,周期 5):代表近期阻力水平。
  • 低点 SMA(最低价,周期 5):代表近期支撑水平。
  • 布林带(周期 156,偏差 1):识别价格在波动率背景下的极值。

所有指标仅在蜡烛收盘后计算。上一根蜡烛的数值会被缓存,以复现 MetaTrader 中使用的一个柱偏移。

交易规则

多头入场

  1. 前一根快速 SMA(收盘)低于前一根高点 SMA。
  2. 当前快速 SMA(收盘)上穿当前高点 SMA。
  3. 蜡烛收盘价低于布林下轨。

空头入场

  1. 前一根快速 SMA(收盘)高于前一根低点 SMA。
  2. 当前快速 SMA(收盘)下穿当前低点 SMA。
  3. 蜡烛收盘价高于布林上轨。

离场逻辑

  • 反向信号:出现反向入场条件时立即平掉当前持仓。
  • 止损:可选的价格步长止损,复现 MQL 参数 StopLoss
  • 止盈:可选的价格步长止盈,对应 TakeProfit
  • 移动止损:可选的跟踪止损,仅在浮盈超过设定距离时向盈利方向移动,完全复制原始 EA 的 OrderModify 行为。

策略同一时间只持有一个方向的仓位。止损、止盈或反向信号触发后,会等下一根完整蜡烛再评估新的入场。

参数

名称 默认值 说明
FastMaLength 3 收盘价快速 SMA 的周期。
HighMaLength 5 最高价 SMA 的周期。
LowMaLength 5 最低价 SMA 的周期。
BollingerLength 156 布林带窗口长度。
BollingerDeviation 1 布林带标准差倍数。
UseStopLoss true 是否启用止损。
StopLossPoints 30 止损距离(价格步长)。
UseTakeProfit true 是否启用止盈。
TakeProfitPoints 60 止盈距离(价格步长)。
UseTrailingStop true 是否启用移动止损。
TrailingStopPoints 30 移动止损偏移(价格步长)。
CandleType 15 分钟周期 指标使用的蜡烛类型。

所有数值参数均通过 StrategyParam<T> 暴露,可在 StockSharp 中直接参与优化。

风险控制

  • 使用 Security.PriceStep 将以“点”表示的距离转换为实际价格差。
  • 移动止损仅在浮盈超过设定距离后更新,保持与原始策略一致的保护节奏。
  • OnStarted 中调用 StartProtection(),开启 StockSharp 内置的防护功能。

使用建议

  • 确保订阅的标的支持所选蜡烛类型。
  • 启动前设置策略的 Volume,以控制下单手数。
  • 策略会等待所有指标形成后才进行交易,前几根蜡烛仅用于指标预热。
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>
/// Suffic369 breakout strategy based on fast/slow SMA crossover.
/// Buy when fast SMA crosses above slow SMA, sell when fast crosses below slow.
/// </summary>
public class Suffic369Strategy : Strategy
{
	private readonly StrategyParam<int> _fastMaLength;
	private readonly StrategyParam<int> _slowMaLength;
	private readonly StrategyParam<DataType> _candleType;

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

	/// <summary>
	/// Fast SMA length.
	/// </summary>
	public int FastMaLength
	{
		get => _fastMaLength.Value;
		set => _fastMaLength.Value = value;
	}

	/// <summary>
	/// Slow SMA length.
	/// </summary>
	public int SlowMaLength
	{
		get => _slowMaLength.Value;
		set => _slowMaLength.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of the <see cref="Suffic369Strategy"/> class.
	/// </summary>
	public Suffic369Strategy()
	{
		_fastMaLength = Param(nameof(FastMaLength), 3)
			.SetDisplay("Fast SMA Length", "Fast moving average period", "Indicators");

		_slowMaLength = Param(nameof(SlowMaLength), 6)
			.SetDisplay("Slow SMA Length", "Slow moving average period", "Indicators");

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

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

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

		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var fastSma = new SimpleMovingAverage { Length = FastMaLength };
		var slowSma = new SimpleMovingAverage { Length = SlowMaLength };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(fastSma, slowSma, 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;
		}

		// Buy signal: fast SMA crosses above slow SMA
		var longSignal = _prevFast <= _prevSlow && fast > slow;
		// Sell signal: fast SMA crosses below slow SMA
		var shortSignal = _prevFast >= _prevSlow && fast < slow;

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

		_prevFast = fast;
		_prevSlow = slow;
	}
}