Estratégia de rompimento que replica o especialista MetaTrader "Previous Candle Breakdown 2". O algoritmo observa a vela mais recentemente completada em um período configurável e aciona operações quando o preço perfura seu máximo ou mínimo por um deslocamento de pips definido pelo usuário. Filtragem opcional por médias móveis, horários de negociação estritos, dimensionamento de posição por volume fixo ou risco percentual e saídas de proteção em camadas replicam o comportamento da versão MQL original dentro do StockSharp.
Visão geral
Lógica de entrada: Entrar comprado quando o preço excede o máximo da vela anterior mais um recuo. Entrar vendido quando o preço rompe abaixo do mínimo da vela anterior menos o mesmo recuo.
Filtros: Médias móveis rápidas/lentas opcionais com parâmetros de deslocamento exigem confirmação direcional antes de negociar. A negociação também é limitada a uma janela de tempo de início/fim.
Dimensionamento de posição: Escolher entre um volume de ordem fixo ou dimensionamento dinâmico baseado no valor do portfólio e na distância do stop-loss.
Controles de risco: Níveis estáticos de stop-loss e take-profit em pips, trailing stop com filtro de passo e um objetivo de lucro global que fecha todas as posições.
Escala: O limite MaxPositions limita o tamanho absoluto da posição líquida para cada direção.
ProfitClose = 100 (unidades de moeda de PnL realizado + não realizado)
MaxPositions = 10 (contagem absoluta de contratos/lotes por lado)
OrderVolume = 0 (desabilitado), RiskPercent = 5 (usado quando OrderVolume é zero e o stop-loss está ativo)
StartTime = 09:09, EndTime = 19:19
CandleType = período de 4 horas
Regras de negociação
Inscrever-se na série de velas configurada e registrar cada vela finalizada.
Verificar se a hora atual está dentro da sessão de negociação permitida. Se ProfitClose for atingido, nivelar imediatamente.
Calcular os níveis de rompimento adicionando/subtraindo o recuo em pips do máximo e mínimo da vela anterior.
Quando o preço romper esses níveis e as condições de MA (se habilitadas) forem satisfeitas, abrir operações respeitando o limite MaxPositions.
Definir distâncias iniciais de stop-loss e take-profit a partir do preço de entrada e ativar trailing stops quando o preço tiver se movido a favor da operação pelo menos a distância de trailing mais o passo.
Monitorar continuamente as velas: acionar saídas de stop-loss/take-profit quando tocados, arrastar stops conforme o preço avança e redefinir níveis de proteção assim que as posições forem fechadas.
Notas
Os cálculos de pips se ajustam automaticamente para instrumentos de 3 ou 5 decimais para imitar a conversão de ponto para pip do MetaTrader.
Ao usar dimensionamento de risco percentual, o algoritmo estima o volume a partir do valor atual do portfólio e do stop-loss configurado.
A verificação de rompimento usa velas finalizadas, portanto picos dentro da barra são avaliados nos níveis de fechamento/máximo/mínimo da vela.
MaxPositions trabalha com a posição líquida da estratégia. Se volumes fracionários forem usados, o parâmetro representa o tamanho líquido absoluto máximo permitido por direção.
Os gráficos exibem velas, as médias móveis ativas quando habilitadas e as operações executadas para confirmação visual.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Previous candle breakdown strategy v2.
/// Trades breakout of previous candle high/low filtered by EMA crossover.
/// </summary>
public class PreviousCandleBreakdown2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _prevFast;
private decimal? _prevSlow;
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 PreviousCandleBreakdown2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = null;
_prevLow = null;
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = null; _prevLow = null; _prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, 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 fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevHigh == null || _prevLow == null || _prevFast == null || _prevSlow == null)
{
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
return;
}
var close = candle.ClosePrice;
var bullTrend = fast > slow;
var bearTrend = fast < slow;
if (bullTrend && close > _prevHigh.Value && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (bearTrend && close < _prevLow.Value && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
}
}
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 previous_candle_breakdown2_strategy(Strategy):
def __init__(self):
super(previous_candle_breakdown2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
@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
def OnReseted(self):
super(previous_candle_breakdown2_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(previous_candle_breakdown2_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
if self._prev_high is None or self._prev_low is None or self._prev_fast is None or self._prev_slow is None:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
bull_trend = fv > sv
bear_trend = fv < sv
if bull_trend and close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif bear_trend and close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return previous_candle_breakdown2_strategy()