Estrategia de Cruce EMA con RSI y Distancia
Esta estrategia utiliza múltiples EMA y RSI para generar señales largas y cortas, verificando la distancia entre las EMA rápidas para confirmar la fortaleza de la tendencia.
Detalles
- Criterios de entrada:
- EMA5 por encima de EMA13.
- EMA40 por encima de EMA55.
- RSI por encima de 50 y por encima de su SMA.
- La distancia entre EMA5 y EMA13 por encima de su promedio y la distancia EMA40-EMA13 en aumento.
- Precio de cierre por encima de EMA5.
- Largo/Corto: Largo y corto.
- Criterios de salida:
- La señal cambia a neutral o dirección opuesta.
- Stops: No.
- Valores predeterminados:
EmaShortLength= 5EmaMediumLength= 13EmaLong1Length= 40EmaLong2Length= 55RsiLength= 14RsiAverageLength= 14DistanceLength= 5
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: EMA, RSI
- Stops: No
- Complejidad: Medio
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// EMA crossover with RSI and distance strategy.
/// The long signal needs EMA short above EMA medium, EMA long1 above EMA long2, RSI above 50 and above its SMA, the distance
/// between EMA short and EMA medium above its average over DistanceLength, a growing distance between EMA long1 and EMA medium
/// and the close above EMA short. The short signal mirrors it. A position is opened on its signal, reversing an opposite one,
/// and closed as soon as the signal turns neutral.
/// </summary>
public class EmaCrossoverRsiDistanceStrategy : Strategy
{
private readonly StrategyParam<int> _emaShortLength;
private readonly StrategyParam<int> _emaMediumLength;
private readonly StrategyParam<int> _emaLong1Length;
private readonly StrategyParam<int> _emaLong2Length;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _rsiAverageLength;
private readonly StrategyParam<int> _distanceLength;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _rsiAverage;
private SimpleMovingAverage _distanceAverage;
private decimal? _prevLongDistance;
/// <summary>
/// Short EMA length.
/// </summary>
public int EmaShortLength
{
get => _emaShortLength.Value;
set => _emaShortLength.Value = value;
}
/// <summary>
/// Medium EMA length.
/// </summary>
public int EmaMediumLength
{
get => _emaMediumLength.Value;
set => _emaMediumLength.Value = value;
}
/// <summary>
/// First long EMA length.
/// </summary>
public int EmaLong1Length
{
get => _emaLong1Length.Value;
set => _emaLong1Length.Value = value;
}
/// <summary>
/// Second long EMA length.
/// </summary>
public int EmaLong2Length
{
get => _emaLong2Length.Value;
set => _emaLong2Length.Value = value;
}
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Length of the RSI SMA.
/// </summary>
public int RsiAverageLength
{
get => _rsiAverageLength.Value;
set => _rsiAverageLength.Value = value;
}
/// <summary>
/// Length of the average of the short-medium EMA distance.
/// </summary>
public int DistanceLength
{
get => _distanceLength.Value;
set => _distanceLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EmaCrossoverRsiDistanceStrategy()
{
_emaShortLength = Param(nameof(EmaShortLength), 5)
.SetGreaterThanZero()
.SetDisplay("EMA Short", "Short EMA length", "Indicators");
_emaMediumLength = Param(nameof(EmaMediumLength), 13)
.SetGreaterThanZero()
.SetDisplay("EMA Medium", "Medium EMA length", "Indicators");
_emaLong1Length = Param(nameof(EmaLong1Length), 40)
.SetGreaterThanZero()
.SetDisplay("EMA Long 1", "First long EMA length", "Indicators");
_emaLong2Length = Param(nameof(EmaLong2Length), 55)
.SetGreaterThanZero()
.SetDisplay("EMA Long 2", "Second long EMA length", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "Indicators");
_rsiAverageLength = Param(nameof(RsiAverageLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Average", "Length of the RSI SMA", "Indicators");
_distanceLength = Param(nameof(DistanceLength), 5)
.SetGreaterThanZero()
.SetDisplay("Distance Length", "Length of the EMA distance average", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevLongDistance = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevLongDistance = null;
var emaShort = new ExponentialMovingAverage { Length = EmaShortLength };
var emaMedium = new ExponentialMovingAverage { Length = EmaMediumLength };
var emaLong1 = new ExponentialMovingAverage { Length = EmaLong1Length };
var emaLong2 = new ExponentialMovingAverage { Length = EmaLong2Length };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
_rsiAverage = new SimpleMovingAverage { Length = RsiAverageLength };
_distanceAverage = new SimpleMovingAverage { Length = DistanceLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(emaShort, emaMedium, emaLong1, emaLong2, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaShort);
DrawIndicator(area, emaMedium);
DrawIndicator(area, emaLong1);
DrawIndicator(area, emaLong2);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaShort, decimal emaMedium, decimal emaLong1, decimal emaLong2, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
var rsiAverageValue = _rsiAverage.Process(new DecimalIndicatorValue(_rsiAverage, rsi, candle.OpenTime) { IsFinal = true });
var distance = Math.Abs(emaShort - emaMedium);
var distanceAverageValue = _distanceAverage.Process(new DecimalIndicatorValue(_distanceAverage, distance, candle.OpenTime) { IsFinal = true });
var longDistance = Math.Abs(emaLong1 - emaMedium);
var prevLongDistance = _prevLongDistance;
_prevLongDistance = longDistance;
if (!rsiAverageValue.IsFormed || !distanceAverageValue.IsFormed || prevLongDistance is not decimal prevDistance)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsiAverage = rsiAverageValue.GetValue<decimal>();
var distanceAverage = distanceAverageValue.GetValue<decimal>();
var close = candle.ClosePrice;
var strongTrend = distance > distanceAverage && longDistance > prevDistance;
var longSignal = emaShort > emaMedium && emaLong1 > emaLong2 && rsi > 50m && rsi > rsiAverage && strongTrend && close > emaShort;
var shortSignal = emaShort < emaMedium && emaLong1 < emaLong2 && rsi < 50m && rsi < rsiAverage && strongTrend && close < emaShort;
if (longSignal)
{
if (Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
}
else if (shortSignal)
{
if (Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
else if (Position > 0)
{
SellMarket(Position);
}
else if (Position < 0)
{
BuyMarket(-Position);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, SimpleMovingAverage, DecimalIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class ema_crossover_rsi_distance_strategy(Strategy):
"""
EMA crossover with RSI and distance strategy.
The long signal needs EMA short above EMA medium, EMA long1 above EMA long2, RSI above 50 and above its SMA, the distance
between EMA short and EMA medium above its average over DistanceLength, a growing distance between EMA long1 and EMA medium
and the close above EMA short. The short signal mirrors it. A position is opened on its signal, reversing an opposite one,
and closed as soon as the signal turns neutral.
"""
def __init__(self):
super(ema_crossover_rsi_distance_strategy, self).__init__()
self._ema_short_length = self.Param("EmaShortLength", 5).SetGreaterThanZero().SetDisplay("EMA Short", "Short EMA length", "Indicators")
self._ema_medium_length = self.Param("EmaMediumLength", 13).SetGreaterThanZero().SetDisplay("EMA Medium", "Medium EMA length", "Indicators")
self._ema_long1_length = self.Param("EmaLong1Length", 40).SetGreaterThanZero().SetDisplay("EMA Long 1", "First long EMA length", "Indicators")
self._ema_long2_length = self.Param("EmaLong2Length", 55).SetGreaterThanZero().SetDisplay("EMA Long 2", "Second long EMA length", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "Indicators")
self._rsi_average_length = self.Param("RsiAverageLength", 14).SetGreaterThanZero().SetDisplay("RSI Average", "Length of the RSI SMA", "Indicators")
self._distance_length = self.Param("DistanceLength", 5).SetGreaterThanZero().SetDisplay("Distance Length", "Length of the EMA distance average", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._rsi_average = None
self._distance_average = None
self._prev_long_distance = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ema_crossover_rsi_distance_strategy, self).OnReseted()
self._prev_long_distance = None
def OnStarted2(self, time):
super(ema_crossover_rsi_distance_strategy, self).OnStarted2(time)
self._prev_long_distance = None
ema_short = ExponentialMovingAverage()
ema_short.Length = self._ema_short_length.Value
ema_medium = ExponentialMovingAverage()
ema_medium.Length = self._ema_medium_length.Value
ema_long1 = ExponentialMovingAverage()
ema_long1.Length = self._ema_long1_length.Value
ema_long2 = ExponentialMovingAverage()
ema_long2.Length = self._ema_long2_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
self._rsi_average = SimpleMovingAverage()
self._rsi_average.Length = self._rsi_average_length.Value
self._distance_average = SimpleMovingAverage()
self._distance_average.Length = self._distance_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema_short, ema_medium, ema_long1, ema_long2, rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_short)
self.DrawIndicator(area, ema_medium)
self.DrawIndicator(area, ema_long1)
self.DrawIndicator(area, ema_long2)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_short_value, ema_medium_value, ema_long1_value, ema_long2_value, rsi_value):
if candle.State != CandleStates.Finished:
return
ema_short = float(ema_short_value)
ema_medium = float(ema_medium_value)
ema_long1 = float(ema_long1_value)
ema_long2 = float(ema_long2_value)
rsi = float(rsi_value)
rsi_input = DecimalIndicatorValue(self._rsi_average, Decimal(rsi), candle.OpenTime)
rsi_input.IsFinal = True
rsi_average_value = self._rsi_average.Process(rsi_input)
distance = abs(ema_short - ema_medium)
distance_input = DecimalIndicatorValue(self._distance_average, Decimal(distance), candle.OpenTime)
distance_input.IsFinal = True
distance_average_value = self._distance_average.Process(distance_input)
long_distance = abs(ema_long1 - ema_medium)
prev_long_distance = self._prev_long_distance
self._prev_long_distance = long_distance
if not rsi_average_value.IsFormed or not distance_average_value.IsFormed or prev_long_distance is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
rsi_average = float(rsi_average_value.GetValue[Decimal](None))
distance_average = float(distance_average_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
strong_trend = distance > distance_average and long_distance > prev_long_distance
long_signal = ema_short > ema_medium and ema_long1 > ema_long2 and rsi > 50 and rsi > rsi_average and strong_trend and close > ema_short
short_signal = ema_short < ema_medium and ema_long1 < ema_long2 and rsi < 50 and rsi < rsi_average and strong_trend and close < ema_short
if long_signal:
if self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal:
if self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
def CreateClone(self):
return ema_crossover_rsi_distance_strategy()