CCI MA v1.5 Strategie
Diese Strategie erstellt den Expertenberater MetaTrader „CCI_MA v1.5“ innerhalb des StockSharp-High-Level-API neu. Der ursprüngliche Roboter wartet darauf, dass der Commodity Channel Index (CCI) einen einfachen gleitenden Durchschnitt überschreitet, der auf den CCI-Werten selbst berechnet wird, und verwendet einen sekundären CCI, um Exits um die ±100-Schwellenwerte herum zu überwachen. Der StockSharp-Port behält die gleiche Signalreihenfolge, das optionale Geldmanagement und die punktbasierten Stopp-/Zielentfernungen bei und passt alles an Kerzenabonnements und Indikatorbindungen an.
Wie es funktioniert
- Datenquelle – Eine benutzerdefinierte Kerzenserie (standardmäßig 15-Minuten-Kerzen) speist beide CCIs. Die Indikatoren lesen den Schlusskurs der Kerze, um die
PRICE_CLOSE-Einstellung von MetaTrader widerzuspiegeln.
- Kernindikatoren – Der primäre
CommodityChannelIndex (Parameter CciPeriod) liefert den Momentumwert. Ein SimpleMovingAverage mit dem Zeitraum MaPeriod wird auf den Strom von CCI-Werten angewendet, um die Triggerlinie zu bilden. Ein sekundärer CCI (SignalCciPeriod) überwacht überkaufte und überverkaufte Umkehrungen um ±100.
- Einstiegslogik – Ein Long-Trade wird auf dem Balken nach einem Aufwärts-Crossover eröffnet: Die zuvor abgeschlossene Kerze (
prevCci) muss über dem gleitenden Durchschnitt CCI liegen, während die Kerze davor (prev2Cci) darunter lag. Ein kurzes Signal ist die symmetrische Kreuzung nach unten. Bestehende gegensätzliche Positionen werden geschlossen und umgedreht, indem der Absolutwert der aktuellen Position zur neuen Ordergröße addiert wird, was dem Verhalten der MQL-Version entspricht.
- Ausstiegslogik – Long-Positionen werden liquidiert, wenn der aufsichtsrechtliche CCI von über +100 auf unter +100 fällt oder wenn der primäre CCI wieder unter seinen gleitenden Durchschnitt fällt (wiederum anhand der beiden zuvor abgeschlossenen Kerzen bewertet). Shorts treten unter den umgekehrten Bedingungen auf. Schutzstopps emulieren die punktbasierten Abstände von MetaTrader: Die Strategie leitet eine Pip-Größe aus dem Instrument
PriceStep ab (Multiplikation mit 10 für drei- oder fünfstellige Kurse) und vergleicht die Kerzenextreme mit entry ± distance bei jeder abgeschlossenen Kerze.
- Positionsgröße –
LotVolume definiert die Basis-Ordergröße. Wenn UseMoneyManagement aktiviert ist, multipliziert die Strategie es mit einem ganzzahligen Faktor gleich floor(balance / DepositPerLot), begrenzt durch MaxMultiplier, wodurch die Einzahlungsleiter des Fachberaters reproduziert wird. Das Bestellvolumen wird vor der Übermittlung an die Instrumentenbeschränkungen VolumeStep, MinVolume und MaxVolume angepasst.
Parameter
- Kerzentyp – Kerzendatentyp, der allen Indikatorberechnungen zugrunde liegt.
- CCI-Periode – Länge des primären CCI-Oszillators.
- Exit-CCI-Zeitraum – Länge des Überwachungszeitraums CCI, der für Schwellenwert-Exits verwendet wird.
- CCI MA-Periode – Periode des einfachen gleitenden Durchschnitts, der auf den primären CCI angewendet wird.
- Lotvolumen – Basishandelsvolumen vor der Skalierung des Geldmanagements.
- Geldverwaltung aktivieren – Aktiviert die einzahlungsbasierte Skalierung des Lotvolumens.
- Einzahlung pro Lot – Erforderliche Guthabenerhöhung, um den Lot-Multiplikator um eins zu erhöhen (wird nur verwendet, wenn die Geldverwaltung aktiv ist).
- Max. Multiplikator – Maximaler Multiplikator, den das Geldmanagement erreichen kann.
- Stop-Loss (Pips) – Abstand in Pips für den Schutzstopp; Zum Deaktivieren auf Null setzen.
- Take Profit (Pips) – Abstand in Pips für das Gewinnziel; Zum Deaktivieren auf Null setzen.
Die Strategie wartet auf zwei vollständig geschlossene Kerzen, bevor sie die erste Order erteilt, sodass die Zwei-Balken-Crossover-Vergleiche genau mit der verzögerten Ausführung des MQL-Experten übereinstimmen. Stop-Loss- und Take-Profit-Prüfungen werden für fertige Kerzen unter Verwendung ihrer Hoch-/Tief-Extremwerte ausgeführt, was den serverseitigen Schutzaufträgen von MetaTrader nahe kommt und gleichzeitig innerhalb des hohen Niveaus StockSharp API bleibt.
namespace StockSharp.Samples.Strategies;
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Commodity Channel Index strategy converted from the MetaTrader "CCI_MA v1.5" expert advisor.
/// Uses a primary CCI with a manually computed SMA of CCI values as a signal line.
/// A secondary CCI provides overbought/oversold exit confirmation.
/// </summary>
public class CciMaV15Strategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _signalCciPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private CommodityChannelIndex _cci;
private CommodityChannelIndex _signalCci;
private readonly List<decimal> _cciHistory = new();
private decimal? _prevCciMa;
private decimal? _prevCci;
private decimal? _prevSignalCci;
/// <summary>
/// Primary CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Secondary CCI period for exit signals.
/// </summary>
public int SignalCciPeriod
{
get => _signalCciPeriod.Value;
set => _signalCciPeriod.Value = value;
}
/// <summary>
/// SMA period applied to the primary CCI values.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Stop loss distance in absolute points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take profit distance in absolute points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public CciMaV15Strategy()
{
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Length of the primary CCI", "CCI")
.SetOptimize(7, 35, 7);
_signalCciPeriod = Param(nameof(SignalCciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Exit CCI Period", "Length of the secondary CCI", "CCI")
.SetOptimize(7, 35, 7);
_maPeriod = Param(nameof(MaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("CCI MA Period", "SMA length applied to the CCI", "CCI")
.SetOptimize(3, 21, 3);
_stopLossPoints = Param(nameof(StopLossPoints), 500m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Protective stop distance in absolute points", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 500m)
.SetNotNegative()
.SetDisplay("Take Profit", "Profit target distance in absolute points", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Market data series", "General");
Volume = 1;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cci = null;
_signalCci = null;
_cciHistory.Clear();
_prevCciMa = null;
_prevCci = null;
_prevSignalCci = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
_cci = new CommodityChannelIndex { Length = CciPeriod };
_signalCci = new CommodityChannelIndex { Length = SignalCciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_cci, _signalCci, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var indArea = CreateChartArea();
if (indArea != null)
{
DrawIndicator(indArea, _cci);
DrawIndicator(indArea, _signalCci);
}
}
var tp = TakeProfitPoints > 0 ? new Unit(TakeProfitPoints, UnitTypes.Absolute) : null;
var sl = StopLossPoints > 0 ? new Unit(StopLossPoints, UnitTypes.Absolute) : null;
if (tp != null || sl != null)
StartProtection(tp, sl);
base.OnStarted2(time);
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue, decimal signalCciValue)
{
if (candle.State != CandleStates.Finished)
return;
// Maintain CCI history for manual SMA calculation
_cciHistory.Add(cciValue);
if (_cciHistory.Count > MaPeriod)
_cciHistory.RemoveAt(0);
// Compute SMA of CCI
decimal? cciMa = null;
if (_cciHistory.Count >= MaPeriod)
{
decimal sum = 0;
for (int i = 0; i < _cciHistory.Count; i++)
sum += _cciHistory[i];
cciMa = sum / _cciHistory.Count;
}
if (cciMa == null || _prevCci == null || _prevCciMa == null || _prevSignalCci == null)
{
_prevCci = cciValue;
_prevCciMa = cciMa;
_prevSignalCci = signalCciValue;
return;
}
// Exit logic: secondary CCI overbought/oversold reversal
if (Position > 0 && _prevSignalCci > 100 && signalCciValue <= 100)
{
SellMarket(Position);
}
else if (Position < 0 && _prevSignalCci < -100 && signalCciValue >= -100)
{
BuyMarket(Math.Abs(Position));
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevCci = cciValue;
_prevCciMa = cciMa;
_prevSignalCci = signalCciValue;
return;
}
// Entry: CCI crosses above its MA (buy) or below (sell)
if (_prevCci < _prevCciMa && cciValue > cciMa.Value && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
}
else if (_prevCci > _prevCciMa && cciValue < cciMa.Value && Position >= 0)
{
if (Position > 0)
SellMarket(Position);
SellMarket(Volume);
}
_prevCci = cciValue;
_prevCciMa = cciMa;
_prevSignalCci = signalCciValue;
}
}
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 DataType, CandleStates, UnitTypes, Unit
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class cci_ma_v15_strategy(Strategy):
"""
CCI MA v1.5 strategy. Uses primary CCI with SMA signal line and secondary CCI for exits.
"""
def __init__(self):
super(cci_ma_v15_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14).SetDisplay("CCI Period", "Length of the primary CCI", "CCI")
self._signal_cci_period = self.Param("SignalCciPeriod", 14).SetDisplay("Exit CCI Period", "Length of the secondary CCI", "CCI")
self._ma_period = self.Param("MaPeriod", 9).SetDisplay("CCI MA Period", "SMA length applied to the CCI", "CCI")
self._stop_loss_points = self.Param("StopLossPoints", 500.0).SetDisplay("Stop Loss", "Protective stop distance in absolute points", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 500.0).SetDisplay("Take Profit", "Profit target distance in absolute points", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Market data series", "General")
self._cci_history = []
self._prev_cci_ma = None
self._prev_cci = None
self._prev_signal_cci = None
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(cci_ma_v15_strategy, self).OnReseted()
self._cci_history = []
self._prev_cci_ma = None
self._prev_cci = None
self._prev_signal_cci = None
def OnStarted2(self, time):
super(cci_ma_v15_strategy, self).OnStarted2(time)
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
signal_cci = CommodityChannelIndex()
signal_cci.Length = self._signal_cci_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, signal_cci, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
ind_area = self.CreateChartArea()
if ind_area is not None:
self.DrawIndicator(ind_area, cci)
self.DrawIndicator(ind_area, signal_cci)
tp = Unit(self._take_profit_points.Value, UnitTypes.Absolute) if self._take_profit_points.Value > 0 else None
sl = Unit(self._stop_loss_points.Value, UnitTypes.Absolute) if self._stop_loss_points.Value > 0 else None
if tp is not None or sl is not None:
self.StartProtection(tp, sl)
def on_process(self, candle, cci_value, signal_cci_value):
if candle.State != CandleStates.Finished:
return
self._cci_history.append(float(cci_value))
ma_period = self._ma_period.Value
if len(self._cci_history) > ma_period:
self._cci_history.pop(0)
cci_ma = None
if len(self._cci_history) >= ma_period:
cci_ma = sum(self._cci_history) / len(self._cci_history)
if cci_ma is None or self._prev_cci is None or self._prev_cci_ma is None or self._prev_signal_cci is None:
self._prev_cci = float(cci_value)
self._prev_cci_ma = cci_ma
self._prev_signal_cci = float(signal_cci_value)
return
# Exit logic
if self.Position > 0 and self._prev_signal_cci > 100 and float(signal_cci_value) <= 100:
self.SellMarket()
elif self.Position < 0 and self._prev_signal_cci < -100 and float(signal_cci_value) >= -100:
self.BuyMarket()
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_cci = float(cci_value)
self._prev_cci_ma = cci_ma
self._prev_signal_cci = float(signal_cci_value)
return
# Entry logic
if self._prev_cci < self._prev_cci_ma and float(cci_value) > cci_ma and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_cci > self._prev_cci_ma and float(cci_value) < cci_ma and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_cci = float(cci_value)
self._prev_cci_ma = cci_ma
self._prev_signal_cci = float(signal_cci_value)
def CreateClone(self):
return cci_ma_v15_strategy()