在 GitHub 上查看

RRS Impulse 策略

RRS Impulse 是 MetaTrader 专家顾问“RRS Impulse”的 StockSharp 高级 API 版本。原始 EA 结合了 RSI、随机指标和布林带, 可以切换多种信号强度模式,并使用虚拟止损/移动止损。本移植在 C# 中复刻了这些特点:通过蜡烛订阅驱动指标,交易指令全部通过 BuyMarketSellMarketClosePosition 等高级方法完成。

交易逻辑

  1. 指标模式 – 四种选择:
    • Rsi:当 RSI 离开超买/超卖区域时入场。
    • Stochastic:要求 %K 与 %D 同时位于设定阈值之上或之下。
    • BollingerBands:收盘价高于上轨或低于下轨时触发。
    • RsiStochasticBollinger:三项过滤器全部一致时才允许下单。
  2. 交易方向Trend 顺势操作(超买做空、超卖做多),CounterTrend 则反向博弈。
  3. 信号强度 – 决定需要多少时间框架同时满足条件:
    • SingleTimeFrame:只检查基础时间框架 CandleType
    • MultiTimeFrame:需要 M1、M5、M15、M30、H1、H4 全部同向。
    • Strong:侧重日内动量(M1、M5、M15、M30)。
    • VeryStrong:同样使用 M1…H4 全阶梯。若启用复合指标模式,则每个时间框架都必须同时满足 RSI、随机指标与布林带条件。
  4. 风险控制 – 每笔仓位都会跟踪三类虚拟保护:
    • 固定止损点数;
    • 固定止盈点数;
    • 当浮动盈利超过 TrailingStartPips 时启动的移动止损,跟随距离由 TrailingGapPips 定义。 当方向反转时,策略先调用 ClosePosition() 平掉现有仓位,下一个确认信号到来后再考虑开立反向单。

参数

分类 名称 说明
Data CandleType 用于决策的基础蜡烛序列。
Orders TradeVolume 下单的合约数量/手数。
Risk StopLossPips, TakeProfitPips, TrailingStartPips, TrailingGapPips 以点数表示的虚拟保护。
Signals IndicatorMode, TradeDirection, SignalStrength 复制自 EA 输入参数的行为开关。
RSI RsiPeriod, RsiUpperLevel, RsiLowerLevel RSI 计算周期及阈值。
Stochastic StochasticKPeriod, StochasticDPeriod, StochasticSlowing, StochasticUpperLevel, StochasticLowerLevel 慢速随机指标设置。
Bollinger BollingerPeriod, BollingerDeviation 布林带长度与标准差倍数。

与原版相同,关键参数保留了可优化范围,例如止损/止盈距离和振荡指标阈值。

数据需求

策略需要分钟级别的历史蜡烛。当 SignalStrength 选择更严格的模式时,程序会自动订阅所需的多个时间框架 (GetWorkingSecurities 会向引擎声明这些需求)。策略不依赖 Level1 行情,所有决策仅基于完成的蜡烛收盘价,因而完全复现了 MetaTrader 中的“虚拟”止损与止盈。

移植说明

  • 原 EA 中随机切换交易品种的逻辑已移除。StockSharp 策略针对单一 Security 运行,品种选择交由用户配置。
  • 持仓管理完全采用市价指令:当出现反向信号或保护条件触发时,先执行 ClosePosition(),对应 MQL 中遍历订单并逐一关闭的流程。
  • 代码注释全部使用英文,缩进使用制表符,完全符合仓库提供的贡献规范。
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

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

