GitHub で見る

MA2CCI Classic Strategy

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

  1. The strategy processes only finished candles, ensuring one decision per bar similar to the original EA that used Volume[0] > 1 as a gate.
  2. 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.
  3. 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;
	}
}