Diese Strategie ist eine C#-Konvertierung des MetaTrader-Experten Exp_i-KlPrice_Vol.mq5. Sie baut den KlPrice-Oszillator
neu auf, der den Abstand zwischen Preis und einem Volatilitätsband misst, multipliziert den Oszillator mit dem Kerzenvolumen
und verfolgt Farbübergänge, die durch adaptive Schwellenwerte erzeugt werden. Zwei unabhängige Positions-Slots werden für jede
Richtung emuliert, was das Dual-Magic-Verhalten des ursprünglichen Expert Advisors widerspiegelt.
Indikatorlogik
Preis wird mit dem ausgewählten AppliedPrice-Modus transformiert (Close, Open, Median, Demark usw.).
Der transformierte Preis wird durch die in PriceMaMethod und PriceMaLength definierte Methode des gleitenden Durchschnitts
geglättet.
Die Kerzenspanne (High - Low) wird mit RangeMaMethod/RangeMaLength geglättet. Die Spanne fungiert als dynamische
Bandbreite.
Der Basis-KlPrice-Oszillator wird als 100 * (Price - (MA - RangeMA)) / (2 * RangeMA) - 50 berechnet.
Der Oszillator wird mit der ausgewählten Volumensquelle (AppliedVolume.Tick oder AppliedVolume.Real) multipliziert.
Ein Jurik-Glätter der Länge SmoothingLength wird sowohl auf den Oszillator als auch auf das Rohvolumen angewendet und
erzeugt zwei adaptive Reihen.
Adaptive Schwellenwerte werden ermittelt, indem das geglättete Volumen mit HighLevel2, HighLevel1, LowLevel1 und
LowLevel2 multipliziert wird.
Die aktuelle Oszillatorfarbe wird durch den Vergleich des geglätteten Oszillatorwerts mit den adaptiven Schwellenwerten
bestimmt:
4 – über HighLevel2 * volume (extremer bullischer Druck).
3 – zwischen HighLevel1 * volume und dem Extremniveau.
2 – zwischen den bullischen und bärischen Schwellenwerten.
1 – zwischen dem unteren Schwellenwert und der Neutrallinie.
0 – unter LowLevel2 * volume (extremer bärischer Druck).
Handelsregeln
Die Farbe bei SignalBar (in der Regel die vorherige abgeschlossene Kerze) und die Farbe davor auswerten.
Long-Einträge:
Slot 1 öffnet, wenn die Farbe von 4 auf einen beliebigen Wert unter 4 wechselt und AllowLongEntrytrue ist.
Slot 2 öffnet, wenn die Farbe von 3 auf unter 3 wechselt.
Short-Einträge:
Slot 1 öffnet, wenn die Farbe von 0 auf über 0 steigt und AllowShortEntrytrue ist.
Slot 2 öffnet, wenn die Farbe von 1 auf über 1 steigt.
Long-Ausstiege erfolgen, wenn die frühere Farbe 0 oder 1 war und AllowLongExit aktiviert ist.
Short-Ausstiege erfolgen, wenn die frühere Farbe 4 oder 3 war und AllowShortExit aktiviert ist.
Jeder Slot verfolgt den Zeitpunkt des letzten Signals, um doppelte Orders auf derselben Kerze zu vermeiden. Schutz-Stops
sind optional und werden durch StartProtection gehandhabt, wenn StopLossPoints oder TakeProfitPoints größer als null
sind.
Parameter
Name
Typ
Standard
Beschreibung
PrimaryVolume
decimal
0.1
Volumen des ersten Long/Short-Slots.
SecondaryVolume
decimal
0.2
Volumen des zweiten Slots.
StopLossPoints
int
1000
Optionaler Schutz-Stop-Abstand in Preisschritten.
TakeProfitPoints
int
2000
Optionaler Take-Profit-Abstand in Preisschritten.
AllowLongEntry
bool
true
Öffnen von Long-Positionen aktivieren.
AllowShortEntry
bool
true
Öffnen von Short-Positionen aktivieren.
AllowLongExit
bool
true
Long-Positionen bei bärischen Farben schließen.
AllowShortExit
bool
true
Short-Positionen bei bullischen Farben schließen.
CandleType
DataType
H8
Kerzen-Zeitrahmen für Berechnungen.
PriceMaMethod
SmoothMethod
Sma
Typ des gleitenden Durchschnitts für den angewendeten Preis.
PriceMaLength
int
100
Länge des Preis-Glätters.
PriceMaPhase
int
15
Phasenparameter für Jurik-basierte Filter.
RangeMaMethod
SmoothMethod
Jjma
Typ des gleitenden Durchschnitts für die Kerzenspanne.
RangeMaLength
int
20
Länge des Spannen-Glätters.
RangeMaPhase
int
100
Phasenparameter für den Spannen-Glätter.
SmoothingLength
int
20
Jurik-Glättungslänge für Oszillator und Volumen.
AppliedPrice
AppliedPrice
Close
Preisquelle für Oszillatorberechnungen.
VolumeType
AppliedVolume
Tick
Volumensquelle, mit der der Oszillator multipliziert wird.
HighLevel2
int
150
Oberer Extremmultiplikator für den adaptiven Schwellenwert.
HighLevel1
int
20
Oberer moderater Multiplikator.
LowLevel1
int
-20
Unterer moderater Multiplikator.
LowLevel2
int
-150
Unterer Extremmultiplikator.
SignalBar
int
1
Historischer Offset für das Lesen von Farbübergängen.
Verwendungshinweise
Hängen Sie die Strategie an ein Wertpapier, das sowohl Preis- als auch Volumesinformationen liefert; wenn nur Tick-Volumen
verfügbar ist, wird der Tick-Zähler als Proxy verwendet.
Die beiden Slot-Volumina können unabhängig abgestimmt werden, um die duale Geldverwaltungseinrichtung des Original-EA zu
emulieren.
Passen Sie SignalBar an, wenn Sie mit teilweise geformten Kerzen arbeiten oder historische Daten resynchronisieren.
Die Glättungsmethoden unterstützen Jurik-Filter durch Reflexion, um das Verhalten der MQL SmoothAlgorithms-Bibliothek zu
replizieren.
Da StartProtection nur aufgerufen wird, wenn Stop- oder Target-Abstände positiv sind, lassen Sie sie auf null, um
Schutzaufträge zu deaktivieren.
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>
/// KlPrice-Vol strategy using EMA crossover with volume confirmation.
/// Buys on fast EMA crossing above slow EMA with rising volume.
/// Sells on reverse crossover.
/// </summary>
public class ExpIKlPriceVolStrategy : 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;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public ExpIKlPriceVolStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 200)
.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");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
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;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// EMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 60;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 60;
}
_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 exp_i_kl_price_vol_strategy(Strategy):
def __init__(self):
super(exp_i_kl_price_vol_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 50) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 200) \
.SetDisplay("Slow Period", "Slow EMA 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(exp_i_kl_price_vol_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(exp_i_kl_price_vol_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
# Check SL/TP
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 = 60
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 = 60
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 = 60
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 = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
# EMA crossover
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 = 60
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 = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_i_kl_price_vol_strategy()