Umrechnung des MetaTrader 5 Expert Advisors „Moving Average with Frames“. Das ursprüngliche System wertet die Beziehung zwischen den Eröffnungs-/Schlusskursen jeder Kerze und einem verschobenen einfachen gleitenden Durchschnitt (SMA) aus und zeigt gleichzeitig mehrere Optimierungs-„Frames“ in Diagrammen an. Dieser StockSharp-Port konzentriert sich auf die Handelslogik: Er reagiert nur einmal pro abgeschlossenem Balken, eröffnet eine einzelne Netting-Position und spiegelt die Geldverwaltungsregeln aus dem Quellcode wider.
Handelslogik
Datenquelle – Die Strategie abonniert den konfigurierten Zeitrahmen (CandleType) und verarbeitet nur fertige Kerzen, wodurch die MetaTrader-Einschränkung if(rt[1].tick_volume>1) return; reproduziert wird.
Indikator – ein einfacher gleitender Durchschnitt mit der Periode MovingPeriod. Die Indikatorausgabe wird um MovingShift abgeschlossene Kerzen nach vorne verschoben, indem ein Puffer vergangener Werte beibehalten wird.
Aufwärmphase – Der Handel wird ausgesetzt, bis mindestens 100 abgeschlossene Kerzen gesammelt wurden, was dem ursprünglichen Bars(_Symbol,_Period)>100-Guard entspricht.
Eintrittsbedingungen
Gehen Sie long, wenn die Kerze unterhalb des verschobenen SMA öffnet und darüber schließt.
Gehen Sie short, wenn die Kerze über dem verschobenen SMA öffnet und darunter schließt.
Der Motor erzwingt eine einzelne Position: Die entgegengesetzte Belichtung wird abgeflacht, bevor in die neue Richtung eingetreten wird.
Ausstiegsbedingungen – eine bestehende Long-Position wird geschlossen, wenn der Eröffnungspreis über und der Schlusskurs unter dem verschobenen SMA liegt; Shorts sind auf der gegenüberliegenden Kreuzung geschlossen. Neue Trades werden nach einem Ausstieg nicht auf demselben Balken eröffnet, genau wie beim ursprünglichen Experten.
Positionsgröße und Risiko
MaximumRisk – bestimmt das rohe Auftragsvolumen als Portfolio.CurrentValue * MaximumRisk / price, wenn Portfoliodaten verfügbar sind. Wenn der Broker-Feed keine Aktieninformationen bereitstellt, greift die Strategie auf die manuelle Eigenschaft Volume zurück.
DecreaseFactor – nach mehr als einem Verlusthandel in Folge wird die nächste Positionsgröße um volume * losses / DecreaseFactor reduziert, was die Lot-Reduktionslogik von MetaTrader nachahmt. Jeder gewinnbringende Handel setzt den Zähler zurück.
Volumenausrichtung – die berechnete Größe wird auf VolumeStep des Instruments normalisiert, zwischen MinVolume und MaxVolume eingeklemmt und auf zwei Dezimalstellen gerundet, wenn die Börse keinen Schritt veröffentlicht.
Zusätzliche Hinweise
Die MetaTrader-Visualisierung „Frames“ wird nicht portiert, da StockSharp bereits umfangreiche Optimierungs-Dashboards bereitstellt. Die Handelslogik, das Signal-Timing und das Größenverhalten bleiben der Quelle treu.
Alle Indikatorwerte werden direkt vom Bind-Callback verbraucht; Es werden keine manuellen GetValue-Aufrufe verwendet.
Die fortlaufende Verlustverfolgung ist in OnOwnTradeReceived implementiert, sodass die Strategie korrekt auf Teilfüllungen und Netting-Verhalten reagieren kann.
Parameter
Parameter
Standard
Beschreibung
MaximumRisk
0.02
Anteil des Portfolio-Eigenkapitals, das bei jedem Einstieg riskiert wird.
DecreaseFactor
3
Divisor wird verwendet, um die Positionsgröße nach zwei oder mehr aufeinanderfolgenden Verlusten zu verkleinern.
MovingPeriod
12
Länge des einfachen gleitenden Durchschnitts, der auf die Schlusskurse angewendet wird.
MovingShift
6
Anzahl der abgeschlossenen Kerzen, die zum zeitlichen Versatz von SMA nach vorne verwendet werden.
CandleType
1h time frame
Von der Strategie verarbeitete primäre Kerzenserie.
Nutzungstipps
Hängen Sie die Strategie in StockSharp Designer oder Code an ein Wertpapier und Portfolio an.
Passen Sie den Kerzentyp an den gewünschten Diagrammzeitraum von MetaTrader an.
Passen Sie MaximumRisk und DecreaseFactor an Ihre Kontogröße und die gewünschte Risikotoleranz an.
Führen Sie Backtests durch, um zu überprüfen, ob die Crossover-Signale mit den ursprünglichen MetaTrader-Ergebnissen übereinstimmen.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Moving Average With Frames: SMA crossover with candle body confirmation.
/// Buys when close crosses above shifted SMA, sells when crosses below.
/// </summary>
public class MovingAverageWithFramesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _prevClose;
public MovingAverageWithFramesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_smaLength = Param(nameof(SmaLength), 12)
.SetDisplay("SMA Length", "SMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevClose = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var close = candle.ClosePrice;
if (_prevClose == 0)
{
_prevClose = close;
return;
}
if (Position > 0)
{
if (close < smaVal && _prevClose >= smaVal)
{
SellMarket();
_entryPrice = 0;
}
else if (close <= _entryPrice - atrVal * 2m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close > smaVal && _prevClose <= smaVal)
{
BuyMarket();
_entryPrice = 0;
}
else if (close >= _entryPrice + atrVal * 2m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > smaVal && _prevClose <= smaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < smaVal && _prevClose >= smaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevClose = close;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange
class moving_average_with_frames_strategy(Strategy):
def __init__(self):
super(moving_average_with_frames_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._sma_length = self.Param("SmaLength", 12) \
.SetDisplay("SMA Length", "SMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._prev_close = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def SmaLength(self):
return self._sma_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(moving_average_with_frames_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._prev_close = 0.0
self._sma = SimpleMovingAverage()
self._sma.Length = self.SmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._sma, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
sv = float(sma_val)
av = float(atr_val)
if av <= 0:
return
close = float(candle.ClosePrice)
if self._prev_close == 0:
self._prev_close = close
return
if self.Position > 0:
if close < sv and self._prev_close >= sv:
self.SellMarket()
self._entry_price = 0.0
elif close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close > sv and self._prev_close <= sv:
self.BuyMarket()
self._entry_price = 0.0
elif close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = close
return
if self.Position == 0:
if close > sv and self._prev_close <= sv:
self._entry_price = close
self.BuyMarket()
elif close < sv and self._prev_close >= sv:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def OnReseted(self):
super(moving_average_with_frames_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_close = 0.0
def CreateClone(self):
return moving_average_with_frames_strategy()