Ver no GitHub

Estratégia Cs2011

Visão geral

A estratégia Cs2011 é um sistema de reversão traduzido do consultor especialista cs2011.mq5 original. Ele monitora o histograma MACD e a linha de sinal em cada vela finalizada e procura padrões de exaustão em torno da linha zero. A porta C# mantém as principais regras de temporização enquanto as expõe por meio do StockSharp API de alto nível.

Lógica de negociação

  • Reversões de linha zero – quando o valor MACD da barra anterior está acima de zero enquanto a barra anterior estava abaixo de zero, a estratégia emite um sinal curto. A transição oposta (de positivo para negativo) emite um sinal longo. Isso imita as entradas contrárias implementadas no script MQL5.
  • Extremos da linha de sinal – a estratégia armazena as últimas três leituras da linha de sinal. Um máximo local enquanto MACD permaneceu negativo aciona uma entrada curta adicional; um mínimo local enquanto MACD permanece positivo desencadeia uma entrada longa. Isso reproduz as verificações de padrão baseadas em Sig[0], Sig[1] e Sig[2] na fonte EA.
  • Os sinais são avaliados apenas em velas finalizadas fornecidas por SubscribeCandles, portanto, os dados parciais são ignorados.

Manipulação de posição

  • A estratégia visa um tamanho de posição absoluto fixo (TargetVolume). Quando chega um sinal de alta, ele compra contratos suficientes para alcançar +TargetVolume. Os sinais de baixa fazem o mesmo para -TargetVolume. A exposição existente na mesma direção é respeitada – nenhum pedido adicional é feito se a meta já tiver sido alcançada.
  • StartProtection reflete as configurações originais de take-profit e stop-loss. As distâncias dos pontos são convertidas em valores UnitTypes.Point e passadas para o módulo de risco integrado. Deixar qualquer um dos valores em 0 desativa a barreira correspondente.
  • Auxiliares de alto nível (BuyMarket, SellMarket) são usados em vez da estrutura de solicitação de baixo nível da versão MQL.

Parâmetros

Nome Padrão Descrição
TargetVolume 1 lote Tamanho absoluto da posição alcançado após um sinal. Substitui a rotina de dimensionamento de saldo Risk × do EA.
TakeProfitPoints 2200 Distância em faixas de preço para gerenciamento de lucro. 0 desativa o take-profit.
StopLossPoints 0 Distância em pontos de preço para o stop loss. 0 desativa o stop-loss, correspondendo aos padrões de EA.
FastEmaPeriod 30 Comprimento EMA rápido para o núcleo MACD.
SlowEmaPeriod 500 Comprimento EMA lento para MACD.
SignalPeriod 36 Período de suavização da linha de sinal.
CandleType 1 hour período de tempo Fonte de vela usada por SubscribeCandles. Ajuste isso para corresponder ao período do gráfico usado em MetaTrader.

Todos os parâmetros são registrados por meio de Param() para que possam ser otimizados dentro da UI do otimizador StockSharp.

Diferenças da versão MQL5

  • A rotina de gerenciamento de dinheiro (Money_M) dependia de transações históricas e do saldo da conta MetaTrader. As estratégias StockSharp operam em carteiras independentes de corretoras, portanto a porta expõe um parâmetro TargetVolume simples. Os usuários podem conectar seu próprio gerenciamento de dinheiro substituindo o valor do parâmetro ou o método ExecuteSignals.
  • As solicitações de pedidos são simplificadas para ordens de mercado único. Lógica de nova tentativa, desvio baseado em spread e verificações de contexto comercial são gerenciadas pela infraestrutura StockSharp.
  • A estratégia é executada com assinaturas de velas em vez do auxiliar IsNewBar personalizado. Isto garante que apenas velas totalmente formadas sejam processadas.

Notas de uso

  1. Configure o título, o portfólio e o tipo de vela antes de lançar a estratégia.
  2. Ajuste TargetVolume para corresponder ao tamanho nominal do lote desejado.
  3. Opcionalmente, ajuste TakeProfitPoints e StopLossPoints para reproduzir os níveis de proteção do EA original.
  4. Inicie a estratégia – as mensagens de registro registram cada gatilho de negociação junto com a exposição desejada.

O código contém comentários embutidos em inglês que descrevem cada etapa do processo de portabilidade.


namespace StockSharp.Samples.Strategies;

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

/// <summary>
/// MACD based reversal strategy converted from the original cs2011 MetaTrader 5 expert advisor.
/// It reacts to zero line crosses and local extremes of the MACD signal line.
/// </summary>
public class Cs2011Strategy : Strategy
{
	private readonly StrategyParam<decimal> _targetVolume;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _macdPrev1;
	private decimal? _macdPrev2;
	private decimal? _signalPrev1;
	private decimal? _signalPrev2;
	private decimal? _signalPrev3;

