Este documento descreve a porta StockSharp do consultor especialista MetaTrader earlyTopProrate_V1. A estratégia procura movimentos intradiários que se estendem para além da abertura diária e saem da posição usando três metas de lucro. Ele foi convertido usando o StockSharp API de alto nível, preservando as ideias originais de gerenciamento de dinheiro e gerenciamento comercial.
Lógica principal
Contexto diário – a estratégia reconstrói a abertura, a máxima e a mínima do dia atual a partir das velas processadas. A direção dominante é definida comparando high - open e open - low.
Janela de entrada – novas negociações só poderão ser abertas entre StartHour (inclusive) e EndHour (exclusivo). A configuração padrão negocia no início da sessão europeia.
Condições de entrada –
Quando a direção dominante é de alta e o último preço de fechamento está acima da abertura diária, a estratégia abre uma posição longa.
Quando a direção dominante é de baixa e o último preço de fechamento está abaixo da abertura diária, a estratégia abre uma posição curta.
Apenas uma posição de mercado é permitida ao mesmo tempo (MaxPositions = 1 por padrão).
Gestão de dinheiro – o volume de cada entrada é obtido a partir do modo de gestão de dinheiro selecionado (veja abaixo). O valor é arredondado usando o passo de volume do instrumento e fixado entre o volume mínimo e máximo do câmbio.
Tratamento de posição – depois de entrar em uma posição, a estratégia aplica as regras de saída em camadas listadas na próxima seção. As regras refletem o consultor especialista original, mas são implementadas com ordens StockSharp de alto nível em vez de modificações diretas de stop-loss/take-profit.
Fechamento da sessão – se uma posição permanecer aberta quando ClosingHour for alcançado, a estratégia forçará uma saída no mercado.
Detalhes de gerenciamento comercial
O consultor especialista MQL original depende de ajustes manuais de stop e take-profit. A porta StockSharp reproduz o comportamento com verificações explícitas em cada vela finalizada:
Resgate do ponto de equilíbrio (BreakEvenTrigger) – se o preço se mover contra a entrada pelo número de pontos configurado, a estratégia aguarda uma recuperação de volta ao preço de entrada e então sai no ponto de equilíbrio.
Parada de emergência (StopLoss0) – quando a excursão adversa ultrapassa esta distância a posição é fechada imediatamente.
Stop para entrada (StopLoss1) – após um movimento positivo da distância especificada, o stop de proteção é movido para o preço de entrada.
Stop no lucro (StopLoss2) – quando o lucro atinge esse limite, o stop de proteção é empurrado acima (longo) ou abaixo (curto) da entrada. O deslocamento é igual a StopLoss2 - StopLoss1, reproduzindo a lógica setSL2-35 de MetaTrader.
Escalonamento (TakeProfit1/2/3 e Ratio1/2/3) – três objetivos de lucro acionam fechamentos parciais do volume de posição restante. Os índices representam porcentagens da posição atual para que as metas subsequentes trabalhem na exposição reduzida. O terceiro alvo fecha todo o restante.
Todos os parâmetros baseados em distância operam em pontos. O parâmetro auxiliar PointMultiplier multiplica o instrumento PriceStep para reproduzir a aritmética value * 10 * Point do script original (multiplicador padrão = 10).
Modos de gerenciamento de dinheiro
O parâmetro MoneyManagementType seleciona um dos quatro modelos de dimensionamento:
Modo
Descrição
0 or 1
Tamanho de lote fixo igual a BaseVolume (espelha o comportamento de MQL onde os modos 0 e 1 são idênticos).
2
Modelo de raiz quadrada – usa 0.1 * sqrt(balance / 1000) * MoneyManagementFactor. O valor atual do portfólio é usado quando disponível.
3
Modelo de risco patrimonial – calcula equity / price / 1000 * MoneyManagementRiskPercent / 100, aproximando a fórmula AccountEquity/Close[0] de MetaTrader.
Cada resultado é normalizado usando a etapa de volume do instrumento e o volume mínimo/máximo de troca.
Parâmetros
Nome
Descrição
CandleType
Série de velas usadas para decisões. O padrão é velas de 5 minutos.
StartHour / EndHour
Janela de negociação em horas (0–23).
ClosingHour
Hora em que qualquer posição aberta é fechada.
TimeZoneShift
Deslocamento de fuso horário informativo mantido para compatibilidade.
BaseVolume
Tamanho base do lote antes dos ajustes de gestão de dinheiro.
MaxPositions
Máximas posições simultâneas (padrão = 1).
TakeProfit1, TakeProfit2, TakeProfit3
Distâncias, em pontos, das três metas de lucro.
BreakEvenTrigger
Perda, em pontos, que aciona a saída de resgate do ponto de equilíbrio.
StopLoss0, StopLoss1, StopLoss2
Limites adversos/lucrativos controlando a lógica de parada de proteção.
Ratio1, Ratio2, Ratio3
Porcentagens da posição atual fechada em cada alvo.
MoneyManagementType
Modo de gerenciamento de dinheiro (0–3).
MoneyManagementFactor
Multiplicador para o modelo de raiz quadrada.
MoneyManagementRiskPercent
Percentual de risco para o modelo de ações.
PointMultiplier
Multiplicador aplicado à etapa de preço do instrumento ao converter pontos em compensações de preço reais.
Notas de uso
Escolha um tipo de vela que corresponda à granularidade dos dados disponíveis no local selecionado. A série padrão de 5 minutos fornece um equilíbrio entre capacidade de resposta e filtragem de ruído.
Ao converter distâncias baseadas em pontos em preços reais, a estratégia multiplica PriceStep * PointMultiplier. Ajuste o multiplicador se o corretor definir pontos de forma diferente do ambiente MetaTrader original.
A lógica de ponto de equilíbrio e trailing requer velas concluídas, portanto o comportamento intrabarra pode diferir ligeiramente da execução MetaTrader baseada em tick. O README destaca essa aproximação para que possa ser considerada durante o teste.
TimeZoneShift é preservado para documentação. Os próprios horários de negociação devem ser configurados usando StartHour, EndHour e ClosingHour.
Primeiros passos
Adicione a estratégia ao seu projeto StockSharp ou execute-a dentro do Designer/Runner.
Configure a série de velas (CandleType) e o horário de negociação do instrumento que você pretende negociar.
Ajuste os limites e proporções baseados em pontos de acordo com a volatilidade do instrumento.
Selecione um modo de gerenciamento de dinheiro e defina os parâmetros correspondentes (BaseVolume, MoneyManagementFactor, MoneyManagementRiskPercent).
Execute primeiro a estratégia na negociação de papel para validar se o comportamento corresponde às suas expectativas antes de usá-la com capital real.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Early Top Prorate V1: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class EarlyTopProrateV1Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public EarlyTopProrateV1Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 25)
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 1.5m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 1.5m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 55) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 45) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
class early_top_prorate_v1_strategy(Strategy):
def __init__(self):
super(early_top_prorate_v1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 10) \
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 25) \
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(early_top_prorate_v1_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fv < sv and self._prev_fast >= self._prev_slow) or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (fv > sv and self._prev_fast <= self._prev_slow) or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 55:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv < 45:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(early_top_prorate_v1_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return early_top_prorate_v1_strategy()