Bias Sentiment Strength
交易规则
在已完成的 15 分钟足上组合 MACD(12,26,9)、Wilder RSI(14)、随机指标(21,14,14)、Awesome Oscillator(5,34)、Alligator 与成交量加权偏差。原始 K 使用 21 根高低价;Ks 为其 14 值 SMA,D 再作 14 值 SMA。Alligator 使用中位价 SMMA:jaw 周期 13 位移 8,teeth 周期 8 位移 5,lips 周期 5 位移 3。
BASS = 2 * (MACD - Signal) + (RSI - 50) / 5 + 0.15 * (Ks - D) + 0.6 * AO + (Lips - Teeth) + (Teeth - Jaw) + VWMA(30) - SMA(30)。必须等所有部分形成;默认至少 47 根完整足。错误配置会明确报错。
分数严格大于 BassThreshold 时目标多仓 Volume,小于 -BassThreshold 时目标空仓 Volume,默认阈值为零。依据水平而非仅零轴交叉,止损后无需再次穿越零轴即可入场。反向用 Volume + abs(Position) 完整反转;活动订单阻止重复。原生本地市价止损按实际成交仓位的 StopLossPercent 百分比执行,默认 1,不设置止盈。
本地原生保护使用该品种的蜡烛和已订阅价格更新,距离以实际入场成交为基准。退出为市价订单,成交价受可用市场及价格步长影响。
参数
| 参数 | 默认值 |
|---|---|
MacdFast |
12 |
MacdSlow |
26 |
MacdSignal |
9 |
RsiPeriod |
14 |
StochK |
21 |
StochSmooth |
14 |
StochD |
14 |
AoShort |
5 |
AoLong |
34 |
VolumeLength |
30 |
BassThreshold |
0 |
StopLossPercent |
1 |
CandleType |
15m |
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>Complete MACD, RSI, smoothed stochastic, AO, Alligator and volume bias with native protection.</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> _stochSmooth;
private readonly StrategyParam<int> _stochD;
private readonly StrategyParam<int> _aoShort;
private readonly StrategyParam<int> _aoLong;
private readonly StrategyParam<int> _volumeLength;
private readonly StrategyParam<decimal> _bassThreshold;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _smoothK, _smoothD;
private Order _pending;
/// <summary>Fast MACD EMA length.</summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>Slow MACD EMA length.</summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>MACD signal EMA length.</summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>Wilder RSI length.</summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>Raw stochastic high-low window.</summary>
public int StochK
{
get => _stochK.Value;
set => _stochK.Value = value;
}
/// <summary>Stochastic K smoothing length.</summary>
public int StochSmooth
{
get => _stochSmooth.Value;
set => _stochSmooth.Value = value;
}
/// <summary>Smoothed stochastic D length.</summary>
public int StochD
{
get => _stochD.Value;
set => _stochD.Value = value;
}
/// <summary>Short median-price SMA length.</summary>
public int AoShort
{
get => _aoShort.Value;
set => _aoShort.Value = value;
}
/// <summary>Long median-price SMA length.</summary>
public int AoLong
{
get => _aoLong.Value;
set => _aoLong.Value = value;
}
/// <summary>Close VWMA and SMA length.</summary>
public int VolumeLength
{
get => _volumeLength.Value;
set => _volumeLength.Value = value;
}
/// <summary>Minimum absolute aggregate bias.</summary>
public decimal BassThreshold
{
get => _bassThreshold.Value;
set => _bassThreshold.Value = value;
}
/// <summary>Native filled-entry percentage stop.</summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>Finished signal candle interval.</summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>Latest fully formed aggregate bias.</summary>
public decimal? LastBass { get; private set; }
/// <summary>Initializes the published indicator lengths, zero bias threshold and percent stop.</summary>
public BiasSentimentStrengthStrategy()
{
_macdFast = Param(nameof(MacdFast), 12).SetGreaterThanZero()
.SetDisplay("MacdFast", "Fast MACD EMA length", "Parameters");
_macdSlow = Param(nameof(MacdSlow), 26).SetGreaterThanZero()
.SetDisplay("MacdSlow", "Slow MACD EMA length", "Parameters");
_macdSignal = Param(nameof(MacdSignal), 9).SetGreaterThanZero()
.SetDisplay("MacdSignal", "MACD signal EMA length", "Parameters");
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetGreaterThanZero()
.SetDisplay("RsiPeriod", "Wilder RSI length", "Parameters");
_stochK = Param(nameof(StochK), 21).SetGreaterThanZero()
.SetDisplay("StochK", "Raw stochastic high-low window", "Parameters");
_stochSmooth = Param(nameof(StochSmooth), 14).SetGreaterThanZero()
.SetDisplay("StochSmooth", "Stochastic K smoothing length", "Parameters");
_stochD = Param(nameof(StochD), 14).SetGreaterThanZero()
.SetDisplay("StochD", "Smoothed stochastic D length", "Parameters");
_aoShort = Param(nameof(AoShort), 5).SetGreaterThanZero()
.SetDisplay("AoShort", "Short median-price SMA length", "Parameters");
_aoLong = Param(nameof(AoLong), 34).SetGreaterThanZero()
.SetDisplay("AoLong", "Long median-price SMA length", "Parameters");
_volumeLength = Param(nameof(VolumeLength), 30).SetGreaterThanZero()
.SetDisplay("VolumeLength", "Close VWMA and SMA length", "Parameters");
_bassThreshold = Param(nameof(BassThreshold), 0m).SetRange(0m, decimal.MaxValue)
.SetDisplay("BassThreshold", "Minimum absolute aggregate bias", "Parameters");
_stopLossPercent = Param(nameof(StopLossPercent), 1m).SetGreaterThanZero()
.SetDisplay("StopLossPercent", "Native filled-entry percentage stop", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("CandleType", "Finished signal candle interval", "Parameters");
OrderRegistering += order => _pending = order;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_smoothK = _smoothD = null;
_pending = null;
LastBass = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
if (MacdFast >= MacdSlow || AoShort >= AoLong)
throw new InvalidOperationException("Fast MACD and AO lengths must be shorter than their slow lengths.");
base.OnStarted2(time);
_smoothK = new SimpleMovingAverage { Length = StochSmooth };
_smoothD = new SimpleMovingAverage { Length = StochD };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = MacdFast }, LongMa = { Length = MacdSlow } },
SignalMa = { Length = MacdSignal },
};
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var stochastic = new StochasticK { Length = StochK };
var ao = new AwesomeOscillator { ShortMa = { Length = AoShort }, LongMa = { Length = AoLong } };
var vwma = new VolumeWeightedMovingAverage { Length = VolumeLength };
var sma = new SimpleMovingAverage { Length = VolumeLength };
var alligator = new Alligator();
StartProtection(new Unit(), StopLossPercent.Percents(), useMarketOrders: true, isLocalStop: true);
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quote = new Subscription(DataType.Level1, Security);
quote.MarketData.BuildField = field;
SubscribeLevel1(quote).Start();
}
var candles = SubscribeCandles(CandleType);
candles.BindEx([macd, rsi, stochastic, ao, vwma, sma, alligator], ProcessCandle).Start();
var area = CreateChartArea();
if (area is not null)
{
DrawCandles(area, candles);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished || !values[2].IsFormed || values[2].IsEmpty)
return;
var smoothed = _smoothK.Process(new DecimalIndicatorValue(_smoothK, values[2].GetValue<decimal>(), candle.OpenTime) { IsFinal = true });
if (!_smoothK.IsFormed) return;
var averageD = _smoothD.Process(new DecimalIndicatorValue(_smoothD, smoothed.GetValue<decimal>(), candle.OpenTime) { IsFinal = true });
if (!_smoothD.IsFormed || !values.All(v => v.IsFormed && !v.IsEmpty)
|| values[0] is not IMovingAverageConvergenceDivergenceSignalValue macd
|| macd.Macd is not decimal line || macd.Signal is not decimal signal
|| values[6] is not IAlligatorValue gator || gator.Jaw is not decimal jaw
|| gator.Teeth is not decimal teeth || gator.Lips is not decimal lips)
return;
var bass = (line - signal) * 2m + (values[1].GetValue<decimal>() - 50m) / 5m
+ (smoothed.GetValue<decimal>() - averageD.GetValue<decimal>()) * 0.15m
+ values[3].GetValue<decimal>() * 0.6m + (lips - teeth) + (teeth - jaw)
+ values[4].GetValue<decimal>() - values[5].GetValue<decimal>();
LastBass = bass;
if (!IsFormedAndOnlineAndAllowTrading() || _pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed))
return;
if (bass > BassThreshold && Position <= 0m)
BuyMarket(Volume + Math.Abs(Position));
else if (bass < -BassThreshold && Position >= 0m)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
import math
from collections import deque
from System import TimeSpan, DateTime, DateTimeKind, Decimal, Nullable
from StockSharp.Messages import DataType, CandleStates, Sides, OrderStates, Unit, UnitTypes
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
from System import Array
from StockSharp.Messages import Level1Fields
from StockSharp.Algo.Indicators import (MovingAverageConvergenceDivergenceSignal, RelativeStrengthIndex,
StochasticK, AwesomeOscillator, VolumeWeightedMovingAverage, SimpleMovingAverage, Alligator,
IIndicator, DecimalIndicatorValue)
class bias_sentiment_strength_strategy(Strategy):
def __init__(self):
super(bias_sentiment_strength_strategy, self).__init__()
self._macd_fast = self.Param("MacdFast", 12).SetGreaterThanZero().SetDisplay(
"MacdFast", "Fast MACD EMA length", "Parameters")
self._macd_slow = self.Param("MacdSlow", 26).SetGreaterThanZero().SetDisplay(
"MacdSlow", "Slow MACD EMA length", "Parameters")
self._macd_signal = self.Param("MacdSignal", 9).SetGreaterThanZero().SetDisplay(
"MacdSignal", "MACD signal EMA length", "Parameters")
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay(
"RsiPeriod", "Wilder RSI length", "Parameters")
self._stoch_k = self.Param("StochK", 21).SetGreaterThanZero().SetDisplay(
"StochK", "Raw stochastic high-low window", "Parameters")
self._stoch_smooth = self.Param("StochSmooth", 14).SetGreaterThanZero().SetDisplay(
"StochSmooth", "Stochastic K smoothing length", "Parameters")
self._stoch_d = self.Param("StochD", 14).SetGreaterThanZero().SetDisplay(
"StochD", "Smoothed stochastic D length", "Parameters")
self._ao_short = self.Param("AoShort", 5).SetGreaterThanZero().SetDisplay(
"AoShort", "Short median-price SMA length", "Parameters")
self._ao_long = self.Param("AoLong", 34).SetGreaterThanZero().SetDisplay(
"AoLong", "Long median-price SMA length", "Parameters")
self._volume_length = self.Param("VolumeLength", 30).SetGreaterThanZero().SetDisplay(
"VolumeLength", "Close VWMA and SMA length", "Parameters")
self._bass_threshold = self.Param("BassThreshold", 0.0).SetRange(0.0, float(Decimal.MaxValue)).SetDisplay(
"BassThreshold", "Minimum absolute aggregate bias", "Parameters")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetGreaterThanZero().SetDisplay(
"StopLossPercent", "Native filled-entry percentage stop", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay(
"CandleType", "Finished signal candle interval", "Parameters")
self._pending = None
self._smooth_k = self._smooth_d = None
self._last_bass = None
self.OrderRegistering += self._remember_order
@property
def MacdFast(self):
return self._macd_fast.Value
@MacdFast.setter
def MacdFast(self, value):
self._macd_fast.Value = value
@property
def MacdSlow(self):
return self._macd_slow.Value
@MacdSlow.setter
def MacdSlow(self, value):
self._macd_slow.Value = value
@property
def MacdSignal(self):
return self._macd_signal.Value
@MacdSignal.setter
def MacdSignal(self, value):
self._macd_signal.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def StochK(self):
return self._stoch_k.Value
@StochK.setter
def StochK(self, value):
self._stoch_k.Value = value
@property
def StochSmooth(self):
return self._stoch_smooth.Value
@StochSmooth.setter
def StochSmooth(self, value):
self._stoch_smooth.Value = value
@property
def StochD(self):
return self._stoch_d.Value
@StochD.setter
def StochD(self, value):
self._stoch_d.Value = value
@property
def AoShort(self):
return self._ao_short.Value
@AoShort.setter
def AoShort(self, value):
self._ao_short.Value = value
@property
def AoLong(self):
return self._ao_long.Value
@AoLong.setter
def AoLong(self, value):
self._ao_long.Value = value
@property
def VolumeLength(self):
return self._volume_length.Value
@VolumeLength.setter
def VolumeLength(self, value):
self._volume_length.Value = value
@property
def BassThreshold(self):
return self._bass_threshold.Value
@BassThreshold.setter
def BassThreshold(self, value):
self._bass_threshold.Value = value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@StopLossPercent.setter
def StopLossPercent(self, value):
self._stop_loss_percent.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def LastBass(self):
return self._last_bass
def _remember_order(self, order):
self._pending = order
def OnReseted(self):
super(bias_sentiment_strength_strategy, self).OnReseted()
self._pending = None
self._smooth_k = self._smooth_d = None
self._last_bass = None
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType), (self.Security, DataType.Level1)]
def OnStarted2(self, time):
if self.MacdFast >= self.MacdSlow or self.AoShort >= self.AoLong:
raise ValueError("Fast MACD and AO lengths must be shorter than their slow lengths.")
super(bias_sentiment_strength_strategy, self).OnStarted2(time)
self._smooth_k, self._smooth_d = SimpleMovingAverage(), SimpleMovingAverage()
self._smooth_k.Length, self._smooth_d.Length = self.StochSmooth, self.StochD
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length, macd.Macd.LongMa.Length, macd.SignalMa.Length = self.MacdFast, self.MacdSlow, self.MacdSignal
rsi, stochastic, ao = RelativeStrengthIndex(), StochasticK(), AwesomeOscillator()
rsi.Length, stochastic.Length = self.RsiPeriod, self.StochK
ao.ShortMa.Length, ao.LongMa.Length = self.AoShort, self.AoLong
vwma, sma, gator = VolumeWeightedMovingAverage(), SimpleMovingAverage(), Alligator()
vwma.Length = sma.Length = self.VolumeLength
self.StartProtection(Unit(), Unit(float(self.StopLossPercent), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quote = Subscription(DataType.Level1, self.Security)
quote.MarketData.BuildField = field
self.SubscribeLevel1(quote).Start()
candles = self.SubscribeCandles(self.CandleType)
candles.BindEx(Array[IIndicator]([macd, rsi, stochastic, ao, vwma, sma, gator]), self._process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, candles)
self.DrawOwnTrades(area)
def _process(self, candle, values):
if candle.State != CandleStates.Finished or not values[2].IsFormed or values[2].IsEmpty:
return
raw = DecimalIndicatorValue(self._smooth_k, values[2].GetValue[Decimal](None), candle.OpenTime)
raw.IsFinal = True
smooth = self._smooth_k.Process(raw)
if not self._smooth_k.IsFormed:
return
smooth_input = DecimalIndicatorValue(self._smooth_d, smooth.GetValue[Decimal](None), candle.OpenTime)
smooth_input.IsFinal = True
average = self._smooth_d.Process(smooth_input)
if not self._smooth_d.IsFormed or not all(v.IsFormed and not v.IsEmpty for v in values):
return
macd, gator = values[0], values[6]
if macd.Macd is None or macd.Signal is None or gator.Jaw is None or gator.Teeth is None or gator.Lips is None:
return
bass = (macd.Macd - macd.Signal) * Decimal(2) + (values[1].GetValue[Decimal](None) - Decimal(50)) / Decimal(5)
bass += (smooth.GetValue[Decimal](None) - average.GetValue[Decimal](None)) * Decimal(0.15)
bass += values[3].GetValue[Decimal](None) * Decimal(0.6)
bass += (gator.Lips - gator.Teeth) + (gator.Teeth - gator.Jaw)
bass += values[4].GetValue[Decimal](None) - values[5].GetValue[Decimal](None)
self._last_bass = bass
if not self.IsFormedAndOnlineAndAllowTrading() or (self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed)):
return
threshold = Decimal(float(self.BassThreshold))
if bass > threshold and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif bass < -threshold and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return bias_sentiment_strength_strategy()