Auf GitHub ansehen

MA2CCI Adaptive Volume-Strategie

Überblick

Die MA2CCI-Strategie ist eine direkte Portierung des Expert Advisors MetaTrader 4, der ursprünglich als „MA2CCI.mq4“ vertrieben wurde. Das System kombiniert einen schnellen/langsamen einfachen gleitenden Durchschnitt (SMA) mit einer Bestätigung der Nulllinie des Commodity Channel Index (CCI). Jeder validierte Crossover eröffnet eine einzelne Marktposition und platziert sofort einen auf dem Average True Range (ATR) basierenden Schutzstopp. Die Positionsgrößenbestimmung folgt der ursprünglichen Money-Management-Logik, indem die Ordergröße im Verhältnis zum Eigenkapital skaliert und nach Phasen von Verlusten bei Trades reduziert wird.

Indikatoren und Daten

  • Schnell SMA (FMa) und Langsam SMA (SMa) im konfigurierten Zeitrahmen, um Trendumkehrungen zu erkennen.
  • Commodity Channel Index (CCI) mit demselben Preisstrom zur Bestätigung der Momentumrichtung durch Nullliniendurchgänge.
  • Average True Range (ATR) zur Messung der jüngsten Volatilität und zur Ableitung der Stop-Loss-Distanz.
  • Kerzen des gewählten Zeitrahmens (Standard 15 Minuten) liefern die Eingabereihen für alle Indikatoren.

Handelsregeln

  • Langer Einstieg: Der schnelle SMA kreuzt den langsamen SMA, während CCI auf demselben Balken von negativ nach positiv kreuzt, keine Position offen ist und Handel erlaubt ist. Eine Marktkauforder wird gesendet und ein Stop-Loss wird bei close − ATR × AtrMultiplier aktiviert.
  • Kurzer Einstieg: Der schnelle SMA kreuzt den langsamen SMA, während CCI von positiv nach negativ kreuzt, es ist keine Position offen. Eine Market-Sell-Order wird mit einem Stop-Loss bei close + ATR × AtrMultiplier platziert.
  • Ausstieg bei Long-Positionen: Wenn der schnelle SMA den langsamen SMA wieder unterschreitet, wird die gesamte Long-Position zum Marktwert geschlossen. Auch der Schutzstopp wird aufgehoben.
  • Ausstieg bei Shorts: Wenn der schnelle SMA den langsamen SMA wieder überschreitet, wird die Short-Position zum Marktwert gedeckt und der Stop aufgehoben.
  • Stop-Loss: Jede neue Position stellt einen Volatilitätsstopp wieder her, der die MetaTrader-Logik widerspiegelt. Stopps werden nur bei neuen Einträgen neu berechnet und als separate bedingte Orders gespeichert.

Positionsgrößen

  • Die Basislosgröße beginnt beim Parameter BaseVolume (Standard: 0,1 Los).
  • Wenn RiskFraction positiv ist, berechnet die Strategie eine zusätzliche Größe mit equity × RiskFraction / 1000, ahmt die ursprüngliche AccountFreeMargin-Formel nach und verwendet das Maximum zwischen beiden Werten.
  • Nach zwei oder mehr aufeinanderfolgenden Verlustgeschäften wird die Losgröße um volume × losses / DecreaseFactor reduziert, wodurch die Drawdown-Kontrolle von DcF repliziert wird.
  • Die Lautstärken werden auf den VolumeStep des Instruments normalisiert.

Parameter

Name Standard Beschreibung
FastMaPeriod 4 Kurzer Lookback-Zeitraum von SMA.
SlowMaPeriod 8 Langsamer Lookback-Zeitraum von SMA.
CciPeriod 4 Zeitraum des Commodity Channel Index.
AtrPeriod 4 Durchschnittliche True-Range-Periode, die für die Stoppdistanz verwendet wird.
AtrMultiplier 1,0 Der Multiplikator wird vor der Platzierung des Stop-Loss auf ATR angewendet.
BaseVolume 0,1 Mindesthandelsgröße vor Risikoanpassungen.
RiskFraction 0,02 Anteil des pro Trade riskierten Eigenkapitals (pro 1000 Währungseinheiten).
DecreaseFactor 3 Divisor, der steuert, wie schnell die Größe nach Verlusten schrumpft.
CandleType 15-Minuten-Kerzen Für Indikatoren und Signale verwendeter Zeitrahmen.

Notizen

  • E-Mail-Benachrichtigungen des ursprünglichen Fachberaters (SndMl) werden bewusst weggelassen.
  • Es kann jeweils nur eine Position offen sein, entsprechend dem MetaTrader-Verhalten des Quellcodes.
  • Schutzstopps werden immer dann neu erstellt, wenn die Position umkippt oder geschlossen wird, um zu verhindern, dass verwaiste Aufträge im Buch verbleiben.
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 Adaptive Volume strategy - dual EMA crossover with CCI filter.
/// Buys when fast EMA crosses above slow EMA and CCI is above zero.
/// Sells when fast EMA crosses below slow EMA and CCI is below zero.
/// </summary>
public class Ma2CciAdaptiveVolumeStrategy : 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 Ma2CciAdaptiveVolumeStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 8)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetDisplay("Slow EMA", "Slow EMA 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 ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { 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;
	}
}