La Estrategia de Aussie Surfer Ltd es una versión API de alto nivel de StockSharp del asesor experto MetaTrader 5 "Aussie Surfer Ltd" (MQL carpeta 43278). La estrategia combina rápidas reversiones de banda Bollinger con un filtro de tendencia Alligator para automatizar la configuración discrecional utilizada en el EA original. Las operaciones se realizan con el instrumento principal configurado para la estrategia y se evalúan en una serie de velas de 15 minutos de forma predeterminada.
Indicadores y datos
Bollinger Bandas (precio de cierre, longitud predeterminada 5, ancho 2,5): detecta cuando el mercado se extiende temporalmente fuera de las bandas y vuelve a entrar.
Promedio móvil suavizado (longitud 21): reproduce la línea de "dientes" Alligator para juzgar la desaceleración de la tendencia.
Precio medio de cada vela ((Máximo + Mínimo) / 2): alimenta el cálculo Alligator para que la pendiente coincida con la implementación original.
La estrategia se suscribe a un único flujo de velas. Los valores del indicador son impulsados únicamente por velas terminadas, lo que garantiza que las señales se generen a partir de datos confirmados.
Lógica de trading
Configuración de entrada
Cuando la vela anterior se abrió por encima de la banda inferior Bollinger y la vela actual se abre por debajo del valor de la banda observado hace dos barras, se abre una posición larga (después de aplanar cualquier exposición corta). Esto recrea la lógica EA en la que el precio perfora la banda inferior e inmediatamente rebota hacia adentro.
Cuando la vela anterior se abrió por debajo de la banda superior Bollinger y la vela actual se abre por encima del valor de la banda observado hace dos barras, se abre una posición corta (después de aplanar cualquier exposición larga).
Salida basada en Alligator
La línea de dientes Alligator se monitorea una y dos barras atrás. Una posición larga se liquida siempre que la pendiente gira hacia abajo (el valor de hace dos barras es mayor que el valor de hace una barra). Una posición corta se cierra cuando la pendiente aumenta.
Capas de riesgo
Al entrar se aplican un stop-loss y una take-profit de pips fijos. Ambos son opcionales y se pueden desactivar estableciendo su distancia de pip en cero.
Un trailing stop opcional realinea el stop-loss con el máximo (para largos) o mínimo (para cortos) de la vela previamente completada menos/más la distancia de pips configurada. La lógica de seguimiento solo está activa si el stop-loss está habilitado y EnableTrailingStop está establecido en true.
Gestión del riesgo
Stop-loss: convierte la distancia de pip configurada en unidades de precio utilizando el paso del precio del valor.
Take-profit: se calcula una vez al momento de la entrada y se mantiene estático hasta que se alcanza o la posición se cierra mediante otra regla.
Trailing stop: avanza el stop-loss cuando aparece un máximo (para largos) o un mínimo (para cortos) más favorable en la vela anterior.
Manejo de reversión: si llega una señal mientras una posición opuesta está abierta, la estrategia envía una orden de mercado de tamaño para revertir completamente y establecer la nueva exposición en una sola transacción.
Parámetros
Parámetro
Descripción
Predeterminado
OrderVolume
Tamaño base del comercio en lotes o contratos.
0.30
StopLossPips
Distancia de parada de protección en pips. 0 desactiva la parada.
46
TakeProfitPips
Distancia objetivo de ganancias en pips. 0 desactiva el objetivo.
0
EnableTrailingStop
Habilita el seguimiento basado en pips cuando un stop-loss está activo.
true
BollingerPeriod
Longitud de la ventana Bollinger Bandas.
5
BollingerDeviation
Multiplicador de desviación estándar para las bandas.
2.5
TeethPeriod
Longitud media móvil suavizada para la línea de dientes Alligator.
21
CandleType
Serie de velas utilizadas para los cálculos (período de tiempo de 15 minutos por defecto).
15m velas
Todos los parámetros numéricos incluyen metadatos de optimización para que puedan ajustarse a través del Analizador de estrategias.
Notas de implementación
Sólo se procesan velas completas; los datos sin terminar se ignoran para imitar la ejecución controlada por temporizador MetaTrader que se ejecutó al comienzo de cada nueva barra.
La lógica de seguimiento requiere una distancia de stop-loss positiva. Se produce una excepción durante la inicialización si la opción de seguimiento está habilitada sin parada.
Las instancias de indicador se dibujan automáticamente cuando hay un área de gráfico disponible, lo que ayuda a validar que el puerto StockSharp coincida con la plantilla MetaTrader.
Uso
Cargue la estrategia en una terminal StockSharp o en un entorno de backtesting.
Configure la seguridad comercial y ajuste los parámetros (especialmente las distancias de pips) para que coincidan con las especificaciones del contrato del corredor.
Inicia la estrategia. Se suscribirá a la serie de velas configuradas, evaluará las entradas en cada vela terminada y gestionará la posición utilizando las reglas descritas.
Para operaciones en vivo, asegúrese de que el corredor admita órdenes de mercado y que el símbolo PriceStep esté disponible para que las conversiones de pips sean precisas.
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()