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:
Espera a que una vela terminada obtenga los valores de apertura, máximo y mínimo.
Utiliza el rango de velas anterior para construir un pivote simple en el punto medio.
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.
Abre una nueva posición corta cuando la vela actual se abre en la mitad superior del rango.
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
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.
Por cada vela completa posterior:
Calcula pivot = (previousHigh + previousLow) / 2.
Si Open < previousHighyOpen > 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).
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.
Si la dirección cambia, la posición actual se cierra mediante ClosePosition() y se envía una nueva orden de mercado mediante OrderVolume.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class simple_pivot_flip_strategy(Strategy):
"""Simple pivot-based strategy. Compares current candle open with the previous
candle range midpoint. If open is in the upper half of the previous range, sell;
otherwise buy. Closes existing position before reversing."""
def __init__(self):
super(simple_pivot_flip_strategy, self).__init__()
self._order_volume = self.Param("OrderVolume", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Order Volume", "Market order volume used for entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1))) \
.SetDisplay("Candle Type", "Type of candles used for pivot calculation", "Data")
self._previous_high = 0.0
self._previous_low = 0.0
self._has_previous_candle = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def OrderVolume(self):
return self._order_volume.Value
def OnReseted(self):
super(simple_pivot_flip_strategy, self).OnReseted()
self._previous_high = 0.0
self._previous_low = 0.0
self._has_previous_candle = False
def OnStarted2(self, time):
super(simple_pivot_flip_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
open_price = float(candle.OpenPrice)
if not self._has_previous_candle:
self._previous_high = high
self._previous_low = low
self._has_previous_candle = True
return
# Calculate the pivot as the midpoint of the previous candle range
pivot = (self._previous_high + self._previous_low) / 2.0
# Default to buy; if open is in upper half of previous range, sell
desired_buy = True
if open_price < self._previous_high and open_price > pivot:
desired_buy = False
# Skip re-entry if already holding position in desired direction
if desired_buy and self.Position > 0:
self._previous_high = high
self._previous_low = low
return
if not desired_buy and self.Position < 0:
self._previous_high = high
self._previous_low = low
return
# Close existing position
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
# Open new position
if desired_buy:
self.BuyMarket()
else:
self.SellMarket()
# Update reference range for next candle
self._previous_high = high
self._previous_low = low
def CreateClone(self):
return simple_pivot_flip_strategy()