在 GitHub 上查看

暗云压顶与刺透形态 CCI 策略

概述

该策略是 MetaTrader Expert_ADC_PL_CCI 顾问在 StockSharp 平台上的移植版本。它识别价格中的刺透形态和暗云压顶蜡烛反转信号,并使用商品通道指数(CCI)作为过滤与确认。当出现合格的形态并伴随极值的 CCI 读数时,策略会顺势开仓;随后当 CCI 脱离极值区域时平仓离场。

指标

  • CCI(Commodity Channel Index):确认动量的极端状态,并提供离场条件。
  • 蜡烛实体均值(SMA):衡量蜡烛实体长度,用于判定形态中的“长实体”要求。
  • 收盘价均值(SMA):简易趋势过滤器,对应原始 MQL 策略中使用的移动平均。

交易规则

入场

  • 多头信号(刺透形态)
    1. 前一根蜡烛为长阴线,开盘价高于收盘价。
    2. 最新蜡烛为长阳线,开盘价低于前低,收盘价位于上一根蜡烛实体内部,高于其实体中点且低于此前开盘价。
    3. 较旧蜡烛的实体中点必须低于移动平均,以确认短期下行趋势。
    4. 最近一根完成蜡烛的 CCI 值需要小于等于 -EntryConfirmationLevel(默认 50)。
    5. 若存在空头持仓,会在建多前先行对冲。
  • 空头信号(暗云压顶):与多头逻辑相反——先出现长阳线,再出现向上跳空并深入前实体后收于实体中点以下的长阴线,同时 CCI 大于等于 EntryConfirmationLevel

离场

  • 多头仓位:当 CCI 向下穿越 ExitLevel 或从上向下穿越 -ExitLevel 时离场。
  • 空头仓位:当 CCI 向上穿越 -ExitLevel 或从下向上穿越 ExitLevel 时离场。

仓位管理

  • 使用基础的 Volume 参数。如果需要反向开仓,策略会在下单数量中加入当前仓位的绝对值,确保完全翻仓。

参数

参数 说明 默认值
CandleType 用于检测的蜡烛类型与周期。 1 小时时间框架
CciPeriod CCI 指标的回溯周期。 49
AverageBodyPeriod 计算蜡烛实体平均值的周期。 11
EntryConfirmationLevel 验证入场信号所需的 CCI 绝对阈值。 50
ExitLevel 触发离场的 CCI 绝对阈值。 80

备注

  • 仅处理已完成的蜡烛,忽略未结束的更新。
  • 策略不自动设置止损或止盈,离场完全依据信号,与原始顾问一致。
  • 请确保交易品种配置了价格最小变动单位,蜡烛形态的比较依赖于该信息。
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Dark Cloud Piercing CCI strategy: trades Dark Cloud Cover and Piercing Line
/// candlestick patterns confirmed by CCI indicator levels.
/// </summary>
public class DarkCloudPiercingCciStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _entryLevel;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevCci;
	private bool _hasPrevCci;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public decimal EntryLevel { get => _entryLevel.Value; set => _entryLevel.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public DarkCloudPiercingCciStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI period", "Indicators");
		_entryLevel = Param(nameof(EntryLevel), 50m)
			.SetDisplay("Entry Level", "CCI level for confirmation", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_prevCci = 0m;
		_hasPrevCci = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_hasPrevCci = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		_candles.Add(candle);
		if (_candles.Count > 5)
			_candles.RemoveAt(0);

		if (_candles.Count >= 2 && _hasPrevCci)
		{
			var curr = _candles[^1];
			var prev = _candles[^2];

			// Piercing Line: prev bearish, curr bullish, curr opens below prev low, closes above midpoint
			var isPiercing = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice < prev.LowPrice
				&& curr.ClosePrice > (prev.OpenPrice + prev.ClosePrice) / 2m;

			// Dark Cloud Cover: prev bullish, curr bearish, curr opens above prev high, closes below midpoint
			var isDarkCloud = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.OpenPrice > prev.HighPrice
				&& curr.ClosePrice < (prev.OpenPrice + prev.ClosePrice) / 2m;

			if (isPiercing && cciValue < -EntryLevel && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (isDarkCloud && cciValue > EntryLevel && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevCci = cciValue;
		_hasPrevCci = true;
	}
}