Vortex-Oszillator-System-Strategie
Überblick
Das Vortex-Oszillator-System ist eine direkte Portierung des MetaTrader 5-Experten, der auf dem Vortex-Oszillator basiert, um starke Verschiebungen zwischen positiver und negativer Richtungsbewegung zu erfassen. Der Oszillator wird als Differenz zwischen der positiven Vortex-Linie (VI+) und der negativen Vortex-Linie (VI-) berechnet, die auf der ausgewählten Kerzenserie kalkuliert wird. Stark negative Werte zeigen an, dass VI- VI+ dominiert, während stark positive Werte die Führung von VI+ zeigen. Die Strategie interpretiert diese Extremwerte als potenzielle Wendepunkte und reagiert mit Mean-Reversion-artigen Einstiegen, unterstützt durch oszillatorgesteuerte Ausstiege.
Funktionsweise der Strategie
- Kerzen werden mit dem konfigurierten Zeitrahmen erstellt und dem integrierten
VortexIndicator zugeführt.
- Sobald der Indikator geformt ist, wird der Oszillatorwert als
VI+ - VI- für jede fertige Kerze abgeleitet.
- Der Oszillator wird mit benutzerdefinierten Schwellenwerten verglichen:
- Wenn er unter den Kaufschwellenwert fällt, wird ein Long-Setup erkannt.
- Wenn er über den Verkaufsschwellenwert steigt, wird ein Short-Setup erkannt.
- Optionale Filter können Long-Signale auf die Zone zwischen dem Kaufschwellenwert und einem dedizierten Stop-Loss-Level beschränken (und umgekehrt für Short-Signale).
- Wenn ein neues Setup erscheint, schließt die Strategie jede Gegenposition und eröffnet einen Trade in der Signalrichtung mit dem konfigurierten Volumen.
- Offene Positionen werden kontinuierlich überwacht. Wenn der Oszillator die konfigurierten Stop-Loss- oder Take-Profit-Grenzen erreicht, wird die Position sofort geschlossen.
Diese Sequenz reproduziert die ursprüngliche MetaTrader-Logik: Trades werden nur auf abgeschlossenen Bars bewertet, beide Richtungen schließen sich gegenseitig aus, und oszillatorbasierte Schutzregeln regeln die Ausstiege.
Einstiegskriterien
- Long-Einstieg
- Wird ausgelöst, wenn der Oszillator kleiner oder gleich dem Kaufschwellenwert ist.
- Wenn die Long-Stop-Loss-Option aktiviert ist, muss der Oszillator auch über dem Long-Stop-Loss-Level bleiben.
- Jede aktive Short-Position wird geschlossen, bevor der Long-Trade eröffnet wird.
- Short-Einstieg
- Wird ausgelöst, wenn der Oszillator größer oder gleich dem Verkaufsschwellenwert ist.
- Wenn die Short-Stop-Loss-Option aktiviert ist, muss der Oszillator auch unter dem Short-Stop-Loss-Level bleiben.
- Jede aktive Long-Position wird geschlossen, bevor der Short-Trade eröffnet wird.
- Wenn der Oszillatorwert zwischen den Kauf- und Verkaufsschwellenwerten liegt, werden alle Setups storniert und keine Positionsänderung vorgenommen.
Ausstiegskriterien
- Long-Positionen
- Sofortiges Schließen, wenn der Oszillator unter das Long-Stop-Loss-Level kreuzt oder es erreicht (wenn aktiviert).
- Sofortiges Schließen, wenn der Oszillator auf oder über das Long-Take-Profit-Level steigt (wenn aktiviert).
- Short-Positionen
- Sofortiges Schließen, wenn der Oszillator über das Short-Stop-Loss-Level kreuzt oder es erreicht (wenn aktiviert).
- Sofortiges Schließen, wenn der Oszillator auf oder unter das Short-Take-Profit-Level fällt (wenn aktiviert).
Die Ausstiegsprüfungen werden nach jedem Kerzenschluss durchgeführt, um eine getreue Nachbildung der MT5-Überwachungsschleife zu gewährleisten.
Parameter
- Vortex Length – Rückblickperiode für den Vortex-Indikator (Standard 14).
- Candle Type – Zeitrahmen für die Erstellung der dem Indikator zugeführten Kerzen.
- Use Buy Stop Loss – Aktiviert den oszillatorbasierten Stop-Loss-Filter und -Ausstieg für Long-Trades.
- Use Buy Take Profit – Aktiviert den oszillatorbasierten Take-Profit-Ausstieg für Long-Trades.
- Use Sell Stop Loss – Aktiviert den oszillatorbasierten Stop-Loss-Filter und -Ausstieg für Short-Trades.
- Use Sell Take Profit – Aktiviert den oszillatorbasierten Take-Profit-Ausstieg für Short-Trades.
- Buy Threshold – Oszillatorwert, der einen Long-Einstieg qualifiziert (Standard -0.75).
- Buy Stop Loss Level – Oszillatorwert, der Long-Positionen schließt, wenn die Long-Stop-Loss-Option aktiv ist (Standard -1.00).
- Buy Take Profit Level – Oszillatorwert, der Long-Positionen schließt, wenn die Long-Take-Profit-Option aktiv ist (Standard 0.00).
- Sell Threshold – Oszillatorwert, der einen Short-Einstieg qualifiziert (Standard 0.75).
- Sell Stop Loss Level – Oszillatorwert, der Short-Positionen schließt, wenn die Short-Stop-Loss-Option aktiv ist (Standard 1.00).
- Sell Take Profit Level – Oszillatorwert, der Short-Positionen schließt, wenn die Short-Take-Profit-Option aktiv ist (Standard 0.00).
- Volume – Handelsgröße für neue Positionen (Standard 0.1, entsprechend dem originalen Experten).
Implementierungshinweise
- Die Verarbeitung erfolgt ausschließlich auf abgeschlossenen Kerzen, um Signalduplizierungen innerhalb desselben Bars zu vermeiden.
- Oszillatorschwellenwerte können dank der in den Parameter-Metadaten bereitgestellten Bereiche optimiert werden.
- Die Strategie dreht Positionen automatisch um, indem sie eine Marktorder sendet, die groß genug ist, um die Gegenseite zu schließen und die neue Exponierung zu etablieren.
- Stop-Loss- und Take-Profit-Funktionen arbeiten unabhängig; die Aktivierung einer erfordert nicht die andere.
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>
/// Vortex oscillator trading system ported from the MetaTrader implementation.
/// Opens long positions when the oscillator drops below a configured level and
/// shorts when the oscillator rises above the upper threshold.
/// Optional stop-loss and take-profit rules monitor the oscillator value to exit positions.
/// </summary>
public class VortexOscillatorSystemStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _useBuyStopLoss;
private readonly StrategyParam<bool> _useBuyTakeProfit;
private readonly StrategyParam<bool> _useSellStopLoss;
private readonly StrategyParam<bool> _useSellTakeProfit;
private readonly StrategyParam<decimal> _buyThreshold;
private readonly StrategyParam<decimal> _buyStopLossLevel;
private readonly StrategyParam<decimal> _buyTakeProfitLevel;
private readonly StrategyParam<decimal> _sellThreshold;
private readonly StrategyParam<decimal> _sellStopLossLevel;
private readonly StrategyParam<decimal> _sellTakeProfitLevel;
private VortexIndicator _vortexIndicator = null!;
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public VortexOscillatorSystemStrategy()
{
_length = Param(nameof(Length), 14)
.SetGreaterThanZero()
.SetDisplay("Vortex Length", "Period used for the Vortex indicator.", "General")
.SetOptimize(7, 28, 7);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used to build candles for calculations.", "General");
_useBuyStopLoss = Param(nameof(UseBuyStopLoss), false)
.SetDisplay("Use Buy Stop Loss", "Enable oscillator-based stop loss for long positions.", "Risk Management");
_useBuyTakeProfit = Param(nameof(UseBuyTakeProfit), false)
.SetDisplay("Use Buy Take Profit", "Enable oscillator-based take profit for long positions.", "Risk Management");
_useSellStopLoss = Param(nameof(UseSellStopLoss), false)
.SetDisplay("Use Sell Stop Loss", "Enable oscillator-based stop loss for short positions.", "Risk Management");
_useSellTakeProfit = Param(nameof(UseSellTakeProfit), false)
.SetDisplay("Use Sell Take Profit", "Enable oscillator-based take profit for short positions.", "Risk Management");
_buyThreshold = Param(nameof(BuyThreshold), -0.1m)
.SetDisplay("Buy Threshold", "Oscillator value that triggers a long setup.", "Signals")
.SetOptimize(-1.5m, -0.25m, 0.25m);
_buyStopLossLevel = Param(nameof(BuyStopLossLevel), -1m)
.SetDisplay("Buy Stop Loss Level", "Oscillator value that closes long trades when stop loss is enabled.", "Signals");
_buyTakeProfitLevel = Param(nameof(BuyTakeProfitLevel), 0m)
.SetDisplay("Buy Take Profit Level", "Oscillator value that closes long trades when take profit is enabled.", "Signals");
_sellThreshold = Param(nameof(SellThreshold), 0.1m)
.SetDisplay("Sell Threshold", "Oscillator value that triggers a short setup.", "Signals")
.SetOptimize(0.25m, 1.5m, 0.25m);
_sellStopLossLevel = Param(nameof(SellStopLossLevel), 1m)
.SetDisplay("Sell Stop Loss Level", "Oscillator value that closes short trades when stop loss is enabled.", "Signals");
_sellTakeProfitLevel = Param(nameof(SellTakeProfitLevel), 0m)
.SetDisplay("Sell Take Profit Level", "Oscillator value that closes short trades when take profit is enabled.", "Signals");
Volume = 0.1m;
}
/// <summary>
/// Vortex indicator lookback length.
/// </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>
/// Enable oscillator-based stop loss for long positions.
/// </summary>
public bool UseBuyStopLoss
{
get => _useBuyStopLoss.Value;
set => _useBuyStopLoss.Value = value;
}
/// <summary>
/// Enable oscillator-based take profit for long positions.
/// </summary>
public bool UseBuyTakeProfit
{
get => _useBuyTakeProfit.Value;
set => _useBuyTakeProfit.Value = value;
}
/// <summary>
/// Enable oscillator-based stop loss for short positions.
/// </summary>
public bool UseSellStopLoss
{
get => _useSellStopLoss.Value;
set => _useSellStopLoss.Value = value;
}
/// <summary>
/// Enable oscillator-based take profit for short positions.
/// </summary>
public bool UseSellTakeProfit
{
get => _useSellTakeProfit.Value;
set => _useSellTakeProfit.Value = value;
}
/// <summary>
/// Oscillator level used to open long trades.
/// </summary>
public decimal BuyThreshold
{
get => _buyThreshold.Value;
set => _buyThreshold.Value = value;
}
/// <summary>
/// Oscillator level that closes long trades when stop loss is enabled.
/// </summary>
public decimal BuyStopLossLevel
{
get => _buyStopLossLevel.Value;
set => _buyStopLossLevel.Value = value;
}
/// <summary>
/// Oscillator level that closes long trades when take profit is enabled.
/// </summary>
public decimal BuyTakeProfitLevel
{
get => _buyTakeProfitLevel.Value;
set => _buyTakeProfitLevel.Value = value;
}
/// <summary>
/// Oscillator level used to open short trades.
/// </summary>
public decimal SellThreshold
{
get => _sellThreshold.Value;
set => _sellThreshold.Value = value;
}
/// <summary>
/// Oscillator level that closes short trades when stop loss is enabled.
/// </summary>
public decimal SellStopLossLevel
{
get => _sellStopLossLevel.Value;
set => _sellStopLossLevel.Value = value;
}
/// <summary>
/// Oscillator level that closes short trades when take profit is enabled.
/// </summary>
public decimal SellTakeProfitLevel
{
get => _sellTakeProfitLevel.Value;
set => _sellTakeProfitLevel.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_vortexIndicator = new VortexIndicator
{
Length = Length,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_vortexIndicator, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue vortexValue)
{
// Process only finished candles to mirror bar-close logic from the original script.
if (candle.State != CandleStates.Finished)
return;
if (!_vortexIndicator.IsFormed)
return;
var vortexTyped = (VortexIndicatorValue)vortexValue;
var viPlus = vortexTyped.PlusVi ?? 0m;
var viMinus = vortexTyped.MinusVi ?? 0m;
// Vortex oscillator equals the difference between VI+ and VI- lines.
var oscillator = viPlus - viMinus;
var longSetupExists = false;
var shortSetupExists = false;
// Long setups are considered when the oscillator falls below the buy threshold.
if (UseBuyStopLoss)
{
if (oscillator <= BuyThreshold && oscillator > BuyStopLossLevel)
{
longSetupExists = true;
shortSetupExists = false;
}
}
else if (oscillator <= BuyThreshold)
{
longSetupExists = true;
shortSetupExists = false;
}
// Short setups require the oscillator to rise above the sell threshold.
if (UseSellStopLoss)
{
if (oscillator >= SellThreshold && oscillator < SellStopLossLevel)
{
shortSetupExists = true;
longSetupExists = false;
}
}
else if (oscillator >= SellThreshold)
{
shortSetupExists = true;
longSetupExists = false;
}
// Neutral zone cancels both long and short intentions.
if (oscillator >= BuyThreshold && oscillator <= SellThreshold)
{
longSetupExists = false;
shortSetupExists = false;
}
var currentPosition = Position;
if (longSetupExists && currentPosition <= 0)
{
// Close existing shorts and open a long position when a valid long setup appears.
BuyMarket();
}
else if (shortSetupExists && currentPosition >= 0)
{
// Close existing longs and open a short position when a valid short setup appears.
SellMarket();
}
currentPosition = Position;
if (currentPosition > 0)
{
// Manage long positions with oscillator-based stops and targets.
if (UseBuyStopLoss && oscillator <= BuyStopLossLevel)
{
SellMarket();
return;
}
if (UseBuyTakeProfit && oscillator >= BuyTakeProfitLevel)
{
SellMarket();
return;
}
}
else if (currentPosition < 0)
{
// Manage short positions with oscillator-based stops and targets.
if (UseSellStopLoss && oscillator >= SellStopLossLevel)
{
BuyMarket();
return;
}
if (UseSellTakeProfit && oscillator <= SellTakeProfitLevel)
{
BuyMarket();
}
}
}
}
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, VortexIndicatorValue
class vortex_oscillator_system_strategy(Strategy):
"""Vortex oscillator system: trades when VI+-VI- crosses configurable thresholds."""
def __init__(self):
super(vortex_oscillator_system_strategy, self).__init__()
self._length = self.Param("Length", 14) \
.SetGreaterThanZero() \
.SetDisplay("Vortex Length", "Period used for the Vortex indicator", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used to build candles", "General")
self._use_buy_stop_loss = self.Param("UseBuyStopLoss", False) \
.SetDisplay("Use Buy Stop Loss", "Enable oscillator-based stop loss for longs", "Risk")
self._use_buy_take_profit = self.Param("UseBuyTakeProfit", False) \
.SetDisplay("Use Buy Take Profit", "Enable oscillator-based take profit for longs", "Risk")
self._use_sell_stop_loss = self.Param("UseSellStopLoss", False) \
.SetDisplay("Use Sell Stop Loss", "Enable oscillator-based stop loss for shorts", "Risk")
self._use_sell_take_profit = self.Param("UseSellTakeProfit", False) \
.SetDisplay("Use Sell Take Profit", "Enable oscillator-based take profit for shorts", "Risk")
self._buy_threshold = self.Param("BuyThreshold", -0.1) \
.SetDisplay("Buy Threshold", "Oscillator value that triggers a long setup", "Signals")
self._buy_stop_loss_level = self.Param("BuyStopLossLevel", -1.0) \
.SetDisplay("Buy Stop Loss Level", "Oscillator value that closes long trades", "Signals")
self._buy_take_profit_level = self.Param("BuyTakeProfitLevel", 0.0) \
.SetDisplay("Buy Take Profit Level", "Oscillator value that closes long trades", "Signals")
self._sell_threshold = self.Param("SellThreshold", 0.1) \
.SetDisplay("Sell Threshold", "Oscillator value that triggers a short setup", "Signals")
self._sell_stop_loss_level = self.Param("SellStopLossLevel", 1.0) \
.SetDisplay("Sell Stop Loss Level", "Oscillator value that closes short trades", "Signals")
self._sell_take_profit_level = self.Param("SellTakeProfitLevel", 0.0) \
.SetDisplay("Sell Take Profit Level", "Oscillator value that closes short trades", "Signals")
@property
def Length(self):
return int(self._length.Value)
@property
def CandleType(self):
return self._candle_type.Value
@property
def UseBuyStopLoss(self):
return self._use_buy_stop_loss.Value
@property
def UseBuyTakeProfit(self):
return self._use_buy_take_profit.Value
@property
def UseSellStopLoss(self):
return self._use_sell_stop_loss.Value
@property
def UseSellTakeProfit(self):
return self._use_sell_take_profit.Value
@property
def BuyThreshold(self):
return float(self._buy_threshold.Value)
@property
def BuyStopLossLevel(self):
return float(self._buy_stop_loss_level.Value)
@property
def BuyTakeProfitLevel(self):
return float(self._buy_take_profit_level.Value)
@property
def SellThreshold(self):
return float(self._sell_threshold.Value)
@property
def SellStopLossLevel(self):
return float(self._sell_stop_loss_level.Value)
@property
def SellTakeProfitLevel(self):
return float(self._sell_take_profit_level.Value)
def OnStarted2(self, time):
super(vortex_oscillator_system_strategy, self).OnStarted2(time)
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 = float(vortex_value.PlusVi) if vortex_value.PlusVi is not None else 0.0
vi_minus = float(vortex_value.MinusVi) if vortex_value.MinusVi is not None else 0.0
oscillator = vi_plus - vi_minus
long_setup = False
short_setup = False
if self.UseBuyStopLoss:
if oscillator <= self.BuyThreshold and oscillator > self.BuyStopLossLevel:
long_setup = True
short_setup = False
elif oscillator <= self.BuyThreshold:
long_setup = True
short_setup = False
if self.UseSellStopLoss:
if oscillator >= self.SellThreshold and oscillator < self.SellStopLossLevel:
short_setup = True
long_setup = False
elif oscillator >= self.SellThreshold:
short_setup = True
long_setup = False
if oscillator >= self.BuyThreshold and oscillator <= self.SellThreshold:
long_setup = False
short_setup = False
if long_setup and self.Position <= 0:
self.BuyMarket()
elif short_setup and self.Position >= 0:
self.SellMarket()
if self.Position > 0:
if self.UseBuyStopLoss and oscillator <= self.BuyStopLossLevel:
self.SellMarket()
return
if self.UseBuyTakeProfit and oscillator >= self.BuyTakeProfitLevel:
self.SellMarket()
return
elif self.Position < 0:
if self.UseSellStopLoss and oscillator >= self.SellStopLossLevel:
self.BuyMarket()
return
if self.UseSellTakeProfit and oscillator <= self.SellTakeProfitLevel:
self.BuyMarket()
def OnReseted(self):
super(vortex_oscillator_system_strategy, self).OnReseted()
def CreateClone(self):
return vortex_oscillator_system_strategy()