Wyckoff Distribution ist eine Toppingphase, die durch starken Verkauf in Rallyes und Tests des Widerstands gekennzeichnet ist.
Das Volumen expandiert oft bei Abwärtsbewegungen und zieht sich bei Erholungen zurück, was darauf hindeutet, dass große Akteure Positionen liquidieren.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 64%. Die Strategie funktioniert am besten am Forexmarkt.
Diese Strategie geht short, wenn der Preis aus der Distributionsrange nach unten ausbricht, in Erwartung eines anhaltenden Rückgangs.
Ein Stop knapp über der Range schützt vor falschen Ausbrüchen, und Positionen werden geschlossen, wenn der Preis zur Oberseite der Struktur zurückkehrt.
Details
Einstiegskriterien: Indikatorsignal
Long/Short: Beide
Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
Stops: Ja, prozentbasiert
Standardwerte:
CandleType = 15 minute
RangePeriod = 20
StopLossPercent = 2
Die Distributionsspanne ist das höchste Hoch und tiefste Tief der vorherigen RangePeriod Kerzen. Die Strategie verkauft nur, bei einem Schluss unter der Spanne; der Stop liegt StopLossPercent über der Oberseite der Struktur, und die Position schließt, wenn eine Kerze darüber schließt.
Filter:
Kategorie: Trendfolge
Richtung: Beide
Indikatoren: Volume, Price
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.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Wyckoff Distribution strategy.
/// The distribution range is the highest high and lowest low of the previous RangePeriod candles. A close below the range sells while flat.
/// The stop lies StopLossPercent above the top of the range, and a close above it closes the position.
/// </summary>
public class WyckoffDistributionStrategy : Strategy
{
private readonly StrategyParam<int> _rangePeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<(decimal High, decimal Low)> _candles = [];
private decimal _stopPrice;
/// <summary>
/// Number of previous candles that form the level.
/// </summary>
public int RangePeriod
{
get => _rangePeriod.Value;
set => _rangePeriod.Value = value;
}
/// <summary>
/// Distance of the stop beyond the level, 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 WyckoffDistributionStrategy()
{
_rangePeriod = Param(nameof(RangePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("RangePeriod", "Number of previous candles that form the level", "Pattern");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Distance of the stop beyond the level, 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;
// The level is formed by the candles before this one.
var ready = _candles.Count == RangePeriod;
var high = ready ? _candles.Max(c => c.High) : 0m;
var low = ready ? _candles.Min(c => c.Low) : 0m;
_candles.Add((candle.HighPrice, candle.LowPrice));
if (_candles.Count > RangePeriod)
_candles.RemoveAt(0);
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position < 0)
{
if (close >= _stopPrice)
BuyMarket(-Position);
return;
}
if (Position != 0 || !ready || !(close < low))
return;
SellMarket(Volume);
_stopPrice = high * (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, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class wyckoff_distribution_strategy(Strategy):
"""
Wyckoff Distribution strategy.
The distribution range is the highest high and lowest low of the previous RangePeriod candles. A close below the range sells while flat.
The stop lies StopLossPercent above the top of the range, and a close above it closes the position.
"""
def __init__(self):
super(wyckoff_distribution_strategy, self).__init__()
self._range_period = self.Param("RangePeriod", 20).SetGreaterThanZero().SetDisplay("RangePeriod", "Number of previous candles that form the level", "Pattern")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Distance of the stop beyond the level, 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(wyckoff_distribution_strategy, self).OnReseted()
self._candles = []
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(wyckoff_distribution_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
# The level is formed by the candles before this one.
period = self._range_period.Value
ready = len(self._candles) == period
high = max(c[0] for c in self._candles) if ready else Decimal(0)
low = min(c[1] for c in self._candles) if ready else Decimal(0)
self._candles.append((candle.HighPrice, candle.LowPrice))
if len(self._candles) > period:
self._candles.pop(0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
if self.Position < 0:
if close >= self._stop_price:
self.BuyMarket(-self.Position)
return
if self.Position != 0 or not ready or not (close < low):
return
percent = Decimal(self._stop_loss_percent.Value) / Decimal(100)
self.SellMarket(self.Volume)
self._stop_price = high * (Decimal(1) + percent)
def CreateClone(self):
return wyckoff_distribution_strategy()