Ver en GitHub

CCI Estrategia MA v1.5

Esta estrategia recrea el asesor experto MetaTrader "CCI_MA v1.5" dentro dla API de alto nivel de StockSharp. El robot original espera a que el índice del canal de productos básicos (CCI) cruce una media móvil simple calculada sobre los propios valores CCI y utiliza un CCI secundario para supervisar las salidas alrededor de los umbrales de ±100. El puerto StockSharp mantiene el mismo orden de señales, administración de dinero opcional y distancias de parada/objetivo basadas en puntos mientras adapta todo a las suscripciones de velas y vinculaciones de indicadores.

como funciona

  • Fuente de datos: una serie de velas definida por el usuario (velas de 15 minutos de forma predeterminada) alimenta ambas CCI. Los indicadores leen el precio de cierre de la vela para reflejar la configuración PRICE_CLOSE de MetaTrader.
  • Indicadores principales: el CommodityChannelIndex principal (parámetro CciPeriod) proporciona la lectura del impulso. Se aplica un SimpleMovingAverage con período MaPeriod al flujo de valores CCI para formar la línea de activación. Un CCI (SignalCciPeriod) secundario supervisa las reversiones de sobrecompra y sobreventa alrededor de ±100.
  • Lógica de entrada: se abre una operación larga en la barra después de un cruce hacia arriba: la vela completada anteriormente (prevCci) debe ubicarse por encima del promedio móvil CCI mientras que la vela anterior (prev2Cci) estaba por debajo. Una señal corta es el cruce simétrico hacia abajo. Las posiciones opuestas existentes se cierran y se invierten sumando el valor absoluto de la posición actual al nuevo tamaño de la orden, coincidiendo con el comportamiento de la versión MQL.
  • Lógica de salida: las posiciones largas se liquidan cuando el CCI supervisor cae de más de +100 a menos de +100 o cuando el CCI primario vuelve a cruzar por debajo de su promedio móvil (evaluado nuevamente en las dos velas terminadas anteriormente). Los pantalones cortos salen en condiciones inversas. Las paradas de protección emulan las distancias basadas en puntos de MetaTrader: la estrategia deriva un tamaño de pip del instrumento PriceStep (multiplicando por 10 para cotizaciones de tres o cinco dígitos) y compara los extremos de las velas con entry ± distance en cada vela completa.
  • Tamaño de la posición: LotVolume define el tamaño del pedido base. Si UseMoneyManagement está habilitado, la estrategia lo multiplica por un factor entero igual a floor(balance / DepositPerLot), limitado por MaxMultiplier, reproduciendo la escalera de depósitos del asesor experto. El volumen del pedido está alineado con las restricciones del instrumento VolumeStep, MinVolume y MaxVolume antes del envío.

Parámetros

  • Tipo de vela: tipo de datos de vela que impulsa todos los cálculos del indicador.
  • CCI Período: longitud del oscilador primario CCI.
  • Período de salida CCI: duración del CCI de supervisión utilizado para las salidas de umbral.
  • CCI Período MA – Período del promedio móvil simple aplicado al CCI primario.
  • Volumen de lote: volumen de operaciones base antes de escalar la administración del dinero.
  • Habilitar administración de dinero: activa el escalado del volumen del lote basado en depósitos.
  • Depósito por lote: incremento de saldo necesario para aumentar el multiplicador del lote en uno (se usa solo cuando la administración de dinero está activa).
  • Multiplicador máximo: multiplicador máximo que puede alcanzar la administración del dinero.
  • Stop Loss (pips) – Distancia en pips para el tope de protección; póngalo en cero para desactivarlo.
  • Take Profit (pips) – Distancia en pips para el objetivo de ganancias; póngalo en cero para desactivarlo.

La estrategia espera dos velas completamente cerradas antes de emitir la primera orden para que las comparaciones cruzadas de dos barras coincidan exactamente con la ejecución retrasada del experto MQL. Las comprobaciones de stop-loss y take-profit se ejecutan en velas terminadas utilizando sus extremos alto/bajo, lo que se aproxima a las órdenes de protección del lado del servidor de MetaTrader mientras se mantiene dentro del nivel alto StockSharp API.

namespace StockSharp.Samples.Strategies;

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

