Ver en GitHub

Estrategia de Exp Cronex AO

Esta estrategia porta el asesor experto de MetaTrader Exp_CronexAO a la API de alto nivel de StockSharp. El robot original opera cruces entre las dos líneas del Cronex Awesome Oscillator (AO). La versión StockSharp se suscribe a una serie de velas configurable, calcula el AO, lo suaviza dos veces con medias móviles para reproducir las líneas Cronex, y abre o cierra posiciones cuando la línea rápida cruza la línea lenta.

Lógica de trading

  1. Construir el Awesome Oscillator desde las velas seleccionadas.
  2. Suavizar el oscilador dos veces con medias móviles simples. El primer suavizado crea la línea Cronex "rápida", el segundo suavizado produce la línea "señal".
  3. Mirar atrás SignalBar velas completadas y comparar las líneas Cronex en esa barra y en la anterior.
  4. Una señal de compra aparece cuando la línea rápida está por encima de la línea lenta y realizó un cruce ascendente en la barra de retroceso. La estrategia opcionalmente cierra cualquier posición corta y, si se permite, abre una orden de compra a mercado.
  5. Una señal de venta refleja la regla anterior: la línea rápida debe estar por debajo de la línea lenta y debe haber cruzado hacia abajo en la barra de retroceso. La estrategia opcionalmente cierra cualquier posición larga y, si se permite, abre una orden de venta a mercado.
  6. Los niveles de stop-loss y take-profit, expresados en puntos del instrumento, se adjuntan a la posición resultante siempre que se abre una nueva operación.

Solo se mantiene una posición neta. Cuando la dirección cambia, la estrategia combina el volumen necesario para cerrar la posición opuesta con el nuevo volumen de trade para emular el modo de netting de MetaTrader.

Parámetros

Parámetro Descripción
CandleType Tipo de datos de las velas utilizadas para los cálculos de Cronex AO. El valor predeterminado es un marco temporal de 8 horas.
FastPeriod Longitud del primer suavizado aplicado al Awesome Oscillator.
SlowPeriod Longitud del segundo suavizado aplicado a la línea rápida.
SignalBar Número de barras completadas hacia atrás que deben contener la señal de cruce. La estrategia también inspecciona la siguiente barra para confirmar la dirección.
BuyOpenEnabled / SellOpenEnabled Habilitar o deshabilitar la apertura de posiciones largas o cortas.
BuyCloseEnabled / SellCloseEnabled Controlar si las posiciones opuestas pueden cerrarse cuando aparece una señal inversa.
TakeProfit Objetivo de beneficio en puntos, aplicado después de cada nueva entrada si es mayor que cero.
StopLoss Stop protector en puntos, también aplicado después de cada nueva entrada si es mayor que cero.

Gestión de riesgo

Las distancias de stop-loss y take-profit imitan las entradas basadas en puntos de la versión MetaTrader. Se recalculan cada vez que se envía una nueva operación para que las órdenes protectoras siempre coincidan con el tamaño de la posición neta actual.

Diferencias de la versión MetaTrader

  • La implementación StockSharp usa medias móviles simples para ambas etapas de suavizado Cronex. La implementación XMA original permite varios métodos de suavizado, pero la configuración predeterminada corresponde a la media simple que se reproduce aquí.
  • Las rutinas de deslizamiento y gestión monetaria de la biblioteca TradeAlgorithms no se replican. El dimensionamiento de posición se controla mediante la propiedad estándar Volume.
  • La ejecución de operaciones depende del comportamiento de netting de StockSharp. Cuando la dirección se revierte, se emite una única orden a mercado con suficiente volumen para aplanar y cambiar la posición en un paso, reflejando la lógica de cuenta de netting de 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;
	}
}