Doppeltop-Muster (Double Top Pattern)
Das Doppeltop identifiziert zwei Hochpunkte, die durch eine Anzahl von Kerzen mit ähnlichen Preisen voneinander getrennt sind. Nach der Ausbildung des zweiten Hochpunkts bestätigt eine bärische Kerze die Umkehr.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 58 %. Die Strategie eignet sich am besten für den Aktienmarkt.
Die Strategie geht nach der Bestätigung short mit einem Stop oberhalb der Musthochs und zielt darauf ab, von einem Trendwechsel nach Erschöpfung der Käufer zu profitieren.
Positionen werden über Stop-Loss oder diskretionäre Ziele geschlossen.
Details
- Einstiegskriterien: Zwei Hochpunkte innerhalb von
SimilarityPercentnachDistanceKerzen. - Long/Short: Nur Short.
- Ausstiegskriterien: Kurs erholt sich oder Stop-Loss.
- Stops: Ja.
- Standardwerte:
Distance= 5SimilarityPercent= 2.0mCandleType= TimeSpan.FromMinutes(15)StopLossPercent= 1.0m
- Filter:
- Kategorie: Muster
- Richtung: Nur Short
- Indikatoren: Price Action
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Ja
- 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>
/// Double Top reversal strategy.
/// Detects two similar tops and enters short on confirmation.
/// Uses SMA for exit signal.
/// </summary>
public class DoubleTopStrategy : Strategy
{
private readonly StrategyParam<int> _distanceParam;
private readonly StrategyParam<decimal> _similarityPercent;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _recentHigh;
private decimal _prevHigh;
private int _barsSinceHigh;
private int _cooldown;
/// <summary>
/// Distance between tops in bars.
/// </summary>
public int Distance
{
get => _distanceParam.Value;
set => _distanceParam.Value = value;
}
/// <summary>
/// Maximum percent difference between two tops.
/// </summary>
public decimal SimilarityPercent
{
get => _similarityPercent.Value;
set => _similarityPercent.Value = value;
}
/// <summary>
/// MA Period for exit.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="DoubleTopStrategy"/>.
/// </summary>
public DoubleTopStrategy()
{
_distanceParam = Param(nameof(Distance), 20)
.SetRange(3, 100)
.SetDisplay("Distance", "Bars between tops", "Pattern");
_similarityPercent = Param(nameof(SimilarityPercent), 1.0m)
.SetRange(0.1m, 5.0m)
.SetDisplay("Similarity %", "Max % diff between tops", "Pattern");
_maPeriod = Param(nameof(MAPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for exit SMA", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_recentHigh = default;
_prevHigh = default;
_barsSinceHigh = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_recentHigh = 0;
_prevHigh = 0;
_barsSinceHigh = 0;
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
TrackHighs(candle);
return;
}
// Track new highs
if (_recentHigh == 0 || candle.HighPrice > _recentHigh)
{
if (_recentHigh > 0)
_prevHigh = _recentHigh;
_recentHigh = candle.HighPrice;
_barsSinceHigh = 0;
}
else
{
_barsSinceHigh++;
}
if (Position == 0 && _prevHigh > 0 && _barsSinceHigh >= Distance)
{
var priceDiff = Math.Abs((_recentHigh - _prevHigh) / _prevHigh * 100);
if (priceDiff <= SimilarityPercent && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
_recentHigh = 0;
_prevHigh = 0;
}
else if (priceDiff <= SimilarityPercent && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
_recentHigh = 0;
_prevHigh = 0;
}
}
else if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
private void TrackHighs(ICandleMessage candle)
{
if (_recentHigh == 0 || candle.HighPrice > _recentHigh)
{
if (_recentHigh > 0)
_prevHigh = _recentHigh;
_recentHigh = candle.HighPrice;
_barsSinceHigh = 0;
}
else
{
_barsSinceHigh++;
}
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class double_top_strategy(Strategy):
"""
Double Top reversal strategy.
Detects two similar tops and enters short on confirmation.
Uses SMA for exit signal.
"""
def __init__(self):
super(double_top_strategy, self).__init__()
self._distance = self.Param("Distance", 20).SetDisplay("Distance", "Bars between tops", "Pattern")
self._similarity_pct = self.Param("SimilarityPercent", 1.0).SetDisplay("Similarity %", "Max % diff between tops", "Pattern")
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for exit SMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._recent_high = 0.0
self._prev_high = 0.0
self._bars_since_high = 0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(double_top_strategy, self).OnReseted()
self._recent_high = 0.0
self._prev_high = 0.0
self._bars_since_high = 0
self._cooldown = 0
def OnStarted2(self, time):
super(double_top_strategy, self).OnStarted2(time)
self._recent_high = 0.0
self._prev_high = 0.0
self._bars_since_high = 0
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _track_highs(self, candle):
high = float(candle.HighPrice)
if self._recent_high == 0 or high > self._recent_high:
if self._recent_high > 0:
self._prev_high = self._recent_high
self._recent_high = high
self._bars_since_high = 0
else:
self._bars_since_high += 1
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
self._track_highs(candle)
return
# Track new highs
high = float(candle.HighPrice)
if self._recent_high == 0 or high > self._recent_high:
if self._recent_high > 0:
self._prev_high = self._recent_high
self._recent_high = high
self._bars_since_high = 0
else:
self._bars_since_high += 1
close = float(candle.ClosePrice)
sv = float(sma_val)
cd = self._cooldown_bars.Value
dist = self._distance.Value
sim = float(self._similarity_pct.Value)
if self.Position == 0 and self._prev_high > 0 and self._bars_since_high >= dist:
price_diff = abs((self._recent_high - self._prev_high) / self._prev_high * 100.0)
if price_diff <= sim and close < sv:
self.SellMarket()
self._cooldown = cd
self._recent_high = 0.0
self._prev_high = 0.0
elif price_diff <= sim and close > sv:
self.BuyMarket()
self._cooldown = cd
self._recent_high = 0.0
self._prev_high = 0.0
elif self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return double_top_strategy()