Estrategia de Pendiente de MA Ponderada por Volumen
La estrategia Pendiente de MA Ponderada por Volumen analiza la dirección de la Media Móvil Ponderada por Volumen (VWMA). El sistema entra en una posición larga cuando la VWMA sube durante dos barras consecutivas y abre una posición corta cuando la VWMA baja durante dos barras. Las posiciones existentes se cierran una vez que la pendiente del indicador se revierte.
Este enfoque intenta seguir tendencias emergentes usando promedios de precio ajustados por volumen, filtrando movimientos que ocurren en volumen bajo.
Detalles
Criterios de entrada: VWMA subiendo durante dos barras (largo) o bajando durante dos barras (corto).
Largo/Corto: Ambas direcciones.
Criterios de salida: Pendiente opuesta de la VWMA.
Stops: Sí (configurable, stop loss predeterminado 1% / take profit 2%).
Valores predeterminados:
VwmaPeriod = 12
CandleType = TimeSpan.FromHours(4)
Filtros:
Categoría: Tendencia
Dirección: Ambos
Indicadores: VWMA
Stops: Sí
Complejidad: Básico
Marco temporal: Swing
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 Weighted Moving Average slope strategy.
/// Goes long when VWMA turns up (valley), short when VWMA turns down (peak).
/// </summary>
public class VolumeWeightedMaSlopeStrategy : Strategy
{
private readonly StrategyParam<int> _vwmaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevVwma1;
private decimal? _prevVwma2;
public int VwmaPeriod { get => _vwmaPeriod.Value; set => _vwmaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VolumeWeightedMaSlopeStrategy()
{
_vwmaPeriod = Param(nameof(VwmaPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("VWMA Period", "Period of the Volume Weighted Moving Average", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
_prevVwma1 = null;
_prevVwma2 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevVwma1 = null;
_prevVwma2 = null;
var vwma = new VolumeWeightedMovingAverage { Length = VwmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(vwma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, vwma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal currentVwma)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevVwma1 is null)
{
_prevVwma1 = currentVwma;
return;
}
if (_prevVwma2 is null)
{
_prevVwma2 = _prevVwma1;
_prevVwma1 = currentVwma;
return;
}
// Valley: was falling, now rising -> buy
if (_prevVwma2 > _prevVwma1 && currentVwma > _prevVwma1 && Position <= 0)
BuyMarket();
// Peak: was rising, now falling -> sell
else if (_prevVwma2 < _prevVwma1 && currentVwma < _prevVwma1 && Position >= 0)
SellMarket();
_prevVwma2 = _prevVwma1;
_prevVwma1 = currentVwma;
}
}
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 VolumeWeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class volume_weighted_ma_slope_strategy(Strategy):
def __init__(self):
super(volume_weighted_ma_slope_strategy, self).__init__()
self._vwma_period = self.Param("VwmaPeriod", 12) \
.SetDisplay("VWMA Period", "Period of the Volume Weighted Moving Average", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_vwma1 = None
self._prev_vwma2 = None
@property
def vwma_period(self):
return self._vwma_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(volume_weighted_ma_slope_strategy, self).OnReseted()
self._prev_vwma1 = None
self._prev_vwma2 = None
def OnStarted2(self, time):
super(volume_weighted_ma_slope_strategy, self).OnStarted2(time)
self._prev_vwma1 = None
self._prev_vwma2 = None
vwma = VolumeWeightedMovingAverage()
vwma.Length = self.vwma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(vwma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, vwma)
self.DrawOwnTrades(area)
def process_candle(self, candle, current_vwma):
if candle.State != CandleStates.Finished:
return
current_vwma = float(current_vwma)
if self._prev_vwma1 is None:
self._prev_vwma1 = current_vwma
return
if self._prev_vwma2 is None:
self._prev_vwma2 = self._prev_vwma1
self._prev_vwma1 = current_vwma
return
if self._prev_vwma2 > self._prev_vwma1 and current_vwma > self._prev_vwma1 and self.Position <= 0:
self.BuyMarket()
elif self._prev_vwma2 < self._prev_vwma1 and current_vwma < self._prev_vwma1 and self.Position >= 0:
self.SellMarket()
self._prev_vwma2 = self._prev_vwma1
self._prev_vwma1 = current_vwma
def CreateClone(self):
return volume_weighted_ma_slope_strategy()