Drei-Balken-Aufwärtsumkehr-Strategie
Dieses Muster fängt schnelle bullische Wenden nach einem kurzen Rückgang. Es erfordert zwei aufeinanderfolgende Abwärtskerzen, gefolgt von einer starken Aufwärtskerze, die über dem Hoch des vorherigen Balkens schließt. Die Logik prüft optional, ob der Preis zuvor nach unten tendierte.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 85%. Sie funktioniert am besten auf dem Kryptomarkt.
Die Strategie hält die letzten drei Kerzen im Speicher. Sobald die Sequenz den Kriterien entspricht und ein Abwärtstrend-Filter erfüllt ist, wird eine Long-Position eröffnet. Ein Volatilitätsstopp unterhalb des Mustertiefs begrenzt das Risiko des Trades.
Nach dem Einstieg wartet das System entweder auf einen Stop-Treffer oder das Erscheinen eines anderen Setups in die entgegengesetzte Richtung. Dieser einfache Ansatz eignet sich für Märkte, die zu starken Erholungen aus überverkauften Bedingungen neigen.
Details
- Einstiegskriterien: Zwei bärische Kerzen mit tieferen Tiefs, dann eine bullische Kerze, die über dem Hoch des mittleren Balkens schließt.
- Long/Short: Nur Long.
- Ausstiegskriterien: Stop-Loss oder nächstes Muster.
- Stops: Ja, unterhalb des Mustertiefs.
- Standardwerte:
CandleType= 15 minuteStopLossPercent= 1RequireDowntrend= trueDowntrendLength= 5
- Filter:
- Kategorie: Muster
- Richtung: Long
- 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 System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Long-only three-bar reversal after an optional net downtrend.
/// A pattern-low stop and an opposite three-bar reversal close the full position.
/// </summary>
public class ThreeBarReversalUpStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<bool> _requireDowntrend;
private readonly StrategyParam<int> _downtrendLength;
private readonly List<ICandleMessage> _recent = new();
private decimal? _patternStop;
private Order _entryOrder;
private Order _exitOrder;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public bool RequireDowntrend { get => _requireDowntrend.Value; set => _requireDowntrend.Value = value; }
public int DowntrendLength { get => _downtrendLength.Value; set => _downtrendLength.Value = value; }
public ThreeBarReversalUpStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Three-bar pattern timeframe", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 1m).SetRange(0m, 99m)
.SetDisplay("Stop below pattern low (%)", "Buffer below the lowest of all three pattern lows; zero places the stop at that low.", "Protection");
_requireDowntrend = Param(nameof(RequireDowntrend), true)
.SetDisplay("Require Downtrend", "Require a net Close decline before the signal candle.", "Pattern");
_downtrendLength = Param(nameof(DowntrendLength), 5).SetRange(2, 100)
.SetDisplay("Downtrend Length", "Number of preceding candles from first Close to last Close, including the two bearish pattern bars.", "Pattern");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
protected override void OnReseted()
{
base.OnReseted();
ClearState();
}
private void ClearState()
{
_recent.Clear();
_patternStop = null;
_entryOrder = _exitOrder = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ClearState();
var bids = new Subscription(DataType.Level1, Security);
bids.MarketData.BuildField = Level1Fields.BestBidPrice;
SubscribeLevel1(bids).Bind(ProcessBid).Start();
var candles = SubscribeCandles(CandleType);
candles.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, candles);
DrawOwnTrades(area);
}
}
private static bool IsPending(Order order)
=> order is not null && order.State is not (OrderStates.Done or OrderStates.Failed);
private void ProcessBid(Level1ChangeMessage message)
{
if (message.TryGetDecimal(Level1Fields.BestBidPrice) is decimal bid && bid > 0m)
CheckStop(bid);
}
private bool CheckStop(decimal executableBid)
{
if (Position <= 0m || _patternStop is not decimal stop || executableBid > stop || IsPending(_exitOrder))
return false;
_exitOrder = SellMarket(Position);
_recent.Clear();
_patternStop = null;
return true;
}
private void Append(ICandleMessage candle)
{
_recent.Add(candle);
if (_recent.Count > DowntrendLength)
_recent.RemoveAt(0);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (Position > 0m && CheckStop(candle.LowPrice))
return;
if (IsPending(_entryOrder) || IsPending(_exitOrder))
{
Append(candle);
return;
}
var previous = _recent.Count >= 1 ? _recent[^1] : null;
var older = _recent.Count >= 2 ? _recent[^2] : null;
var upPattern = older is not null && previous.ClosePrice < previous.OpenPrice &&
older.ClosePrice < older.OpenPrice && previous.LowPrice < older.LowPrice &&
candle.ClosePrice > candle.OpenPrice && candle.ClosePrice > previous.HighPrice;
var downPattern = older is not null && previous.ClosePrice > previous.OpenPrice &&
older.ClosePrice > older.OpenPrice && previous.HighPrice > older.HighPrice &&
candle.ClosePrice < candle.OpenPrice && candle.ClosePrice < previous.LowPrice;
if (Position > 0m && downPattern && IsFormedAndOnlineAndAllowTrading())
{
_exitOrder = SellMarket(Position);
_recent.Clear();
_patternStop = null;
}
else if (Position == 0m && upPattern &&
(!RequireDowntrend || _recent.Count >= DowntrendLength &&
_recent[^1].ClosePrice < _recent[^DowntrendLength].ClosePrice) &&
IsFormedAndOnlineAndAllowTrading())
{
_patternStop = Math.Min(Math.Min(older.LowPrice, previous.LowPrice), candle.LowPrice) *
(1m - StopLossPercent / 100m);
_entryOrder = BuyMarket(Volume);
}
Append(candle);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal, Math
from StockSharp.Messages import DataType, CandleStates, Level1Fields, OrderStates
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class three_bar_reversal_up_strategy(Strategy):
"""
Long-only three-bar reversal after an optional net downtrend.
A pattern-low stop and an opposite three-bar reversal close the full position.
"""
def __init__(self):
super(three_bar_reversal_up_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Three-bar pattern timeframe", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetRange(0.0, 99.0).SetDisplay("Stop below pattern low (%)", "Buffer below the lowest of all three pattern lows; zero places the stop at that low.", "Protection")
self._require_downtrend = self.Param("RequireDowntrend", True).SetDisplay("Require Downtrend", "Require a net Close decline before the signal candle.", "Pattern")
self._downtrend_length = self.Param("DowntrendLength", 5).SetRange(2, 100).SetDisplay("Downtrend Length", "Number of preceding candles from first Close to last Close, including the two bearish pattern bars.", "Pattern")
self._clear_state()
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, DataType.Level1)]
def _clear_state(self):
self._recent = []
self._pattern_stop = None
self._entry_order = None
self._exit_order = None
def OnReseted(self):
super(three_bar_reversal_up_strategy, self).OnReseted()
self._clear_state()
def OnStarted2(self, time):
super(three_bar_reversal_up_strategy, self).OnStarted2(time)
self._clear_state()
bids = Subscription(DataType.Level1, self.Security)
bids.MarketData.BuildField = Level1Fields.BestBidPrice
self.SubscribeLevel1(bids).Bind(self._process_bid).Start()
candles = self.SubscribeCandles(self.candle_type)
candles.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, candles)
self.DrawOwnTrades(area)
def _is_pending(self, order):
return order is not None and order.State not in (OrderStates.Done, OrderStates.Failed)
def _process_bid(self, message):
if message.Changes.ContainsKey(Level1Fields.BestBidPrice):
bid = message.Changes[Level1Fields.BestBidPrice]
if bid is not None and bid > Decimal(0):
self._check_stop(bid)
def _check_stop(self, executable_bid):
if (self.Position <= 0 or self._pattern_stop is None or
executable_bid > self._pattern_stop or self._is_pending(self._exit_order)):
return False
self._exit_order = self.SellMarket(self.Position)
self._recent = []
self._pattern_stop = None
return True
def _append(self, candle):
self._recent.append(candle)
if len(self._recent) > self._downtrend_length.Value:
self._recent.pop(0)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self.Position > 0 and self._check_stop(candle.LowPrice):
return
if self._is_pending(self._entry_order) or self._is_pending(self._exit_order):
self._append(candle)
return
previous = self._recent[-1] if len(self._recent) >= 1 else None
older = self._recent[-2] if len(self._recent) >= 2 else None
up_pattern = (older is not None and previous.ClosePrice < previous.OpenPrice and
older.ClosePrice < older.OpenPrice and previous.LowPrice < older.LowPrice and
candle.ClosePrice > candle.OpenPrice and candle.ClosePrice > previous.HighPrice)
down_pattern = (older is not None and previous.ClosePrice > previous.OpenPrice and
older.ClosePrice > older.OpenPrice and previous.HighPrice > older.HighPrice and
candle.ClosePrice < candle.OpenPrice and candle.ClosePrice < previous.LowPrice)
if self.Position > 0 and down_pattern and self.IsFormedAndOnlineAndAllowTrading():
self._exit_order = self.SellMarket(self.Position)
self._recent = []
self._pattern_stop = None
elif (self.Position == 0 and up_pattern and
(not self._require_downtrend.Value or
len(self._recent) >= self._downtrend_length.Value and
self._recent[-1].ClosePrice < self._recent[-self._downtrend_length.Value].ClosePrice) and
self.IsFormedAndOnlineAndAllowTrading()):
pattern_low = Math.Min(Math.Min(older.LowPrice, previous.LowPrice), candle.LowPrice)
self._pattern_stop = pattern_low * (Decimal(1) - Decimal(self._stop_loss_percent.Value) / Decimal(100))
self._entry_order = self.BuyMarket(self.Volume)
self._append(candle)
def CreateClone(self):
return three_bar_reversal_up_strategy()