A estratégia Range Follower reproduz o MetaTrader 5 consultor especialista "Range Follower" usando o StockSharp API de alto nível. Ele monitora a faixa de preço do dia atual em relação a um benchmark diário Average True Range (ATR) e abre uma única negociação de breakout quando o preço se afasta o suficiente da máxima ou mínima da sessão. A conversão mantém a abordagem original de dividir o ATR em uma parte de gatilho e uma parte residual que se torna a distância de lucro.
Lógica de negociação
Linha de base de volatilidade diária
Um ATR de 20 períodos calculado em velas diárias fornece o intervalo de referência para o dia de negociação atual.
O valor ATR é dividido por TriggerPercent em dois segmentos: a distância de acionamento que deve ser excedida antes de entrar e a distância restante que é usada como meta de lucro.
Rastreamento de alcance
A estratégia registra continuamente a máxima e a mínima da sessão atual a partir da vela diária ativa.
As atualizações de nível 1 fornecem os melhores preços de compra e venda mais recentes que são usados para medir a distância das cotações atuais até os extremos da sessão.
Entrada única por dia
Quando a melhor oferta é superior à distância de disparo acima da mínima da sessão e nenhuma negociação foi aberta ainda, a estratégia compra no mercado.
Quando a melhor venda é superior à distância de disparo abaixo da máxima da sessão e nenhuma negociação foi aberta ainda, a estratégia vende a mercado.
Apenas uma negociação é permitida por dia; o sinalizador é redefinido quando uma nova sessão é iniciada.
Stop-loss e take-profit
Para posições longas, o stop loss é colocado uma distância de disparo abaixo do preço de entrada e o take-profit uma distância residual acima dele.
Para posições curtas, o stop loss está uma distância de gatilho acima do preço de entrada e o take-profit uma distância residual abaixo dele.
O monitoramento de preços é realizado tanto nos ticks de Nível 1 quanto nas atualizações de velas para fechar posições assim que um nível é violado.
Reinicialização da sessão diária
Na primeira vela de um novo dia de negociação, a estratégia fecha qualquer posição aberta, limpa o estado interno e recarrega a linha de base ATR.
Se o intervalo diário atual já exceder a distância de acionamento quando a sessão for inicializada, a negociação será ignorada pelo resto do dia para imitar a verificação de segurança do EA original.
Parâmetros
Nome
Padrão
Descrição
CandleType
Velas de 15 minutos
Período de trabalho usado para detectar limites de sessão.
TriggerPercent
60
Porcentagem do ATR diário usado como distância de acionamento do rompimento. Deve ficar entre 10 e 90.
Volume
0,1
Volume de ordens de mercado para entradas longas e curtas.
Gestão de risco
Stops e metas são derivados da mesma linha de base ATR para que a relação recompensa-risco seja sempre igual a (100 - TriggerPercent) : TriggerPercent.
A estratégia registra uma única posição por vez e a liquida imediatamente quando o stop ou alvo é tocado, evitando múltiplas negociações sobrepostas.
StartProtection() ativa a infraestrutura de proteção de StockSharp, permitindo que componentes externos anexem trailing stops ou proteções de portfólio, se necessário.
Notas de implementação
Os valores diários ATR são produzidos por uma assinatura de vela diária dedicada e o indicador AverageTrueRange vinculado ao API de alto nível.
Os dados de nível 1 são necessários para espelhar as decisões baseadas em ticks do EA; os melhores preços de oferta e de venda impulsionam as verificações de entrada e saída.
Os limites da sessão diária são derivados das velas do período de trabalho, garantindo que qualquer calendário de negociação usado em StockSharp redefinirá a estratégia de forma consistente.
A conversão evita buffers de indicadores manuais ou loops históricos, contando, em vez disso, com campos com estado atualizados pelos retornos de chamada Bind.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Range Follower strategy: ATR-based range breakout.
/// Tracks high/low range and enters when price breaks out by ATR threshold.
/// </summary>
public class RangeFollowerStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _rangePeriod;
private decimal _rangeHigh;
private decimal _rangeLow;
private int _barCount;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public int RangePeriod { get => _rangePeriod.Value; set => _rangePeriod.Value = value; }
public RangeFollowerStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_rangePeriod = Param(nameof(RangePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Range Period", "Bars for range calculation", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rangeHigh = 0m;
_rangeLow = decimal.MaxValue;
_barCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rangeHigh = 0;
_rangeLow = decimal.MaxValue;
_barCount = 0;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
_barCount++;
if (candle.HighPrice > _rangeHigh) _rangeHigh = candle.HighPrice;
if (candle.LowPrice < _rangeLow) _rangeLow = candle.LowPrice;
if (_barCount < RangePeriod) return;
var threshold = atrValue * 0.5m;
if (close > _rangeHigh - threshold && Position <= 0)
{
BuyMarket();
ResetRange();
}
else if (close < _rangeLow + threshold && Position >= 0)
{
SellMarket();
ResetRange();
}
// Slide the range window
if (_barCount > RangePeriod * 2)
ResetRange();
}
private void ResetRange()
{
_rangeHigh = 0;
_rangeLow = decimal.MaxValue;
_barCount = 0;
}
}