A estratégia Candle Trailing Stop é uma portagem do StockSharp do expert advisor MetaTrader com o mesmo nome. O robô original combinava filtros de tendência multitemporal, confirmação de momentum e um motor de trailing stop agressivo que seguia as mínimas e máximas das velas recentes. A versão C# mantém o mesmo fluxo de trabalho, mas se apoia em componentes de alto nível do StockSharp e expõe todas as configurações críticas como parâmetros de estratégia.
Lógica principal
Assinaturas de dados
O período de negociação impulsiona as entradas e as atualizações do trailing stop.
Um período superior fornece confirmação usando médias móveis linearmente ponderadas (LWMA) e um indicador de momentum.
Uma terceira assinatura calcula uma linha MACD em um período lento (mensal por padrão) para filtrar as operações.
Alinhamento de tendência
As operações são permitidas apenas quando as sequências de LWMA rápida, média e lenta estão alinhadas em ambos os períodos (sequência de alta para comprados, de baixa para vendidos).
Portão de momentum
O indicador de momentum deve estar próximo do valor neutro de 100 em pelo menos uma das últimas três barras do período superior.
Confirmação de MACD
Comprados exigem que a linha MACD esteja acima da linha de sinal; vendidos exigem a relação inversa.
Gatilho de entrada
Uma ruptura através da LWMA rápida no período atual (vela fechando acima/abaixo da média após tocá-la na barra anterior) inicia novas operações respeitando um limite de posição configurável.
Gestão de risco e saída
As distâncias iniciais de stop-loss e take-profit são definidas em pips e automaticamente convertidas em passos de preço.
Os stops podem migrar para o ponto de equilíbrio, seguir o extremo das velas recentes, ou recuar para um trailing clássico de distância fixa.
Funções opcionais baseadas em capital espelham o EA original: take profit monetário, take profit percentual, trailing de capital e proteção contra drawdown.
Parâmetros
Grupo
Nome
Descrição
Padrão
Negociação
Volume
Tamanho da ordem em lotes/contratos.
1
MaxTrades
Exposição agregada máxima expressa como Volume * MaxTrades.
10
Indicadores
FastCurrentLength
LWMA rápida no período de negociação.
9
MiddleCurrentLength
LWMA média no período de negociação.
20
SlowCurrentLength
LWMA lenta no período de negociação.
52
FastHigherLength
LWMA rápida no período superior.
9
MiddleHigherLength
LWMA média no período superior.
20
SlowHigherLength
LWMA lenta no período superior.
52
MomentumPeriod
Período de momentum no período superior.
14
MomentumBuyThreshold
Desvio máximo de 100 permitido para operações compradas.
0.3
MomentumSellThreshold
Desvio máximo de 100 permitido para operações vendidas.
0.3
MacdFastLength
Comprimento da EMA rápida para confirmação MACD.
12
MacdSlowLength
Comprimento da EMA lenta para confirmação MACD.
26
MacdSignalLength
Comprimento da EMA de sinal para confirmação MACD.
9
Risco
StopLossPips
Distância do stop-loss em pips.
20
TakeProfitPips
Distância do take-profit em pips.
50
UseMoveToBreakEven
Ativa a lógica de ponto de equilíbrio.
true
BreakEvenTriggerPips
Lucro em pips necessário antes de mover o stop.
30
BreakEvenOffsetPips
Offset adicionado ao deslocar o stop para o ponto de equilíbrio.
30
UseCandleTrail
Escolher entre trailing baseado em velas (true) ou trailing clássico (false).
true
CandleTrailLength
Número de velas fechadas usadas para calcular os extremos de trailing.
3
PadAmountPips
Buffer extra adicionado abaixo/acima do extremo de trailing.
10
TrailTriggerPips
Lucro necessário antes de o trailing clássico ativar.
40
TrailAmountPips
Distância mantida pelo trailing clássico.
40
Regras de capital
UseMoneyTakeProfit
Fechar todas as posições quando o lucro flutuante exceder o alvo monetário.
false
MoneyTakeProfit
Alvo de lucro monetário.
40
UsePercentTakeProfit
Fechar todas as posições quando o lucro flutuante exceder o alvo percentual.
false
PercentTakeProfit
Percentual do capital inicial usado como alvo de lucro.
10
EnableMoneyTrailing
Ativa trailing do lucro flutuante após um limite.
true
MoneyTrailTarget
Nível de lucro que ativa a lógica de trailing monetário.
40
MoneyTrailStop
Retração máxima permitida após o alvo ser atingido.
10
UseEquityStop
Ativa proteção contra drawdown de capital.
true
EquityRiskPercent
Drawdown máximo do pico de capital antes de forçar posição plana.
1
Dados
CurrentCandleType
Período de negociação.
5m
HigherCandleType
Período superior usado para filtros.
30m
MacdCandleType
Período para confirmação MACD (mensal por padrão).
30d
Notas e suposições
Pips são convertidos em passos de preço usando o tamanho de tick do instrumento. Em símbolos onde um pip difere de um tick pode ser necessário ajustar as distâncias de pip padrão.
Funções monetárias dependem do lucro não realizado aproximado como (fechamento - preçoMédio) * posição. Ajustes de swap e comissão não são simulados.
A estratégia usa ordens a mercado para entradas e saídas. As ordens iniciais de take-profit são registradas assim que uma operação é aberta, enquanto o gerenciamento de stop-loss é tratado internamente e fecha através de ordens a mercado quando o nível calculado é cruzado.
Todos os comentários no código são escritos em inglês conforme as diretrizes do projeto.
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 CandleTrailingStopStrategy : 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 CandleTrailingStopStrategy()
{
_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 candle_trailing_stop_strategy(Strategy):
def __init__(self):
super(candle_trailing_stop_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(candle_trailing_stop_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(candle_trailing_stop_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 candle_trailing_stop_strategy()