Send Close Order ist eine Portierung des 2009 MetaTrader 4 Expertenberaters „SendCloseOrder“ von Vladimir Hlystov. Das ursprüngliche Skript zeichnet vier manuelle Trendlinien basierend auf Bill Williams-Fraktalen und öffnet oder schließt Marktaufträge, wann immer der Preis eines dieser prognostizierten Niveaus berührt. Die StockSharp-Version repliziert die Entscheidungslogik mit vollautomatischem Linienmanagement und funktioniert auf allen von der Plattform bereitgestellten Kerzenserien.
Handelslogik
Fraktale Erkennung – jede fertige Kerze aktualisiert ein Schiebefenster mit fünf Balken. Sobald das Fenster voll ist, wird die Kerze in der Mitte mit den fraktalen Bedingungen von Bill Williams verglichen. Bestätigte Höchst- und Tiefstwerte werden chronologisch gespeichert.
Trendlinienrekonstruktion
Verkaufslinie verbindet die letzten beiden Aufwärtsfraktale, die durch ein Abwärtsfraktal getrennt sind, und bildet eine Widerstandssteigung.
Close #1 ist die Verkaufslinie, die um 15 Preisschritte (15 × Security.PriceStep) nach oben verschoben wird und als lange Ausstiegsschiene fungiert.
Kauflinie verbindet die letzten beiden Abwärtsfraktale, die durch ein Aufwärtsfraktal getrennt sind, und bildet eine Unterstützungsneigung.
Close #2 ist die um 15 Preisschritte nach unten verschobene Kauflinie und fungiert als Short-Exit-Schiene.
Signalauswertung – die vier Zeilen werden auf den Zeitstempel der fertigen Kerze hochgerechnet. Liegt der prognostizierte Preis innerhalb des Hoch-/Tief-Bereichs der Kerze (mit einer kleinen Toleranz von zwei Preisschritten), wird die entsprechende Aktion ausgelöst.
Auftragsverwaltung
Durch Berühren von „Schließen Nr. 1“ oder „Schließen Nr. 2“ wird die gesamte Position sofort über ClosePosition() geschlossen.
Durch Berühren der Verkaufs- oder Kauflinie wird eine Marktorder mit einem Volumen von TradeVolume geöffnet, sofern die resultierende absolute Position MaxOrders × TradeVolume nicht überschreitet. Wenn eine Gegenposition vorhanden ist, gleicht die Order diese zunächst aus und stapelt dann einen neuen Eintrag, was dem Verhalten von Sicherungskonten entspricht.
Parameter
Name
Standard
Beschreibung
EnableSellLine
true
Erlauben Sie Trades, wenn die prognostizierte Widerstandslinie erreicht wird.
EnableBuyLine
true
Erlauben Sie Trades, wenn die prognostizierte Unterstützungslinie erreicht wird.
EnableCloseLongLine
true
Ermöglichen Sie das Schließen von Long-Positionen auf der verschobenen Widerstandslinie (Schluss Nr. 1).
EnableCloseShortLine
true
Erlauben Sie das Schließen von Short-Positionen auf der verschobenen Unterstützungslinie (Schluss #2).
MaxOrders
1
Maximale Anzahl gestapelter Einträge in der aktuellen Richtung.
TradeVolume
0.1
Volumen jeder einzelnen Market Order.
CandleType
1h Zeitrahmen
Für fraktale Berechnungen verwendete Kerzenserie.
Unterschiede zur MetaTrader-Version
Der Port StockSharp berechnet die vier Linien jedes Mal neu, wenn ein neues Fraktal erscheint. In MetaTrader musste der Benutzer Trendlinien manuell löschen und neu zeichnen.
Die Ausführung basiert auf aggregierten Nettopositionen; Gleichzeitige Long- und Short-Baskets werden vom Standardportfoliomodell von StockSharp nicht unterstützt.
Die Berührungserkennung verwendet das Hoch/Tief der fertigen Kerze mit einer Preisschritttoleranz anstelle der momentanen Bid/Ask-Kurse von Ticks.
Diagrammobjekte (Trendlinien und Beschriftungen) werden nicht erstellt; Der Fokus liegt auf Handelssignalen.
Nutzungshinweise
Die Strategie kann auf jedem Instrument ausgeführt werden, das Kerzen und einen gültigen PriceStep bereitstellt. Wenn Security.PriceStep Null ist, fällt der Code auf 0.0001 zurück.
Erhöhen Sie MaxOrders, um das Stapelverhalten des ursprünglichen EA zu emulieren. Halten Sie TradeVolume an der Losgröße des Instruments ausgerichtet, um Rundungen zu vermeiden.
Der Linienversatz ist auf den historischen Wert von 15 Punkten festgelegt. Passen Sie den Quellcode an, wenn die Eingabe MetaTrader geändert wird.
Es wird nur die C#-Implementierung bereitgestellt. Eine Python-Übersetzung wird bei Bedarf separat hinzugefügt.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SendCloseOrder: Fractal high/low breakout with EMA filter and ATR stops.
/// </summary>
public class SendCloseOrderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _fractalHigh;
private decimal _fractalLow;
private decimal _prev2High;
private decimal _prev1High;
private decimal _prev2Low;
private decimal _prev1Low;
private int _barCount;
public SendCloseOrderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
// Detect fractal high: prev1High > prev2High and prev1High > current high
if (_barCount > 3 && _prev1High > _prev2High && _prev1High > high)
_fractalHigh = _prev1High;
// Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if (_barCount > 3 && _prev1Low < _prev2Low && _prev1Low < low)
_fractalLow = _prev1Low;
_prev2High = _prev1High;
_prev1High = high;
_prev2Low = _prev1Low;
_prev1Low = low;
if (_fractalHigh == 0 || _fractalLow == 0 || atrVal <= 0)
return;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > _fractalHigh && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < _fractalLow && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
}
}
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 ExponentialMovingAverage, AverageTrueRange
class send_close_order_strategy(Strategy):
def __init__(self):
super(send_close_order_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(send_close_order_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
self._bar_count += 1
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
# Detect fractal high: prev1High > prev2High and prev1High > current high
if self._bar_count > 3 and self._prev1_high > self._prev2_high and self._prev1_high > high:
self._fractal_high = self._prev1_high
# Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if self._bar_count > 3 and self._prev1_low < self._prev2_low and self._prev1_low < low:
self._fractal_low = self._prev1_low
self._prev2_high = self._prev1_high
self._prev1_high = high
self._prev2_low = self._prev1_low
self._prev1_low = low
if self._fractal_high == 0 or self._fractal_low == 0 or av <= 0:
return
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > self._fractal_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._fractal_low and close < ev:
self._entry_price = close
self.SellMarket()
def OnReseted(self):
super(send_close_order_strategy, self).OnReseted()
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
def CreateClone(self):
return send_close_order_strategy()