在 GitHub 上查看

DT RSI EXP1 策略

本移植复刻了 MT4 智能交易系统 DT-RSI-EXP1。策略在 15 分钟 RSI 上寻找 60/40 附近的双顶与双底结构:如果 RSI 连续形成两次高点且第二个高于 60、期间没有低于 40 的低点,并且 4 小时趋势过滤器向下,则买入;若出现对称的双底且趋势向上,则做空。开仓后同时挂出固定止损与止盈,并可选用追踪止损保护浮盈。当 RSI 触及 70/30 极值时,无论盈亏都会强制平仓,与原版一致。

详情

  • 入场条件
    • 多头:两次 RSI 高点且第二次 > 60,中间没有低于 40 的低点;240 分钟 EMA(代替 MT4 的 RFTL)位于上一根收盘价之下;RSI(1) 上穿推算出的“颈线”,RSI(2) 仍在其下方,且 RSI(2) < 50、RSI(0) < 55。
    • 空头:两次 RSI 低点且第二次 < 40,中间没有高于 60 的高点;240 分钟 EMA 位于上一根收盘价之上;RSI(1) 下穿颈线,RSI(2) > 50 且 RSI(0) > 47。
  • 方向:双向。
  • 离场条件
    • RSI 极值(多头 RSI > 70,空头 RSI < 30)。
    • 价格触发止损或止盈(按价格步长换算)。
    • 当浮盈超过 TrailingStopPoints 时,可选的追踪止损开始跟随。
  • 止损管理:固定止损、固定止盈,可选追踪止损。
  • 默认参数
    • CandleType = 15 分钟。
    • TrendCandleType = 240 分钟(EMA 趋势过滤器)。
    • RsiPeriod = 47。
    • StopLossPoints = 26。
    • TakeProfitPoints = 76。
    • TrailingStopPoints = 0(关闭)。
  • 过滤器标签
    • 类型:基于 RSI 结构的趋势跟随入场。
    • 方向:双向。
    • 指标:RSI、EMA 趋势过滤器。
    • 止损:有。
    • 复杂度:中等(多条件摆动识别)。
    • 周期:日内(M15 + H4)。
    • 季节性:无。
    • 神经网络:无。
    • 背离:无。
    • 风险等级:中等。

参数

名称 默认值 说明 可优化
CandleType 15 分钟 计算 RSI 与信号的主级别蜡烛序列。
TrendCandleType 240 分钟 作为趋势过滤器的高周期 EMA(替代 MT4 RFTL)。
RsiPeriod 47 主周期上的 RSI 长度。
StopLossPoints 26 止损距离(价格步长单位)。
TakeProfitPoints 76 止盈距离(价格步长单位)。
TrailingStopPoints 0 追踪止损距离(价格步长,0 表示禁用)。

说明

  • MT4 自定义指标 RFTL 以 240 分钟图上的 10 周期 EMA 近似实现,必要时可调整周期以贴近原策略表现。
  • 需要确保交易品种的 PriceStepStepPrice 设置正确,使点值计算与实际报价一致。
  • 追踪止损仅在价格较入场价前进超过 TrailingStopPoints 后才会启动,并且不会放宽原始风险区间。
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>
/// DT RSI EXP1 strategy - RSI overbought/oversold with EMA trend filter.
/// Buys when RSI crosses above oversold level while above EMA trend.
/// Sells when RSI crosses below overbought level while below EMA trend.
/// </summary>
public class DtRsiExp1Strategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevRsi;
	private bool _hasPrev;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public DtRsiExp1Strategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI period", "Indicators");

		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");

		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "RSI oversold level", "Indicators");

		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "RSI overbought level", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevRsi = 0m; _hasPrev = false; }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ema, ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);
	}

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevRsi = rsi;
			_hasPrev = true;
			return;
		}

		// RSI crosses above oversold + bullish trend
		if (_prevRsi <= Oversold && rsi > Oversold && close > ema && Position == 0)
		{
			BuyMarket();
		}
		// RSI crosses below overbought + bearish trend
		else if (_prevRsi >= Overbought && rsi < Overbought && close < ema && Position == 0)
		{
			SellMarket();
		}

		_prevRsi = rsi;
	}
}