Estratégia VWMA Cross
A Média Móvel Ponderada por Volume (VWMA) enfatiza os níveis de preço com maior volume de negociação. Esta estratégia opera os cruzamentos entre o preço e a VWMA.
Os testes indicam um retorno anual médio de aproximadamente 184%. Funciona melhor no mercado de criptomoedas.
Um fechamento acima da VWMA após estar abaixo dela gera uma entrada comprada, enquanto uma queda abaixo da VWMA provoca uma operação vendida. As posições são encerradas quando o preço cruza de volta na direção oposta.
Usar uma média ponderada por volume reduz o ruído dos períodos de baixo volume.
Detalhes
- Critérios de entrada: O preço cruza a VWMA de baixo para cima ou de cima para baixo.
- Comprado/Vendido: Ambas as direções.
- Critérios de saída: Cruzamento inverso ou stop.
- Stops: Sim.
- Valores padrão:
VWMAPeriod= 14CandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoria: Tendência
- Direção: Ambos
- Indicadores: VWMA
- Stops: Sim
- Complexidade: Básico
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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 (VWMA) Strategy.
/// Long entry: Price crosses above VWMA.
/// Short entry: Price crosses below VWMA.
/// </summary>
public class VWMAStrategy : Strategy
{
private readonly StrategyParam<int> _vwmaPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _previousClosePrice;
private decimal _previousVWMA;
private int _cooldown;
/// <summary>
/// VWMA Period.
/// </summary>
public int VWMAPeriod
{
get => _vwmaPeriod.Value;
set => _vwmaPeriod.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize <see cref="VWMAStrategy"/>.
/// </summary>
public VWMAStrategy()
{
_vwmaPeriod = Param(nameof(VWMAPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("VWMA Period", "Period for Volume Weighted Moving Average", "Indicators")
.SetOptimize(5, 30, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousClosePrice = default;
_previousVWMA = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousClosePrice = 0;
_previousVWMA = 0;
_cooldown = 0;
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 vwmaPrice)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_previousClosePrice == 0)
{
_previousClosePrice = candle.ClosePrice;
_previousVWMA = vwmaPrice;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_previousClosePrice = candle.ClosePrice;
_previousVWMA = vwmaPrice;
return;
}
var crossoverUp = _previousClosePrice <= _previousVWMA && candle.ClosePrice > vwmaPrice;
var crossoverDown = _previousClosePrice >= _previousVWMA && candle.ClosePrice < vwmaPrice;
if (Position == 0 && crossoverUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && crossoverDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && crossoverDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && crossoverUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
_previousClosePrice = candle.ClosePrice;
_previousVWMA = vwmaPrice;
}
}
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 vwma_strategy(Strategy):
"""
Volume Weighted Moving Average (VWMA) Strategy.
Long entry: Price crosses above VWMA.
Short entry: Price crosses below VWMA.
"""
def __init__(self):
super(vwma_strategy, self).__init__()
self._vwma_period = self.Param("VWMAPeriod", 14).SetDisplay("VWMA Period", "Period for Volume Weighted Moving Average", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._previous_close = 0.0
self._previous_vwma = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vwma_strategy, self).OnReseted()
self._previous_close = 0.0
self._previous_vwma = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(vwma_strategy, self).OnStarted2(time)
self._previous_close = 0.0
self._previous_vwma = 0.0
self._cooldown = 0
vwma = VolumeWeightedMovingAverage()
vwma.Length = self._vwma_period.Value
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, vwma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
vp = float(vwma_val)
if self._previous_close == 0:
self._previous_close = close
self._previous_vwma = vp
return
if self._cooldown > 0:
self._cooldown -= 1
self._previous_close = close
self._previous_vwma = vp
return
cd = self._cooldown_bars.Value
crossover_up = self._previous_close <= self._previous_vwma and close > vp
crossover_down = self._previous_close >= self._previous_vwma and close < vp
if self.Position == 0 and crossover_up:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and crossover_down:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and crossover_down:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and crossover_up:
self.BuyMarket()
self._cooldown = cd
self._previous_close = close
self._previous_vwma = vp
def CreateClone(self):
return vwma_strategy()