Estrategia Parabolic SAR Bug5
Descripción general
La Estrategia Parabolic SAR Bug5 opera reversiones de precio detectadas por el indicador Parabolic SAR. Abre una posición larga cuando el precio cruza por encima del SAR y una posición corta cuando el precio cruza por debajo. La estrategia opcionalmente invierte la dirección de trading, cierra posiciones abiertas cuando el SAR cambia de lado, y admite stop trailing, toma de ganancias y reglas de stop-loss.
Reglas de entrada
- Comprar cuando el precio cruza por encima del SAR y no hay posición larga abierta.
- Vender cuando el precio cruza por debajo del SAR y no hay posición corta abierta.
- Si
Reverseestá habilitado, las señales se invierten.
Reglas de salida
- Cerrar posición cuando aparece la señal SAR opuesta si
SarCloseestá habilitado. - Aplicar objetivos fijos de stop-loss y toma de ganancias.
- Si
Trailingestá habilitado, el stop-loss sigue el precio más alto (para largos) o más bajo (para cortos) desde la entrada.
Parámetros
| Parámetro | Descripción |
|---|---|
Step |
Factor de aceleración inicial para Parabolic SAR. |
Maximum |
Factor de aceleración máximo para Parabolic SAR. |
StopLossPoints |
Distancia del stop-loss en puntos. |
TakeProfitPoints |
Distancia de la toma de ganancias en puntos. |
Trailing |
Habilitar gestión de stop trailing. |
TrailPoints |
Distancia del stop trailing en puntos. |
Reverse |
Invertir dirección de trading. |
SarClose |
Cerrar posición al cambio de SAR. |
CandleType |
Marco temporal de las velas a procesar. |
using System;
using System.Linq;
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>
/// Parabolic SAR strategy with optional reversal and trailing stop.
/// Based on conversion of MQL script pSAR_bug_5.
/// </summary>
public class ParabolicSarBug5Strategy : Strategy
{
private readonly StrategyParam<decimal> _step;
private readonly StrategyParam<decimal> _maximum;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<bool> _trailing;
private readonly StrategyParam<decimal> _trailPoints;
private readonly StrategyParam<bool> _reverse;
private readonly StrategyParam<bool> _sarClose;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevSar;
private bool _prevAbove;
private decimal? _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private decimal _highestPrice;
private decimal _lowestPrice;
/// <summary>
/// Parabolic SAR acceleration factor.
/// </summary>
public decimal Step { get => _step.Value; set => _step.Value = value; }
/// <summary>
/// Parabolic SAR maximum acceleration factor.
/// </summary>
public decimal Maximum { get => _maximum.Value; set => _maximum.Value = value; }
/// <summary>
/// Stop loss distance in points.
/// </summary>
public decimal StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
/// <summary>
/// Take profit distance in points.
/// </summary>
public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
/// <summary>
/// Enable trailing stop.
/// </summary>
public bool Trailing { get => _trailing.Value; set => _trailing.Value = value; }
/// <summary>
/// Trailing distance in points.
/// </summary>
public decimal TrailPoints { get => _trailPoints.Value; set => _trailPoints.Value = value; }
/// <summary>
/// Reverse trading direction.
/// </summary>
public bool Reverse { get => _reverse.Value; set => _reverse.Value = value; }
/// <summary>
/// Close position on SAR switch.
/// </summary>
public bool SarClose { get => _sarClose.Value; set => _sarClose.Value = value; }
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public ParabolicSarBug5Strategy()
{
_step = Param(nameof(Step), 0.001m)
.SetDisplay("Step", "Initial acceleration factor", "Indicators");
_maximum = Param(nameof(Maximum), 0.2m)
.SetDisplay("Maximum", "Maximum acceleration factor", "Indicators");
_stopLossPoints = Param(nameof(StopLossPoints), 90m)
.SetDisplay("Stop Loss", "Stop loss distance in points", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 20m)
.SetDisplay("Take Profit", "Take profit distance in points", "Risk");
_trailing = Param(nameof(Trailing), false)
.SetDisplay("Use Trailing", "Enable trailing stop", "Risk");
_trailPoints = Param(nameof(TrailPoints), 10m)
.SetDisplay("Trail Points", "Trailing distance in points", "Risk");
_reverse = Param(nameof(Reverse), false)
.SetDisplay("Reverse", "Reverse trading direction", "General");
_sarClose = Param(nameof(SarClose), true)
.SetDisplay("SAR Close", "Close position on SAR switch", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSar = 0m;
_prevAbove = false;
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
_highestPrice = 0m;
_lowestPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var psar = new ParabolicSar
{
Acceleration = Step,
AccelerationMax = Maximum
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(psar, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, psar);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal sar)
{
if (candle.State != CandleStates.Finished)
return;
var priceAbove = candle.ClosePrice > sar;
var crossing = _prevSar > 0 && priceAbove != _prevAbove;
if (crossing)
{
var isBuySignal = priceAbove;
if (Reverse)
isBuySignal = !isBuySignal;
if (isBuySignal && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_takePrice = _entryPrice + TakeProfitPoints;
_stopPrice = _entryPrice - StopLossPoints;
_highestPrice = candle.HighPrice;
}
else if (!isBuySignal && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_takePrice = _entryPrice - TakeProfitPoints;
_stopPrice = _entryPrice + StopLossPoints;
_lowestPrice = candle.LowPrice;
}
}
if (Position > 0 && _entryPrice is decimal)
{
_highestPrice = Math.Max(_highestPrice, candle.HighPrice);
if (Trailing)
_stopPrice = Math.Max(_stopPrice ?? 0m, _highestPrice - TrailPoints);
if (_stopPrice is decimal sl && candle.LowPrice <= sl)
{
SellMarket();
ResetState();
}
else if (_takePrice is decimal tp && candle.HighPrice >= tp)
{
SellMarket();
ResetState();
}
}
else if (Position < 0 && _entryPrice is decimal)
{
_lowestPrice = Math.Min(_lowestPrice, candle.LowPrice);
if (Trailing)
_stopPrice = Math.Min(_stopPrice ?? decimal.MaxValue, _lowestPrice + TrailPoints);
if (_stopPrice is decimal sl && candle.HighPrice >= sl)
{
BuyMarket();
ResetState();
}
else if (_takePrice is decimal tp && candle.LowPrice <= tp)
{
BuyMarket();
ResetState();
}
}
_prevSar = sar;
_prevAbove = priceAbove;
}
private void ResetState()
{
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
_highestPrice = 0m;
_lowestPrice = 0m;
}
}
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 ParabolicSar
from StockSharp.Algo.Strategies import Strategy
class parabolic_sar_bug5_strategy(Strategy):
def __init__(self):
super(parabolic_sar_bug5_strategy, self).__init__()
self._step = self.Param("Step", 0.001) \
.SetDisplay("Step", "Initial acceleration factor", "Indicators")
self._maximum = self.Param("Maximum", 0.2) \
.SetDisplay("Maximum", "Maximum acceleration factor", "Indicators")
self._stop_loss_points = self.Param("StopLossPoints", 90.0) \
.SetDisplay("Stop Loss", "Stop loss distance in points", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 20.0) \
.SetDisplay("Take Profit", "Take profit distance in points", "Risk")
self._trailing = self.Param("Trailing", False) \
.SetDisplay("Use Trailing", "Enable trailing stop", "Risk")
self._trail_points = self.Param("TrailPoints", 10.0) \
.SetDisplay("Trail Points", "Trailing distance in points", "Risk")
self._reverse = self.Param("Reverse", False) \
.SetDisplay("Reverse", "Reverse trading direction", "General")
self._sar_close = self.Param("SarClose", True) \
.SetDisplay("SAR Close", "Close position on SAR switch", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_sar = 0.0
self._prev_above = False
self._entry_price = None
self._stop_price = None
self._take_price = None
self._highest_price = 0.0
self._lowest_price = 0.0
@property
def step(self):
return self._step.Value
@property
def maximum(self):
return self._maximum.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
@property
def trailing(self):
return self._trailing.Value
@property
def trail_points(self):
return self._trail_points.Value
@property
def reverse(self):
return self._reverse.Value
@property
def sar_close(self):
return self._sar_close.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(parabolic_sar_bug5_strategy, self).OnReseted()
self._prev_sar = 0.0
self._prev_above = False
self._entry_price = None
self._stop_price = None
self._take_price = None
self._highest_price = 0.0
self._lowest_price = 0.0
def OnStarted2(self, time):
super(parabolic_sar_bug5_strategy, self).OnStarted2(time)
psar = ParabolicSar()
psar.Acceleration = self.step
psar.AccelerationMax = self.maximum
self._prev_sar = 0.0
self._prev_above = False
self._entry_price = None
self._stop_price = None
self._take_price = None
self._highest_price = 0.0
self._lowest_price = 0.0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(psar, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, psar)
self.DrawOwnTrades(area)
def on_process(self, candle, sar):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sar_f = float(sar)
price_above = close > sar_f
crossing = self._prev_sar > 0 and price_above != self._prev_above
if crossing:
is_buy_signal = price_above
if self.reverse:
is_buy_signal = not is_buy_signal
if is_buy_signal and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._take_price = close + self.take_profit_points
self._stop_price = close - self.stop_loss_points
self._highest_price = float(candle.HighPrice)
elif not is_buy_signal and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._take_price = close - self.take_profit_points
self._stop_price = close + self.stop_loss_points
self._lowest_price = float(candle.LowPrice)
if self.Position > 0 and self._entry_price is not None:
self._highest_price = max(self._highest_price, float(candle.HighPrice))
if self.trailing:
self._stop_price = max(self._stop_price if self._stop_price is not None else 0, self._highest_price - self.trail_points)
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
self._reset_state()
elif self._take_price is not None and float(candle.HighPrice) >= self._take_price:
self.SellMarket()
self._reset_state()
elif self.Position < 0 and self._entry_price is not None:
self._lowest_price = min(self._lowest_price, float(candle.LowPrice))
if self.trailing:
self._stop_price = min(self._stop_price if self._stop_price is not None else 1e18, self._lowest_price + self.trail_points)
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
self._reset_state()
elif self._take_price is not None and float(candle.LowPrice) <= self._take_price:
self.BuyMarket()
self._reset_state()
self._prev_sar = sar_f
self._prev_above = price_above
def _reset_state(self):
self._entry_price = None
self._stop_price = None
self._take_price = None
self._highest_price = 0.0
self._lowest_price = 0.0
def CreateClone(self):
return parabolic_sar_bug5_strategy()