Esta estratégia porta o consultor especialista 15M Scalper do MetaTrader para a API de alto nível do StockSharp. Recria a
lógica de entrada com múltiplos filtros (médias móveis ponderadas, oscilador estocástico, Parabolic SAR, momentum multi-temporal e
MACD mensal) e a pilha de saída completa que combina alvos baseados em dinheiro, stops de seguimento, movimentos de ponto de
equilíbrio e um guardião de drawdown de capital. A versão StockSharp opera em candles completadas exatamente como o EA e mantém o
código orientado a eventos preservando os parâmetros originais.
Como Funciona
Filtro de Tendência – as médias móveis ponderadas rápida e lenta calculadas no período atual (padrão 15 minutos) devem
estar alinhadas com a direção da operação. As médias usam o preço típico ((High + Low + Close) / 3) para corresponder à
entrada PRICE_TYPICAL do MQL.
Reversão Estocástica – um oscilador estocástico 5/3/3 é amostrado nas duas últimas candles fechadas. Sinais comprados
requerem que o %K cruze de volta acima de 20, enquanto os vendidos requerem um cruzamento abaixo de 80, espelhando as
verificações Stoc1/Stoc2 do script.
Confirmação do Parabolic SAR – o valor do SAR da barra completada deve estar abaixo da abertura anterior para comprados e
acima para vendidos, reproduzindo o filtro de segurança SAR < Open[1] / SAR > Open[1].
Momentum em Período Superior – um indicador de momentum de 14 períodos no período superior configurável (padrão 1 hora)
deve se desviar de 100 em qualquer uma das últimas três barras fechadas pelo menos nos limiares de compra/venda. Isso
implementa o trio MomLevelB/MomLevelS sem acessar os buffers de indicadores diretamente.
MACD Mensal – uma série de MACD no fluxo de candles mensais (padrão barras de 30 dias) mantém a linha principal acima do
sinal para comprados e abaixo para vendidos. O mesmo filtro MACD também alimenta a lógica de saída opcional que fecha posições
quando as linhas se cruzam na direção oposta.
Gestão de Ordens – quando uma configuração oposta aparece, a estratégia primeiro fecha a posição existente e depois aguarda
a próxima barra para abrir operações na nova direção. O escalonamento de volume segue a regra de martingale do EA via
LotExponent e o IncreaseFactor sensível a perdas.
Gestão de Risco
Stop Loss / Take Profit – as distâncias são inseridas em "pontos" do MetaTrader e são convertidas para preços absolutos
através de Security.PriceStep. Para ticks fracionários de FX (passo de preço < 1), a implementação multiplica o passo por 10
para imitar o tratamento de pips do EA.
Ponto de Equilíbrio ("sem perda") – uma vez que o preço se move por BreakEvenTriggerSteps, o stop é movido virtualmente
para a entrada mais o offset configurado. Se o preço recuar através desse nível, a posição é fechada a mercado.
Trailing Stop – um trailing stop baseado em candles observa o máximo mais alto (para comprados) ou o mínimo mais baixo
(para vendidos). Quando o recuo excede TrailingStopSteps, a posição é fechada, duplicando o comportamento original do
OrderModify.
Alvos Monetários – UseProfitTargetMoney, UseProfitTargetPercent e EnableMoneyTrailing trabalham com P&L flutuante
medido via PriceStep × StepPrice. O port mantém intacta a lógica de take-profit, alvo percentual e drawdown de seguimento
(MoneyTrailingStop).
Stop de Capital – UseEquityStop rastreia o pico de (capital inicial + P&L realizado + lucro flutuante). Se o drawdown
atual exceder TotalEquityRisk por cento desse pico, cada posição é fechada, replicando AccountEquityHigh() e
TotalEquityRisk do EA.
Dimensionamento Martingale – cada operação adicional na mesma direção escala o volume por LotExponent. Perdas
consecutivas aumentam o próximo volume base em IncreaseFactor por perda, fornecendo o mesmo dimensionamento de lote
"adaptativo" que o ramo IncreaseFactor do MQL.
Parâmetros
Parâmetro
Descrição
CandleType
Período de trabalho principal (padrão candles de 15 minutos).
MomentumCandleType
Período superior para o filtro de momentum (padrão candles de 1 hora).
MacdCandleType
Período para o filtro de tendência MACD (padrão candles de 30 dias).
FastMaPeriod, SlowMaPeriod
Comprimentos das médias móviles ponderadas que definem o filtro de tendência.
MomentumPeriod
Comprimento do Momentum no período superior.
MomentumBuyThreshold, MomentumSellThreshold
Desvio absoluto mínimo de 100 necessário para permitir operações compradas/vendidas.
StopLossSteps, TakeProfitSteps
Distâncias de stop protetor e alvo em passos de preço. Definir como zero para desativar.
Opções de dimensionamento martingale: lote inicial, multiplicador, incremento baseado em perdas e máximo de adições.
UseExitByMacd
Fechar posições quando a linha principal do MACD cruza o sinal contra a operação.
Uso
Conecte a estratégia a um ativo e certifique-se de que Security.PriceStep e Security.StepPrice estejam preenchidos. Esses
valores são usados para traduzir entradas baseadas em pips e alvos monetários em números absolutos.
Ajuste CandleType, MomentumCandleType e MacdCandleType se quiser executar o scalper em diferentes períodos. Os padrões
replicam a configuração original de 15 minutos / 1 hora / mensal.
Ajuste as distâncias baseadas em pips (StopLossSteps, TakeProfitSteps, TrailingStopSteps, configurações de ponto de
equilíbrio) para se adequar ao tamanho de tick do instrumento. Comece com os padrões fornecidos e aumente-os para mercados
mais voláteis.
Defina preferências de gestão de dinheiro: decida se usa take profits monetários ou percentuais, ative o trailing monetário e
configure o stop de capital se quiser uma rede de segurança contra drawdowns profundos.
Lance a estratégia. Ela se inscreverá automaticamente em todos os fluxos de candles necessários, plotará indicadores (se um
gráfico estiver disponível) e começará a avaliar sinais assim que cada indicador tiver histórico suficiente.
Notas e Diferenças do EA Original
O port usa o modelo de posição agregada do StockSharp. Quando um sinal oposto aparece, a posição atual é fechada primeiro e a
nova direção é avaliada na próxima candle, mantendo o comportamento determinístico.
Cálculos baseados em dinheiro dependem de Security.PriceStep e Security.StepPrice. Se o local não fornecer esses valores,
os alvos monetários são ignorados (o lucro flutuante é relatado como zero), exatamente como indicado nos comentários do código.
IncreaseFactor adiciona IncreaseFactor × perdas_consecutivas ao próximo volume base em vez de usar margem livre (que não
está disponível no ambiente sandbox). Isso ainda captura a intenção de aumentar o tamanho após sequências de perdas.
Todas as decisões são tomadas em candles finalizadas para evitar dupla contagem de sinais, correspondendo às verificações barra
por barra da implementação MetaTrader.
A estratégia desenha os mesmos indicadores no gráfico quando um visualizador está disponível, auxiliando na depuração e
tornando o port fácil de comparar com o EA.
Revise cuidadosamente o tamanho de tick, o preço do passo e as restrições de volume do seu corretor antes do trading ao vivo.
Esses valores impactam diretamente como as distâncias baseadas em pips e os alvos monetários são convertidos dentro da estratégia.
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;
/// <summary>
/// 15-minute scalper strategy using WMA crossover with ParabolicSar confirmation.
/// Buys when fast WMA crosses above slow WMA and SAR is below price.
/// Sells on reverse conditions.
/// </summary>
public class FifteenMinuteScalperStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private WeightedMovingAverage _fast;
private WeightedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast WMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow WMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public FifteenMinuteScalperStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast WMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 85)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow WMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new WeightedMovingAverage { Length = FastPeriod };
_slow = new WeightedMovingAverage { 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;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// WMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fifteen_minute_scalper_strategy(Strategy):
"""
15-minute scalper using WMA crossover with SL/TP and cooldown.
Buys when fast WMA crosses above slow WMA, sells on reverse.
"""
def __init__(self):
super(fifteen_minute_scalper_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast Period", "Fast WMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 85) \
.SetDisplay("Slow Period", "Slow WMA 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._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Trading timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fifteen_minute_scalper_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(fifteen_minute_scalper_strategy, self).OnStarted2(time)
fast = WeightedMovingAverage()
fast.Length = self._fast_period.Value
slow = WeightedMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
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 = 80
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 = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return fifteen_minute_scalper_strategy()