This strategy reproduces the "SimplePivot" MetaTrader 5 expert advisor. It continuously evaluates the relationship between the
current bar open and the prior bar's pivot level, always maintaining a single directional position. When the bias flips, the
strategy closes the existing position and immediately opens one in the opposite direction.
Instruments: Any instrument that provides candle data for the selected timeframe.
Timeframes: Configurable through the Candle Type parameter (default 1-hour candles).
Orders: Market orders sized by the Volume parameter.
How It Works
Pivot calculation
Wait for at least one completed candle to seed the calculation.
Compute the pivot of the previous candle as the arithmetic mean of its high and low prices.
Retain the previous high and low so the pivot for the next bar can be produced immediately when a new candle finishes.
Directional decision
Default bias is long (buy).
If the current candle opens below the previous high while staying above the pivot, the bias switches to short (sell).
If the desired direction is unchanged from the last executed trade, the existing position is preserved and no new orders are
sent.
Position management
If the desired direction differs from the current trade, the running position is flattened via an opposite market order.
After flatting, a market order sized by Volume establishes the new directional exposure.
The process repeats on each finished candle, ensuring the strategy is always either long or short.
Parameters
Volume: Trade size used for every entry. It also determines the size of the closing order when the strategy flips
direction.
Candle Type: Data type of the candles used for pivot and entry calculations. The default is a 1-hour time frame but any
available time frame can be selected.
Additional Notes
The logic reacts on fully completed candles (CandleStates.Finished) to avoid repeated signals while a candle is still
forming.
No stops or profit targets are defined; exits occur only when the pivot rule requests a direction change.
Because the strategy is always in the market, risk controls such as max drawdown monitoring or session filters should be
handled externally if required.
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()