Esta estratégia replica o consultor especialista MetaTrader 5 MA no Momentum Min Profit.mq5 negociando o cruzamento entre um indicador Momentum e uma média móvel que é calculada no topo da série de momentum. Um sinal de alta aparece quando o momentum ultrapassa sua média, enquanto a barra anterior manteve o momentum abaixo do nível neutro 100. Um sinal de baixa é gerado quando o momentum cruza abaixo da média com a barra anterior acima de 100. A implementação mantém o stop original de capital baseado em dinheiro e a distância fixa de lucro medida em pontos.
Lógica de negociação
Solicite velas definidas por CandleType e insira-as no indicador Momentum.
Suavize o fluxo de impulso com uma média móvel definida por MomentumMovingAverageType e MomentumMovingAveragePeriod.
Detecte cruzamentos usando os valores da barra anterior para evitar sinais duplos.
Recursos opcionais da versão MQL:
Inverta a direção dos sinais gerados.
Feche a exposição oposta antes de entrar em uma nova negociação ou pule totalmente a entrada.
Aplique uma única posição líquida a qualquer momento.
Permitir o acionamento na vela atual (em formação) em vez da barra totalmente fechada.
Aplicar gerenciamento de risco:
Parada de capital em dinheiro: PnL + Position * (close - PositionPrice) deve permanecer acima de StopLossMoney.
Distância de lucro em pontos convertidos por meio de Security.PriceStep.
Parâmetros
Parâmetro
Tipo
Padrão
Descrição
CandleType
DataType
TimeSpan.FromMinutes(5).TimeFrame()
Velas usadas para calcular o momento.
MomentumPeriod
int
14
Período de retrospectiva do indicador Momentum.
MomentumMovingAveragePeriod
int
6
Comprimento da média móvel aplicada ao momentum.
MomentumMovingAverageType
MomentumMovingAverageType
Smoothed
Algoritmo de média móvel (Simples, Exponencial, Suavizada, Ponderada).
ReverseSignals
bool
false
Espelhe MetaTrader sinais de compra/venda.
CloseOpposite
bool
true
Feche a exposição oposta antes de abrir uma nova posição.
OnlyOnePosition
bool
true
Mantenha uma única posição líquida.
UseCurrentCandle
bool
false
Avalie os sinais na vela em formação atual em vez da barra fechada.
StopLossMoney
decimal
15
Rebaixamento de capital permitido antes de fechar todas as negociações.
TakeProfitPoints
decimal
460
Meta de lucro em pontos de instrumento (multiplicado por PriceStep).
MomentumReference
decimal
100
Nível de impulso neutro copiado da estratégia MQL.
Notas de implementação
A média móvel é implementada com LengthIndicator<decimal> instâncias para reutilizar StockSharp classes SMA/EMA/SMMA/WMA integradas.
A fila de pedidos original e os filtros de números mágicos são mapeados para StockSharp posições líquidas, portanto, a estratégia envia um único pedido de mercado dimensionado para achatar o lado oposto e abrir a nova exposição quando CloseOpposite estiver ativado.
A proteção de ações fecha todas as posições via CloseAll() assim que a perda flutuante ultrapassa o limite, correspondendo exatamente ao comportamento MetaTrader de monitorar a comissão, swap e lucro combinados.
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>
/// Momentum crossing its own moving average strategy.
/// Converted from MetaTrader 5 (MA on Momentum Min Profit.mq5).
/// </summary>
public class MaOnMomentumMinProfitStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<int> _maPeriod;
private Momentum _momentum;
private readonly Queue<decimal> _momentumHistory = new();
private decimal? _prevMomentum;
private decimal? _prevSignal;
/// <summary>
/// Candle type used for signal calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Momentum period.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Moving average period applied to momentum values.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Initialize <see cref="MaOnMomentumMinProfitStrategy"/>.
/// </summary>
public MaOnMomentumMinProfitStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for the momentum calculation", "General");
_momentumPeriod = Param(nameof(MomentumPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Lookback for the momentum indicator", "Momentum");
_maPeriod = Param(nameof(MaPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the moving average applied to momentum", "Momentum");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMomentum = null;
_prevSignal = null;
_momentumHistory.Clear();
_momentum = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_momentum, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal momentumValue)
{
if (candle.State != CandleStates.Finished)
return;
_momentumHistory.Enqueue(momentumValue);
while (_momentumHistory.Count > MaPeriod)
_momentumHistory.Dequeue();
if (!_momentum.IsFormed)
return;
if (_momentumHistory.Count < MaPeriod)
{
_prevMomentum = momentumValue;
return;
}
// Calculate SMA of momentum
var sum = 0m;
var history = _momentumHistory.ToArray();
foreach (var v in history)
sum += v;
var signalValue = sum / history.Length;
if (_prevMomentum is null || _prevSignal is null)
{
_prevMomentum = momentumValue;
_prevSignal = signalValue;
return;
}
var crossUp = _prevMomentum < _prevSignal && momentumValue > signalValue;
var crossDown = _prevMomentum > _prevSignal && momentumValue < signalValue;
var volume = Volume;
if (volume <= 0)
volume = 1;
var minSpread = 0.5m;
if (crossUp && Math.Abs(momentumValue - signalValue) >= minSpread)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown && Math.Abs(momentumValue - signalValue) >= minSpread)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevMomentum = momentumValue;
_prevSignal = signalValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevMomentum = null;
_prevSignal = null;
_momentum = null;
_momentumHistory.Clear();
base.OnReseted();
}
}