RRS Impulse 策略
RRS Impulse 是 MetaTrader 专家顾问“RRS Impulse”的 StockSharp 高级 API 版本。原始 EA 结合了 RSI、随机指标和布林带,
可以切换多种信号强度模式,并使用虚拟止损/移动止损。本移植在 C# 中复刻了这些特点:通过蜡烛订阅驱动指标,交易指令全部通过
BuyMarket、SellMarket 与 ClosePosition 等高级方法完成。
交易逻辑
- 指标模式 – 四种选择:
Rsi:当 RSI 离开超买/超卖区域时入场。Stochastic:要求 %K 与 %D 同时位于设定阈值之上或之下。BollingerBands:收盘价高于上轨或低于下轨时触发。RsiStochasticBollinger:三项过滤器全部一致时才允许下单。
- 交易方向 –
Trend顺势操作(超买做空、超卖做多),CounterTrend则反向博弈。 - 信号强度 – 决定需要多少时间框架同时满足条件:
SingleTimeFrame:只检查基础时间框架CandleType。MultiTimeFrame:需要 M1、M5、M15、M30、H1、H4 全部同向。Strong:侧重日内动量(M1、M5、M15、M30)。VeryStrong:同样使用 M1…H4 全阶梯。若启用复合指标模式,则每个时间框架都必须同时满足 RSI、随机指标与布林带条件。
- 风险控制 – 每笔仓位都会跟踪三类虚拟保护:
- 固定止损点数;
- 固定止盈点数;
- 当浮动盈利超过
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();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import BollingerBands, RelativeStrengthIndex, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class rrs_impulse_strategy(Strategy):
"""RSI + Stochastic + Bollinger Bands counter-trend strategy."""
def __init__(self):
super(rrs_impulse_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "RSI length", "RSI")
self._rsi_upper_level = self.Param("RsiUpperLevel", 65.0) \
.SetDisplay("RSI Upper", "Overbought", "RSI")
self._rsi_lower_level = self.Param("RsiLowerLevel", 35.0) \
.SetDisplay("RSI Lower", "Oversold", "RSI")
self._stochastic_k_period = self.Param("StochasticKPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %K", "%K period", "Stochastic")
self._stochastic_d_period = self.Param("StochasticDPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %D", "%D period", "Stochastic")
self._stochastic_upper_level = self.Param("StochasticUpperLevel", 70.0) \
.SetDisplay("Stochastic Upper", "Overbought", "Stochastic")
self._stochastic_lower_level = self.Param("StochasticLowerLevel", 30.0) \
.SetDisplay("Stochastic Lower", "Oversold", "Stochastic")
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Period", "BB length", "Bollinger")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Deviation", "BB deviation", "Bollinger")
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(rrs_impulse_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
stochastic = StochasticOscillator()
stochastic.K.Length = self._stochastic_k_period.Value
stochastic.D.Length = self._stochastic_d_period.Value
bollinger = BollingerBands()
bollinger.Length = self._bollinger_period.Value
bollinger.Width = self._bollinger_deviation.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(rsi, stochastic, bollinger, self._process_candle).Start()
def _process_candle(self, candle, rsi_val, stoch_val, bb_val):
if candle.State != CandleStates.Finished:
return
if not rsi_val.IsFinal or not stoch_val.IsFinal or not bb_val.IsFinal:
return
if not rsi_val.IsFormed or not stoch_val.IsFormed or not bb_val.IsFormed:
return
rsi = float(rsi_val)
stoch_k = float(stoch_val.K) if stoch_val.K is not None else 50.0
bb_upper = bb_val.UpBand
bb_lower = bb_val.LowBand
upper = float(bb_upper) if bb_upper is not None else float(candle.ClosePrice)
lower = float(bb_lower) if bb_lower is not None else float(candle.ClosePrice)
close = float(candle.ClosePrice)
ob_signals = 0
os_signals = 0
if rsi >= float(self._rsi_upper_level.Value):
ob_signals += 1
if rsi <= float(self._rsi_lower_level.Value):
os_signals += 1
if stoch_k >= float(self._stochastic_upper_level.Value):
ob_signals += 1
if stoch_k <= float(self._stochastic_lower_level.Value):
os_signals += 1
if close >= upper:
ob_signals += 1
if close <= lower:
os_signals += 1
if os_signals >= 2 and self.Position <= 0:
self.BuyMarket()
elif ob_signals >= 2 and self.Position >= 0:
self.SellMarket()
elif self.Position > 0 and rsi > 50.0 and stoch_k > 50.0:
self.SellMarket()
elif self.Position < 0 and rsi < 50.0 and stoch_k < 50.0:
self.BuyMarket()
def CreateClone(self):
return rrs_impulse_strategy()