La Estrategia Expotest es una conversión directa a StockSharp del asesor experto original Expotest.mq5. Opera un único instrumento usando el indicador Parabolic SAR y una regla de gestión monetaria simple inspirada en el martingale. La estrategia abre solo una posición a la vez y depende de niveles de stop-loss y take-profit predefinidos para las salidas.
Lógica de trading
Indicador: Parabolic SAR calculado en la serie de velas seleccionada. Tanto el factor de aceleración (SarStep) como la aceleración máxima (SarMaximum) son configurables.
Condiciones de entrada: Cuando no hay posición abierta, la estrategia verifica la última vela cerrada.
Si el valor del Parabolic SAR está por debajo o igual al precio de cierre, se inicia una posición larga.
Si el valor del Parabolic SAR está por encima o igual al precio de cierre, se inicia una posición corta.
Condiciones de salida: Los niveles de stop-loss y take-profit se colocan a una distancia fija del precio de entrada, medida en pasos de precio. Durante cada nueva vela, la estrategia monitorea si el rango de la vela toca cualquiera de los niveles y cierra la posición en consecuencia. El tipo de salida (ganancia o pérdida) se recuerda para futuras decisiones de dimensionamiento de posición.
Dimensionamiento de posición
Volumen base: Definido por el parámetro FixedVolume cuando es mayor que cero. De lo contrario, la estrategia estima el tamaño a partir de los valores RiskPercent y StopLossPoints usando el capital actual del portafolio. Si ningún método devuelve un tamaño válido, se usa el Strategy.Volume predeterminado.
Paso martingale: Después de una operación perdedora, el siguiente tamaño de posición se duplica en comparación con el volumen de la posición perdedora. Una salida rentable restablece el multiplicador y la siguiente orden usa el volumen base nuevamente.
Parámetros configurables
CandleType – Tipo de datos para la agregación de velas (marco temporal u otro formato de vela).
SarStep – Factor de aceleración inicial para el Parabolic SAR.
SarMaximum – Factor de aceleración máximo para el Parabolic SAR.
StopLossPoints – Distancia del stop-loss desde la entrada expresada en pasos de precio.
TakeProfitPoints – Distancia del take-profit desde la entrada expresada en pasos de precio.
RiskPercent – Porcentaje del capital del portafolio a arriesgar por operación cuando el dimensionamiento dinámico está habilitado.
FixedVolume – Volumen de orden explícito. Establecer en 0 para habilitar el dimensionamiento basado en riesgo.
Notas adicionales
La estrategia procesa solo velas terminadas para mantenerse cerca de la implementación MQL original basada en ticks mientras permanece compatible con las suscripciones de StockSharp.
Los niveles de protección se rastrean internamente en lugar de órdenes stop/limit separadas, lo que mantiene la lógica transparente y fácil de backtestear.
La implementación Python se omite intencionalmente según lo solicitado.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Parabolic SAR based strategy converted from the Expotest MQL expert advisor.
/// Enters long when SAR is below price, short when SAR is above price.
/// </summary>
public class ExpotestStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMaximum;
private bool _prevSarBelow;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal SarStep
{
get => _sarStep.Value;
set => _sarStep.Value = value;
}
public decimal SarMaximum
{
get => _sarMaximum.Value;
set => _sarMaximum.Value = value;
}
public ExpotestStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle type for signal generation", "General");
_sarStep = Param(nameof(SarStep), 0.02m)
.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Indicators");
_sarMaximum = Param(nameof(SarMaximum), 0.2m)
.SetDisplay("SAR Maximum", "Maximum acceleration factor for Parabolic SAR", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSarBelow = false;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSarBelow = false;
_initialized = false;
var sar = new ParabolicSar
{
Acceleration = SarStep,
AccelerationMax = SarMaximum
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sar, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sar);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal sarValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sarBelow = sarValue < candle.ClosePrice;
if (!_initialized)
{
_prevSarBelow = sarBelow;
_initialized = true;
return;
}
// SAR flipped from above to below price => Buy signal
if (sarBelow && !_prevSarBelow && Position <= 0)
{
BuyMarket();
}
// SAR flipped from below to above price => Sell signal
else if (!sarBelow && _prevSarBelow && Position >= 0)
{
SellMarket();
}
_prevSarBelow = sarBelow;
}
}
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 ParabolicSar
from StockSharp.Algo.Strategies import Strategy
class expotest_strategy(Strategy):
def __init__(self):
super(expotest_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle type for signal generation", "General")
self._sar_step = self.Param("SarStep", 0.02) \
.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Indicators")
self._sar_maximum = self.Param("SarMaximum", 0.2) \
.SetDisplay("SAR Maximum", "Maximum acceleration factor for Parabolic SAR", "Indicators")
self._prev_sar_below = False
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def SarStep(self):
return self._sar_step.Value
@property
def SarMaximum(self):
return self._sar_maximum.Value
def OnReseted(self):
super(expotest_strategy, self).OnReseted()
self._prev_sar_below = False
self._initialized = False
def OnStarted2(self, time):
super(expotest_strategy, self).OnStarted2(time)
self._prev_sar_below = False
self._initialized = False
sar = ParabolicSar()
sar.Acceleration = self.SarStep
sar.AccelerationMax = self.SarMaximum
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(sar, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
def _on_process(self, candle, sar_value):
if candle.State != CandleStates.Finished:
return
sv = float(sar_value)
sar_below = sv < float(candle.ClosePrice)
if not self._initialized:
self._prev_sar_below = sar_below
self._initialized = True
return
if sar_below and not self._prev_sar_below and self.Position <= 0:
self.BuyMarket()
elif not sar_below and self._prev_sar_below and self.Position >= 0:
self.SellMarket()
self._prev_sar_below = sar_below
def CreateClone(self):
return expotest_strategy()