在 GitHub 上查看

Micro Trend Breakouts 策略

概览

Micro Trend Breakouts 策略将 MetaTrader 上的同名专家顾问迁移到 StockSharp 的高层 API。它结合线性加权移动平均、Momentum 冲量和 MACD 方向过滤器来捕捉短线突破行情。策略一次仅持有一个仓位,并仅在蜡烛收盘时做出交易决策。

指标

  • 线性加权移动平均 (LWMA):快速与慢速均线在分析周期内定义趋势方向。
  • Momentum:最近三根完成蜡烛的 Momentum 绝对值必须超过阈值,以确认价格正在加速。
  • MACD:主线与信号线的相对位置用于确认多空方向。

入场规则

  1. 等待配置周期的蜡烛收盘。
  2. 做多时要求快速 LWMA 高于慢速 LWMA(做空则相反)。
  3. 检查小幅突破结构:两根前蜡烛的最低价必须低于上一根蜡烛的最高价(做空时条件镜像)。
  4. 确认 Momentum 冲量——最近三根蜡烛中任意一根的 Momentum 绝对值达到阈值。
  5. 验证 MACD 方向:
    • 多头:MACD 主线高于信号线,无论其是否在零轴上方。
    • 空头:MACD 主线低于信号线,无论其是否在零轴下方。

满足所有条件后策略按默认交易量发送市价单。

出场与风险控制

  • 初始止损和止盈以价格步长表示,在建仓时计算;如果参数为 0 则禁用对应保护。
  • 可选的保本模块在达到指定盈利后将止损移至进场价附近,可附加缓冲步长。
  • 启用追踪止损时,当浮盈超过触发阈值,止损将跟随自进场以来的最高价(多头)或最低价(空头)向前移动。
  • 每根收盘蜡烛都会评估保护位;一旦价格触发止损或止盈,策略以市价单平仓并重置内部状态。

参数

名称 说明 默认值
Order Volume 入场市价单的交易量。 1
Candle Type 分析使用的蜡烛类型/周期。 15 分钟
Fast LWMA 快速线性加权移动平均的周期。 6
Slow LWMA 慢速线性加权移动平均的周期。 85
Momentum Period Momentum 指标回溯长度。 14
Momentum Threshold 最近三根蜡烛 Momentum 绝对值的最小要求。 0.3
MACD Fast / Slow / Signal MACD 快速、慢速与信号均线周期。 12 / 26 / 9
Stop Loss 止损距离(价格步长),0 表示不使用。 20
Take Profit 止盈距离(价格步长),0 表示不使用。 50
Use Trailing 是否启用追踪止损。 true
Trail Activation 激活追踪止损所需的盈利步长。 40
Trail Step 追踪止损与最近极值之间的距离。 40
Use Breakeven 是否启用保本止损。 true
Breakeven Trigger 激活保本模块的盈利步长。 30
Breakeven Padding 将止损移至保本时附加的缓冲步长。 30

备注

  • 策略仅订阅一个蜡烛流,不使用低层 API,完全符合转换要求。
  • 保护逻辑通过蜡烛检查与 StartProtection() 相结合,实现框架级别的仓位监控。
  • 源码中的注释全部为英文,以满足项目规范。
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;

public class MicroTrendBreakoutsStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

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

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public MicroTrendBreakoutsStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { 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;

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

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}