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;
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class sea_dragon_2_strategy(Strategy):
def __init__(self):
super(sea_dragon_2_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 20).SetGreaterThanZero().SetDisplay("EMA Length", "EMA period for trend", "General")
self._grid_percent = self.Param("GridPercent", 0.5).SetDisplay("Grid %", "Grid spacing as price percent", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle Type", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(sea_dragon_2_strategy, self).OnReseted()
self._entry_price = 0
self._last_grid_price = 0
def OnStarted2(self, time):
super(sea_dragon_2_strategy, self).OnStarted2(time)
self._entry_price = 0
self._last_grid_price = 0
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
grid_step = price * self._grid_percent.Value / 100.0
if self.Position == 0:
if price > ema_val:
self.BuyMarket()
self._entry_price = price
self._last_grid_price = price
elif price < ema_val:
self.SellMarket()
self._entry_price = price
self._last_grid_price = price
return
if self._last_grid_price == 0:
self._last_grid_price = price
if self.Position > 0:
if price >= self._entry_price + grid_step * 2:
self.SellMarket()
self._entry_price = 0
self._last_grid_price = 0
elif price <= self._entry_price - grid_step * 4:
self.SellMarket()
self._entry_price = 0
self._last_grid_price = 0
elif self.Position < 0:
if price <= self._entry_price - grid_step * 2:
self.BuyMarket()
self._entry_price = 0
self._last_grid_price = 0
elif price >= self._entry_price + grid_step * 4:
self.BuyMarket()
self._entry_price = 0
self._last_grid_price = 0
def CreateClone(self):
return sea_dragon_2_strategy()