The MA2CCI strategy ports the classic MetaTrader expert advisor built around the interaction of two simple moving averages (SMA) and the Commodity Channel Index (CCI). It filters trades using the CCI zero line and applies protective stops derived from the Average True Range (ATR). The system is designed for trend-following entries with fast reaction to reversals.
The StockSharp version keeps the original trading logic while adapting risk management to the .NET environment. Position sizing follows a risk-per-thousand rule with an additional decrease factor that cuts trade size after consecutive losses. Each entry attaches a volatility-driven stop that mirrors the ATR distance used in the MQL implementation.
Trading Logic
Indicators
Fast SMA with default length 4.
Slow SMA with default length 8.
CCI filter using 4-period lookback.
ATR with period 4 for stop placement.
Entry Conditions
Long: fast SMA crosses above the slow SMA and the previous finished bar shows CCI rising through zero (from negative to positive).
Short: fast SMA crosses below the slow SMA and the previous bar shows CCI falling through zero (from positive to negative).
Exit Conditions
Opposite SMA crossover closes open positions even if no new trade is initiated.
ATR stop: long positions exit when price falls to entry - ATR; short positions exit when price rises to entry + ATR.
Risk Management
Base order volume is configurable; by default 0.1 lots (or exchange equivalent).
Optional dynamic sizing scales the volume to free capital * MaxRiskPerThousand / 1000 when portfolio data is available.
After more than one consecutive loss, the position size is linearly reduced by losses / DecreaseFactor of the calculated volume.
Volatility stops rely on the most recent finished candle; intrabar spikes beyond stop levels trigger a market exit on the next strategy tick.
Parameters
Name
Description
Default
CandleType
Working timeframe for all indicators.
1 hour candles
OrderVolume
Minimum trade size when risk-based sizing is unavailable.
0.1
FastMaPeriod
Period of the fast SMA.
4
SlowMaPeriod
Period of the slow SMA.
8
CciPeriod
Period of the CCI filter.
4
AtrPeriod
ATR length for stop calculation.
4
MaxRiskPerThousand
Fraction of free capital allocated per trade (per 1000 units).
0.02
DecreaseFactor
Divisor used to shrink volume after losing streaks.
3
Notes
The strategy processes only finished candles, ensuring one decision per bar similar to the original EA that used Volume[0] > 1 as a gate.
Stop levels are simulated internally instead of registering exchange stop orders; this matches the behaviour of the MetaTrader version that relied on market closes when ATR thresholds were hit.
Enable charting inside StockSharp Designer to visualize SMA, CCI and executed trades using the built-in drawing helpers.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MA2CCI Classic strategy - dual SMA crossover with CCI zero-line filter.
/// Buys when fast SMA crosses above slow SMA and CCI above zero.
/// Sells when fast SMA crosses below slow SMA and CCI below zero.
/// </summary>
public class Ma2CciClassicStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Ma2CciClassicStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetDisplay("CCI Period", "CCI lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new SimpleMovingAverage { Length = FastPeriod };
var slow = new SimpleMovingAverage { Length = SlowPeriod };
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal cci)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_prevFast <= _prevSlow && fast > slow && cci > 0 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (_prevFast >= _prevSlow && fast < slow && cci < 0 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevFast = fast; _prevSlow = slow;
}
}
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 SimpleMovingAverage, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class ma2_cci_classic_strategy(Strategy):
def __init__(self):
super(ma2_cci_classic_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12).SetDisplay("Fast SMA", "Fast SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26).SetDisplay("Slow SMA", "Slow SMA period", "Indicators")
self._cci_period = self.Param("CciPeriod", 14).SetDisplay("CCI Period", "CCI lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
@property
def fast_period(self): return self._fast_period.Value
@property
def slow_period(self): return self._slow_period.Value
@property
def cci_period(self): return self._cci_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(ma2_cci_classic_strategy, self).OnReseted()
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(ma2_cci_classic_strategy, self).OnStarted2(time)
self._has_prev = False
fast = SimpleMovingAverage(); fast.Length = self.fast_period
slow = SimpleMovingAverage(); slow.Length = self.slow_period
cci = CommodityChannelIndex(); cci.Length = self.cci_period
sub = self.SubscribeCandles(self.candle_type)
sub.Bind(fast, slow, cci, self.process_candle).Start()
def process_candle(self, candle, fast, slow, cci):
if candle.State != CandleStates.Finished: return
f = float(fast); s = float(slow); c = float(cci)
if not self._has_prev: self._prev_fast = f; self._prev_slow = s; self._has_prev = True; return
if self._prev_fast <= self._prev_slow and f > s and c > 0 and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and f < s and c < 0 and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_fast = f; self._prev_slow = s
def CreateClone(self): return ma2_cci_classic_strategy()