Strategie Tweezer Bottom
Der Tweezer Bottom ist ein Zwei-Kerzen-Umkehrmuster, das nach einem Rückgang erscheint. Beide Kerzen teilen ein ähnliches Tief, was signalisiert, dass Verkäufer nicht über dieses Niveau hinaus drücken konnten.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 184%. Die Strategie funktioniert am besten am Kryptomarkt.
Diese Strategie geht long, nachdem die zweite Kerze den gemeinsamen Boden bestätigt hat, in Erwartung einer Erholung, wenn der Verkaufsdruck nachlässt.
Stops werden knapp unterhalb des gemeinsamen Tiefs platziert, um das Risiko zu steuern, und die Position wird beendet, wenn der Preis keine Erholung zeigt.
Details
- Einstiegskriterien: Mustererkennung
- Long/Short: Beide
- Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
- Stops: Ja, prozentbasiert
- Standardwerte:
CandleType= 15 MinutenStopLoss= 2%
- Filter:
- Kategorie: Muster
- Richtung: Beide
- Indikatoren: Candlestick
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- 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>
/// Tweezer Bottom strategy.
/// Enters long on Tweezer Bottom (bearish then bullish with matching lows).
/// Enters short on Tweezer Top (bullish then bearish with matching highs).
/// Uses SMA for exit confirmation.
/// Uses cooldown to control trade frequency.
/// </summary>
public class TweezerBottomStrategy : Strategy
{
private readonly StrategyParam<decimal> _tolerancePercent;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private ICandleMessage _prevCandle;
private int _cooldown;
/// <summary>
/// Tolerance for matching lows/highs.
/// </summary>
public decimal TolerancePercent
{
get => _tolerancePercent.Value;
set => _tolerancePercent.Value = value;
}
/// <summary>
/// MA period for exit.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public TweezerBottomStrategy()
{
_tolerancePercent = Param(nameof(TolerancePercent), 0.1m)
.SetRange(0.05m, 1m)
.SetDisplay("Tolerance %", "Max diff between lows/highs", "Pattern");
_maLength = Param(nameof(MaLength), 20)
.SetRange(10, 50)
.SetDisplay("MA Length", "Period of SMA for exit", "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();
_prevCandle = null;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = MaLength };
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 (_prevCandle == null)
{
_prevCandle = candle;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevCandle = candle;
return;
}
var lowTolerance = _prevCandle.LowPrice * (TolerancePercent / 100m);
var highTolerance = _prevCandle.HighPrice * (TolerancePercent / 100m);
// Tweezer Bottom: prev bearish, current bullish, matching lows
var isTweezerBottom =
_prevCandle.ClosePrice < _prevCandle.OpenPrice &&
candle.ClosePrice > candle.OpenPrice &&
Math.Abs(_prevCandle.LowPrice - candle.LowPrice) <= lowTolerance;
// Tweezer Top: prev bullish, current bearish, matching highs
var isTweezerTop =
_prevCandle.ClosePrice > _prevCandle.OpenPrice &&
candle.ClosePrice < candle.OpenPrice &&
Math.Abs(_prevCandle.HighPrice - candle.HighPrice) <= highTolerance;
if (Position == 0 && isTweezerBottom)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && isTweezerTop)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevCandle = candle;
}
}
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 tweezer_bottom_strategy(Strategy):
"""
Tweezer Bottom strategy.
Enters long on Tweezer Bottom (bearish then bullish with matching lows).
Enters short on Tweezer Top (bullish then bearish with matching highs).
Uses SMA for exit confirmation.
"""
def __init__(self):
super(tweezer_bottom_strategy, self).__init__()
self._tolerance_percent = self.Param("TolerancePercent", 0.1).SetDisplay("Tolerance %", "Max diff between lows/highs", "Pattern")
self._ma_length = self.Param("MaLength", 20).SetDisplay("MA Length", "Period of SMA for exit", "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._prev_candle = None
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(tweezer_bottom_strategy, self).OnReseted()
self._prev_candle = None
self._cooldown = 0
def OnStarted2(self, time):
super(tweezer_bottom_strategy, self).OnStarted2(time)
self._prev_candle = None
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_length.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 _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if self._prev_candle is None:
self._prev_candle = candle
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_candle = candle
return
tol = self._tolerance_percent.Value
low_tolerance = float(self._prev_candle.LowPrice) * (tol / 100.0)
high_tolerance = float(self._prev_candle.HighPrice) * (tol / 100.0)
cd = self._cooldown_bars.Value
sv = float(sma_val)
# Tweezer Bottom: prev bearish, current bullish, matching lows
is_tweezer_bottom = (
self._prev_candle.ClosePrice < self._prev_candle.OpenPrice and
candle.ClosePrice > candle.OpenPrice and
abs(float(self._prev_candle.LowPrice) - float(candle.LowPrice)) <= low_tolerance
)
# Tweezer Top: prev bullish, current bearish, matching highs
is_tweezer_top = (
self._prev_candle.ClosePrice > self._prev_candle.OpenPrice and
candle.ClosePrice < candle.OpenPrice and
abs(float(self._prev_candle.HighPrice) - float(candle.HighPrice)) <= high_tolerance
)
if self.Position == 0 and is_tweezer_bottom:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and is_tweezer_top:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and float(candle.ClosePrice) < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and float(candle.ClosePrice) > sv:
self.BuyMarket()
self._cooldown = cd
self._prev_candle = candle
def CreateClone(self):
return tweezer_bottom_strategy()