Стратегия BnB
Стратегия является портом советника MetaTrader 5 «Exp_BnB». Она использует индикатор BnB (Bulls and Bears), который оценивает бычье и медвежье давление в каждой свече и сглаживает их экспоненциальной средней.
Принцип работы
- Для каждой завершённой свечи вычисляются значения bulls и bears.
- Оба ряда сглаживаются EMA.
- Когда линия bulls пересекает сверху линию bears:
- закрывается короткая позиция;
- открывается длинная позиция.
- Когда линия bears пересекает сверху линию bulls:
- закрывается длинная позиция;
- открывается короткая позиция.
- Стоп‑лосс и тейк‑профит задаются в абсолютных пунктах цены.
Параметры
Candle Type– таймфрейм используемых свечей;EMA Length– период сглаживания;Stop Loss– расстояние до защитного стопа;Take Profit– расстояние до уровня фиксации прибыли;Allow Long Entry– разрешить открытие длинных позиций;Allow Short Entry– разрешить открытие коротких позиций;Allow Long Exit– разрешить закрытие длинных позиций;Allow Short Exit– разрешить закрытие коротких позиций.
Примечания
Оригинальный индикатор поддерживает множество методов сглаживания. В этом портировании он заменён стандартной экспоненциальной средней.
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>
/// Strategy based on bull/bear power comparison using EMA smoothing.
/// </summary>
public class BnBStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _minNetPower;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevBull;
private decimal _prevBear;
private bool _initialized;
private decimal _bullEma;
private decimal _bearEma;
private decimal _k;
private int _count;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Length { get => _length.Value; set => _length.Value = value; }
public decimal MinNetPower { get => _minNetPower.Value; set => _minNetPower.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public BnBStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles used for calculations", "General");
_length = Param(nameof(Length), 14)
.SetDisplay("EMA Length", "Length of smoothing for bulls and bears", "Parameters");
_minNetPower = Param(nameof(MinNetPower), 20m)
.SetDisplay("Minimum Net Power", "Minimum absolute net bull/bear power for entries", "Filters");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevBull = 0m;
_prevBear = 0m;
_initialized = false;
_bullEma = 0m;
_bearEma = 0m;
_k = 0m;
_count = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_k = 2m / (Length + 1m);
_count = 0;
var sma = new SimpleMovingAverage { Length = Length };
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 (_cooldownRemaining > 0)
_cooldownRemaining--;
var bullPower = candle.HighPrice - smaValue;
var bearPower = candle.LowPrice - smaValue;
_count++;
if (_count == 1)
{
_bullEma = bullPower;
_bearEma = bearPower;
}
else
{
_bullEma = bullPower * _k + _bullEma * (1m - _k);
_bearEma = bearPower * _k + _bearEma * (1m - _k);
}
if (_count < Length)
return;
if (!_initialized)
{
_prevBull = _bullEma;
_prevBear = _bearEma;
_initialized = true;
return;
}
var netPower = _bullEma + _bearEma;
var prevNet = _prevBull + _prevBear;
var crossUp = prevNet <= 0m && netPower > 0m && Math.Abs(netPower) >= MinNetPower;
var crossDown = prevNet >= 0m && netPower < 0m && Math.Abs(netPower) >= MinNetPower;
if (_cooldownRemaining == 0)
{
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
_prevBull = _bullEma;
_prevBear = _bearEma;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bnb_strategy(Strategy):
def __init__(self):
super(bnb_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles used for calculations", "General")
self._length = self.Param("Length", 14) \
.SetDisplay("EMA Length", "Length of smoothing for bulls and bears", "Parameters")
self._min_net_power = self.Param("MinNetPower", 20.0) \
.SetDisplay("Minimum Net Power", "Minimum absolute net bull/bear power for entries", "Filters")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._prev_bull = 0.0
self._prev_bear = 0.0
self._initialized = False
self._bull_ema = 0.0
self._bear_ema = 0.0
self._k = 0.0
self._count = 0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def length(self):
return self._length.Value
@property
def min_net_power(self):
return self._min_net_power.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(bnb_strategy, self).OnReseted()
self._prev_bull = 0.0
self._prev_bear = 0.0
self._initialized = False
self._bull_ema = 0.0
self._bear_ema = 0.0
self._k = 0.0
self._count = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(bnb_strategy, self).OnStarted2(time)
self._k = 2.0 / (self.length + 1.0)
self._count = 0
sma = SimpleMovingAverage()
sma.Length = self.length
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_value):
if candle.State != CandleStates.Finished:
return
sma_value = float(sma_value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
bull_power = float(candle.HighPrice) - sma_value
bear_power = float(candle.LowPrice) - sma_value
self._count += 1
if self._count == 1:
self._bull_ema = bull_power
self._bear_ema = bear_power
else:
self._bull_ema = bull_power * self._k + self._bull_ema * (1.0 - self._k)
self._bear_ema = bear_power * self._k + self._bear_ema * (1.0 - self._k)
if self._count < self.length:
return
if not self._initialized:
self._prev_bull = self._bull_ema
self._prev_bear = self._bear_ema
self._initialized = True
return
net_power = self._bull_ema + self._bear_ema
prev_net = self._prev_bull + self._prev_bear
min_np = float(self.min_net_power)
cross_up = prev_net <= 0 and net_power > 0 and abs(net_power) >= min_np
cross_down = prev_net >= 0 and net_power < 0 and abs(net_power) >= min_np
if self._cooldown_remaining == 0:
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_bull = self._bull_ema
self._prev_bear = self._bear_ema
def CreateClone(self):
return bnb_strategy()