Estrategia de Reversión en Pivot Point
Los Pivot Points diarios y sus niveles de soporte y resistencia a menudo actúan como puntos de giro para la acción del precio intradía. Esta estrategia calcula los pivotes clásicos del floor-trader a partir del máximo, mínimo y cierre del día anterior, y luego busca velas que reboten desde S1 o R1.
Las pruebas indican un rendimiento anual promedio de aproximadamente el 127%. Funciona mejor en el mercado de acciones.
Cuando el precio se acerca al nivel de soporte S1 y forma una vela alcista, se toma una entrada larga. Si el precio prueba el nivel de resistencia R1 e imprime una vela bajista, se abre un corto. Las operaciones salen al alcanzar el pivot central o si se activa el stop de protección.
El método se restablece al inicio de cada sesión de negociación con nuevos cálculos de pivote, lo que lo hace muy adecuado para sesiones con rangos intradía claros.
Detalles
- Criterios de entrada: Vela alcista cerca de S1 o vela bajista cerca de R1.
- Largo/Corto: Ambos.
- Criterios de salida: Precio cruzando el pivot central o stop-loss.
- Stops: Sí, basados en porcentaje.
- Valores predeterminados:
CandleType = 5 minute
StopLossPercent = 2
Los pivotes de cada día UTC salen del máximo, mínimo y cierre del día anterior. Una compra requiere una vela alcista que toque S1 y cierre por encima, una venta una vela bajista que alcance R1 y cierre por debajo; el stop es un porcentaje del precio de entrada.
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: Pivot Points
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: Sí
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Pivot Point Reversal strategy.
/// Each day the classic floor pivots come from the previous day's high, low and close: P = (H + L + C) / 3, R1 = 2P - L, S1 = 2P - H.
/// While flat, a bullish candle that dips to S1 and closes above it buys, and a bearish candle that reaches R1 and closes below it sells.
/// The position closes when the close reaches the central pivot or at the percent stop.
/// </summary>
public class PivotPointReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _day;
private decimal _dayHigh;
private decimal _dayLow;
private decimal _dayClose;
private (decimal Pivot, decimal R1, decimal S1)? _levels;
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public PivotPointReversalStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_day = null;
_dayHigh = default;
_dayLow = default;
_dayClose = default;
_levels = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_day = null;
_levels = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
if (_day != day)
{
// A new day takes its pivots from the day that has just ended.
if (_day != null)
{
var pivot = (_dayHigh + _dayLow + _dayClose) / 3m;
_levels = (pivot, 2 * pivot - _dayLow, 2 * pivot - _dayHigh);
}
_day = day;
_dayHigh = candle.HighPrice;
_dayLow = candle.LowPrice;
}
else
{
_dayHigh = Math.Max(_dayHigh, candle.HighPrice);
_dayLow = Math.Min(_dayLow, candle.LowPrice);
}
_dayClose = candle.ClosePrice;
if (_levels is not { } levels || !IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= levels.Pivot)
SellMarket(Position);
}
else if (Position < 0)
{
if (close <= levels.Pivot)
BuyMarket(-Position);
}
else if (close > candle.OpenPrice && candle.LowPrice <= levels.S1 && close > levels.S1)
{
BuyMarket(Volume);
}
else if (close < candle.OpenPrice && candle.HighPrice >= levels.R1 && close < levels.R1)
{
SellMarket(Volume);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class pivot_point_reversal_strategy(Strategy):
"""
Pivot Point Reversal strategy.
Each day the classic floor pivots come from the previous day's high, low and close: P = (H + L + C) / 3, R1 = 2P - L, S1 = 2P - H.
While flat, a bullish candle that dips to S1 and closes above it buys, and a bearish candle that reaches R1 and closes below it sells.
The position closes when the close reaches the central pivot or at the percent stop.
"""
def __init__(self):
super(pivot_point_reversal_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._day = None
self._day_high = Decimal(0)
self._day_low = Decimal(0)
self._day_close = Decimal(0)
self._levels = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(pivot_point_reversal_strategy, self).OnReseted()
self._day = None
self._day_high = Decimal(0)
self._day_low = Decimal(0)
self._day_close = Decimal(0)
self._levels = None
def OnStarted2(self, time):
super(pivot_point_reversal_strategy, self).OnStarted2(time)
self._day = None
self._levels = None
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
if self._day is None or self._day != day:
# A new day takes its pivots from the day that has just ended.
if self._day is not None:
pivot = (self._day_high + self._day_low + self._day_close) / Decimal(3)
self._levels = (pivot, Decimal(2) * pivot - self._day_low, Decimal(2) * pivot - self._day_high)
self._day = day
self._day_high = candle.HighPrice
self._day_low = candle.LowPrice
else:
self._day_high = Math.Max(self._day_high, candle.HighPrice)
self._day_low = Math.Min(self._day_low, candle.LowPrice)
self._day_close = candle.ClosePrice
if self._levels is None or not self.IsFormedAndOnlineAndAllowTrading():
return
pivot, r1, s1 = self._levels
close = candle.ClosePrice
if self.Position > 0:
if close >= pivot:
self.SellMarket(self.Position)
elif self.Position < 0:
if close <= pivot:
self.BuyMarket(-self.Position)
elif close > candle.OpenPrice and candle.LowPrice <= s1 and close > s1:
self.BuyMarket(self.Volume)
elif close < candle.OpenPrice and candle.HighPrice >= r1 and close < r1:
self.SellMarket(self.Volume)
def CreateClone(self):
return pivot_point_reversal_strategy()