MaRsi Trigger 策略
该策略将快慢指数移动平均线(EMA)和 RSI 结合以识别趋势反转。 当快速 EMA 和 RSI 同时高于慢速指标时,认为市场看涨并开多头仓位。 当两者都低于慢速指标时,开空头仓位。参数可控制是否允许做多、做空及其平仓。
详情
- 入场条件:
- 多头:快速 EMA > 慢速 EMA 且快速 RSI > 慢速 RSI,并且上一趋势为看空。
- 空头:快速 EMA < 慢速 EMA 且快速 RSI < 慢速 RSI,并且上一趋势为看多。
- 出场条件:
- 多头:趋势转为看空且允许平多。
- 空头:趋势转为看多且允许平空。
- 指标:EMA、RSI。
- 止损:未包含。
- 时间框架:默认使用4小时K线。
- 参数:
FastRsiPeriod= 3SlowRsiPeriod= 13FastMaPeriod= 5SlowMaPeriod= 10AllowBuyEntry= trueAllowSellEntry= trueAllowLongExit= trueAllowShortExit= true
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on moving averages and RelativeStrengthIndex crossover.
/// </summary>
public class MaRsiTriggerStrategy : Strategy
{
private readonly StrategyParam<int> _fastRsiPeriod;
private readonly StrategyParam<int> _slowRsiPeriod;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<bool> _allowBuyEntry;
private readonly StrategyParam<bool> _allowSellEntry;
private readonly StrategyParam<bool> _allowLongExit;
private readonly StrategyParam<bool> _allowShortExit;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _minRsiSpread;
private readonly StrategyParam<decimal> _minMaSpreadPercent;
private readonly StrategyParam<int> _cooldownBars;
private int _previousTrend;
private int _cooldownRemaining;
/// <summary>
/// Fast RelativeStrengthIndex period.
/// </summary>
public int FastRsiPeriod { get => _fastRsiPeriod.Value; set => _fastRsiPeriod.Value = value; }
/// <summary>
/// Slow RelativeStrengthIndex period.
/// </summary>
public int SlowRsiPeriod { get => _slowRsiPeriod.Value; set => _slowRsiPeriod.Value = value; }
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool AllowBuyEntry { get => _allowBuyEntry.Value; set => _allowBuyEntry.Value = value; }
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool AllowSellEntry { get => _allowSellEntry.Value; set => _allowSellEntry.Value = value; }
/// <summary>
/// Allow closing long positions when trend becomes bearish.
/// </summary>
public bool AllowLongExit { get => _allowLongExit.Value; set => _allowLongExit.Value = value; }
/// <summary>
/// Allow closing short positions when trend becomes bullish.
/// </summary>
public bool AllowShortExit { get => _allowShortExit.Value; set => _allowShortExit.Value = value; }
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Minimum spread between fast and slow RelativeStrengthIndex values.
/// </summary>
public decimal MinRsiSpread { get => _minRsiSpread.Value; set => _minRsiSpread.Value = value; }
/// <summary>
/// Minimum normalized EMA spread.
/// </summary>
public decimal MinMaSpreadPercent { get => _minMaSpreadPercent.Value; set => _minMaSpreadPercent.Value = value; }
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="MaRsiTriggerStrategy"/> class.
/// </summary>
public MaRsiTriggerStrategy()
{
_fastRsiPeriod = Param(nameof(FastRsiPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Fast RelativeStrengthIndex Period", "Period of the fast RelativeStrengthIndex", "RelativeStrengthIndex")
.SetOptimize(2, 10, 1);
_slowRsiPeriod = Param(nameof(SlowRsiPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("Slow RelativeStrengthIndex Period", "Period of the slow RelativeStrengthIndex", "RelativeStrengthIndex")
.SetOptimize(10, 30, 1);
_fastMaPeriod = Param(nameof(FastMaPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Period", "Period of the fast EMA", "MA")
.SetOptimize(3, 15, 1);
_slowMaPeriod = Param(nameof(SlowMaPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Period", "Period of the slow EMA", "MA")
.SetOptimize(5, 30, 1);
_allowBuyEntry = Param(nameof(AllowBuyEntry), true)
.SetDisplay("Allow Buy Entry", "Enable entering long positions", "General");
_allowSellEntry = Param(nameof(AllowSellEntry), true)
.SetDisplay("Allow Sell Entry", "Enable entering short positions", "General");
_allowLongExit = Param(nameof(AllowLongExit), true)
.SetDisplay("Allow Long Exit", "Enable exiting long positions", "General");
_allowShortExit = Param(nameof(AllowShortExit), true)
.SetDisplay("Allow Short Exit", "Enable exiting short positions", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_minRsiSpread = Param(nameof(MinRsiSpread), 6m)
.SetDisplay("Minimum RelativeStrengthIndex Spread", "Minimum spread between fast and slow RelativeStrengthIndex values", "Filters");
_minMaSpreadPercent = Param(nameof(MinMaSpreadPercent), 0.0025m)
.SetDisplay("Minimum EMA Spread %", "Minimum normalized spread between fast and slow EMA values", "Filters");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousTrend = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastRsi = new RelativeStrengthIndex { Length = FastRsiPeriod };
var slowRsi = new RelativeStrengthIndex { Length = SlowRsiPeriod };
var fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
var slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastRsi, slowRsi, fastMa, slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawIndicator(area, fastRsi);
DrawIndicator(area, slowRsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastRsiValue, decimal slowRsiValue, decimal fastMaValue, decimal slowMaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var normalizedMaSpread = slowMaValue != 0m ? Math.Abs(fastMaValue - slowMaValue) / slowMaValue : 0m;
var rsiSpread = Math.Abs(fastRsiValue - slowRsiValue);
var trend = 0;
if (fastMaValue > slowMaValue && normalizedMaSpread >= MinMaSpreadPercent)
trend++;
else if (fastMaValue < slowMaValue && normalizedMaSpread >= MinMaSpreadPercent)
trend--;
if (fastRsiValue > slowRsiValue && rsiSpread >= MinRsiSpread)
trend++;
else if (fastRsiValue < slowRsiValue && rsiSpread >= MinRsiSpread)
trend--;
if (_cooldownRemaining == 0)
{
if (_previousTrend < 0 && trend > 0)
{
if (AllowShortExit && Position < 0)
BuyMarket();
if (AllowBuyEntry && Position <= 0)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
}
else if (_previousTrend > 0 && trend < 0)
{
if (AllowLongExit && Position > 0)
SellMarket();
if (AllowSellEntry && Position >= 0)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
}
if (trend != 0)
_previousTrend = trend;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_rsi_trigger_strategy(Strategy):
def __init__(self):
super(ma_rsi_trigger_strategy, self).__init__()
self._fast_rsi_period = self.Param("FastRsiPeriod", 3) \
.SetDisplay("Fast RSI Period", "Period of the fast RSI", "RSI")
self._slow_rsi_period = self.Param("SlowRsiPeriod", 13) \
.SetDisplay("Slow RSI Period", "Period of the slow RSI", "RSI")
self._fast_ma_period = self.Param("FastMaPeriod", 5) \
.SetDisplay("Fast EMA Period", "Period of the fast EMA", "MA")
self._slow_ma_period = self.Param("SlowMaPeriod", 10) \
.SetDisplay("Slow EMA Period", "Period of the slow EMA", "MA")
self._allow_buy_entry = self.Param("AllowBuyEntry", True) \
.SetDisplay("Allow Buy Entry", "Enable entering long positions", "General")
self._allow_sell_entry = self.Param("AllowSellEntry", True) \
.SetDisplay("Allow Sell Entry", "Enable entering short positions", "General")
self._allow_long_exit = self.Param("AllowLongExit", True) \
.SetDisplay("Allow Long Exit", "Enable exiting long positions", "General")
self._allow_short_exit = self.Param("AllowShortExit", True) \
.SetDisplay("Allow Short Exit", "Enable exiting short positions", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._min_rsi_spread = self.Param("MinRsiSpread", 6.0) \
.SetDisplay("Minimum RSI Spread", "Minimum spread between fast and slow RSI values", "Filters")
self._min_ma_spread_percent = self.Param("MinMaSpreadPercent", 0.0025) \
.SetDisplay("Minimum EMA Spread %", "Minimum normalized spread between fast and slow EMA values", "Filters")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._previous_trend = 0
self._cooldown_remaining = 0
@property
def fast_rsi_period(self):
return self._fast_rsi_period.Value
@property
def slow_rsi_period(self):
return self._slow_rsi_period.Value
@property
def fast_ma_period(self):
return self._fast_ma_period.Value
@property
def slow_ma_period(self):
return self._slow_ma_period.Value
@property
def allow_buy_entry(self):
return self._allow_buy_entry.Value
@property
def allow_sell_entry(self):
return self._allow_sell_entry.Value
@property
def allow_long_exit(self):
return self._allow_long_exit.Value
@property
def allow_short_exit(self):
return self._allow_short_exit.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def min_rsi_spread(self):
return self._min_rsi_spread.Value
@property
def min_ma_spread_percent(self):
return self._min_ma_spread_percent.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(ma_rsi_trigger_strategy, self).OnReseted()
self._previous_trend = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ma_rsi_trigger_strategy, self).OnStarted2(time)
fast_rsi = RelativeStrengthIndex()
fast_rsi.Length = self.fast_rsi_period
slow_rsi = RelativeStrengthIndex()
slow_rsi.Length = self.slow_rsi_period
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.fast_ma_period
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self.slow_ma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_rsi, slow_rsi, fast_ma, slow_ma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast_rsi_val, slow_rsi_val, fast_ma_val, slow_ma_val):
if candle.State != CandleStates.Finished:
return
fast_rsi_val = float(fast_rsi_val)
slow_rsi_val = float(slow_rsi_val)
fast_ma_val = float(fast_ma_val)
slow_ma_val = float(slow_ma_val)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
normalized_ma_spread = abs(fast_ma_val - slow_ma_val) / slow_ma_val if slow_ma_val != 0 else 0.0
rsi_spread = abs(fast_rsi_val - slow_rsi_val)
trend = 0
min_ma_sp = float(self.min_ma_spread_percent)
min_rsi_sp = float(self.min_rsi_spread)
if fast_ma_val > slow_ma_val and normalized_ma_spread >= min_ma_sp:
trend += 1
elif fast_ma_val < slow_ma_val and normalized_ma_spread >= min_ma_sp:
trend -= 1
if fast_rsi_val > slow_rsi_val and rsi_spread >= min_rsi_sp:
trend += 1
elif fast_rsi_val < slow_rsi_val and rsi_spread >= min_rsi_sp:
trend -= 1
if self._cooldown_remaining == 0:
if self._previous_trend < 0 and trend > 0:
if self.allow_short_exit and self.Position < 0:
self.BuyMarket()
if self.allow_buy_entry and self.Position <= 0:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif self._previous_trend > 0 and trend < 0:
if self.allow_long_exit and self.Position > 0:
self.SellMarket()
if self.allow_sell_entry and self.Position >= 0:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
if trend != 0:
self._previous_trend = trend
def CreateClone(self):
return ma_rsi_trigger_strategy()