在 GitHub 上查看

5MIN Scalping 策略

将 MT4 智能交易系统 "5MIN SCALPING"(MQL ID 22828) 迁移到 StockSharp 高级 API。策略在主时间框内捕捉快速突破形态,并利用更高周期的动量与月线 MACD 方向进行确认后才开仓。

  • 类别: 突破 / 动量式短线交易
  • 原始平台: MetaTrader 4
  • 数据需求: 为所有配置的时间框(默认 5 分钟、30 分钟、1 个月)提供蜡烛或逐笔数据

交易逻辑

  1. 趋势过滤。 两条线性加权移动平均线(LWMA,默认周期 6 和 85)定义主要趋势。做多要求快线在慢线上方,做空则要求快线在慢线下方。
  2. 多K结构判定。 内部 LWMA 三组(周期 8、13、21)对最近 20 根完成的蜡烛进行评估,完全复刻 MQL 中的 scalper() 函数:
    • 多头:循环中的每根蜡烛满足 LWMA8 > LWMA13 > LWMA21,蜡烛低点回踩均线带,且当前收盘价突破前 5 根蜡烛的最高点。
    • 空头:条件对称,检查蜡烛高点与均线带的关系,并要求收盘价跌破前 5 根蜡烛的最低点。
  3. 重叠过滤。 小型重叠条件(多头使用 Low[2] < High[1],空头使用 Low[1] < High[2])用来屏蔽单根尖刺行情。
  4. 动量确认。 在更高时间框(默认 30 分钟,周期 14)的 Momentum 指标上,最近三次取值至少一次与 100 的差值超过设定阈值(默认 0.3)。
  5. 宏观 MACD。 在月线级别计算 MACD(12, 26, 9)。做多要求 MACD 线位于信号线之上,做空则要求 MACD 线位于信号线之下。
  6. 持仓聚合。 反向开仓会先平掉当前持仓,再用设定手数立即进入新的方向。

风险控制

  • 固定目标。 可选的止盈、止损距离(以点数表示,并通过 PriceStep 转换为价格)。
  • 保本机制。 当价格获利达到阈值时,可自动把止损移动到入场价上下偏移量的位置。
  • 移动止损。 可选的固定点数追踪止损,在行情向盈利方向发展时逐步抬高/降低保护水平。
  • 手动平仓。 所有风控均在策略内部完成,不会主动挂出止损/止盈委托,以保持与原始 EA 相同的行为模式。

参数说明

参数 默认值 说明
CandleType 5 分钟 用于识别突破形态的主时间框。
MomentumCandleType 30 分钟 用于动量过滤的高阶时间框。
MacroMacdCandleType 1 个月 用于长期 MACD 的时间框。
FastMaLength 6 快速 LWMA 的周期。
SlowMaLength 85 慢速 LWMA 的周期。
MomentumLength 14 Momentum 指标的计算周期。
MomentumBuyThreshold 0.3 做多所需的最小 Momentum-100 偏离。
MomentumSellThreshold 0.3 做空所需的最小 Momentum-100 偏离。
TakeProfitPips 50 止盈距离(点)。设为 0 可关闭。
StopLossPips 20 止损距离(点)。设为 0 可关闭。
TrailingStopPips 40 移动止损距离(点),仅在 EnableTrailing 为 true 时生效。
EnableTrailing true 是否启用移动止损。
EnableBreakEven true 是否启用自动保本。
BreakEvenTriggerPips 30 触发保本所需的盈利点数。
BreakEvenOffsetPips 30 保本时相对于入场价的额外缓冲点数。
TradeVolume 1 每次下单的数量。

使用步骤

  1. 将策略加入 StockSharp 项目并绑定目标交易品种。
  2. 在启动前确保所有相关时间框的历史数据已经加载。
  3. 根据标的波动性调整手数、时间框和各类阈值。
  4. 启动策略。系统会自动订阅所有所需蜡烛序列,绘制指标(若图表可用),并在信号出现时执行进出场。

与原始 EA 的差异

  • MQL 版本中的金额型拖尾(Take_Profit_In_MoneyTRAIL_PROFIT_IN_MONEY2)以及账户权益保护未移植,风险控制改为点数距离。
  • 原策略的马丁格尔加仓逻辑(Lots * MathPow(LotExponent, CountTrades()))未实现,如需动态手数请自行调整 TradeVolume
  • 邮件 / 推送通知未实现,如有需要可使用 StockSharp 的通知机制。
  • 点值转换依赖于品种的 PriceStep。请在接入环境中确认该参数设置正确。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// 5-minute scalping strategy using fast/slow WMA crossover with momentum confirmation.
/// Buys on bullish crossover, sells on bearish crossover.
/// </summary>
public class FiveMinScalpingStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private WeightedMovingAverage _fast;
	private WeightedMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// Fast WMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow WMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="FiveMinScalpingStrategy"/> class.
	/// </summary>
	public FiveMinScalpingStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast WMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 85)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow WMA period", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 500)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_fast = new WeightedMovingAverage { Length = FastPeriod };
		_slow = new WeightedMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

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

		if (!_fast.IsFormed || !_slow.IsFormed)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// WMA crossover buy
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 60;
		}
		// WMA crossover sell
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 60;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}