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()