Ver no GitHub

3207 – Estratégia de MA Trend

Visão geral

A Estratégia de MA Trend replica o especialista MetaTrader MA Trend.mq5 usando a API de alto nível do StockSharp. O bot segue uma única média móvel ponderada linear com um deslocamento configurável para frente. Quando o preço de fechamento sobe acima da média deslocada, a estratégia vai comprada, enquanto uma queda abaixo da média abre posições vendidas. Regras opcionais de stop-loss, take-profit e trailing stop replicam os controles de risco da implementação MQL original.

Lógica de trading

  1. Subscrever ao tipo de vela configurado (padrão: período de 1 minuto) e calcular uma média móvel usando o método selecionado e a fonte de preço.
  2. Deslocar o valor da média móvel para frente pelo número solicitado de velas concluídas antes de compará-lo com o fechamento mais recente.
  3. Gerar sinais:
    • Comprado – preço de fechamento acima da MA deslocada (invertido quando ReverseSignals está habilitado).
    • Vendido – preço de fechamento abaixo da MA deslocada (invertido quando ReverseSignals está habilitado).
  4. Aplicar opções de gerenciamento de posição:
    • Fechar a exposição oposta antes de abrir uma negociação quando CloseOpposite é true.
    • Bloquear novas entradas se OnlyOnePosition estiver habilitado e já existir uma posição.
  5. Gerenciar saídas com distâncias de stop-loss, take-profit e trailing stop expressas em pips. A lógica de trailing requer que o preço se mova por TrailingStopPips + TrailingStepPips antes de ajustar o stop, assim como o especialista MQL.

Parâmetros

Nome Tipo Padrão Descrição
OrderVolume decimal 0.1 Tamanho da ordem em lotes/contratos.
StopLossPips int 50 Distância do stop-loss em pips. Zero desabilita o stop fixo.
TakeProfitPips int 140 Distância do take-profit em pips. Zero desabilita o alvo.
TrailingStopPips int 15 Distância do trailing stop. Definir como zero para desabilitar o trailing.
TrailingStepPips int 5 Pips adicionais necessários antes de mover o trailing stop. Deve permanecer positivo quando TrailingStopPips for maior que zero.
MaPeriod int 12 Comprimento da média móvel.
MaShift int 3 Número de barras concluídas usadas para deslocar a média móvel para frente.
MaMethod MovingAverageKind Weighted Modo de cálculo da média móvel (Simple, Exponential, Smoothed, Weighted).
AppliedPrice AppliedPriceMode Weighted Preço de vela usado como entrada do indicador (Close, Open, High, Low, Median, Typical, Weighted).
OnlyOnePosition bool false Restringir a estratégia a uma única posição aberta.
ReverseSignals bool false Trocar as direções de sinal comprado/vendido.
CloseOpposite bool false Fechar a exposição oposta antes de entrar em uma nova posição.
CandleType DataType 1 minute Tipo de vela/período fornecido ao indicador.

Notas

  • O tamanho do pip se adapta automaticamente a instrumentos com preços de 3/5 decimais para coincidir com o comportamento original do MetaTrader.
  • A validação do trailing stop reproduz a verificação MQL: se TrailingStopPips > 0 e TrailingStepPips <= 0, a estratégia lança uma exceção durante o início.
  • Todas as atualizações de indicadores e decisões de ordens usam apenas velas concluídas, garantindo backtests deterministas.
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;

public class MaTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public MaTrendStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { 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;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}