Стратегия Bias And Sentiment Strength
Стратегия объединяет несколько индикаторов импульса и объёма (MACD, RSI, Stochastic, Awesome Oscillator, усреднения Alligator и объёмный перекос) в единый показатель. Длинная позиция открывается при положительном значении, короткая — при отрицательном.
Детали
- Критерии входа:
- Длинная позиция: совокупный показатель > 0.
- Короткая позиция: совокупный показатель < 0.
- Длинные/короткие: обе стороны.
- Критерии выхода: смена знака.
- Стопы: процентный стоп через
StopLossPercent. - Значения по умолчанию:
- MACD быстрый 12, медленный 26, сигнальный 9.
- Период RSI 14.
- Периоды Stochastic 21/14/14.
- Периоды AO 5/34.
- Длина объёмного перекоса 30.
- Фильтры:
- Категория: Следование тренду
- Направление: Обе
- Индикаторы: Множественные
- Стопы: Да
- Сложность: Высокая
- Таймфрейм: Среднесрочный
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()