Esta estrategia de momentum opera tanto posiciones largas como cortas en un marco temporal de 3 horas. Las configuraciones largas requieren que el precio se mantenga por encima de las medias móviles de 100 y 500 períodos y pueden filtrarse por RSI, ADX, ATR y alineación de tendencia. Las entradas cortas buscan que el precio rompa por debajo de la Banda de Bollinger inferior mientras permanece bajo ambas medias, con confirmación ATR opcional y la posibilidad de bloquear cortos durante tendencias alcistas fuertes.
Detalles
Criterios de entrada:
Largo: precio por encima de MA100 y MA500, alineación de tendencia opcional, RSI por encima de su valor suavizado, ADX por encima de su valor suavizado y ATR por encima de su valor suavizado.
Corto: precio por debajo de MA100 y MA500, por debajo de la Banda de Bollinger inferior, RSI por debajo del umbral, ATR por encima de su valor suavizado y bloqueo de tendencia alcista opcional.
Largo/Corto: Ambos lados.
Criterios de salida:
Largo: stop-loss en slPercentLong% por debajo de la entrada; cierre anticipado si el precio cae por debajo de MA500.
Corto: stop-loss y take-profit basados en porcentajes slPercentShort y tpPercentShort.
Stops: Sí.
Valores predeterminados:
slPercentLong = 3
slPercentShort = 3
tpPercentShort = 4
rsiLengthLong = 14
rsiLengthShort = 14
adxLength = 14
atrLength = 14
bbLength = 20
Filtros:
Categoría: Momentum
Dirección: Ambos
Indicadores: Múltiples
Stops: Sí
Complejidad: Intermedio
Marco temporal: Medio plazo
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>
/// Momentum strategy with EMA cross, RSI filter and cooldown.
/// </summary>
public class MomentumLongShortStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _barsFromSignal;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MomentumLongShortStrategy()
{
_fastLength = Param(nameof(FastLength), 20).SetGreaterThanZero();
_slowLength = Param(nameof(SlowLength), 50).SetGreaterThanZero();
_rsiLength = Param(nameof(RsiLength), 14).SetGreaterThanZero();
_signalCooldownBars = Param(nameof(SignalCooldownBars), 10).SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_hasPrev = false;
_barsFromSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_prevFast = 0m;
_prevSlow = 0m;
_hasPrev = false;
_barsFromSignal = SignalCooldownBars;
var maFast = new ExponentialMovingAverage { Length = FastLength };
var maSlow = new ExponentialMovingAverage { Length = SlowLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(maFast, maSlow, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal maFast, decimal maSlow, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
_barsFromSignal++;
if (!_hasPrev)
{
_prevFast = maFast;
_prevSlow = maSlow;
_hasPrev = true;
return;
}
var crossUp = _prevFast <= _prevSlow && maFast > maSlow;
var crossDown = _prevFast >= _prevSlow && maFast < maSlow;
if (_barsFromSignal >= SignalCooldownBars && crossUp && rsiValue <= 65m && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_barsFromSignal = 0;
}
else if (_barsFromSignal >= SignalCooldownBars && crossDown && rsiValue >= 35m && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_barsFromSignal = 0;
}
_prevFast = maFast;
_prevSlow = maSlow;
}
}