This strategy calculates a Volume Weighted Moving Average (VWMA) of closing prices. When the VWMA changes direction, it acts as a signal for potential entries or exits:
If the VWMA was falling and turns upward (forms a valley), the strategy closes any short position and may open a long position.
If the VWMA was rising and turns downward (forms a peak), the strategy closes any long position and may open a short position.
Parameters
Period – number of candles used for VWMA calculation.
Candle Type – timeframe of processed candles.
Buy Open – enable opening long positions.
Sell Open – enable opening short positions.
Buy Close – allow closing long positions when the VWMA turns down.
Sell Close – allow closing short positions when the VWMA turns up.
Notes
The strategy uses VolumeWeightedMovingAverage indicator from StockSharp and processes only finished candles. Trade volume is taken from the strategy's Volume property; when opening a new position, any opposite position is closed automatically.
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>
/// Strategy based on Volume Weighted Moving Average (VWMA) slope reversals.
/// Opens or closes positions when the VWMA changes direction.
/// </summary>
public class VwapCloseStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prev1;
private decimal? _prev2;
public int Period { get => _period.Value; set => _period.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VwapCloseStrategy()
{
_period = Param(nameof(Period), 2)
.SetDisplay("Period", "VWMA calculation period", "Indicator")
.SetOptimize(2, 5, 1);
_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();
_prev1 = null;
_prev2 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prev1 = null;
_prev2 = null;
var vwma = new VolumeWeightedMovingAverage { Length = Period };
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 vwmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prev1 is null || _prev2 is null)
{
_prev2 = _prev1;
_prev1 = vwmaValue;
return;
}
var prev1 = _prev1.Value;
var prev2 = _prev2.Value;
// Open long on valley (VWMA turns up)
if (prev1 < prev2 && vwmaValue > prev1 && Position <= 0)
BuyMarket();
// Open short on peak (VWMA turns down)
else if (prev1 > prev2 && vwmaValue < prev1 && Position >= 0)
SellMarket();
_prev2 = prev1;
_prev1 = vwmaValue;
}
}
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 vwap_close_strategy(Strategy):
def __init__(self):
super(vwap_close_strategy, self).__init__()
self._period = self.Param("Period", 2) \
.SetDisplay("Period", "VWMA calculation period", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev1 = None
self._prev2 = None
@property
def period(self):
return self._period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vwap_close_strategy, self).OnReseted()
self._prev1 = None
self._prev2 = None
def OnStarted2(self, time):
super(vwap_close_strategy, self).OnStarted2(time)
self._prev1 = None
self._prev2 = None
vwma = VolumeWeightedMovingAverage()
vwma.Length = self.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, vwma_value):
if candle.State != CandleStates.Finished:
return
vwma_value = float(vwma_value)
if self._prev1 is None or self._prev2 is None:
self._prev2 = self._prev1
self._prev1 = vwma_value
return
prev1 = self._prev1
prev2 = self._prev2
if prev1 < prev2 and vwma_value > prev1 and self.Position <= 0:
self.BuyMarket()
elif prev1 > prev2 and vwma_value < prev1 and self.Position >= 0:
self.SellMarket()
self._prev2 = prev1
self._prev1 = vwma_value
def CreateClone(self):
return vwap_close_strategy()