Открыть на GitHub

Стратегия JBrainTrend1Stop

JBrainTrend1Stop — порт советника MetaTrader 5 Exp_JBrainTrend1Stop на платформу StockSharp. Стратегия сочетает две оценки Average True Range, осциллятор Stochastic и три Jurik Moving Average для обнаружения разворотов BrainTrading. Когда сглаженная Jurik цена делает достаточно сильный импульс и Stochastic выходит из нейтральной зоны, стратегия переключает направление, обновляет линию BrainTrend и (при необходимости) переворачивает позицию после заданной задержки.

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

  1. Подписываемся на свечи типа CandleType и подаём их на:
    • Основной AverageTrueRange длиной AtrPeriod.
    • Дополнительный AverageTrueRange длиной AtrPeriod + StopDPeriod.
    • StochasticOscillator с периодом StochasticPeriod и сглаживанием %K в один бар (как в MT5).
    • Три JurikMovingAverage (для High, Low и Close) с параметрами JmaLength и JmaPhase.
  2. Для каждой закрывшейся свечи вычисляем:
    • range = ATR / 2.3 — аналог константы d из оригинала.
    • range1 = ATR_extended * 1.5 — аналог константы s.
    • val3 = |JMA_close - JMA_close[сдвиг 2]|, воспроизводя разность буферов MT5.
  3. Если val3 > range и Stochastic вышел из нейтральной зоны:
    • При %K < 47 фиксируем медвежий режим (_trendState = -1), инициализируем стоп JMA_high + range1 / 4 и формируем sell-сигнал.
    • При %K > 53 переходим в бычий режим (_trendState = 1), инициализируем стоп JMA_low - range1 / 4 и формируем buy-сигнал.
  4. Пока режим не меняется, линия BrainTrend подтягивается к цене на величину range1 (JMA_high + range1 для продаж, JMA_low - range1 для покупок).
  5. Сигнал выполняется после SignalBar закрытых свечей. При исполнении:
    • Buy-сигнал закрывает шорты (если SellClose = true) и, при разрешении BuyOpen, открывает новый лонг.
    • Sell-сигнал закрывает лонги (если BuyClose = true) и, при SellOpen = true, открывает шорт.

На графике автоматически рисуются Jurik-сглаженное закрытие, осциллятор Stochastic и сделки.

Параметры

Параметр Описание Значение по умолчанию
CandleType Тип свечей, используемый стратегией. H4 (4-часовые свечи)
AtrPeriod Длина основного ATR, запускающего BrainTrend. 7
StochasticPeriod Период %K/%D Stochastic (с однобарным сглаживанием %K). 9
StopDPeriod Дополнительные бары в периоде второго ATR (AtrPeriod + StopDPeriod). 3
JmaLength Длина Jurik Moving Average для high/low/close. 7
JmaPhase Фаза Jurik (ограничивается диапазоном [-100; 100]). 100
SignalBar Сколько закрытых свечей ждать перед отправкой сигнала. 1
BuyOpen / SellOpen Разрешить открытие лонга/шорта. true
BuyClose / SellClose Разрешить закрытие существующих позиций при обратном сигнале. true

Объём сделки задаётся через свойство Volume стратегии или настройки брокера.

Отличия от версии MT5

  • Блок управления капиталом (MM, MMMode, Deviation_, динамический лот) заменён стандартным выставлением объёма через Volume и рыночные ордера StockSharp. Учёт проскальзывания не реализован.
  • Абсолютные уровни StopLoss_ и TakeProfit_ не перенесены. При необходимости настройте защиту на стороне терминала или риск-менеджера.
  • Линии BrainTrend используются только для вычисления сигналов; отложенные ордера по ним не выставляются.
  • Jurik Moving Average берётся из библиотеки StockSharp. Параметр фазы устанавливается через reflection, как и в других BrainTrading-стратегиях репозитория.

Использование

  1. Привяжите стратегию к инструменту и задайте CandleType (для соответствия оригиналу — 4-часовые свечи).
  2. Настройте параметры индикаторов (AtrPeriod, StochasticPeriod, StopDPeriod, JmaLength, JmaPhase) под требуемую чувствительность BrainTrend.
  3. Регулируйте SignalBar, если нужна задержка между появлением сигнала и входом в позицию.
  4. Установите Volume и флаги открытия/закрытия (BuyOpen, SellOpen, BuyClose, SellClose) под вашу торговую модель.
  5. При необходимости добавьте внешние ограничения по риску (стоп-лоссы, лимиты портфеля и т. п.) средствами хоста.

После запуска стратегия отслеживает развороты BrainTrend, закрывает противоположные позиции и, при необходимости, разворачивает позицию после заданной задержки.

using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class JBrainTrend1StopStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public JBrainTrend1StopStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}