Send Close Order es una adaptación del asesor experto MetaTrader 4 de 2009 "SendCloseOrder" de Vladimir Hlystov. El script original dibuja cuatro líneas de tendencia manuales basadas en fractales de Bill Williams y abre o cierra órdenes de mercado cada vez que el precio toca uno de esos niveles proyectados. La versión StockSharp replica la lógica de decisión con una gestión de línea totalmente automatizada y funciona con cualquier serie de velas proporcionada por la plataforma.
Lógica comercial
Detección de fractales: cada vela terminada actualiza una ventana deslizante de cinco barras. Una vez que la ventana está llena, la vela en el medio se compara con las condiciones fractales de Bill Williams. Los máximos y mínimos confirmados se almacenan cronológicamente.
Reconstrucción de la línea de tendencia
Línea de venta conecta los dos últimos fractales ascendentes que están separados por un fractal descendente, formando una pendiente de resistencia.
El cierre #1 es la línea de venta desplazada hacia arriba en 15 pasos de precio (15 × Security.PriceStep) y actúa como el carril de salida largo.
Línea de compra conecta los dos últimos fractales descendentes que están separados por un fractal ascendente, formando una pendiente de soporte.
Cierre #2 es la línea de compra desplazada hacia abajo en 15 pasos de precio y actúa como carril de salida corto.
Evaluación de señal: las cuatro líneas se extrapolan a la marca de tiempo de la vela terminada. Si el precio proyectado se encuentra dentro del rango máximo/bajo de la vela (con una pequeña tolerancia de dos pasos de precio), se activa la acción correspondiente.
Gestión de pedidos
Al tocar Cerrar #1 o Cerrar #2 se cierra inmediatamente toda la posición a través de ClosePosition().
Al tocar la línea Vender o Comprar se abre una orden de mercado con volumen TradeVolume, siempre que la posición absoluta resultante no supere MaxOrders × TradeVolume. Cuando existe una posición opuesta, la orden la compensa primero y luego acumula una nueva entrada, reflejando el comportamiento de las cuentas de cobertura.
Parámetros
Nombre
Predeterminado
Descripción
EnableSellLine
true
Permita operaciones cuando se alcance la línea de resistencia proyectada.
EnableBuyLine
true
Permitir operaciones cuando se alcance la línea de soporte proyectada.
EnableCloseLongLine
true
Permitir cerrar posiciones largas en la línea de resistencia desplazada (Cierre n.º 1).
EnableCloseShortLine
true
Permitir cerrar posiciones cortas en la línea de soporte desplazada (Cierre #2).
MaxOrders
1
Número máximo de entradas apiladas en la dirección actual.
TradeVolume
0.1
Volumen de cada orden de mercado individual.
CandleType
1h período de tiempo
Serie de velas utilizadas para cálculos fractales.
Diferencias versus la versión MetaTrader
El puerto StockSharp recalcula las cuatro líneas cada vez que aparece un nuevo fractal. En MetaTrader el usuario tuvo que eliminar y volver a dibujar líneas de tendencia manualmente.
La ejecución se basa en posiciones netas agregadas; El modelo de cartera predeterminado de StockSharp no admite cestas largas y cortas simultáneas.
La detección táctil utiliza el máximo/mínimo de la vela terminada con una tolerancia de paso de precio en lugar de las cotizaciones instantáneas de oferta y demanda de los ticks.
Los objetos del gráfico (líneas de tendencia y etiquetas) no se crean; la atención se centra en las señales comerciales.
Notas de uso
La estrategia puede ejecutarse en cualquier instrumento que proporcione velas y un PriceStep válido. Cuando Security.PriceStep es cero, el código vuelve a ser 0.0001.
Aumente MaxOrders para emular el comportamiento de apilamiento del EA original. Mantenga TradeVolume alineado con el tamaño de lote del instrumento para evitar el redondeo.
El desplazamiento de línea se fija en el valor histórico de 15 puntos. Ajuste el código fuente si se modifica la entrada MetaTrader.
Solo se proporciona la implementación de C#. Se agregará una traducción de Python por separado si es necesario.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SendCloseOrder: Fractal high/low breakout with EMA filter and ATR stops.
/// </summary>
public class SendCloseOrderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _fractalHigh;
private decimal _fractalLow;
private decimal _prev2High;
private decimal _prev1High;
private decimal _prev2Low;
private decimal _prev1Low;
private int _barCount;
public SendCloseOrderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
// Detect fractal high: prev1High > prev2High and prev1High > current high
if (_barCount > 3 && _prev1High > _prev2High && _prev1High > high)
_fractalHigh = _prev1High;
// Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if (_barCount > 3 && _prev1Low < _prev2Low && _prev1Low < low)
_fractalLow = _prev1Low;
_prev2High = _prev1High;
_prev1High = high;
_prev2Low = _prev1Low;
_prev1Low = low;
if (_fractalHigh == 0 || _fractalLow == 0 || atrVal <= 0)
return;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > _fractalHigh && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < _fractalLow && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class send_close_order_strategy(Strategy):
def __init__(self):
super(send_close_order_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(send_close_order_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
self._bar_count += 1
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
# Detect fractal high: prev1High > prev2High and prev1High > current high
if self._bar_count > 3 and self._prev1_high > self._prev2_high and self._prev1_high > high:
self._fractal_high = self._prev1_high
# Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if self._bar_count > 3 and self._prev1_low < self._prev2_low and self._prev1_low < low:
self._fractal_low = self._prev1_low
self._prev2_high = self._prev1_high
self._prev1_high = high
self._prev2_low = self._prev1_low
self._prev1_low = low
if self._fractal_high == 0 or self._fractal_low == 0 or av <= 0:
return
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > self._fractal_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._fractal_low and close < ev:
self._entry_price = close
self.SellMarket()
def OnReseted(self):
super(send_close_order_strategy, self).OnReseted()
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
def CreateClone(self):
return send_close_order_strategy()