Ver en GitHub

Estrategia de Pivote Simple

Esta estrategia reproduce el asesor experto "SimplePivot" de MetaTrader 5. Evalúa continuamente la relación entre la apertura de la barra actual y el nivel de pivote de la barra anterior, manteniendo siempre una única posición direccional. Cuando el sesgo cambia, la estrategia cierra la posición existente y abre inmediatamente una en la dirección opuesta.

Descripción general

  • Régimen de mercado: Trading de swing siempre en el mercado.
  • Instrumentos: Cualquier instrumento que proporcione datos de velas para el período de tiempo seleccionado.
  • Períodos de tiempo: Configurable mediante el parámetro Candle Type (velas de 1 hora por defecto).
  • Órdenes: Órdenes de mercado dimensionadas por el parámetro Volume.

Cómo funciona

Cálculo del pivote

  1. Esperar al menos una vela completada para inicializar el cálculo.
  2. Calcular el pivote de la vela anterior como la media aritmética de sus precios máximo y mínimo.
  3. Retener el máximo y mínimo anteriores para que el pivote de la siguiente barra pueda producirse inmediatamente cuando termine una nueva vela.

Decisión direccional

  1. El sesgo predeterminado es largo (compra).
  2. Si la vela actual abre por debajo del máximo anterior mientras permanece por encima del pivote, el sesgo cambia a corto (venta).
  3. Si la dirección deseada no cambia respecto a la última operación ejecutada, se preserva la posición existente y no se envían nuevas órdenes.

Gestión de posición

  1. Si la dirección deseada difiere de la operación actual, la posición en ejecución se liquida mediante una orden de mercado opuesta.
  2. Después de liquidar, una orden de mercado dimensionada por Volume establece la nueva exposición direccional.
  3. El proceso se repite en cada vela completada, asegurando que la estrategia esté siempre larga o corta.

Parámetros

  • Volume: Tamaño de la operación usado para cada entrada. También determina el tamaño de la orden de cierre cuando la estrategia cambia de dirección.
  • Candle Type: Tipo de datos de las velas usadas para los cálculos de pivote y entrada. El valor predeterminado es un período de 1 hora, pero se puede seleccionar cualquier período disponible.

Notas adicionales

  • La lógica reacciona en velas completamente cerradas (CandleStates.Finished) para evitar señales repetidas mientras una vela todavía se está formando.
  • No se definen stops ni objetivos de beneficio; las salidas ocurren solo cuando la regla de pivote solicita un cambio de dirección.
  • Debido a que la estrategia siempre está en el mercado, los controles de riesgo como el monitoreo de máxima drawdown o los filtros de sesión deben manejarse externamente si se requieren.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simple pivot strategy that flips position based on the previous bar pivot.
/// </summary>
public class SimplePivotStrategy : Strategy
{
	private enum TradeDirections
	{
		None,
		Long,
		Short,
	}

	private readonly StrategyParam<DataType> _candleType;

	private TradeDirections _lastDirection = TradeDirections.None;
	private decimal _previousHigh;
	private decimal _previousLow;
	private bool _hasPreviousCandle;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public SimplePivotStrategy()
	{

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "Data");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
	}

	protected override void OnReseted()
	{
		base.OnReseted();

		_lastDirection = TradeDirections.None;
		_previousHigh = 0m;
		_previousLow = 0m;
		_hasPreviousCandle = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

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

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

		if (!_hasPreviousCandle)
		{
			// Collect the very first completed candle as the seed for the pivot calculation.
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			_hasPreviousCandle = true;
			return;
		}

		var pivot = (_previousHigh + _previousLow) / 2m;
		var desiredDirection = TradeDirections.Long;

		// When the new open sits between the previous high and pivot we switch to a short bias.
		if (candle.OpenPrice < _previousHigh && candle.OpenPrice > pivot)
			desiredDirection = TradeDirections.Short;

		if (desiredDirection == _lastDirection && _lastDirection != TradeDirections.None)
		{
			// Keep the existing position when direction has not changed.
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			return;
		}

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			return;
		}

		CloseExistingPosition();

		if (desiredDirection == TradeDirections.Long)
		{
			// Enter a long position when the open is below the pivot.
			BuyMarket(Volume);
		}
		else
		{
			// Enter a short position when the open is above the pivot zone.
			SellMarket(Volume);
		}

		_lastDirection = desiredDirection;
		_previousHigh = candle.HighPrice;
		_previousLow = candle.LowPrice;
	}

	private void CloseExistingPosition()
	{
		if (Position > 0)
		{
			// Flip from long to flat before opening the opposite direction.
			SellMarket(Math.Abs(Position));
			_lastDirection = TradeDirections.None;
		}
		else if (Position < 0)
		{
			// Flip from short to flat before opening the opposite direction.
			BuyMarket(Math.Abs(Position));
			_lastDirection = TradeDirections.None;
		}
	}
}