O TrendScalperStrategy é uma conversão C# do MetaTrader 4 consultor especialista Currencyprofits_01_1.mq4. O robô original é um scalper leve que segue tendências que combina um filtro cruzado EMA/SMA de curto prazo com entradas de breakout em torno dos máximos e mínimos de oscilação mais recentes. A porta StockSharp mantém as mesmas regras de decisão enquanto adota a assinatura de vela de alto nível e o pipeline de indicadores da estrutura.
Lógica de negociação
Indicadores
EMA rápida (padrão 6) em preços de fechamento.
Lento SMA (padrão 12) em preços de fechamento.
Máximo mais alto (janela padrão 6) e Mínimo mais baixo (janela padrão 6) calculados a partir dos máximos e mínimos da vela.
Condições de entrada
Longo: o preço atinge a banda baixa recente (Lowest Low) enquanto o EMA rápida está acima do lento SMA. A estratégia envia uma ordem de compra a mercado com o volume definido pela regra de gestão de dinheiro.
Venda: o preço toca a banda alta recente (Highest High) enquanto a rápida EMA está abaixo da lenta SMA. Uma ordem de venda a mercado é colocada usando o mesmo cálculo de volume.
O sistema permanece estável enquanto uma posição está aberta, refletindo o comportamento de ordem única da versão MQL.
Exit Conditions
Saída longa: quando uma posição longa aberta vê a máxima da vela quebrar o registrado Highest High, a posição é fechada no mercado.
Saída curta: quando uma posição curta aberta observa a queda da mínima da vela através do Lowest Low, a venda é coberta no mercado.
Um stop-loss protetor gerenciado por StartProtection é anexado a cada negociação quando StopLossPoints é maior que zero.
Gestão de capital
A lógica de dimensionamento de lote reproduz os três modos expostos no script MQL:
Modo
Descrição
Comportamento no porto
0
Lotes fixos (LotsIfNoMM).
Retorna o FixedVolume configurado.
<0
Lotes fracionários calculados a partir do saldo da conta e do fator de risco.
Dimensionamento total a partir do equilíbrio e do fator de risco.
Usa a mesma fórmula base, mas o resultado é arredondado para o próximo número inteiro, com limite mínimo de 1 lote e limite de 100.
O saldo é retirado de Portfolio.CurrentValue (voltando para BeginValue). Caso o valor da carteira não esteja disponível, a estratégia reverte para o volume fixo para que as ordens ainda sejam emitidas durante os backtests.
Gestão de risco
Stop-loss: o parâmetro StopLossPoints é expresso em pontos de preço (pips). Durante OnStarted a distância é multiplicada por Security.PriceStep e passada para StartProtection, permitindo que StockSharp mantenha a ordem de proteção.
Posição única: a lógica aplica Position == 0 antes de abrir uma nova negociação, evitando posições sobrepostas exatamente como o especialista MT4.
Parâmetros
Nome
Padrão
Descrição
CandleType
Período de 15 minutos
Série de velas usada para cálculos de indicadores e sinais.
FastLength
6
Período do EMA rápida.
SlowLength
12
Período da lentidão SMA.
BreakoutWindow
6
Número de velas inspecionadas para o filtro de fuga mais alto/mais baixo.
FixedVolume
0,1 lote
Volume quando o gerenciamento de dinheiro está desativado ou é necessário um substituto.
MoneyManagementMode
0
Seleciona entre tamanho de lote fixo, fracionário ou arredondado.
MoneyManagementRisk
40
Multiplicador de fator de risco usado no dimensionamento de lote baseado em saldo.
StopLossPoints
50
Distância de stop-loss em pontos de preço (convertido em preço absoluto antes de chamar StartProtection).
Notas de implementação
O encadeamento de indicadores depende do fluxo de trabalho de alto nível SubscribeCandles().Bind(...); nenhum buffer de série manual é necessário.
Comentários no código foram adicionados em inglês para corresponder às diretrizes do repositório.
Nenhum teste unitário foi modificado; o foco desta conversão é a estratégia e a documentação que a acompanha.
Dicas de uso
Selecione um intervalo de velas que corresponda ao ambiente de negociação original (por exemplo, prazos intradiários curtos para scalping).
Certifique-se de que o portfólio tenha um PriceStep válido para que a conversão de stop loss em preço absoluto funcione corretamente.
Ajuste MoneyManagementRisk com cuidado: valores mais altos levam a posições maiores devido ao cálculo ceil(balance * risk / 10000) herdado do especialista MQL.
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>
/// Trend Scalper strategy - EMA crossover with Highest/Lowest breakout confirmation.
/// Buys when fast EMA crosses above slow EMA and close is near highest.
/// Sells when fast EMA crosses below slow EMA and close is near lowest.
/// </summary>
public class TrendScalperStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TrendScalperStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 8)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_channelPeriod = Param(nameof(ChannelPeriod), 20)
.SetDisplay("Channel Period", "Highest/Lowest lookback", "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(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 trend_scalper_strategy(Strategy):
def __init__(self):
super(trend_scalper_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 8).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._channel_period = self.Param("ChannelPeriod", 20).SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
@property
def fast_period(self): return self._fast_period.Value
@property
def slow_period(self): return self._slow_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(trend_scalper_strategy, self).OnReseted()
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(trend_scalper_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished: return
f = float(fast); s = float(slow)
if not self._has_prev:
self._prev_fast = f; self._prev_slow = s; self._has_prev = True; return
if self._prev_fast <= self._prev_slow and f > s and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and f < s and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_fast = f; self._prev_slow = s
def CreateClone(self): return trend_scalper_strategy()