/// <summary>
/// RRS Impulse strategy.
/// Combines RSI, Stochastic and Bollinger Bands for counter-trend entries.
/// </summary>
public class RrsImpulseStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiUpperLevel;
	private readonly StrategyParam<decimal> _rsiLowerLevel;
	private readonly StrategyParam<int> _stochasticKPeriod;
	private readonly StrategyParam<int> _stochasticDPeriod;
	private readonly StrategyParam<decimal> _stochasticUpperLevel;
	private readonly StrategyParam<decimal> _stochasticLowerLevel;
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerDeviation;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal RsiUpperLevel { get => _rsiUpperLevel.Value; set => _rsiUpperLevel.Value = value; }
	public decimal RsiLowerLevel { get => _rsiLowerLevel.Value; set => _rsiLowerLevel.Value = value; }
	public int StochasticKPeriod { get => _stochasticKPeriod.Value; set => _stochasticKPeriod.Value = value; }
	public int StochasticDPeriod { get => _stochasticDPeriod.Value; set => _stochasticDPeriod.Value = value; }
	public decimal StochasticUpperLevel { get => _stochasticUpperLevel.Value; set => _stochasticUpperLevel.Value = value; }
	public decimal StochasticLowerLevel { get => _stochasticLowerLevel.Value; set => _stochasticLowerLevel.Value = value; }
	public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
	public decimal BollingerDeviation { get => _bollingerDeviation.Value; set => _bollingerDeviation.Value = value; }

	public RrsImpulseStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI length", "RSI");

		_rsiUpperLevel = Param(nameof(RsiUpperLevel), 65m)
			.SetDisplay("RSI Upper", "Overbought", "RSI");

		_rsiLowerLevel = Param(nameof(RsiLowerLevel), 35m)
			.SetDisplay("RSI Lower", "Oversold", "RSI");

		_stochasticKPeriod = Param(nameof(StochasticKPeriod), 10)
			.SetDisplay("Stochastic %K", "%K period", "Stochastic");

		_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
			.SetDisplay("Stochastic %D", "%D period", "Stochastic");

		_stochasticUpperLevel = Param(nameof(StochasticUpperLevel), 70m)
			.SetDisplay("Stochastic Upper", "Overbought", "Stochastic");

		_stochasticLowerLevel = Param(nameof(StochasticLowerLevel), 30m)
			.SetDisplay("Stochastic Lower", "Oversold", "Stochastic");

		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetDisplay("Bollinger Period", "BB length", "Bollinger");

		_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
			.SetDisplay("Bollinger Deviation", "BB deviation", "Bollinger");
	}

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

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

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var stochastic = new StochasticOscillator();
		stochastic.K.Length = StochasticKPeriod;
		stochastic.D.Length = StochasticDPeriod;
		var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerDeviation };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(rsi, stochastic, bollinger, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, bollinger);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiVal, IIndicatorValue stochVal, IIndicatorValue bbVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!rsiVal.IsFinal || !stochVal.IsFinal || !bbVal.IsFinal)
			return;

		if (!rsiVal.IsFormed || !stochVal.IsFormed || !bbVal.IsFormed)
			return;

		var rsi = rsiVal.GetValue<decimal>();
		var stoch = (StochasticOscillatorValue)stochVal;
		var stochK = stoch.K ?? 50m;
		var bb = (BollingerBandsValue)bbVal;
		var bbUpper = bb.UpBand ?? candle.ClosePrice;
		var bbLower = bb.LowBand ?? candle.ClosePrice;

		var close = candle.ClosePrice;

		// Count how many indicators signal overbought/oversold
		var obSignals = 0;
		var osSignals = 0;

		if (rsi >= RsiUpperLevel) obSignals++;
		if (rsi <= RsiLowerLevel) osSignals++;

		if (stochK >= StochasticUpperLevel) obSignals++;
		if (stochK <= StochasticLowerLevel) osSignals++;

		if (close >= bbUpper) obSignals++;
		if (close <= bbLower) osSignals++;

		// Counter-trend: need at least 2 of 3 indicators confirming
		if (osSignals >= 2 && Position <= 0)
		{
			BuyMarket();
		}
		else if (obSignals >= 2 && Position >= 0)
		{
			SellMarket();
		}
		// Exit long when RSI and stoch neutralize
		else if (Position > 0 && rsi > 50m && stochK > 50m)
		{
			SellMarket();
		}
		// Exit short when RSI and stoch neutralize
		else if (Position < 0 && rsi < 50m && stochK < 50m)
		{
			BuyMarket();
		}
	}
}