A Estratégia de tendência plana reproduz as ideias centrais do consultor especialista Flat Trend original, combinando filtros de tendência de várias velocidades, confirmação de ADX e um filtro de quebra de desvio padrão "suco". A estratégia se concentra em detectar o momento em que o preço sai de uma fase de variação e o impulso se expande, permitindo-lhe unir movimentos direcionais com proteção de posição dinâmica.
Lógica de negociação
Filtros de tendência – três médias móveis exponenciais (EMAs) com comprimentos configuráveis representam o gatilho, o primeiro filtro e o segundo filtro. Sua inclinação e a posição do preço em relação a cada EMA são traduzidas em estados:
Forte alta (preço acima de EMA e EMA subindo).
Alta moderada (preço acima de EMA, mas inclinação neutra).
Forte baixa (preço abaixo de EMA e EMA caindo).
Baixa moderada (preço abaixo de EMA, mas inclinação neutra).
Regras de entrada
As negociações longas exigem estados de alta no gatilho e no filtro EMA. O segundo filtro pode ser opcionalmente ignorado. O modo estrito força o uso apenas de estados fortes de alta.
As negociações curtas refletem as condições para estados de baixa.
A confirmação opcional ADX garante que o Índice Direcional Médio exceda um limite e, quando ativado, os componentes +DI e –DI concordam com a direção da negociação.
O filtro “juice” verifica se o desvio padrão dos preços está acima de um nível de rompimento definido pelo usuário, evitando negociações durante fases de volatilidade plana.
A negociação pode ser restrita a uma janela intradiária selecionada.
Regras de saída
Estados de tendência opostos no gatilho EMA iniciam uma saída. No modo estrito, a estratégia aguarda o contra-sinal mais forte.
A dinâmica para as posições de saída sempre que o preço atinge o nível de parada calculado.
Gestão de risco
Parada inicial – calculada a partir de uma distância de pip estática ou do valor Average True Range (ATR), emulando a lógica baseada em ADR do EA original.
Trailing stop – movimentos com o preço mais alto (ou mais baixo) desde a entrada usando o ATR multiplicado por um divisor.
Ponto de equilíbrio – quando o preço avança pela distância configurada, o stop se move além do preço de entrada em um pequeno valor de bloqueio.
Parâmetros
Nome
Descrição
TriggerLength
Comprimento de EMA para o filtro do acionador.
FilterLength1
Comprimento EMA para o primeiro filtro de confirmação.
FilterLength2
Comprimento EMA para o segundo filtro de confirmação.
UseOnlyPrimaryIndicators
Use apenas o gatilho e o primeiro filtro para entradas.
IgnoreModerateForEntry
Exigir estados de tendência fortes para novas negociações.
IgnoreModerateForExit
Exigir contra-sinais fortes para fechar negociações.
Ativação do ponto de equilíbrio e distância de bloqueio.
AtrPeriod
Lookback ATR usado para estimativa de volatilidade.
CandleType
Prazo da vela primária.
Resumo do Indicador
Média Móvel Exponencial (EMA) – três instâncias para avaliação de tendências em múltiplas velocidades.
Desvio Padrão – modela o filtro de quebra de volatilidade "suco".
Average True Range (ATR) – mede a volatilidade para stops e trailing.
Índice direcional médio (ADX) – confirma a força e a direção da tendência.
Notas de uso
Garantir que a segurança da estratégia tenha um PriceStep definido; caso contrário, a etapa padrão de 0,0001 será usada para distâncias baseadas em pip.
A estratégia usa ordens de mercado (BuyMarket, SellMarket) e dimensiona automaticamente o volume ao reverter posições.
As paradas dinâmicas são simuladas internamente fechando posições quando o nível de parada virtual é tocado.
Combine a janela de negociação e opções de entrada rigorosas para se concentrar em sessões de alta liquidez e evitar períodos agitados.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Flat Trend strategy - breakout from low volatility using ATR and EMA.
/// Buys when ATR expands and price is above EMA.
/// Sells when ATR expands and price is below EMA.
/// </summary>
public class FlatTrendStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevAtr;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FlatTrendStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR volatility period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevAtr = 0m; _prevClose = 0m; _prevEma = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevAtr = atr;
_prevClose = close;
_prevEma = ema;
_hasPrev = true;
return;
}
// Volatility expansion: ATR increasing
var atrExpanding = atr > _prevAtr;
// Breakout above EMA with expanding volatility
if (atrExpanding && _prevClose <= _prevEma && close > ema && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Breakout below EMA with expanding volatility
else if (atrExpanding && _prevClose >= _prevEma && close < ema && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevAtr = atr;
_prevClose = close;
_prevEma = ema;
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class flat_trend_strategy(Strategy):
def __init__(self):
super(flat_trend_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR volatility period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_atr = 0.0
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
@property
def ema_period(self):
return self._ema_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(flat_trend_strategy, self).OnReseted()
self._prev_atr = 0.0
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(flat_trend_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
atr = AverageTrueRange()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self.process_candle).Start()
def process_candle(self, candle, ema, atr):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
atr_val = float(atr)
if not self._has_prev:
self._prev_atr = atr_val
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
atr_expanding = atr_val > self._prev_atr
if atr_expanding and self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif atr_expanding and self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_atr = atr_val
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return flat_trend_strategy()