Открыть на GitHub

Стратегия Aussie Surfer Ltd

Обзор

Aussie Surfer Ltd — это порт советника MetaTrader 5 "Aussie Surfer Ltd" (папка MQL 43278) на высокоуровневый API StockSharp. Стратегия сочетает отскоки от полос Боллинджера с фильтром по линии "зубов" индикатора Alligator, чтобы автоматизировать авторский подход оригинального эксперта. По умолчанию анализ ведётся по 15-минутным свечам и исполняется на выбранном инструменте рыночными ордерами.

Индикаторы и данные

  • Полосы Боллинджера (цена закрытия, период 5, отклонение 2.5) — фиксируют моменты, когда цена выходит за пределы канала и быстро возвращается внутрь.
  • Сглаженная скользящая средняя (период 21) — воссоздаёт линию зубов Alligator и позволяет оценить изменение наклона тренда.
  • Медианная цена свечи ((High + Low) / 2) — подаётся на вход Alligator, чтобы сохранить динамику, идентичную MetaTrader.

Стратегия подписывается на единственную свечную серию и обрабатывает только завершённые свечи, что исключает ложные сигналы на незакрытых барах.

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

  1. Вход
    • Если предыдущая свеча открылась выше своей нижней полосы, а текущая открывается ниже значения нижней полосы двух баров назад, то стратегия закрывает шорт (если он есть) и открывает лонг. Это повторяет идею «прокола» нижней полосы и возврата внутрь канала.
    • Если предыдущая свеча открылась ниже верхней полосы, а текущая открывается выше значения верхней полосы двух баров назад, то закрывается лонг и открывается шорт.
  2. Выход по Alligator
    • Отслеживаются значения зубов Alligator на двух предыдущих свечах. Если для лонга значение два бара назад выше, чем один бар назад, тренд ослабевает и позиция закрывается. Для шорта условие зеркальное: когда наклон разворачивается вверх, шорт закрывается.
  3. Управление рисками
    • При входе задаются фиксированные уровни стоп-лосса и тейк-профита в пунктах. Нулевое значение отключает соответствующий уровень.
    • При активном стоп-лоссе и включённой опции трейлинг-стоп подтягивается к максимуму (для лонга) или минимуму (для шорта) предыдущей завершённой свечи с учётом выбранного расстояния в пунктах.

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

  • Стоп-лосс — переводит заданные пункты в цену с помощью шага цены инструмента (PriceStep).
  • Тейк-профит — фиксируется при открытии позиции и остаётся неизменным до срабатывания или закрытия по другому правилу.
  • Трейлинг-стоп — перемещает стоп вслед за благоприятными экстремумами предыдущей свечи.
  • Реверс — при появлении противоположного сигнала позиция разворачивается одним рыночным ордером достаточного объёма.

Параметры

Параметр Описание Значение по умолчанию
OrderVolume Базовый торговый объём в лотах или контрактах. 0.30
StopLossPips Размер стоп-лосса в пунктах (0 — отключено). 46
TakeProfitPips Размер тейк-профита в пунктах (0 — отключено). 0
EnableTrailingStop Включить подтягивание стопа при активном стоп-лоссе. true
BollingerPeriod Период расчёта полос Боллинджера. 5
BollingerDeviation Множитель стандартного отклонения для полос. 2.5
TeethPeriod Период сглаженной СМА для линии зубов Alligator. 21
CandleType Тип свечей для анализа (по умолчанию 15 минут). Свечи 15m

Все числовые параметры снабжены диапазонами оптимизации для быстрой настройки в Strategy Analyzer.

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

  • Обрабатываются только завершённые свечи, что имитирует работу советника в MetaTrader, запускавшегося при открытии нового бара.
  • Если включён трейлинг-стоп без положительного значения стоп-лосса, на этапе инициализации выбрасывается исключение — это защищает от некорректной конфигурации.
  • При наличии области графика стратегия автоматически рисует свечи, полосы Боллинджера и линию зубов Alligator для визуальной проверки.

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

  1. Загрузите стратегию в терминал или тестер StockSharp.
  2. Укажите торгуемый инструмент и подстройте параметры объёма и пунктов в соответствии с спецификацией брокера.
  3. Запустите стратегию. Она подпишется на выбранную свечную серию, будет оценивать сигналы на каждой завершённой свече и управлять позицией по описанным правилам.

При работе на реальном счёте убедитесь, что брокер поддерживает рыночные ордера и предоставляет корректный PriceStep, чтобы перевод пунктов в цену выполнялся без ошибок.

using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "Aussie Surfer Ltd" MetaTrader expert.
/// Uses Bollinger Band reversals with an SMA slope filter for entries.
/// </summary>
public class AussieSurferLtdStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerWidth;
	private readonly StrategyParam<int> _smaPeriod;

	private ExponentialMovingAverage _bandEma;
	private ExponentialMovingAverage _slopeEma;
	private decimal? _prevSma;
	private decimal? _prevClose;

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

	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	public decimal BollingerWidth
	{
		get => _bollingerWidth.Value;
		set => _bollingerWidth.Value = value;
	}

	public int SmaPeriod
	{
		get => _smaPeriod.Value;
		set => _smaPeriod.Value = value;
	}

	public AussieSurferLtdStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(120).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe", "General");

		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Period", "Bollinger Bands window", "Indicators");

		_bollingerWidth = Param(nameof(BollingerWidth), 2.5m)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Width", "Standard deviation multiplier", "Indicators");

		_smaPeriod = Param(nameof(SmaPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA period for slope filter", "Indicators");
	}

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

		_bandEma = new ExponentialMovingAverage { Length = BollingerPeriod };
		_slopeEma = new ExponentialMovingAverage { Length = SmaPeriod };
		_prevSma = null;
		_prevClose = null;

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

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

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

		if (!_bandEma.IsFormed || !_slopeEma.IsFormed)
		{
			_prevClose = candle.ClosePrice;
			_prevSma = smaValue;
			return;
		}

		var close = candle.ClosePrice;
		var bandOffset = bandValue * (BollingerWidth / 100m);
		var upperBand = bandValue + bandOffset;
		var lowerBand = bandValue - bandOffset;

		if (_prevSma is null || _prevClose is null)
		{
			_prevSma = smaValue;
			_prevClose = close;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// SMA slope (uptrend or downtrend)
		var smaRising = smaValue > _prevSma.Value;
		var smaFalling = smaValue < _prevSma.Value;

		// Long: price was below lower band and crosses back above, SMA falling (reversal)
		var longSignal = _prevClose.Value < lowerBand && close >= lowerBand && smaFalling;
		// Short: price was above upper band and crosses back below, SMA rising (reversal)
		var shortSignal = _prevClose.Value > upperBand && close <= upperBand && smaRising;

		if (longSignal)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (shortSignal)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		// Exit at opposite band or SMA reversal
		if (Position > 0 && (close >= upperBand || (smaFalling && _prevSma.Value > smaValue)))
		{
			SellMarket(Position);
		}
		else if (Position < 0 && (close <= lowerBand || (smaRising && _prevSma.Value < smaValue)))
		{
			BuyMarket(Math.Abs(Position));
		}

		_prevSma = smaValue;
		_prevClose = close;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_bandEma = null;
		_slopeEma = null;
		_prevSma = null;
		_prevClose = null;

		base.OnReseted();
	}
}