Открыть на GitHub

Стратегия FT Bill Williams Trader

Обзор

FT Bill Williams Trader Strategy — это высокоуровневая портированная версия эксперта MetaTrader «FT_BillWillams_Trader». Стратегия объединяет фракталы Билла Вильямса и индикатор Alligator для работы на пробоях тренда. Алгоритм отслеживает появление новых фракталов, проверяет согласованность линий Alligator в сторону пробоя и, при необходимости, накладывает фильтры по расстоянию, выравниванию и обратным сигналам перед открытием позиции.

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

  1. Определение фракталов. Буфер последних FractalPeriod максимумов и минимумов используется для поиска центральной свечи — если она выше (или ниже) остальных, фиксируется новый уровень пробоя. К уровню прибавляется отступ IndentPoints, чтобы избежать ложных срабатываний.
  2. Подтверждение пробоя. Параметр EntryConfirmation определяет тип подтверждения:
    • PriceBreakout реагирует, когда диапазон свечи пересекает уровень пробоя.
    • CloseBreakout дожидается, пока закрытие предыдущей свечи окажется за уровнем.
  3. Проверка расстояния. Если уровень пробоя находится дальше, чем MaxDistancePoints пунктов от линии губ Alligator (значение на предыдущей свече), вход игнорируется. Значение 0 отключает фильтр.
  4. Фильтр зубов. При включённом UseTeethFilter закрытие предыдущей свечи должно быть выше зубов (для покупок) или ниже (для продаж).
  5. Выравнивание тренда. Если UseTrendAlignment = true, линии губ, зубов и челюсти должны быть разнесены минимум на TeethLipsDistancePoints и JawTeethDistancePoints пунктов, подтверждая трендовую фазу.
  6. Обратные выходы. При ReverseExit = OppositeFractal появление нового противоположного фрактала немедленно закрывает позицию. При OppositePosition стратегия сначала закрывает текущую позицию и только затем открывает противоположную.
  7. Выход по челюсти. Параметр JawExit задаёт, закрывать ли позицию при возврате цены к челюсти Alligator (по экстремуму свечи или по закрытию).
  8. Трейлинг-стоп. Когда EnableTrailing включён и позиция прибыльна, стоп двигается к губам или зубам в зависимости от наклона губ относительно SMA с периодом SlopeSmaPeriod. Стартовые защитные уровни задаются параметрами StopLossPoints и TakeProfitPoints.

Параметры

Свойство Описание По умолчанию
OrderVolume Объём позиции при отправке рыночных заявок. 0.1
FractalPeriod Размер окна для поиска фракталов (желательно нечётное значение). 5
IndentPoints Отступ от уровня фрактала в пунктах. 1
EntryConfirmation Тип подтверждения пробоя (PriceBreakout, CloseBreakout). CloseBreakout
UseTeethFilter Требовать закрытие выше/ниже зубов Alligator. true
MaxDistancePoints Максимальное расстояние до линии губ (в пунктах). 1000
UseTrendAlignment Проверять правильный порядок линий Alligator. false
JawTeethDistancePoints Минимальный зазор между челюстью и зубами. 10
TeethLipsDistancePoints Минимальный зазор между зубами и губами. 10
JawExit Поведение при возврате к челюсти (Disabled, PriceCross, CloseCross). CloseCross
ReverseExit Реакция на противоположные сигналы (Disabled, OppositeFractal, OppositePosition). OppositePosition
EnableTrailing Включить трейлинг по линиям Alligator. true
SlopeSmaPeriod Период SMA, сравниваемой с наклоном губ. 5
StopLossPoints Дистанция стоп-лосса (0 — без стопа). 50
TakeProfitPoints Дистанция тейк-профита (0 — без цели). 50
JawPeriod, TeethPeriod, LipsPeriod Периоды линий Alligator. 13, 8, 5
JawShift, TeethShift, LipsShift Смещение каждой линии вперёд. 8, 5, 3
MaMethod Тип скользящей средней (Simple, Exponential, Smoothed, Weighted). Simple
AppliedPrice Цена свечи, подаваемая на Alligator. CandlePrice.Median
CandleType Тип свечей, на которых работает стратегия. 15 минут

