Avalanche AV es una estrategia de martingala aleatorizada que alterna entre entradas largas y cortas con igual probabilidad. Las operaciones se abren solo después de un número configurable de velas terminadas, y cada posición hereda niveles fijos de stop-loss y take-profit definidos en pips. Cuando una operación cierra con pérdida, el tamaño de posición se multiplica por el coeficiente de martingala para perseguir la recuperación; las operaciones rentables restablecen el tamaño al volumen inicial una vez que el saldo de la cuenta registra un nuevo máximo de capital. La estrategia también aplica una caída flotante máxima como porcentaje del saldo de la cuenta y cerrará cualquier posición que supere este umbral.
La versión original MQL abría operaciones en ticks. El port de StockSharp mantiene el mismo comportamiento probabilístico pero trabaja en actualizaciones de velas, haciéndola adecuada tanto para backtesting como para trading en vivo con datos de barras.
Reglas de trading
Intervalo de decisión: esperar el número especificado de velas terminadas antes de evaluar una nueva señal. Si una posición aún está abierta, el intervalo continúa contando pero no se toma ninguna nueva operación.
Dirección de entrada: generar un número aleatorio; valores por encima de 16384 desencadenan una entrada larga, de lo contrario una entrada corta. Las posiciones se abren solo cuando no hay operación activa.
Tamaño de orden: empezar con InitialVolume. Después de cada operación perdedora, el próximo tamaño de orden se convierte en PreviousVolume * MartingaleMultiplier (normalizado al paso de volumen del instrumento). Las operaciones ganadoras restablecen el tamaño a InitialVolume una vez que el saldo realizado registra un nuevo máximo; de lo contrario la expansión de martingala continúa.
Stops y objetivos: el stop-loss y take-profit se calculan en pips desde el precio de entrada. Un pip es igual al paso de precio del instrumento.
Caída flotante: mientras una posición está activa, la estrategia monitorea el PnL no realizado. Si la pérdida excede MaxDrawdownPercent del saldo realizado de la cuenta (saldo inicial + PnL realizado), la posición se cierra inmediatamente.
Parámetros
Parámetro
Predeterminado
Descripción
InitialVolume
0.1
Volumen inicial de operación.
StopLossPips
15
Distancia de stop en pips (0 deshabilita el stop).
TakeProfitPips
30
Distancia de take profit en pips (0 deshabilita el objetivo).
MaxDrawdownPercent
75
Pérdida flotante máxima tolerada como porcentaje del saldo.
MartingaleMultiplier
1.6
Multiplicador de volumen aplicado después de una pérdida.
DecisionInterval
9
Número de velas terminadas entre nuevas decisiones de operación.
CandleType
Marco temporal de 1 minuto
Tipo de vela que impulsa la estrategia.
Notas
El volumen se normaliza automáticamente a los límites VolumeStep, MinVolume y MaxVolume del instrumento. Si la normalización falla, el tamaño se restablece al volumen inicial.
Los niveles de stop-loss y take-profit se basan en el PriceStep del instrumento como un pip; verifique el paso para símbolos exóticos.
La protección de caída requiere que tanto PriceStep como StepPrice estén definidos; de lo contrario, la verificación de seguridad se omite.
Dado que la estrategia depende de la aleatoriedad, los resultados varían entre ejecuciones incluso con datos de mercado idénticos a menos que la semilla aleatoria se controle externamente.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Avalanche strategy using channel breakout with Highest/Lowest.
/// </summary>
public class AvalancheAvStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal? _prevHigh;
private decimal? _prevLow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
public AvalancheAvStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_period = Param(nameof(Period), 14)
.SetGreaterThanZero()
.SetDisplay("Period", "Channel period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = null;
_prevLow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = null;
_prevLow = null;
var highest = new Highest { Length = Period };
var lowest = new Lowest { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = high;
_prevLow = low;
return;
}
var close = candle.ClosePrice;
if (_prevHigh == null || _prevLow == null)
{
_prevHigh = high;
_prevLow = low;
return;
}
if (close > _prevHigh.Value && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (close < _prevLow.Value && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevHigh = high;
_prevLow = low;
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class avalanche_av_strategy(Strategy):
def __init__(self):
super(avalanche_av_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 14) \
.SetDisplay("Period", "Channel period", "Indicators")
self._prev_high = None
self._prev_low = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(avalanche_av_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
def OnStarted2(self, time):
super(avalanche_av_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
highest = Highest()
highest.Length = self.Period
lowest = Lowest()
lowest.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
close = float(candle.ClosePrice)
if self._prev_high is None or self._prev_low is None:
self._prev_high = hv
self._prev_low = lv
return
if close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = hv
self._prev_low = lv
def CreateClone(self):
return avalanche_av_strategy()