Estrategia Adaptive Renko
Esta estrategia construye una cuadrícula Renko adaptativa donde el tamaño del ladrillo sigue la volatilidad del mercado medida por el indicador Average True Range (ATR). Se ejecuta una operación cada vez que el precio recorre un ladrillo completo en cualquier dirección.
Lógica
- El ATR se calcula sobre un
VolatilityPeriodconfigurable. - El tamaño del ladrillo es igual a
ATR * Multiplierpero no puede ser menor queMinBrickSize. - Cuando el precio sube por encima del ladrillo anterior al menos un tamaño de ladrillo, la estrategia compra (cerrando posiciones cortas si es necesario).
- Cuando el precio cae por debajo del ladrillo anterior al menos un tamaño de ladrillo, la estrategia vende (cerrando posiciones largas si es necesario).
Parámetros
Volume– volumen de la orden.VolatilityPeriod– período utilizado para el ATR.Multiplier– coeficiente aplicado al ATR.MinBrickSize– tamaño mínimo permitido del ladrillo en unidades de precio.CandleType– marco temporal para el cálculo del ATR.
Marco temporal
- Predeterminado: 4 horas.
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>
/// Strategy that trades on adaptive renko movements based on ATR volatility.
/// </summary>
public class AdaptiveRenkoStrategy : Strategy
{
private readonly StrategyParam<int> _volatilityPeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<decimal> _minBrick;
private readonly StrategyParam<DataType> _candleType;
private readonly AverageTrueRange _atr = new();
private decimal _lastBrickPrice;
private bool _hasBrick;
public int VolatilityPeriod
{
get => _volatilityPeriod.Value;
set => _volatilityPeriod.Value = value;
}
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
public decimal MinBrickSize
{
get => _minBrick.Value;
set => _minBrick.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public AdaptiveRenkoStrategy()
{
_volatilityPeriod = Param(nameof(VolatilityPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Volatility Period", "ATR calculation period", "Indicator")
.SetOptimize(5, 20, 1);
_multiplier = Param(nameof(Multiplier), 1m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "ATR multiplier", "Indicator")
.SetOptimize(0.5m, 2m, 0.5m);
_minBrick = Param(nameof(MinBrickSize), 2m)
.SetGreaterThanZero()
.SetDisplay("Min Brick", "Minimum brick size", "Indicator")
.SetOptimize(1m, 5m, 1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for ATR calculation", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atr.Length = VolatilityPeriod;
_atr.Reset();
_lastBrickPrice = 0m;
_hasBrick = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_atr.Length = VolatilityPeriod;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
StartProtection(null, null);
}
private void ProcessCandle(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var brick = Math.Max(atr * Multiplier, MinBrickSize);
if (!_hasBrick)
{
_lastBrickPrice = candle.ClosePrice;
_hasBrick = true;
return;
}
var diff = candle.ClosePrice - _lastBrickPrice;
if (diff >= brick)
{
if (Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
_lastBrickPrice = candle.ClosePrice;
}
else if (diff <= -brick)
{
if (Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_lastBrickPrice = candle.ClosePrice;
}
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class adaptive_renko_strategy(Strategy):
def __init__(self):
super(adaptive_renko_strategy, self).__init__()
self._volatility_period = self.Param("VolatilityPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("Volatility Period", "ATR calculation period", "Indicator") \
.SetOptimize(5, 20, 1)
self._multiplier = self.Param("Multiplier", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Multiplier", "ATR multiplier", "Indicator") \
.SetOptimize(0.5, 2.0, 0.5)
self._min_brick = self.Param("MinBrickSize", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Min Brick", "Minimum brick size", "Indicator") \
.SetOptimize(1.0, 5.0, 1.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for ATR calculation", "General")
self._last_brick_price = 0.0
self._has_brick = False
@property
def volatility_period(self):
return self._volatility_period.Value
@property
def multiplier(self):
return self._multiplier.Value
@property
def min_brick_size(self):
return self._min_brick.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_renko_strategy, self).OnReseted()
self._last_brick_price = 0.0
self._has_brick = False
def OnStarted2(self, time):
super(adaptive_renko_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self.volatility_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(atr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
self.StartProtection(None, None)
def process_candle(self, candle, atr):
if candle.State != CandleStates.Finished:
return
atr_val = float(atr)
brick = max(atr_val * float(self.multiplier), float(self.min_brick_size))
if not self._has_brick:
self._last_brick_price = float(candle.ClosePrice)
self._has_brick = True
return
diff = float(candle.ClosePrice) - self._last_brick_price
if diff >= brick:
if self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._last_brick_price = float(candle.ClosePrice)
elif diff <= -brick:
if self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._last_brick_price = float(candle.ClosePrice)
def CreateClone(self):
return adaptive_renko_strategy()