Ver no GitHub

Estratégia de Exp Cronex AO

Esta estratégia porta o consultor especialista do MetaTrader Exp_CronexAO para a API de alto nível do StockSharp. O robô original opera cruzamentos entre as duas linhas do Cronex Awesome Oscillator (AO). A versão StockSharp subscreve uma série de velas configurável, calcula o AO, suaviza-o duas vezes com médias móveis para reproduzir as linhas Cronex, e abre ou fecha posições quando a linha rápida cruza a linha lenta.

Lógica de trading

  1. Construir o Awesome Oscillator a partir das velas selecionadas.
  2. Suavizar o oscilador duas vezes com médias móveis simples. A primeira suavização cria a linha Cronex "rápida", a segunda suavização produz a linha "sinal".
  3. Olhar SignalBar velas concluídas para trás e comparar as linhas Cronex nessa barra e na anterior.
  4. Um sinal de compra aparece quando a linha rápida está acima da linha lenta e fez um cruzamento ascendente na barra de retrocesso. A estratégia opcionalmente fecha qualquer posição vendida e, se permitido, abre uma ordem de compra a mercado.
  5. Um sinal de venda espelha a regra anterior: a linha rápida deve estar abaixo da linha lenta e deve ter cruzado para baixo na barra de retrocesso. A estratégia opcionalmente fecha qualquer posição comprada e, se permitido, abre uma ordem de venda a mercado.
  6. Os níveis de stop-loss e take-profit, expressos em pontos do instrumento, são anexados à posição resultante sempre que uma nova operação é aberta.

Apenas uma posição líquida é mantida. Quando a direção muda, a estratégia combina o volume necessário para fechar a posição oposta com o novo volume de trade para emular o modo de netting do MetaTrader.

Parâmetros

Parâmetro Descrição
CandleType Tipo de dados das velas usadas para os cálculos de Cronex AO. O padrão é um período de 8 horas.
FastPeriod Comprimento da primeira suavização aplicada ao Awesome Oscillator.
SlowPeriod Comprimento da segunda suavização aplicada à linha rápida.
SignalBar Número de barras concluídas para trás que devem conter o sinal de cruzamento. A estratégia também inspeciona a barra seguinte para confirmar a direção.
BuyOpenEnabled / SellOpenEnabled Habilitar ou desabilitar a abertura de posições compradas ou vendidas.
BuyCloseEnabled / SellCloseEnabled Controlar se posições opostas podem ser fechadas quando um sinal inverso aparece.
TakeProfit Alvo de lucro em pontos, aplicado após cada nova entrada se maior que zero.
StopLoss Stop protetor em pontos, também aplicado após cada nova entrada se maior que zero.

Gestão de risco

As distâncias de stop-loss e take-profit imitam as entradas baseadas em pontos da versão MetaTrader. Elas são recalculadas cada vez que uma nova operação é enviada para que as ordens protetoras sempre correspondam ao tamanho atual da posição líquida.

Diferenças da versão MetaTrader

  • A implementação StockSharp usa médias móveis simples para ambos os estágios de suavização Cronex. A implementação XMA original permite vários métodos de suavização, mas a configuração padrão corresponde à média simples que é reproduzida aqui.
  • Rotinas de deslizamento e gestão monetária da biblioteca TradeAlgorithms não são replicadas. O dimensionamento de posição é controlado via a propriedade padrão Volume.
  • A execução de operações depende do comportamento de netting do StockSharp. Quando a direção é revertida, uma única ordem a mercado é emitida com volume suficiente para achatar e inverter a posição em uma etapa, refletindo a lógica de conta de netting do MT5.
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 ExpCronexAOStrategy : 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 ExpCronexAOStrategy()
	{
		_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;
	}
}