Estrategia de Reversión por Mecha Bajista
La estrategia compra cuando una vela bajista forma una larga mecha inferior que supera un umbral porcentual definido por el usuario. Un filtro EMA opcional requiere que el cierre esté por encima de una media móvil para confirmar la dirección de la tendencia. Las posiciones se cierran cuando el precio cierra por encima del máximo de la vela anterior.
Detalles
- Criterios de entrada: vela bajista con mecha inferior <= umbral y dentro de la ventana de operación; opcionalmente precio por encima de EMA.
- Largo/Corto: Solo largos.
- Criterios de salida: precio de cierre > máximo anterior.
- Stops: Ninguno.
- Valores predeterminados:
- Umbral = -1 (%)
- Filtro EMA desactivado, período EMA = 200
- Hora de inicio = 2014-01-01, Hora de fin = 2099-01-01
- Marco temporal de velas = 1 minuto
- Filtros:
- Categoría: Reversión
- Dirección: Largo
- Indicadores: EMA
- Stops: No
- Complejidad: Bajo
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bearish Wick Reversal strategy.
/// Enter long when a bearish candle shows a large lower wick.
/// Exit when price closes above previous candle high.
/// </summary>
public class BearishWickReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _threshold;
private readonly StrategyParam<bool> _useEmaFilter;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DateTimeOffset> _startTime;
private readonly StrategyParam<DateTimeOffset> _endTime;
private ExponentialMovingAverage _ema;
private decimal _previousHigh;
private decimal _previousLow;
private int _cooldown;
/// <summary>
/// Percentage threshold for lower wick (negative value).
/// </summary>
public decimal Threshold
{
get => _threshold.Value;
set => _threshold.Value = value;
}
/// <summary>
/// Use EMA filter.
/// </summary>
public bool UseEmaFilter
{
get => _useEmaFilter.Value;
set => _useEmaFilter.Value = value;
}
/// <summary>
/// EMA period.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Type of candles.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Trading start time.
/// </summary>
public DateTimeOffset StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// Trading end time.
/// </summary>
public DateTimeOffset EndTime
{
get => _endTime.Value;
set => _endTime.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="BearishWickReversalStrategy"/>.
/// </summary>
public BearishWickReversalStrategy()
{
_threshold = Param(nameof(Threshold), -1.5m)
.SetRange(-5m, 0m)
.SetDisplay("Long Threshold", "Percentage threshold for lower wick", "Strategy Settings")
;
_useEmaFilter = Param(nameof(UseEmaFilter), false)
.SetDisplay("Use EMA Filter", "Enable EMA trend filter", "Trend Filter");
_emaPeriod = Param(nameof(EmaPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Period for EMA trend filter", "Trend Filter")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
_startTime = Param(nameof(StartTime), new DateTimeOffset(new DateTime(2014, 1, 1)))
.SetDisplay("Start Time", "Trading window start", "Time Settings");
_endTime = Param(nameof(EndTime), new DateTimeOffset(new DateTime(2099, 1, 1)))
.SetDisplay("End Time", "Trading window end", "Time Settings");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousHigh = 0m;
_previousLow = 0m;
_cooldown = 0;
_ema = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new EMA { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
var time = candle.OpenTime;
if (time < StartTime || time > EndTime)
{
_previousHigh = candle.HighPrice;
return;
}
if (_cooldown > 0)
_cooldown--;
var longCondition = false;
var shortCondition = false;
// Bearish candle with large lower wick -> potential reversal up
if (candle.ClosePrice < candle.OpenPrice)
{
var percentageSize = 100m * (candle.LowPrice - candle.ClosePrice) / candle.ClosePrice;
longCondition = percentageSize <= Threshold;
}
// Bullish candle with large upper wick -> potential reversal down
if (candle.ClosePrice > candle.OpenPrice)
{
var percentageSize = 100m * (candle.HighPrice - candle.ClosePrice) / candle.ClosePrice;
shortCondition = percentageSize >= -Threshold;
}
if (longCondition && Position <= 0 && _cooldown == 0)
{
BuyMarket();
_cooldown = 60;
}
else if (shortCondition && Position >= 0 && _cooldown == 0)
{
SellMarket();
_cooldown = 60;
}
// Exit long when close above previous high
if (Position > 0 && _previousHigh > 0 && candle.ClosePrice > _previousHigh)
{
SellMarket();
_cooldown = 60;
}
// Exit short when close below previous low
else if (Position < 0 && _previousLow > 0 && candle.ClosePrice < _previousLow)
{
BuyMarket();
_cooldown = 60;
}
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bearish_wick_reversal_strategy(Strategy):
def __init__(self):
super(bearish_wick_reversal_strategy, self).__init__()
self._threshold = self.Param("Threshold", -1.5) \
.SetDisplay("Long Threshold", "Percentage threshold for lower wick", "Strategy Settings")
self._ema_period = self.Param("EmaPeriod", 200) \
.SetGreaterThanZero() \
.SetDisplay("EMA Period", "Period for EMA trend filter", "Trend Filter")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._previous_high = 0.0
self._previous_low = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(bearish_wick_reversal_strategy, self).OnReseted()
self._previous_high = 0.0
self._previous_low = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(bearish_wick_reversal_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self._cooldown > 0:
self._cooldown -= 1
threshold = float(self._threshold.Value)
long_cond = False
short_cond = False
if close < open_p and close != 0:
pct = 100.0 * (low - close) / close
long_cond = pct <= threshold
if close > open_p and close != 0:
pct = 100.0 * (high - close) / close
short_cond = pct >= -threshold
if long_cond and self.Position <= 0 and self._cooldown == 0:
self.BuyMarket()
self._cooldown = 60
elif short_cond and self.Position >= 0 and self._cooldown == 0:
self.SellMarket()
self._cooldown = 60
if self.Position > 0 and self._previous_high > 0 and close > self._previous_high:
self.SellMarket()
self._cooldown = 60
elif self.Position < 0 and self._previous_low > 0 and close < self._previous_low:
self.BuyMarket()
self._cooldown = 60
self._previous_high = high
self._previous_low = low
def CreateClone(self):
return bearish_wick_reversal_strategy()