Estrategia de Ruptura de Pendiente de Volumen
La estrategia de Ruptura de Pendiente de Volumen rastrea la tasa de cambio del Volumen. Una pendiente inusualmente pronunciada sugiere que se está formando una nueva tendencia.
Las pruebas indican un rendimiento anual promedio de aproximadamente 151%. Funciona mejor en el mercado de acciones.
Las entradas ocurren cuando la pendiente supera su nivel típico en un múltiplo de la desviación estándar, tomando operaciones en la dirección de la aceleración con un stop protector.
Atrae a los traders activos que buscan una exposición temprana a la tendencia. Las posiciones se cierran cuando la pendiente regresa a lecturas normales. VolumeSMAPeriod predeterminado = 20.
Detalles
- Criterios de entrada: El indicador supera la media por el multiplicador de desviación.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: El indicador revierte a la media.
- Stops: Sí.
- Valores predeterminados:
VolumeSMAPeriod= 20SlopePeriod= 20BreakoutMultiplier= 2.0mStopLossPercent= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: Volumen
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Corto plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Volume slope breakout.
/// Enters when the slope of the smoothed volume exceeds its average by a standard deviation multiplier,
/// in the direction of the breakout candle. Exits when the slope returns to its average.
/// </summary>
public class VolumeSlopeBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _volumeSmaPeriod;
private readonly StrategyParam<int> _slopePeriod;
private readonly StrategyParam<decimal> _breakoutMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _volumeSma;
private SimpleMovingAverage _slopeAverage;
private StandardDeviation _slopeStdDev;
private decimal? _prevVolume;
/// <summary>
/// Period of the volume moving average.
/// </summary>
public int VolumeSMAPeriod
{
get => _volumeSmaPeriod.Value;
set => _volumeSmaPeriod.Value = value;
}
/// <summary>
/// Period for slope statistics.
/// </summary>
public int SlopePeriod
{
get => _slopePeriod.Value;
set => _slopePeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for breakout detection.
/// </summary>
public decimal BreakoutMultiplier
{
get => _breakoutMultiplier.Value;
set => _breakoutMultiplier.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="VolumeSlopeBreakoutStrategy"/>.
/// </summary>
public VolumeSlopeBreakoutStrategy()
{
_volumeSmaPeriod = Param(nameof(VolumeSMAPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Volume SMA Period", "Period of the volume moving average", "Indicators");
_slopePeriod = Param(nameof(SlopePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Slope Period", "Period for slope statistics", "Strategy");
_breakoutMultiplier = Param(nameof(BreakoutMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Breakout Multiplier", "Standard deviation multiplier for breakout", "Strategy");
_stopLossPercent = Param(nameof(StopLossPercent), 2.0m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_volumeSma = null;
_slopeAverage = null;
_slopeStdDev = null;
_prevVolume = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_volumeSma = new SimpleMovingAverage { Length = VolumeSMAPeriod };
_slopeAverage = new SimpleMovingAverage { Length = SlopePeriod };
_slopeStdDev = new StandardDeviation { Length = SlopePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
takeProfit: null,
stopLoss: StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : null
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var volume = _volumeSma.Process(candle.TotalVolume, candle.ServerTime, true).ToDecimal();
if (!_volumeSma.IsFormed)
return;
if (_prevVolume is not decimal prev)
{
_prevVolume = volume;
return;
}
_prevVolume = volume;
var slope = volume - prev;
var avgSlope = _slopeAverage.Process(slope, candle.ServerTime, true).ToDecimal();
var stdSlope = _slopeStdDev.Process(slope, candle.ServerTime, true).ToDecimal();
if (!_slopeAverage.IsFormed || !_slopeStdDev.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Volume has no direction, so the breakout candle decides the side.
if (slope > avgSlope + BreakoutMultiplier * stdSlope)
{
if (candle.ClosePrice > candle.OpenPrice && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
return;
}
if (candle.ClosePrice < candle.OpenPrice && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
return;
}
}
if (slope < avgSlope)
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class volume_slope_breakout_strategy(Strategy):
"""
Volume slope breakout.
Enters when the slope of the smoothed volume exceeds its average by a standard deviation multiplier,
in the direction of the breakout candle. Exits when the slope returns to its average.
"""
def __init__(self):
super(volume_slope_breakout_strategy, self).__init__()
self._volume_sma_period = self.Param("VolumeSMAPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Volume SMA Period", "Period of the volume moving average", "Indicators")
self._slope_period = self.Param("SlopePeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Slope Period", "Period for slope statistics", "Strategy")
self._breakout_multiplier = self.Param("BreakoutMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Breakout Multiplier", "Standard deviation multiplier for breakout", "Strategy")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._volume_sma = None
self._slope_average = None
self._slope_std_dev = None
self._prev_volume = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(volume_slope_breakout_strategy, self).OnReseted()
self._volume_sma = None
self._slope_average = None
self._slope_std_dev = None
self._prev_volume = None
def OnStarted2(self, time):
super(volume_slope_breakout_strategy, self).OnStarted2(time)
self._volume_sma = SimpleMovingAverage()
self._volume_sma.Length = self._volume_sma_period.Value
self._slope_average = SimpleMovingAverage()
self._slope_average.Length = self._slope_period.Value
self._slope_std_dev = StandardDeviation()
self._slope_std_dev.Length = self._slope_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
stop = float(self._stop_loss_percent.Value)
self.StartProtection(None, Unit(stop, UnitTypes.Percent) if stop > 0 else None)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
volume = float(process_float(self._volume_sma, candle.TotalVolume, candle.ServerTime, True))
if not self._volume_sma.IsFormed:
return
if self._prev_volume is None:
self._prev_volume = volume
return
slope = volume - self._prev_volume
self._prev_volume = volume
avg_slope = float(process_float(self._slope_average, slope, candle.ServerTime, True))
std_slope = float(process_float(self._slope_std_dev, slope, candle.ServerTime, True))
if not self._slope_average.IsFormed or not self._slope_std_dev.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
# Volume has no direction, so the breakout candle decides the side.
if slope > avg_slope + float(self._breakout_multiplier.Value) * std_slope:
if close > open_price and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
return
if close < open_price and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
return
if slope < avg_slope:
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
def CreateClone(self):
return volume_slope_breakout_strategy()