在 GitHub 上查看
XRSI DeMarker Histogram 策略
策略概述
该策略复刻 Exp_XRSIDeMarker_Histogram 专家顾问的思路。它利用一个由 RSI 和 DeMarker 组合而成的自定义振荡器来捕捉趋势反转,并对结果进行平滑处理。多空方向可以分别开启或关闭,并提供以价格步长表示的可选止损与止盈。
指标构成
- 价格输入:按照设定的周期,对所选价格类型(收盘、开盘、最高、最低、中价、典型价或加权价)计算 RSI。
- DeMarker 部分:对每根完结的 K 线计算向上压力
deMax 和向下压力 deMin:
deMax = max(High_t - High_{t-1}, 0)
deMin = max(Low_{t-1} - Low_t, 0)
两个序列均采用与 RSI 相同长度的简单移动平均进行平滑,
DeMarker = deMaxAvg / (deMaxAvg + deMinAvg),并缩放到 0–100 区间。
- 综合振荡器:最终数值为
(RSI + 100 * DeMarker) / 2。
- 平滑处理:综合振荡器经由所选均线(SMA、EMA、SMMA、LWMA 或 Jurik)进行平滑。如果选择了 StockSharp 尚未实现的算法(ParMA、T3、VIDYA、AMA),系统会退回到指定周期的 EMA;Jurik 模式会应用相应的相位参数。
- 信号历史:保存平滑值并在
SignalBar 指定的历史位置上评估条件,从而模拟原始 EA 等待下一根 K 线再下单的行为。
交易规则
- 多头反转
- 条件:
SignalBar+1 的数值低于 SignalBar+2(形成下降坡度),且 SignalBar 的数值重新抬头(>=)。
- 执行:
- 若
CloseShortOnLongSignal 为真,则先平掉现有空单。
- 若
AllowBuyEntries 为真,在当前收盘价开多,数量为 TradeVolume,并在从空头翻多时加回原仓位。
- 空头反转
- 条件:
SignalBar+1 的数值高于 SignalBar+2(形成上升坡度),且 SignalBar 的数值转向下行(<=)。
- 执行:
- 若
CloseLongOnShortSignal 为真,则先平掉现有多单。
- 若
AllowSellEntries 为真,在当前收盘价开空。
- 在 RSI 与 DeMarker 尚未完成初始化之前信号会被忽略,并且只有在策略在线且允许交易时才会下单。
风险控制
StopLossTicks 与 TakeProfitTicks 表示价格步长数量。策略将该数值乘以 Security.PriceStep(若未知则回退为 1),当 K 线的高低点触及该距离时平仓。
- 设置为
0 可关闭对应的止损或止盈。
TradeVolume 为默认下单数量,同时在反手时用于补偿原有仓位。
参数说明
| 参数 |
含义 |
默认值 |
TradeVolume |
新建仓位的下单量。 |
0.1 |
StopLossTicks |
以价格步长表示的止损距离。 |
1000 |
TakeProfitTicks |
以价格步长表示的止盈距离。 |
2000 |
AllowBuyEntries |
是否允许做多。 |
true |
AllowSellEntries |
是否允许做空。 |
true |
CloseLongOnShortSignal |
出现做空信号时是否平多。 |
true |
CloseShortOnLongSignal |
出现做多信号时是否平空。 |
true |
CandleType |
使用的 K 线周期(默认 4 小时)。 |
H4 |
IndicatorPeriod |
RSI 与 DeMarker 的周期。 |
14 |
AppliedPriceSelection |
RSI 的价格类型。 |
Close |
SmoothingMethodSelection |
平滑方法(SMA/EMA/SMMA/LWMA/Jurik/Adaptive)。 |
Sma |
SmoothingLength |
平滑均线的周期。 |
5 |
SmoothingPhase |
Jurik 平滑的相位。 |
15 |
SignalBar |
用于判定信号的历史 K 线偏移。 |
1 |
与原版 EA 的差异
- 原版的资金管理模式(基于余额或可用保证金等)由直接的
TradeVolume 参数替代。
- 由于使用市价下单,无需
Deviation(滑点)设定。
- StockSharp 暂不提供 ParMA、T3、VIDYA、AMA 等平滑算法,
Adaptive 选项会自动回退为 EMA。
- C# 源码中的注释全部采用英文,且与原 EA 一样只处理收盘完成的 K 线。
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// XRSI DeMarker Histogram strategy (simplified).
/// Uses RSI combined with momentum to detect reversals.
/// Buys on RSI reversal from oversold, sells on reversal from overbought.
/// </summary>
public class XrsidDeMarkerHistogramStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _smaPeriod;
private decimal _prevRsi;
private decimal _prevPrevRsi;
private bool _initialized;
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 XrsidDeMarkerHistogramStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI indicator period", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "Smoothing period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0m;
_prevPrevRsi = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_prevPrevRsi = 0;
_initialized = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, sma, (ICandleMessage candle, decimal rsiValue, decimal smaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!_initialized)
{
_prevPrevRsi = rsiValue;
_prevRsi = rsiValue;
_initialized = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevPrevRsi = _prevRsi;
_prevRsi = rsiValue;
return;
}
// Buy on RSI reversal from oversold (V-bottom)
var buySignal = _prevPrevRsi > _prevRsi && rsiValue >= _prevRsi && _prevRsi < 35m;
// Sell on RSI reversal from overbought (inverse V)
var sellSignal = _prevPrevRsi < _prevRsi && rsiValue <= _prevRsi && _prevRsi > 65m;
if (buySignal && Position <= 0)
{
BuyMarket();
}
else if (sellSignal && Position >= 0)
{
SellMarket();
}
_prevPrevRsi = _prevRsi;
_prevRsi = rsiValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
}
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 xrsid_de_marker_histogram_strategy(Strategy):
def __init__(self):
super(xrsid_de_marker_histogram_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI indicator period", "Indicators")
self._sma_period = self.Param("SmaPeriod", 5) \
.SetDisplay("SMA Period", "Smoothing period", "Indicators")
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def SmaPeriod(self):
return self._sma_period.Value
def OnReseted(self):
super(xrsid_de_marker_histogram_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
def OnStarted2(self, time):
super(xrsid_de_marker_histogram_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
sma = SimpleMovingAverage()
sma.Length = self.SmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(rsi, sma, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value, sma_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self._initialized:
self._prev_prev_rsi = rv
self._prev_rsi = rv
self._initialized = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_prev_rsi = self._prev_rsi
self._prev_rsi = rv
return
buy_signal = self._prev_prev_rsi > self._prev_rsi and rv >= self._prev_rsi and self._prev_rsi < 35.0
sell_signal = self._prev_prev_rsi < self._prev_rsi and rv <= self._prev_rsi and self._prev_rsi > 65.0
if buy_signal and self.Position <= 0:
self.BuyMarket()
elif sell_signal and self.Position >= 0:
self.SellMarket()
self._prev_prev_rsi = self._prev_rsi
self._prev_rsi = rv
def CreateClone(self):
return xrsid_de_marker_histogram_strategy()