GitHub で見る

BandOsMa Strategy

Overview

The BandOsMa Strategy converts the MetaTrader 5 "BandOsMA" expert advisor into a StockSharp strategy. It evaluates the MACD histogram (OsMA) using Bollinger Bands built directly on the histogram values. Breakouts above or below the bands create entry signals, while an additional moving average of the histogram manages signal exits.

The strategy operates on a single symbol and timeframe selected by the user. Indicator values are calculated on finished candles using StockSharp's high-level candle subscriptions.

Trading Logic

  1. Indicators
    • MovingAverageConvergenceDivergenceSignal provides the MACD histogram (OsMA).
    • BollingerBands is applied to the OsMA sequence to detect extreme deviations.
    • A configurable moving average smooths the histogram and acts as an exit filter.
  2. Entry
    • A long signal appears when the current OsMA closes below the lower band while the previous bar stayed above it.
    • A short signal appears when the current OsMA closes above the upper band while the previous bar stayed below it.
  3. Exit
    • Signals are cleared when the histogram crosses the moving average in the opposite direction.
    • When an open position no longer matches the active signal, the position is closed immediately.
    • A pip-based stop-loss is attached to each position. The stop also acts as a trailing stop with the same distance and a trailing step equal to StopLossPoints / 50 (mirroring the MetaTrader helper class).

Position Management

  • Stop Loss & Trailing: The stop distance is expressed in MetaTrader points and converted into price units using the instrument's PriceStep. The same distance is used for the trailing stop, which moves forward once the close price improves by at least the trailing step.
  • One Position at a Time: Only one net position is maintained. Opposite signals close the current position before considering a new entry.

Parameters

Group Name Description Default
General CandleType Timeframe for candle subscription and indicator calculation. H1
Risk LotSize Trade volume in lots. 0.01
Risk StopLossPoints Stop-loss distance expressed in MetaTrader points (also used for trailing). 1000
Indicators MacdFastPeriod Fast EMA length in MACD. 12
Indicators MacdSlowPeriod Slow EMA length in MACD. 26
Indicators MacdSignalPeriod Signal EMA length in MACD. 9
Indicators PriceType Applied price for MACD input (Close, Open, High, Low, Median, Typical, Weighted). Typical
Indicators BollingerPeriod Period of Bollinger Bands over the OsMA sequence. 26
Indicators BollingerShift Shift applied to Bollinger buffers (non-negative). 0
Indicators BollingerDeviation Standard deviation multiplier for Bollinger Bands. 2
Indicators MovingAveragePeriod Length of the moving average applied to OsMA. 10
Indicators MovingAverageShift Shift applied to the moving average buffer (non-negative). 0
Indicators MovingAverageMethod Moving average type (Simple, Exponential, Smoothed, LinearWeighted). Simple

Implementation Notes

  • Candle processing uses WhenCandlesFinished to ensure only final bars drive the logic.
  • Indicator values are stored in history buffers to emulate MetaTrader-style buffer shifts. Negative shifts are not supported; use zero or positive values as in the original expert defaults.
  • Trailing stops rely on candle closes rather than tick-by-tick updates. Adjust the pip distance if precise tick-level trailing is required.

Usage

  1. Select the desired symbol and timeframe in StockSharp.
  2. Configure the parameters, especially CandleType, LotSize, and indicator periods.
  3. Start the strategy; it will subscribe to candles, compute the indicators, and execute trades according to the described logic.
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>
/// Strategy combining the MACD histogram (OsMA) with Bollinger Bands to trade reversals.
/// When OsMA crosses below lower Bollinger band, buy signal is generated.
/// When OsMA crosses above upper Bollinger band, sell signal is generated.
/// </summary>
public class BandOsMaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _macdFastPeriod;
	private readonly StrategyParam<int> _macdSlowPeriod;
	private readonly StrategyParam<int> _macdSignalPeriod;
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerDeviation;

	private decimal _prevOsma;
	private decimal _prevUpper;
	private decimal _prevLower;
	private bool _hasPrev;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int MacdFastPeriod
	{
		get => _macdFastPeriod.Value;
		set => _macdFastPeriod.Value = value;
	}

	public int MacdSlowPeriod
	{
		get => _macdSlowPeriod.Value;
		set => _macdSlowPeriod.Value = value;
	}

	public int MacdSignalPeriod
	{
		get => _macdSignalPeriod.Value;
		set => _macdSignalPeriod.Value = value;
	}

	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	public decimal BollingerDeviation
	{
		get => _bollingerDeviation.Value;
		set => _bollingerDeviation.Value = value;
	}

	public BandOsMaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe", "General");

		_macdFastPeriod = Param(nameof(MacdFastPeriod), 20)
			.SetDisplay("MACD Fast", "Fast EMA length", "Indicators");

		_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 50)
			.SetDisplay("MACD Slow", "Slow EMA length", "Indicators");

		_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 12)
			.SetDisplay("MACD Signal", "Signal EMA length", "Indicators");

		_bollingerPeriod = Param(nameof(BollingerPeriod), 14)
			.SetDisplay("Bollinger Period", "OsMA Bollinger Bands period", "Indicators");

		_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
			.SetDisplay("Bollinger Deviation", "Bollinger Bands deviation", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = MacdFastPeriod },
				LongMa = { Length = MacdSlowPeriod }
			},
			SignalMa = { Length = MacdSignalPeriod }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, macd);
			DrawOwnTrades(area);
		}
	}

	private BollingerBands _bollinger;

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var val = (IMovingAverageConvergenceDivergenceSignalValue)macdValue;
		if (val.Macd is not decimal macdLine || val.Signal is not decimal signalLine)
			return;

		var osma = macdLine - signalLine;

		_bollinger ??= new BollingerBands
		{
			Length = BollingerPeriod,
			Width = BollingerDeviation
		};

		var bbResult = (BollingerBandsValue)_bollinger.Process(new DecimalIndicatorValue(_bollinger, osma, candle.CloseTime));
		if (bbResult.UpBand is not decimal upper || bbResult.LowBand is not decimal lower)
		{
			return;
		}

		if (_hasPrev)
		{
			// Buy: OsMA crosses below lower band (reversal up expected)
			if (_prevOsma > _prevLower && osma <= lower && Position <= 0)
			{
				BuyMarket(Position < 0 ? Math.Abs(Position) + 1 : 1);
			}
			// Sell: OsMA crosses above upper band (reversal down expected)
			else if (_prevOsma < _prevUpper && osma >= upper && Position >= 0)
			{
				SellMarket(Position > 0 ? Math.Abs(Position) + 1 : 1);
			}
		}

		_prevOsma = osma;
		_prevUpper = upper;
		_prevLower = lower;
		_hasPrev = true;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_prevOsma = 0;
		_prevUpper = 0;
		_prevLower = 0;
		_hasPrev = false;
		_bollinger = null;

		base.OnReseted();
	}
}