在 GitHub 上查看
Zone Recovery Hedge 策略
Zone Recovery Hedge Strategy 是将 MetaTrader 顾问 Zone Recovery Hedge V1 移植到 StockSharp 平台的结果。策略在基准价位附近交替开多单和空单,只要价格穿越恢复区间就追加新的订单。每次追加的手数按照马丁格尔方式放大,直到达到设定的盈利目标或触发最大亏损限制。
策略原理
- 入场过滤:在 RSI Multi-Timeframe 模式下,策略会检查所选多个时间框(从 M1 到 MN1)的 RSI 数值,要求所有启用的时间框同时离开超买或超卖区。当 RSI 从超卖区向上离开时启动买入循环,从超买区向下离开时启动卖出循环。在 Manual 模式中没有自动信号,可通过
StartManualMarketCycle 或 StartManualPendingCycle 方法手动启动一个新的循环。
- 初始订单:首单手数可以使用固定值,也可以按照账户权益和计划止损距离计算的风险百分比来确定。启用 ATR 后,止损距离与恢复区宽度来自日线 ATR;否则使用经纪商点值。
- 恢复网格:当价格逆向运行并跨越恢复区距离时,策略会以更大的手数在相反方向开仓(可使用自定义手数列表、乘数或加法步进)。循环始终围绕基准价格交替方向,直到达到利润目标或达到最大下单次数。
- 利润控制:目标以账户货币计算,可使用基础止盈或恢复阶段的专用止盈(支持 ATR 比例)。
Test Commission 参数可用来模拟手续费。当前浮盈超过目标加成本后,策略会一次性平掉整组持仓。
- 风险保护:若
MaxTrades 非零且 SetMaxLoss 启用,当下单数达到上限且浮动亏损低于 MaxLoss 时,会强制关闭所有持仓并重置循环。
提示: StockSharp 默认使用净头寸模式,不支持同时持有多空对冲。本移植版本通过反转净持仓来复现恢复逻辑,但仍保持原策略的交替顺序和盈利计算方式。
关键参数
- CandleType:主交易时间框。
- Mode:
Manual 仅手动入场,RsiMultiTimeframe 启用 RSI 自动信号。
- RsiPeriod、OverboughtLevel、OversoldLevel 与
UseM1Timeframe … UseMonthlyTimeframe:配置各时间框的 RSI。
- TradeOnBarOpen:使用前一根 K 线作为确认条件(与原版一致)。
- RecoveryZoneSize、TakeProfitPoints:未启用 ATR 时的恢复区宽度和基础止盈距离。
- UseAtr、AtrPeriod、AtrZoneFraction、AtrTakeProfitFraction、AtrRecoveryFraction、AtrCandleType:ATR 动态设置。
- UseRecoveryTakeProfit、RecoveryTakeProfitPoints:恢复阶段使用的专用止盈距离。
- MaxTrades、SetMaxLoss、MaxLoss、TestCommission:限制最大单数、最大亏损并模拟手续费。
- RiskPercent、InitialLotSize、LotMultiplier、LotAddition、
CustomLotSize1 … CustomLotSize10:控制每一步的下单手数。
- UseTimer、StartHour、StartMinute、EndHour、EndMinute、UseLocalTime:交易时间窗口。
- PendingPrice:
StartManualPendingCycle 使用的参考价格。
使用建议
- 数据源需要提供所有被选中的 RSI 时间框。若无高阶数据,可由基础时间框自动聚合生成。
- 在手动模式下,调用
StartManualMarketCycle(true/false) 可立即按市价开启买入或卖出循环,StartManualPendingCycle 则从自定义价位启动循环。
- 使用风险百分比计算手数时,百分比会像原版 EA 一样被限制在 10% 以内。
- 为了正确计算盈利,需要从连接器获得
PriceStep 和 StepPrice 信息。
- 未移植 MT4 中的图形面板、按钮和利润线,功能通过参数与公开方法提供。
- 未模拟点差成本,只有
TestCommission 会在利润目标中扣除。
- 由于采用净头寸模式,相反方向的订单会相互抵消,但恢复步骤和手数扩张逻辑保持一致。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Zone Recovery Hedge strategy: RSI mean reversion with trend filter.
/// Buys when RSI crosses above oversold with close above SMA, sells on overbought cross below SMA.
/// </summary>
public class ZoneRecoveryHedgeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _smaPeriod;
private decimal _prevRsi;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public ZoneRecoveryHedgeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA trend filter period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, sma, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal smaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevRsi < 30 && rsiValue >= 30 && candle.ClosePrice > smaValue && Position <= 0)
BuyMarket();
else if (_prevRsi > 70 && rsiValue <= 70 && candle.ClosePrice < smaValue && Position >= 0)
SellMarket();
}
_prevRsi = rsiValue;
_hasPrev = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class zone_recovery_hedge_strategy(Strategy):
def __init__(self):
super(zone_recovery_hedge_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._sma_period = self.Param("SmaPeriod", 50)
self._prev_rsi = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def SmaPeriod(self):
return self._sma_period.Value
@SmaPeriod.setter
def SmaPeriod(self, value):
self._sma_period.Value = value
def OnReseted(self):
super(zone_recovery_hedge_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(zone_recovery_hedge_strategy, self).OnStarted2(time)
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
sma = SimpleMovingAverage()
sma.Length = self.SmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, sma, self._process_candle).Start()
def _process_candle(self, candle, rsi_value, sma_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
sma_val = float(sma_value)
close = float(candle.ClosePrice)
if self._has_prev:
if self._prev_rsi < 30 and rsi_val >= 30 and close > sma_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_rsi > 70 and rsi_val <= 70 and close < sma_val and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi_val
self._has_prev = True
def CreateClone(self):
return zone_recovery_hedge_strategy()