Quelle: Konvertiert vom MetaTrader 5 Expert Advisor "Vortex Indicator System" (MQL ID 19137).
Konzept: Verwendet den Vortex-Indikator, um bullische oder bärische Crossover zu erkennen und dann Ausbruchstrigger am Hoch/Tief der Crossover-Kerze zu setzen.
Ausführungsstil: Ausbruchsverfolgung; Trades werden nur initiiert, nachdem der Preis den Crossover durch Überschreiten des Trigger-Levels bestätigt.
Marktregime: Funktioniert auf jedem Instrument und Zeitrahmen, der den Vortex-Indikator und Kerzendaten unterstützt; broker-spezifische Funktionen sind nicht erforderlich.
Ordertypen: Market Orders über BuyMarket und SellMarket. Die Strategie schließt entgegengesetzte Positionen automatisch, bevor ein neuer Trigger in die Warteschlange gestellt wird.
Handelslogik
Das konfigurierte Kerzentyp abonnieren und den Vortex-Indikator mit der angegebenen Länge berechnen.
Einen bullischen Crossover erkennen, wenn VI+ sich über VI- bewegt, nachdem er auf der vorherigen Kerze darunter war:
Jede bestehende Short-Position über ClosePosition() schließen.
Das Hoch der Crossover-Kerze als Long-Trigger-Preis speichern.
Jeden ausstehenden Short-Trigger stornieren.
Einen bärischen Crossover erkennen, wenn VI- sich über VI+ bewegt, nachdem er auf der vorherigen Kerze darunter war:
Jede bestehende Long-Position schließen.
Das Tief der Crossover-Kerze als Short-Trigger-Preis speichern.
Jeden ausstehenden Long-Trigger stornieren.
Während ein Trigger aktiv ist, nachfolgende Kerzen überwachen:
Wenn der High-Preis den gespeicherten Long-Trigger durchbricht und die aktuelle Position flat oder short ist, eine Market Buy-Order senden, die groß genug ist, um jedes Short-Exposure umzukehren.
Wenn der Low-Preis den gespeicherten Short-Trigger durchbricht und die aktuelle Position flat oder long ist, eine Market Sell-Order senden, die groß genug ist, um jedes Long-Exposure umzukehren.
Jeder ausgeführte Trade löscht seinen entsprechenden Trigger. Entgegengesetzte Trigger schließen sich gegenseitig aus.
Parameter
Parameter
Standard
Beschreibung
Length
14
Periode des Vortex-Indikators. Entspricht dem ursprünglichen MQL-Eingang VI_Length.
CandleType
60-Minuten-Zeitrahmen
Kerzentyp für Indikatorberechnung und Trigger-Auswertung. Kann auf jeden von der verbundenen Datenquelle unterstützten Zeitrahmen angepasst werden.
Volume
Aus der Basis-Strategy-Eigenschaft
Handelsvolumen für Market Orders. Vor dem Start der Strategie konfigurieren, wenn ein anderer Wert als 1 Kontrakt/Lot erforderlich ist.
Wie Parameter das Verhalten beeinflussen
Eine Erhöhung von Length glättet die Vortex-Linien, reduziert die Anzahl der Crossover, verbessert aber deren Zuverlässigkeit.
Eine Verringerung von Length macht das System reaktiver und generiert mehr Trigger und potenzielle Trades.
Der CandleType sollte mit der Datengranularität in der ursprünglichen MQL-Einrichtung (typischerweise der Chart-Zeitrahmen) abgestimmt werden. Kürzere Kerzen liefern schnellere Signale, längere Kerzen konzentrieren sich auf breitere Trends.
Risikohinweise
Der ursprüngliche Expert Advisor definiert keine Stop-Loss- oder Take-Profit-Levels. Diese Konvertierung behält dieses Verhalten bei; das Risikomanagement muss extern oder durch Erweiterung der Strategie behandelt werden.
Die Positionsumkehr erfolgt sofort: Wenn ein entgegengesetztes Signal auftritt, gibt die Strategie ClosePosition() aus und wartet auf einen Ausbruch über den Trigger hinaus, bevor sie in die neue Richtung einsteigt.
Es kann jeweils nur ein Trigger (Long oder Short) aktiv sein. Trigger werden gelöscht, wenn der Preis sie durchbricht oder wenn ein entgegengesetzter Crossover auftritt.
Gebrauchsanweisung
Füge die Strategie zu deinem StockSharp-Projekt hinzu und stelle sicher, dass das Paket StockSharp.Algo.Indicators verfügbar ist.
Konfiguriere das gewünschte Wertpapier und den Konnektor in der Hostanwendung.
Setze den CandleType-Parameter auf den Zeitrahmen, den du handeln möchtest. Er sollte einem verfügbaren Kerzenabonnement für das ausgewählte Instrument entsprechen.
Optional passe Length und Volume vor dem Start der Strategie oder durch Optimierung an.
Starte die Strategie. Orders werden generiert, sobald der Indikator gebildet ist und Echtzeit-Daten verfügbar sind.
Implementierungshöhepunkte
Verwendet die High-Level SubscribeCandles-API mit Indikator-Binding (Bind) für saubere ereignisgesteuerte Verarbeitung.
Speichert die vorherigen Vortex-Werte, um Crossover genau wie die MQL-Implementierung zu erkennen (VI+ und VI--Vergleiche über zwei aufeinanderfolgende Kerzen).
Einstiegs-Trigger sind als nullable Decimal-Felder implementiert, um den ursprünglichen "Armen und Brechen"-Mechanismus nachzuahmen.
Englische Inline-Kommentare in der C#-Datei beschreiben jeden Entscheidungsschritt und helfen bei der Pflege des Codes.
Mögliche Erweiterungen
Stop-Loss- und Take-Profit-Regeln hinzufügen (z. B. ATR-basierte Ausstiege), wenn eine striktere Risikokontrolle erforderlich ist.
Eine Abkühlzeit oder maximale Haltezeit einführen, um längere Flat-Phasen zu vermeiden, wenn Trigger nicht ausgeführt werden.
Mit einem Volatilitätsfilter kombinieren, um nur dann zu handeln, wenn die Preisspannen breit genug sind, um Ausbruchsversuche zu rechtfertigen.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy based on the Vortex indicator crossover system.
/// Replicates the logic of the original MQL expert by arming entry triggers
/// on the candle where VI+ and VI- lines cross and executing when price breaks the trigger.
/// </summary>
public class VortexIndicatorSystemStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private VortexIndicator _vortex = null!;
private decimal _previousPlus;
private decimal _previousMinus;
private bool _hasPrevious;
private decimal? _pendingBuyTrigger;
private decimal? _pendingSellTrigger;
/// <summary>
/// Length of the Vortex indicator.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes parameters for the strategy.
/// </summary>
public VortexIndicatorSystemStrategy()
{
_length = Param(nameof(Length), 14)
.SetDisplay("Vortex Length", "Period for the Vortex indicator", "General")
.SetGreaterThanZero()
.SetOptimize(7, 28, 7);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for analysis", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousPlus = 0m;
_previousMinus = 0m;
_hasPrevious = false;
_pendingBuyTrigger = null;
_pendingSellTrigger = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_vortex = new VortexIndicator
{
Length = Length
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_vortex, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue vortexValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_vortex.IsFormed)
return;
if (vortexValue is not VortexIndicatorValue typed)
return;
var viPlusN = typed.PlusVi;
var viMinusN = typed.MinusVi;
if (viPlusN is not decimal viPlus || viMinusN is not decimal viMinus)
return;
if (_pendingBuyTrigger is decimal buyTrigger && candle.HighPrice > buyTrigger)
{
if (Position <= 0)
{
// Reverse existing short if present and open a new long position when price breaks the trigger.
BuyMarket();
}
_pendingBuyTrigger = null;
}
else if (_pendingSellTrigger is decimal sellTrigger && candle.LowPrice < sellTrigger)
{
if (Position >= 0)
{
// Reverse existing long if present and open a new short position when price breaks the trigger.
SellMarket();
}
_pendingSellTrigger = null;
}
if (!_hasPrevious)
{
_previousPlus = viPlus;
_previousMinus = viMinus;
_hasPrevious = true;
return;
}
var crossedUp = _previousPlus <= _previousMinus && viPlus > viMinus;
var crossedDown = _previousPlus >= _previousMinus && viPlus < viMinus;
if (crossedUp)
{
if (Position < 0)
{
// Flatten existing short positions when a bullish crossover appears.
BuyMarket();
}
_pendingBuyTrigger = candle.HighPrice;
_pendingSellTrigger = null;
}
else if (crossedDown)
{
if (Position > 0)
{
// Flatten existing long positions when a bearish crossover appears.
SellMarket();
}
_pendingSellTrigger = candle.LowPrice;
_pendingBuyTrigger = null;
}
_previousPlus = viPlus;
_previousMinus = viMinus;
}
}
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 VortexIndicator
class vortex_indicator_system_strategy(Strategy):
"""Vortex indicator crossover breakout: arms triggers on VI+/VI- cross, executes on price breakout."""
def __init__(self):
super(vortex_indicator_system_strategy, self).__init__()
self._length = self.Param("Length", 14) \
.SetGreaterThanZero() \
.SetDisplay("Vortex Length", "Period for the Vortex indicator", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used for analysis", "General")
self._previous_plus = 0.0
self._previous_minus = 0.0
self._has_previous = False
self._pending_buy_trigger = None
self._pending_sell_trigger = None
@property
def Length(self):
return int(self._length.Value)
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(vortex_indicator_system_strategy, self).OnStarted2(time)
self._previous_plus = 0.0
self._previous_minus = 0.0
self._has_previous = False
self._pending_buy_trigger = None
self._pending_sell_trigger = None
self._vortex = VortexIndicator()
self._vortex.Length = self.Length
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._vortex, self.process_candle).Start()
def process_candle(self, candle, vortex_value):
if candle.State != CandleStates.Finished:
return
if not self._vortex.IsFormed:
return
vi_plus_n = vortex_value.PlusVi
vi_minus_n = vortex_value.MinusVi
if vi_plus_n is None or vi_minus_n is None:
return
vi_plus = float(vi_plus_n)
vi_minus = float(vi_minus_n)
h = float(candle.HighPrice)
lo = float(candle.LowPrice)
# Check pending triggers
if self._pending_buy_trigger is not None and h > self._pending_buy_trigger:
if self.Position <= 0:
self.BuyMarket()
self._pending_buy_trigger = None
elif self._pending_sell_trigger is not None and lo < self._pending_sell_trigger:
if self.Position >= 0:
self.SellMarket()
self._pending_sell_trigger = None
if not self._has_previous:
self._previous_plus = vi_plus
self._previous_minus = vi_minus
self._has_previous = True
return
crossed_up = self._previous_plus <= self._previous_minus and vi_plus > vi_minus
crossed_down = self._previous_plus >= self._previous_minus and vi_plus < vi_minus
if crossed_up:
if self.Position < 0:
self.BuyMarket()
self._pending_buy_trigger = h
self._pending_sell_trigger = None
elif crossed_down:
if self.Position > 0:
self.SellMarket()
self._pending_sell_trigger = lo
self._pending_buy_trigger = None
self._previous_plus = vi_plus
self._previous_minus = vi_minus
def OnReseted(self):
super(vortex_indicator_system_strategy, self).OnReseted()
self._previous_plus = 0.0
self._previous_minus = 0.0
self._has_previous = False
self._pending_buy_trigger = None
self._pending_sell_trigger = None
def CreateClone(self):
return vortex_indicator_system_strategy()