Ver no GitHub

Estratégia Pendente DeMarker

Visão geral

Esta estratégia StockSharp reproduz o comportamento do consultor especialista MetaTrader "DeMarker Pending 2.5". O bot avalia o oscilador DeMarker em um período de tempo configurável e, quando níveis extremos são ultrapassados, coloca uma ordem pendente na direção do rompimento. A ordem pode ser uma ordem de parada ou de limite compensada por um número fixo de pontos. A filtragem opcional da janela de negociação e a expiração automática mantêm as ordens pendentes alinhadas com o comportamento original do especialista.

Lógica de negociação

  • Assine a série de velas selecionada e calcule o indicador DeMarker com período DemarkerPeriod.
  • Detecte cruzamentos dos limites inferior (DemarkerLowerLevel) e superior (DemarkerUpperLevel) usando os valores de vela finalizados atuais e anteriores.
  • Quando o nível inferior for cruzado para cima, coloque uma configuração longa na fila; quando o nível superior for cruzado para baixo, coloque uma configuração curta na fila.
  • Converta configurações em ordens pendentes ao preço Close ± PendingIndentPoints * PriceStep, usando ordens stop no modo breakout ou ordens limitadas para entradas de pullback dependendo de Mode.
  • Anexe níveis de stop-loss e take-profit à ordem pendente compensando o preço de entrada em StopLossPoints e TakeProfitPoints pontos.
  • Cancele ou reutilize pedidos pendentes mais antigos de acordo com ReplacePreviousPending e SinglePendingOnly antes de registrar um novo.
  • Remova pedidos pendentes automaticamente assim que seu tempo de vida de PendingExpirationMinutes expirar.
  • Ignore sinais fora da janela intradiária quando UseTimeWindow estiver ativado. Cada barra é processada apenas uma vez, portanto, no máximo uma nova ordem pendente é criada por barra e direção.

Gerenciamento de pedidos

  • Todas as entradas são criadas como ordens pendentes (BuyStop, SellStop, BuyLimit, SellLimit).
  • Cada ordem pendente carrega seus próprios preços de stop-loss e take-profit para que a posição seja protegida imediatamente após a ativação.
  • Os pedidos pendentes são cancelados no vencimento, quando substituídos por novas configurações ou quando o estado do pedido muda para um status inativo (preenchido, cancelado, rejeitado).

Parâmetros

Parâmetro Descrição
Volume Volume do pedido em lotes.
StopLossPoints Distância entre o preço de entrada e o stop loss em pontos.
TakeProfitPoints Distância entre o preço de entrada e o take-profit em pontos.
PendingIndentPoints Compensação entre o preço de mercado e a ordem pendente.
PendingExpirationMinutes Vida útil de cada ordem pendente em minutos (0 desativa a expiração).
Mode Tipo de ordem pendente (stop para rompimentos ou limite para pullbacks).
SinglePendingOnly Se ativado, impede a colocação de mais de uma ordem pendente ativa.
ReplacePreviousPending Cancela ordens pendentes ativas antes de emitir uma nova.
DemarkerPeriod Período de lookback do oscilador DeMarker.
DemarkerUpperLevel Limite do DeMarker que aciona configurações de venda.
DemarkerLowerLevel Limite do DeMarker que aciona configurações de compra.
CandleType Prazo utilizado para assinatura de velas e avaliação de indicadores.
UseTimeWindow Ativa a filtragem de horário intradiário.
StartTime Início da janela de negociação intradiária.
EndTime Fim da janela de negociação intradiária.

Notas

  • O especialista original inclui gerenciamento sofisticado de dinheiro e rotinas de trailing stop. Esta porta mantém a geração de sinal e o tratamento de ordens pendentes, mas simplifica o dimensionamento da posição para um único parâmetro Volume fixo.
  • StockSharp anexa preços de stop-loss e take-profit no momento do registro do pedido; dependendo da corretora, pode ser necessário verificar se as ordens stop e limit suportam esses níveis de proteção.
  • Certifique-se sempre de que as distâncias baseadas em pontos sejam compatíveis com o PriceStep do símbolo negociado. Defina PendingIndentPoints, StopLossPoints e TakeProfitPoints com valores que atendam aos requisitos de distância mínima do corretor.
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from the "DeMarker Pending 2.5" MetaTrader expert.
/// Uses DeMarker indicator level crossovers to generate buy/sell market signals.
/// Original used pending orders; this version uses market orders.
/// </summary>
public class DeMarkerPendingStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _demarkerPeriod;
	private readonly StrategyParam<decimal> _demarkerUpper;
	private readonly StrategyParam<decimal> _demarkerLower;

	private RelativeStrengthIndex _rsi;
	private decimal? _prevOscillator;

	/// <summary>
	/// Candle type used for signal evaluation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// DeMarker indicator period.
	/// </summary>
	public int DemarkerPeriod
	{
		get => _demarkerPeriod.Value;
		set => _demarkerPeriod.Value = value;
	}

	/// <summary>
	/// Upper DeMarker threshold that triggers sell signals.
	/// </summary>
	public decimal DemarkerUpperLevel
	{
		get => _demarkerUpper.Value;
		set => _demarkerUpper.Value = value;
	}

	/// <summary>
	/// Lower DeMarker threshold that triggers buy signals.
	/// </summary>
	public decimal DemarkerLowerLevel
	{
		get => _demarkerLower.Value;
		set => _demarkerLower.Value = value;
	}

	public DeMarkerPendingStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for signal evaluation", "General");

		_demarkerPeriod = Param(nameof(DemarkerPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("DeMarker Period", "Averaging period for DeMarker indicator", "Indicator");

		_demarkerUpper = Param(nameof(DemarkerUpperLevel), 0.7m)
			.SetDisplay("Upper Level", "DeMarker value that triggers sell setup", "Indicator");

		_demarkerLower = Param(nameof(DemarkerLowerLevel), 0.3m)
			.SetDisplay("Lower Level", "DeMarker value that triggers buy setup", "Indicator");
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevOscillator = null;
		_rsi = new RelativeStrengthIndex { Length = DemarkerPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_rsi, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_rsi.IsFormed)
		{
			_prevOscillator = rsiValue / 100m;
			return;
		}

		if (_prevOscillator is null)
		{
			_prevOscillator = rsiValue / 100m;
			return;
		}

		var oscillatorValue = rsiValue / 100m;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// DeMarker crosses below lower level -> buy signal
		var crossDown = _prevOscillator.Value > DemarkerLowerLevel && oscillatorValue <= DemarkerLowerLevel;
		// DeMarker crosses above upper level -> sell signal
		var crossUp = _prevOscillator.Value < DemarkerUpperLevel && oscillatorValue >= DemarkerUpperLevel;

		if (crossDown)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (crossUp)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevOscillator = oscillatorValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_rsi = null;
		_prevOscillator = null;

		base.OnReseted();
	}
}