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
Esperar al menos una vela completada para inicializar el cálculo.
Calcular el pivote de la vela anterior como la media aritmética de sus precios máximo y mínimo.
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
El sesgo predeterminado es largo (compra).
Si la vela actual abre por debajo del máximo anterior mientras permanece por encima del pivote, el sesgo cambia a corto (venta).
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
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.
Después de liquidar, una orden de mercado dimensionada por Volume establece la nueva exposición direccional.
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;
}
}
}
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
from datatype_extensions import *
from indicator_extensions import *
class simple_pivot_strategy(Strategy):
def __init__(self):
super(simple_pivot_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles", "Data")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(simple_pivot_strategy, self).OnReseted()
self._prev_high = 0
self._prev_low = 0
self._has_prev = False
self._last_dir = 0 # 1=long, -1=short
def OnStarted2(self, time):
super(simple_pivot_strategy, self).OnStarted2(time)
self._prev_high = 0
self._prev_low = 0
self._has_prev = False
self._last_dir = 0
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
return
pivot = (self._prev_high + self._prev_low) / 2.0
open_price = float(candle.OpenPrice)
desired = 1 # long
if open_price < self._prev_high and open_price > pivot:
desired = -1 # short
if desired == self._last_dir and self._last_dir != 0:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
# Close existing
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
if desired == 1:
self.BuyMarket()
else:
self.SellMarket()
self._last_dir = desired
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
def CreateClone(self):
return simple_pivot_strategy()