TenPointThree MACD Grid-Strategie
Überblick
Diese Strategie ist eine C#-Portierung des MetaTrader Expert Advisors 10p3v003 (10point3.mq4). Es kombiniert einen MACD-Crossover-Trigger mit einer Martingale-Grid-Engine. Die ursprüngliche Logik wurde unter Verwendung des High-Levels API von StockSharp mit den folgenden Schlüsselverhalten repliziert:
- MACD-Signallogik – Eine Handelsrichtung wird bestimmt, wenn die MACD-Hauptlinie die Signallinie auf dem verschobenen Balken (
SignalShift) kreuzt. Bei Long-Einträgen muss der vorherige Signalwert unter -TradingRangePips liegen, der aktuelle MACD-Wert muss unter Null bleiben und umgekehrt gilt für Short-Einträge. Signale können optional über ReverseSignal invertiert werden.
- Gitterschichtung – Nachdem die erste Position eröffnet wurde, sind weitere Eingaben in die gleiche Richtung nur dann zulässig, wenn sich der Preis um mindestens
GridStepPips gegenüber der letzten Füllung bewegt. Jedes neue Bein multipliziert das Volumen mit LotMultiplier (oder mit 1.5, wenn MaxTrades > 12) und ahmt die Martingal-Skalierung von MQL4 nach.
- Risikoschutz – Die letzte Etappe wird geschlossen und es werden keine weiteren Einträge hinzugefügt, wenn
OrdersToProtect oder mehr Geschäfte aktiv sind und der variable Gewinn den Geldschwellenwert überschreitet. Der Schwellenwert basiert entweder auf dem konfigurierten Risikoprozentsatz (Geldmanagement aktiviert) oder auf der Vertragsgrößenheuristik (Geldmanagement deaktiviert).
- Ausstiege pro Etappe – Jede Etappe verfolgt ihren eigenen Take-Profit, virtuellen Stop-Loss und Trailing-Stop. Der Stoppabstand entspricht der Originalformel:
InitialStopPips + (MaxTrades - existingOrders) * GridStepPips. Das Trailing wird erst aktiviert, wenn sich der Preis um TrailingStopPips + GridStepPips zugunsten der Position bewegt, und schließt das Bein, wenn der Preis um TrailingStopPips zurückgeht.
- Sitzungsfilter – Wenn
UseTimeFilter aktiviert ist, werden keine neuen Grids gestartet, solange die Kerzenzeit genau zwischen StopHour und StartHour liegt, wodurch der „Gefahrenzeitzone“-Schutz aus dem Skript reproduziert wird.
Bei allen Geldumrechnungen werden die PriceStep/StepPrice-Metadaten des Wertpapiers verwendet. Wenn die Börse keine Kontraktgröße offenlegt, wird ein Fallback-Wert von 100000 angewendet, der der ursprünglichen Forex-Annahme entspricht.
Parameter
| Name |
Beschreibung |
CandleType |
Candle-Abonnement, das für die Verarbeitung von MACD verwendet wird (Standard: 30-Minuten-Zeitraum). |
Volume |
Basislosgröße für die erste Rasterbestellung. |
TakeProfitPips |
Abstand in Pips für den Take-Profit jedes Beins (0 Deaktivierungen). |
InitialStopPips |
Basis-Stopp-Distanz in Pips. Der tatsächliche Stopp wächst mit der Anzahl der freien Rasterplätze. |
TrailingStopPips |
Trailing-Stop-Distanz in Pips, die angewendet wird, nachdem das Bein ausreichend profitabel ist (0 deaktiviert). |
MaxTrades |
Maximale Anzahl gleichzeitiger Martingaleinträge. |
LotMultiplier |
Multiplikator, der auf das Volumen jedes zusätzlichen Gitterzweigs angewendet wird (wird auf 1.5 überschrieben, wenn MaxTrades > 12). |
GridStepPips |
Mindestens erforderliche ungünstige Preisbewegung (in Pips), bevor der nächste Rastereintrag geöffnet wird. |
OrdersToProtect |
Mindestanzahl aktiver Zweige, bevor der Floating-Profit-Schutz den letzten Trade schließen kann. |
UseMoneyManagement |
Ermöglicht die dynamische Lotberechnung basierend auf dem Kontokapital. |
AccountType |
Wählt die Risikoformel aus: 0 – Standard (Eigenkapital / 10.000); 1 – Normal (Eigenkapital / 100.000); 2 – Nano (Eigenkapital / 1.000). |
RiskPercent |
Prozentsatz des verwendeten Eigenkapitals, wenn die Geldverwaltung aktiviert ist. |
ReverseSignal |
Invertiert lange/kurze MACD-Signale. |
FastEmaLength, SlowEmaLength, SignalLength |
MACD Zeiträume (standardmäßig 26.12.9). |
SignalShift |
Anzahl der geschlossenen Balken, die für die Crossover-Prüfung verwendet werden (Standard: 1). |
TradingRangePips |
MACD Signalband (in Pips), das durchbrochen werden muss, bevor ein Crossover akzeptiert wird. |
UseTimeFilter |
Aktiviert den Sitzungswächter basierend auf StopHour/StartHour. |
StopHour, StartHour |
Exklusiver Bereich, der die Erstellung eines neuen Rasters blockiert, wenn UseTimeFilter wahr ist. |
Hinweise zum Geldmanagement
Wenn UseMoneyManagement deaktiviert ist, wird das Basislos (Volume) direkt verwendet. Andernfalls berechnet EA die Losgröße aus dem aktuellen Eigenkapital unter Verwendung der gleichen Formeln wie das ursprüngliche EA:
- Kontotyp 0:
Ceil(risk% * equity / 10,000) / 10
- Kontotyp 1:
risk% * equity / 100,000
- Kontotyp 2:
risk% * equity / 1,000
Die Volumina werden mit Security.VolumeStep normalisiert und dann durch Security.MinVolume/MaxVolume begrenzt.
Ausführungsworkflow
- Abonnieren Sie den konfigurierten Kerzenstream und füttern Sie den Indikator MACD über
BindEx.
- Aktualisieren Sie bei jeder fertigen Kerze die Trailing-/Stopp-Logik für aktive Beine.
- Wenn die Crossover-Regeln MACD ausgelöst werden, stellen Sie sicher, dass der Sitzungsfilter den Handel zulässt, die Rasterrichtung mit der vorhandenen Position übereinstimmt und sich der Preis gegenüber der letzten Füllung um
GridStepPips bewegt hat.
- Berechnen Sie das nächste Beinvolumen mithilfe des Martingal-Multiplikators und senden Sie eine Marktorder.
- Überwachen Sie den schwankenden Gewinn; Sobald die Schutzschwelle erreicht ist, schließen Sie das neueste Bein und pausieren Sie bis zur nächsten Kerze.
Konvertierungshinweise
- Alle Kommentare wurden nach Bedarf in Englisch umgeschrieben.
- High-Level StockSharp API (Kerzen +
BindEx) wird verwendet. Ein direkter Zugriff auf den Indikatorwert wird vermieden.
- Berechnungen des variablen Gewinns basieren auf
PriceStep/StepPrice. Stellen Sie bei exotischen Instrumenten sicher, dass diese Felder ausgefüllt sind.
- Die Strategie verwaltet intern den Zustand pro Bein, um die Auftragsverwaltung von MQL4 zu emulieren, da StockSharp standardmäßig Positionen aggregiert.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// 10point3 MACD Grid: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class TenPointThreeMacdGridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public TenPointThreeMacdGridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 12)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 26)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI 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 FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class ten_point_three_macd_grid_strategy(Strategy):
"""EMA crossover with RSI filter and ATR stops."""
def __init__(self):
super(ten_point_three_macd_grid_strategy, self).__init__()
self._fast_ema = self.Param("FastEmaLength", 12).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_ema = self.Param("SlowEmaLength", 26).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetDisplay("RSI Length", "RSI period", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(ten_point_three_macd_grid_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
def OnStarted2(self, time):
super(ten_point_three_macd_grid_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
fast = ExponentialMovingAverage()
fast.Length = self._fast_ema.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_ema.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, rsi, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._prev_fast == 0 or self._prev_slow == 0 or atr_val <= 0:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fast_val < slow_val and self._prev_fast >= self._prev_slow) or close <= self._entry_price - atr_val * 2:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if (fast_val > slow_val and self._prev_fast <= self._prev_slow) or close >= self._entry_price + atr_val * 2:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
if fast_val > slow_val and self._prev_fast <= self._prev_slow and rsi_val > 50:
self._entry_price = close
self.BuyMarket()
elif fast_val < slow_val and self._prev_fast >= self._prev_slow and rsi_val < 50:
self._entry_price = close
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return ten_point_three_macd_grid_strategy()