La Estrategia de reversión de retroceso TST busca reversiones agresivas dentro de la barra. Se convirtió del MetaTrader 4 asesor experto original TST.mq4 y se reconstruyó utilizando el StockSharp API de alto nivel. El método busca velas en las que el precio se ha alejado bruscamente de la apertura de la vela después de establecer un extremo intradiario, luego desvanece ese movimiento esperando una reversión a la media. La estrategia opera tanto a largo como a corto y utiliza niveles estáticos de stop-loss y take-profit medidos en pasos de precios.
Lógica de señal
Configuración larga
La vela cierra por debajo de su apertura (Open > Close).
La distancia entre el máximo de la vela y el cierre es mayor que GapPoints * PriceStep.
No se ejecutó ninguna operación anteriormente en la misma barra.
Cuando está satisfecho, la estrategia cierra cualquier exposición corta y compra OrderVolume unidades (más el tamaño necesario para pasar de una posición corta a una larga).
Configuración corta
La vela cierra por encima de su apertura (Close > Open).
La distancia entre el cierre y el mínimo de la vela es mayor que GapPoints * PriceStep.
No se ejecutó ninguna operación anteriormente en la misma barra.
Cuando está satisfecho, la estrategia cierra cualquier exposición larga y vende OrderVolume unidades (más el tamaño necesario para pasar de una posición larga a una corta).
Gestión de Puestos
Una nueva operación asigna inmediatamente niveles estáticos de stop-loss y take-profit calculados a partir del precio de ejecución y los parámetros StopLossPoints/TakeProfitPoints.
En cada vela terminada, la estrategia verifica el máximo/mínimo de la vela para ver si se tocó el stop o el objetivo y sale de la posición si se activa. Los controles de limitación de pérdidas tienen prioridad sobre los controles de obtención de beneficios.
Después de una salida, los niveles de riesgo almacenados se borran, pero la estrategia aún recuerda el tiempo de la barra para evitar volver a entrar durante la misma vela (reproduciendo la guardia NevBar() de la versión MQL4).
Parámetros
StopLossPoints (por defecto 500): distancia desde la entrada hasta el stop de protección, expresada en incrementos de precio.
TakeProfitPoints (predeterminado 100): distancia desde la entrada hasta el objetivo de ganancias, expresada en incrementos de precio.
GapPoints (predeterminado 500): retroceso mínimo entre el extremo de la vela y el cierre requerido para generar una señal.
OrderVolume (predeterminado 0.1): cantidad enviada con cada nueva orden de mercado.
CandleType (predeterminado 1 hour): período de tiempo de las velas suministradas a través de SubscribeCandles.
Todas las configuraciones basadas en la distancia se multiplican por el PriceStep del instrumento. Si el valor no informa un paso, la estrategia vuelve a 1.
Notas de implementación
La conversión utiliza el nivel alto API de StockSharp y no crea colecciones de indicadores personalizados.
Sólo se procesan velas terminadas para seguir siendo compatibles con el Diseñador de estrategias; esto aproxima las decisiones intrabarra del robot MT4 utilizando datos de barra completos.
Una bandera dedicada _lastSignalBarTime replica la protección NevBar() del código MQL4 para que solo se pueda abrir una orden por vela.
El manejo del volumen de órdenes refleja el comportamiento de MT4: las posiciones opuestas existentes se aplanan antes de establecer la nueva dirección en una orden de mercado única.
Las órdenes de stop-loss y take-profit se simulan dentro de la lógica de la estrategia (en lugar de órdenes del lado del servidor) para igualar las distancias originales mientras se mantiene el control dentro de StockSharp.
Consejos de uso
Elija GapPoints en relación con la volatilidad del instrumento negociado; los valores más grandes reducen la frecuencia del comercio pero filtran retrocesos más pequeños.
Debido a que las comprobaciones de parada y objetivo dependen de velas terminadas, considere usar velas de menor duración si necesita una ejecución más estricta.
Combine la estrategia con filtros adicionales (tendencia, hora del día, volumen) cuando la implemente en mercados reales para reducir las operaciones irregulares.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// TST Pullback Reversal: buys after deep pullback from candle high,
/// sells after rally from candle low. Uses ATR for thresholds.
/// </summary>
public class TstStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _pullbackMultiplier;
private readonly StrategyParam<decimal> _stopMultiplier;
private readonly StrategyParam<decimal> _takeMultiplier;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
public TstStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
_pullbackMultiplier = Param(nameof(PullbackMultiplier), 0.5m)
.SetDisplay("Pullback Mult", "ATR multiplier for pullback threshold.", "Signals");
_stopMultiplier = Param(nameof(StopMultiplier), 2.0m)
.SetDisplay("Stop Mult", "ATR multiplier for stop loss.", "Risk");
_takeMultiplier = Param(nameof(TakeMultiplier), 1.0m)
.SetDisplay("Take Mult", "ATR multiplier for take profit.", "Risk");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal PullbackMultiplier
{
get => _pullbackMultiplier.Value;
set => _pullbackMultiplier.Value = value;
}
public decimal StopMultiplier
{
get => _stopMultiplier.Value;
set => _stopMultiplier.Value = value;
}
public decimal TakeMultiplier
{
get => _takeMultiplier.Value;
set => _takeMultiplier.Value = value;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var close = candle.ClosePrice;
var open = candle.OpenPrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
var threshold = atrVal * PullbackMultiplier;
var stopDist = atrVal * StopMultiplier;
var takeDist = atrVal * TakeMultiplier;
// Risk management
if (Position > 0)
{
if (_stopPrice > 0 && close <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
if (_takePrice > 0 && close >= _takePrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
}
else if (Position < 0)
{
if (_stopPrice > 0 && close >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
if (_takePrice > 0 && close <= _takePrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
}
// Entry: deep pullback from high = buy reversal
if (Position == 0)
{
if (open > close && high - close > threshold)
{
_entryPrice = close;
_stopPrice = close - stopDist;
_takePrice = close + takeDist;
BuyMarket();
}
else if (close > open && close - low > threshold)
{
_entryPrice = close;
_stopPrice = close + stopDist;
_takePrice = close - takeDist;
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 AverageTrueRange
class tst_strategy(Strategy):
def __init__(self):
super(tst_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._pullback_multiplier = self.Param("PullbackMultiplier", 0.5) \
.SetDisplay("Pullback Mult", "ATR multiplier for pullback threshold", "Signals")
self._stop_multiplier = self.Param("StopMultiplier", 2.0) \
.SetDisplay("Stop Mult", "ATR multiplier for stop loss", "Risk")
self._take_multiplier = self.Param("TakeMultiplier", 1.0) \
.SetDisplay("Take Mult", "ATR multiplier for take profit", "Risk")
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def PullbackMultiplier(self):
return self._pullback_multiplier.Value
@property
def StopMultiplier(self):
return self._stop_multiplier.Value
@property
def TakeMultiplier(self):
return self._take_multiplier.Value
def OnStarted2(self, time):
super(tst_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
av = float(atr_val)
if av <= 0:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
threshold = av * float(self.PullbackMultiplier)
stop_dist = av * float(self.StopMultiplier)
take_dist = av * float(self.TakeMultiplier)
# Risk management
if self.Position > 0:
if self._stop_price > 0 and close <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
if self._take_price > 0 and close >= self._take_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
elif self.Position < 0:
if self._stop_price > 0 and close >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
if self._take_price > 0 and close <= self._take_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
# Entry: deep pullback from high = buy reversal
if self.Position == 0:
if open_p > close and high - close > threshold:
self._entry_price = close
self._stop_price = close - stop_dist
self._take_price = close + take_dist
self.BuyMarket()
elif close > open_p and close - low > threshold:
self._entry_price = close
self._stop_price = close + stop_dist
self._take_price = close - take_dist
self.SellMarket()
def OnReseted(self):
super(tst_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def CreateClone(self):
return tst_strategy()