GitHub で見る

CCI Histogram Strategy

This strategy uses the Commodity Channel Index (CCI) to detect reversals when the indicator leaves extreme zones. A long position is opened when CCI falls back below the upper level after being above it. A short position is opened when CCI rises above the lower level after being below it. Optional stop loss and take profit levels in points can protect open positions.

Details

  • Entry Criteria:
    • Long: Previous CCI > UpperLevel and current CCI ≤ UpperLevel.
    • Short: Previous CCI < LowerLevel and current CCI ≥ LowerLevel.
  • Long/Short: Both.
  • Exit Criteria: Opposite signal closes existing position and opens a new one.
  • Stops: Optional fixed stop loss and take profit in points.
  • Default Values:
    • CCI Period = 14
    • Upper Level = 100
    • Lower Level = -100
    • Stop Loss = 100 points
    • Take Profit = 200 points
  • Filters:
    • Category: Reversal
    • Direction: Both
    • Indicators: CCI
    • Stops: Optional
    • Complexity: Simple
    • Timeframe: Any (default 4H)
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: Medium
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>
/// CCI Histogram reversal strategy.
/// Buys when CCI leaves the upper extreme and sells when it leaves the lower extreme.
/// Optional stop loss and take profit protection in points.
/// </summary>
public class CciHistogramStrategy : Strategy
{
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _upperLevel;
	private readonly StrategyParam<decimal> _lowerLevel;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<bool> _useStopLoss;
	private readonly StrategyParam<bool> _useTakeProfit;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevCci;

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

	/// <summary>
	/// Upper CCI level that defines overbought zone.
	/// </summary>
	public decimal UpperLevel
	{
		get => _upperLevel.Value;
		set => _upperLevel.Value = value;
	}

	/// <summary>
	/// Lower CCI level that defines oversold zone.
	/// </summary>
	public decimal LowerLevel
	{
		get => _lowerLevel.Value;
		set => _lowerLevel.Value = value;
	}

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

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

	/// <summary>
	/// Use stop loss protection.
	/// </summary>
	public bool UseStopLoss
	{
		get => _useStopLoss.Value;
		set => _useStopLoss.Value = value;
	}

	/// <summary>
	/// Use take profit protection.
	/// </summary>
	public bool UseTakeProfit
	{
		get => _useTakeProfit.Value;
		set => _useTakeProfit.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of the <see cref="CciHistogramStrategy"/> class.
	/// </summary>
	public CciHistogramStrategy()
	{
		_cciPeriod = Param(nameof(CciPeriod), 14)
						 .SetGreaterThanZero()
						 .SetDisplay("CCI Period", "Period length for the CCI indicator", "General")
						 
						 .SetOptimize(7, 28, 7);

		_upperLevel = Param(nameof(UpperLevel), 100m)
						  .SetDisplay("Upper Level", "Upper CCI level", "General")
						  
						  .SetOptimize(80m, 120m, 10m);

		_lowerLevel = Param(nameof(LowerLevel), -100m)
						  .SetDisplay("Lower Level", "Lower CCI level", "General")
						  
						  .SetOptimize(-120m, -80m, 10m);

		_stopLossPoints = Param(nameof(StopLossPoints), 100m)
							  .SetDisplay("Stop Loss", "Stop loss in points", "Risk Management")
							  
							  .SetOptimize(50m, 200m, 25m);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
								.SetDisplay("Take Profit", "Take profit in points", "Risk Management")
								
								.SetOptimize(50m, 300m, 25m);

		_useStopLoss = Param(nameof(UseStopLoss), false)
						   .SetDisplay("Enable Stop Loss", "Use stop loss protection", "Risk Management");

		_useTakeProfit = Param(nameof(UseTakeProfit), false)
							 .SetDisplay("Enable Take Profit", "Use take profit protection", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
		_prevCci = 0m;
	}

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

		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);

		var isInitialized = false;

		subscription
			.Bind(cci,
				  (candle, cciValue) =>
				  {
					  if (candle.State != CandleStates.Finished)
						  return;

					  if (!IsFormedAndOnlineAndAllowTrading())
					  {
						  _prevCci = cciValue;
						  isInitialized = true;
						  return;
					  }

					  if (!isInitialized)
					  {
						  _prevCci = cciValue;
						  isInitialized = true;
						  return;
					  }

					  if (_prevCci > UpperLevel && cciValue <= UpperLevel && Position <= 0)
					  {
						  BuyMarket();
					  }
					  else if (_prevCci < LowerLevel && cciValue >= LowerLevel && Position >= 0)
					  {
						  SellMarket();
					  }

					  _prevCci = cciValue;
				  })
			.Start();

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