Esta estratégia é uma versão C# do consultor especialista MetaTrader BollingerBandsEA (ver. 3.0). Ele negocia configurações de reversão à média que ocorrem após o preço ultrapassar as bandas Bollinger durante a sessão de negociação ativa.
Lógica de negociação
Assine a série primária de velas intradiárias (velas de 15 minutos por padrão) e uma série diária de velas usada para construir o filtro de tendência.
Calcule Bollinger bandas (comprimento 20, largura 2,0) na série intradiária e um período de 100 SMA nos fechamentos diários.
Acompanhe os máximos/mínimos do dia atual e anterior e mantenha os valores da banda Bollinger anteriores para avaliação do sinal.
Permitir entradas apenas dentro da janela da sessão de negociação: de SessionStartOffsetMinutes após a abertura do dia de negociação até SessionEndOffsetMinutes antes do final do dia de negociação.
Ignore a negociação assim que o PnL cumulativo do dia atual se tornar positivo, imitando o stop diário de EA.
Entre em posição curta quando a vela anterior for de baixa, fechada acima da banda superior, o fechamento atual permanecer acima dessa banda, a largura da banda for ampla o suficiente, o preço estiver abaixo do diário SMA e o preço for negociado acima da alta diária atual ou anterior.
Entre em posição comprada quando a vela anterior for de alta, fechada abaixo da banda inferior, o fechamento atual permanecer abaixo dessa banda, a largura da banda for ampla o suficiente, o preço estiver acima do diário SMA e o preço for negociado abaixo da mínima diária atual ou anterior.
O tamanho da posição é determinado pelo volume fixo configurado ou pelo dimensionamento baseado em risco que utiliza a distância até o stop loss em pontos.
As saídas são realizadas verificando stop-loss, take-profit, fechamento opcional na banda intermediária, um trailing stop opcional e a lógica opcional de ponto de equilíbrio. As negociações perdedoras também podem ser liquidadas após um tempo de retenção configurável.
Parâmetros
Parâmetro
Descrição
CandleType
Série de velas intradiárias usadas para negociação.
BollingerLength
Período da média móvel das bandas Bollinger.
BollingerWidth
Multiplicador de largura das bandas Bollinger.
DailyMaLength
Duração do filtro diário SMA.
StopLossPoints
Distância de stop-loss expressa em pontos do instrumento.
UseRiskVolume
Permite dimensionamento de posição baseado em risco.
RiskPercent
Porcentagem da conta usada para dimensionamento baseado em risco.
FixedVolume
Volume fixo de fallback quando o dimensionamento de risco está desabilitado ou não é possível.
SessionStartOffsetMinutes
Minutos após o início da sessão antes que as entradas sejam permitidas.
SessionEndOffsetMinutes
Minutos antes do final da sessão, quando as entradas são bloqueadas.
CloseOnMiddleBand
Posição de saída quando o preço cruzar a banda intermediária Bollinger.
EnableTrailing
Permite ajustes de trailing stop.
TrailingFactor
Multiplicador de distância necessário antes de seguir a parada.
EnableBreakEven
Permite mover o stop para o preço de entrada.
BreakEvenFactor
Múltiplo de lucro necessário para mover o stop para o ponto de equilíbrio.
CloseLosingAfterMinutes
Fecha negociações perdedoras após mantê-las durante os minutos especificados.
Notas
As ordens protetoras de stop-loss e take-profit são simuladas verificando os extremos das velas em cada atualização. Ajuste esta seção se forem necessárias ordens de proteção do lado da bolsa.
O dimensionamento baseado em risco depende de Security.Step e Security.StepPrice. Se esses valores estiverem faltando, a estratégia voltará ao volume fixo.
O stop de lucro diário utiliza a estratégia PnL, portanto o PnL realizado e flutuante precisam estar na mesma moeda da carteira.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trades reversals from Bollinger Bands.
/// Buys when price closes below lower band and sells when price closes above upper band.
/// </summary>
public class BollingerBandsSessionReversalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerWidth;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Length of the Bollinger Bands moving average.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Width multiplier for Bollinger Bands.
/// </summary>
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public BollingerBandsSessionReversalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary candle series", "General");
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators");
_bollingerWidth = Param(nameof(BollingerWidth), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BollingerLength,
Width = BollingerWidth
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (bbValue is not IBollingerBandsValue bb)
return;
var middle = bb.MovingAverage ?? 0m;
var upper = bb.UpBand ?? 0m;
var lower = bb.LowBand ?? 0m;
if (middle == 0m || upper == 0m || lower == 0m)
return;
var price = candle.ClosePrice;
// Reversal: buy when price falls below lower band
if (price < lower && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Reversal: sell when price rises above upper band
else if (price > upper && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit at middle band
else if (Position > 0 && price >= middle)
{
SellMarket();
}
else if (Position < 0 && price <= middle)
{
BuyMarket();
}
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class bollinger_bands_session_reversal_strategy(Strategy):
def __init__(self):
super(bollinger_bands_session_reversal_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary candle series", "General")
self._bollinger_length = self.Param("BollingerLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators")
self._bollinger_width = self.Param("BollingerWidth", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BollingerLength(self):
return self._bollinger_length.Value
@BollingerLength.setter
def BollingerLength(self, value):
self._bollinger_length.Value = value
@property
def BollingerWidth(self):
return self._bollinger_width.Value
@BollingerWidth.setter
def BollingerWidth(self, value):
self._bollinger_width.Value = value
def OnStarted2(self, time):
super(bollinger_bands_session_reversal_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self.BollingerLength
bollinger.Width = self.BollingerWidth
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, self._process_candle).Start()
def _process_candle(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
upper = bb_value.UpBand
lower = bb_value.LowBand
middle = bb_value.MovingAverage
if upper is None or lower is None or middle is None:
return
up = float(upper)
lo = float(lower)
mid = float(middle)
if up == 0 or lo == 0 or mid == 0:
return
price = float(candle.ClosePrice)
if price < lo and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif price > up and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and price >= mid:
self.SellMarket()
elif self.Position < 0 and price <= mid:
self.BuyMarket()
def CreateClone(self):
return bollinger_bands_session_reversal_strategy()