Ver en GitHub

Estrategia simple de giro de pivote

Descripción general

Esta estrategia es un puerto C# de alto nivel del MetaTrader 4 Expert Advisor almacenado en MQL/7610/Simplepivot_www_forex-instruments_info.mq4. El programa original compara el precio de apertura de cada nueva vela con el rango de velas anterior y alterna entre posiciones de mercado largas y cortas. La versión StockSharp mantiene el mismo comportamiento al confiar exclusivamente en ayudantes de alto nivel como SubscribeCandles, Bind, BuyMarket, SellMarket y ClosePosition.

La lógica convertida:

  1. Espera a que una vela terminada obtenga los valores de apertura, máximo y mínimo.
  2. Utiliza el rango de velas anterior para construir un pivote simple en el punto medio.
  3. Abre una nueva posición larga cuando la vela actual se abre en la mitad inferior del rango o se abre por encima del máximo anterior.
  4. Abre una nueva posición corta cuando la vela actual se abre en la mitad superior del rango.
  5. Siempre cierra la posición existente antes de ingresar en la dirección opuesta, replicando el comportamiento de boleto único de la versión MQL.

No se implementan niveles de stop-loss o take-profit en el Asesor Experto original, por lo que la posición se invierte sólo cuando una nueva vela dicta una dirección diferente.

Parámetros

Nombre Predeterminado Descripción
OrderVolume 1 Volumen de orden de mercado utilizado al entrar en una posición.
CandleType marco de tiempo de 1 minuto Tipo de vela solicitado desde la fuente de datos.

Detalles de la lógica comercial

  1. La primera vela terminada se almacena y se utiliza como referencia para la siguiente decisión. No se envía ningún pedido hasta que no haya una vela completa para analizar.
  2. Por cada vela completa posterior:
    • Calcula pivot = (previousHigh + previousLow) / 2.
    • Si Open < previousHigh y Open > pivot, la estrategia prepara una entrada corta.
    • De lo contrario, prepara una entrada larga (esto cubre las aperturas en la mitad inferior, las aperturas iguales al pivote y cualquier hueco por encima del máximo anterior o por debajo del mínimo anterior).
  3. Si la estrategia ya mantiene una posición en la dirección elegida, la señal se ignora para evitar pagar el diferencial dos veces, reflejando el rendimiento anticipado que se encuentra en el código MQL.
  4. Si la dirección cambia, la posición actual se cierra mediante ClosePosition() y se envía una nueva orden de mercado mediante OrderVolume.
  5. El buffer máximo/bajo anterior se actualiza con la última vela completada para impulsar la siguiente decisión.

Gestión del riesgo

El algoritmo convertido no incluye paradas ni objetivos de ganancias. El tamaño de la posición está controlado únicamente por el parámetro OrderVolume, por lo que el riesgo debe gestionarse externamente (por ejemplo, ajustando el volumen o combinando la estrategia con protecciones a nivel de cuenta).

Visualización

Cuando hay un área del gráfico disponible, la estrategia traza las velas solicitadas y las operaciones ejecutadas, lo que ayuda a validar los cambios de pivote durante las pruebas retrospectivas.

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;

using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simple pivot-based strategy converted from the MetaTrader expert advisor in MQL/7610.
/// The strategy compares the current candle open with the previous candle range to decide
/// whether the next trade should be long or short.
/// </summary>
public class SimplePivotFlipStrategy : Strategy
{
	private readonly StrategyParam<decimal> _orderVolume;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _previousHigh;
	private decimal _previousLow;
	private bool _hasPreviousCandle;

	/// <summary>
	/// Order volume used for market entries.
	/// </summary>
	public decimal OrderVolume
	{
		get => _orderVolume.Value;
		set => _orderVolume.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of the <see cref="SimplePivotFlipStrategy"/> class.
	/// </summary>
	public SimplePivotFlipStrategy()
	{
		_orderVolume = Param(nameof(OrderVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Order Volume", "Market order volume used for entries.", "General");

		_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles used for pivot calculation.", "Data");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_previousHigh = 0m;
		_previousLow = 0m;
		_hasPreviousCandle = false;
	}

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

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

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

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

		

		if (!_hasPreviousCandle)
		{
			// Store the first completed candle to build the reference range.
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			_hasPreviousCandle = true;
			return;
		}

		// Calculate the pivot as the midpoint of the previous candle range.
		var pivot = (_previousHigh + _previousLow) / 2m;
		var desiredSide = Sides.Buy;

		// If the new candle opens inside the upper half of the previous range we go short.
		if (candle.OpenPrice < _previousHigh && candle.OpenPrice > pivot)
			desiredSide = Sides.Sell;

		// Skip re-entry if we already hold a position in the desired direction.
		if ((desiredSide == Sides.Buy && Position > 0) || (desiredSide == Sides.Sell && Position < 0))
		{
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			return;
		}

		if (Position > 0)
			SellMarket();
		else if (Position < 0)
			BuyMarket();

		if (desiredSide == Sides.Buy)
		{
			BuyMarket();
		}
		else
		{
			SellMarket();
		}

		// Update reference range for the next candle.
		_previousHigh = candle.HighPrice;
		_previousLow = candle.LowPrice;
	}
}