Ver no GitHub

Estratégia de Stoch Sell

Visão geral

Esta estratégia reproduz o comportamento do expert MetaTrader original stochSell. Ela ouve um único fluxo de velas e aguarda uma confirmação estocástica tripla combinada com um filtro de volatilidade antes de enviar uma ordem de venda a mercado inicial. Imediatamente após a entrada vendida, implanta uma escada de sell stops pendentes para escalar no movimento se o preço continuar caindo.

Lógica de negociação

  • Filtro de volatilidade – um Average True Range (ATR) com comprimento configurável deve permanecer abaixo do limiar especificado.
  • Confirmação estocástica lenta – o oscilador estocástico de período mais longo deve permanecer abaixo do nível de sobrevendido de longo prazo antes de qualquer negociação ser permitida.
  • Confirmação de cruzamento – tanto o oscilador estocástico médio quanto o rápido devem cruzar para baixo através do gatilho de sobrevendido durante a mesma vela finalizada.
  • Verificação de posição – novas entradas são colocadas apenas quando a estratégia não tem ordens ativas e a posição está zerada.

Uma vez que todas as condições são atendidas, a estratégia envia uma ordem de venda a mercado usando o volume configurado e imediatamente programa um conjunto de ordens de sell stop de acordo com as configurações de grade. Ordens pendentes são opcionais e podem ser desativadas definindo a contagem de ordens de grade como zero.

Regras de saída

  • Alvo de lucro – quando a cesta vendida acumula o lucro desejado em pips (calculado a partir do preço de entrada ponderado por volume), a estratégia recompra toda a posição e remove cada ordem pendente restante.
  • Stop manual – ordens de grade respeitam um tempo de vida configurável. Quando uma ordem de stop expira sem ser executada, é cancelada automaticamente.
  • Fechamento completo – qualquer operação de compra que retorne a posição a zero limpa as estatísticas de entrada internas e cancela a grade pendente.

Gestão de grade

  • Ordens pendentes são colocadas abaixo do preço de referência usando o offset inicial e o passo expressos em pips.
  • Cada ordem pendente usa o multiplicador de volume da grade, permitindo que o tamanho da cesta difira da entrada a mercado inicial.
  • O vencimento (em minutos) é aplicado a cada ordem pendente; zero desativa o tempo limite.

Parâmetros

Nome Descrição
CandleType Período principal para cada indicador e decisão de negociação.
AtrPeriod / AtrThreshold Filtro de volatilidade que controla quando a estratégia pode negociar.
FastKPeriod, FastDPeriod, FastSlowing Configuração do oscilador estocástico rápido.
MediumKPeriod, MediumDPeriod, MediumSlowing Configuração do oscilador estocástico médio.
SlowKPeriod, SlowDPeriod, SlowSlowing Configuração do oscilador estocástico lento.
OversoldLevel Nível que os valores estocásticos rápido e médio devem cruzar para baixo.
LongTermOversoldLevel Limite superior para o estocástico lento durante a entrada.
ProfitTargetPips Lucro líquido em pips necessário para fechar a cesta vendida.
GridOrdersCount Número de sell stops pendentes criados após a entrada.
GridStartOffsetPips Offset em pips entre o preço de entrada e a primeira ordem pendente.
GridStepPips Distância em pips entre ordens pendentes consecutivas.
GridVolume Volume aplicado a cada ordem pendente.
GridExpirationMinutes Tempo de vida das ordens pendentes em minutos.
MarketVolume Volume usado para a venda a mercado inicial.

Notas

  • Os valores dos indicadores são processados através da API BindEx de alto nível e apenas velas finalizadas acionam decisões de negociação.
  • A lógica de rastreamento de posição mantém um preço de entrada ponderado por volume para traduzir o alvo de lucro bruto em pips.
  • Para desativar o escalonamento, basta definir a contagem de ordens de grade como zero; a estratégia ainda dependerá da confirmação estocástica e do filtro ATR para operações de disparo único.
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 StochSellStrategy : 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 StochSellStrategy()
	{
		_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;
	}
}