Esta estrategia convierte el experto MQL5 Exp_BlauTVI en una estrategia de alto nivel de StockSharp. Utiliza el Blau True Volume Index (TVI) para detectar reversiones en el flujo de volumen de ticks.
Idea
El True Volume Index separa los up-ticks y down-ticks y los suaviza con tres medias móviles exponenciales. El valor final oscila entre -100 y +100 y representa el dominio de compradores o vendedores. La estrategia abre una posición larga cuando el indicador gira hacia arriba tras un descenso y abre una posición corta cuando el indicador gira hacia abajo tras una subida. Las posiciones existentes en la dirección opuesta se cierran.
Parámetros
Length1 – primer período de suavizado para up-ticks y down-ticks.
Length2 – segundo período de suavizado.
Length3 – período de suavizado final aplicado al TVI.
CandleType – tipo de velas utilizado para los cálculos (por defecto: marco temporal de 4 horas).
Allow Buy Open – habilitar apertura de posiciones largas.
Allow Sell Open – habilitar apertura de posiciones cortas.
Allow Buy Close – habilitar cierre de posiciones largas cuando aparece una señal de venta.
Allow Sell Close – habilitar cierre de posiciones cortas cuando aparece una señal de compra.
Enable Stop Loss – usar protección de stop-loss en puntos.
Stop Loss – valor del stop-loss en puntos.
Enable Take Profit – usar protección de take-profit en puntos.
Take Profit – valor del take-profit en puntos.
Volume – volumen de la orden en lotes.
Señales
Compra – cuando el valor anterior del TVI es menor que el de antes y el valor actual del TVI es mayor que el anterior. Si está habilitado, se cierran las posiciones cortas existentes.
Venta – cuando el valor anterior del TVI es mayor que el de antes y el valor actual del TVI es menor que el anterior. Si está habilitado, se cierran las posiciones largas existentes.
Solo se procesan velas finalizadas y todos los cálculos usan el volumen de ticks de la vela. El stop-loss y el take-profit son opcionales y se expresan en puntos de precio.
Notas
La estrategia usa la API de alto nivel: se suscribe a velas, calcula el indicador internamente con instancias de ExponentialMovingAverage y gestiona posiciones con los métodos BuyMarket y SellMarket. El gráfico muestra el indicador TVI junto con las operaciones ejecutadas por la estrategia.
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>
/// Strategy based on smoothed momentum direction changes (Blau TVI concept).
/// Uses triple-smoothed EMA direction changes for signals.
/// </summary>
public class BlauTviStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevEma;
private decimal _prevPrevEma;
private int _count;
public int Length { get => _length.Value; set => _length.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public BlauTviStrategy()
{
_length = Param(nameof(Length), 12)
.SetGreaterThanZero()
.SetDisplay("Length", "Smoothing length", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevEma = 0;
_prevPrevEma = 0;
_count = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = Length };
SubscribeCandles(CandleType)
.Bind(ema, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
_count++;
if (_count < 3)
{
_prevPrevEma = _prevEma;
_prevEma = emaValue;
return;
}
var turnUp = _prevEma < _prevPrevEma && emaValue > _prevEma;
var turnDown = _prevEma > _prevPrevEma && emaValue < _prevEma;
if (turnUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (turnDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevPrevEma = _prevEma;
_prevEma = emaValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class blau_tvi_strategy(Strategy):
def __init__(self):
super(blau_tvi_strategy, self).__init__()
self._length = self.Param("Length", 12) \
.SetDisplay("Length", "Smoothing length", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_ema = 0.0
self._prev_prev_ema = 0.0
self._count = 0
@property
def Length(self):
return self._length.Value
@Length.setter
def Length(self, value):
self._length.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(blau_tvi_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.Length
self.SubscribeCandles(self.CandleType) \
.Bind(ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_value)
self._count += 1
if self._count < 3:
self._prev_prev_ema = self._prev_ema
self._prev_ema = ema_val
return
turn_up = self._prev_ema < self._prev_prev_ema and ema_val > self._prev_ema
turn_down = self._prev_ema > self._prev_prev_ema and ema_val < self._prev_ema
if turn_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif turn_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_ema = self._prev_ema
self._prev_ema = ema_val
def OnReseted(self):
super(blau_tvi_strategy, self).OnReseted()
self._prev_ema = 0.0
self._prev_prev_ema = 0.0
self._count = 0
def CreateClone(self):
return blau_tvi_strategy()