Los Pinbars destacan rechazos repentinos del precio y pueden señalar puntos de inflexión a corto plazo. Esta estrategia mide la longitud de la mecha de la vela en relación con su cuerpo, buscando sombras largas que sobresalgan de la acción reciente del precio. Un filtro de media móvil ayuda a operar en la dirección de la tendencia subyacente.
Las pruebas indican un rendimiento anual promedio de aproximadamente el 82%. Funciona mejor en el mercado de acciones.
Durante cada actualización de vela, el sistema calcula las sombras superiores e inferiores y las compara con el tamaño del cuerpo. Un Pinbar alcista con una mecha inferior larga puede activar una entrada larga si el precio está por encima de la media móvil. Del mismo modo, un Pinbar bajista con una cola superior extendida puede iniciar una posición corta en una tendencia bajista. Los stops se colocan a un porcentaje fijo desde la entrada.
La operación se cierra cuando aparece un Pinbar opuesto contra la posición abierta o cuando se alcanza el stop protector. Combinar la lógica del Pinbar con un filtro de tendencia mejora la fiabilidad al evitar configuraciones contratendencia.
Detalles
Criterios de entrada: Pinbar con cola larga y sombra opuesta pequeña, confirmado por la tendencia.
Largo/Corto: Ambos.
Criterios de salida: Pinbar opuesto o stop-loss.
Stops: Sí, basado en porcentaje.
Valores predeterminados:
TailToBodyRatio = 2
OppositeTailRatio = 0.5
MAPeriod = 20
CandleType = 15 minute
StopLossPercent = 1
Filtros:
Categoría: Patrón
Dirección: Ambos
Indicadores: Candlestick, MA
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 Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Pinbar Reversal strategy.
/// Enters long on bullish pinbar (long lower wick) below SMA.
/// Enters short on bearish pinbar (long upper wick) above SMA.
/// Exits via SMA crossover.
/// </summary>
public class PinbarReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _tailToBodyRatio;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
/// <summary>
/// Tail to body ratio.
/// </summary>
public decimal TailToBodyRatio
{
get => _tailToBodyRatio.Value;
set => _tailToBodyRatio.Value = value;
}
/// <summary>
/// MA Period.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public PinbarReversalStrategy()
{
_tailToBodyRatio = Param(nameof(TailToBodyRatio), 2.0m)
.SetRange(1.5m, 5.0m)
.SetDisplay("Tail/Body Ratio", "Min tail to body ratio", "Pattern");
_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 lowerWick = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
var upperWick = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);
// Bullish pinbar: long lower wick, small upper wick
var isBullishPinbar = bodySize > 0 && lowerWick > bodySize * TailToBodyRatio && upperWick < bodySize * 0.5m;
// Bearish pinbar: long upper wick, small lower wick
var isBearishPinbar = bodySize > 0 && upperWick > bodySize * TailToBodyRatio && lowerWick < bodySize * 0.5m;
if (Position == 0 && isBullishPinbar && candle.ClosePrice < smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && isBearishPinbar && 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;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class pinbar_reversal_strategy(Strategy):
"""
Pinbar Reversal strategy.
Enters long on bullish pinbar (long lower wick) below SMA.
Enters short on bearish pinbar (long upper wick) above SMA.
Exits via SMA crossover.
"""
def __init__(self):
super(pinbar_reversal_strategy, self).__init__()
self._tail_to_body_ratio = self.Param("TailToBodyRatio", 2.0).SetDisplay("Tail/Body Ratio", "Min tail to body ratio", "Pattern")
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for SMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(pinbar_reversal_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(pinbar_reversal_strategy, self).OnStarted2(time)
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
body_size = abs(float(candle.OpenPrice) - float(candle.ClosePrice))
lower_wick = min(float(candle.OpenPrice), float(candle.ClosePrice)) - float(candle.LowPrice)
upper_wick = float(candle.HighPrice) - max(float(candle.OpenPrice), float(candle.ClosePrice))
ratio = float(self._tail_to_body_ratio.Value)
# Bullish pinbar: long lower wick, small upper wick
is_bullish_pinbar = body_size > 0 and lower_wick > body_size * ratio and upper_wick < body_size * 0.5
# Bearish pinbar: long upper wick, small lower wick
is_bearish_pinbar = body_size > 0 and upper_wick > body_size * ratio and lower_wick < body_size * 0.5
close = float(candle.ClosePrice)
sv = float(sma_val)
cd = self._cooldown_bars.Value
if self.Position == 0 and is_bullish_pinbar and close < sv:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and is_bearish_pinbar and close > sv:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return pinbar_reversal_strategy()