Trendless AG Histogram-Strategie
Die Strategie handelt Umkehrungen, die vom Trendless AG Histogram-Indikator erkannt werden. Der Indikator misst den Abstand zwischen dem Preis und einem geglätteten gleitenden Durchschnitt und glättet das Ergebnis erneut, um ein Histogramm um null zu bilden. Lokale Minima weisen auf potenzielle Aufwärtsumkehrungen hin, während lokale Maxima auf Abwärtsumkehrungen hindeuten.
Positionen werden eröffnet, wenn das Histogramm die Richtung wechselt. Wenn der Indikator steigt, nachdem er unter früheren Werten lag, wird eine Long-Position eröffnet. Wenn er fällt, nachdem er über früheren Werten lag, wird eine Short-Position eröffnet. Optionale Stop-Loss- und Take-Profit-Niveaus managen das Risiko.
Details
- Einstiegskriterien:
- Long: Der Histogrammwert steigt, während der vorherige Wert niedriger als sein Vorgänger war.
- Short: Der Histogrammwert fällt, während der vorherige Wert höher als sein Vorgänger war.
- Long/Short: Beide Richtungen.
- Ausstiegskriterien:
- Entgegengesetztes Signal oder Stop-Loss/Take-Profit-Niveaus.
- Stops: Fester Stop-Loss und Take-Profit in Preiseinheiten.
- Standardwerte:
Fast Length= 7.Slow Length= 5.Stop Loss= 1000.Take Profit= 2000.Candle Type= 12-Stunden-Kerzen.
- Filter:
- Kategorie: Trendfolge.
- Richtung: Beide.
- Indikatoren: Benutzerdefinierter Indikator auf Basis gleitender Durchschnitte.
- Stops: Ja.
- Komplexität: Moderat.
- Zeitrahmen: Mittelfristig.
- Saisonalität: Nein.
- Neuronale Netze: Nein.
- Divergenz: Ja.
- Risikolevel: Mittel.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the Trendless AG Histogram indicator.
/// Opens long when the histogram forms a trough and starts rising.
/// Opens short when it forms a peak and starts falling.
/// Includes optional stop-loss and take-profit levels.
/// </summary>
public class TrendlessAgHistStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private TrendlessAgHist _indicator;
private decimal _prev1;
private decimal _prev2;
private bool _initialized;
private decimal _entryPrice;
private bool _isLong;
private int _barsSinceTrade;
/// <summary>
/// Fast smoothing period.
/// </summary>
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
/// <summary>
/// Slow smoothing period.
/// </summary>
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
/// <summary>
/// Stop loss in price units.
/// </summary>
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
/// <summary>
/// Take profit in price units.
/// </summary>
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initializes a new instance of <see cref="TrendlessAgHistStrategy"/>.
/// </summary>
public TrendlessAgHistStrategy()
{
_fastLength = Param(nameof(FastLength), 7)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Period of the first smoothing", "Parameters");
_slowLength = Param(nameof(SlowLength), 5)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Period of the second smoothing", "Parameters");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Loss limit in price units", "Risk Management");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Profit target in price units", "Risk Management");
_cooldownBars = Param(nameof(CooldownBars), 1)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(12).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_indicator?.Reset();
_prev1 = 0m;
_prev2 = 0m;
_initialized = false;
_entryPrice = 0m;
_isLong = false;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_indicator = new TrendlessAgHist
{
FastLength = FastLength,
SlowLength = SlowLength
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_indicator, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _indicator);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal value)
{
if (candle.State != CandleStates.Finished)
return;
if (!_initialized)
{
_prev2 = _prev1;
_prev1 = value;
_initialized = true;
return;
}
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
if (_barsSinceTrade >= CooldownBars && _prev1 < _prev2 && value > _prev1 && _prev1 < 0m)
{
if (Position <= 0)
{
_entryPrice = candle.ClosePrice;
_isLong = true;
BuyMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
}
else if (_barsSinceTrade >= CooldownBars && _prev1 > _prev2 && value < _prev1 && _prev1 > 0m)
{
if (Position >= 0)
{
_entryPrice = candle.ClosePrice;
_isLong = false;
SellMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
}
_prev2 = _prev1;
_prev1 = value;
if (Position != 0 && _entryPrice != 0m)
CheckRisk(candle.ClosePrice);
}
private void CheckRisk(decimal price)
{
if (_isLong && Position > 0)
{
if (price <= _entryPrice - StopLoss || price >= _entryPrice + TakeProfit)
SellMarket(Position);
}
else if (!_isLong && Position < 0)
{
if (price >= _entryPrice + StopLoss || price <= _entryPrice - TakeProfit)
BuyMarket(Math.Abs(Position));
}
}
private class TrendlessAgHist : BaseIndicator
{
public int FastLength { get; set; } = 7;
public int SlowLength { get; set; } = 5;
private readonly ExponentialMovingAverage _fast = new() { Length = 7 };
private readonly ExponentialMovingAverage _slow = new() { Length = 5 };
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var candle = input.GetValue<ICandleMessage>();
var price = candle.ClosePrice;
var fastResult = _fast.Process(new DecimalIndicatorValue(_fast, price, input.Time) { IsFinal = true });
var fastVal = fastResult.IsEmpty ? price : fastResult.ToDecimal();
var diff = price - fastVal;
var slowResult = _slow.Process(new DecimalIndicatorValue(_slow, diff, input.Time) { IsFinal = true });
var slowVal = slowResult.IsEmpty ? diff : slowResult.ToDecimal();
IsFormed = _fast.IsFormed && _slow.IsFormed;
return new DecimalIndicatorValue(this, slowVal, input.Time);
}
public override void Reset()
{
base.Reset();
_fast.Reset();
_slow.Reset();
}
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class trendless_ag_hist_strategy(Strategy):
def __init__(self):
super(trendless_ag_hist_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 7) \
.SetDisplay("Fast Length", "Period of the first smoothing", "Parameters")
self._slow_length = self.Param("SlowLength", 5) \
.SetDisplay("Slow Length", "Period of the second smoothing", "Parameters")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Loss limit in price units", "Risk Management")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Profit target in price units", "Risk Management")
self._cooldown_bars = self.Param("CooldownBars", 1) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(12))) \
.SetDisplay("Candle Type", "Working candle timeframe", "General")
self._fast_ema = None
self._slow_ema = None
self._prev1 = 0.0
self._prev2 = 0.0
self._initialized = False
self._entry_price = 0.0
self._is_long = False
self._bars_since_trade = 0
@property
def FastLength(self):
return self._fast_length.Value
@FastLength.setter
def FastLength(self, value):
self._fast_length.Value = value
@property
def SlowLength(self):
return self._slow_length.Value
@SlowLength.setter
def SlowLength(self, value):
self._slow_length.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _compute_trendless(self, candle):
price = float(candle.ClosePrice)
fast_result = process_float(self._fast_ema, price, candle.OpenTime, True)
fast_val = price if fast_result.IsEmpty else float(fast_result)
diff = price - fast_val
slow_result = process_float(self._slow_ema, diff, candle.OpenTime, True)
slow_val = diff if slow_result.IsEmpty else float(slow_result)
return slow_val
def _check_risk(self, price):
if self._is_long and self.Position > 0:
if price <= self._entry_price - self.StopLoss or price >= self._entry_price + self.TakeProfit:
self.SellMarket(self.Position)
elif not self._is_long and self.Position < 0:
if price >= self._entry_price + self.StopLoss or price <= self._entry_price - self.TakeProfit:
self.BuyMarket(abs(self.Position))
def OnStarted2(self, time):
super(trendless_ag_hist_strategy, self).OnStarted2(time)
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
value = self._compute_trendless(candle)
if not self._initialized:
self._prev2 = self._prev1
self._prev1 = value
self._initialized = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev2 = self._prev1
self._prev1 = value
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
if (self._bars_since_trade >= self.CooldownBars
and self._prev1 < self._prev2
and value > self._prev1
and self._prev1 < 0.0):
if self.Position <= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long = True
self.BuyMarket(self.Volume + abs(self.Position))
self._bars_since_trade = 0
elif (self._bars_since_trade >= self.CooldownBars
and self._prev1 > self._prev2
and value < self._prev1
and self._prev1 > 0.0):
if self.Position >= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long = False
self.SellMarket(self.Volume + abs(self.Position))
self._bars_since_trade = 0
self._prev2 = self._prev1
self._prev1 = value
if self.Position != 0 and self._entry_price != 0.0:
self._check_risk(float(candle.ClosePrice))
def OnReseted(self):
super(trendless_ag_hist_strategy, self).OnReseted()
self._fast_ema = None
self._slow_ema = None
self._prev1 = 0.0
self._prev2 = 0.0
self._initialized = False
self._entry_price = 0.0
self._is_long = False
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return trendless_ag_hist_strategy()