Estratégia Three Signal Directional Trend
A estratégia Three Signal Directional Trend combina MACD, oscilador estocástico e taxa de variação da média móvel para determinar a direção da tendência. Cada indicador vota por condições compradas ou vendidas e as posições são abertas quando pelo menos dois indicadores concordam. O método visa capturar movimentos direcionais amplos filtrando o ruído por meio de múltiplos sinais de confirmação.
Detalhes
- Critérios de entrada:
- Pelo menos dois de três sinais concordam.
- Comprado: sinal MACD em alta, estocástico abaixo da zona de sobrevenda, MA ROC positivo.
- Vendido: sinal MACD em queda, estocástico acima da zona de sobrecompra, MA ROC negativo.
- Comprado/Vendido: Ambos os lados.
- Critérios de saída:
- Sinal oposto.
- Stops: Nenhum.
- Valores padrão:
AvgLength= 50RocLength= 1AvgRocLength= 10StochLength= 14SmoothK= 3Overbought= 80Oversold= 20MacdFastLength= 12MacdSlowLength= 26MacdAvgLength= 9
- Filtros:
- Categoria: Seguidor de tendência
- Direção: Ambos
- Indicadores: MACD, Stochastic, SMA, ROC
- Stops: Nenhum
- Complexidade: Baixo
- Período: Qualquer
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Three Signal Directional Trend Strategy.
/// Three indicators vote: the MACD signal line rising or falling, the Stochastic %K (StochLength, smoothed over SmoothK) below
/// Oversold or above Overbought, and the rate of change over RocLength bars of the AvgLength SMA, averaged over AvgRocLength
/// bars, above or below zero. At least two long votes open a long and at least two short votes open a short, reversing
/// an opposite position.
/// </summary>
public class ThreeSignalDirectionalTrendStrategy : Strategy
{
private readonly StrategyParam<int> _avgLength;
private readonly StrategyParam<int> _rocLength;
private readonly StrategyParam<int> _avgRocLength;
private readonly StrategyParam<int> _stochLength;
private readonly StrategyParam<int> _smoothK;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<int> _macdFastLength;
private readonly StrategyParam<int> _macdSlowLength;
private readonly StrategyParam<int> _macdAvgLength;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _kSmooth;
private SimpleMovingAverage _rocAverage;
private readonly List<decimal> _maHistory = [];
private decimal? _prevSignal;
/// <summary>
/// SMA period of the rate of change.
/// </summary>
public int AvgLength
{
get => _avgLength.Value;
set => _avgLength.Value = value;
}
/// <summary>
/// Bars of the rate of change.
/// </summary>
public int RocLength
{
get => _rocLength.Value;
set => _rocLength.Value = value;
}
/// <summary>
/// Averaging period of the rate of change.
/// </summary>
public int AvgRocLength
{
get => _avgRocLength.Value;
set => _avgRocLength.Value = value;
}
/// <summary>
/// Stochastic lookback.
/// </summary>
public int StochLength
{
get => _stochLength.Value;
set => _stochLength.Value = value;
}
/// <summary>
/// Smoothing of %K.
/// </summary>
public int SmoothK
{
get => _smoothK.Value;
set => _smoothK.Value = value;
}
/// <summary>
/// Stochastic overbought level.
/// </summary>
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
/// <summary>
/// Stochastic oversold level.
/// </summary>
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
/// <summary>
/// MACD fast EMA period.
/// </summary>
public int MacdFastLength
{
get => _macdFastLength.Value;
set => _macdFastLength.Value = value;
}
/// <summary>
/// MACD slow EMA period.
/// </summary>
public int MacdSlowLength
{
get => _macdSlowLength.Value;
set => _macdSlowLength.Value = value;
}
/// <summary>
/// MACD signal line period.
/// </summary>
public int MacdAvgLength
{
get => _macdAvgLength.Value;
set => _macdAvgLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ThreeSignalDirectionalTrendStrategy()
{
_avgLength = Param(nameof(AvgLength), 50)
.SetGreaterThanZero()
.SetDisplay("Average Length", "SMA period of the rate of change", "ROC");
_rocLength = Param(nameof(RocLength), 1)
.SetGreaterThanZero()
.SetDisplay("ROC Length", "Bars of the rate of change", "ROC");
_avgRocLength = Param(nameof(AvgRocLength), 10)
.SetGreaterThanZero()
.SetDisplay("Average ROC Length", "Averaging period of the rate of change", "ROC");
_stochLength = Param(nameof(StochLength), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Length", "Stochastic lookback", "Stochastic");
_smoothK = Param(nameof(SmoothK), 3)
.SetGreaterThanZero()
.SetDisplay("Smooth K", "Smoothing of %K", "Stochastic");
_overbought = Param(nameof(Overbought), 80m)
.SetDisplay("Overbought", "Stochastic overbought level", "Stochastic");
_oversold = Param(nameof(Oversold), 20m)
.SetDisplay("Oversold", "Stochastic oversold level", "Stochastic");
_macdFastLength = Param(nameof(MacdFastLength), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "MACD fast EMA period", "MACD");
_macdSlowLength = Param(nameof(MacdSlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "MACD slow EMA period", "MACD");
_macdAvgLength = Param(nameof(MacdAvgLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "MACD signal line period", "MACD");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_maHistory.Clear();
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_maHistory.Clear();
_prevSignal = null;
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastLength },
LongMa = { Length = MacdSlowLength },
},
SignalMa = { Length = MacdAvgLength },
};
var stochK = new StochasticK { Length = StochLength };
var sma = new SimpleMovingAverage { Length = AvgLength };
_kSmooth = new SimpleMovingAverage { Length = SmoothK };
_rocAverage = new SimpleMovingAverage { Length = AvgRocLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, stochK, sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, macd);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue stochValue, IIndicatorValue smaValue)
{
if (candle.State != CandleStates.Finished)
return;
var time = candle.OpenTime;
decimal? k = null;
if (stochValue.IsFormed)
{
var smoothed = _kSmooth.Process(stochValue.GetValue<decimal>(), time, true);
if (_kSmooth.IsFormed)
k = smoothed.ToDecimal();
}
decimal? avgRoc = null;
if (smaValue.IsFormed)
{
var ma = smaValue.GetValue<decimal>();
_maHistory.Add(ma);
if (_maHistory.Count > RocLength + 1)
_maHistory.RemoveAt(0);
if (_maHistory.Count == RocLength + 1 && _maHistory[0] != 0m)
{
var roc = (ma - _maHistory[0]) / _maHistory[0] * 100m;
var averaged = _rocAverage.Process(roc, time, true);
if (_rocAverage.IsFormed)
avgRoc = averaged.ToDecimal();
}
}
decimal? signal = null;
if (macdValue.IsFormed && macdValue is IMovingAverageConvergenceDivergenceSignalValue { Signal: decimal s })
signal = s;
var prevSignal = _prevSignal;
if (signal is not null)
_prevSignal = signal;
if (k is not decimal kValue || avgRoc is not decimal rocValue || signal is not decimal signalValue || prevSignal is not decimal previous)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var longVotes = (signalValue > previous ? 1 : 0) + (kValue < Oversold ? 1 : 0) + (rocValue > 0m ? 1 : 0);
var shortVotes = (signalValue < previous ? 1 : 0) + (kValue > Overbought ? 1 : 0) + (rocValue < 0m ? 1 : 0);
if (longVotes >= 2 && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortVotes >= 2 && Position >= 0)
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")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal, StochasticK, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class three_signal_directional_trend_strategy(Strategy):
"""
Three Signal Directional Trend Strategy.
Three indicators vote: the MACD signal line rising or falling, the Stochastic %K (StochLength, smoothed over SmoothK) below
Oversold or above Overbought, and the rate of change over RocLength bars of the AvgLength SMA, averaged over AvgRocLength
bars, above or below zero. At least two long votes open a long and at least two short votes open a short, reversing
an opposite position.
"""
def __init__(self):
super(three_signal_directional_trend_strategy, self).__init__()
self._avg_length = self.Param("AvgLength", 50).SetGreaterThanZero().SetDisplay("Average Length", "SMA period of the rate of change", "ROC")
self._roc_length = self.Param("RocLength", 1).SetGreaterThanZero().SetDisplay("ROC Length", "Bars of the rate of change", "ROC")
self._avg_roc_length = self.Param("AvgRocLength", 10).SetGreaterThanZero().SetDisplay("Average ROC Length", "Averaging period of the rate of change", "ROC")
self._stoch_length = self.Param("StochLength", 14).SetGreaterThanZero().SetDisplay("Stochastic Length", "Stochastic lookback", "Stochastic")
self._smooth_k = self.Param("SmoothK", 3).SetGreaterThanZero().SetDisplay("Smooth K", "Smoothing of %K", "Stochastic")
self._overbought = self.Param("Overbought", 80.0).SetDisplay("Overbought", "Stochastic overbought level", "Stochastic")
self._oversold = self.Param("Oversold", 20.0).SetDisplay("Oversold", "Stochastic oversold level", "Stochastic")
self._macd_fast_length = self.Param("MacdFastLength", 12).SetGreaterThanZero().SetDisplay("MACD Fast", "MACD fast EMA period", "MACD")
self._macd_slow_length = self.Param("MacdSlowLength", 26).SetGreaterThanZero().SetDisplay("MACD Slow", "MACD slow EMA period", "MACD")
self._macd_avg_length = self.Param("MacdAvgLength", 9).SetGreaterThanZero().SetDisplay("MACD Signal", "MACD signal line period", "MACD")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._k_smooth = None
self._roc_average = None
self._ma_history = []
self._prev_signal = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(three_signal_directional_trend_strategy, self).OnReseted()
self._ma_history = []
self._prev_signal = None
def OnStarted2(self, time):
super(three_signal_directional_trend_strategy, self).OnStarted2(time)
self._ma_history = []
self._prev_signal = None
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast_length.Value
macd.Macd.LongMa.Length = self._macd_slow_length.Value
macd.SignalMa.Length = self._macd_avg_length.Value
stoch_k = StochasticK()
stoch_k.Length = self._stoch_length.Value
sma = SimpleMovingAverage()
sma.Length = self._avg_length.Value
self._k_smooth = SimpleMovingAverage()
self._k_smooth.Length = self._smooth_k.Value
self._roc_average = SimpleMovingAverage()
self._roc_average.Length = self._avg_roc_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, stoch_k, sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, macd)
def _process_candle(self, candle, macd_value, stoch_value, sma_value):
if candle.State != CandleStates.Finished:
return
time = candle.OpenTime
k = None
if stoch_value.IsFormed:
smoothed = process_float(self._k_smooth, stoch_value.GetValue[Decimal](None), time, True)
if self._k_smooth.IsFormed:
k = float(smoothed.GetValue[Decimal](None))
avg_roc = None
if sma_value.IsFormed:
ma = float(sma_value.GetValue[Decimal](None))
roc_length = self._roc_length.Value
self._ma_history.append(ma)
if len(self._ma_history) > roc_length + 1:
self._ma_history.pop(0)
if len(self._ma_history) == roc_length + 1 and self._ma_history[0] != 0:
roc = (ma - self._ma_history[0]) / self._ma_history[0] * 100.0
averaged = process_float(self._roc_average, roc, time, True)
if self._roc_average.IsFormed:
avg_roc = float(averaged.GetValue[Decimal](None))
signal = None
if macd_value.IsFormed and macd_value.Signal is not None:
signal = float(macd_value.Signal)
previous = self._prev_signal
if signal is not None:
self._prev_signal = signal
if k is None or avg_roc is None or signal is None or previous is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
oversold = float(self._oversold.Value)
overbought = float(self._overbought.Value)
long_votes = (1 if signal > previous else 0) + (1 if k < oversold else 0) + (1 if avg_roc > 0 else 0)
short_votes = (1 if signal < previous else 0) + (1 if k > overbought else 0) + (1 if avg_roc < 0 else 0)
if long_votes >= 2 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_votes >= 2 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return three_signal_directional_trend_strategy()