Divergence Trader Basket-Strategie
Überblick
Diese Strategie ist eine StockSharp-Portierung des Expertenberaters „Divergence Trader“ MetaTrader. Es vergleicht zwei einfache gleitende Durchschnitte
berechnet auf konfigurierbaren Preisquellen und misst deren Differenz (Divergenz). Wenn der Abstand zwischen schnell und langsam ist
Wenn die Durchschnittswerte in einen neutralen Korridor fallen, geht der Algorithmus davon aus, dass die Dynamik bald wieder einsetzt, und eröffnet eine Position im
Richtung der vorherrschenden Voreingenommenheit. Die Implementierung verwendet nur abgeschlossene Kerzen aus einem ausgewählten Zeitrahmen und verlässt sich auf die
High-Level-API mit Indikatorbindungen.
Parameter
- Lotgröße – Handelsvolumen, das mit jeder neuen Position übermittelt wird. Der Wert richtet sich nach dem Lautstärkeschritt des Instruments.
- Schneller SMA-Zeitraum/Preis – Länge und Preisquelle für den sich schnell bewegenden Durchschnitt.
- Langsamer SMA Zeitraum / Preis – Länge und Preisquelle für den langsamen gleitenden Durchschnitt.
- Kaufschwelle – minimale positive Divergenz erforderlich, bevor eine Long-Position eröffnet wird.
- Stay-Out-Schwelle – maximal zulässige Abweichung für neue Einträge; Werte außerhalb dieses Bereichs deaktivieren den Handel.
- Take Profit (Pips) – Gewinnziel ausgedrückt in Pips. Deaktiviert, wenn auf Null gesetzt.
- Stop Loss (Pips) – Verlusttoleranz in Pips. Deaktiviert, wenn auf Null gesetzt.
- Trailing Stop (Pips) – Trailing-Distanz wird aktiviert, nachdem der Handel profitabel wird. Deaktiviert, wenn Null.
- Break-Even-Trigger/Puffer (Pips) – Pip-Gewinn erforderlich, bevor die Position beim Break-Even und optionalem Puffer geschützt wird
Ausgleich des Break-Even-Stops vom Einstiegspreis.
- Korbgewinn/Korbverlust – auf Kontoeigenkapital basierende Schwellenwerte, die bei Erreichen alle Positionen abflachen. Verlustkontrolle ist
standardmäßig deaktiviert.
- Startstunde / Stoppstunde – Handelsfenster in Ortszeit. Wenn beide Werte gleich sind, funktioniert die Strategie den ganzen Tag.
- Kerzentyp – Zeitrahmen, der sowohl für die Signalgenerierung als auch für das Risikomanagement verwendet wird.
Handelslogik
- Abonnieren Sie die konfigurierte Kerzenserie und berechnen Sie die schnellen und langsamen einfachen gleitenden Durchschnitte.
- Arbeiten Sie nur mit fertigen Kerzen, um Intrabar-Rauschen zu vermeiden und nahe am ursprünglichen EA-Verhalten zu bleiben.
- Verfolgen Sie die Divergenz (schnell minus langsam), die für die zuvor fertige Kerze berechnet wurde:
- Wenn die Divergenz positiv ist und zwischen der Kaufschwelle und der Stay-Out-Schwelle bleibt, erteilen Sie eine Marktkauforder.
- Wenn die Divergenz negativ ist und ihr absoluter Wert innerhalb des Korridors bleibt, erteilen Sie einen Marktverkaufsauftrag.
- Trades werden außerhalb der erlaubten Zeiten oder wenn die Strategie bereits eine offene Position hat, ignoriert.
Positionsmanagement
- Break-even-Kontrolle – wenn der variable Gewinn den Auslöser erreicht, speichert die Strategie ein Break-even-Stop-Level (optional).
durch den Puffer verschoben). Eine Kerze, die dieses Niveau berührt, schließt die Position.
- Trailing Stop – sobald der Gewinn die Trailing-Distanz überschreitet, folgt das Stop-Level immer dem günstigsten Preis
um die konfigurierte Anzahl an Pips dahinter zu bleiben.
- Take Profit / Stop Loss – feste Ausstiege, berechnet aus dem Einstiegspreis in Pip-Einheiten.
- Korbschutz – Portfolioeigenkapital wird mit den konfigurierten Gewinn- und Verlustgrenzen verglichen. Beide Grenzen erreichen
Schließt die aktuelle Position und storniert aktive Aufträge und emuliert dabei die Routine „CloseEverything“ aus der MQL-Version.
Nutzungshinweise
- Der Divergenzkorridor ist symmetrisch: Durch die Ausweitung des Stay-Out-Schwellenwerts können Geschäfte länger offen bleiben, während er gleichzeitig enger wird
erhöht die Frequenz der Signale.
- Preisquellenoptionen entsprechen StockSharp
CandlePrice-Werten und ermöglichen die Verwendung von Eröffnung, Schluss, Median oder Typisch
Preise wie in MetaTrader.
- Die Strategie zeichnet Kerzen, sowohl gleitende Durchschnitte als auch ausgeführte Orders, zur Überwachung und Fehlerbehebung in einem Diagrammbereich auf.
- Die Funktionen zur Geldverwaltung hängen von den Portfoliodaten ab. Bei der Ausführung in einer Sandbox ohne Portfoliostatistiken sind Korbkontrollen vorhanden
automatisch ignoriert.
using System;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Divergence-based strategy converted from the Divergence Trader MQL expert advisor.
/// Trades based on the divergence between fast and slow moving averages.
/// </summary>
public class DivergenceTraderBasketStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<decimal> _buyThreshold;
private readonly StrategyParam<decimal> _stayOutThreshold;
private readonly StrategyParam<DataType> _candleType;
private decimal? _previousDifference;
private decimal _entryPrice;
public DivergenceTraderBasketStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 7)
.SetDisplay("Fast SMA Period", "Length of the fast simple moving average.", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 88)
.SetDisplay("Slow SMA Period", "Length of the slow simple moving average.", "Indicators");
_buyThreshold = Param(nameof(BuyThreshold), 0.0001m)
.SetDisplay("Buy Threshold", "Minimum divergence value required before buying.", "Signals");
_stayOutThreshold = Param(nameof(StayOutThreshold), 1000m)
.SetDisplay("Stay-Out Threshold", "Upper divergence limit that disables new entries.", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe used for calculations.", "General");
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public decimal BuyThreshold
{
get => _buyThreshold.Value;
set => _buyThreshold.Value = value;
}
public decimal StayOutThreshold
{
get => _stayOutThreshold.Value;
set => _stayOutThreshold.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousDifference = null;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousDifference = null;
_entryPrice = 0;
var fastMa = new SimpleMovingAverage { Length = FastPeriod };
var slowMa = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastMa, slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
var currentDiff = fastValue - slowValue;
if (_previousDifference == null)
{
_previousDifference = currentDiff;
return;
}
var prevDiff = _previousDifference.Value;
_previousDifference = currentDiff;
// Manage open position
if (Position != 0)
{
// Exit on divergence sign change
if (Position > 0 && currentDiff < 0)
{
SellMarket();
_entryPrice = 0;
}
else if (Position < 0 && currentDiff > 0)
{
BuyMarket();
_entryPrice = 0;
}
return;
}
// Entry logic: divergence crosses zero line
if (currentDiff > 0 && prevDiff <= 0)
{
// Bullish divergence crossover
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (currentDiff < 0 && prevDiff >= 0)
{
// Bearish divergence crossover
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class divergence_trader_basket_strategy(Strategy):
"""
Divergence-based strategy using fast and slow SMA.
Trades based on the divergence crossing zero line between two moving averages.
"""
def __init__(self):
super(divergence_trader_basket_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast SMA Period", "Length of the fast simple moving average", "Indicators")
self._slow_period = self.Param("SlowPeriod", 88) \
.SetDisplay("Slow SMA Period", "Length of the slow simple moving average", "Indicators")
self._buy_threshold = self.Param("BuyThreshold", 0.0001) \
.SetDisplay("Buy Threshold", "Minimum divergence value required before buying", "Signals")
self._stay_out_threshold = self.Param("StayOutThreshold", 1000.0) \
.SetDisplay("Stay-Out Threshold", "Upper divergence limit that disables new entries", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Primary timeframe used for calculations", "General")
self._previous_difference = None
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(divergence_trader_basket_strategy, self).OnReseted()
self._previous_difference = None
self._entry_price = 0.0
def OnStarted2(self, time):
super(divergence_trader_basket_strategy, self).OnStarted2(time)
fast_ma = SimpleMovingAverage()
fast_ma.Length = self._fast_period.Value
slow_ma = SimpleMovingAverage()
slow_ma.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ma, slow_ma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
current_diff = float(fast_value) - float(slow_value)
if self._previous_difference is None:
self._previous_difference = current_diff
return
prev_diff = self._previous_difference
self._previous_difference = current_diff
if self.Position != 0:
if self.Position > 0 and current_diff < 0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0 and current_diff > 0:
self.BuyMarket()
self._entry_price = 0.0
return
if current_diff > 0 and prev_diff <= 0:
self.BuyMarket()
self._entry_price = float(candle.ClosePrice)
elif current_diff < 0 and prev_diff >= 0:
self.SellMarket()
self._entry_price = float(candle.ClosePrice)
def CreateClone(self):
return divergence_trader_basket_strategy()