ADX für BTC
Die Strategie verwendet den Average Directional Index (ADX) mit einem optionalen SMA-Trendfilter, um starke Bewegungen in Bitcoin zu erfassen.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 80%. Am besten funktioniert sie auf dem Kryptomarkt.
Das System kauft, wenn der ADX das Einstiegsniveau nach oben kreuzt und der Trendfilter bullisch ist. Die Position schließt sich, wenn der ADX unter das Ausstiegsniveau fällt.
Details
- Einstiegskriterien: ADX kreuzt
EntryLevelnach oben und (falls aktiviert) schnelle SMA > langsame SMA. - Long/Short: Nur Long.
- Ausstiegskriterien: ADX kreuzt
ExitLevelnach unten. - Stops: Nein.
- Standardwerte:
EntryLevel= 14mExitLevel= 45mSmaFilter= trueSmaLength= 200CandleType= TimeSpan.FromMinutes(5)
- Filter:
- Kategorie: Trend
- Richtung: Nur Long
- Indikatoren: ADX, SMA
- Stops: Nein
- Komplexität: Grundlegend
- Zeitrahmen: Intraday (5m)
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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 based long-only strategy for BTC.
/// Enters when ADX crosses above entry level, exits when ADX crosses below exit level.
/// </summary>
public class AdxForBtcStrategy : Strategy
{
private readonly StrategyParam<decimal> _entryLevel;
private readonly StrategyParam<decimal> _exitLevel;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevAdx;
private int _cooldownRemaining;
public decimal EntryLevel { get => _entryLevel.Value; set => _entryLevel.Value = value; }
public decimal ExitLevel { get => _exitLevel.Value; set => _exitLevel.Value = value; }
public int SmaLength { get => _smaLength.Value; set => _smaLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AdxForBtcStrategy()
{
_entryLevel = Param(nameof(EntryLevel), 14m)
.SetGreaterThanZero()
.SetDisplay("Entry Level", "ADX threshold for entry", "Strategy");
_exitLevel = Param(nameof(ExitLevel), 40m)
.SetGreaterThanZero()
.SetDisplay("Exit Level", "ADX threshold for exit", "Strategy");
_smaLength = Param(nameof(SmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("SMA Length", "Length for trend SMA", "Strategy");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAdx = 0m;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = 14 };
var sma = new SimpleMovingAverage { Length = SmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var adxTyped = (IAverageDirectionalIndexValue)adxValue;
if (adxTyped.MovingAverage is not decimal adxMa ||
adxTyped.Dx.Plus is not decimal diPlus ||
adxTyped.Dx.Minus is not decimal diMinus)
return;
var smaVal = smaValue.ToDecimal();
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevAdx = adxMa;
return;
}
// Enter long when ADX crosses above entry level with +DI > -DI and price above SMA
if (_prevAdx > 0 && _prevAdx <= EntryLevel && adxMa > EntryLevel && diPlus > diMinus && candle.ClosePrice > smaVal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Enter short when ADX crosses above entry level with -DI > +DI and price below SMA
else if (_prevAdx > 0 && _prevAdx <= EntryLevel && adxMa > EntryLevel && diMinus > diPlus && candle.ClosePrice < smaVal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit when ADX drops below exit level
else if (Position > 0 && adxMa < ExitLevel && _prevAdx >= ExitLevel)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
else if (Position < 0 && adxMa < ExitLevel && _prevAdx >= ExitLevel)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevAdx = adxMa;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageDirectionalIndex, SimpleMovingAverage, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class adx_for_btc_strategy(Strategy):
def __init__(self):
super(adx_for_btc_strategy, self).__init__()
self._entry_level = self.Param("EntryLevel", 14.0) \
.SetGreaterThanZero() \
.SetDisplay("Entry Level", "ADX threshold for entry", "Strategy")
self._exit_level = self.Param("ExitLevel", 40.0) \
.SetGreaterThanZero() \
.SetDisplay("Exit Level", "ADX threshold for exit", "Strategy")
self._sma_length = self.Param("SmaLength", 50) \
.SetGreaterThanZero() \
.SetDisplay("SMA Length", "Length for trend SMA", "Strategy")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._prev_adx = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adx_for_btc_strategy, self).OnReseted()
self._prev_adx = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(adx_for_btc_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = 14
sma = SimpleMovingAverage()
sma.Length = int(self._sma_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, sma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, adx_value, sma_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
adx_ma = adx_value.MovingAverage
if adx_ma is None:
return
dx = adx_value.Dx
di_plus_val = dx.Plus
di_minus_val = dx.Minus
if di_plus_val is None or di_minus_val is None:
return
adx_v = float(adx_ma)
di_plus = float(di_plus_val)
di_minus = float(di_minus_val)
sma_v = float(IndicatorHelper.ToDecimal(sma_value))
close = float(candle.ClosePrice)
entry = float(self._entry_level.Value)
exit_lv = float(self._exit_level.Value)
cooldown = int(self._cooldown_bars.Value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_adx = adx_v
return
if self._prev_adx > 0 and self._prev_adx <= entry and adx_v > entry and di_plus > di_minus and close > sma_v and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self._prev_adx > 0 and self._prev_adx <= entry and adx_v > entry and di_minus > di_plus and close < sma_v and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and adx_v < exit_lv and self._prev_adx >= exit_lv:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and adx_v < exit_lv and self._prev_adx >= exit_lv:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_adx = adx_v
def CreateClone(self):
return adx_for_btc_strategy()