Auf GitHub ansehen

Hammer-Kerzen-Umkehr (Hammer Candle Reversal)

Hammer-Kerzen markieren oft eine Intraday-Umkehr, nachdem der Verkaufsdruck nachlässt. Diese Strategie sucht nach dem Hammer-Muster und geht long, in der Erwartung einer Erholung.

Tests zeigen eine durchschnittliche jährliche Rendite von etwa 64 %. Die Strategie eignet sich am besten für den Forex-Markt.

Das System erfordert einen unteren Docht von mindestens der doppelten Kerzenlänge und kaum einen oberen Docht. Nach der Erkennung kauft es mit der definierten Positionsgröße und wartet auf Gewinn oder Stop-Loss.

Details

  • Einstiegskriterien: Hammer-Kerze erkannt.
  • Long/Short: Nur Long.
  • Ausstiegskriterien: Stop-Loss oder diskretionärer Ausstieg.
  • Stops: Ja.
  • Standardwerte:
    • CandleType = TimeSpan.FromMinutes(5)
  • Filter:
    • Kategorie: Muster
    • Richtung: Nur Long
    • Indikatoren: Candlestick
    • Stops: Ja
    • Komplexität: Grundlegend
    • 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>
/// Hammer Candle strategy.
/// Enters long on hammer pattern (long lower shadow, small upper shadow).
/// Enters short on inverted hammer (long upper shadow, small lower shadow).
/// Exits via SMA crossover.
/// </summary>
public class HammerCandleStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private int _cooldown;

	/// <summary>
	/// MA Period for exit.
	/// </summary>
	public int MAPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Candle type and timeframe.
	/// </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>
	/// Constructor.
	/// </summary>
	public HammerCandleStrategy()
	{
		_maPeriod = Param(nameof(MAPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Period for 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();
		_cooldown = default;
	}

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

		_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--;
			return;
		}

		var bodySize = Math.Abs(candle.OpenPrice - candle.ClosePrice);
		var lowerShadow = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
		var upperShadow = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);

		var isHammer = bodySize > 0 && lowerShadow > bodySize * 2m && upperShadow < bodySize * 0.5m;
		var isInvertedHammer = bodySize > 0 && upperShadow > bodySize * 2m && lowerShadow < bodySize * 0.5m;

		if (Position == 0 && isHammer && candle.ClosePrice < smaValue)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
		else if (Position == 0 && isInvertedHammer && candle.ClosePrice > smaValue)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position > 0 && candle.ClosePrice < smaValue)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position < 0 && candle.ClosePrice > smaValue)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
	}
}