A Estratégia ACB1 é a porta StockSharp do consultor especialista MetaTrader distribuída como MQL/8586/ACB1.MQ4. O sistema original negocia o par EURUSD e espera por fortes rompimentos diários antes de entrar no mercado. Esta conversão reproduz o mesmo processo de decisão com StockSharp primitivas de alto nível:
As velas diárias (SignalCandleType) definem a direção do rompimento e fornecem as âncoras de stop e take-profit.
Velas H4 (TrailCandleType) determinam a distância final que é multiplicada por TrailFactor.
As ordens são executadas no mercado quando as condições de rompimento são satisfeitas e a estratégia mantém apenas uma posição líquida, espelhando as verificações OrdersTotal() no código MQL.
Stop-loss e take-profit são gerenciados internamente: a estratégia observa os melhores preços de compra/venda e fecha a posição com ordens de mercado quando os níveis de proteção virtuais são violados.
Regras de negociação
Configuração longa
Use a vela diária finalizada anteriormente.
Se Close > (High + Low) / 2e o preço de venda atual estiver acima da máxima anterior, abra uma posição longa no mercado.
O stop loss é colocado no mínimo anterior (arredondado para a etapa do preço do instrumento).
O take-profit é igual ao preço de entrada mais (High − Low) × TakeFactor.
Configuração curta
Se Close < (High + Low) / 2e o preço de oferta atual estiver abaixo do mínimo anterior, abra uma posição curta no mercado.
Stop-loss é definido para a máxima anterior; o take-profit subtrai (High − Low) × TakeFactor do preço de entrada.
Parada final
Os TrailCandleType suprimentos de vela finalizados mais recentes (High − Low) × TrailFactor.
Para posições longas, o stop segue Bid − TrailDistance enquanto o preço permanece abaixo do take-profit menos o nível de stop do corretor.
Para posições curtas, o stop segue Ask + TrailDistance enquanto o preço permanece acima do take-profit mais o nível de stop do corretor.
Guarda de risco
A estratégia acompanha o patrimônio máximo observado do portfólio. A negociação é interrompida sempre que o patrimônio atual cai abaixo de 50% desse pico, exatamente como no consultor original.
Um resfriamento de cinco segundos (CooldownSeconds) evita novos pedidos ou interrompe atualizações com muita frequência, reproduzindo o acelerador TimeLocal() de MQL.
Dimensionamento de posição e controle de risco
O volume por negociação é derivado de Portfolio.CurrentValue × RiskFraction.
O risco monetário por contrato é calculado a partir da distância de parada e dos metadados de segurança (PriceStep e StepPrice).
O tamanho resultante é alinhado a Security.VolumeStep e fixado a [Security.MinVolume, Security.MaxVolume], então limitado pelo parâmetro MaxVolume (padrão 5 lotes).
Os pedidos são ignorados quando o volume normalizado é zero ou quando a distância de parada viola MinStopDistancePoints, que emula a verificação MetaTrader MODE_STOPLEVEL.
Parâmetros
Parâmetro
Padrão
Descrição
SignalCandleType
Diariamente
Tipo de vela usado para detecção de fuga.
TrailCandleType
4 horas
Tipo de vela que fornece a distância do trailing stop.
TakeFactor
0,8
Multiplicador aplicado ao intervalo diário para calcular o lucro.
TrailFactor
10
Multiplicador aplicado ao intervalo final ao atualizar o stop.
RiskFraction
0,05
Fração do patrimônio do portfólio arriscado em cada negociação (5%).
MaxVolume
5
Limite rígido para o volume final do pedido.
MinStopDistancePoints
0
Distância mínima de parada/tomada expressa em faixas de preço; configure-o para o corretor MODE_STOPLEVEL.
CooldownSeconds
5
Atraso mínimo entre ações comerciais consecutivas.
Notas de implementação
A estratégia requer metadados de instrumento adequados: Security.PriceStep, Security.StepPrice, Security.VolumeStep, Security.MinVolume e (se disponível) Security.MaxVolume.
Os níveis de proteção são virtuais. StockSharp fecha posições por meio de ordens de mercado quando a oferta/venda toca o stop-loss ou o take-profit calculado.
O acompanhamento de patrimônio usa Portfolio.CurrentValue. Caso o conector não forneça este campo o Risk Guard manterá a negociação desabilitada até que esteja disponível.
Apenas uma única posição líquida é mantida. Os sinais opostos enquanto uma negociação está ativa são ignorados até que a posição seja totalmente fechada.
Nenhuma porta Python está incluída; este diretório contém apenas a implementação e documentação do C#.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy converted from the "ACB1" MetaTrader expert advisor.
/// Enters on breakouts above previous candle high / below previous candle low,
/// with trailing stop based on ATR.
/// </summary>
public class Acb1Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _takeFactor;
private readonly StrategyParam<decimal> _trailFactor;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _prevClose;
private decimal _prevMid;
private decimal _entryPrice;
private decimal _stopPrice;
private bool _hasPrev;
public Acb1Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "Period for ATR indicator used in trailing.", "Indicators");
_takeFactor = Param(nameof(TakeFactor), 2m)
.SetDisplay("Take Factor", "ATR multiplier for take profit distance.", "Execution");
_trailFactor = Param(nameof(TrailFactor), 1.5m)
.SetDisplay("Trail Factor", "ATR multiplier for trailing stop distance.", "Execution");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public decimal TakeFactor
{
get => _takeFactor.Value;
set => _takeFactor.Value = value;
}
public decimal TrailFactor
{
get => _trailFactor.Value;
set => _trailFactor.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_prevClose = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0;
_prevLow = 0;
_prevClose = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
_hasPrev = false;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (atrValue <= 0)
return;
// Manage open position
if (Position != 0)
{
if (Position > 0)
{
// Trailing stop for long
var newStop = candle.ClosePrice - atrValue * TrailFactor;
if (newStop > _stopPrice)
_stopPrice = newStop;
// Check stop hit
if (candle.LowPrice <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Check take profit
else if (_entryPrice > 0 && candle.HighPrice >= _entryPrice + atrValue * TakeFactor)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
else
{
// Trailing stop for short
var newStop = candle.ClosePrice + atrValue * TrailFactor;
if (_stopPrice == 0 || newStop < _stopPrice)
_stopPrice = newStop;
// Check stop hit
if (candle.HighPrice >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Check take profit
else if (_entryPrice > 0 && candle.LowPrice <= _entryPrice - atrValue * TakeFactor)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
}
// Entry logic after managing position
if (_hasPrev && Position == 0)
{
if (_prevClose > _prevMid && candle.ClosePrice > _prevHigh)
{
// Bullish breakout
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _prevLow;
}
else if (_prevClose < _prevMid && candle.ClosePrice < _prevLow)
{
// Bearish breakout
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _prevHigh;
}
}
// Store for next candle
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevClose = candle.ClosePrice;
_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
_hasPrev = true;
}
}
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 CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class acb1_strategy(Strategy):
"""
Breakout strategy converted from the "ACB1" MetaTrader expert advisor.
Enters on breakouts above previous candle high / below previous candle low,
with trailing stop based on ATR.
"""
def __init__(self):
super(acb1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "Period for ATR indicator used in trailing.", "Indicators")
self._take_factor = self.Param("TakeFactor", 2.0) \
.SetDisplay("Take Factor", "ATR multiplier for take profit distance.", "Execution")
self._trail_factor = self.Param("TrailFactor", 1.5) \
.SetDisplay("Trail Factor", "ATR multiplier for trailing stop distance.", "Execution")
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def TakeFactor(self):
return self._take_factor.Value
@TakeFactor.setter
def TakeFactor(self, value):
self._take_factor.Value = value
@property
def TrailFactor(self):
return self._trail_factor.Value
@TrailFactor.setter
def TrailFactor(self, value):
self._trail_factor.Value = value
def OnReseted(self):
super(acb1_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(acb1_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
if atr_value <= 0:
return
# Manage open position
if self.Position != 0:
if self.Position > 0:
# Trailing stop for long
new_stop = float(candle.ClosePrice) - atr_value * self.TrailFactor
if new_stop > self._stop_price:
self._stop_price = new_stop
# Check stop hit
if float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Check take profit
elif self._entry_price > 0 and float(candle.HighPrice) >= self._entry_price + atr_value * self.TakeFactor:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
else:
# Trailing stop for short
new_stop = float(candle.ClosePrice) + atr_value * self.TrailFactor
if self._stop_price == 0 or new_stop < self._stop_price:
self._stop_price = new_stop
# Check stop hit
if float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Check take profit
elif self._entry_price > 0 and float(candle.LowPrice) <= self._entry_price - atr_value * self.TakeFactor:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Entry logic after managing position
if self._has_prev and self.Position == 0:
if self._prev_close > self._prev_mid and float(candle.ClosePrice) > self._prev_high:
# Bullish breakout
self.BuyMarket()
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._prev_low
elif self._prev_close < self._prev_mid and float(candle.ClosePrice) < self._prev_low:
# Bearish breakout
self.SellMarket()
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._prev_high
# Store for next candle
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_mid = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
self._has_prev = True
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return acb1_strategy()