Wave Power EA-Strategie
Die Wave Power EA-Strategie ist eine C#-Portierung des MQL4-Expertenberaters „Wave Power EA1“. Der Originalroboter baut eine Position ein
Richtung eines stochastischen oder MACD-Signals und fügt dann bei jeder festgelegten Anzahl von Pips zusätzliche Marktaufträge hinzu, während die angepasst wird
gemeinsame Take-Profit-Ebene. Die StockSharp-Version reproduziert dieses Verhalten mithilfe der High-Level-Strategie API, Indikatorbindung
und integrierte Ordnungshelfer. Alle Kommentare bleiben nach Bedarf auf Englisch.
Wie die Strategie funktioniert
Signalauswahl – der erste Trade wird nur eröffnet, wenn einer der Indikatorfilter eine Richtung generiert:
Stochastic – %K überschreitet %D innerhalb überverkaufter/überkaufter Regionen.
MacdSlope – Die Linie MACD steigt über oder unter ihren vorherigen Wert.
CciLevels – CCI fällt unter –120 oder steigt über +120.
AwesomeBreakout – Fantastischer Oszillator, der das adaptive historische Tief/Hoch durchbricht, das während der Initialisierung erfasst wurde.
RsiMa – schnelles SMA kreuzt langsames SMA, während RSI den Schwung bestätigt (über/unter 50).
SmaTrend – ein 15/20/25/50 SMA-Fächer, der in die gleiche Richtung zeigt, mit einem minimalen Neigungsunterschied.
Netzerweiterung – nachdem die erste Marktorder ausgeführt wurde, merkt sich die Strategie den Ausführungspreis. Immer wenn sich der Markt bewegt
um GridStepPips gegenüber der aktuellen Position und die maximale Orderanzahl wird nicht überschritten, übermittelt die Strategie einen neuen Markt
Reihenfolge in der gleichen Richtung. Jede neue Ebene multipliziert das Volumen mit dem Parameter Multiplier.
Gemeinsame Ziele – bei jeder neuen Order wird ein gemeinsamer Take-Profit- und (optional) Stop-Loss-Preis neu berechnet. Wenn die Anzahl der
Wenn sich aktive Bestellungen dem Schwellenwert von OrdersToProtect nähern, wird die Take-Profit-Distanz durch ReboundProfitPrimary ersetzt.
Nachdem der Schwellenwert überschritten wurde, wechselt die Distanz auf ReboundProfitSecondary, um eine schnellere Wiederherstellung zu fördern.
Korbüberwachung – bei jedem Kerzenschluss wandelt die Strategie den offenen Gewinn und Verlust in Pips pro Lot um. Wenn der Rebound-Gewinn bzw
Bei Erreichen der Verlustschutzschwellen wird der gesamte Warenkorb mittels Marktaufträgen liquidiert. Das gleiche passiert, wenn der Älteste
Handel ist älter als OrdersTimeAliveSeconds oder wenn der Handel am Freitag deaktiviert ist.
Lebenszyklus – sobald der Korb flach ist, werden alle internen Zähler zurückgesetzt, sodass das nächste Signal eine neue Mittelwertbildung starten kann
Zyklus.
Im Vergleich zum ursprünglichen EA vermeidet dieser Port absichtlich die Eröffnung entgegengesetzter (Absicherungs-)Positionen nach einer bestimmten Anzahl von Rastern
Schichten. Alle weiteren Einträge folgen der ursprünglichen Richtung. Der Rest der Geldverwaltungsregeln, Schutzlogik und
Indikatorfilter bleiben mit der Referenzimplementierung MQL4 kompatibel.
Parameter
| Parameter |
Beschreibung |
EntryLogic |
Indikatormodus, der für die allererste Bestellung verwendet wird. |
CandleType |
Zeitrahmen, der alle Indikatoren speist (Standard: 1 Stunde). |
InitialVolume |
Volumen der ersten Bestellung in Losen/Kontrakten. |
GridStepPips |
Minimaler Abstand in Pips zwischen Gitterschichten. |
MaxOrders |
Maximale Anzahl gleichzeitiger Bestellungen im Warenkorb. |
TakeProfitPips |
Gemeinsame Take-Profit-Distanz in Pips (0 deaktiviert das Ziel). |
StopLossPips |
Gemeinsamer Stop-Loss-Abstand in Pips (0 deaktiviert den Stop). |
Multiplier |
Auf jede weitere Bestellung wird ein Volumenmultiplikator angewendet. |
SecureProfitProtection |
Aktiviert die Rebound-Gewinnlogik. |
OrdersToProtect |
Anzahl der erforderlichen Bestellungen, bevor der Rebound-Schutz beginnt. |
ReboundProfitPrimary |
Gewinn pro Los (in Pips) für die erste Schutzstufe. |
ReboundProfitSecondary |
Gewinn pro Lot (in Pips), sobald die geschützte Orderanzahl überschritten wird. |
LossProtection |
Aktiviert den Floating-Loss-Schutz. |
LossThreshold |
Verlust pro Los (in Pips), der die Wache auslöst, wenn der Korb voll ist. |
ReverseCondition |
Kehrt Kauf-/Verkaufssignale um. |
TradeOnFriday |
Ermöglicht die Eröffnung neuer Bestellungen freitags. |
OrdersTimeAliveSeconds |
Maximale Lebensdauer der neuesten Bestellung in Sekunden (0 deaktiviert den Timer). |
TrendSlopeThreshold |
Minimale Steigungsdifferenz von SMA, die von der SmaTrend-Logik verwendet wird. |
Anwendungstipps
- Hängen Sie die Strategie an ein Wertpapier mit einem konfigurierten Preisschritt an, damit die Pip-Umrechnung korrekt funktioniert.
- Passen Sie
GridStepPips, Multiplier und MaxOrders entsprechend der Instrumentenvolatilität und der Broker-Margin-Richtlinie an.
- Aktivieren Sie die Schutzblöcke, wenn Sie auf einem Live-Konto laufen, um unkontrollierte Verluste bei längeren Trends zu verhindern.
- Die Strategie basiert auf geschlossenen Kerzen; Wählen Sie einen Zeitrahmen, der den gewünschten Handelsrhythmus widerspiegelt (das ursprüngliche EA verwendet M30
und H1-Kombinationen, aber die Standard-H1-Kerzen funktionieren gut).
- Da eine Absicherung nach der fünften Ebene nicht implementiert ist, sollten Sie eine Senkung von
MaxOrders in Betracht ziehen, wenn Sie das exakte Original benötigen
Verhalten.
Dateien
CS/WavePowerEAStrategy.cs – StockSharp-Implementierung der Wave Power EA-Gitterlogik.
README.md / README_ru.md / README_zh.md – Dokumentation in Englisch, Russisch und Chinesisch.
Die Python-Version wird gemäß den Aufgabenanforderungen absichtlich weggelassen.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Wave Power strategy using RSI + EMA crossover for entry
/// with grid-like averaging on drawdown.
/// </summary>
public class WavePowerEAStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _gridStepPercent;
private readonly StrategyParam<int> _maxGridOrders;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _gridCount;
public WavePowerEAStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 12)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI period.", "Indicators");
_gridStepPercent = Param(nameof(GridStepPercent), 0.5m)
.SetDisplay("Grid Step %", "Price move % to add to position.", "Grid");
_maxGridOrders = Param(nameof(MaxGridOrders), 5)
.SetDisplay("Max Grid Orders", "Maximum averaging orders.", "Grid");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public decimal GridStepPercent
{
get => _gridStepPercent.Value;
set => _gridStepPercent.Value = value;
}
public int MaxGridOrders
{
get => _maxGridOrders.Value;
set => _maxGridOrders.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_gridCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast == 0 || _prevSlow == 0)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
var bullishCross = _prevFast <= _prevSlow && fastVal > slowVal;
var bearishCross = _prevFast >= _prevSlow && fastVal < slowVal;
// Exit on opposite cross
if (Position > 0 && bearishCross)
{
SellMarket();
_gridCount = 0;
_entryPrice = 0;
}
else if (Position < 0 && bullishCross)
{
BuyMarket();
_gridCount = 0;
_entryPrice = 0;
}
// Grid averaging: add to position if price moved against us
if (Position > 0 && _entryPrice > 0 && _gridCount < MaxGridOrders)
{
var dropPercent = (_entryPrice - close) / _entryPrice * 100;
if (dropPercent >= GridStepPercent * (_gridCount + 1))
{
BuyMarket();
_gridCount++;
}
}
else if (Position < 0 && _entryPrice > 0 && _gridCount < MaxGridOrders)
{
var risePercent = (close - _entryPrice) / _entryPrice * 100;
if (risePercent >= GridStepPercent * (_gridCount + 1))
{
SellMarket();
_gridCount++;
}
}
// New entry
if (Position == 0)
{
if (bullishCross && rsiVal > 50)
{
_entryPrice = close;
_gridCount = 0;
BuyMarket();
}
else if (bearishCross && rsiVal < 50)
{
_entryPrice = close;
_gridCount = 0;
SellMarket();
}
}
_prevFast = fastVal;
_prevSlow = slowVal;
}
}
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 ExponentialMovingAverage, RelativeStrengthIndex
class wave_power_ea_strategy(Strategy):
def __init__(self):
super(wave_power_ea_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 12) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._grid_step_percent = self.Param("GridStepPercent", 0.5) \
.SetDisplay("Grid Step %", "Price move % to add to position", "Grid")
self._max_grid_orders = self.Param("MaxGridOrders", 5) \
.SetDisplay("Max Grid Orders", "Maximum averaging orders", "Grid")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._grid_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def GridStepPercent(self):
return self._grid_step_percent.Value
@property
def MaxGridOrders(self):
return self._max_grid_orders.Value
def OnStarted2(self, time):
super(wave_power_ea_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._grid_count = 0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastPeriod
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowPeriod
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_val)
if self._prev_fast == 0 or self._prev_slow == 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
bullish_cross = self._prev_fast <= self._prev_slow and fv > sv
bearish_cross = self._prev_fast >= self._prev_slow and fv < sv
# Exit on opposite cross
if self.Position > 0 and bearish_cross:
self.SellMarket()
self._grid_count = 0
self._entry_price = 0.0
elif self.Position < 0 and bullish_cross:
self.BuyMarket()
self._grid_count = 0
self._entry_price = 0.0
# Grid averaging: add to position if price moved against us
grid_step = float(self.GridStepPercent)
max_grid = self.MaxGridOrders
if self.Position > 0 and self._entry_price > 0 and self._grid_count < max_grid:
drop_percent = (self._entry_price - close) / self._entry_price * 100.0
if drop_percent >= grid_step * (self._grid_count + 1):
self.BuyMarket()
self._grid_count += 1
elif self.Position < 0 and self._entry_price > 0 and self._grid_count < max_grid:
rise_percent = (close - self._entry_price) / self._entry_price * 100.0
if rise_percent >= grid_step * (self._grid_count + 1):
self.SellMarket()
self._grid_count += 1
# New entry
if self.Position == 0:
if bullish_cross and rv > 50:
self._entry_price = close
self._grid_count = 0
self.BuyMarket()
elif bearish_cross and rv < 50:
self._entry_price = close
self._grid_count = 0
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(wave_power_ea_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._grid_count = 0
def CreateClone(self):
return wave_power_ea_strategy()