This strategy is a high-level C# port of the MetaTrader 4 Expert Advisor stored in MQL/7610/Simplepivot_www_forex-instruments_info.mq4. The original program checks the open price of each new candle against the previous candle range and flips between long and short market positions. The StockSharp version keeps the same behaviour by relying exclusively on high-level helpers such as SubscribeCandles, Bind, BuyMarket, SellMarket, and ClosePosition.
The converted logic:
Waits for a finished candle to obtain the open, high, and low values.
Uses the previous candle range to build a simple pivot at the midpoint.
Opens a new long position when the current candle opens in the lower half of the range or gaps above the previous high.
Opens a new short position when the current candle opens in the upper half of the range.
Always closes the existing position before entering in the opposite direction, replicating the single-ticket behaviour of the MQL version.
No stop-loss or take-profit levels are implemented in the original Expert Advisor, so the position is reversed only when a new candle dictates a different direction.
Parameters
Name
Default
Description
OrderVolume
1
Market order volume used when entering a position.
CandleType
1 minute time frame
Candle type requested from the data feed.
Trading Logic Details
The very first finished candle is stored and used as the reference for the next decision. No order is sent until there is a complete candle to analyse.
For every subsequent completed candle:
Compute pivot = (previousHigh + previousLow) / 2.
If Open < previousHighandOpen > pivot, the strategy prepares a short entry.
Otherwise it prepares a long entry (this covers opens in the lower half, opens equal to the pivot, and any gaps above the previous high or below the previous low).
If the strategy already holds a position in the chosen direction, the signal is ignored to avoid paying the spread twice—mirroring the early return found in the MQL code.
If the direction changes, the current position is closed via ClosePosition() and a new market order is sent using OrderVolume.
The previous high/low buffer is updated with the latest completed candle to drive the next decision.
Risk Management
The converted algorithm does not include stops or profit targets. Position sizing is controlled only by the OrderVolume parameter, so risk should be managed externally (for example by adjusting the volume or by combining the strategy with account-level protections).
Visualisation
When a chart area is available, the strategy plots the requested candles and the executed trades, which helps validate the pivot flips during backtests.
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()