Esta estrategia transfiere el experto MetaTrader Vortex Indicator System.mq4 al StockSharp nivel alto API. La idea original era
Se publica en Análisis técnico de acciones y materias primas (enero de 2010) y se basa en el cruce del indicador Vortex para armar bre.
Órdenes akout en el máximo/mínimo de la vela cruzada. La versión StockSharp mantiene el mismo flujo de decisión: un cruce cierra el
posición opuesta, activa un disparador de ruptura en el extremo de la barra de cruce, y la siguiente vela que rompe ese nivel ejecuta el
orden del mercado.
como funciona
Se abre una suscripción de vela única según CandleType. La transmisión resultante está vinculada a una instancia VortexIndicator
Una vez que se usa Bind, la estrategia siempre recibe valores VI+ y VI- sincronizados para las velas terminadas.
Cuando el indicador termina de calentarse, el algoritmo rastrea los valores VI anteriores para detectar las mismas condiciones de cruce u
sed en el experto MQL: VI+ cruzando por encima de VI- o viceversa entre las dos últimas velas cerradas.
Fase de configuración: tan pronto como se detecta un cruce alcista, cualquier posición corta abierta se cierra inmediatamente y el máximo del
La vela cruzada se convierte en el disparador largo pendiente. El cruce opuesto cierra una posición larga existente y almacena el mínimo.
de esa barra como disparador corto.
Fase de activación: en cada vela finalizada posterior, la estrategia verifica si se tocó el precio de activación registrado ("Hola
ghPrice≥ long trigger orLowPrice≤ disparador corto). Si es así, envía una orden de mercado de tamaño suficiente para aplanar al oponente restante. exposición del sitio (si el pedido anterior aún no se completó) y abra una nueva posición conTradeVolume`.
Una vez que se activa una orden, se borra el activador correspondiente. Si no se produce ninguna ruptura, la configuración permanece activa hasta un nuevo cruce.
r lo anula.
Las salidas se basan exclusivamente en la lógica de cruce: la señal opuesta inmediatamente aplana la posición actual y arma una nueva b.
desencadenador de consulta, que refleja la implementación de MetaTrader.
Señales
Configuración alcista: ocurre cuando VI+ estaba por debajo o igual a VI- en la vela cerrada anterior y se eleva por encima de ella en la vela más alta.
reciente. El disparador largo se fija en el máximo de esa vela.
Ejecución alcista: la siguiente vela cuyo máximo alcanza el disparador envía una orden de compra de mercado usando TradeVolume (más cualquier vo
volumen necesario para cerrar una posición corta pendiente).
Configuración bajista: ocurre cuando VI- estaba por debajo o igual a VI+ en la vela cerrada anterior y se eleva por encima de ella en la vela más alta.
reciente. El disparador corto se fija en el mínimo de esa vela.
Ejecución bajista: la siguiente vela cuyo mínimo toca el disparador envía una orden de venta de mercado usando TradeVolume (más el vo
lume necesario para aplanar una posición larga abierta).
Parámetros
Parámetro
Predeterminado
Descripción
VortexLength
14
Período aplicado al indicador Vortex.
CandleType
1 hora
Marco de tiempo utilizado para velas y actualizaciones de indicadores.
TradeVolume
1
Tamaño de orden de mercado utilizado para nuevas entradas.
Notas de implementación
La estrategia solo reacciona a velas terminadas para cumplir con las pautas de conversión. Las rupturas intrabar se reconocen como
tan pronto como una vela se cierra con un máximo/mínimo más allá del disparador almacenado.
Los activadores pendientes se borran el OnStopped para que la instancia se pueda reiniciar limpiamente sin que quede ningún estado.
Al ejecutar una orden de ruptura, el algoritmo aumenta el volumen si aún mantiene una posición opuesta, logrando la misma e
Funciona como el experto MetaTrader, que cerró la orden activa antes de abrir la nueva.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Vortex Indicator Breakout: Dual EMA crossover breakout with ATR stops.
/// </summary>
public class VortexIndicatorBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public VortexIndicatorBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 14)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 28)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
if (Position > 0)
{
if (fastVal < slowVal && _prevFast >= _prevSlow)
{
SellMarket();
_entryPrice = 0;
}
else if (close <= _entryPrice - atrVal * 2m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow)
{
BuyMarket();
_entryPrice = 0;
}
else if (close >= _entryPrice + atrVal * 2m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow)
{
_entryPrice = close;
BuyMarket();
}
else if (fastVal < slowVal && _prevFast >= _prevSlow)
{
_entryPrice = close;
SellMarket();
}
}
_prevFast = fastVal;
_prevSlow = slowVal;
}
}
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 vortex_indicator_breakout_strategy(Strategy):
def __init__(self):
super(vortex_indicator_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 14) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 28) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(vortex_indicator_breakout_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if fv < sv and self._prev_fast >= self._prev_slow:
self.SellMarket()
self._entry_price = 0.0
elif close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if fv > sv and self._prev_fast <= self._prev_slow:
self.BuyMarket()
self._entry_price = 0.0
elif close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(vortex_indicator_breakout_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return vortex_indicator_breakout_strategy()