Ver en GitHub

Estrategia FiveMinuteRsiCci

FiveMinuteRsiCciStrategy es un puerto StockSharp del asesor experto MetaTrader 4 5Mins Rsi Cci EA.mq4. El script original intercambia velas de cinco minutos combinando un cruce de umbral RSI con un filtro de media móvil suavizado/EMA y la polaridad de dos indicadores CCI. La versión C# mantiene la misma lógica de decisión mientras utiliza el nivel alto API de StockSharp para suscripciones de datos, vinculación de indicadores y gestión de riesgos.

Lógica comercial

  1. Suscríbase al tipo de vela configurado (período de tiempo de cinco minutos de forma predeterminada) y actualice cinco indicadores en tiempo real: RSI, un MA suavizado del precio de apertura, un EMA del precio de apertura, además de CCI rápidos y lentos calculados a partir de precios típicos.
  2. Cada vela terminada se evalúa solo cuando no hay ninguna posición abierta y el diferencial de oferta/demanda actual es inferior a MaxSpreadPoints (convertido a unidades de precio).
  3. Una señal larga requiere:
    • el MA suavizado por encima del EMA,
    • el RSI cruzando hacia arriba a través de BullishRsiLevel entre la vela anterior y la actual,
    • ambos valores CCI superiores a cero.
  4. Una señal corta requiere condiciones inversas (MA suavizada por debajo de EMA, RSI cruzando hacia abajo a través de BearishRsiLevel, ambos CCI por debajo de cero).
  5. El volumen de la orden reproduce el tamaño de posición dinámica del EA: LotCoefficient × sqrt(Equity / EquityDivisor) redondeado al paso de volumen del instrumento y restringido por VolumeMin/VolumeMax.
  6. La lógica de protección es manejada por StartProtection, que adjunta distancias de stop-loss, take-profit y trailing-stop convertidas de MetaTrader puntos a compensaciones de precios absolutos.

Parámetros

Parámetro Predeterminado Descripción
CandleType TimeSpan.FromMinutes(5).TimeFrame() Plazo utilizado para las actualizaciones de indicadores y la evaluación de señales.
RsiPeriod 14 Número de velas utilizadas en el cálculo RSI.
FastSmmaPeriod 2 Período de media móvil suavizada rápida aplicada a los precios de apertura.
SlowEmaPeriod 6 Período de la EMA lenta aplicada a los precios de apertura.
FastCciPeriod 34 Periodo del rápido CCI computado a partir del precio típico (H+L+C)/3.
SlowCciPeriod 175 Periodo del CCI lento calculado a partir del precio típico.
BullishRsiLevel 55 RSI umbral que se debe cruzar hacia arriba para armar una entrada larga.
BearishRsiLevel 45 RSI umbral que se debe cruzar hacia abajo para armar una entrada corta.
StopLossPoints 60 Distancia de stop-loss en MetaTrader puntos (convertida a precio absoluto). Establezca en 0 para desactivar.
TakeProfitPoints 0 Distancia de obtención de beneficios en MetaTrader puntos. Zero mantiene el comportamiento original EA (sin TP).
TrailingStopPoints 20 Distancia del trailing-stop en MetaTrader puntos. Zero desactiva el seguimiento.
LotCoefficient 0.01 Coeficiente base utilizado en la fórmula de dimensionamiento de posición dinámica.
EquityDivisor 10 Divisor dentro de la raíz cuadrada para el tamaño basado en acciones (sqrt(Equity / EquityDivisor)).
MaxSpreadPoints 18 Spread máximo permitido (en MetaTrader puntos). Las órdenes se omiten hasta que el diferencial se reduce.

Notas

  • El filtro de dispersión se basa en datos de nivel 1; Si las mejores cotizaciones de oferta/demanda no están disponibles, la estrategia espera antes de abrir nuevas posiciones.
  • La conversión de punto a precio escala automáticamente en PriceStep y la precisión del instrumento (5/3 instrumentos decimales multiplican el paso por 10) para reflejar el valor Point de MetaTrader.
  • Las paradas y el seguimiento se gestionan a través del motor de protección integrado de StockSharp con salidas de mercado, lo que coincide con el uso de órdenes de mercado por parte de EA para las actualizaciones de las paradas de seguimiento.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

