On-Balance Volume (OBV) verfolgt den Kauf- und Verkaufsdruck durch Akkumulation von Volumen. Diese Strategie sucht nach einem OBV-Ausbruch über ein Hoch oder unter ein Tief innerhalb des Beobachtungsfensters, während der Preis die Bewegung bestätigt.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 178%. Am besten funktioniert es im Aktienmarkt.
Ein Ausbruch im OBV deutet auf starkes Interesse hin. Das System geht long, wenn OBV sein vorheriges Maximum überschreitet, oder short, wenn es das Minimum unterschreitet. Das Kreuzen des OBV mit seinem gleitenden Durchschnitt signalisiert einen Ausstieg.
Dies verbindet Volumen-Momentum mit Preisaktionen.
Details
Einstiegskriterien: OBV überschreitet den höchsten oder niedrigsten Wert im Lookback-Zeitraum.
Long/Short: Beide Richtungen.
Ausstiegskriterien: OBV kreuzt seinen MA oder Stop.
Stops: Ja.
Standardwerte:
LookbackPeriod = 20
OBVMAPeriod = 20
CandleType = TimeSpan.FromMinutes(5)
Filter:
Kategorie: Ausbruch
Richtung: Beide
Indikatoren: OBV, MA
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>
/// On-Balance Volume (OBV) Breakout strategy.
/// Enters when OBV crosses its moving average indicating volume confirmation of trend.
/// </summary>
public class ObvBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevObv;
private int _cooldown;
/// <summary>
/// MA Period for OBV average.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.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 the OBV Breakout strategy.
/// </summary>
public ObvBreakoutStrategy()
{
_maPeriod = Param(nameof(MAPeriod), 20)
.SetDisplay("MA Period", "Period for OBV moving average", "Indicators")
.SetOptimize(10, 50, 10);
_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();
_prevObv = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevObv = 0;
_cooldown = 0;
var obv = new OnBalanceVolume();
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(obv, sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal obvValue, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevObv == 0)
{
_prevObv = obvValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevObv = obvValue;
return;
}
// Use OBV direction + price vs SMA for signals
var obvRising = obvValue > _prevObv;
if (Position == 0)
{
if (obvRising && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (!obvRising && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position > 0 && !obvRising)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && obvRising)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevObv = obvValue;
}
}
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 OnBalanceVolume, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class obv_breakout_strategy(Strategy):
"""
On-Balance Volume (OBV) Breakout strategy.
Enters when OBV direction confirms price trend relative to SMA.
"""
def __init__(self):
super(obv_breakout_strategy, self).__init__()
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for OBV 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._prev_obv = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(obv_breakout_strategy, self).OnReseted()
self._prev_obv = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(obv_breakout_strategy, self).OnStarted2(time)
self._prev_obv = 0.0
self._cooldown = 0
obv = OnBalanceVolume()
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(obv, sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, obv_val, sma_val):
if candle.State != CandleStates.Finished:
return
ov = float(obv_val)
if self._prev_obv == 0:
self._prev_obv = ov
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_obv = ov
return
obv_rising = ov > self._prev_obv
close = float(candle.ClosePrice)
sv = float(sma_val)
cd = self._cooldown_bars.Value
if self.Position == 0:
if obv_rising and close > sv:
self.BuyMarket()
self._cooldown = cd
elif not obv_rising and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and not obv_rising:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and obv_rising:
self.BuyMarket()
self._cooldown = cd
self._prev_obv = ov
def CreateClone(self):
return obv_breakout_strategy()