Estrategia de Reversión Pin Bar
Utiliza velas Pin Bar con un filtro de tendencia y stops y objetivos basados en ATR. Un Pin Bar alcista por encima de la SMA abre una posición larga, mientras que uno bajista por debajo de ella abre una posición corta. Las entradas se omiten cuando la volatilidad es demasiado baja.
Detalles
- Criterios de entrada: Pin Bar en dirección de la tendencia con mecha larga, cuerpo pequeño y ATR por encima de
MinAtr. - Largo/Corto: Ambos.
- Criterios de salida: Stop-loss o take-profit basado en ATR.
- Stops: Sí, múltiplos de ATR.
- Valores predeterminados:
TrendLength= 50MaxBodyPct= 0.30MinWickPct= 0.66AtrLength= 14StopMultiplier= 1TakeMultiplier= 1.5MinAtr= 0.0015CandleType= 1 hour
- Filtros:
- Categoría: Patrón
- Dirección: Ambos
- Indicadores: SMA, ATR
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- 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>
/// Pin bar reversal strategy with ATR based stops and targets.
/// </summary>
public class PinBarReversalStrategy : Strategy
{
private readonly StrategyParam<int> _trendLength;
private readonly StrategyParam<decimal> _maxBodyPct;
private readonly StrategyParam<decimal> _minWickPct;
private readonly StrategyParam<int> __atrLength;
private readonly StrategyParam<decimal> __stopMultiplier;
private readonly StrategyParam<decimal> __takeMultiplier;
private readonly StrategyParam<decimal> __minAtr;
private readonly StrategyParam<DataType> __candleType;
/// <summary>
/// Period for trend SMA.
/// </summary>
public int TrendLength
{
get => _trendLength.Value;
set => _trendLength.Value = value;
}
/// <summary>
/// Maximum body percent of candle range.
/// </summary>
public decimal MaxBodyPct
{
get => _maxBodyPct.Value;
set => _maxBodyPct.Value = value;
}
/// <summary>
/// Minimum wick percent of candle range.
/// </summary>
public decimal MinWickPct
{
get => _minWickPct.Value;
set => _minWickPct.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrLength
{
get => __atrLength.Value;
set => __atrLength.Value = value;
}
/// <summary>
/// Stop loss ATR multiplier.
/// </summary>
public decimal StopMultiplier
{
get => __stopMultiplier.Value;
set => __stopMultiplier.Value = value;
}
/// <summary>
/// Take profit ATR multiplier.
/// </summary>
public decimal TakeMultiplier
{
get => __takeMultiplier.Value;
set => __takeMultiplier.Value = value;
}
/// <summary>
/// Minimum ATR value to allow entry.
/// </summary>
public decimal MinAtr
{
get => __minAtr.Value;
set => __minAtr.Value = value;
}
/// <summary>
/// Working candle type.
/// </summary>
public DataType CandleType
{
get => __candleType.Value;
set => __candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="PinBarReversalStrategy"/>.
/// </summary>
public PinBarReversalStrategy()
{
_trendLength = Param(nameof(TrendLength), 50)
.SetGreaterThanZero()
.SetDisplay("Trend SMA Length", "Period for trend SMA", "General")
;
_maxBodyPct = Param(nameof(MaxBodyPct), 0.30m)
.SetRange(0.1m, 0.5m)
.SetDisplay("Max Body %", "Maximum body as % of range", "Pattern")
;
_minWickPct = Param(nameof(MinWickPct), 0.66m)
.SetRange(0.5m, 0.9m)
.SetDisplay("Min Wick %", "Minimum wick as % of range", "Pattern")
;
__atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Risk")
;
__stopMultiplier = Param(nameof(StopMultiplier), 1m)
.SetGreaterThanZero()
.SetDisplay("ATR Stop Mult", "Stop loss ATR multiplier", "Risk");
__takeMultiplier = Param(nameof(TakeMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Take Mult", "Take profit ATR multiplier", "Risk");
__minAtr = Param(nameof(MinAtr), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Min ATR", "Minimum ATR to allow entry", "Risk");
__candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
// no additional state to reset
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = 14 };
var slow = new SimpleMovingAverage { Length = TrendLength };
var prevF = 0m;
var prevS = 0m;
var init = false;
var lastSignal = DateTimeOffset.MinValue;
var cooldown = TimeSpan.FromMinutes(600);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, (candle, f, s) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!fast.IsFormed || !slow.IsFormed)
return;
if (!init)
{
prevF = f;
prevS = s;
init = true;
return;
}
if (candle.OpenTime - lastSignal >= cooldown)
{
if (prevF <= prevS && f > s && Position <= 0)
{
BuyMarket();
lastSignal = candle.OpenTime;
}
else if (prevF >= prevS && f < s && Position >= 0)
{
SellMarket();
lastSignal = candle.OpenTime;
}
}
prevF = f;
prevS = s;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
}
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, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class pin_bar_reversal_strategy(Strategy):
def __init__(self):
super(pin_bar_reversal_strategy, self).__init__()
self._trend_length = self.Param("TrendLength", 50) \
.SetGreaterThanZero()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._prev_fast = 0.0
self._prev_slow = 0.0
self._initialized = False
self._last_signal_ticks = 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(pin_bar_reversal_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._initialized = False
self._last_signal_ticks = 0
def OnStarted2(self, time):
super(pin_bar_reversal_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._initialized = False
self._last_signal_ticks = 0
self._fast = ExponentialMovingAverage()
self._fast.Length = 14
self._slow = SimpleMovingAverage()
self._slow.Length = self._trend_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast, self._slow, self.OnProcess).Start()
def OnProcess(self, candle, f, s):
if candle.State != CandleStates.Finished:
return
if not self._fast.IsFormed or not self._slow.IsFormed:
return
fv = float(f)
sv = float(s)
if not self._initialized:
self._prev_fast = fv
self._prev_slow = sv
self._initialized = True
return
cooldown_ticks = TimeSpan.FromMinutes(600).Ticks
current_ticks = candle.OpenTime.Ticks
if current_ticks - self._last_signal_ticks >= cooldown_ticks:
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
self.BuyMarket()
self._last_signal_ticks = current_ticks
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
self.SellMarket()
self._last_signal_ticks = current_ticks
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return pin_bar_reversal_strategy()