Der volumengewichtete Durchschnittspreis (VWAP) ist ein beliebter Intraday-Referenzwert. Wenn der Kurs erheblich vom VWAP abweicht und dann eine Kerze zurück in Richtung VWAP druckt, folgt oft eine kurze Umkehrbewegung. Diese Strategie handelt diese Abpraller.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 130%. Die Strategie eignet sich am besten für den Aktienmarkt.
Für jeden Balken wird der aktuelle VWAP berechnet. Wenn eine bullische Kerze unterhalb des VWAP schließt, geht das System Long; wenn eine bearische Kerze oberhalb des VWAP schließt, geht es Short. Ein fester Stop-Loss-Prozentsatz steuert das Risiko, und Positionen werden in der Regel nur gehalten, bis ein entgegengesetztes Signal entsteht oder der Stop erreicht wird.
Da die Methode gegen Intraday-Extreme handelt, funktioniert sie am besten in Range-gebundenen Märkten statt in starken Trends.
Details
Einstiegskriterien: Schlusskurs unterhalb VWAP mit bullischer Kerze oder oberhalb VWAP mit bearischer Kerze.
Long/Short: Beide.
Ausstiegskriterien: Entgegengesetztes Signal oder Stop-Loss.
Stops: Ja, prozentbasiert.
Standardwerte:
CandleType = 5 minute
StopLoss = 2%
Filter:
Kategorie: Mean Reversion
Richtung: Beide
Indikatoren: VWAP
Stops: Ja
Komplexität: Grundlegend
Zeitrahmen: Intraday
Saisonalität: Nein
Neuronale Netze: Nein
Divergenz: Nein
Risikolevel: Mittel
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>
/// VWAP Bounce strategy.
/// Enters long when price bounces off VWAP from below with a bullish candle.
/// Enters short when price bounces off VWAP from above with a bearish candle.
/// Uses SMA for exit signals.
/// </summary>
public class VwapBounceStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevClose;
private int _cooldown;
/// <summary>
/// MA Period.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public VwapBounceStrategy()
{
_maPeriod = Param(nameof(MAPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for SMA", "Indicators");
_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();
_prevClose = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_cooldown = 0;
var vwma = new VolumeWeightedMovingAverage { Length = 20 };
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(vwma, sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, vwma);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal vwmaValue, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevClose == 0)
{
_prevClose = candle.ClosePrice;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevClose = candle.ClosePrice;
return;
}
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
// Bounce off VWAP from below (bullish): prev close was below VWAP, now above or near, bullish candle
var bouncedUp = _prevClose < vwmaValue && candle.ClosePrice >= vwmaValue && isBullish;
// Bounce off VWAP from above (bearish): prev close was above VWAP, now below or near, bearish candle
var bouncedDown = _prevClose > vwmaValue && candle.ClosePrice <= vwmaValue && isBearish;
if (Position == 0 && bouncedUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && bouncedDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevClose = candle.ClosePrice;
}
}
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, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class vwap_bounce_strategy(Strategy):
"""
VWAP Bounce strategy.
Enters long when price bounces off VWAP from below with a bullish candle.
Enters short when price bounces off VWAP from above with a bearish candle.
Uses SMA for exit signals.
"""
def __init__(self):
super(vwap_bounce_strategy, self).__init__()
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for SMA", "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._prev_close = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vwap_bounce_strategy, self).OnReseted()
self._prev_close = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(vwap_bounce_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._cooldown = 0
vwma = VolumeWeightedMovingAverage()
vwma.Length = 20
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(vwma, sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, vwma)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, vwma_val, sma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
vv = float(vwma_val)
sv = float(sma_val)
if self._prev_close == 0:
self._prev_close = close
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_close = close
return
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
cd = self._cooldown_bars.Value
# Bounce off VWAP from below (bullish): prev close was below VWAP, now above or near, bullish candle
bounced_up = self._prev_close < vv and close >= vv and is_bullish
# Bounce off VWAP from above (bearish): prev close was above VWAP, now below or near, bearish candle
bounced_down = self._prev_close > vv and close <= vv and is_bearish
if self.Position == 0 and bounced_up:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and bounced_down:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = cd
self._prev_close = close
def CreateClone(self):
return vwap_bounce_strategy()