/// <summary>
/// Finished-bar RSI threshold crossings confirmed by open-price SMMA/EMA and two typical-price CCIs.
/// </summary>
public class FiveMinuteRsiCciStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _fastSmmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _fastCciPeriod;
	private readonly StrategyParam<int> _slowCciPeriod;
	private readonly StrategyParam<decimal> _bullishRsiLevel;
	private readonly StrategyParam<decimal> _bearishRsiLevel;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _trailingStopPoints;
	private readonly StrategyParam<decimal> _lotCoefficient;
	private readonly StrategyParam<decimal> _equityDivisor;
	private readonly StrategyParam<decimal> _maxSpreadPoints;

	private RelativeStrengthIndex _rsi;
	private SmoothedMovingAverage _smma;
	private ExponentialMovingAverage _ema;
	private CommodityChannelIndex _fastCci;
	private CommodityChannelIndex _slowCci;
	private decimal? _previousRsi;
	private decimal? _bestBid;
	private decimal? _bestAsk;
	private decimal? _entryPrice;
	private decimal? _trailingStop;
	private bool _exitRequested;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int FastSmmaPeriod { get => _fastSmmaPeriod.Value; set => _fastSmmaPeriod.Value = value; }
	public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }
	public int FastCciPeriod { get => _fastCciPeriod.Value; set => _fastCciPeriod.Value = value; }
	public int SlowCciPeriod { get => _slowCciPeriod.Value; set => _slowCciPeriod.Value = value; }
	public decimal BullishRsiLevel { get => _bullishRsiLevel.Value; set => _bullishRsiLevel.Value = value; }
	public decimal BearishRsiLevel { get => _bearishRsiLevel.Value; set => _bearishRsiLevel.Value = value; }
	public decimal StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
	public decimal TrailingStopPoints { get => _trailingStopPoints.Value; set => _trailingStopPoints.Value = value; }
	public decimal LotCoefficient { get => _lotCoefficient.Value; set => _lotCoefficient.Value = value; }
	public decimal EquityDivisor { get => _equityDivisor.Value; set => _equityDivisor.Value = value; }
	public decimal MaxSpreadPoints { get => _maxSpreadPoints.Value; set => _maxSpreadPoints.Value = value; }

	public FiveMinuteRsiCciStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame());
		_rsiPeriod = Param(nameof(RsiPeriod), 14).SetGreaterThanZero();
		_fastSmmaPeriod = Param(nameof(FastSmmaPeriod), 2).SetGreaterThanZero();
		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 6).SetGreaterThanZero();
		_fastCciPeriod = Param(nameof(FastCciPeriod), 34).SetGreaterThanZero();
		_slowCciPeriod = Param(nameof(SlowCciPeriod), 175).SetGreaterThanZero();
		_bullishRsiLevel = Param(nameof(BullishRsiLevel), 55m);
		_bearishRsiLevel = Param(nameof(BearishRsiLevel), 45m);
		_stopLossPoints = Param(nameof(StopLossPoints), 60m).SetNotNegative();
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 0m).SetNotNegative();
		_trailingStopPoints = Param(nameof(TrailingStopPoints), 20m).SetNotNegative();
		_lotCoefficient = Param(nameof(LotCoefficient), 0.01m).SetGreaterThanZero();
		_equityDivisor = Param(nameof(EquityDivisor), 10m).SetGreaterThanZero();
		_maxSpreadPoints = Param(nameof(MaxSpreadPoints), 18m).SetNotNegative();
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType), (Security, DataType.Level1)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		ResetRuntime();
	}

	private void ResetRuntime()
	{
		_previousRsi = null;
		_bestBid = null;
		_bestAsk = null;
		_entryPrice = null;
		_trailingStop = null;
		_exitRequested = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		ResetRuntime();

		_rsi = new() { Length = RsiPeriod };
		_smma = new() { Length = FastSmmaPeriod };
		_ema = new() { Length = SlowEmaPeriod };
		_fastCci = new() { Length = FastCciPeriod };
		_slowCci = new() { Length = SlowCciPeriod };

		var point = GetPoint();
		var takeProfit = TakeProfitPoints > 0m ? new Unit(TakeProfitPoints * point, UnitTypes.Absolute) : new Unit();
		var stopLoss = StopLossPoints > 0m ? new Unit(StopLossPoints * point, UnitTypes.Absolute) : new Unit();
		StartProtection(takeProfit, stopLoss, useMarketOrders: true, isLocalStop: true);

		foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
		{
			var quotes = new Subscription(DataType.Level1, Security);
			quotes.MarketData.BuildField = field;
			SubscribeLevel1(quotes).Bind(ProcessLevel1).Start();
		}
		SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
	}

	private decimal GetPoint()
	{
		var step = Security?.PriceStep ?? 0m;
		if (step <= 0m)
			throw new InvalidOperationException("Positive Security.PriceStep is required.");
		return Security.Decimals is 3 or 5 ? step * 10m : step;
	}

	private void ProcessLevel1(Level1ChangeMessage message)
	{
		if (message.TryGetDecimal(Level1Fields.BestBidPrice) is decimal bid && bid > 0m)
			_bestBid = bid;
		if (message.TryGetDecimal(Level1Fields.BestAskPrice) is decimal ask && ask > 0m)
			_bestAsk = ask;

		if (Position == 0m || _entryPrice is null || TrailingStopPoints <= 0m || _exitRequested)
			return;

		var gap = TrailingStopPoints * GetPoint();
		var price = Position > 0m ? _bestBid : _bestAsk;
		if (price is null)
			return;

		if (Position > 0m)
		{
			if (price.Value >= _entryPrice.Value + gap)
			{
				var candidate = price.Value - gap;
				if (_trailingStop is null || candidate > _trailingStop.Value)
					_trailingStop = candidate;
			}
			if (_trailingStop is decimal stop && price.Value <= stop)
				RequestExit();
		}
		else
		{
			if (price.Value <= _entryPrice.Value - gap)
			{
				var candidate = price.Value + gap;
				if (_trailingStop is null || candidate < _trailingStop.Value)
					_trailingStop = candidate;
			}
			if (_trailingStop is decimal stop && price.Value >= stop)
				RequestExit();
		}
	}

	private void RequestExit()
	{
		if (_exitRequested || Position == 0m)
			return;
		_exitRequested = true;
		ClosePosition();
	}

	/// <inheritdoc />
	protected override void OnOwnTradeReceived(MyTrade trade)
	{
		base.OnOwnTradeReceived(trade);
		if (Position == 0m)
		{
			_entryPrice = null;
			_trailingStop = null;
			_exitRequested = false;
		}
		else if (_entryPrice is null)
		{
			_entryPrice = trade.Trade.Price;
		}
	}

	private bool IsSpreadAllowed()
		=> _bestBid is decimal bid && _bestAsk is decimal ask
			&& ask >= bid && ask - bid < MaxSpreadPoints * GetPoint();

	private decimal GetVolume()
	{
		var equity = Portfolio?.CurrentValue ?? Portfolio?.BeginValue ?? 0m;
		if (equity <= 0m || EquityDivisor <= 0m)
			return 0m;

		var raw = LotCoefficient * (decimal)Math.Sqrt((double)(equity / EquityDivisor));
		var minimum = Security?.MinVolume ?? 0m;
		var maximum = Security?.MaxVolume ?? decimal.MaxValue;
		var step = Security?.VolumeStep ?? 0m;
		if (maximum < minimum)
			return 0m;

		if (step > 0m)
		{
			var first = Math.Ceiling(minimum / step) * step;
			var last = Math.Floor(maximum / step) * step;
			if (first > last)
				return 0m;
			return Math.Clamp(Math.Floor(raw / step) * step, first, last);
		}

		return Math.Clamp(raw, minimum, maximum);
	}

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

		var rsi = _rsi.Process(candle.ClosePrice, candle.OpenTime, true);
		var smma = _smma.Process(candle.OpenPrice, candle.OpenTime, true);
		var ema = _ema.Process(candle.OpenPrice, candle.OpenTime, true);
		var fastCci = _fastCci.Process(candle);
		var slowCci = _slowCci.Process(candle);

		if (!rsi.IsFormed || !smma.IsFormed || !ema.IsFormed || !fastCci.IsFormed || !slowCci.IsFormed)
			return;

		var currentRsi = rsi.ToDecimal();
		if (_previousRsi is decimal previous && Position == 0m && !_exitRequested && IsSpreadAllowed())
		{
			var volume = GetVolume();
			if (volume > 0m)
			{
				if (previous <= BullishRsiLevel && currentRsi > BullishRsiLevel &&
					smma.ToDecimal() > ema.ToDecimal() &&
					fastCci.ToDecimal() > 0m && slowCci.ToDecimal() > 0m)
					BuyMarket(volume);
				else if (previous >= BearishRsiLevel && currentRsi < BearishRsiLevel &&
					smma.ToDecimal() < ema.ToDecimal() &&
					fastCci.ToDecimal() < 0m && slowCci.ToDecimal() < 0m)
					SellMarket(volume);
			}
		}

		_previousRsi = currentRsi;
	}
}