La Distribución Wyckoff es una fase de techo caracterizada por ventas pesadas en los rallies y pruebas de resistencia.
El volumen a menudo se expande en los movimientos bajistas y se contrae en los rebotes, sugiriendo que los grandes intereses están liquidando posiciones.
Las pruebas indican un rendimiento anual promedio de aproximadamente 64%. Funciona mejor en el mercado de divisas.
Esta estrategia vende en corto cuando el precio rompe hacia abajo desde el rango de distribución, anticipando una caída sostenida.
Un stop justo por encima del rango protege contra falsos rompimientos, y las posiciones se cierran si el precio regresa a la parte superior de la estructura.
Detalles
Criterios de entrada: señal de indicador
Largo/Corto: Ambos
Criterios de salida: stop-loss o señal opuesta
Stops: Sí, basados en porcentaje
Valores predeterminados:
CandleType = 15 minute
RangePeriod = 20
StopLossPercent = 2
El rango de distribución es el máximo más alto y el mínimo más bajo de las RangePeriod velas anteriores. La estrategia solo vende, con un cierre por debajo del rango; el stop se coloca StopLossPercent por encima del techo de la estructura y la posición se cierra cuando una vela cierra por encima.
Filtros:
Categoría: Seguimiento de tendencia
Dirección: Ambos
Indicadores: Volume, Price
Stops: Sí
Complejidad: Intermedio
Marco temporal: Intradía
Estacionalidad: No
Redes neuronales: No
Divergencia: No
Nivel de riesgo: Medio
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()