Стратегия Боллинджер с дистанцией
Стратегия торгует развороты по полосам Боллинджера с дополнительным фильтром расстояния. Продаёт, когда цена закрывается выше верхней полосы плюс заданное расстояние, и покупает, когда закрывается ниже нижней полосы минус это расстояние. Позиции закрываются по тейк-профиту или стоп-лоссу, измеренным в шагах цены.
Детали
- Условия входа:
- Лонг: закрытие ниже нижней полосы минус расстояние
- Шорт: закрытие выше верхней полосы плюс расстояние
- Лонг/Шорт: оба направления
- Условия выхода:
- достижение тейк-профита
- срабатывание стоп-лосса
- Стопы: абсолютные в шагах цены
- Значения по умолчанию:
BollingerPeriod= 4BollingerDeviation= 2mBandDistance= 3mProfitTarget= 3mLossLimit= 20mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Фильтры:
- Категория: Реверсия
- Направление: Оба
- Индикаторы: Полосы Боллинджера
- Стопы: Да
- Сложность: Базовая
- Таймфрейм: Краткосрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Strategy trading reversals from Bollinger Bands with extra distance.
/// </summary>
public class BollingerBandsDistanceStrategy : Strategy
{
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<decimal> _bbDeviation;
private readonly StrategyParam<decimal> _bandDistance;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _closes = new();
public int BollingerPeriod
{
get => _bbPeriod.Value;
set => _bbPeriod.Value = value;
}
public decimal BollingerDeviation
{
get => _bbDeviation.Value;
set => _bbDeviation.Value = value;
}
public decimal BandDistance
{
get => _bandDistance.Value;
set => _bandDistance.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public BollingerBandsDistanceStrategy()
{
_bbPeriod = Param(nameof(BollingerPeriod), 20)
.SetDisplay("BB Period", "Bollinger Bands length", "Parameters");
_bbDeviation = Param(nameof(BollingerDeviation), 2m)
.SetDisplay("Deviation", "Bollinger Bands deviation", "Parameters");
_bandDistance = Param(nameof(BandDistance), 1m)
.SetDisplay("Band Distance", "Extra distance from bands in price steps", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_closes.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
StartProtection(
new Unit(2000m, UnitTypes.Absolute),
new Unit(1000m, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_closes.Add(candle.ClosePrice);
if (_closes.Count > BollingerPeriod)
_closes.RemoveAt(0);
if (_closes.Count < BollingerPeriod)
return;
var sum = 0m;
foreach (var close in _closes)
sum += close;
var middle = sum / _closes.Count;
var variance = 0m;
foreach (var close in _closes)
{
var delta = close - middle;
variance += delta * delta;
}
var stdDev = (decimal)Math.Sqrt((double)(variance / _closes.Count));
var upper = middle + BollingerDeviation * stdDev;
var lower = middle - BollingerDeviation * stdDev;
var closePrice = candle.ClosePrice;
var distance = BandDistance * (Security?.PriceStep ?? 1m);
if (Position > 0 && closePrice >= middle)
SellMarket();
else if (Position < 0 && closePrice <= middle)
BuyMarket();
if (Position == 0)
{
if (closePrice > upper + distance)
SellMarket();
else if (closePrice < lower - distance)
BuyMarket();
}
}
}
import clr
import math
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class bollinger_bands_distance_strategy(Strategy):
def __init__(self):
super(bollinger_bands_distance_strategy, self).__init__()
self._bb_period = self.Param("BollingerPeriod", 20)
self._bb_deviation = self.Param("BollingerDeviation", 2.0)
self._band_distance = self.Param("BandDistance", 1.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._closes = []
@property
def BollingerPeriod(self):
return self._bb_period.Value
@BollingerPeriod.setter
def BollingerPeriod(self, value):
self._bb_period.Value = value
@property
def BollingerDeviation(self):
return self._bb_deviation.Value
@BollingerDeviation.setter
def BollingerDeviation(self, value):
self._bb_deviation.Value = value
@property
def BandDistance(self):
return self._band_distance.Value
@BandDistance.setter
def BandDistance(self, value):
self._band_distance.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(bollinger_bands_distance_strategy, self).OnStarted2(time)
self._closes = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close_price = float(candle.ClosePrice)
period = int(self.BollingerPeriod)
self._closes.append(close_price)
if len(self._closes) > period:
self._closes.pop(0)
if len(self._closes) < period:
return
total = 0.0
for c in self._closes:
total += c
middle = total / len(self._closes)
variance = 0.0
for c in self._closes:
delta = c - middle
variance += delta * delta
std_dev = math.sqrt(variance / len(self._closes))
dev = float(self.BollingerDeviation)
upper = middle + dev * std_dev
lower = middle - dev * std_dev
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
distance = float(self.BandDistance) * step
if self.Position > 0 and close_price >= middle:
self.SellMarket()
elif self.Position < 0 and close_price <= middle:
self.BuyMarket()
if self.Position == 0:
if close_price > upper + distance:
self.SellMarket()
elif close_price < lower - distance:
self.BuyMarket()
def OnReseted(self):
super(bollinger_bands_distance_strategy, self).OnReseted()
self._closes = []
def CreateClone(self):
return bollinger_bands_distance_strategy()