Rejection Candle Strategie
Eine Rejection Candle bildet sich, wenn der Preis ein Niveau testet, aber nicht darüber hinaus halten kann, und dabei einen langen Docht und einen kleinen Körper hinterlässt. Solche Kerzen zeigen an, dass ein Versuch, sich in eine Richtung zu bewegen, vom Markt entschieden abgelehnt wurde.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 49%. Die Strategie funktioniert am besten am Kryptomarkt.
Die Strategie tritt in die entgegengesetzte Richtung des Dochts ein, sobald die Kerze schließt, und erwartet, dass der Preis durch die Range zurückkehrt.
Stops werden außerhalb des abgelehnten Hochs oder Tiefs gesetzt, um das Risiko zu begrenzen, und Trades werden beendet, wenn der Impuls ausbleibt.
Details
- Einstiegskriterien: Mustererkennung
- Long/Short: Beide
- Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
- Stops: Ja, prozentbasiert
- Standardwerte:
CandleType= 15 minuteStopLoss= 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>
/// Rejection Candle (Pin Bar) strategy.
/// Enters long on bullish rejection (lower low + bullish close + long lower wick).
/// Enters short on bearish rejection (higher high + bearish close + long upper wick).
/// Uses SMA for exit confirmation.
/// Uses cooldown to control trade frequency.
/// </summary>
public class RejectionCandleStrategy : Strategy
{
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<decimal> _wickRatio;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private ICandleMessage _prevCandle;
private int _cooldown;
/// <summary>
/// MA period for exit.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Wick to body ratio threshold.
/// </summary>
public decimal WickRatio
{
get => _wickRatio.Value;
set => _wickRatio.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 RejectionCandleStrategy()
{
_maLength = Param(nameof(MaLength), 20)
.SetRange(10, 50)
.SetDisplay("MA Length", "Period of SMA for exit", "Indicators");
_wickRatio = Param(nameof(WickRatio), 1.5m)
.SetRange(1m, 3m)
.SetDisplay("Wick Ratio", "Min wick to body ratio for rejection", "Pattern");
_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 bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);
if (bodySize == 0) bodySize = 0.01m; // avoid div by zero
var upperWick = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);
var lowerWick = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
// Bullish rejection: made lower low, bullish close, long lower wick
var bullishRejection =
candle.LowPrice < _prevCandle.LowPrice &&
isBullish &&
lowerWick > bodySize * WickRatio;
// Bearish rejection: made higher high, bearish close, long upper wick
var bearishRejection =
candle.HighPrice > _prevCandle.HighPrice &&
isBearish &&
upperWick > bodySize * WickRatio;
if (Position == 0 && bullishRejection)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && bearishRejection)
{
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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class rejection_candle_strategy(Strategy):
"""
Rejection Candle (Pin Bar) strategy.
Enters long on bullish rejection (lower low + bullish close + long lower wick).
Enters short on bearish rejection (higher high + bearish close + long upper wick).
Uses SMA for exit confirmation.
"""
def __init__(self):
super(rejection_candle_strategy, self).__init__()
self._ma_length = self.Param("MaLength", 20).SetDisplay("MA Length", "Period of SMA for exit", "Indicators")
self._wick_ratio = self.Param("WickRatio", 1.5).SetDisplay("Wick Ratio", "Min wick to body ratio for rejection", "Pattern")
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(rejection_candle_strategy, self).OnReseted()
self._prev_candle = None
self._cooldown = 0
def OnStarted2(self, time):
super(rejection_candle_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
cd = self._cooldown_bars.Value
sv = float(sma_val)
wr = self._wick_ratio.Value
body_size = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
if body_size == 0:
body_size = 0.01
upper_wick = float(candle.HighPrice) - max(float(candle.OpenPrice), float(candle.ClosePrice))
lower_wick = min(float(candle.OpenPrice), float(candle.ClosePrice)) - float(candle.LowPrice)
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
# Bullish rejection: made lower low, bullish close, long lower wick
bullish_rejection = (
candle.LowPrice < self._prev_candle.LowPrice and
is_bullish and
lower_wick > body_size * wr
)
# Bearish rejection: made higher high, bearish close, long upper wick
bearish_rejection = (
candle.HighPrice > self._prev_candle.HighPrice and
is_bearish and
upper_wick > body_size * wr
)
if self.Position == 0 and bullish_rejection:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and bearish_rejection:
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 rejection_candle_strategy()