Aussie Surfer Ltd — это порт советника MetaTrader 5 "Aussie Surfer Ltd" (папка MQL 43278) на высокоуровневый API StockSharp. Стратегия сочетает отскоки от полос Боллинджера с фильтром по линии "зубов" индикатора Alligator, чтобы автоматизировать авторский подход оригинального эксперта. По умолчанию анализ ведётся по 15-минутным свечам и исполняется на выбранном инструменте рыночными ордерами.
Индикаторы и данные
Полосы Боллинджера (цена закрытия, период 5, отклонение 2.5) — фиксируют моменты, когда цена выходит за пределы канала и быстро возвращается внутрь.
Сглаженная скользящая средняя (период 21) — воссоздаёт линию зубов Alligator и позволяет оценить изменение наклона тренда.
Медианная цена свечи ((High + Low) / 2) — подаётся на вход Alligator, чтобы сохранить динамику, идентичную MetaTrader.
Стратегия подписывается на единственную свечную серию и обрабатывает только завершённые свечи, что исключает ложные сигналы на незакрытых барах.
Логика торговли
Вход
Если предыдущая свеча открылась выше своей нижней полосы, а текущая открывается ниже значения нижней полосы двух баров назад, то стратегия закрывает шорт (если он есть) и открывает лонг. Это повторяет идею «прокола» нижней полосы и возврата внутрь канала.
Если предыдущая свеча открылась ниже верхней полосы, а текущая открывается выше значения верхней полосы двух баров назад, то закрывается лонг и открывается шорт.
Выход по Alligator
Отслеживаются значения зубов Alligator на двух предыдущих свечах. Если для лонга значение два бара назад выше, чем один бар назад, тренд ослабевает и позиция закрывается. Для шорта условие зеркальное: когда наклон разворачивается вверх, шорт закрывается.
Управление рисками
При входе задаются фиксированные уровни стоп-лосса и тейк-профита в пунктах. Нулевое значение отключает соответствующий уровень.
При активном стоп-лоссе и включённой опции трейлинг-стоп подтягивается к максимуму (для лонга) или минимуму (для шорта) предыдущей завершённой свечи с учётом выбранного расстояния в пунктах.
Управление риском
Стоп-лосс — переводит заданные пункты в цену с помощью шага цены инструмента (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 для визуальной проверки.
Использование
Загрузите стратегию в терминал или тестер StockSharp.
Укажите торгуемый инструмент и подстройте параметры объёма и пунктов в соответствии с спецификацией брокера.
Запустите стратегию. Она подпишется на выбранную свечную серию, будет оценивать сигналы на каждой завершённой свече и управлять позицией по описанным правилам.
При работе на реальном счёте убедитесь, что брокер поддерживает рыночные ордера и предоставляет корректный 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();
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class aussie_surfer_ltd_strategy(Strategy):
def __init__(self):
super(aussie_surfer_ltd_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(120)))
self._bollinger_period = self.Param("BollingerPeriod", 20)
self._bollinger_width = self.Param("BollingerWidth", 2.5)
self._sma_period = self.Param("SmaPeriod", 21)
self._prev_sma = None
self._prev_close = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BollingerPeriod(self):
return self._bollinger_period.Value
@BollingerPeriod.setter
def BollingerPeriod(self, value):
self._bollinger_period.Value = value
@property
def BollingerWidth(self):
return self._bollinger_width.Value
@BollingerWidth.setter
def BollingerWidth(self, value):
self._bollinger_width.Value = value
@property
def SmaPeriod(self):
return self._sma_period.Value
@SmaPeriod.setter
def SmaPeriod(self, value):
self._sma_period.Value = value
def OnReseted(self):
super(aussie_surfer_ltd_strategy, self).OnReseted()
self._prev_sma = None
self._prev_close = None
def OnStarted2(self, time):
super(aussie_surfer_ltd_strategy, self).OnStarted2(time)
self._prev_sma = None
self._prev_close = None
band_ema = ExponentialMovingAverage()
band_ema.Length = self.BollingerPeriod
slope_ema = ExponentialMovingAverage()
slope_ema.Length = self.SmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(band_ema, slope_ema, self._process_candle).Start()
def _process_candle(self, candle, band_value, sma_value):
if candle.State != CandleStates.Finished:
return
band_val = float(band_value)
sma_val = float(sma_value)
close = float(candle.ClosePrice)
band_offset = band_val * (float(self.BollingerWidth) / 100.0)
upper_band = band_val + band_offset
lower_band = band_val - band_offset
if self._prev_sma is None or self._prev_close is None:
self._prev_sma = sma_val
self._prev_close = close
return
sma_rising = sma_val > self._prev_sma
sma_falling = sma_val < self._prev_sma
# Long: price was below lower band and crosses back above, SMA falling (reversal)
long_signal = self._prev_close < lower_band and close >= lower_band and sma_falling
# Short: price was above upper band and crosses back below, SMA rising (reversal)
short_signal = self._prev_close > upper_band and close <= upper_band and sma_rising
if long_signal:
if self.Position <= 0:
self.BuyMarket()
elif short_signal:
if self.Position >= 0:
self.SellMarket()
# Exit at opposite band or SMA reversal
if self.Position > 0 and (close >= upper_band or (sma_falling and self._prev_sma > sma_val)):
self.SellMarket()
elif self.Position < 0 and (close <= lower_band or (sma_rising and self._prev_sma < sma_val)):
self.BuyMarket()
self._prev_sma = sma_val
self._prev_close = close
def CreateClone(self):
return aussie_surfer_ltd_strategy()