Ver no GitHub

Estratégia de Plateau

A estratégia Plateau é uma conversão do consultor especialista original de MetaTrader 5. Combina um par de médias móvias ponderadas lineares com Bollinger Bands para detectar possíveis reversões quando o preço negocia próximo à banda inferior.

Ideia de trading

  • Calcular médias móvias rápidas e lentas usando o método de suavização selecionado e a fonte de preço.
  • Construir Bollinger Bands em torno da mesma série de preços.
  • Quando a média rápida cruza acima da lenta enquanto a vela anterior fechou abaixo da banda inferior, abrir uma posição comprada.
  • Quando a média rápida cruza abaixo da lenta enquanto a vela anterior fechou acima da banda inferior, abrir uma posição vendida.
  • Opcionalmente inverter os sinais se o interruptor Reverse estiver habilitado.

Gestão de ordens

  • As posições podem ser dimensionadas com um lote fixo ou arriscando uma porcentagem do valor do portfólio por operação.
  • Os níveis de stop-loss e take-profit são expressos em pips e anexados imediatamente após o preenchimento da ordem de mercado.
  • Um trailing stop pode ser ativado quando tanto a distância de trailing quanto o passo são positivos.
  • Quando Close Opposite está habilitado, a estratégia fecha automaticamente a posição oposta antes de entrar em uma nova operação.

Parâmetros

Parâmetro Descrição
Stop Loss Distância do stop-loss em pips.
Take Profit Distância do take-profit em pips.
Trailing Stop Distância do trailing stop em pips.
Trailing Step Incremento mínimo (em pips) necessário para mover o trailing stop.
Money Mode Escolher entre lote fixo e dimensionamento por porcentagem de risco.
Lot / Risk O tamanho de lote fixo ou a porcentagem de risco dependendo do modo de dinheiro selecionado.
Fast MA / Slow MA Períodos para o par de médias móvias.
MA Shift Deslocamento horizontal aplicado a ambas as médias móvias.
MA Method Algoritmo de suavização da média móvia.
MA Price Fonte de preço usada para os cálculos de média móvia.
Bands Period Período de média para Bollinger Bands.
Bands Shift Deslocamento horizontal aplicado aos valores de Bollinger Bands.
Bands Deviation Multiplicador de desvio padrão para Bollinger Bands.
Bands Price Fonte de preço usada para os cálculos de Bollinger Bands.
Reverse Inverter a lógica de sinal comprado e vendido.
Close Opposite Fechar uma posição existente na direção oposta antes de abrir uma nova.
Verbose Log Imprimir informações detalhadas de execução no log.
Candle Type Série de dados de velas usada para cálculos de indicadores.

Notas

  • O tamanho do pip é automaticamente ajustado para instrumentos com três ou cinco dígitos decimais para corresponder ao comportamento do especialista original.
  • Quando o trailing stop está habilitado, o passo de trailing deve ser estritamente positivo; caso contrário, a estratégia lança um erro na inicialização.
  • O dimensionamento de posição baseado em risco requer tanto uma distância de stop-loss válida quanto dados de valoração do portfólio. Quando não disponíveis, a estratégia recorre ao volume padrão.
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 PlateauStrategy : 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 PlateauStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 15).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 60).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;
	}
}