The Pipsover 8167 strategy is a StockSharp port of the MetaTrader 4 expert advisor Pipsover.mq4 distributed with build 8167. The expert searches for strong Chaikin oscillator spikes that appear immediately after a pullback to the 20-period simple moving average on the previous candle. When that combination happens, the script opens a position in the direction of the impulse and protects it with fixed stop-loss and take-profit distances (70 and 140 points respectively in the original MQL code). This C# version rebuilds the exact same logic using high-level StockSharp components so that no direct buffer access is required.
The implementation uses the Accumulation/Distribution Line (ADL) indicator and two exponential moving averages to reconstruct the Chaikin oscillator values produced by iCustom("Chaikin", ...) in MetaTrader. All trading decisions are delayed until the candle is fully closed, replicating the OrdersTotal() and Close[1] / Open[1] checks from the source script.
Indicators and Signals
Simple Moving Average (SMA 20) – applied to candle closes. The previous candle must pierce the SMA (low below for longs, high above for shorts) while keeping a body in the direction of the setup.
Chaikin Oscillator (EMA 3 – EMA 10 of ADL) – rebuilt internally from the ADL stream to mirror iCustom("Chaikin", 0, 0, 1) readings. Entry and exit thresholds are expressed in absolute oscillator units.
Price Action Filter – the strategy checks the previous candle body direction: bullish bodies enable long trades while bearish bodies enable shorts.
Previous high is above the SMA20 value from that candle.
Previous Chaikin value is above OpenLevel.
No position is currently open.
Exit Conditions
Long positions close when the next candle satisfies: bearish body, high above SMA20, and Chaikin above CloseLevel (default 90).
Short positions close when the next candle has a bullish body, low below SMA20, and Chaikin below -CloseLevel.
Additionally, every trade carries a protective stop at StopLossPoints and a take-profit at TakeProfitPoints, both expressed in price steps of the selected instrument.
Risk Management
Stop-loss distance: StopLossPoints × PriceStep (defaults to 70 points).
Take-profit distance: TakeProfitPoints × PriceStep (defaults to 140 points).
Position size: configurable via TradeVolume, mapped directly to the Volume property of the StockSharp strategy and used for all market orders.
Parameters
Parameter
Default
Description
TradeVolume
0.1
Market order volume (lots or contracts, depending on the security).
MaLength
20
Period of the SMA used for the pullback check.
StopLossPoints
70
Stop-loss distance measured in price steps.
TakeProfitPoints
140
Take-profit distance measured in price steps.
OpenLevel
55
Absolute Chaikin oscillator threshold that unlocks new entries.
CloseLevel
90
Absolute Chaikin oscillator threshold that forces exits.
ChaikinFastLength
3
Fast EMA length in the Chaikin reconstruction.
ChaikinSlowLength
10
Slow EMA length in the Chaikin reconstruction.
CandleType
H1
Time-frame used to subscribe for candles and calculate indicators.
Implementation Notes
Candles and indicators are connected via SubscribeCandles().Bind(...), so the strategy stays within the high-level API guidelines.
Chaikin values are computed in memory by feeding ADL readings into two EMA objects, avoiding prohibited calls such as GetValue() on indicator buffers.
Previous candle information is cached inside the strategy state to reproduce the MQL access pattern Close[1], Low[1], High[1], and iCustom(...,1).
Stop-loss and take-profit levels are tracked manually because the original expert sent plain market orders with static offsets instead of server-side protective orders.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Pipsover8167Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Pipsover8167Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevEma = ema;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage
class pipsover8167_strategy(Strategy):
def __init__(self):
super(pipsover8167_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown = 0
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(pipsover8167_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_close = close
self._prev_ema = ema_val
return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume)
self._cooldown = 6
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume)
self._cooldown = 6
self._prev_close = close
self._prev_ema = ema_val
def OnReseted(self):
super(pipsover8167_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown = 0
def CreateClone(self):
return pipsover8167_strategy()