	/// <summary>
	/// Target absolute position in lots when a bullish signal appears.
	/// </summary>
	public decimal TargetVolume
	{
		get => _targetVolume.Value;
		set => _targetVolume.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in instrument points.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in instrument points.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Fast EMA length used in MACD calculation.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA length used in MACD calculation.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period of the MACD signal line.
	/// </summary>
	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

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

	/// <summary>
	/// Initialize strategy parameters with defaults close to the original expert.
	/// </summary>
	public Cs2011Strategy()
	{
		_targetVolume = Param(nameof(TargetVolume), 1m)
			.SetDisplay("Target Volume", "Absolute position size targeted on entries", "Risk")
			
			.SetOptimize(0.5m, 3m, 0.5m);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2200)
			.SetDisplay("Take Profit (points)", "Take-profit distance in price points", "Risk")
			
			.SetOptimize(200, 4000, 200);

		_stopLossPoints = Param(nameof(StopLossPoints), 0)
			.SetDisplay("Stop Loss (points)", "Stop-loss distance in price points", "Risk")
			
			.SetOptimize(0, 2000, 200);

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 30)
			.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators")
			
			.SetOptimize(10, 60, 5);

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 50)
			.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators")
			
			.SetOptimize(200, 700, 20);

		_signalPeriod = Param(nameof(SignalPeriod), 36)
			.SetDisplay("Signal Period", "Signal line period for MACD", "Indicators")
			
			.SetOptimize(10, 60, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Source timeframe for MACD", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_macdPrev1 = null;
		_macdPrev2 = null;
		_signalPrev1 = null;
		_signalPrev2 = null;
		_signalPrev3 = null;
	}

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

		Volume = TargetVolume;

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastEmaPeriod },
				LongMa = { Length = SlowEmaPeriod }
			},
			SignalMa = { Length = SignalPeriod }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessCandle)
			.Start();

		var chartArea = CreateChartArea();
		if (chartArea != null)
		{
			DrawCandles(chartArea, subscription);
			DrawIndicator(chartArea, macd);
			DrawOwnTrades(chartArea);
		}

		// removed StartProtection
	}

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

		if (indicatorValue is not IMovingAverageConvergenceDivergenceSignalValue macdValue)
			return;

		if (macdValue.Macd is not decimal macd || macdValue.Signal is not decimal signal)
			return;

		var prevMacd1 = _macdPrev1;
		var prevMacd2 = _macdPrev2;
		var prevSignal1 = _signalPrev1;
		var prevSignal2 = _signalPrev2;
		var prevSignal3 = _signalPrev3;

		var upSignal = false;
		var downSignal = false;

		if (prevMacd1.HasValue && prevMacd2.HasValue)
		{
			if (prevMacd1 > 0m && prevMacd2 < 0m)
				downSignal = true;

			if (prevMacd1 < 0m && prevMacd2 > 0m)
				upSignal = true;
		}

		if (prevMacd2.HasValue && prevSignal1.HasValue && prevSignal2.HasValue && prevSignal3.HasValue)
		{
			if (prevMacd2 < 0m && prevSignal1 < prevSignal2 && prevSignal2 > prevSignal3)
				downSignal = true;

			if (prevMacd2 > 0m && prevSignal1 > prevSignal2 && prevSignal2 < prevSignal3)
				upSignal = true;
		}

		if (upSignal || downSignal)
			ExecuteSignals(upSignal, downSignal);

		_macdPrev2 = _macdPrev1;
		_macdPrev1 = macd;
		_signalPrev3 = _signalPrev2;
		_signalPrev2 = _signalPrev1;
		_signalPrev1 = signal;
	}

	private void ExecuteSignals(bool upSignal, bool downSignal)
	{
		// removed IsFormedAndOnlineAndAllowTrading guard

		if (upSignal)
		{
			var targetPosition = TargetVolume;
			var difference = targetPosition - Position;
			if (difference > 0m)
			{
				BuyMarket(difference);
				LogInfo($"Buy signal executed. Target={targetPosition:F2}, current position after order={Position + difference:F2}");
			}
		}

		if (downSignal)
		{
			var targetPosition = -TargetVolume;
			var difference = targetPosition - Position;
			if (difference < 0m)
			{
				SellMarket(-difference);
				LogInfo($"Sell signal executed. Target={targetPosition:F2}, current position after order={Position - difference:F2}");
			}
		}
	}
}