Auf GitHub ansehen

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 minute
    • WickRatio = 1.5
    • StopLossPercent = 2 Eine Ablehnungskerze sticht unter das Tief (über das Hoch) der Vorkerze, schließt in die Gegenrichtung und hat einen Docht länger als WickRatio Körper. Der Stop liegt StopLossPercent jenseits des abgelehnten Tiefs oder Hochs, und ein Schluss dahinter schließt die Position.
  • 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 strategy.
/// A bullish rejection probes below the previous candle's low, closes up, and has a lower wick longer than WickRatio bodies;
/// a bearish rejection mirrors it above the previous high. While flat the strategy trades against the wick.
/// The stop lies StopLossPercent beyond the rejected low or high, and a close beyond it closes the position.
/// </summary>
public class RejectionCandleStrategy : Strategy
{
	private readonly StrategyParam<decimal> _wickRatio;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private ICandleMessage _prevCandle;
	private decimal _stopPrice;

	/// <summary>
	/// How many bodies long the rejecting wick must be.
	/// </summary>
	public decimal WickRatio
	{
		get => _wickRatio.Value;
		set => _wickRatio.Value = value;
	}

	/// <summary>
	/// Distance of the stop beyond the rejected extreme, 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 RejectionCandleStrategy()
	{
		_wickRatio = Param(nameof(WickRatio), 1.5m)
			.SetGreaterThanZero()
			.SetDisplay("Wick Ratio", "How many bodies long the rejecting wick must be", "Pattern");

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetNotNegative()
			.SetDisplay("Stop Loss %", "Distance of the stop beyond the rejected extreme, 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();
		_prevCandle = null;
		_stopPrice = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevCandle = null;
		_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;

		var previous = _prevCandle;
		_prevCandle = candle;

		if (previous == null || !IsFormedAndOnlineAndAllowTrading())
			return;

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close <= _stopPrice)
				SellMarket(Position);

			return;
		}

		if (Position < 0)
		{
			if (close >= _stopPrice)
				BuyMarket(-Position);

			return;
		}

		var body = Math.Abs(close - candle.OpenPrice);
		var upperWick = candle.HighPrice - Math.Max(candle.OpenPrice, close);
		var lowerWick = Math.Min(candle.OpenPrice, close) - candle.LowPrice;

		if (candle.LowPrice < previous.LowPrice && close > candle.OpenPrice && lowerWick > body * WickRatio)
		{
			BuyMarket(Volume);
			_stopPrice = candle.LowPrice * (1 - StopLossPercent / 100m);
		}
		else if (candle.HighPrice > previous.HighPrice && close < candle.OpenPrice && upperWick > body * WickRatio)
		{
			SellMarket(Volume);
			_stopPrice = candle.HighPrice * (1 + StopLossPercent / 100m);
		}
	}
}