Anstelle des proprietären VQ-Indikators wendet die Strategie einen einfachen gleitenden Durchschnitt auf den Medianpreis an und glättet ihn optional noch einmal.
Die Steigung der geglätteten Reihe spielt die Rolle der ursprünglichen Farbänderung der VQ-Linie.
Ein konfigurierbarer, in Punkten ausgedrückter Filter verhindert Signale, die durch geringfügige Schwankungen verursacht werden.
Marktaufträge werden für Ein- und Ausstiege verwendet und spiegeln das ursprüngliche EA-Verhalten wider.
Signalerzeugung
Abonnieren Sie den ausgewählten Kerzentyp und berechnen Sie den Medianpreis für jede fertige Kerze.
Wenden Sie den gleitenden Basisdurchschnitt (Length) und, falls gewünscht, eine zusätzliche Glättung (Smoothing) an.
Vergleichen Sie den aktuellen geglätteten Wert mit dem vorherigen. Wenn die absolute Änderung FilterPoints (umgerechnet in Preiseinheiten) überschreitet, markieren Sie die Richtung als steigend oder fallend.
Wenn die Richtung von unten nach oben wechselt, wird ein langer Eintrag ausgegeben. Ein Flip von oben nach unten führt zu einem kurzen Einstieg. Bestehende Positionen werden durch Addition des absoluten Positionsvolumens zur Ordergröße rückgängig gemacht.
Risikomanagement
StopLossPoints, TakeProfitPoints und TrailingStopPoints werden durch Multiplikation mit der Instrumentenpreisstufe in absolute Preise umgerechnet.
Wenn mindestens einer dieser Schutzmaßnahmen aktiviert ist, wird StartProtection mit Market-Order-Anpassungen aufgerufen, sodass Stopps der Position folgen, wie im MQL-Experten.
Der optionale Trailing Stop wird nur aktiviert, wenn UseTrailing den Wert true hat und die Trailing-Distanz größer als Null ist.
Parameter
Length – Basis-Glättungszeitraum des Medianpreises. Standard: 5.
TakeProfitPoints – schützender Take-Profit in Punkten. Standard: 0 (deaktiviert).
UseTrailing – Trailing Stops aktivieren oder deaktivieren. Default: false.
TrailingStopPoints – Nachlaufdistanz in Punkten. Standard: 0 (wird ignoriert, wenn UseTrailing falsch ist).
CandleType – für Berechnungen verwendeter Zeitrahmen. Standard: 1-Stunden-Kerzen.
Volume – geerbt von Strategy.Volume, standardmäßig 1 Vertrag und wird für jeden neuen Eintrag verwendet.
Unterschiede zum ursprünglichen Experten
Die genauen VQ-Pufferwerte werden durch geglättete Medianpreise angenähert; Der Indikator ist nicht eins zu eins portiert.
Erweiterte Funktionen wie Break-Even-Schichten, Planung von Warntönen, manuelle Protokollausgabe und Teilmengen-Geldverwaltung werden nicht reproduziert.
Die Handhabung von Trailing-Steps wird durch den integrierten Trailing-Stop-Manager von StockSharp vereinfacht.
Nutzungshinweise
Signale werden nur bei fertigen Kerzen generiert und entsprechen dem Modus „Handel bei Bar-Schluss“ des ursprünglichen EA.
Stellen Sie sicher, dass das Instrument über einen ordnungsgemäßen PriceStep verfügt. Andernfalls greift die Strategie bei der Konvertierung punktbasierter Parameter auf einen Schritt von 1,0 zurück.
Die Strategie ist zu Demonstrationszwecken gedacht und kann bei Bedarf um weitere Money-Management-Regeln erweitert werden.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class VqEaStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMom; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VqEaStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA filter", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var mom = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, mom, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal mom)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevMom = mom; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevMom = mom;
return;
}
if (close > ema && _prevMom <= 0 && mom > 0 && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 2;
}
else if (close < ema && _prevMom >= 0 && mom < 0 && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 2;
}
_prevMom = mom;
}
}
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, Momentum
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class vq_ea_strategy(Strategy):
"""EMA trend filter with Momentum zero-cross for entries and cooldown."""
def __init__(self):
super(vq_ea_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA filter", "Indicators")
self._mom_period = self.Param("MomentumPeriod", 14).SetDisplay("Momentum", "Momentum period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(vq_ea_strategy, self).OnReseted()
self._prev_mom = 0
self._has_prev = False
self._cooldown = 0
def OnStarted2(self, time):
super(vq_ea_strategy, self).OnStarted2(time)
self._prev_mom = 0
self._has_prev = False
self._cooldown = 0
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
mom = Momentum()
mom.Length = self._mom_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(ema, mom, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val, mom_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ev = float(ema_val)
mv = float(mom_val)
close = float(candle.ClosePrice)
if not self._has_prev:
self._prev_mom = mv
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_mom = mv
return
if close > ev and self._prev_mom <= 0 and mv > 0 and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume)
self._cooldown = 2
elif close < ev and self._prev_mom >= 0 and mv < 0 and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume)
self._cooldown = 2
self._prev_mom = mv
def CreateClone(self):
return vq_ea_strategy()