Ver en GitHub

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
  • 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.
/// Calculates pivot points from a rolling window of highs, lows, closes.
/// P = (H + L + C) / 3, S1 = 2*P - H, R1 = 2*P - L
/// Buys on bounce off S1, sells on bounce off R1, exits at pivot.
/// </summary>
public class PivotPointReversalStrategy : Strategy
{
	private readonly StrategyParam<int> _lookback;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private readonly List<decimal> _highs = new();
	private readonly List<decimal> _lows = new();
	private readonly List<decimal> _closes = new();
	private int _cooldown;

	/// <summary>
	/// Lookback period for pivot calculation.
	/// </summary>
	public int Lookback
	{
		get => _lookback.Value;
		set => _lookback.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Cooldown bars.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public PivotPointReversalStrategy()
	{
		_lookback = Param(nameof(Lookback), 60)
			.SetGreaterThanZero()
			.SetDisplay("Lookback", "Lookback for pivot calc", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_cooldownBars = Param(nameof(CooldownBars), 500)
			.SetRange(1, 1000)
			.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_highs.Clear();
		_lows.Clear();
		_closes.Clear();
		_cooldown = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_highs.Clear();
		_lows.Clear();
		_closes.Clear();
		_cooldown = 0;

		var sma = new SimpleMovingAverage { Length = 20 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, sma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal smaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_highs.Add(candle.HighPrice);
		_lows.Add(candle.LowPrice);
		_closes.Add(candle.ClosePrice);

		if (_highs.Count > Lookback)
		{
			_highs.RemoveAt(0);
			_lows.RemoveAt(0);
			_closes.RemoveAt(0);
		}

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_highs.Count < Lookback)
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		// Calculate pivot points from lookback window
		decimal high = decimal.MinValue, low = decimal.MaxValue, close = 0;
		for (int i = 0; i < _highs.Count; i++)
		{
			if (_highs[i] > high) high = _highs[i];
			if (_lows[i] < low) low = _lows[i];
		}
		close = _closes[_closes.Count - 1];

		var pivot = (high + low + close) / 3;
		var r1 = 2 * pivot - low;
		var s1 = 2 * pivot - high;
		var buffer = (r1 - s1) * 0.02m;

		if (buffer <= 0)
			return;

		var isBullish = candle.ClosePrice > candle.OpenPrice;
		var isBearish = candle.ClosePrice < candle.OpenPrice;

		// Bounce off S1 (buy)
		if (Position == 0 && candle.LowPrice <= s1 + buffer && isBullish)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
		// Bounce off R1 (sell)
		else if (Position == 0 && candle.HighPrice >= r1 - buffer && isBearish)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		// Exit at pivot
		else if (Position > 0 && candle.ClosePrice > pivot)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position < 0 && candle.ClosePrice < pivot)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
	}
}