Die Extreme EA Strategie ist ein Trendfolge-Experte, der ursprünglich für MetaTrader geschrieben wurde. Sie kombiniert zwei gleitende Durchschnitte mit einem Commodity Channel Index (CCI) Filter und einem adaptiven Geldverwaltungsmodul. Dieser Port hält die Handelslogik intakt, während alle wichtigen Parameter über die High-Level-API von StockSharp zugänglich gemacht werden. Die Strategie operiert nur auf abgeschlossenen Kerzen und ist mit mehreren Zeitrahmen kompatibel, indem die gleitenden Durchschnitte und der CCI auf unabhängigen Kerzenabonnements ausgeführt werden.
Strategie-Überblick
Trendfilter: Zwei gleitende Durchschnitte werden auf dem konfigurierbaren MaCandleType berechnet. Der schnelle Durchschnitt verfolgt kurzfristiges Momentum, während der langsame Durchschnitt die dominante Trendsteigung definiert. Die Strategie prüft die Steigung des langsamen Durchschnitts mit den zwei vorherigen Werten, um die ursprünglichen CopyBuffer-Array-Offsets aus dem MQL-Code nachzuahmen.
Momentum-Filter: Der CCI wird auf seinem eigenen Zeitrahmen (CciCandleType) und seiner Preisquelle ausgewertet. Der letzte abgeschlossene Wert wird zwischengespeichert und wiederverwendet, bis eine neue CCI-Kerze erscheint, was dem Verhalten der MetaTrader-Puffer entspricht.
Einstiegsregeln:
Long eintreten, wenn der langsame MA steigt, der schnelle MA steigt und der CCI unter das untere Niveau fällt.
Short eintreten, wenn der langsame MA fällt, der schnelle MA fällt und der CCI über das obere Niveau steigt.
Ausstiegsregeln:
Alle Longs schließen, wenn der langsame MA aufhört zu steigen.
Alle Shorts schließen, wenn der langsame MA aufhört zu fallen.
Risikomanagement
MaximumRisk steuert die Zielpositionsgröße basierend auf dem aktuellen Portfolio-Eigenkapital und dem letzten Preis. Wenn das berechnete Volumen null ist oder die Portfolio-Werte nicht verfügbar sind, greift die Strategie auf das konfigurierte Volume oder das Börsenminimum zurück.
DecreaseFactor reduziert das berechnete Volumen nach zwei oder mehr aufeinanderfolgenden Verlust-Trades. Die Reduktion spiegelt die ursprüngliche Formel lot = lot - lot * losses / DecreaseFactor wider.
HistoryDays begrenzt, wie lange eine Verluststrähne erinnert wird. Wenn ein Schließungshandel nach der angegebenen Anzahl von Tagen stattfindet, wird die Strähne vor der Anwendung der Reduktion zurückgesetzt.
MaxPositions begrenzt die Pyramidisierung, indem die Nettoexposition pro Richtung begrenzt wird. Wenn das Limit erreicht wird, werden neue Einstiege unterdrückt, bis die Exposition sinkt.
Parameter
Name
Typ
Standard
Beschreibung
MaximumRisk
decimal
0.05
Eigenkapitalanteil für die Dimensionierung jedes neuen Trades.
DecreaseFactor
decimal
6
Verluststrähnen-Reduktionsfaktor. Auf 0 setzen, um zu deaktivieren.
HistoryDays
int
60
Anzahl der Tage, die beim Zählen aufeinanderfolgender Verluste aufbewahrt werden.
MaxPositions
int
3
Maximale gleichzeitige Einstiege pro Richtung.
FastMaPeriod
int
15
Periode für den schnellen gleitenden Durchschnitt.
SlowMaPeriod
int
75
Periode für den langsamen gleitenden Durchschnitt.
CciPeriod
int
12
Lookback-Länge für den CCI.
CciUpperLevel
decimal
50
Oberer CCI-Schwellenwert für Shorts.
CciLowerLevel
decimal
-50
Unterer CCI-Schwellenwert für Longs.
MaCandleType
DataType
15m
Zeitrahmen für beide gleitenden Durchschnitte und Ausführung.
Die Strategie abonniert den Zeitrahmen der gleitenden Durchschnitte einmal und optional ein zweites Abonnement für den CCI. Wenn beide Zeitrahmen übereinstimmen, speist ein einzelnes Abonnement beide Komponenten und reproduziert den ursprünglichen Einzel-Chart-Workflow.
Vorherige Indikatorwerte werden in privaten Feldern zwischengespeichert, um die Vergleiche ma_slow_array[1], ma_slow_array[2] und ma_fast_array[0] ohne manuelle Indikatorpuffer zu emulieren.
Die Positionsgrößenbestimmung wird gegen den Instrument-Volumenschritt, das Minimum und Maximum normalisiert, um abgelehnte Orders zu vermeiden.
Das Risikomodul zeichnet Einstiegs- und Ausstiegspreise auf, um den realisierten PnL pro abgeschlossener Position zu schätzen, was die in MetaTrader verwendete HistoryDealGet-Schleife ersetzt.
Unterschiede zur MQL-Version
MetaTrader-spezifische Funktionen wie FreeMarginCheck, MarginCheck und HistorySelect werden mit StockSharp-Portfolio-Metriken und dem internen Verluststrähnen-Tracker approximiert.
Der StockSharp-Port operiert auf Nettopositionen. Schließungsorders glätten die gesamte Exposition in der relevanten Richtung, was mit dem konsolidierten Positionsmodell übereinstimmt.
Protokollierungsroutinen aus dem Original-EA wurden zugunsten der integrierten Diagnose von StockSharp weggelassen.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Extreme EA strategy using fast/slow EMA crossover with CCI filter.
/// Buys when both EMAs rising and CCI below lower level (oversold bounce).
/// Sells when both EMAs falling and CCI above upper level (overbought reversal).
/// </summary>
public class ExtremeEaStrategy : Strategy
{
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciUpperLevel;
private readonly StrategyParam<decimal> _cciLowerLevel;
private ExponentialMovingAverage _fastMa;
private ExponentialMovingAverage _slowMa;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _prevFast2;
private decimal _prevSlow2;
private bool _hasPrev;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastMaPeriod
{
get => _fastMaPeriod.Value;
set => _fastMaPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
/// <summary>
/// CCI indicator length.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Upper CCI threshold for sell entries.
/// </summary>
public decimal CciUpperLevel
{
get => _cciUpperLevel.Value;
set => _cciUpperLevel.Value = value;
}
/// <summary>
/// Lower CCI threshold for buy entries.
/// </summary>
public decimal CciLowerLevel
{
get => _cciLowerLevel.Value;
set => _cciLowerLevel.Value = value;
}
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public ExtremeEaStrategy()
{
_fastMaPeriod = Param(nameof(FastMaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Fast MA", "Fast EMA period", "Indicator");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("Slow MA", "Slow EMA period", "Indicator");
_cciPeriod = Param(nameof(CciPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI lookback period", "Indicator");
_cciUpperLevel = Param(nameof(CciUpperLevel), 50m)
.SetDisplay("CCI Upper", "Upper CCI threshold for sell", "Levels");
_cciLowerLevel = Param(nameof(CciLowerLevel), -50m)
.SetDisplay("CCI Lower", "Lower CCI threshold for buy", "Levels");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastMa = null;
_slowMa = null;
_prevFast = 0;
_prevSlow = 0;
_prevFast2 = 0;
_prevSlow2 = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fastMa, _slowMa, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fastMa.IsFormed || !_slowMa.IsFormed)
{
_prevFast2 = _prevFast;
_prevSlow2 = _prevSlow;
_prevFast = fastValue;
_prevSlow = slowValue;
_hasPrev = true;
return;
}
if (!_hasPrev)
{
_prevFast2 = _prevFast;
_prevSlow2 = _prevSlow;
_prevFast = fastValue;
_prevSlow = slowValue;
_hasPrev = true;
return;
}
var slowIsRising = _prevSlow > _prevSlow2;
var slowIsFalling = _prevSlow < _prevSlow2;
var fastIsRising = fastValue > _prevFast;
var fastIsFalling = fastValue < _prevFast;
// Buy: fast crosses above slow
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Sell: fast crosses below slow
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevFast2 = _prevFast;
_prevSlow2 = _prevSlow;
_prevFast = fastValue;
_prevSlow = slowValue;
}
}