This strategy reproduces the MetaTrader 5 expert "Expert_AML_CCI" inside the StockSharp high-level framework. The original robot
combines the Japanese candlestick pattern "Meeting Lines" with a Commodity Channel Index (CCI) filter and uses the Expert Advisor
engine to weight bullish and bearish votes. The StockSharp port keeps the same confirmation logic, translates the candlestick
pattern detection into pure candle arithmetic, and exposes all thresholds as optimizer-friendly parameters.
How it works
Data source – The strategy subscribes to a configurable time-frame candle series (30-minute candles by default) using
SubscribeCandles. Every finished candle is dispatched together with the synchronized CCI value via the high-level Bind
pipeline, so no manual indicator management is required.
Core indicator – A single CommodityChannelIndex with period CciPeriod mirrors the MetaTrader oscillator. Its values are
cached internally to compare the two most recent completed readings, replicating the CCI(1) and CCI(2) calls from MQL.
Candlestick logic – The helper methods rebuild the "Bullish Meeting Lines" and "Bearish Meeting Lines" checks. They compute
the moving average of body lengths over AverageBodyPeriod candles (default 3) and enforce the long-body and equal-close
requirements from the original CML_CCI filter. Because StockSharp delivers completed candles, the pattern is evaluated exactly
when the second candle of the pattern closes – the same moment the MQL expert casts its 80-point vote.
Entry rules –
Long positions require a bullish Meeting Lines formation and the latest completed CCI value to stay below or equal to
LongEntryCciLevel (−50 by default). If an opposite short is open, the order size automatically includes the absolute value
of the current position to flip direction, matching the EA behaviour.
Short positions mirror the logic: a bearish Meeting Lines pattern plus a CCI value above or equal to ShortEntryCciLevel
(+50 by default).
Exit rules – Instead of the Expert Advisor's voting weights, the port uses explicit flattening orders. Positions are closed
when the CCI crosses the extreme band defined by ExtremeCciLevel (80 by default):
Shorts exit when the CCI jumps upward through −Extreme or drops back below +Extreme.
Longs exit when the CCI falls below +Extreme or plunges through −Extreme.
These rules reflect the 40 vote branch inside LongCondition and ShortCondition in the MQL signal class.
Risk management – The strategy leaves protective stops to the caller. It is compatible with StockSharp's StartProtection
helper if a stop-loss or take-profit needs to be attached externally.
Parameters
Parameter
Description
Default
CandleType
Time-frame of the source candles.
30-minute time frame
CciPeriod
Length of the Commodity Channel Index.
18
AverageBodyPeriod
Number of candles used to compute the average body size for pattern validation.
3
LongEntryCciLevel
Oversold level that confirms bullish Meeting Lines.
−50
ShortEntryCciLevel
Overbought level that confirms bearish Meeting Lines.
+50
ExtremeCciLevel
Absolute extreme band for CCI exit crossovers.
80
All numeric parameters expose optimizer ranges identical to the EA defaults so the strategy can be tuned through StockSharp's
optimization tools.
Usage notes
Attach the strategy to a security and set the desired Volume before starting.
Optionally tweak the thresholds to match the original money-management profile or to adjust sensitivity.
The chart integration draws candles, the CCI curve, and executed trades for quick visual validation of the pattern detection.
By focusing on the same candlestick+CCI combination, this StockSharp implementation delivers a faithful port of the Expert
Advisor while staying within the recommended high-level API style.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Meeting Lines + CCI strategy.
/// Buys on bullish meeting lines with low CCI, sells on bearish meeting lines with high CCI.
/// </summary>
public class AmlCciMeetingLinesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLow;
private readonly StrategyParam<decimal> _cciHigh;
private ICandleMessage _prevCandle;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public decimal CciLow { get => _cciLow.Value; set => _cciLow.Value = value; }
public decimal CciHigh { get => _cciHigh.Value; set => _cciHigh.Value = value; }
public AmlCciMeetingLinesStrategy()
{
_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");
_cciLow = Param(nameof(CciLow), -50m)
.SetDisplay("CCI Low", "CCI level for bullish entry", "Signals");
_cciHigh = Param(nameof(CciHigh), 50m)
.SetDisplay("CCI High", "CCI level for bearish entry", "Signals");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished) return;
if (_prevCandle != null)
{
var avgBody = (Math.Abs(candle.ClosePrice - candle.OpenPrice) +
Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice)) / 2m;
if (avgBody > 0)
{
var prevBearish = _prevCandle.OpenPrice > _prevCandle.ClosePrice;
var currBullish = candle.ClosePrice > candle.OpenPrice;
var closesNear = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;
// Bullish meeting lines
if (prevBearish && currBullish && closesNear && cciValue < CciLow && Position <= 0)
BuyMarket();
var prevBullish = _prevCandle.ClosePrice > _prevCandle.OpenPrice;
var currBearish = candle.OpenPrice > candle.ClosePrice;
var closesNear2 = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;
// Bearish meeting lines
if (prevBullish && currBearish && closesNear2 && cciValue > CciHigh && Position >= 0)
SellMarket();
}
}
_prevCandle = candle;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import CandleStates
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class aml_cci_meeting_lines_strategy(Strategy):
"""
Meeting Lines + CCI strategy.
Buys on bullish meeting lines with low CCI, sells on bearish meeting lines with high CCI.
"""
def __init__(self):
super(aml_cci_meeting_lines_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._cci_period = self.Param("CciPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("CCI Period", "CCI period", "Indicators")
self._cci_low = self.Param("CciLow", -50.0) \
.SetDisplay("CCI Low", "CCI level for bullish entry", "Signals")
self._cci_high = self.Param("CciHigh", 50.0) \
.SetDisplay("CCI High", "CCI level for bearish entry", "Signals")
self._prev_open = None
self._prev_close = None
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
@property
def CciPeriod(self): return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, v): self._cci_period.Value = v
@property
def CciLow(self): return self._cci_low.Value
@CciLow.setter
def CciLow(self, v): self._cci_low.Value = v
@property
def CciHigh(self): return self._cci_high.Value
@CciHigh.setter
def CciHigh(self, v): self._cci_high.Value = v
def OnReseted(self):
super(aml_cci_meeting_lines_strategy, self).OnReseted()
self._prev_open = None
self._prev_close = None
def OnStarted2(self, time):
super(aml_cci_meeting_lines_strategy, self).OnStarted2(time)
self._prev_open = None
self._prev_close = None
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cci, self.ProcessCandle).Start()
def ProcessCandle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
c_open = float(candle.OpenPrice)
c_close = float(candle.ClosePrice)
if self._prev_open is not None and self._prev_close is not None:
avg_body = (abs(c_close - c_open) + abs(self._prev_close - self._prev_open)) / 2.0
if avg_body > 0:
prev_bearish = self._prev_open > self._prev_close
curr_bullish = c_close > c_open
closes_near = abs(c_close - self._prev_close) < avg_body * 0.3
if prev_bearish and curr_bullish and closes_near and cci_value < self.CciLow and self.Position <= 0:
self.BuyMarket()
prev_bullish = self._prev_close > self._prev_open
curr_bearish = c_open > c_close
closes_near2 = abs(c_close - self._prev_close) < avg_body * 0.3
if prev_bullish and curr_bearish and closes_near2 and cci_value > self.CciHigh and self.Position >= 0:
self.SellMarket()
self._prev_open = c_open
self._prev_close = c_close
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return aml_cci_meeting_lines_strategy()