/// <summary>
/// Commodity Channel Index strategy converted from the MetaTrader "CCI_MA v1.5" expert advisor.
/// Uses a primary CCI with a manually computed SMA of CCI values as a signal line.
/// A secondary CCI provides overbought/oversold exit confirmation.
/// </summary>
public class CciMaV15Strategy : Strategy
{
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<int> _signalCciPeriod;
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private CommodityChannelIndex _cci;
	private CommodityChannelIndex _signalCci;

	private readonly List<decimal> _cciHistory = new();
	private decimal? _prevCciMa;
	private decimal? _prevCci;
	private decimal? _prevSignalCci;

	/// <summary>
	/// Primary CCI period.
	/// </summary>
	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	/// <summary>
	/// Secondary CCI period for exit signals.
	/// </summary>
	public int SignalCciPeriod
	{
		get => _signalCciPeriod.Value;
		set => _signalCciPeriod.Value = value;
	}

	/// <summary>
	/// SMA period applied to the primary CCI values.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Stop loss distance in absolute points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take profit distance in absolute points.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public CciMaV15Strategy()
	{
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "Length of the primary CCI", "CCI")
			.SetOptimize(7, 35, 7);

		_signalCciPeriod = Param(nameof(SignalCciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Exit CCI Period", "Length of the secondary CCI", "CCI")
			.SetOptimize(7, 35, 7);

		_maPeriod = Param(nameof(MaPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("CCI MA Period", "SMA length applied to the CCI", "CCI")
			.SetOptimize(3, 21, 3);

		_stopLossPoints = Param(nameof(StopLossPoints), 500m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Protective stop distance in absolute points", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 500m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Profit target distance in absolute points", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Market data series", "General");

		Volume = 1;
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_cci = null;
		_signalCci = null;
		_cciHistory.Clear();
		_prevCciMa = null;
		_prevCci = null;
		_prevSignalCci = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		_cci = new CommodityChannelIndex { Length = CciPeriod };
		_signalCci = new CommodityChannelIndex { Length = SignalCciPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_cci, _signalCci, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);

			var indArea = CreateChartArea();
			if (indArea != null)
			{
				DrawIndicator(indArea, _cci);
				DrawIndicator(indArea, _signalCci);
			}
		}

		var tp = TakeProfitPoints > 0 ? new Unit(TakeProfitPoints, UnitTypes.Absolute) : null;
		var sl = StopLossPoints > 0 ? new Unit(StopLossPoints, UnitTypes.Absolute) : null;
		if (tp != null || sl != null)
			StartProtection(tp, sl);

		base.OnStarted2(time);
	}

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

		// Maintain CCI history for manual SMA calculation
		_cciHistory.Add(cciValue);
		if (_cciHistory.Count > MaPeriod)
			_cciHistory.RemoveAt(0);

		// Compute SMA of CCI
		decimal? cciMa = null;
		if (_cciHistory.Count >= MaPeriod)
		{
			decimal sum = 0;
			for (int i = 0; i < _cciHistory.Count; i++)
				sum += _cciHistory[i];
			cciMa = sum / _cciHistory.Count;
		}

		if (cciMa == null || _prevCci == null || _prevCciMa == null || _prevSignalCci == null)
		{
			_prevCci = cciValue;
			_prevCciMa = cciMa;
			_prevSignalCci = signalCciValue;
			return;
		}

		// Exit logic: secondary CCI overbought/oversold reversal
		if (Position > 0 && _prevSignalCci > 100 && signalCciValue <= 100)
		{
			SellMarket(Position);
		}
		else if (Position < 0 && _prevSignalCci < -100 && signalCciValue >= -100)
		{
			BuyMarket(Math.Abs(Position));
		}

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevCci = cciValue;
			_prevCciMa = cciMa;
			_prevSignalCci = signalCciValue;
			return;
		}

		// Entry: CCI crosses above its MA (buy) or below (sell)
		if (_prevCci < _prevCciMa && cciValue > cciMa.Value && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(Volume);
		}
		else if (_prevCci > _prevCciMa && cciValue < cciMa.Value && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Position);
			SellMarket(Volume);
		}

		_prevCci = cciValue;
		_prevCciMa = cciMa;
		_prevSignalCci = signalCciValue;
	}
}