Die Zone-Recovery-Button-Strategie ist eine direkte Umwandlung des MetaTrader Expert Advisors "ZONE RECOVERY BUTTON VER1" (MQL/25347). Der ursprüngliche Roboter stützte sich auf BUY/SELL-Schaltflächen im Chart, um einen gehedgten Basket zu starten. In dieser StockSharp-Portierung wird das manuelle Panel durch Parameter ersetzt, während Recovery-Logik, Money-/Prozent-Take-Profits, Trailing Stop in Währung und Equity-Stop-Schutz erhalten bleiben.
Sobald die Strategie eine Startrichtung erhält, eröffnet sie eine initiale Marktorder. Immer wenn der Preis die konfigurierte Zonenbreite durchläuft, stapelt das System einen Gegentrade mit erhöhtem Volumen. Der Basket wird geschlossen, wenn der Referenz-Take-Profit erreicht ist, der schwebende Gewinn das konfigurierte Geld-/Prozentziel erreicht, der Trailing Stop zu viel Gewinn zurückgibt oder die Equity-Stop-Schwelle verletzt wird.
Handelsregeln
Startrichtung - emuliert das Drücken der BUY- oder SELL-Schaltfläche. Die Strategie eröffnet die erste Order sofort, sobald sie Daten erhält und handeln darf. Nach dem Schließen des Baskets kann sie automatisch mit derselben Richtung neu starten.
Zone Recovery - bei jedem Recovery-Schritt wechselt der Algorithmus die Richtung. Für Long-Zyklen verkauft er, sobald der Preis unter Base Price - Zone Width fällt, und kauft dann wieder, wenn der Markt über die Basis zurückkehrt. Für Short-Zyklen ist die Logik gespiegelt.
Volumenskalierung - jeder zusätzliche Hedge multipliziert entweder das vorherige Volumen oder fügt einen festen Zuwachs hinzu und reproduziert damit die "Lots"/"Multiply"-Einstellungen des EA.
Take-Profit-Steuerungen - der Basket wird geschlossen durch:
pipbasierten Take-Profit, gemessen vom Referenzpreis;
Geldziel in Kontowährung;
Prozentziel, berechnet aus dem aktuellen Portfoliowert;
Trailing-Logik, die Gewinne sperrt, sobald der schwebende Gewinn eine Schwelle überschreitet und danach mehr als den erlaubten Drawdown zurückgibt;
Notfall-Equity-Stop, der den aktuellen schwebenden Verlust mit der höchsten während des Zyklus beobachteten Equity vergleicht.
Parameter
Parameter
Standard
Beschreibung
CandleType
TimeSpan.FromMinutes(5)
Kerzentyp zur Überwachung von Preisbewegungen.
StartDirection
Buy
Anfängliche Zyklusrichtung (BUY/SELL/NONE).
AutoRestart
true
Startet automatisch einen neuen Zyklus, nachdem der vorherige Basket geschlossen wurde.
TakeProfitPips
200
Pip-Distanz zwischen Basispreis und pipbasiertem Take-Profit-Ziel.
ZoneRecoveryPips
10
Pip-Distanz, die den nächsten Hedge in Gegenrichtung auslöst.
InitialVolume
0.01
Volumen (Lots) des ersten Trades.
UseVolumeMultiplier
true
Wenn aktiviert, multipliziert jeder Hedge das vorherige Volumen; andernfalls wird VolumeIncrement addiert.
VolumeMultiplier
2
Multiplikator, der angewendet wird, wenn UseVolumeMultipliertrue ist.
VolumeIncrement
0.01
Volumenzuwachs, wenn UseVolumeMultiplierfalse ist.
MaxTrades
100
Maximale Anzahl von Trades im Basket.
UseMoneyTakeProfit
false
Aktiviert das Schließen, wenn schwebender Gewinn MoneyTakeProfit überschreitet.
MoneyTakeProfit
40
Gewinnziel in Kontowährung.
UsePercentTakeProfit
false
Aktiviert das Schließen, wenn schwebender Gewinn PercentTakeProfit Prozent des Saldos überschreitet.
PercentTakeProfit
10
Gewinnziel in Prozent des aktuellen Portfoliowerts.
EnableTrailing
true
Aktiviert Gewinn-Trailing in Währung.
TrailingProfitThreshold
40
Gewinnniveau, das Trailing aktiviert.
TrailingDrawdown
10
Zulässiger Drawdown vom höchsten schwebenden Gewinn vor dem Schließen des Baskets.
UseEquityStop
true
Aktiviert den Notfall-Equity-Stop.
TotalEquityRiskPercent
1
Maximaler schwebender Verlust (in Prozent des Equity-Hochs) vor dem Glattstellen.
Hinweise
Die Strategie arbeitet mit jedem Instrument, das PriceStep- und StepPrice-Werte bereitstellt. Diese Parameter werden benötigt, um Pip-Distanzen in Preis- und Währungseinheiten umzuwandeln.
Da StockSharp ein Nettopositionsmodell verwendet, wird das Hedging-Grid intern simuliert. Die Strategie führt eine eigene Liste von Tradeschritten, um die MetaTrader-Gewinnberechnung zu reproduzieren.
Die Trailing-Logik arbeitet auf dem schwebenden Gewinn des aktiven Baskets. Sie verwendet keine orderbasierten Trailing Stops.
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;
public class ZoneRecoveryButtonStrategy : 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 ZoneRecoveryButtonStrategy()
{
_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 zone_recovery_button_strategy(Strategy):
def __init__(self):
super(zone_recovery_button_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(zone_recovery_button_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(zone_recovery_button_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 zone_recovery_button_strategy()