Ver no GitHub

Estratégia semanal do corredor de recuperação

Visão geral

A estratégia Weekly Rebound Corridor replica o comportamento do MetaTrader 4 Expert Advisor 2_Otkat_Sys_v1_1. O sistema procura uma forte lacuna entre o fechamento da sessão anterior e o preço de abertura que ocorreu 24 velas antes. Quando o gap detectado excede um limite de corredor configurável e é o dia de negociação da semana especificado, a estratégia entra no mercado durante os primeiros minutos do novo dia de negociação. São aplicados níveis protetores de stop-loss e take-profit, e todas as posições abertas são fechadas à força pouco antes do término da sessão de negociação.

Lógica de negociação

  1. Preparação de dados
    • Usa velas de minutos por padrão. O tipo de vela é configurável para acomodar outros tamanhos de barra.
    • Acompanha o fechamento da vela anterior e mantém um buffer circular que retorna o preço de abertura observado há 24 velas.
  2. Geração de sinal
    • No dia de negociação da semana especificado (formato MetaTrader: 0 = Sunday, 6 = Saturday), a estratégia avalia velas concluídas cujo horário local está entre 00h00 e 00h03.
    • Calcula a diferença entre a abertura histórica (24 velas atrás) e a última vela fechada. Se a diferença exceder o limite do corredor configurado, uma ordem de mercado será enviada:
      • Configuração longa: a abertura histórica menos o fechamento anterior é maior que o limite do corredor.
      • Configuração curta: o fechamento anterior menos a abertura histórica é maior que o limite do corredor.
    • Cada dia de negociação pode acionar no máximo uma entrada.
  3. Gestão Comercial
    • Os níveis de stop-loss e take-profit são expressos em pontos. O tamanho do tick do instrumento converte os valores dos pontos em compensações de preços reais.
    • As negociações longas adicionam o deslocamento MT4 original de três pontos extras à distância de obtenção de lucro.
    • A estratégia monitora continuamente os máximos e mínimos das velas para detectar acertos de stop-loss ou take-profit e fecha a posição aberta com uma ordem de mercado quando acionada.
    • Qualquer posição aberta restante será fechada após as 22h45, horário local do câmbio, para emular a regra fixa de final do dia do Expert Advisor original.

Parâmetros

Nome Descrição Padrão
TakeProfitPoints Distância de lucro em pontos. As negociações longas adicionam três pontos adicionais, conforme definido no script MT4. 5
StopLossPoints Distância de stop-loss em pontos. 49
TradeVolume Volume submetido com ordens de mercado. O valor é alinhado automaticamente com o passo de volume do instrumento. 1
CorridorPoints Gap mínimo exigido entre a abertura histórica e o fechamento mais recente. 10
TradeDayOfWeek Dia de negociação em numeração MetaTrader (0 = Sunday6 = Saturday). 5 (sexta-feira)
CandleType Tipo de dados Candle usado para análise. 1 minute

Notas

  • A estratégia opera exclusivamente em velas concluídas para alinhar com as diretrizes do projeto.
  • Certifique-se de que o instrumento selecionado forneça dados históricos suficientes para construir o buffer de 24 velas antes de esperar entradas.
  • Os parâmetros baseados em volume e pontos devem ser ajustados para corresponder à especificação do instrumento (tamanho do tick, passo do lote, cronograma de negociação).
using System;

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

namespace StockSharp.Samples.Strategies;

public class WeeklyReboundCorridorStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevRsi;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public WeeklyReboundCorridorStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI lookback", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA filter", "Indicators");
		_oversold = Param(nameof(Oversold), 25m).SetDisplay("Oversold", "RSI oversold level", "Levels");
		_overbought = Param(nameof(Overbought), 75m).SetDisplay("Overbought", "RSI overbought level", "Levels");
		_cooldownCandles = Param(nameof(CooldownCandles), 50).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevRsi = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ema)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!_hasPrev) { _prevRsi = rsi; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevRsi = rsi;
			return;
		}

		var oversold = Oversold;
		var overbought = Overbought;

		if (_prevRsi >= oversold && rsi < oversold && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevRsi <= overbought && rsi > overbought && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevRsi = rsi;
	}
}