Открыть на GitHub

Стратегия JK BullP AutoTrader

Обзор

JK BullP AutoTrader — это советник для MetaTrader 4, построенный на индикаторе Elder Bulls Power. Он реагирует на ослабление бычьего импульса либо на уход значения индикатора в отрицательную область. Порт на StockSharp сохраняет первоначальную логику и добавляет удобные параметры, детальную обработку трейлинг-стопа и дружественные к платформе механизмы защиты.

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

  1. Стратегия подписывается на настраиваемый поток свечей (по умолчанию часовые) и рассчитывает экспоненциальную среднюю (EMA) с периодом 13 для воссоздания базы индикатора Bulls Power.
  2. Для каждой завершённой свечи Bulls Power вычисляется как разница между максимумом свечи и значением EMA.
  3. Сравниваются два последовательных значения Bulls Power:
    • если предыдущее значение выше текущего и при этом текущее остаётся положительным, открывается короткая позиция;
    • если текущее значение опускается ниже нуля, открывается длинная позиция.
  4. В любой момент времени может быть открыта только одна позиция — как и в оригинальном советнике, который блокировал новые заявки при наличии активных сделок.

Управление рисками и выходы

  • Начальные стоп и тейк: расстояния задаются в пунктах и конвертируются в ценовые единицы на основе шага цены инструмента. Защита активируется через StartProtection, что позволяет повторить работу параметров MetaTrader.
  • Трейлинг-стоп: когда плавающая прибыль превышает заданное расстояние, уровень стопа двигается от свечи к свече. Вместо модификации существующих стоп-заявок (как в MetaTrader) порт закрывает позицию рыночной заявкой при пробое трейлингового уровня. Это гарантирует выход даже там, где защитные приказы недоступны.

Параметры

Имя Описание Значение по умолчанию
OrderVolume Объём рыночной заявки. 8.5
TakeProfitPips Расстояние до тейк-профита в пунктах. 500
StopLossPips Расстояние до стоп-лосса в пунктах. 20
TrailingStopPips Прибыль в пунктах для активации и ведения трейлинг-стопа. 10
EmaPeriod Период EMA, используемой в расчёте Bulls Power. 13
CandleType Тип свечей, на основе которых ведутся расчёты (по умолчанию часовые). Часовые свечи

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

  • Неиспользуемые параметры исходного советника (Patr, Prange, Kstop, kts, Vts) опущены, поскольку они не влияли на оригинальную логику.
  • Расстояния в пунктах опираются на PriceStep. При отсутствии шага цены используется значение 1 как консервативная оценка.
  • Используется высокоуровневый API Bind, обработка ведётся только по завершённым свечам, а внутреннее состояние (_previousBullsPower) воссоздаёт обращение к данным со сдвигом из MT4.
  • Трейлинг-логика автоматически сбрасывается после закрытия позиции, что исключает устаревшие уровни при новом входе.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// JK BullP AutoTrader: RSI momentum with EMA filter and ATR stops.
/// </summary>
public class JkBullPAutoTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public JkBullPAutoTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_rsiLength = Param(nameof(RsiLength), 13)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevRsi = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = 0; _entryPrice = 0;
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 75) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 25) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
			else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
		}
		_prevRsi = rsiVal;
	}
}