Parabolic SAR Estrategia de error 2
Descripción general
La Parabolic SAR estrategia de error 2 es la StockSharp conversión de alto nivel del MetaTrader asesor experto pSAR_bug2 de la carpeta MQL/9503. El EA original reacciona al primer punto Parabolic SAR que aparece en el lado opuesto del precio. Cuando el punto pasa por debajo del cierre, el sistema cierra cualquier operación corta e inmediatamente abre una posición larga; cuando el punto salta por encima del cierre, la lógica refleja el comportamiento en el lado corto. Los niveles protectores de stop-loss y take-profit se calculan en puntos de precio bruto, exactamente como en MetaTrader donde los valores se multiplican por el tamaño del instrumento Point.
El puerto StockSharp mantiene la misma intención y al mismo tiempo aprovecha el API de alto nivel del marco. Se suscribe a velas terminadas, vincula un indicador Parabolic SAR con parámetros de aceleración configurables, monitorea las reversiones de puntos y envía órdenes de mercado dimensionadas para aplanar la exposición anterior y establecer la nueva operación.
Lógica de trading
- Preparación de indicadores. La estrategia se suscribe a un tipo de vela definido por el usuario (período de tiempo de 15 minutos de forma predeterminada) y vincula un Parabolic SAR con un paso de aceleración
SarStep y una aceleración máxima SarMaximum.
- Seguimiento del estado. En la primera vela completa, el algoritmo registra si el valor SAR está por encima o por debajo del cierre. Cada nueva vela compara la nueva posición SAR con el estado almacenado previamente.
- Reglas de entrada.
- Entrada larga: se activa cuando el SAR se mueve desde arriba del cierre hasta debajo del cierre. El volumen de la orden se calcula como
TradeVolume + |Position|, por lo que una posición corta existente se cierra y se revierte en una única orden de mercado. Después de la entrada, los niveles de stop-loss y take-profit se almacenan en relación con el cierre de la vela.
- Entrada corta: se activa cuando el SAR se mueve desde debajo del cierre hasta arriba del cierre. Cualquier posición larga existente se aplana y se ingresa una nueva operación corta en el mercado con la misma fórmula de tamaño combinado.
- Salidas de protección. En cada vela completa, los niveles almacenados de stop-loss y take-profit se comparan con el máximo/mínimo. Si el precio atraviesa un nivel de protección, la estrategia envía una orden de mercado para cerrar la posición abierta y restablece los valores de parada y toma almacenados en caché.
Gestión del riesgo
- Las distancias de stop-loss y take-profit se calculan en puntos de precio bruto multiplicando el
StopLossPoints o TakeProfitPoints configurado por el paso del precio del valor. Se utiliza un respaldo conservador de 0.0001 cuando el instrumento no publica un paso de precio.
- La estrategia verifica
IsFormedAndOnlineAndAllowTrading() antes de enviar órdenes, asegurando que los datos del mercado estén en línea y que se permita el comercio.
- Las entradas de reversión siempre incluyen el tamaño absoluto de la posición actual, lo que garantiza que la nueva orden aplana la exposición anterior antes de establecer la operación opuesta.
Parámetros
| Nombre |
Predeterminado |
Descripción |
TradeVolume |
0.1 |
Volumen base de pedidos en lotes. El mismo valor se asigna a la propiedad interna Strategy.Volume. |
StopLossPoints |
90 |
Distancia de stop-loss en puntos de precio. La distancia se multiplica por el paso del precio del instrumento para obtener la compensación del precio real. |
TakeProfitPoints |
20 |
Distancia de obtención de beneficios en puntos de precio convertidos a través del paso del precio del instrumento. |
SarStep |
0.001 |
Factor de aceleración inicial para el indicador Parabolic SAR. |
SarMaximum |
0.2 |
Factor de aceleración máximo para el indicador Parabolic SAR. |
CandleType |
15m time-frame |
Tipo de vela utilizado para cálculos y evaluación de señales. |
Notas sobre la conversión
- Las órdenes de stop-loss y take-profit del lado del corredor de MetaTrader se emulan monitoreando los extremos de las velas y enviando salidas del mercado cuando se superan los umbrales.
- El MetaTrader EA requería gestión manual de
OrdersTotal() y llamadas explícitas OrderClose(). La versión StockSharp logra el mismo comportamiento enviando una única orden de mercado de tamaño TradeVolume + |Position|, que simultáneamente cierra cualquier posición opuesta y abre la nueva.
- No se proporciona ninguna implementación de Python que coincida con la solicitud de la tarea. Actualmente, la carpeta contiene solo la versión C# de la estrategia.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Parabolic SAR Bug2: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class ParabolicSarBug2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public ParabolicSarBug2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 30)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI 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 RsiLength { get => _rsiLength.Value; set => _rsiLength.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 rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, 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 rsiVal, 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) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _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.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class parabolic_sar_bug2_strategy(Strategy):
def __init__(self):
super(parabolic_sar_bug2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._fast_ema_length = self.Param("FastEmaLength", 10).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 30).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetDisplay("RSI Length", "RSI period", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(parabolic_sar_bug2_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
def OnStarted2(self, time):
super(parabolic_sar_bug2_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_ema_length.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_ema_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast_ema, slow_ema, rsi, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._prev_fast == 0 or self._prev_slow == 0 or atr_val <= 0:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = candle.ClosePrice
if self.Position > 0:
if (fast_val < slow_val and self._prev_fast >= self._prev_slow) or close <= self._entry_price - atr_val * 2:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if (fast_val > slow_val and self._prev_fast <= self._prev_slow) or close >= self._entry_price + atr_val * 2:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
if fast_val > slow_val and self._prev_fast <= self._prev_slow and rsi_val > 50:
self._entry_price = close
self.BuyMarket()
elif fast_val < slow_val and self._prev_fast >= self._prev_slow and rsi_val < 50:
self._entry_price = close
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return parabolic_sar_bug2_strategy()