Konvertiert vom MetaTrader 5-Experten Exp_VortexIndicator_MMRec_Duplex.mq5 (MQL ID 23180).
Pflegt zwei unabhängige Vortex-Indikatorströme: einen für Long-Trades und einen für Short-Trades. Jeder Strom hat seinen eigenen Zeitrahmen, Länge und Balkenverschiebung, sodass die bullische und bärische Logik separat optimiert werden kann.
Repliziert das "MMRec" Geldverwaltungs-Recovery-Modul des originalen EA. Die Strategie verfolgt die letzten Handelsergebnisse pro Richtung und wechselt vorübergehend zu einer reduzierten Ordergröße nach einer konfigurierbaren Anzahl von Verlusten.
Signallogik
Den konfigurierten Kerzentyp für jeden Strom abonnieren und den Vortex-Indikator (VI+ und VI-) berechnen.
Long-Einstiege: wenn die vorherige Kerze VI+ unterhalb oder gleich VI- hatte und die aktuelle Kerze mit VI+ oberhalb von VI- schließt (bullisches Crossover). Einstiege sind nur erlaubt, wenn AllowLongEntries aktiviert ist.
Long-Ausstiege: wenn VI- auf der bewerteten Kerze über VI+ steigt, sofern AllowLongExits aktiviert ist.
Short-Einstiege: wenn die vorherige Kerze VI+ oberhalb oder gleich VI- hatte und die aktuelle Kerze mit VI+ unterhalb von VI- schließt (bärisches Crossover), gesteuert durch AllowShortEntries.
Short-Ausstiege: wenn VI+ auf der bewerteten Kerze wieder über VI- klettert, gesteuert durch AllowShortExits.
Jede Richtung behält ihre eigenen Stop-Loss- und Take-Profit-Niveaus, gemessen in Preisschritten. Das Erreichen eines der beiden Niveaus schließt die Position sofort und registriert das Ergebnis für die Recovery-Zähler.
Geldverwaltungs-Recovery
Der originale EA prüft ein gleitendes Fenster vergangener Trades, um zu entscheiden, ob die nächste Order das normale oder reduzierte Volumen verwenden soll. Dieser Port spiegelt dasselbe Verhalten wider.
Für Long-Trades speichert die Warteschlange bis zu LongTotalTrigger neueste PnL-Ergebnisse. Wenn mindestens LongLossTrigger davon Verlust-Trades sind, verwendet der nächste Long-Einstieg LongSmallMoneyManagement; andernfalls wird LongMoneyManagement verwendet.
Short-Trades wiederholen dieselbe Logik mit ShortTotalTrigger, ShortLossTrigger, ShortSmallMoneyManagement und ShortMoneyManagement.
Wenn die Triggerwerte null sind, werden die Warteschlangen geleert und das Basisvolumen wird immer verwendet.
Margin-Modi
MarginModeOption beschreibt, wie der Geldverwaltungswert in ein ausführbares Volumen umgewandelt wird:
FreeMargin (0): den Wert als Kapitalbrucheil behandeln (Annäherung an den originalen "freie Margin"-Modus).
Balance (1): identisch mit FreeMargin in diesem Port; verwendet den aktuellen Portfolio-Wert.
LossFreeMargin (2): einen Kapitalbrucheil riskieren unter Verwendung der konfigurierten Stop-Loss-Distanz. Fällt auf preisbasierte Größenbestimmung zurück, wenn die Stop-Distanz null ist.
LossBalance (3): gleich wie LossFreeMargin in dieser Implementierung.
Lot (4): den Wert direkt als Ordervolumen interpretieren.
Alle berechneten Größen werden mithilfe des Volumen-Schritts des Instruments sowie der Mindest- und Höchstvolumenbeschränkungen normalisiert.
Parameter
Parameter
Standard
Beschreibung
LongCandleType
H4
Zeitrahmen für den Long-seitigen Vortex-Indikator.
ShortCandleType
H4
Zeitrahmen für den Short-seitigen Vortex-Indikator.
LongLength
14
Periode des Vortex-Indikators für Long-Signale.
ShortLength
14
Periode des Vortex-Indikators für Short-Signale.
LongSignalBar
1
Geschlossener Balken-Offset für Long-Crossovers (0 = letzter geschlossener Balken).
ShortSignalBar
1
Geschlossener Balken-Offset für Short-Crossovers.
AllowLongEntries
true
Long-Einstiege beim bullischen Crossover aktivieren.
AllowLongExits
true
Schließen von Long-Positionen aktivieren, wenn VI- über VI+ dominiert.
AllowShortEntries
true
Short-Einstiege beim bärischen Crossover aktivieren.
AllowShortExits
true
Schließen von Short-Positionen aktivieren, wenn VI+ über VI- dominiert.
LongTotalTrigger
5
Anzahl der letzten Long-Trades, die vom Recovery-Zähler geprüft werden.
LongLossTrigger
3
Verlierende Long-Trades, die vor dem Wechsel zum reduzierten Long-Volumen erforderlich sind.
LongMoneyManagement
0.1
Basis-Geldverwaltungswert für Long-Trades.
LongSmallMoneyManagement
0.01
Reduzierter Geldverwaltungswert nach einer Long-Verlustserie.
LongMarginMode
Lot
Interpretation des Long-Geldverwaltungswerts (siehe Modi oben).
LongStopLossSteps
1000
Schutzabstand unterhalb des Long-Einstiegs in Preisschritten.
LongTakeProfitSteps
2000
Take-Profit-Abstand oberhalb des Long-Einstiegs in Preisschritten.
LongSlippageSteps
10
Informationsslippage-Toleranz für Long-Orders (nicht für Größenbestimmung verwendet).
ShortTotalTrigger
5
Anzahl der letzten Short-Trades, die vom Recovery-Zähler geprüft werden.
ShortLossTrigger
3
Verlierende Short-Trades, die vor dem Wechsel zum reduzierten Short-Volumen erforderlich sind.
ShortMoneyManagement
0.1
Basis-Geldverwaltungswert für Short-Trades.
ShortSmallMoneyManagement
0.01
Reduzierter Geldverwaltungswert nach einer Short-Verlustserie.
ShortMarginMode
Lot
Interpretation des Short-Geldverwaltungswerts.
ShortStopLossSteps
1000
Schutzabstand oberhalb des Short-Einstiegs in Preisschritten.
ShortTakeProfitSteps
2000
Take-Profit-Abstand unterhalb des Short-Einstiegs in Preisschritten.
ShortSlippageSteps
10
Informationsslippage-Toleranz für Short-Orders.
Implementierungshinweise
Vollständig auf der StockSharp High-Level-API aufgebaut. Kerzenabonnements treiben die Vortex-Indikatoren über Bind, das fertige Balken liefert, bevor eine Entscheidung getroffen wird.
Die Trade-Recovery-Logik speichert richtungsbezogene Gewinnreihen in Warteschlangen und spiegelt die MetaTrader BuyTradeMMRecounterS / SellTradeMMRecounterS-Funktionen wider.
Stop-Loss- und Take-Profit-Niveaus werden in Preiseinheiten (Instrument-Preisschritt × konfigurierte Schritte) neu berechnet und bei jeder eingehenden Kerze durchgesetzt.
Ordervolumina werden über die VolumeStep-, MinVolume- und MaxVolume-Beschränkungen des Instruments normalisiert, um ungültige Einreichungen zu vermeiden.
Slippage-Parameter werden zu Dokumentationszwecken beibehalten, werden aber nicht direkt von den StockSharp-Order-Handlern verwendet.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Vortex Indicator MMRec Duplex strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class VortexIndicatorMmrecDuplexStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public VortexIndicatorMmrecDuplexStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class vortex_indicator_mmrec_duplex_strategy(Strategy):
def __init__(self):
super(vortex_indicator_mmrec_duplex_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(vortex_indicator_mmrec_duplex_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(vortex_indicator_mmrec_duplex_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return vortex_indicator_mmrec_duplex_strategy()