Estratégia Adx Stochastic
Estratégia que combina o ADX (Índice Direcional Médio) para a força da tendência e o Oscilador Stochastic para o timing de entrada com condições de sobrevenda/sobrecompra.
Os testes indicam um retorno anual médio de aproximadamente 172%. Funciona melhor no mercado de câmbio.
O ADX destaca a força da tendência enquanto o Stochastic identifica as correções. Sinais de compra ou venda aparecem quando o momentum vira enquanto o ADX permanece alto.
É adequado para traders que combinam seguidor de tendência com timing de oscilador. Um stop protetor percentual ajuda a controlar os drawdowns.
Detalhes
- Critérios de entrada:
- Comprado:
ADX > AdxThreshold && StochK < StochOversold && Bullish - Vendido:
ADX > AdxThreshold && StochK > StochOverbought && Bearish
- Comprado:
- Comprado/Vendido: Ambos
- Critérios de saída:
- Sair quando
ADX < AdxThreshold
- Sair quando
- Stops: Baseado em percentual em
StopLossPercent - Valores padrão:
AdxPeriod= 14AdxThreshold= 25mStochPeriod= 14StochK= 3StochOversold= 20mStochOverbought= 80mStopLossPercent= 2.0mCandleType= TimeSpan.FromMinutes(5).TimeFrame() Alta significa +DI acima de -DI e baixa -DI acima de +DI. StochK nas regras é o %K: o estocástico de StochPeriod candles suavizado em StochK candles; o %D não participa, por isso não tem ajuste. Uma entrada contra uma posição aberta a inverte.
- Filtros:
- Categoria: Reversão à média
- Direção: Ambos
- Indicadores: ADX, Stochastic Oscillator
- Stops: Sim
- Complexidade: Intermediário
- Período: Médio prazo
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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>
/// ADX Stochastic strategy.
/// Bullish means +DI above -DI and bearish means -DI above +DI. While ADX is above AdxThreshold, %K below StochOversold in a bullish
/// trend goes long and %K above StochOverbought in a bearish trend goes short, reversing an opposite position; %K is the stochastic over
/// StochPeriod candles smoothed over StochK candles. The position closes once ADX falls below AdxThreshold, and a percent stop limits the loss.
/// </summary>
public class AdxStochasticStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochK;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Period of ADX.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// ADX level of a strong trend.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Lookback period of the raw stochastic.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Smoothing period of %K.
/// </summary>
public int StochK
{
get => _stochK.Value;
set => _stochK.Value = value;
}
/// <summary>
/// %K level for longs.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// %K level for shorts.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AdxStochasticStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period of ADX", "ADX");
_adxThreshold = Param(nameof(AdxThreshold), 25m)
.SetDisplay("ADX Threshold", "ADX level of a strong trend", "ADX");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Lookback period of the raw stochastic", "Stochastic");
_stochK = Param(nameof(StochK), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Smoothing period of %K", "Stochastic");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Stochastic Oversold", "%K level for longs", "Stochastic");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Stochastic Overbought", "%K level for shorts", "Stochastic");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
// The D line of the core oscillator is the smoothed %K.
var stochastic = new StochasticOscillator
{
K = { Length = StochPeriod },
D = { Length = StochK },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, stochastic, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, adx);
DrawIndicator(oscillators, stochastic);
}
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue stochasticValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!adxValue.IsFormed || !stochasticValue.IsFormed)
return;
if (adxValue is not AverageDirectionalIndexValue { MovingAverage: decimal strength } typedAdx
|| typedAdx.Dx.Plus is not decimal plusDi || typedAdx.Dx.Minus is not decimal minusDi)
return;
if (stochasticValue is not IStochasticOscillatorValue { D: decimal k })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var strong = strength > AdxThreshold;
if (strong && k < StochOversold && plusDi > minusDi && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (strong && k > StochOverbought && minusDi > plusDi && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && strength < AdxThreshold)
SellMarket(Position);
else if (Position < 0 && strength < AdxThreshold)
BuyMarket(-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")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import AverageDirectionalIndex, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class adx_stochastic_strategy(Strategy):
"""
ADX Stochastic strategy.
Bullish means +DI above -DI and bearish means -DI above +DI. While ADX is above AdxThreshold, %K below StochOversold in a bullish
trend goes long and %K above StochOverbought in a bearish trend goes short, reversing an opposite position; %K is the stochastic over
StochPeriod candles smoothed over StochK candles. The position closes once ADX falls below AdxThreshold, and a percent stop limits the loss.
"""
def __init__(self):
super(adx_stochastic_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14).SetGreaterThanZero().SetDisplay("ADX Period", "Period of ADX", "ADX")
self._adx_threshold = self.Param("AdxThreshold", 25.0).SetDisplay("ADX Threshold", "ADX level of a strong trend", "ADX")
self._stoch_period = self.Param("StochPeriod", 14).SetGreaterThanZero().SetDisplay("Stochastic Period", "Lookback period of the raw stochastic", "Stochastic")
self._stoch_k = self.Param("StochK", 3).SetGreaterThanZero().SetDisplay("Stochastic %K", "Smoothing period of %K", "Stochastic")
self._stoch_oversold = self.Param("StochOversold", 20.0).SetDisplay("Stochastic Oversold", "%K level for longs", "Stochastic")
self._stoch_overbought = self.Param("StochOverbought", 80.0).SetDisplay("Stochastic Overbought", "%K level for shorts", "Stochastic")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(adx_stochastic_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
# The D line of the core oscillator is the smoothed %K.
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_period.Value
stochastic.D.Length = self._stoch_k.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, stochastic, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, adx)
self.DrawIndicator(oscillators, stochastic)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, adx_value, stochastic_value):
if candle.State != CandleStates.Finished:
return
if not adx_value.IsFormed or not stochastic_value.IsFormed:
return
if adx_value.MovingAverage is None or adx_value.Dx.Plus is None or adx_value.Dx.Minus is None or stochastic_value.D is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
strength = adx_value.MovingAverage
plus_di = adx_value.Dx.Plus
minus_di = adx_value.Dx.Minus
k = stochastic_value.D
threshold = Decimal(self._adx_threshold.Value)
strong = strength > threshold
if strong and k < Decimal(self._stoch_oversold.Value) and plus_di > minus_di and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif strong and k > Decimal(self._stoch_overbought.Value) and minus_di > plus_di and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and strength < threshold:
self.SellMarket(self.Position)
elif self.Position < 0 and strength < threshold:
self.BuyMarket(-self.Position)
def CreateClone(self):
return adx_stochastic_strategy()