Дополнительная информация

  • На график выводятся линии Alligator и собственные сделки стратегии.
  • Для корректного определения фрактала параметр FractalPeriod должен быть нечётным; значение по умолчанию соответствует исходному советнику.
  • Дистанционные параметры (IndentPoints, MaxDistancePoints, JawTeethDistancePoints, TeethLipsDistancePoints, StopLossPoints, TakeProfitPoints) выражены в пунктах (Security.PriceStep).
  • Управление трейлинг-стопом и выход по челюсти выполняются только по завершённым свечам, как и в оригинальной реализации на MQL4.
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bill Williams Fractal Breakout strategy filtered by Alligator alignment.
/// Buys on up-fractal breakout when price is above alligator teeth,
/// sells on down-fractal breakout when price is below alligator teeth.
/// Exits on opposite signal (reverse position).
/// </summary>
public class FTBillWillamsTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _jawPeriod;
	private readonly StrategyParam<int> _teethPeriod;
	private readonly StrategyParam<int> _lipsPeriod;
	private readonly StrategyParam<int> _fractalLen;

	private SMA _jaw = null!;
	private SMA _teeth = null!;
	private SMA _lips = null!;

	private decimal[] _highBuf = Array.Empty<decimal>();
	private decimal[] _lowBuf = Array.Empty<decimal>();
	private int _bufCount;

	private decimal? _pendingBuyLevel;
	private decimal? _pendingSellLevel;
	private decimal _prevJaw;
	private decimal _prevTeeth;
	private decimal _prevLips;
	private decimal _entryPrice;

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

	public int JawPeriod
	{
		get => _jawPeriod.Value;
		set => _jawPeriod.Value = value;
	}

	public int TeethPeriod
	{
		get => _teethPeriod.Value;
		set => _teethPeriod.Value = value;
	}

	public int LipsPeriod
	{
		get => _lipsPeriod.Value;
		set => _lipsPeriod.Value = value;
	}

	public int FractalLen
	{
		get => _fractalLen.Value;
		set => _fractalLen.Value = value;
	}

	public FTBillWillamsTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for strategy", "General");

		_jawPeriod = Param(nameof(JawPeriod), 13)
			.SetDisplay("Jaw Period", "Alligator jaw SMA period", "Alligator");

		_teethPeriod = Param(nameof(TeethPeriod), 8)
			.SetDisplay("Teeth Period", "Alligator teeth SMA period", "Alligator");

		_lipsPeriod = Param(nameof(LipsPeriod), 5)
			.SetDisplay("Lips Period", "Alligator lips SMA period", "Alligator");

		_fractalLen = Param(nameof(FractalLen), 5)
			.SetDisplay("Fractal Length", "Number of bars for fractal detection", "Signals");
	}

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

		_jaw = new SMA { Length = JawPeriod };
		_teeth = new SMA { Length = TeethPeriod };
		_lips = new SMA { Length = LipsPeriod };
		_highBuf = new decimal[FractalLen];
		_lowBuf = new decimal[FractalLen];
		_bufCount = 0;
		_pendingBuyLevel = null;
		_pendingSellLevel = null;
		_prevJaw = 0;
		_prevTeeth = 0;
		_prevLips = 0;
		_entryPrice = 0;
	}

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

		_jaw = new SMA { Length = JawPeriod };
		_teeth = new SMA { Length = TeethPeriod };
		_lips = new SMA { Length = LipsPeriod };

		_highBuf = new decimal[FractalLen];
		_lowBuf = new decimal[FractalLen];
		_bufCount = 0;
		_pendingBuyLevel = null;
		_pendingSellLevel = null;
		_prevJaw = 0;
		_prevTeeth = 0;
		_prevLips = 0;
		_entryPrice = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_jaw, _teeth, _lips, OnProcess)
			.Start();

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

	private void OnProcess(ICandleMessage candle, decimal jawVal, decimal teethVal, decimal lipsVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		// Update fractal buffers
		UpdateFractals(candle);

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

		// Manage existing positions - exit on opposite signal
		if (Position > 0)
		{
			// Close long if price breaks below pending sell level or close drops below teeth
			if (_pendingSellLevel is decimal sellLvl && low < sellLvl)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			// Close short if price breaks above pending buy level or close rises above teeth
			if (_pendingBuyLevel is decimal buyLvl && high > buyLvl)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Enter long: price breaks above up-fractal level, close above teeth (bullish)
		if (Position <= 0 && _pendingBuyLevel is decimal pendBuy)
		{
			if (high > pendBuy && close > teethVal)
			{
				if (Position < 0)
					BuyMarket(); // close short first

				BuyMarket();
				_entryPrice = close;
			}
		}

		// Enter short: price breaks below down-fractal level, close below teeth (bearish)
		if (Position >= 0 && _pendingSellLevel is decimal pendSell)
		{
			if (low < pendSell && close < teethVal)
			{
				if (Position > 0)
					SellMarket(); // close long first

				SellMarket();
				_entryPrice = close;
			}
		}

		_prevJaw = jawVal;
		_prevTeeth = teethVal;
		_prevLips = lipsVal;
	}

	private void UpdateFractals(ICandleMessage candle)
	{
		var len = _highBuf.Length;
		if (len < 3)
			return;

		// Shift buffers
		Array.Copy(_highBuf, 1, _highBuf, 0, len - 1);
		_highBuf[len - 1] = candle.HighPrice;
		Array.Copy(_lowBuf, 1, _lowBuf, 0, len - 1);
		_lowBuf[len - 1] = candle.LowPrice;

		_bufCount++;
		if (_bufCount < len)
			return;

		var wing = (len - 1) / 2;
		var center = len - 1 - wing;

		// Check up fractal
		var centerHigh = _highBuf[center];
		var isUp = true;
		for (var i = 0; i < len; i++)
		{
			if (i != center && _highBuf[i] >= centerHigh)
			{
				isUp = false;
				break;
			}
		}

		if (isUp)
			_pendingBuyLevel = centerHigh;

		// Check down fractal
		var centerLow = _lowBuf[center];
		var isDown = true;
		for (var i = 0; i < len; i++)
		{
			if (i != center && _lowBuf[i] <= centerLow)
			{
				isDown = false;
				break;
			}
		}

		if (isDown)
			_pendingSellLevel = centerLow;
	}
}