Estratégia de Reversão em Pivot Point
Os Pivot Points diários e seus níveis de suporte e resistência frequentemente atuam como pontos de reversão para a ação de preço intradiária. Esta estratégia calcula os pivôs clássicos do floor-trader a partir da máxima, mínima e fechamento do dia anterior, e então procura velas que ricocheteiem em S1 ou R1.
Os testes indicam um retorno anual médio de aproximadamente 127%. Funciona melhor no mercado de ações.
Quando o preço se aproxima do nível de suporte S1 e forma uma vela de alta, uma entrada comprada é feita. Se o preço testa o nível de resistência R1 e imprime uma vela de baixa, um vendido é aberto. As operações saem ao atingir o pivot central ou se o stop de proteção for acionado.
O método é reiniciado no início de cada sessão de negociação com novos cálculos de pivô, tornando-o bem adequado para sessões com intervalos intradiários claros.
Detalhes
- Critérios de entrada: Vela de alta próxima a S1 ou vela de baixa próxima a R1.
- Comprado/Vendido: Ambos.
- Critérios de saída: Preço cruzando o pivot central ou stop-loss.
- Stops: Sim, baseados em percentual.
- Valores padrão:
CandleType = 5 minute
StopLossPercent = 2
Os pivôs de cada dia UTC vêm da máxima, mínima e fechamento do dia anterior. Uma compra exige uma vela de alta que toque S1 e feche acima dele, uma venda uma vela de baixa que alcance R1 e feche abaixo dele; o stop é uma porcentagem do preço de entrada.
- Filtros:
- Categoria: Reversão à média
- Direção: Ambos
- Indicadores: Pivot Points
- Stops: Sim
- Complexidade: Intermediário
- Período: Intradiário
- Sazonalidade: Sim
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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()