Открыть на GitHub

Стратегия Bill Williams Alligator

Эта стратегия переносит советник MetaTrader 5 «Bill Williams.mq5» автора Владимира Карпутова на высокоуровневый API StockSharp. Алгоритм подписывается на один поток свечей, восстанавливает фракталы Билла Вильямса и сравнивает их с линиями Аллигатора, сдвинутыми вперёд на заданное количество баров. Когда текущая свеча закрывается за пределами ближайшего бычьего или медвежьего фрактала и соответствующий фрактал расположен вне всех трёх линий (челюсть, зубы, губы), открывается позиция. Дополнительные параметры повторяют настройки оригинального робота: стоп-лосс, тейк-профит, трейлинг, реверс сигналов и принудительное закрытие встречных позиций.

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

  1. Определение фракталов. После закрытия каждой свечи обновляются скользящие буферы максимумов и минимумов. Скрипт просматривает до FractalsLookback завершённых баров и находит последние подтверждённые верхний и нижний фракталы (паттерн из пяти свечей).
  2. Аллигатор. В качестве входных данных трём индикаторам SmoothedMovingAverage подаётся медианная цена (High + Low) / 2. Полученные значения сдвигаются вперёд на заданное количество баров, что воспроизводит отображение индикатора в MetaTrader.
  3. Проверка пробоя. Для открытия лонга последний верхний фрактал должен находиться выше всех линий Аллигатора, а текущая свеча — закрыться выше цены фрактала. Для шорта выполняется зеркальное условие ниже индикатора.
  4. Исполнение сделок. По умолчанию стратегия открывает одну рыночную позицию объёмом OrderVolume, если выполняется пробой и нет открытых сделок. Если включен флаг CloseOppositePositions, противоположная позиция закрывается перед новым входом. Параметр ReverseSignals меняет направления сигналов, как и в исходном советнике.
  5. Управление рисками. Стоп-лосс и тейк-профит хранятся внутри стратегии и проверяются на каждом баре. Трейлинг активируется после прохождения ценой расстояния TrailingStopPips + TrailingStepPips и срабатывает ступенчато по мере дальнейшего движения. Все расстояния указываются в «пипсах», рассчитанных из PriceStep инструмента с учётом трёх- и пятизнаковых котировок MetaTrader.

Параметры

Имя Описание Значение по умолчанию
OrderVolume Объём входа в лотах или контрактах. 0.1
StopLossPips Размер первичного стоп-лосса в пипсах. 0 отключает уровень. 50
TakeProfitPips Размер тейк-профита в пипсах. 0 отключает уровень. 50
TrailingStopPips Дистанция трейлинг-стопа в пипсах. 0 отключает трейлинг. 10
TrailingStepPips Дополнительное смещение для перестановки трейлинг-стопа. Должно быть положительным при включённом трейлинге. 5
JawPeriod Период сглаженной средней для линии челюсти (синяя). 13
JawShift Горизонтальный сдвиг линии челюсти в барах. 8
TeethPeriod Период сглаженной средней для линии зубов (красная). 8
TeethShift Горизонтальный сдвиг линии зубов. 5
LipsPeriod Период сглаженной средней для линии губ (зелёная). 5
LipsShift Горизонтальный сдвиг линии губ. 3
FractalsLookback Количество завершённых свечей для поиска последних фракталов. 100
ReverseSignals При true покупки генерируются по пробою вниз, продажи — по пробою вверх. false
CloseOppositePositions При true противоположная позиция закрывается перед новым входом. false
CandleType Тип свечей, используемый для расчётов и сигналов. TimeFrame(1h)

Дополнительные замечания

  • Стратегия работает только по закрытым свечам и не реагирует на внутрибартовые тики, полностью повторяя логику исходного советника.
  • Для корректного пересчёта пипсов при трёх- и пятизначных котировках PriceStep умножается на 10, как в MetaTrader 5.
  • Стоп-уровни и трейлинг управляются внутри стратегии: при достижении цены на следующей свече позиция закрывается рыночным ордером.
  • При наличии области графика автоматически выводятся свечи и линии Аллигатора, что облегчает визуальное сравнение с шаблоном MetaTrader.
  • Версия на Python и модульные тесты не создавались, что соответствует требованиям репозитория при добавлении новых конверсий.
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 BillWilliamsAlligatorStrategy : 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 BillWilliamsAlligatorStrategy()
	{
		_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;
	}
}