Стратегия Tiger EMA ADX RSI
Эта стратегия следует за трендом, используя пересечение двух экспоненциальных скользящих средних (EMA) и фильтрацию по индикаторам Average Directional Index (ADX) и Relative Strength Index (RSI). Быстрая EMA сравнивается с медленной EMA для определения направления тренда. Сделки разрешаются только когда ADX превышает настраиваемый порог, а значение RSI находится между верхней и нижней границами.
При отсутствии открытой позиции и выполнении всех условий стратегия входит в направлении тренда. Для каждой сделки задаются фиксированные уровни тейк-профита и стоп-лосса относительно цены входа. Позиция закрывается при достижении одного из уровней. Размер заявки задаётся свойством Volume стратегии.
Параметры
- Fast EMA – период быстрой экспоненциальной средней.
- Slow EMA – период медленной экспоненциальной средней.
- ADX Period – период расчёта ADX.
- ADX Threshold – минимальное значение ADX для торговли.
- RSI Period – период расчёта RSI.
- RSI Upper – максимально допустимое значение RSI для длинных входов.
- RSI Lower – минимально допустимое значение RSI для коротких входов.
- Take Profit – расстояние до тейк-профита в ценовых пунктах.
- Stop Loss – расстояние до стоп-лосса в ценовых пунктах.
- Candle Type – тип свечей для расчётов индикаторов.
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>
/// Trend following strategy using EMA crossover with momentum and RSI filters.
/// </summary>
public class TigerEmaAdxRsiStrategy : Strategy
{
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiUpper;
private readonly StrategyParam<decimal> _rsiLower;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _takePrice;
private decimal _stopPrice;
private decimal? _prevFast;
private decimal? _prevSlow;
private int _cooldownRemaining;
public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal RsiUpper { get => _rsiUpper.Value; set => _rsiUpper.Value = value; }
public decimal RsiLower { get => _rsiLower.Value; set => _rsiLower.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public TigerEmaAdxRsiStrategy()
{
_fastMaPeriod = Param(nameof(FastMaPeriod), 21)
.SetDisplay("Fast EMA", "Fast EMA period", "Parameters")
.SetOptimize(5, 50, 5);
_slowMaPeriod = Param(nameof(SlowMaPeriod), 89)
.SetDisplay("Slow EMA", "Slow EMA period", "Parameters")
.SetOptimize(50, 200, 10);
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("Momentum Period", "Momentum confirmation period", "Parameters")
.SetOptimize(7, 28, 7);
_adxThreshold = Param(nameof(AdxThreshold), 52m)
.SetDisplay("Momentum Threshold", "Minimum RSI momentum value", "Parameters")
.SetOptimize(50m, 70m, 5m);
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI calculation period", "Parameters")
.SetOptimize(7, 28, 7);
_rsiUpper = Param(nameof(RsiUpper), 65m)
.SetDisplay("RSI Upper", "Upper RSI bound", "Parameters")
.SetOptimize(60m, 80m, 5m);
_rsiLower = Param(nameof(RsiLower), 35m)
.SetDisplay("RSI Lower", "Lower RSI bound", "Parameters")
.SetOptimize(20m, 40m, 5m);
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetDisplay("Take Profit", "Take profit distance", "Risk")
.SetOptimize(100m, 1000m, 100m);
_stopLoss = Param(nameof(StopLoss), 200m)
.SetDisplay("Stop Loss", "Stop loss distance", "Risk")
.SetOptimize(50m, 500m, 50m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "Parameters");
_cooldownBars = Param(nameof(CooldownBars), 3)
.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();
_takePrice = 0m;
_stopPrice = 0m;
_prevFast = null;
_prevSlow = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
var fastEma = new ExponentialMovingAverage { Length = FastMaPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowMaPeriod };
var momentum = new RelativeStrengthIndex { Length = AdxPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, momentum, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal momentumValue, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (_prevFast is not decimal prevFast || _prevSlow is not decimal prevSlow)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var crossUp = prevFast <= prevSlow && fast > slow;
var crossDown = prevFast >= prevSlow && fast < slow;
var canLong = momentumValue >= AdxThreshold && rsi > RsiLower && rsi < RsiUpper;
var canShort = momentumValue <= 100m - AdxThreshold && rsi > RsiLower && rsi < RsiUpper;
if (Position == 0 && _cooldownRemaining == 0)
{
if (crossUp && canLong)
{
BuyMarket();
_takePrice = candle.ClosePrice + TakeProfit;
_stopPrice = candle.ClosePrice - StopLoss;
_cooldownRemaining = CooldownBars;
}
else if (crossDown && canShort)
{
SellMarket();
_takePrice = candle.ClosePrice - TakeProfit;
_stopPrice = candle.ClosePrice + StopLoss;
_cooldownRemaining = CooldownBars;
}
}
else if (Position > 0)
{
if (candle.ClosePrice >= _takePrice || candle.ClosePrice <= _stopPrice || crossDown)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
else if (Position < 0)
{
if (candle.ClosePrice <= _takePrice || candle.ClosePrice >= _stopPrice || crossUp)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class tiger_ema_adx_rsi_strategy(Strategy):
def __init__(self):
super(tiger_ema_adx_rsi_strategy, self).__init__()
self._fast_ma = self.Param("FastMaPeriod", 21) \
.SetDisplay("Fast EMA", "Fast EMA period", "Parameters")
self._slow_ma = self.Param("SlowMaPeriod", 89) \
.SetDisplay("Slow EMA", "Slow EMA period", "Parameters")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("Momentum Period", "Momentum RSI period", "Parameters")
self._adx_threshold = self.Param("AdxThreshold", 52.0) \
.SetDisplay("Momentum Threshold", "Min RSI momentum value", "Parameters")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Parameters")
self._rsi_upper = self.Param("RsiUpper", 65.0) \
.SetDisplay("RSI Upper", "Upper RSI bound", "Parameters")
self._rsi_lower = self.Param("RsiLower", 35.0) \
.SetDisplay("RSI Lower", "Lower RSI bound", "Parameters")
self._take_profit = self.Param("TakeProfit", 500.0) \
.SetDisplay("Take Profit", "Take profit distance", "Risk")
self._stop_loss = self.Param("StopLoss", 200.0) \
.SetDisplay("Stop Loss", "Stop loss distance", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "Parameters")
self._cooldown_bars = self.Param("CooldownBars", 3) \
.SetDisplay("Cooldown Bars", "Candles to wait after position change", "Trading")
self._take_price = 0.0
self._stop_price = 0.0
self._prev_fast = None
self._prev_slow = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(tiger_ema_adx_rsi_strategy, self).OnReseted()
self._take_price = 0.0
self._stop_price = 0.0
self._prev_fast = None
self._prev_slow = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(tiger_ema_adx_rsi_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_ma.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_ma.Value
momentum = RelativeStrengthIndex()
momentum.Length = self._adx_period.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
sub = self.SubscribeCandles(self.candle_type)
sub.Bind(fast_ema, slow_ema, momentum, rsi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast, slow, momentum_val, rsi_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast)
slow = float(slow)
momentum_val = float(momentum_val)
rsi_val = float(rsi_val)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fast
self._prev_slow = slow
return
close = float(candle.ClosePrice)
cross_up = self._prev_fast <= self._prev_slow and fast > slow
cross_down = self._prev_fast >= self._prev_slow and fast < slow
adx_thresh = float(self._adx_threshold.Value)
rsi_upper = float(self._rsi_upper.Value)
rsi_lower = float(self._rsi_lower.Value)
can_long = momentum_val >= adx_thresh and rsi_val > rsi_lower and rsi_val < rsi_upper
can_short = momentum_val <= 100.0 - adx_thresh and rsi_val > rsi_lower and rsi_val < rsi_upper
if self.Position == 0 and self._cooldown_remaining == 0:
if cross_up and can_long:
self.BuyMarket()
self._take_price = close + float(self._take_profit.Value)
self._stop_price = close - float(self._stop_loss.Value)
self._cooldown_remaining = self._cooldown_bars.Value
elif cross_down and can_short:
self.SellMarket()
self._take_price = close - float(self._take_profit.Value)
self._stop_price = close + float(self._stop_loss.Value)
self._cooldown_remaining = self._cooldown_bars.Value
elif self.Position > 0:
if close >= self._take_price or close <= self._stop_price or cross_down:
self.SellMarket()
self._cooldown_remaining = self._cooldown_bars.Value
elif self.Position < 0:
if close <= self._take_price or close >= self._stop_price or cross_up:
self.BuyMarket()
self._cooldown_remaining = self._cooldown_bars.Value
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return tiger_ema_adx_rsi_strategy()