在 GitHub 上查看

Bruno 趋势策略

概览

Bruno 趋势策略是 MetaTrader 专家顾问“Bruno_v1”的 StockSharp 移植版本。策略运行在 30 分钟 K 线之上,专注于多个经典趋势与动量指标在看涨方向上的同步信号。与原始脚本一致,仅建立多头仓位。

交易逻辑

  1. 时间框架:30 分钟。
  2. 指标组合
    • 长度为 4 的简单移动平均线(SMA),监控极短期动量。
    • 长度为 8 与 21 的指数移动平均线(EMA),定义主要趋势方向。
    • 长度为 13 的平均趋向指数(ADX),通过 +DI 与 -DI 判断趋势强度。
    • 随机指标参数:%K=21、%D=3、平滑=3,用于确认动量并避免过度超买。
    • MACD 指标(13, 34, 8),同时比较柱状图与信号线。
    • 抛物线转向(Parabolic SAR),步长 0.055、最大值 0.21,用于验证上升趋势并辅助止损。
  3. 入场条件
    • EMA(8) 高于 EMA(21)。
    • ADX 过滤:+DI 大于 -DI 且高于 20。
    • 随机指标:%K 高于 %D 且低于 80。
    • MACD 柱状图大于 0 且高于信号线。
    • Parabolic SAR 持续上升(当前值高于上一根值)。
    • 当前仓位需为空仓或净空头,若存在空头将先行平仓再开多。
  4. 出场条件
    • 若上一根 K 线的收盘价跌破上一根 Parabolic SAR 值,则平掉所有多头仓位。

风险管理

  • 默认手数:0.1 手。
  • 通过 StartProtection 复刻 MetaTrader 风格的保护:默认止盈 50 点、止损 30 点。为保持与原始代码一致,默认不启用追踪止损。

额外说明

  • 原始脚本中的做空逻辑在 MQL 中被禁用,移植时也保持该设计。
  • 指标计算全部使用 StockSharp 高阶 API 绑定,避免手工缓存数据,符合项目规范。
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>
/// Bruno trend strategy - EMA trend following with RSI confirmation.
/// Buys when fast EMA is above slow EMA and RSI is rising.
/// Sells when fast EMA crosses below slow EMA.
/// </summary>
public class BrunoTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<DataType> _candleType;

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

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

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

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

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI 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; _prevRsi = 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 rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

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

		var crossUp = _prevFast <= _prevSlow && fast > slow;
		var crossDown = _prevFast >= _prevSlow && fast < slow;

		// Buy on bullish crossover with rising RSI
		if (crossUp && rsi > _prevRsi && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Sell on bearish crossover
		else if (crossDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
		_prevRsi = rsi;
	}
}