Bei dieser Strategie handelt es sich um eine StockSharp-High-Level-Portierung des klassischen Expert Advisors Moving Average, der mit MetaTrader ausgeliefert wird. 4. Das System beobachtet abgeschlossene Kerzen und vergleicht sie mit einem verschobenen einfachen gleitenden Durchschnitt (SMA), um Richtungsänderungen zu erkennen. Aufträge werden immer zum Marktwert ausgeführt und die Strategie bleibt mit höchstens einer offenen Position zu jedem Zeitpunkt auf dem Markt.
Handelslogik
Abonnieren Sie Kerzen des konfigurierbaren Zeitrahmens (Standard: 5 Minuten) und berechnen Sie einen SMA mit dem gewünschten Zeitraum.
Verschieben Sie SMA um die angegebene Anzahl abgeschlossener Kerzen, um das ursprüngliche Verhalten der Funktion iMA zu emulieren.
Bewerten Sie die zuvor fertige Kerze:
Bullish Cross (eröffnet unter dem verschobenen SMA und schließt darüber) löst einen Long-Einstieg aus, wenn keine Position offen ist.
Bearish Cross (eröffnet über und schließt unter dem verschobenen SMA) löst einen Short-Einstieg aus, wenn keine Position offen ist.
Verwalten Sie Exits mit denselben Kreuzregeln:
Eine Long-Position wird geschlossen, wenn die letzte Kerze den verschobenen SMA unterschreitet.
Eine Short-Position wird geschlossen, wenn die letzte Kerze den verschobenen SMA überschreitet.
Es kann immer nur eine Position existieren, die dem Verhalten des ursprünglichen EA entspricht, der zwischen Kauf- und Verkaufsaufträgen wechselte.
Parameter
Name
Beschreibung
Standard
CandleType
Für Berechnungen verwendete Kerzenreihe. Es kann ein beliebiger Zeitrahmen DataType ausgewählt werden.
Zeitrahmen von 5 Minuten
MovingPeriod
Anzahl der Kerzen für die Länge SMA.
12
MovingShift
Offset des SMA-Werts in abgeschlossenen Kerzen. Emuliert das shift-Argument von iMA.
6
BaseVolume
Standard-Auftragsvolumen für Einträge. Für Long- und Short-Trades wird das gleiche Volumen verwendet.
1
Umgang mit Indikatoren
Ein SimpleMovingAverage-Indikator wird in OnStarted erstellt und über das übergeordnete Bind API an das Kerzenabonnement gebunden.
Die Rohausgabe von SMA wird in einer kleinen FIFO-Warteschlange gepuffert, um den Wert von MovingShift Kerzen zu erhalten. Es wird keine manuelle Neuberechnung des Indikators durchgeführt.
Die Warteschlange behält nur MovingShift + 1-Werte bei, sodass die Speichernutzung auch bei großen Schichten konstant bleibt.
Auftrags- und Risikomanagement
Bestellungen werden mit BuyMarket/SellMarket aufgegeben und durch den Parameter BaseVolume dimensioniert. Beim Schließen wird die aktuelle absolute Positionsgröße verwendet, um einen vollständigen Ausstieg sicherzustellen.
Die ursprüngliche MetaTrader-Implementierung passte die Losgröße basierend auf der freien Marge und den jüngsten Verlusten dynamisch an. Der Port StockSharp hält die Logik deterministisch und delegiert die Positionsgröße über den Parameter BaseVolume an den Benutzer. Dies vermeidet die Abhängigkeit von Broker-spezifischen Kontometriken und behält gleichzeitig die Ein-/Ausstiegsregeln bei.
Konvertierungshinweise
Signale werden auf der vorherigen Kerze ausgewertet und entsprechen der Volume[0] == 1-Prüfung von MetaTrader, die auf einen neuen Balken wartete, bevor sie reagierte.
Es werden nur abgeschlossene Kerzen (CandleStates.Finished) verarbeitet, um vorzeitige Trades zu vermeiden.
Die Strategie nutzt die StockSharp-Chart-Helfer, um Kerzen, Indikatorwerte und Handelsmarkierungen zu zeichnen, wenn ein Chartbereich verfügbar ist.
Nutzung
Kompilieren Sie die Strategie in StockSharp Designer, Shell oder Runner.
Wählen Sie das gewünschte Instrument aus und weisen Sie ein Portfolio zu.
Konfigurieren Sie die Parameter, wenn unterschiedliche Zeitrahmen, Längen oder Volumina erforderlich sind.
Starten Sie die Strategie; Es abonniert die ausgewählte Kerzenserie, überwacht SMA Kreuze und handelt entsprechend.
Weitere Ideen
Fügen Sie Schutzstopps oder Take-Profit-Levels mit StartProtection hinzu, wenn ein Risikomanagement erforderlich ist, das über den grundlegenden Umkehrausstieg hinausgeht.
Ersetzen Sie den einfachen SMA durch einen anderen Indikator (EMA, LWMA usw.), indem Sie die Indikatorinstanz ändern und dabei den bestehenden Abonnement-Workflow beibehalten.
Führen Sie Positionsskalierungsregeln ein, indem Sie die Methode GetEntryVolume anpassen.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MovingAverageShiftStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
private int _cooldownRemaining;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MovingAverageShiftStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevEma = ema;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class moving_average_shift_strategy(Strategy):
def __init__(self):
super(moving_average_shift_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(moving_average_shift_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(moving_average_shift_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_ema = ema_val
return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return moving_average_shift_strategy()