A estratégia Zone Recovery Button é uma conversão direta do expert advisor do MetaTrader "ZONE RECOVERY BUTTON VER1" (MQL/25347). O robô original dependia de botões BUY/SELL no gráfico para iniciar uma cesta com hedge. Nesta versão StockSharp, o painel manual é substituído por parâmetros, enquanto a lógica de recuperação, take-profits monetários/percentuais, trailing stop em moeda e proteção equity-stop são preservados.
Quando a estratégia recebe uma direção inicial, ela abre uma ordem a mercado inicial. Sempre que o preço atravessa a largura de zona configurada, o sistema empilha uma operação oposta com volume aumentado. A cesta é fechada quando o take-profit de referência é atingido, o lucro flutuante alcança o alvo monetário/percentual configurado, o trailing stop devolve lucro demais ou o limite de equity-stop é violado.
Regras de negociação
Direção inicial - emula pressionar o botão BUY ou SELL. A estratégia abre a primeira ordem imediatamente quando recebe dados e tem permissão para negociar. Depois de fechar a cesta, ela pode reiniciar automaticamente com a mesma direção.
Recuperação por zona - em cada passo de recuperação, o algoritmo alterna a direção. Para ciclos comprados, ele vende quando o preço cai abaixo de Base Price - Zone Width, depois compra novamente quando o mercado retorna acima da base. Para ciclos vendidos, a lógica é espelhada.
Escalonamento de volume - cada hedge adicional multiplica o volume anterior ou adiciona um incremento fixo, reproduzindo as configurações "Lots"/"Multiply" do EA.
Controles de take-profit - a cesta é fechada por:
take-profit baseado em pips medido a partir do preço de referência;
alvo monetário na moeda da conta;
alvo percentual calculado a partir do valor atual do portfólio;
lógica trailing que trava ganhos quando o lucro flutuante excede um limite e depois devolve mais do que o drawdown permitido;
equity-stop emergencial que compara a perda flutuante atual com o maior equity observado durante o ciclo.
Parâmetros
Parâmetro
Padrão
Descrição
CandleType
TimeSpan.FromMinutes(5)
Tipo de candle usado para monitorar movimentos de preço.
StartDirection
Buy
Direção inicial do ciclo (BUY/SELL/NONE).
AutoRestart
true
Reinicia automaticamente um novo ciclo depois que a cesta anterior fecha.
TakeProfitPips
200
Distância em pips entre o preço base e o alvo de take-profit em pips.
ZoneRecoveryPips
10
Distância em pips que aciona o próximo hedge na direção oposta.
InitialVolume
0.01
Volume (lotes) da primeira operação.
UseVolumeMultiplier
true
Se habilitado, cada hedge multiplica o volume anterior; caso contrário, VolumeIncrement é adicionado.
VolumeMultiplier
2
Multiplicador aplicado quando UseVolumeMultiplier é true.
VolumeIncrement
0.01
Incremento de volume quando UseVolumeMultiplier é false.
MaxTrades
100
Número máximo de operações na cesta.
UseMoneyTakeProfit
false
Habilita fechamento quando o lucro flutuante excede MoneyTakeProfit.
MoneyTakeProfit
40
Alvo de lucro na moeda da conta.
UsePercentTakeProfit
false
Habilita fechamento quando o lucro flutuante excede PercentTakeProfit por cento do saldo.
PercentTakeProfit
10
Alvo de lucro em percentual do valor atual do portfólio.
EnableTrailing
true
Habilita trailing de lucro em moeda.
TrailingProfitThreshold
40
Nível de lucro que ativa trailing.
TrailingDrawdown
10
Drawdown permitido a partir do pico de lucro flutuante antes de fechar a cesta.
UseEquityStop
true
Habilita o equity stop emergencial.
TotalEquityRiskPercent
1
Perda flutuante máxima (em percentual do pico de equity) antes de zerar.
Observações
A estratégia funciona com qualquer instrumento que forneça valores PriceStep e StepPrice. Esses parâmetros são necessários para converter distâncias em pips para unidades de preço e moeda.
Como o StockSharp usa um modelo de posição líquida, a grade de hedge é simulada internamente. A estratégia mantém sua própria lista de passos de operação para reproduzir o cálculo de lucro do MetaTrader.
A lógica trailing opera sobre o lucro flutuante da cesta ativa. Ela não usa trailing stops baseados em ordens.
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()