Открыть на GitHub

Стратегия BandOsMa

Обзор

BandOsMa — перенос эксперта MetaTrader 5 "BandOsMA" на платформу StockSharp. Стратегия анализирует гистограмму MACD (OsMA), строит на ней полосы Боллинджера и отслеживает пробои за пределы канала. Дополнительное скользящее среднее по OsMA помогает определить, когда сигнал перестаёт быть актуальным.

Стратегия работает с одним инструментом и таймфреймом, выбранными пользователем. Все расчёты выполняются на закрытых свечах через высокоуровневую подписку StockSharp.

Логика торговли

  1. Индикаторы
    • MovingAverageConvergenceDivergenceSignal используется для получения гистограммы MACD (OsMA).
    • BollingerBands применяется непосредственно к значениям OsMA для выявления экстремальных отклонений.
    • Настраиваемое скользящее среднее сглаживает OsMA и служит фильтром выхода.
  2. Вход в позицию
    • Покупка: текущая OsMA закрывается ниже нижней полосы, а предыдущая свеча находилась выше неё.
    • Продажа: текущая OsMA закрывается выше верхней полосы, а предыдущая свеча была ниже неё.
  3. Выход
    • Сигнал сбрасывается при обратном пересечении гистограммы и скользящей средней.
    • Если открытая позиция не соответствует активному сигналу, она закрывается немедленно.
    • На каждую сделку устанавливается стоп-лосс в пунктах, который одновременно используется как трейлинг-стоп с шагом StopLossPoints / 50 (по аналогии с MQL-реализацией).

Управление позицией

  • Стоп-лосс и трейлинг: расстояние задаётся в пунктах MetaTrader и переводится в цену с помощью PriceStep. Тот же диапазон применён к трейлингу; стоп переносится вперёд при улучшении цены закрытия на величину шага трейлинга.
  • Единственная позиция: стратегия держит только один нетто-объём. При появлении противоположного сигнала текущая позиция закрывается, после чего рассматривается новый вход.

Параметры

Группа Параметр Описание Значение по умолчанию
General CandleType Таймфрейм для расчётов. H1
Risk LotSize Торговый объём в лотах. 0.01
Risk StopLossPoints Стоп-лосс в пунктах MetaTrader (также задаёт трейлинг). 1000
Indicators MacdFastPeriod Быстрая EMA в MACD. 12
Indicators MacdSlowPeriod Медленная EMA в MACD. 26
Indicators MacdSignalPeriod EMA сигнальной линии MACD. 9
Indicators PriceType Тип цены для MACD (Close, Open, High, Low, Median, Typical, Weighted). Typical
Indicators BollingerPeriod Период полос Боллинджера по OsMA. 26
Indicators BollingerShift Сдвиг буферов полос Боллинджера (неотрицательный). 0
Indicators BollingerDeviation Множитель стандартного отклонения. 2
Indicators MovingAveragePeriod Период сглаживающего скользящего среднего OsMA. 10
Indicators MovingAverageShift Сдвиг буфера скользящего среднего (неотрицательный). 0
Indicators MovingAverageMethod Тип скользящей средней (Simple, Exponential, Smoothed, LinearWeighted). Simple

Особенности реализации

  • Используется WhenCandlesFinished, поэтому расчёты выполняются только на завершённых свечах.
  • Значения индикаторов сохраняются в списках, что позволяет имитировать сдвиги буферов MetaTrader. Отрицательные значения shift не поддерживаются.
  • Трейлинг-стоп обновляется по ценам закрытия свечей. Для более точного тикового трейлинга можно увеличить расстояние в пунктах.

Как использовать

  1. Выберите инструмент и таймфрейм в StockSharp.
  2. Настройте ключевые параметры (CandleType, LotSize, периоды индикаторов).
  3. Запустите стратегию — она подпишется на свечи, рассчитает индикаторы и будет исполнять сделки в соответствии с описанной логикой.
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();
	}
}