The Above Below MA strategy mirrors the MetaTrader expert advisor Above Below MA (barabashkakvn's edition). It monitors how far current prices trade relative to a configurable moving average and allows trades only when price is on the "wrong" side of the average by at least a defined distance while the average itself trends in the anticipated direction. The logic has been ported to the StockSharp high level API and executes exclusively on completed candles.
Overview
Market Regime: Works best on instruments that frequently retest a moving average before resuming the trend.
Instruments: Any instrument supported by your StockSharp connection. Forex pairs benefit the most because the original script measured distance in pips.
Timeframe: Adjustable through the Candle Type parameter (default 1-minute time frame).
Position Direction: Both long and short trades are supported, but only one net position can exist at any given time.
Strategy Logic
Calculate a moving average on the selected candle series. The averaging method (SMA, EMA, SMMA, WMA), applied price (close, open, high, low, median, typical, weighted) and forward shift replicate the MetaTrader inputs.
Convert the minimum distance expressed in pips into an actual price offset using the instrument's PriceStep. If the broker does not publish a price step, the distance filter is skipped automatically.
On each finished candle:
Long setup:
Candle open and close must lie at least the configured distance below the shifted moving average.
The moving average must be rising compared with the previous candle.
Short setup:
Candle open and close must lie at least the configured distance above the shifted moving average.
The moving average must be falling compared with the previous candle.
The strategy closes any opposite position before sending a new market order in the signal direction. No simultaneous long/short exposure is allowed.
All trading decisions are made on completed candles to avoid repeated entries inside a forming bar. Orders are executed via BuyMarket or SellMarket with the configured volume.
Parameters
Parameter
Description
MaPeriod
Moving average length. Default 6.
MaShift
Number of candles to shift the moving average forward. A value of 0 uses the current bar, n uses the value from n bars ago. Default 0.
MaMethod
Moving average type: Simple, Exponential, Smoothed, or Weighted. Default Exponential.
Required distance in pips between candle prices and the moving average. Converted using PriceStep. Default 5.
CandleType
Candle type driving indicator updates. Default 1-minute time frame.
TradeVolume
Order volume for new entries. Default 1.
Additional Notes
No stop-loss or take-profit logic is included. Risk management must be implemented via portfolio settings or external modules.
The moving average shift buffer is kept minimal and respects the "no collections" guideline by storing only the values required for the specified shift.
When PriceStep is unavailable the minimum distance filter cannot be evaluated, so entries rely solely on the moving average conditions.
The strategy draws the candle series, the moving average indicator, and your trades on the default chart area when a chart container is available.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Above/Below MA strategy. Trades when price crosses above or below a moving average.
/// </summary>
public class AboveBelowMaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private decimal? _prevClose;
private decimal? _prevMa;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public AboveBelowMaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = null;
_prevMa = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
_prevMa = null;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maVal)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevClose = close;
_prevMa = maVal;
return;
}
if (_prevClose == null || _prevMa == null)
{
_prevClose = close;
_prevMa = maVal;
return;
}
// Price crosses above MA → buy
if (_prevClose.Value <= _prevMa.Value && close > maVal && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Price crosses below MA → sell
else if (_prevClose.Value >= _prevMa.Value && close < maVal && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevClose = close;
_prevMa = maVal;
}
}
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 CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class above_below_ma_strategy(Strategy):
"""
Above/Below MA strategy. Trades when price crosses above or below a moving average.
"""
def __init__(self):
super(above_below_ma_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ma_period = self.Param("MaPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("MA Period", "Moving average period", "Indicators")
self._prev_close = None
self._prev_ma = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
def OnReseted(self):
super(above_below_ma_strategy, self).OnReseted()
self._prev_close = None
self._prev_ma = None
def OnStarted2(self, time):
super(above_below_ma_strategy, self).OnStarted2(time)
self._prev_close = None
self._prev_ma = None
sma = SimpleMovingAverage()
sma.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, ma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
if self._prev_close is None or self._prev_ma is None:
self._prev_close = close
self._prev_ma = ma_val
return
# Price crosses above MA -> buy
if self._prev_close <= self._prev_ma and close > ma_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Price crosses below MA -> sell
elif self._prev_close >= self._prev_ma and close < ma_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_ma = ma_val
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return above_below_ma_strategy()