Ver en GitHub

Estrategia Sea Dragon 2

Sea Dragon 2 es una estrategia de cuadrícula con cobertura que abre posiciones en ambas direcciones y añade nuevas órdenes cuando el precio se mueve un paso definido por el usuario. Los tamaños de las órdenes siguen una secuencia predefinida y los niveles de toma de ganancias se adaptan según el equilibrio entre la exposición larga y corta.

Detalles

  • Órdenes iniciales: Abre tanto una orden de compra como una de venta con el mismo volumen al inicio.
  • Adición de órdenes: Cuando el mercado se mueve Step puntos desde el precio de la última orden, se agrega un nuevo par de órdenes. El lado con mayor exposición recibe la orden más grande según la secuencia.
  • Secuencia de volumen: 1,1,2,3,6,9,14,22,33,48,82,111,122,164,185 escalado por Volume Scale.
  • Toma de ganancias:
    • Cuando los conteos largo y corto son iguales, cada lado usa Take Profit.
    • Si un lado domina, ese lado usa Alt Take Profit mientras el otro mantiene Take Profit.
  • Stop Loss: Cada lado tiene un stop colocado a Max Stop puntos de su precio promedio.
  • Fuente de datos: La estrategia opera en velas completadas de tipo Candle Type.
  • Largo/Corto: Ambos, con cobertura.
  • Salida: Las posiciones se cierran cuando el precio alcanza los niveles de toma de ganancias o stop.
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;

/// <summary>
/// Grid-based mean reversion strategy inspired by Sea Dragon hedging approach.
/// Buys at grid levels below entry, sells at grid levels above, with EMA trend filter.
/// </summary>
public class SeaDragon2Strategy : Strategy
{
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<decimal> _gridPercent;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _entryPrice;
	private decimal _lastGridPrice;

	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public decimal GridPercent { get => _gridPercent.Value; set => _gridPercent.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public SeaDragon2Strategy()
	{
		_emaLength = Param(nameof(EmaLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA period for trend", "General");

		_gridPercent = Param(nameof(GridPercent), 0.5m)
			.SetDisplay("Grid %", "Grid spacing as price percent", "Trading");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_entryPrice = 0;
		_lastGridPrice = 0;
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();

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

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

		var price = candle.ClosePrice;
		var gridStep = price * GridPercent / 100m;

		if (Position == 0)
		{
			// Enter based on EMA direction
			if (price > emaValue)
			{
				BuyMarket();
				_entryPrice = price;
				_lastGridPrice = price;
			}
			else if (price < emaValue)
			{
				SellMarket();
				_entryPrice = price;
				_lastGridPrice = price;
			}
			return;
		}

		if (_lastGridPrice == 0)
			_lastGridPrice = price;

		// Grid logic: add to position or take profit
		if (Position > 0)
		{
			// Take profit if price moves up by grid step from entry
			if (price >= _entryPrice + gridStep * 2)
			{
				SellMarket();
				_entryPrice = 0;
				_lastGridPrice = 0;
			}
			// Stop loss if too far below
			else if (price <= _entryPrice - gridStep * 4)
			{
				SellMarket();
				_entryPrice = 0;
				_lastGridPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (price <= _entryPrice - gridStep * 2)
			{
				BuyMarket();
				_entryPrice = 0;
				_lastGridPrice = 0;
			}
			else if (price >= _entryPrice + gridStep * 4)
			{
				BuyMarket();
				_entryPrice = 0;
				_lastGridPrice = 0;
			}
		}
	}
}