Pipso is a night-session breakout system converted from the MetaTrader expert advisor Pipso.mq4. The strategy measures the
highest and lowest prices of the previously completed candles and reacts when the market breaks outside of that range. Every
breakout reverses the position: long positions are closed and a short is opened when price breaks above the recent highs, while
short positions are covered and a new long is established when price pierces the recent lows. Protective stops are derived from
the width of the range so that the stop distance automatically adapts to current volatility.
How It Works
Subscribe to the configured time-frame (15 minutes by default) and wait for the indicators to build a complete history.
For every new finished candle, compute the highest high and lowest low of the previous BreakoutPeriod candles. The current
candle is not part of that range, exactly as in the original EA where iHighest(..., shift = 1) skips the working bar.
Recalculate the stop distance as (high - low) * StopLossMultiplier while enforcing the minimum distance defined by
MinStopDistance.
Maintain a trading window defined by SessionStartHour and SessionLengthHours. When the window crosses midnight on Friday
it is extended by two days so that open trades survive the weekend just like in MetaTrader.
When the candle's high exceeds the stored breakout high:
Close any existing long position and, if trading is allowed, open a short position with size OrderVolume.
Attach a stop-loss above the entry price using the calculated stop distance.
When the candle's low falls below the stored breakout low:
Close any existing short position and, if trading is allowed, open a long position with size OrderVolume.
Attach a stop-loss below the entry price using the calculated stop distance.
Protective stops are evaluated on every finished candle. If the low touches the long stop or the high reaches the short stop,
the position is flattened immediately.
Trading Session Logic
SessionStartHour is expressed in exchange hours. The window length is set with SessionLengthHours.
If the session extends beyond 24 hours and the current day is Friday, the end of the window is shifted forward by 48 hours so
that trading resumes on Monday, matching the weekend handling in the MQL4 code.
Outside of the trading window the strategy only closes existing positions; new trades are allowed again once the window opens.
Parameters
Name
Description
Default
CandleType
Candle data type used for signal calculation.
15-minute time-frame
OrderVolume
Fixed order size for every market order.
1
SessionStartHour
Hour of day when the breakout window begins.
21
SessionLengthHours
Duration of the trading window in hours.
9
BreakoutPeriod
Number of completed candles that define the breakout range.
36
StopLossMultiplier
Multiplier applied to the range width to derive the stop distance (value 3 corresponds to the original SLpp = 300).
3
MinStopDistance
Minimal stop-loss distance in absolute price units, emulating the MetaTrader stop level restriction.
0
Notes
The strategy uses market orders only; there is no take-profit. The protective stop-loss is the only exit mechanism besides
the opposite breakout signal.
When switching from long to short (or vice versa) the strategy sends a single market order that both closes the previous
position and opens the new one, mirroring the behaviour of the source EA that sequentially called OrderClose and
OrderSend.
Indicator lines for the breakout highs and lows are plotted automatically on the strategy chart together with executed trades.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Range breakout strategy that uses Highest/Lowest channel.
/// Enters on breakouts above/below the channel and exits on reversion.
/// </summary>
public class PipsoNightBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _breakoutPeriod;
private decimal _entryPrice;
private decimal _prevHighest;
private decimal _prevLowest;
private bool _hasPrev;
public PipsoNightBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_breakoutPeriod = Param(nameof(BreakoutPeriod), 36)
.SetDisplay("Breakout Period", "Period for Highest/Lowest channel.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BreakoutPeriod
{
get => _breakoutPeriod.Value;
set => _breakoutPeriod.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevHighest = 0;
_prevLowest = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_prevHighest = 0;
_prevLowest = 0;
_hasPrev = false;
var highest = new Highest { Length = BreakoutPeriod };
var lowest = new Lowest { Length = BreakoutPeriod };
var ema = new ExponentialMovingAverage { Length = BreakoutPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var mid = (highestValue + lowestValue) / 2m;
// Exit conditions
if (Position > 0)
{
// Exit when price reverts to middle or stop-loss
if (close < mid || (_entryPrice > 0 && close < _entryPrice * 0.98m))
{
SellMarket();
}
}
else if (Position < 0)
{
if (close > mid || (_entryPrice > 0 && close > _entryPrice * 1.02m))
{
BuyMarket();
}
}
// Entry conditions: breakout above previous highest or below previous lowest
if (Position == 0 && _hasPrev)
{
if (close > _prevHighest && close > emaValue)
{
_entryPrice = close;
BuyMarket();
}
else if (close < _prevLowest && close < emaValue)
{
_entryPrice = close;
SellMarket();
}
}
_prevHighest = highestValue;
_prevLowest = lowestValue;
_hasPrev = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class pipso_night_breakout_strategy(Strategy):
def __init__(self):
super(pipso_night_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._breakout_period = self.Param("BreakoutPeriod", 36).SetDisplay("Breakout Period", "Period for Highest/Lowest channel", "Indicators")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(pipso_night_breakout_strategy, self).OnReseted()
self._entry_price = 0
self._prev_highest = 0
self._prev_lowest = 0
self._has_prev = False
def OnStarted2(self, time):
super(pipso_night_breakout_strategy, self).OnStarted2(time)
self._entry_price = 0
self._prev_highest = 0
self._prev_lowest = 0
self._has_prev = False
highest = Highest()
highest.Length = self._breakout_period.Value
lowest = Lowest()
lowest.Length = self._breakout_period.Value
ema = ExponentialMovingAverage()
ema.Length = self._breakout_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(highest, lowest, ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, highest_val, lowest_val, ema_val):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
mid = (highest_val + lowest_val) / 2.0
if self.Position > 0:
if close < mid or (self._entry_price > 0 and close < self._entry_price * 0.98):
self.SellMarket()
elif self.Position < 0:
if close > mid or (self._entry_price > 0 and close > self._entry_price * 1.02):
self.BuyMarket()
if self.Position == 0 and self._has_prev:
if close > self._prev_highest and close > ema_val:
self._entry_price = close
self.BuyMarket()
elif close < self._prev_lowest and close < ema_val:
self._entry_price = close
self.SellMarket()
self._prev_highest = highest_val
self._prev_lowest = lowest_val
self._has_prev = True
def CreateClone(self):
return pipso_night_breakout_strategy()