Esta estratégia é uma versão StockSharp do MetaTrader 4 consultor especialista RUBBERBANDS_3. Ele mantém dois extremos de preços correntes, abre posições adicionais sempre que o preço se expande por uma distância configurável e liquida toda a sequência quando ocorre um contra-movimento de um determinado tamanho. Após uma retração, a estratégia opcionalmente muda para a direção oposta enquanto monitora uma meta de lucros e perdas no nível da sessão.
Observação: StockSharp opera em posições líquidas. O script MT4 original pode manter ordens longas e curtas simultaneamente, mas a porta fecha a sequência ativa antes de mudar de direção. O comportamento geral de escalar tendências e desfazer-se em retrocessos é preservado.
Lógica de negociação
Registre o preço de fechamento atual como máximo e mínimo em execução (ou reutilize os valores salvos ao reiniciar).
Quando o preço subir PipStep pontos acima do máximo atual, envie uma ordem de compra a mercado de tamanho OrderVolume e atualize o máximo para o novo preço.
Quando o preço cair PipStep pontos abaixo do mínimo atual, envie uma ordem de venda a mercado de tamanho OrderVolume e atualize o mínimo.
Se o mercado recuar BackStep pontos contra a direção ativa, feche todas as posições nessa direção e estabeleça uma reversão. O lado oposto é aberto quando a sequência anterior estiver totalmente liquidada.
Monitore o resultado cumulativo da sessão. Se o lucro realizado mais o lucro aberto atingir SessionTakeProfit × OrderVolume, feche a sessão. Quando o rebaixamento durante a reversão exceder SessionStopLoss × OrderVolume, feche tudo também.
A alternância QuiesceNow evita novas negociações quando a estratégia é plana. O sinalizador StopNow pausa toda a lógica e CloseNow solicita um nivelamento imediato do portfólio.
Os pedidos são gerados a partir de velas finalizadas do CandleType configurado. O período padrão é de um minuto, correspondendo ao tempo do EA original que acionou as verificações no início de cada minuto.
Parâmetros
Parâmetro
Descrição
Padrão
OrderVolume
Tamanho base de cada ordem de mercado.
0.02
MaxOrders
Número máximo de posições simultâneas em uma única direção. Entradas adicionais são bloqueadas quando o limite é atingido.
10
PipStep
Distância de expansão em pontos que agrega um novo comércio.
100
BackStep
Contra-movimento em pontos que força uma saída e prepara uma reversão.
20
QuiesceNow
Quando true, a estratégia permanece ociosa enquanto nenhuma posição estiver aberta.
false
DoNow
Abre a primeira sequência longa imediatamente após o início da estratégia.
false
StopNow
Sinalizador de parada brusca que impede qualquer processamento adicional. As posições existentes permanecem intocadas.
false
CloseNow
Solicita uma posição plana imediata, acionando fechamentos sequenciais.
false
UseSessionTakeProfit
Habilita o take-profit cumulativo da sessão.
true
SessionTakeProfit
Lucro alvo na moeda da conta por lote usado para fechar a sessão.
2000
UseSessionStopLoss
Ativa o stop-loss cumulativo da sessão.
true
SessionStopLoss
Perda máxima tolerada por lote durante a reversão antes do encerramento da sessão.
4000
UseInitialValues
Ao reiniciar, reutilize os InitialMax e InitialMin fornecidos manualmente em vez do preço de fechamento mais recente.
false
InitialMax
Extremo superior armazenado reutilizado quando UseInitialValues está ativado.
0
InitialMin
Extremo inferior armazenado reutilizado quando UseInitialValues está ativado.
0
CandleType
Série de velas processada pela estratégia. O padrão é velas de um minuto.
TimeFrame(1m)
Gerenciamento de sessão
Agregação de lucros: os lucros realizados são acumulados após cada fechamento completo, enquanto os ganhos não realizados são recalculados a partir da média ponderada dos preços de entrada de todas as posições abertas.
Take-profit da sessão: assim que SessionTakeProfit for atingido, a estratégia fecha todas as negociações e redefine os extremos armazenados.
Stop-loss da sessão: durante uma sequência de reversão (BackStep acionada), a estratégia rastreia a perda flutuante. Se o rebaixamento exceder SessionStopLoss, todas as posições serão liquidadas e a sessão será reiniciada com estatísticas limpas.
Notas de uso
A etapa de preço usada para converter pontos em preços é obtida de Security.PriceStep. Configure os metadados do instrumento adequadamente; caso contrário, um substituto de 0.0001 será aplicado.
Como as ordens são compensadas, a estratégia executa o fechamento das negociações antes de abrir na direção oposta. Ao migrar dados legados, esteja ciente de que o histórico de pedidos pode ser diferente das plataformas protegidas.
A bandeira DoNow abre apenas a primeira posição longa. As entradas adicionais seguem as condições regulares de breakout.
Use QuiesceNow quando quiser deixar a estratégia carregada, mas inativa depois de nivelar o livro.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Rubberbands 3: Grid expansion strategy using SMA+ATR bands.
/// Enters at band extremes, adds on continuation, exits at mean.
/// </summary>
public class Rubberbands3Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private int _gridCount;
public Rubberbands3Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_smaLength = Param(nameof(SmaLength), 20)
.SetDisplay("SMA Length", "SMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_gridCount = 0;
var sma = new SimpleMovingAverage { Length = SmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var close = candle.ClosePrice;
var upper = smaVal + atrVal * 1.5m;
var lower = smaVal - atrVal * 1.5m;
if (Position > 0)
{
if (close >= smaVal)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (close <= _entryPrice - atrVal * 5m)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (_gridCount < 4 && close <= _entryPrice - atrVal * 0.8m)
{
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
BuyMarket();
}
}
else if (Position < 0)
{
if (close <= smaVal)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (close >= _entryPrice + atrVal * 5m)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (_gridCount < 4 && close >= _entryPrice + atrVal * 0.8m)
{
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
SellMarket();
}
}
if (Position == 0)
{
if (close <= lower)
{
_entryPrice = close;
_gridCount = 0;
BuyMarket();
}
else if (close >= upper)
{
_entryPrice = close;
_gridCount = 0;
SellMarket();
}
}
}
}
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 SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class rubberbands_3_strategy(Strategy):
def __init__(self):
super(rubberbands_3_strategy, self).__init__()
self._sma_length = self.Param("SmaLength", 20).SetDisplay("SMA Length", "SMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(rubberbands_3_strategy, self).OnReseted()
self._entry_price = 0
self._grid_count = 0
def OnStarted2(self, time):
super(rubberbands_3_strategy, self).OnStarted2(time)
self._entry_price = 0
self._grid_count = 0
sma = SimpleMovingAverage()
sma.Length = self._sma_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(sma, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
if atr_val <= 0:
return
close = float(candle.ClosePrice)
upper = sma_val + atr_val * 1.5
lower = sma_val - atr_val * 1.5
if self.Position > 0:
if close >= sma_val:
self.SellMarket()
self._entry_price = 0
self._grid_count = 0
elif close <= self._entry_price - atr_val * 5.0:
self.SellMarket()
self._entry_price = 0
self._grid_count = 0
elif self._grid_count < 4 and close <= self._entry_price - atr_val * 0.8:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.BuyMarket()
elif self.Position < 0:
if close <= sma_val:
self.BuyMarket()
self._entry_price = 0
self._grid_count = 0
elif close >= self._entry_price + atr_val * 5.0:
self.BuyMarket()
self._entry_price = 0
self._grid_count = 0
elif self._grid_count < 4 and close >= self._entry_price + atr_val * 0.8:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.SellMarket()
if self.Position == 0:
if close <= lower:
self._entry_price = close
self._grid_count = 0
self.BuyMarket()
elif close >= upper:
self._entry_price = close
self._grid_count = 0
self.SellMarket()
def CreateClone(self):
return rubberbands_3_strategy()