Ver no GitHub

Estratégia STO M5xM15xM30

Visão geral

Esta estratégia é uma conversão C# fiel do consultor especialista MetaTrader 4 "STO_m5xm15xm30". Ele usa três osciladores estocásticos calculados nos prazos M5, M15 e M30 para identificar mudanças de momento sincronizadas. A implementação StockSharp mantém a estrutura original de entrada/saída, substitui o gerenciamento manual de pedidos pelo API de alto nível e expõe cada constante de chave como um StrategyParam configurável.

Lógica de negociação

  1. Confirmação de vários prazos
    • O estocástico primário (padrão M5) deve mostrar um cruzamento de alta (%K cruzamentos acima de %D).
    • Os valores estocásticos intermediários (padrão M15) e lentos (padrão M30) já devem ser de alta (%K acima de %D).
    • Uma configuração de baixa requer condições espelhadas (%K abaixo de %D).
  2. Filtro de mudança
    • O estocástico primário também verifica o estado das velas ShiftBars anteriormente. Um sinal de compra exige que o histórico %K esteja abaixo de %D, garantindo um novo cruzamento. Os sinais de venda exigem o oposto.
  3. Filtro de impulso de preço
    • O último fechamento deve ser superior (para compras) ou inferior (para vendas) do que o fechamento anterior da vela concluída. Isso reflete a regra Close[0] > Close[1] do script MT4.
  4. Regras de entrada
    • Se nenhuma posição estiver aberta e os critérios de alta forem atendidos, a estratégia abre uma ordem longa de mercado com o TradeVolume configurado.
    • Se existir uma posição curta quando um sinal de alta chegar, ela será achatada primeiro e uma posição longa será aberta depois. O inverso é verdadeiro para sinais de baixa.
  5. Regras de saída
    • Um estocástico M5 dedicado com período ExitKPeriod verifica a vela anterior (shift = 1). Uma posição longa é fechada quando %K cai abaixo de %D; uma venda é fechada quando %K sobe acima de %D.
    • Depois que uma saída é acionada, a estratégia pula a reentrada imediata na mesma vela, replicando o comportamento do loop de pedidos MT4.

Indicadores e assinaturas de dados

  • Velas primárias: período padrão de 5 minutos (CandleType).
  • Velas de confirmação intermediárias: período padrão de 15 minutos (MiddleCandleType).
  • Velas de confirmação lentas: período padrão de 30 minutos (SlowCandleType).
  • Osciladores Stochastic: todos usam suavização %K = 3 e suavização %D = 3, correspondendo aos parâmetros originais.

Parâmetros

Nome Padrão Descrição
CandleType Velas de 5 minutos Prazo de trabalho para entradas e saídas.
MiddleCandleType Velas de 15 minutos Prazo de confirmação nº 1.
SlowCandleType Velas de 30 minutos Prazo de confirmação nº 2.
FastKPeriod 5 Período %K para o estocástico primário.
MiddleKPeriod 5 Período %K para o estocástico médio.
SlowKPeriod 5 Período %K para o estocástico lento.
ExitKPeriod 5 Período %K para o estocástico de saída que opera na barra anterior.
ShiftBars 3 Número de barras entre o cruzamento de referência e a barra atual.
TakeProfitPoints 30 Distância protetora de take-profit (pontos).
StopLossPoints 10 Distância protetora de stop-loss (pontos).
TradeVolume 0,1 Volume de pedidos usado para novas entradas.

Todos os parâmetros são expostos por meio de StrategyParam<T>, disponibilizando-os para otimização dentro do StockSharp Designer.

Gestão de risco

StartProtection() traduz as entradas MT4 TP e SL em StockSharp ordens de proteção. Ambos podem ser desabilitados definindo o parâmetro correspondente como zero.

Notas de implementação

  • Os valores dos indicadores são obtidos exclusivamente por meio do SubscribeCandles(...).BindEx(...), obedecendo às diretrizes de alto nível do API e evitando coletas manuais de indicadores.
  • O auxiliar StochasticShiftBuffer imita o argumento shift do MT4 sem chamar GetValue, mantendo apenas o histórico de barras necessário.
  • O processamento de entrada acontece uma vez por vela concluída. A avaliação de saída ocorre antes da lógica de entrada, correspondendo à ordem de processamento do EA original.
  • Os comentários embutidos explicam cada etapa do processamento e esclarecem como a lógica MQL é mapeada para o código StockSharp.

Uso

  1. Adicione a estratégia a um esquema StockSharp ou projeto de designer.
  2. Configure o símbolo desejado e garanta que os dados históricos das velas M5, M15 e M30 estejam disponíveis.
  3. Ajuste os parâmetros para se adequar ao mercado-alvo ou ao cenário de otimização.
  4. Inicie a estratégia; Os níveis protetores de stop-loss/take-profit são registrados automaticamente para cada posição.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// STO M5xM15xM30: RSI momentum with dual EMA confirmation.
/// Uses RSI crossover of 50 level with fast/slow EMA alignment.
/// </summary>
public class StoM5xM15xM30Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public StoM5xM15xM30Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 30)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevRsi = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevRsi = 0;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, fast, slow, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal fastVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevRsi == 0 || atrVal <= 0)
		{
			_prevRsi = rsiVal;
			return;
		}

		var close = candle.ClosePrice;

		// Exit management
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (rsiVal < 40 && fastVal < slowVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (rsiVal > 60 && fastVal > slowVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry: RSI crosses 50 with EMA alignment
		if (Position == 0)
		{
			if (_prevRsi <= 50 && rsiVal > 50 && fastVal > slowVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevRsi >= 50 && rsiVal < 50 && fastVal < slowVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevRsi = rsiVal;
	}
}