Стратегия является портом советника MetaTrader Expert_ADC_PL_CCI на платформу StockSharp. Алгоритм ищет на графике свечные разворотные комбинации «Пронзание» и «Темное облако» и подтверждает их с помощью индикатора Commodity Channel Index (CCI). После появления корректного паттерна и экстремального значения CCI стратегия открывает позицию в направлении разворота, а закрывает её, когда CCI выходит из зоны экстремума.
Индикаторы
Commodity Channel Index (CCI) — отслеживает экстремальные значения импульса и формирует условия выхода.
Средний размер тела свечи (SMA) — определяет «длинные» свечи, необходимые для паттернов.
Среднее значение закрытия (SMA) — фильтрует тренд, повторяя использование скользящей средней в оригинальном MQL-коде.
Правила торговли
Вход
Покупка (Piercing Line):
Предыдущая свеча — длинная медвежья, её открытие выше закрытия.
Текущая завершенная свеча — длинная бычья, открывается ниже минимума предыдущей и закрывается внутри её тела, выше середины, но ниже предыдущего открытия.
Середина тела старшей свечи должна находиться ниже скользящей средней, подтверждая нисходящий импульс.
Последнее завершенное значение CCI должно быть меньше или равно -EntryConfirmationLevel (по умолчанию 50).
При наличии короткой позиции она полностью закрывается перед открытием лонга.
Продажа (Dark Cloud Cover): зеркальные условия — длинная бычья свеча, за которой следует длинная медвежья с гэпом вверх и закрытием ниже середины предыдущего тела при 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class dark_cloud_piercing_cci_strategy(Strategy):
def __init__(self):
super(dark_cloud_piercing_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI period", "Indicators")
self._entry_level = self.Param("EntryLevel", 50.0) \
.SetDisplay("Entry Level", "CCI level for confirmation", "Signals")
self._signal_cooldown = self.Param("SignalCooldownCandles", 6) \
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading")
self._cci = None
self._candles = []
self._has_prev_cci = False
self._candles_since_trade = 0
@property
def cci_period(self):
return self._cci_period.Value
@property
def entry_level(self):
return self._entry_level.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(dark_cloud_piercing_cci_strategy, self).OnReseted()
self._cci = None
self._candles = []
self._has_prev_cci = False
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(dark_cloud_piercing_cci_strategy, self).OnStarted2(time)
self._cci = CommodityChannelIndex()
self._cci.Length = self.cci_period
self._candles = []
self._has_prev_cci = False
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._cci, self._process_candle)
subscription.Start()
self.StartProtection(takeProfit=Unit(2, UnitTypes.Percent), stopLoss=Unit(1, UnitTypes.Percent))
def _process_candle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
cci_val = float(cci_value)
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2 and self._has_prev_cci:
curr = self._candles[-1]
prev = self._candles[-2]
is_piercing = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) < float(prev.LowPrice)
and float(curr.ClosePrice) > (float(prev.OpenPrice) + float(prev.ClosePrice)) / 2.0)
is_dark_cloud = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.OpenPrice) > float(prev.HighPrice)
and float(curr.ClosePrice) < (float(prev.OpenPrice) + float(prev.ClosePrice)) / 2.0)
if is_piercing and cci_val < -self.entry_level and self.Position == 0 and self._candles_since_trade >= self.signal_cooldown:
self.BuyMarket()
self._candles_since_trade = 0
elif is_dark_cloud and cci_val > self.entry_level and self.Position == 0 and self._candles_since_trade >= self.signal_cooldown:
self.SellMarket()
self._candles_since_trade = 0
self._has_prev_cci = True
def CreateClone(self):
return dark_cloud_piercing_cci_strategy()