OBV ATR Стратегия
Стратегия отслеживает индикатор OBV и открывает позиции, когда OBV пробивает предыдущий максимум или минимум, формируя динамический канал, похожий на ATR.
Детали
- Условия входа: OBV выше предыдущего максимума — покупка; ниже предыдущего минимума — продажа.
- Направление: обе стороны.
- Условия выхода: противоположный сигнал или защитные ордера.
- Стопы: да.
- Значения по умолчанию:
LookbackLength= 30CandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Пробой
- Направление: Обе
- Индикаторы: OBV, Highest, Lowest
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// On-Balance Volume (OBV) breakout strategy with ATR-like channel.
/// Opens long when OBV crosses above previous high, short when below previous low.
/// </summary>
public class ObvAtrStrategy : Strategy
{
private readonly StrategyParam<int> _lookbackLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevObv;
private decimal? _prevHigh;
private decimal? _prevLow;
private int _mode;
private int _prevMode;
/// <summary>
/// Lookback length for OBV high/low calculation.
/// </summary>
public int LookbackLength
{
get => _lookbackLength.Value;
set => _lookbackLength.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="ObvAtrStrategy"/>.
/// </summary>
public ObvAtrStrategy()
{
_lookbackLength = Param(nameof(LookbackLength), 60)
.SetGreaterThanZero()
.SetDisplay("OBV Lookback", "Lookback length for OBV highs and lows", "Parameters")
.SetOptimize(10, 100, 10);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for strategy", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevObv = null;
_prevHigh = null;
_prevLow = null;
_mode = 0;
_prevMode = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var obv = new OnBalanceVolume();
var highest = new Highest { Length = LookbackLength };
var lowest = new Lowest { Length = LookbackLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(obv, (candle, value) =>
{
if (candle.State != CandleStates.Finished)
return;
var obvVal = value.ToDecimal();
var prevHigh = _prevHigh;
var prevLow = _prevLow;
var prevObv = _prevObv;
var high = highest.Process(value).ToDecimal();
var low = lowest.Process(value).ToDecimal();
_prevObv = obvVal;
_prevHigh = high;
_prevLow = low;
if (prevHigh.HasValue && prevObv.HasValue && obvVal > prevHigh.Value && prevObv <= prevHigh.Value)
_mode = 1;
else if (prevLow.HasValue && prevObv.HasValue && obvVal < prevLow.Value && prevObv >= prevLow.Value)
_mode = -1;
var bullSignal = _mode == 1 && _prevMode != 1;
var bearSignal = _mode == -1 && _prevMode != -1;
_prevMode = _mode;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (bullSignal && Position <= 0)
BuyMarket();
if (bearSignal && Position >= 0)
SellMarket();
})
.Start();
StartProtection(
takeProfit: new Unit(5, UnitTypes.Percent),
stopLoss: new Unit(3, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, obv);
DrawOwnTrades(area);
}
}
}
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, UnitTypes, Unit
from StockSharp.Algo.Indicators import OnBalanceVolume, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class obv_atr_strategy(Strategy):
def __init__(self):
super(obv_atr_strategy, self).__init__()
self._lookback = self.Param("LookbackLength", 60) \
.SetGreaterThanZero() \
.SetDisplay("OBV Lookback", "Lookback length for OBV highs and lows", "Parameters")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles for strategy", "Parameters")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(obv_atr_strategy, self).OnReseted()
self._prev_obv = None
self._prev_high = None
self._prev_low = None
self._mode = 0
self._prev_mode = 0
def OnStarted2(self, time):
super(obv_atr_strategy, self).OnStarted2(time)
self._prev_obv = None
self._prev_high = None
self._prev_low = None
self._mode = 0
self._prev_mode = 0
self._obv = OnBalanceVolume()
self._highest = Highest()
self._highest.Length = self._lookback.Value
self._lowest = Lowest()
self._lowest.Length = self._lookback.Value
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(self._obv, self.OnProcess).Start()
self.StartProtection(
takeProfit=Unit(5, UnitTypes.Percent),
stopLoss=Unit(3, UnitTypes.Percent)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, self._obv)
self.DrawOwnTrades(area)
def OnProcess(self, candle, obv_value):
if candle.State != CandleStates.Finished:
return
obv_val = float(obv_value)
prev_high = self._prev_high
prev_low = self._prev_low
prev_obv = self._prev_obv
high = float(self._highest.Process(obv_value))
low = float(self._lowest.Process(obv_value))
self._prev_obv = obv_val
self._prev_high = high
self._prev_low = low
if prev_high is not None and prev_obv is not None and obv_val > prev_high and prev_obv <= prev_high:
self._mode = 1
elif prev_low is not None and prev_obv is not None and obv_val < prev_low and prev_obv >= prev_low:
self._mode = -1
bull_signal = self._mode == 1 and self._prev_mode != 1
bear_signal = self._mode == -1 and self._prev_mode != -1
self._prev_mode = self._mode
if bull_signal and self.Position <= 0:
self.BuyMarket()
if bear_signal and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return obv_atr_strategy()