Открыть на GitHub

Стратегия Bobnaley

Обзор

Стратегия Bobnaley переносит эксперт MetaTrader 5 «bobnaley» на высокоуровневый API StockSharp. Она сочетает трендовый фильтр в виде простой скользящей средней с осциллятором Стохастик, чтобы находить разворотные точки. В оригинале расчёт велся по тикам; в порте используется закрытие свечей, при этом логика заявок полностью сохранена.

Логика работы

  1. Индикаторы
    • Простая скользящая средняя заданного периода оценивает наклон цены за последние три выборки.
    • Стохастик (главная и сигнальная линии) определяет зоны перекупленности и перепроданности. Для сигналов задействуется только главная линия, сигнальная рассчитывается для совместимости с исходным кодом.
  2. Условия входа
    • Проверка сигналов выполняется только после формирования свечи и получения значений обоих индикаторов.
    • Лонг: последние три значения средней должны строго убывать, текущая цена закрытия должна находиться выше средней; главная линия стохастика должна быть ниже уровня перепроданности, а её предыдущее значение обязано быть выше более раннего (stochVal[1] > stochVal[2] в оригинале).
    • Шорт: последние три значения средней должны строго возрастать, текущая цена закрытия — ниже средней; главная линия стохастика должна быть выше уровня перекупленности, а её предыдущее значение — ниже ещё более раннего.
    • Сделка открывается только при отсутствии действующей позиции, что соответствует проверке PositionSelect у MetaTrader.
  3. Управление рисками
    • После входа стратегия включает защиту StockSharp и выставляет тейк-профит и стоп-лосс на фиксированном расстоянии в ценовых единицах (по умолчанию 0.007 и 0.0035), как в исходном советнике.
    • Перед каждой заявкой оценивается текущая стоимость портфеля и сравнивается с параметром Minimum Balance. Это повторяет фильтр ACCOUNT_FREEMARGIN > 5000: если значение известно и меньше порога, вход пропускается.
  4. Объём позиций
    • Используется фиксированный параметр Base Volume, который воспроизводит рассчитанный в MQL размер лота.

Параметры

Категория Название Описание Значение по умолчанию
Общие Candle Type Тип свечей для расчёта индикаторов. Таймфрейм 5 минут
Торговля Base Volume Фиксированный объём каждой новой позиции. 5
Индикаторы MA Period Период простой скользящей средней. 76
Индикаторы Stochastic Period Длина расчёта основной линии стохастика. 5
Индикаторы Stochastic %K Период сглаживания линии %K. 3
Индикаторы Stochastic %D Период сглаживания линии %D. 3
Индикаторы Stochastic Oversold Уровень перепроданности для основной линии. 30
Индикаторы Stochastic Overbought Уровень перекупленности для основной линии. 70
Риск Take Profit Расстояние от входа до тейк-профита (в ценовых единицах). 0.007
Риск Stop Loss Расстояние от входа до стоп-лосса (в ценовых единицах). 0.0035
Риск Minimum Balance Минимальная стоимость портфеля, допускающая новый вход. 5000

Примечания

  • Оригинал использовал котировки Bid/Ask; в реализации StockSharp в качестве ориентиров применяется цена закрытия свечи.
  • Выход осуществляется только по защитным ордерам, трейлинг-логика не реализована.
  • Настройки стохастика соответствуют значениям 5/3/3 и доступны для оптимизации.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Bobnaley strategy: EMA trend with ATR volatility filter.
/// </summary>
public class BobnaleyStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _atrPeriod;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }

	public BobnaleyStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var atr = new AverageTrueRange { Length = AtrPeriod };

		decimal? prevClose = null;
		decimal? prevEma = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, atr, (candle, emaVal, atrVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				if (prevClose.HasValue && prevEma.HasValue)
				{
					var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
					var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;

					if (crossUp && Position <= 0)
						BuyMarket();
					else if (crossDown && Position >= 0)
						SellMarket();
				}

				prevClose = close;
				prevEma = emaVal;
			})
			.Start();

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