Esta estrategia implementa un enfoque de stop trailing basado en ATR inspirado en el Asesor Experto original "Batman".
Rastrea niveles dinámicos de soporte y resistencia derivados del indicador Average True Range (ATR) y reacciona cuando el precio cruza estos niveles.
Lógica
Calcular el ATR con el período configurable.
Determinar soporte y resistencia:
support = price - ATR * factor
resistance = price + ATR * factor
Mantener el soporte o resistencia más cercano dependiendo de la tendencia actual.
Cuando el precio supera la resistencia, abrir una posición larga.
Cuando el precio cae por debajo del soporte, abrir una posición corta.
El precio puede ser el precio de cierre o el precio típico (high + low + close) / 3.
Parámetros
Nombre
Descripción
ATR Period
Período del indicador ATR.
ATR Factor
Multiplicador aplicado al valor ATR para construir las líneas de stop.
Use Typical Price
Si está habilitado, usa (High + Low + Close)/3 en lugar del precio de cierre.
Candle Type
Tipo de velas usadas para los cálculos.
Notas
La estrategia usa la API de alto nivel con SubscribeCandles y Bind.
StartProtection() se llama al inicio para garantizar la seguridad de la posición.
El trading se realiza solo en velas completadas.
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>
/// Strategy based on ATR trailing stop similar to "Batman" EA.
/// Opens long when price breaks above ATR-based support.
/// Opens short when price breaks below ATR-based resistance.
/// </summary>
public class BatmanAtrTrailingStopStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _factor;
private readonly StrategyParam<DataType> _candleType;
private decimal _levelUp;
private decimal _levelDown;
private int _direction;
private bool _isInitialized;
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal Factor { get => _factor.Value; set => _factor.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public BatmanAtrTrailingStopStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR indicator period", "General");
_factor = Param(nameof(Factor), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Factor", "Multiplier for ATR distance", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_levelUp = 0;
_levelDown = 0;
_direction = 1;
_isInitialized = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stdev = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType).Bind(stdev, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal stdevValue)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var currUp = close - stdevValue * Factor;
var currDown = close + stdevValue * Factor;
if (!_isInitialized)
{
_levelUp = currUp;
_levelDown = currDown;
_isInitialized = true;
return;
}
if (_direction == 1)
{
if (currUp > _levelUp)
_levelUp = currUp;
if (candle.LowPrice < _levelUp)
{
_direction = -1;
_levelDown = currDown;
if (Position > 0) SellMarket();
SellMarket();
}
}
else
{
if (currDown < _levelDown)
_levelDown = currDown;
if (candle.HighPrice > _levelDown)
{
_direction = 1;
_levelUp = currUp;
if (Position < 0) BuyMarket();
BuyMarket();
}
}
}
}
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 StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class batman_atr_trailing_stop_strategy(Strategy):
def __init__(self):
super(batman_atr_trailing_stop_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR indicator period", "General")
self._factor = self.Param("Factor", 1.5) \
.SetDisplay("ATR Factor", "Multiplier for ATR distance", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._level_up = 0.0
self._level_down = 0.0
self._direction = 1
self._is_initialized = False
@property
def atr_period(self):
return self._atr_period.Value
@property
def factor(self):
return self._factor.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(batman_atr_trailing_stop_strategy, self).OnReseted()
self._level_up = 0.0
self._level_down = 0.0
self._direction = 1
self._is_initialized = False
def OnStarted2(self, time):
super(batman_atr_trailing_stop_strategy, self).OnStarted2(time)
stdev = StandardDeviation()
stdev.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(stdev, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, stdev_value):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
curr_up = close - stdev_value * self.factor
curr_down = close + stdev_value * self.factor
if not self._is_initialized:
self._level_up = curr_up
self._level_down = curr_down
self._is_initialized = True
return
if self._direction == 1:
if curr_up > self._level_up:
self._level_up = curr_up
if candle.LowPrice < self._level_up:
self._direction = -1
self._level_down = curr_down
if self.Position > 0:
self.SellMarket()
self.SellMarket()
else:
if curr_down < self._level_down:
self._level_down = curr_down
if candle.HighPrice > self._level_down:
self._direction = 1
self._level_up = curr_up
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
def CreateClone(self):
return batman_atr_trailing_stop_strategy()