Starke Volumenspitzen signalisieren oft das Ende einer Bewegung, wenn Trader hastig Positionen schließen oder eröffnen. Diese Strategie misst das aktuelle Volumen im Vergleich zu einem Durchschnitt, um Erschöpfung zu erkennen. In Kombination mit der Kerzenrichtung und einem gleitenden Durchschnittsfilter können Umkehreinsteigspunkte präzise identifiziert werden.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 133%. Am besten funktioniert die Strategie auf dem Kryptomarkt.
Jede Kerze aktualisiert das durchschnittliche Volumen. Wenn das Volumen des neuen Balkens diesen Durchschnitt um einen festgelegten Multiplikator überschreitet und die Kerze in der entgegengesetzten Richtung des vorherrschenden Trends schließt, eröffnet das System einen Trade. Ein ATR-basierter Stop schützt die Position.
Der Trade wird typischerweise über den Stop-Loss beendet, da die Strategie eine schnelle Umkehr nach dem Volumenausbruch erwartet.
Details
Einstiegskriterien: Volumenspitze über dem Durchschnitt mit Kerze entgegen dem Trend.
Long/Short: Beide.
Ausstiegskriterien: Stop-Loss.
Stops: Ja, ATR-basiert.
Standardwerte:
VolumePeriod = 20
VolumeMultiplier = 2.0
MAPeriod = 20
AtrMultiplier = 2 ATR
CandleType = 5 minute
Filter:
Kategorie: Umkehr
Richtung: Beide
Indikatoren: Volume, MA, ATR
Stops: Ja
Komplexität: Mittel
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>
/// Volume Exhaustion strategy.
/// Looks for volume spikes (current volume much higher than previous) with directional candles.
/// High volume + bullish above SMA = buy.
/// High volume + bearish below SMA = sell.
/// Exits when price crosses SMA in opposite direction.
/// </summary>
public class VolumeExhaustionStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevVolume;
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 VolumeExhaustionStrategy()
{
_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();
_prevVolume = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevVolume = 0;
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevVolume = candle.TotalVolume;
return;
}
if (_prevVolume == 0)
{
_prevVolume = candle.TotalVolume;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevVolume = candle.TotalVolume;
return;
}
// Volume spike: current volume significantly higher than previous
var volumeSpike = _prevVolume > 0 && candle.TotalVolume > _prevVolume * 1.5m;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
if (Position == 0 && volumeSpike && isBullish && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && volumeSpike && isBearish && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevVolume = candle.TotalVolume;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class volume_exhaustion_strategy(Strategy):
"""
Volume Exhaustion strategy.
Looks for volume spikes (current volume much higher than previous) with directional candles.
High volume + bullish above SMA = buy.
High volume + bearish below SMA = sell.
Exits when price crosses SMA in opposite direction.
"""
def __init__(self):
super(volume_exhaustion_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_volume = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(volume_exhaustion_strategy, self).OnReseted()
self._prev_volume = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(volume_exhaustion_strategy, self).OnStarted2(time)
self._prev_volume = 0.0
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_volume = float(candle.TotalVolume)
return
vol = float(candle.TotalVolume)
if self._prev_volume == 0:
self._prev_volume = vol
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_volume = vol
return
# Volume spike: current volume significantly higher than previous
volume_spike = self._prev_volume > 0 and vol > self._prev_volume * 1.5
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
close = float(candle.ClosePrice)
sv = float(sma_val)
cd = self._cooldown_bars.Value
if self.Position == 0 and volume_spike and is_bullish and close > sv:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and volume_spike and is_bearish and close < sv:
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_volume = vol
def CreateClone(self):
return volume_exhaustion_strategy()