Esta estratégia reproduz o consultor especialista MetaTrader 4 "800BB" usando a API de alto nível do StockSharp. Ela entra em trades de reversão à média quando o preço perfura uma Bollinger Band muito longa e imediatamente volta a entrar no canal na próxima barra. O risco é controlado por meio de distâncias de stop e take-profit baseadas em ATR combinadas com dimensionamento dinâmico de posição baseado no percentual de risco configurado.
Visão geral
Funciona em qualquer instrumento e período fornecido através do parâmetro CandleType.
Usa uma Bollinger Band de 800 períodos com um envelope de dois desvios padrão para detectar excursões extremas.
Confirma entradas na barra que abre de volta dentro da banda logo após um fechamento externo.
Dimensiona ordens estimando a distância de stop derivada do ATR em pips e aplicando o RiskPercent selecionado ao valor atual do portfólio.
Replica o cálculo de pips do MetaTrader multiplicando o passo de preço por 10 quando o símbolo tem 3 ou 5 casas decimais.
Lógica de trading
Configuração longa
O candle completado anterior abriu ou fechou abaixo da banda inferior de Bollinger, sinalizando uma excursão de sobrevendido.
O candle atual abre em ou acima do nível anterior da banda inferior (o preço voltou a entrar no canal).
Nenhuma posição longa está atualmente ativa. Qualquer posição curta aberta é fechada antes de abrir a nova posição longa.
O tamanho da posição é calculado usando a distância de stop baseada em ATR e o percentual de risco configurado.
Uma ordem de compra a mercado é enviada na abertura do candle. O stop-loss é colocado StopLossAtrMultiplier × ATR abaixo da entrada, enquanto o take-profit é TakeProfitAtrMultiplier × ATR acima da entrada.
Configuração curta
O candle completado anterior abriu ou fechou acima da banda superior de Bollinger, sinalizando uma excursão de sobrecomprado.
O candle atual abre em ou abaixo do nível anterior da banda superior (o preço voltou a entrar no canal).
Nenhuma posição curta está atualmente ativa. Qualquer posição longa aberta é fechada antes de abrir a nova posição curta.
O tamanho da posição é determinado pelo mesmo cálculo de ATR e percentual de risco.
Uma ordem de venda a mercado é enviada na abertura do candle. O stop-loss é colocado StopLossAtrMultiplier × ATR acima da entrada, enquanto o take-profit é TakeProfitAtrMultiplier × ATR abaixo da entrada.
Gestão de saída
Ordens protetoras: Os níveis de stop-loss e take-profit são rastreados internamente e avaliados em cada candle completado. Se qualquer limiar for ultrapassado, a posição é fechada a mercado.
Sinais opostos: Quando uma configuração oposta é acionada, a posição atual é zerada antes de a nova ordem ser colocada.
Visualização: O EA original podia desenhar linhas verticais para trades potenciais. As anotações de gráfico não são recriadas aqui; em vez disso, a estratégia desenha candles, a Bollinger Band e os próprios trades quando uma área de gráfico está disponível.
Parâmetros
Parâmetro
Padrão
Descrição
RiskPercent
2
Percentual do valor do portfólio arriscado por trade.
TakeProfitAtrMultiplier
1.5
Múltiplo do ATR usado para calcular a distância do take-profit.
StopLossAtrMultiplier
1
Múltiplo do ATR usado para calcular a distância do stop-loss.
AtrPeriod
14
Período de retrospectiva para o indicador ATR.
BollingerPeriod
800
Período da média móvel da Bollinger Band.
BollingerDeviation
2
Multiplicador de desvio padrão para a Bollinger Band.
CandleType
1 hour
Período (ou qualquer outro tipo de candle) usado para geração de sinais.
Notas
Certifique-se de que o adaptador de portfólio fornece Portfolio.CurrentValue; caso contrário, o dimensionamento de posição baseado em risco retorna zero e a estratégia não irá operar.
Se o símbolo não expõe um passo de preço ou valor de tick válido, os cálculos de pips e dinheiro por pip recorrem a padrões conservadores.
O longo período de retrospectiva do Bollinger (800 barras) significa que o primeiro trade só pode ocorrer após receber dados históricos suficientes para aquecer tanto os indicadores Bollinger quanto ATR.
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 EightHundredBbStrategy : 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 EightHundredBbStrategy()
{
_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 eight_hundred_bb_strategy(Strategy):
def __init__(self):
super(eight_hundred_bb_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(eight_hundred_bb_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(eight_hundred_bb_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 eight_hundred_bb_strategy()