Открыть на GitHub

Стратегия Billy Expert

Общее описание

  • Конвертация советника MetaTrader 4 "Billy_expert.mq4" в StockSharp.
  • Работает только на покупку: ищет серию из четырёх свечей с последовательно падающими максимумами и ценами открытия.
  • Подтверждение выполняют две стохастические кривые: быстрая на рабочем таймфрейме и медленная на старшем таймфрейме.
  • Предназначена для валютных пар, но может применяться к любым инструментам с минутными свечами.

Логика сигналов

Фильтр по цене

  1. Анализируются только закрытые свечи рабочего таймфрейма.
  2. Требуется, чтобы максимумы и цены открытия четырёх подряд идущих свечей строго уменьшались (High[0] < High[1] < High[2] < High[3] и Open[0] < Open[1] < Open[2] < Open[3]).
  3. Паттерн интерпретируется как иссякающее нисходящее движение и готовит вход на разворот.

Подтверждение стохастикой

  1. Стохастик на рабочем таймфрейме и стохастик на старшем таймфрейме рассчитываются с одинаковыми параметрами (5/3/3).
  2. Для каждого индикатора проверяется условие %K(0) > %D(0) и %K(1) > %D(1), то есть %K располагается выше %D как на текущей, так и на предыдущей свече.
  3. Сделка открывается только при одновременном выполнении условий для обоих стохастиков.

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

  • Входы: рыночная покупка объёмом Volume (если открыта короткая позиция, она перекрывается и переворачивается).
  • Стоп-лосс: фиксированное смещение вниз от цены входа (Stop Loss (pts)), значение 0 отключает уровень.
  • Тейк-профит: фиксированное смещение вверх от цены входа (Take Profit (pts)), значение 0 отключает цель.
  • Ограничение позиций: параметр Max Orders задаёт максимально допустимое число одновременных покупок. StockSharp ведёт суммарную позицию, поэтому стратегия оценивает количество открытых "лот-блоков" через отношение текущего объёма к Volume.
  • Трейлинг-стоп: в исходном советнике параметр присутствовал, но логика не реализована. В портированной версии trailing также отсутствует для соответствия оригиналу.

Параметры

Имя Описание Значение по умолчанию
Trading Candle Рабочий таймфрейм для паттерна и быстрого стохастика. 1 минута
Slow Stochastic Candle Таймфрейм для медленного подтверждающего стохастика. 5 минут
Stochastic Length Глубина расчёта %K. 5
%K Smoothing Сглаживание линии %K. 3
%D Period Сглаживание линии %D. 3
Slowing Дополнительное сглаживание %K. 3
Stop Loss (pts) Размер стоп-лосса в шагах цены. 0
Take Profit (pts) Размер тейк-профита в шагах цены. 12
Max Orders Максимальное число одновременных покупок. 1

Рекомендации по использованию

  • Перед запуском обязательно задайте свойство Volume, иначе заявки не будут отправляться.
  • Шаг цены берётся из Security.PriceStep (при отсутствии — из Security.Step, либо используется значение 1). Убедитесь, что инструмент имеет корректно заполненные параметры.
  • Если таймфреймы различаются, стратегия до появления новой медленной свечи использует последнюю доступную величину стохастика — так же, как исходный советник.
  • Выход из позиции осуществляется рыночными заявками при достижении уровней стопа или тейка, что эквивалентно серверному исполнению в MT4.
  • Для корректной работы ограничения Max Orders > 1 желательно, чтобы каждый вход использовал одинаковый объём Volume.

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

  • Добавлена проверка наличия шага цены с предупреждением в журнале.
  • Дополнительные проверки на готовность данных (история свечей и значения стохастика), чтобы избежать преждевременных сделок.
  • В StockSharp вычисления выполняются только по закрытым свечам, что исключает повторную обработку одного и того же бара и делает поведение детерминированным.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Long-only reversal strategy that looks for consecutive descending highs
/// and enters when RSI confirms oversold conditions with momentum turning up.
/// </summary>
public class BillyExpertReversalStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;

	private decimal _prevHigh1, _prevHigh2, _prevHigh3;
	private int _barCount;
	private decimal _prevRsi;
	private bool _hasPrevRsi;
	private decimal _entryPrice;

	public BillyExpertReversalStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "Length for RSI indicator.", "Indicators");
	}

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

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh1 = 0;
		_prevHigh2 = 0;
		_prevHigh3 = 0;
		_barCount = 0;
		_prevRsi = 50;
		_hasPrevRsi = false;
		_entryPrice = 0;
	}

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

		_prevHigh1 = 0;
		_prevHigh2 = 0;
		_prevHigh3 = 0;
		_barCount = 0;
		_prevRsi = 50;
		_hasPrevRsi = false;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ProcessCandle)
			.Start();

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

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

		_barCount++;

		var high = candle.HighPrice;
		var close = candle.ClosePrice;

		// Check descending highs pattern (3 consecutive lower highs)
		var descendingHighs = _barCount >= 4 &&
			high < _prevHigh1 &&
			_prevHigh1 < _prevHigh2 &&
			_prevHigh2 < _prevHigh3;

		// RSI turning up from oversold
		var rsiBullish = _hasPrevRsi && _prevRsi < 40 && rsiValue > _prevRsi;

		// Manage long position
		if (Position > 0)
		{
			// Exit on take-profit, stop-loss, or RSI overbought
			if (_entryPrice > 0 && close >= _entryPrice * 1.015m)
			{
				SellMarket();
			}
			else if (_entryPrice > 0 && close <= _entryPrice * 0.985m)
			{
				SellMarket();
			}
			else if (rsiValue > 75)
			{
				SellMarket();
			}
		}

		// Manage short position (exit only, this is mostly long-only)
		if (Position < 0)
		{
			if (rsiValue < 30)
			{
				BuyMarket();
			}
		}

		// Entry: descending highs (selling exhaustion) + RSI confirms reversal
		if (Position == 0)
		{
			if (descendingHighs && rsiBullish)
			{
				_entryPrice = close;
				BuyMarket();
			}
			// Also allow short on ascending lows pattern with overbought RSI
			else if (_barCount >= 4 && rsiValue > 70 && _prevRsi > 70)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		// Update history
		_prevHigh3 = _prevHigh2;
		_prevHigh2 = _prevHigh1;
		_prevHigh1 = high;
		_prevRsi = rsiValue;
		_hasPrevRsi = true;
	}
}