Bias And Sentiment Strength Strategy
This strategy aggregates multiple momentum and volume indicators (MACD, RSI, Stochastic, Awesome Oscillator, Alligator averages and volume bias) into a single bias value. A long position is opened when the combined bias is above zero and a short position when it is below zero.
Details
- Entry Criteria:
- Long: Combined bias > 0.
- Short: Combined bias < 0.
- Long/Short: Both.
- Exit Criteria: Reverse signal.
- Stops: Stop-loss percentage via
StopLossPercent. - Default Values:
- MACD fast 12, slow 26, signal 9.
- RSI period 14.
- Stochastic periods 21/14/14.
- Awesome Oscillator periods 5/34.
- Volume bias length 30.
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: Multiple
- Stops: Yes
- Complexity: Complex
- 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>
/// Bias and Sentiment Strength (BASS) strategy.
/// Combines several momentum and volume indicators.
/// Enters long when the aggregated bias is positive and short when negative.
/// </summary>
public class BiasSentimentStrengthStrategy : Strategy
{
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _stochK;
private readonly StrategyParam<int> _stochD;
private readonly StrategyParam<int> _aoShort;
private readonly StrategyParam<int> _aoLong;
private readonly StrategyParam<int> _volumeLength;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _bassThreshold;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevBass;
public int MacdFast { get => _macdFast.Value; set => _macdFast.Value = value; }
public int MacdSlow { get => _macdSlow.Value; set => _macdSlow.Value = value; }
public int MacdSignal { get => _macdSignal.Value; set => _macdSignal.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int StochK { get => _stochK.Value; set => _stochK.Value = value; }
public int StochD { get => _stochD.Value; set => _stochD.Value = value; }
public int AoShort { get => _aoShort.Value; set => _aoShort.Value = value; }
public int AoLong { get => _aoLong.Value; set => _aoLong.Value = value; }
public int VolumeLength { get => _volumeLength.Value; set => _volumeLength.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal BassThreshold { get => _bassThreshold.Value; set => _bassThreshold.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public BiasSentimentStrengthStrategy()
{
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA period for MACD", "Indicators");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA period for MACD", "Indicators");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal period for MACD", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation length", "Indicators");
_stochK = Param(nameof(StochK), 21)
.SetGreaterThanZero()
.SetDisplay("Stochastic K", "%K period for Stochastic", "Indicators");
_stochD = Param(nameof(StochD), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic D", "%D period for Stochastic", "Indicators");
_aoShort = Param(nameof(AoShort), 5)
.SetGreaterThanZero()
.SetDisplay("AO Short", "Short period for Awesome Oscillator", "Indicators");
_aoLong = Param(nameof(AoLong), 34)
.SetGreaterThanZero()
.SetDisplay("AO Long", "Long period for Awesome Oscillator", "Indicators");
_volumeLength = Param(nameof(VolumeLength), 30)
.SetGreaterThanZero()
.SetDisplay("Volume Bias Length", "Length for VWMA/SMA", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management");
_bassThreshold = Param(nameof(BassThreshold), 70m)
.SetGreaterThanZero()
.SetDisplay("BASS Threshold", "Minimum BASS value for signal", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Time frame for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevBass = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
},
SignalMa = { Length = MacdSignal }
};
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var stoch = new StochasticOscillator
{
K = { Length = StochK },
D = { Length = StochD },
};
var ao = new AwesomeOscillator
{
ShortMa = { Length = AoShort },
LongMa = { Length = AoLong }
};
var vwma = new VolumeWeightedMovingAverage { Length = VolumeLength };
var sma = new SimpleMovingAverage { Length = VolumeLength };
var jaw = new SmoothedMovingAverage { Length = 13 };
var teeth = new SmoothedMovingAverage { Length = 8 };
var lips = new SmoothedMovingAverage { Length = 5 };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx([macd, rsi, stoch, ao, vwma, sma, jaw, teeth, lips], ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
if (values.Length < 9)
return;
for (var i = 0; i < values.Length; i++)
{
if (values[i] is null || !values[i].IsFinal)
return;
}
decimal macdHist, rsiHist, stochHist, aoVal, volumeHist, gatorHist;
try
{
if (values[0] is not IMovingAverageConvergenceDivergenceSignalValue macdTyped)
return;
var macdLine = macdTyped.Macd ?? 0m;
var signalLine = macdTyped.Signal ?? 0m;
macdHist = (macdLine - signalLine) * 2m;
var rsi = values[1].ToDecimal();
rsiHist = (rsi - 50m) / 5m;
if (values[2] is not IStochasticOscillatorValue stochTyped)
return;
if (stochTyped.K is not decimal stochKVal || stochTyped.D is not decimal stochDVal)
return;
stochHist = ((stochKVal - stochDVal) / 10m) * 1.5m;
aoVal = values[3].ToDecimal() * 0.6m;
var vwmaVal = values[4].ToDecimal();
var smaVal = values[5].ToDecimal();
volumeHist = vwmaVal - smaVal;
var jawVal = values[6].ToDecimal();
var teethVal = values[7].ToDecimal();
var lipsVal = values[8].ToDecimal();
gatorHist = (lipsVal - teethVal) + (teethVal - jawVal);
}
catch (Exception)
{
return;
}
var bass = macdHist + rsiHist + stochHist + aoVal + gatorHist + volumeHist;
// Trade on zero crossover
if (bass > 0m && _prevBass <= 0m && Position <= 0)
{
BuyMarket();
}
else if (bass < 0m && _prevBass >= 0m && Position >= 0)
{
SellMarket();
}
_prevBass = bass;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Array
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (MovingAverageConvergenceDivergenceSignal,
RelativeStrengthIndex, StochasticOscillator, AwesomeOscillator,
VolumeWeightedMovingAverage, SimpleMovingAverage, SmoothedMovingAverage, IIndicator)
from StockSharp.Algo.Strategies import Strategy
class bias_sentiment_strength_strategy(Strategy):
def __init__(self):
super(bias_sentiment_strength_strategy, self).__init__()
self._macd_fast = self.Param("MacdFast", 12) \
.SetDisplay("MACD Fast", "Fast EMA period for MACD", "Indicators")
self._macd_slow = self.Param("MacdSlow", 26) \
.SetDisplay("MACD Slow", "Slow EMA period for MACD", "Indicators")
self._macd_signal = self.Param("MacdSignal", 9) \
.SetDisplay("MACD Signal", "Signal period for MACD", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation length", "Indicators")
self._stoch_k = self.Param("StochK", 21) \
.SetDisplay("Stochastic K", "%K period for Stochastic", "Indicators")
self._stoch_d = self.Param("StochD", 14) \
.SetDisplay("Stochastic D", "%D period for Stochastic", "Indicators")
self._ao_short = self.Param("AoShort", 5) \
.SetDisplay("AO Short", "Short period for Awesome Oscillator", "Indicators")
self._ao_long = self.Param("AoLong", 34) \
.SetDisplay("AO Long", "Long period for Awesome Oscillator", "Indicators")
self._volume_length = self.Param("VolumeLength", 30) \
.SetDisplay("Volume Bias Length", "Length for VWMA/SMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Time frame for strategy", "General")
self._prev_bass = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bias_sentiment_strength_strategy, self).OnReseted()
self._prev_bass = 0.0
def OnStarted2(self, time):
super(bias_sentiment_strength_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast.Value
macd.Macd.LongMa.Length = self._macd_slow.Value
macd.SignalMa.Length = self._macd_signal.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
stoch = StochasticOscillator()
stoch.K.Length = self._stoch_k.Value
stoch.D.Length = self._stoch_d.Value
ao = AwesomeOscillator()
ao.ShortMa.Length = self._ao_short.Value
ao.LongMa.Length = self._ao_long.Value
vwma = VolumeWeightedMovingAverage()
vwma.Length = self._volume_length.Value
sma = SimpleMovingAverage()
sma.Length = self._volume_length.Value
jaw = SmoothedMovingAverage()
jaw.Length = 13
teeth = SmoothedMovingAverage()
teeth.Length = 8
lips = SmoothedMovingAverage()
lips.Length = 5
subscription = self.SubscribeCandles(self.candle_type)
indicators = Array[IIndicator]([macd, rsi, stoch, ao, vwma, sma, jaw, teeth, lips])
subscription.BindEx(indicators, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnProcess(self, candle, values):
if candle.State != CandleStates.Finished:
return
if values.Length < 9:
return
for i in range(values.Length):
if values[i] is None or not values[i].IsFinal:
return
try:
macd_val = values[0]
macd_line = float(macd_val.Macd) if macd_val.Macd is not None else 0.0
signal_line = float(macd_val.Signal) if macd_val.Signal is not None else 0.0
macd_hist = (macd_line - signal_line) * 2.0
rsi = float(values[1])
rsi_hist = (rsi - 50.0) / 5.0
stoch_val = values[2]
stoch_k_val = stoch_val.K
stoch_d_val = stoch_val.D
if stoch_k_val is None or stoch_d_val is None:
return
stoch_hist = ((float(stoch_k_val) - float(stoch_d_val)) / 10.0) * 1.5
ao_val = float(values[3]) * 0.6
vwma_val = float(values[4])
sma_val = float(values[5])
volume_hist = vwma_val - sma_val
jaw_val = float(values[6])
teeth_val = float(values[7])
lips_val = float(values[8])
gator_hist = (lips_val - teeth_val) + (teeth_val - jaw_val)
except Exception:
return
bass = macd_hist + rsi_hist + stoch_hist + ao_val + gator_hist + volume_hist
if bass > 0 and self._prev_bass <= 0 and self.Position <= 0:
self.BuyMarket()
elif bass < 0 and self._prev_bass >= 0 and self.Position >= 0:
self.SellMarket()
self._prev_bass = bass
def CreateClone(self):
return bias_sentiment_strength_strategy()