Origem: MetaTrader 4 consultores especialistas et4_MTC_v1.mq4 da coleção GlobeInvestFund.
Objetivo: Fornecer um modelo nativo StockSharp que espelhe os auxiliares de gerenciamento de dinheiro e salvaguardas de tempo do consultor original, deixando a lógica de entrada/saída comercial aberta para desenvolvimento adicional.
Estilo de negociação: Estratégia esqueleto – nenhuma entrada automática é gerada por padrão. A classe se concentra em impor restrições de tempo e replicar a interface de parâmetros do script MQL4 para que possa servir como base para regras personalizadas.
Recursos principais
Paridade de parâmetros
Expõe propriedades TakeProfit, StopLoss, Slippage, Lots e EnableLogging que mapeiam um a um com as variáveis externas do especialista.
Adiciona TradeCooldown para descrever o atraso de 30 segundos codificado entre as operações no código-fonte.
Publica o contexto de dados do gráfico por meio de CandleType para emular o comportamento do "período atual" dos gráficos MetaTrader.
Dimensionamento de posição baseado em equilíbrio
Suporta entradas de lote negativo (padrão do script original) para derivar o volume do pedido do saldo da conta: floor((balance / 1000 * |Lots|) / 10) / 10, com um mínimo de 0,1 lote.
Aplicação do período de espera comercial
Bloqueia qualquer outra tentativa de negociação até que TradeCooldown decorra após a atividade de pedido mais recente (registro, modificação, cancelamento ou negociação preenchida). Isso reflete a proteção CurTime() - LastTradeTime < 30 em start().
Detecção de Nova Vela
Mantém a semântica CheckLevels marcando IsNewCandle por meio de uma comparação de tempo entre velas concluídas subsequentes. Embora o sinalizador seja interno, os ganchos em OpenPosition, ManagePosition e ClosePosition podem usá-lo quando a lógica personalizada é adicionada.
Uso de StockSharp API de alto nível
Utiliza SubscribeCandles().Bind(...) para entrega de dados.
Aplica-se StartProtection() uma vez na inicialização, seguindo as práticas recomendadas da estrutura.
Não aloca coleções personalizadas nem solicita explicitamente o histórico de indicadores, alinhando-se com as diretrizes de todo o projeto.
Referência de parâmetro
Propriedade
Padrão
Otimizável
Descrição
TakeProfit
150
✔️
Distância alvo em pontos (espaço reservado para regras de saída personalizadas).
Lots
-10
✔️
Lotes fixos quando ≥ 0; dimensionamento proporcional ao equilíbrio quando negativo.
StopLoss
50
✔️
Distância de parada em pontos, pronta para lógica de extensão.
Slippage
3
✖️
Tolerância de execução em pontos; preservados para compatibilidade.
EnableLogging
false
✖️
Imprime mensagens informativas quando o cooldown bloqueia negociações.
TradeCooldown
30 segundos
✖️
Atraso mínimo entre negociações consecutivas.
CandleType
Velas com intervalo de tempo de 1 minuto
✖️
Assinatura de dados de mercado usada para timing de velas.
Fluxo de Execução
Inicialização
Calcula o Volume inicial usando o auxiliar de dimensionamento com reconhecimento de equilíbrio.
Assina o fluxo de velas configurado e inicia mecanismos de proteção.
Na vela fechada
Confirma que a vela terminou antes de prosseguir (equivalente ao fechamento de Time[0] no MT4).
Atualiza o rastreador new-candle (_isNewCandle).
Verifica IsFormedAndOnlineAndAllowTrading() para respeitar o estado do motor.
Aborta se o resfriamento da negociação estiver ativo, registrando o próximo horário disponível quando ativado.
Executa ganchos de espaço reservado (OpenPosition, ManagePosition, ClosePosition), retornando antecipadamente quando qualquer etapa executa uma ação.
Retornos de chamada para pedidos e negociações
OnOrderRegistered, OnOrderChanged, OnOrderCanceled e OnNewMyTrade atualizam _lastTradeTime, garantindo que cada tipo de operação redefina o resfriamento assim como as funções do wrapper (MOrderSend, MOrderModify, etc.) fizeram no código original.
Estendendo o modelo
Implemente a lógica de entrada dentro de OpenPosition (retorne true após enviar pedidos para interromper o processamento adicional na mesma vela).
Insira o comportamento de gerenciamento de parada dentro de ManagePosition usando os parâmetros preservados.
Preencha ClosePosition com regras de saída. O método atualmente retorna false para corresponder ao comportamento inativo do script de origem.
Use _isNewCandle se as regras precisarem ser acionadas uma vez por barra.
Portando Notas
O especialista MQL4 foi enviado sem regras de negociação; apenas rotinas de infraestrutura estavam presentes. Consequentemente, a conversão StockSharp prioriza a paridade de recursos de suporte em vez de adicionar indicadores especulativos.
Todos os comentários são escritos em inglês, obedecendo aos padrões do repositório.
Tabulações são usadas para recuo para corresponder às diretrizes de estilo definidas em AGENTS.md.
A tradução do Python é omitida intencionalmente, de acordo com a solicitação de conversão.
Etapas de uso
Faça referência a Et4MtcV1Strategy em um projeto StockSharp e atribua um Security e um Portfolio antes de começar.
Ajuste Lots ou outros parâmetros por meio das propriedades fornecidas ou vinculações de IU.
Substitua os métodos de espaço reservado ou herde da classe para injetar lógica de negociação concreta.
Execute a estratégia; a proteção de resfriamento garante que não haja operações consecutivas dentro do intervalo especificado.
Teste
Nenhum teste automatizado acompanha este modelo porque a fonte upstream também não possuía regras executáveis. As extensões manuais da estratégia devem introduzir testes relevantes quando o comportamento comercial concreto for implementado.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Et4MtcV1Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMom;
private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Et4MtcV1Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var mom = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, mom, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal mom)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevMom = mom; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevMom = mom;
return;
}
if (close > ema && _prevMom <= 0 && mom > 0 && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 2;
}
else if (close < ema && _prevMom >= 0 && mom < 0 && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 2;
}
_prevMom = mom;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, Momentum
from StockSharp.Algo.Strategies import Strategy
class et4_mtc_v1_strategy(Strategy):
"""
EMA + Momentum crossover strategy.
Buys when price > EMA and momentum crosses above 0.
Sells when price < EMA and momentum crosses below 0.
"""
def __init__(self):
super(et4_mtc_v1_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetDisplay("Momentum", "Momentum period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_mom = 0.0
self._has_prev = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(et4_mtc_v1_strategy, self).OnReseted()
self._prev_mom = 0.0
self._has_prev = False
self._cooldown = 0
def OnStarted2(self, time):
super(et4_mtc_v1_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
mom = Momentum()
mom.Length = self._momentum_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, mom, self._process_candle).Start()
def _process_candle(self, candle, ema_val, mom_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
ema_val = float(ema_val)
mom_val = float(mom_val)
if not self._has_prev:
self._prev_mom = mom_val
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_mom = mom_val
return
if close > ema_val and self._prev_mom <= 0 and mom_val > 0 and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume)
self._cooldown = 2
elif close < ema_val and self._prev_mom >= 0 and mom_val < 0 and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume)
self._cooldown = 2
self._prev_mom = mom_val
def CreateClone(self):
return et4_mtc_v1_strategy()