BullBear Volume Percentile TP Strategy
This strategy uses Bull/Bear Power normalized by a Z-Score. Long positions are opened when the Z-Score crosses above the threshold, while short positions are opened when it crosses below the negative threshold. Take profit levels are based on ATR multipliers adjusted by volume and price percentiles.
Details
- Entry Criteria:
- Long: Z-Score crosses above
ZThreshold. - Short: Z-Score crosses below
-ZThreshold.
- Long: Z-Score crosses above
- Long/Short: Both.
- Exit Criteria: Z-Score crosses back through zero or take-profit levels.
- Stops: Take profit via ATR multipliers.
- Default Values:
- EMA length 21, Z-Score length 252, threshold 1.618.
- ATR period 20, multipliers 1.618 / 2.382 / 3.618.
- Volume MA period 100, percentile period 100.
- Filters:
- Category: Momentum
- Direction: Both
- Indicators: EMA, ATR
- Stops: Yes
- Complexity: Medium
- Timeframe: Medium-term
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>
/// Bull/Bear Power strategy using EMA crossover for trend detection.
/// Enters long on golden cross, short on death cross.
/// </summary>
public class BullBearVolumePercentileTpStrategy : Strategy
{
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFastEma;
private decimal _prevSlowEma;
public int FastEmaPeriod { get => _fastEmaPeriod.Value; set => _fastEmaPeriod.Value = value; }
public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public BullBearVolumePercentileTpStrategy()
{
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 120)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 450)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFastEma = 0m;
_prevSlowEma = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastEmaValue, decimal slowEmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFastEma == 0m || _prevSlowEma == 0m)
{
_prevFastEma = fastEmaValue;
_prevSlowEma = slowEmaValue;
return;
}
if (_prevFastEma <= _prevSlowEma && fastEmaValue > slowEmaValue && Position <= 0)
{
BuyMarket();
}
else if (_prevFastEma >= _prevSlowEma && fastEmaValue < slowEmaValue && Position >= 0)
{
SellMarket();
}
_prevFastEma = fastEmaValue;
_prevSlowEma = slowEmaValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bull_bear_volume_percentile_tp_strategy(Strategy):
"""
Bull/Bear Power strategy using EMA crossover for trend detection.
Enters long on golden cross, short on death cross.
"""
def __init__(self):
super(bull_bear_volume_percentile_tp_strategy, self).__init__()
self._fast_ema_period = self.Param("FastEmaPeriod", 120) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_ema_period = self.Param("SlowEmaPeriod", 450) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_fast_ema = 0.0
self._prev_slow_ema = 0.0
@property
def fast_ema_period(self):
return self._fast_ema_period.Value
@fast_ema_period.setter
def fast_ema_period(self, value):
self._fast_ema_period.Value = value
@property
def slow_ema_period(self):
return self._slow_ema_period.Value
@slow_ema_period.setter
def slow_ema_period(self, value):
self._slow_ema_period.Value = value
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(bull_bear_volume_percentile_tp_strategy, self).OnReseted()
self._prev_fast_ema = 0.0
self._prev_slow_ema = 0.0
def OnStarted2(self, time):
super(bull_bear_volume_percentile_tp_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.fast_ema_period
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.slow_ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, slow_ema, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def on_process(self, candle, fast_ema_value, slow_ema_value):
if candle.State != CandleStates.Finished:
return
if self._prev_fast_ema == 0.0 or self._prev_slow_ema == 0.0:
self._prev_fast_ema = fast_ema_value
self._prev_slow_ema = slow_ema_value
return
if self._prev_fast_ema <= self._prev_slow_ema and fast_ema_value > slow_ema_value and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast_ema >= self._prev_slow_ema and fast_ema_value < slow_ema_value and self.Position >= 0:
self.SellMarket()
self._prev_fast_ema = fast_ema_value
self._prev_slow_ema = slow_ema_value
def CreateClone(self):
return bull_bear_volume_percentile_tp_strategy()