The KWAN CCC strategy reproduces the MetaTrader expert Exp_KWAN_CCC.mq5 using StockSharp's high level API. The system derives trading signals from a custom oscillator built as follows:
Calculate the Chaikin oscillator (difference between fast and slow moving averages of the accumulation/distribution line).
Multiply the Chaikin value by the Commodity Channel Index (CCI).
Divide the result by the Momentum indicator value. When momentum equals zero the script substitutes a constant value of 100 to avoid division by zero, exactly like the original code.
Smooth the resulting series with the user-selected XMA method.
Detect the slope of the smoothed series. Rising bars are coloured 0, falling bars 2, otherwise 1.
When the colour changes from 0 to anything else the strategy closes shorts and opens a long position. When the colour changes from 2 to anything else it closes longs and opens a short. This mirrors the logic implemented in the MQL expert, including the optional signal shift (SignalBar).
Trading Rules
Long entry: colour on the bar at SignalBar + 1 equals 0 and the bar at SignalBar is different from 0.
Short entry: colour on the bar at SignalBar + 1 equals 2 and the bar at SignalBar is different from 2.
Long exit: enabled when EnableLongExits = true and the short entry condition triggers.
Short exit: enabled when EnableShortExits = true and the long entry condition triggers.
Protective stop and target orders are created through StartProtection using absolute price offsets derived from StopLossPoints and TakeProfitPoints multiplied by the instrument PriceStep.
Parameters
Parameter
Description
OrderVolume
Base order size used when opening a new position.
CandleType
Timeframe for all indicator calculations. Default is 1 hour.
FastPeriod / SlowPeriod
Lengths of the moving averages inside the Chaikin oscillator.
ChaikinMethod
Moving average type (simple, exponential, smoothed, weighted) applied to the accumulation/distribution line.
CciPeriod
Period of the Commodity Channel Index.
MomentumPeriod
Period of the Momentum indicator.
SmoothingMethod
XMA smoothing method mapped from the original options. JurX, Parabolic, and T3 fall back to Jurik MA; Vidya uses a Chande Momentum Oscillator driven adaptive smoothing; Adaptive uses Kaufman AMA.
SmoothingLength
Number of bars used by the selected smoothing filter.
SmoothingPhase
Additional parameter used by specific methods (e.g., VIDYA CMO length, AMA slow period).
SignalBar
Offset (in completed bars) used to evaluate the colour transitions. 1 reproduces the MetaTrader default.
EnableLongEntries / EnableShortEntries
Allow or block opening new positions in the corresponding direction.
EnableLongExits / EnableShortExits
Allow or block indicator-driven position closing.
StopLossPoints / TakeProfitPoints
Protective stop/target measured in price steps (set to zero to disable).
Implementation Notes
The strategy only acts on finished candles and uses StockSharp's Bind helper to stream candle data into the indicators.
The smoothing method list mirrors the XMA implementation from the original library. Methods that are unavailable in StockSharp are mapped to the closest alternative, as noted in the parameter table.
MetaTrader's VolumeType input is omitted because StockSharp candles already encapsulate total volume information used by the accumulation/distribution line.
Money management in the original expert relied on custom lot sizing helpers. The conversion assumes a fixed volume specified by OrderVolume.
Usage Tips
Ensure that the instrument provides meaningful volume data if Chaikin oscillator behaviour is important. For illiquid instruments consider increasing MomentumPeriod to reduce noise.
When optimising smoothing parameters, combine SmoothingLength and SmoothingPhase carefully: extreme combinations may delay signals considerably.
The default protective values (StopLossPoints = 1000, TakeProfitPoints = 2000) correspond to large offsets. Adjust them to match the instrument tick size.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Strategy converted from the KWAN_CCC expert advisor.
/// Uses CCI and Momentum to detect trend transitions.
/// Enters long when CCI turns up while momentum is positive,
/// enters short when CCI turns down while momentum is negative.
/// </summary>
public class KwanCccStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private decimal _prevCci;
private decimal _prevClose;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
public KwanCccStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI length", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCci = 0m;
_prevClose = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = 0m;
_prevClose = 0m;
_initialized = false;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, (ICandleMessage candle, decimal cciValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_initialized)
{
_prevCci = cciValue;
_prevClose = candle.ClosePrice;
_initialized = true;
return;
}
var closeUp = candle.ClosePrice > _prevClose;
var closeDown = candle.ClosePrice < _prevClose;
// Buy when CCI crosses into positive territory and price confirms the move.
if (_prevCci <= 0m && cciValue > 0m && closeUp && Position <= 0)
{
BuyMarket();
}
// Sell when CCI crosses into negative territory and price confirms the move.
else if (_prevCci >= 0m && cciValue < 0m && closeDown && Position >= 0)
{
SellMarket();
}
_prevCci = cciValue;
_prevClose = candle.ClosePrice;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, cci);
DrawOwnTrades(area);
}
}
}
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
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class kwan_ccc_strategy(Strategy):
def __init__(self):
super(kwan_ccc_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI length", "Indicators")
self._prev_cci = 0.0
self._prev_close = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def CciPeriod(self):
return self._cci_period.Value
def OnReseted(self):
super(kwan_ccc_strategy, self).OnReseted()
self._prev_cci = 0.0
self._prev_close = 0.0
self._initialized = False
def OnStarted2(self, time):
super(kwan_ccc_strategy, self).OnStarted2(time)
self._prev_cci = 0.0
self._prev_close = 0.0
self._initialized = False
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(cci, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def _on_process(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
cv = float(cci_value)
close = float(candle.ClosePrice)
if not self._initialized:
self._prev_cci = cv
self._prev_close = close
self._initialized = True
return
close_up = close > self._prev_close
close_down = close < self._prev_close
if self._prev_cci <= 0 and cv > 0 and close_up and self.Position <= 0:
self.BuyMarket()
elif self._prev_cci >= 0 and cv < 0 and close_down and self.Position >= 0:
self.SellMarket()
self._prev_cci = cv
self._prev_close = close
def CreateClone(self):
return kwan_ccc_strategy()