Ver en GitHub

CCI Automatizado

CCI Automatizado es una estrategia de reversión que reacciona a los cruces de umbral del Índice de Canal de Materias Primas (CCI). Va largo cuando el CCI sube por encima de −80 después de caer por debajo de −90, y va corto cuando el CCI cae por debajo de 80 después de superar 90. El sistema duplica las operaciones hasta un límite definido por el usuario, gestiona el riesgo con niveles fijos de take-profit y stop-loss, y sigue los beneficios con un stop de arrastre configurable.

El enfoque busca capturar cambios tempranos de momentum después de condiciones de sobrecompra o sobreventa. Al acumular múltiples posiciones y mover el stop a medida que el precio avanza, intenta capitalizar reversiones sostenidas mientras limita el riesgo a la baja.

Detalles

  • Criterios de entrada: CCI cruza por encima de -80 después de estar por debajo de -90 para largos; cruza por debajo de 80 después de estar por encima de 90 para cortos.
  • Largo/Corto: Ambas direcciones.
  • Criterios de salida: Stop loss, take profit o stop de arrastre.
  • Stops: Sí.
  • Valores predeterminados:
    • CciPeriod = 9
    • TradesDuplicator = 3
    • Volume = 0.03
    • StopLoss = 50
    • TakeProfit = 200
    • TrailingStop = 50
    • CandleType = TimeSpan.FromMinutes(5)
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Ambos
    • Indicadores: CCI
    • Stops: Sí
    • Complejidad: Básico
    • Marco temporal: Intradía (5m)
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
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;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that trades based on CCI crossing specific thresholds.
/// </summary>
public class CciAutomatedStrategy : Strategy
{
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<int> _tradesDuplicator;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _trailingStop;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevCci;
	private decimal? _trailPrice;

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

	/// <summary>
	/// Maximum number of duplicated trades.
	/// </summary>
	public int TradesDuplicator
	{
		get => _tradesDuplicator.Value;
		set => _tradesDuplicator.Value = value;
	}


	/// <summary>
	/// Stop loss distance in price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take profit distance in price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Trailing stop distance in price units.
	/// </summary>
	public decimal TrailingStop
	{
		get => _trailingStop.Value;
		set => _trailingStop.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of the <see cref="CciAutomatedStrategy" /> class.
	/// </summary>
	public CciAutomatedStrategy()
	{
		_cciPeriod = Param(nameof(CciPeriod), 9)
			.SetRange(5, 50)
			.SetDisplay("CCI Period", "CCI indicator length", "Indicators")
			;

		_tradesDuplicator = Param(nameof(TradesDuplicator), 3)
			.SetRange(1, 10)
			.SetDisplay("Trades Duplicator", "Maximum number of concurrent trades", "General")
			;


		_stopLoss = Param(nameof(StopLoss), 50m)
			.SetRange(10m, 200m)
			.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
			;

		_takeProfit = Param(nameof(TakeProfit), 200m)
			.SetRange(10m, 500m)
			.SetDisplay("Take Profit", "Take profit in price units", "Risk")
			;

		_trailingStop = Param(nameof(TrailingStop), 50m)
			.SetRange(10m, 200m)
			.SetDisplay("Trailing Stop", "Trailing stop in price units", "Risk")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = null;
		_trailPrice = null;
	}

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

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

		var cci = new CommodityChannelIndex { Length = CciPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var maxVolume = TradesDuplicator * Volume;

		if (_prevCci is decimal prev)
		{
			if (prev < -90m && cciValue > -80m && Position + Volume <= maxVolume)
			{
				BuyMarket();
				_trailPrice = candle.ClosePrice - TrailingStop;
			}
			else if (prev > 90m && cciValue < 80m && Position - Volume >= -maxVolume)
			{
				SellMarket();
				_trailPrice = candle.ClosePrice + TrailingStop;
			}
		}

		if (Position > 0)
		{
			var candidate = candle.ClosePrice - TrailingStop;
			if (_trailPrice is null || candidate > _trailPrice)
				_trailPrice = candidate;
			if (_trailPrice is decimal tp && candle.ClosePrice <= tp)
			{
				SellMarket();
				_trailPrice = null;
			}
		}
		else if (Position < 0)
		{
			var candidate = candle.ClosePrice + TrailingStop;
			if (_trailPrice is null || candidate < _trailPrice)
				_trailPrice = candidate;
			if (_trailPrice is decimal tp && candle.ClosePrice >= tp)
			{
				BuyMarket();
				_trailPrice = null;
			}
		}

		_prevCci = cciValue;
	}

	/// <inheritdoc />
	protected override void OnPositionReceived(Position position)
	{
		base.OnPositionReceived(position);

		if (Position == 0)
			_trailPrice = null;
	}
}