VWAP Mean Reversion-Strategie
Diese Strategie handelt gegen Bewegungen weg vom volumengewichteten Durchschnittspreis. ATR wird verwendet, um zu messen, wie weit der Preis vom VWAP abweichen muss, bevor ein Umkehrhandel in Betracht gezogen wird.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 58%. Er funktioniert am besten auf dem Aktienmarkt.
Eine Long-Position wird eröffnet, wenn der Preis mehr als K mal den ATR unter den VWAP fällt. Eine Short wird eingegangen, wenn der Preis um denselben Betrag über den VWAP steigt. Trades werden beendet, sobald der Preis zur VWAP-Linie zurückkehrt.
Der Ansatz ist für Intraday-Trader konzipiert, die erwarten, dass die Preise um den VWAP oszillieren anstatt stark zu trenden. Stops, die als Vielfaches des ATR dimensioniert sind, helfen, Verluste zu kontrollieren, wenn die Bewegung gegen den Trade weitergeht.
Details
- Einstiegskriterien:
- Long: Close < VWAP - K * ATR
- Short: Close > VWAP + K * ATR
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Long: Ausstieg wenn close >= VWAP
- Short: Ausstieg wenn close <= VWAP
- Stops: Ja, ATR-basierter Stop.
- Standardwerte:
K= 2.0mCandleType= TimeSpan.FromMinutes(5)AtrPeriod= 14
- Filter:
- Kategorie: Mean Reversion
- Richtung: Beide
- Indikatoren: VWAP, ATR
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// VWAP Mean Reversion Strategy.
/// Enter when price deviates from VWAP by a certain ATR multiple.
/// Exit when price returns to VWAP.
/// </summary>
public class VwapMeanReversionStrategy : Strategy
{
private readonly StrategyParam<decimal> _kParam;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private AverageTrueRange _atr;
private VolumeWeightedMovingAverage _vwap;
private decimal _currentAtr;
private decimal _currentVwap;
/// <summary>
/// ATR multiplier for entry.
/// </summary>
public decimal K
{
get => _kParam.Value;
set => _kParam.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="VwapMeanReversionStrategy"/>.
/// </summary>
public VwapMeanReversionStrategy()
{
_kParam = Param(nameof(K), 2.0m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "ATR multiplier for entry distance from VWAP", "Strategy Parameters")
.SetOptimize(1.0m, 4.0m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "Strategy Parameters");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR indicator period", "Strategy Parameters")
.SetOptimize(10, 20, 2);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atr = null;
_vwap = null;
_currentAtr = default;
_currentVwap = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_atr = new AverageTrueRange { Length = AtrPeriod };
_vwap = new VolumeWeightedMovingAverage { Length = AtrPeriod };
// Create subscription for candles
var subscription = SubscribeCandles(CandleType);
// Bind indicators to candles
subscription
.Bind(_atr, ProcessATR)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _atr);
DrawOwnTrades(area);
}
// Enable position protection
StartProtection(
takeProfit: new Unit(5, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent)
);
}
private void ProcessATR(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
try
{
_currentVwap = _vwap.Process(candle).ToDecimal();
}
catch
{
return;
}
_currentAtr = atr;
ProcessStrategy(candle.ClosePrice);
}
private void ProcessStrategy(decimal currentPrice)
{
// Check if strategy is ready for trading
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Skip if we don't have valid VWAP or ATR yet
if (_currentVwap <= 0 || _currentAtr <= 0)
return;
// Calculate distance to VWAP
var upperBand = _currentVwap + K * _currentAtr;
var lowerBand = _currentVwap - K * _currentAtr;
LogInfo($"Current Price: {currentPrice}, VWAP: {_currentVwap}, Upper: {upperBand}, Lower: {lowerBand}");
// Entry logic
if (Position == 0)
{
// Long Entry: Price is below lower band
if (currentPrice < lowerBand)
{
// Buy when price is too low compared to VWAP
LogInfo($"Buy Signal - Price ({currentPrice}) < Lower Band ({lowerBand})");
BuyMarket(Volume);
}
// Short Entry: Price is above upper band
else if (currentPrice > upperBand)
{
// Sell when price is too high compared to VWAP
LogInfo($"Sell Signal - Price ({currentPrice}) > Upper Band ({upperBand})");
SellMarket(Volume);
}
}
// Exit logic
else if (Position > 0 && currentPrice > _currentVwap)
{
// Exit Long: Price returned to VWAP
LogInfo($"Exit Long - Price ({currentPrice}) > VWAP ({_currentVwap})");
SellMarket(Math.Abs(Position));
}
else if (Position < 0 && currentPrice < _currentVwap)
{
// Exit Short: Price returned to VWAP
LogInfo($"Exit Short - Price ({currentPrice}) < VWAP ({_currentVwap})");
BuyMarket(Math.Abs(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, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageTrueRange, VolumeWeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class vwap_mean_reversion_strategy(Strategy):
"""
VWAP Mean Reversion Strategy.
Enter when price deviates from VWAP by a certain ATR multiple.
Exit when price returns to VWAP.
"""
def __init__(self):
super(vwap_mean_reversion_strategy, self).__init__()
# Initialize strategy parameters
self._k_param = self.Param("K", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("ATR Multiplier", "ATR multiplier for entry distance from VWAP", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 4.0, 0.5)
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "Strategy Parameters")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "ATR indicator period", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 20, 2)
# Internal indicators
self._atr = None
self._vwap = None
self._current_atr = 0
self._current_vwap = 0
@property
def K(self):
"""ATR multiplier for entry."""
return self._k_param.Value
@K.setter
def K(self, value):
self._k_param.Value = value
@property
def CandleType(self):
"""Type of candles to use."""
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
"""ATR period."""
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
def GetWorkingSecurities(self):
"""!! REQUIRED!! Override to return securities used by the strategy."""
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
"""Set up indicators, subscriptions and protection."""
super(vwap_mean_reversion_strategy, self).OnStarted2(time)
# Create indicators
self._atr = AverageTrueRange()
self._atr.Length = self.AtrPeriod
self._vwap = VolumeWeightedMovingAverage()
self._vwap.Length = self.AtrPeriod
# Create subscription for candles
subscription = self.SubscribeCandles(self.CandleType)
# Bind indicators to candles
subscription.Bind(self._atr, self.ProcessATR).Start()
# Setup chart visualization if available
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._atr)
self.DrawOwnTrades(area)
# Enable position protection
self.StartProtection(
takeProfit=Unit(5, UnitTypes.Percent),
stopLoss=Unit(2, UnitTypes.Percent)
)
def OnReseted(self):
super(vwap_mean_reversion_strategy, self).OnReseted()
self._current_atr = 0
self._current_vwap = 0
def ProcessATR(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
self._current_vwap = float(process_candle(self._vwap, candle))
self._current_vwap = self._current_vwap if self._current_vwap is not None else 0
self._current_atr = atr_value
self.ProcessStrategy(candle.ClosePrice)
def ProcessStrategy(self, current_price):
# Check if strategy is ready for trading
# Skip if we don't have valid VWAP or ATR yet
if self._current_vwap <= 0 or self._current_atr <= 0:
return
# Calculate distance to VWAP
upper_band = self._current_vwap + self.K * self._current_atr
lower_band = self._current_vwap - self.K * self._current_atr
self.LogInfo(
"Current Price: {0}, VWAP: {1}, Upper: {2}, Lower: {3}".format(
current_price, self._current_vwap, upper_band, lower_band))
# Entry logic
if self.Position == 0:
# Long Entry: Price is below lower band
if current_price < lower_band:
# Buy when price is too low compared to VWAP
self.LogInfo(
"Buy Signal - Price ({0}) < Lower Band ({1})".format(current_price, lower_band))
self.BuyMarket(self.Volume)
# Short Entry: Price is above upper band
elif current_price > upper_band:
# Sell when price is too high compared to VWAP
self.LogInfo(
"Sell Signal - Price ({0}) > Upper Band ({1})".format(current_price, upper_band))
self.SellMarket(self.Volume)
# Exit logic
elif self.Position > 0 and current_price > self._current_vwap:
# Exit Long: Price returned to VWAP
self.LogInfo(
"Exit Long - Price ({0}) > VWAP ({1})".format(current_price, self._current_vwap))
self.SellMarket(Math.Abs(self.Position))
elif self.Position < 0 and current_price < self._current_vwap:
# Exit Short: Price returned to VWAP
self.LogInfo(
"Exit Short - Price ({0}) < VWAP ({1})".format(current_price, self._current_vwap))
self.BuyMarket(Math.Abs(self.Position))
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return vwap_mean_reversion_strategy()