Ver no GitHub

Estratégia de canal equidistante

Visão geral

A estratégia de canal equidistante porta o expert advisor MQL4 original "Equidistant Channel" para a API de alto nível do StockSharp. A estratégia analisa cruzamentos da linha MACD e gerencia posições existentes por toques nas Bollinger Bands, lógica de breakeven e alvos de trailing baseados em dinheiro.

Quando a linha MACD cruza acima do seu sinal, a estratégia abre posições compradas; quando cruza abaixo do sinal, abre posições vendidas. Enquanto uma operação está ativa, a estratégia observa saídas quando o preço alcança Bollinger Bands, quando o lucro flutuante atinge alvos monetários ou percentuais configuráveis, ou quando um limite de drawdown trailing é violado. Um modo breakeven espelha a implementação do MetaTrader movendo o stop de proteção quando o lucro excede um número configurável de passos de preço.

Indicadores

  • MACD (12, 26, 9) - gera sinais de entrada em cruzamentos entre a linha MACD e sua linha de sinal.
  • Bollinger Bands (20, 2) - fornecem níveis de saída sempre que o fechamento do candle atinge a banda superior ou inferior.

Gestão da posição

  • Distâncias opcionais de stop loss, take profit e trailing stop expressas em pontos de preço via StartProtection.
  • Lógica de take profit e trailing baseada em dinheiro que acompanha o lucro flutuante usando metadados de preço/tamanho do passo do instrumento.
  • Take profit percentual calculado a partir do valor inicial do portfólio.
  • Modo breakeven que empurra o stop para a entrada mais um offset quando o lucro alcança um gatilho definido.

Parâmetros

Grupo Nome Padrão Descrição
Negociação Volume 1 Volume da ordem para novas entradas.
Geral Tipo de candle 5 minutos Série de candles usada para os cálculos.
Indicadores MACD rápido 12 Comprimento da EMA rápida para MACD.
Indicadores MACD lento 26 Comprimento da EMA lenta para MACD.
Indicadores Sinal MACD 9 Comprimento da linha de sinal para MACD.
Indicadores Período BB 20 Período de retrospectiva das Bollinger Bands.
Indicadores Desvio BB 2 Largura das Bollinger Bands em desvios padrão.
Risco Stop Loss 20 Distância do stop loss em pontos de preço.
Risco Take Profit 50 Distância do take profit em pontos de preço.
Risco Trailing Stop 40 Distância do trailing stop em pontos de preço.
Risco Usar TP (dinheiro) false Fecha quando o lucro flutuante atinge um alvo monetário absoluto.
Risco Dinheiro TP 10 Valor absoluto de take profit na moeda da conta.
Risco Usar TP (%) false Fecha quando o lucro flutuante atinge um percentual do capital inicial.
Risco Percentual TP 10 Percentual do capital inicial para o take profit percentual.
Risco Habilitar trailing true Habilita a lógica de trailing sobre o lucro flutuante.
Risco Ativar trailing 40 Nível de lucro (moeda) que arma a lógica de trailing.
Risco Passo trailing 10 Drawdown máximo permitido a partir do pico de lucro (moeda).
Risco Usar stop BB true Habilita saídas quando o preço toca Bollinger Bands.
Risco Usar breakeven true Habilita o comportamento breakeven.
Risco Gatilho breakeven 10 Lucro (passos de preço) necessário para armar o stop breakeven.
Risco Offset breakeven 5 Offset (passos de preço) aplicado ao nível breakeven.

Observações

  • A estratégia funciona com um único instrumento que forneça metadados válidos de PriceStep e StepPrice, para que os cálculos monetários sejam precisos.
  • O módulo de trailing de lucro segue o comportamento do MetaTrader: quando o lucro flutuante excede o limite de ativação, a estratégia registra o máximo corrente e fecha a operação quando o drawdown excede o passo trailing configurado.
  • A lógica breakeven espelha o EA original usando gatilhos e offsets baseados em passos de preço.
  • Todos os comentários dentro do código da estratégia são escritos em inglês, conforme exigido pelas diretrizes do projeto.
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 EquidistantChannelStrategy : 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 EquidistantChannelStrategy()
	{
		_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;
	}
}