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 Minuten
TolerancePercent = 0.1
StopLossPercent = 2
Die Strategie kauft nur. Der Stop liegt StopLossPercent unter dem tiefsten Tief des Musters, und die Position schließt, wenn eine Kerze darunter schließt.
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.
/// While flat it buys after a bearish candle followed by a bullish one whose low is within TolerancePercent of the first low.
/// The stop lies StopLossPercent below the pattern's lowest low, and a close beyond it closes the position.
/// </summary>
public class TweezerBottomStrategy : Strategy
{
private readonly StrategyParam<decimal> _tolerancePercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _candles = [];
private decimal _stopPrice;
/// <summary>
/// How close the two extremes must be, in percent of the first.
/// </summary>
public decimal TolerancePercent
{
get => _tolerancePercent.Value;
set => _tolerancePercent.Value = value;
}
/// <summary>
/// Distance of the stop beyond the pattern, in percent.
/// </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 TweezerBottomStrategy()
{
_tolerancePercent = Param(nameof(TolerancePercent), 0.1m)
.SetNotNegative()
.SetDisplay("Tolerance %", "How close the two extremes must be, in percent of the first", "Pattern");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Distance of the stop beyond the pattern, in percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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 OnReseted()
{
base.OnReseted();
_candles.Clear();
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_stopPrice = default;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_candles.Add(candle);
if (_candles.Count > 2)
_candles.RemoveAt(0);
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if (candle.ClosePrice <= _stopPrice)
SellMarket(Position);
return;
}
if (Position != 0 || _candles.Count < 2)
return;
var c0 = _candles[0];
var c1 = _candles[1];
if (!(c0.ClosePrice < c0.OpenPrice && c1.ClosePrice > c1.OpenPrice && Math.Abs(c0.LowPrice - c1.LowPrice) <= c0.LowPrice * TolerancePercent / 100m))
return;
BuyMarket(Volume);
_stopPrice = Math.Min(c0.LowPrice, c1.LowPrice) * (1 - StopLossPercent / 100m);
}
}
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, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class tweezer_bottom_strategy(Strategy):
"""
Tweezer Bottom strategy.
While flat it buys after a bearish candle followed by a bullish one whose low is within TolerancePercent of the first low.
The stop lies StopLossPercent below the pattern's lowest low, and a close beyond it closes the position.
"""
def __init__(self):
super(tweezer_bottom_strategy, self).__init__()
self._tolerance_percent = self.Param("TolerancePercent", 0.1).SetNotNegative().SetDisplay("Tolerance %", "How close the two extremes must be, in percent of the first", "Pattern")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Distance of the stop beyond the pattern, in percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._candles = []
self._stop_price = Decimal(0)
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(tweezer_bottom_strategy, self).OnReseted()
self._candles = []
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(tweezer_bottom_strategy, self).OnStarted2(time)
self._candles = []
self._stop_price = Decimal(0)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._candles.append(candle)
if len(self._candles) > 2:
self._candles.pop(0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if candle.ClosePrice <= self._stop_price:
self.SellMarket(self.Position)
return
if self.Position != 0 or len(self._candles) < 2:
return
c0, c1 = self._candles
if not (c0.ClosePrice < c0.OpenPrice and c1.ClosePrice > c1.OpenPrice and Math.Abs(c0.LowPrice - c1.LowPrice) <= c0.LowPrice * Decimal(self._tolerance_percent.Value) / Decimal(100)):
return
self.BuyMarket(self.Volume)
self._stop_price = min(c0.LowPrice, c1.LowPrice) * (Decimal(1) - Decimal(self._stop_loss_percent.Value) / Decimal(100))
def CreateClone(self):
return tweezer_bottom_strategy()