Estratégia de Nível Renko
Visão geral
A Estratégia de Nível Renko é uma conversão fiel do consultor especializado MetaTrader 5 "Renko Level EA". Ela reconstrói a lógica orientada por indicadores dentro do StockSharp e negocia sempre que o nível Renko arredondado salta para um novo bloco. A estratégia interpreta cada mudança de nível como um rompimento do tijolo Renko sintético e entra na direção do rompimento ou na direção oposta quando o modo reverso está habilitado.
O sistema usa velas regulares baseadas em tempo (1 minuto por padrão) apenas como fonte de dados. Os fechamentos de velas são arredondados para um tamanho de bloco configurável que emula tijolos Renko sem a necessidade de subscrições de dados Renko. Cada vez que o bloco arredondado muda, a estratégia fecha qualquer exposição oposta e abre uma nova posição alinhada com o movimento detectado.
Lógica de trading
- Inicialização
- Detectar o tamanho do pip do instrumento (
PriceStep).
- Converter o parâmetro
Block Size de pips para unidades de preço (instrumentos de 3 e 5 dígitos multiplicam automaticamente o valor do pip por 10).
- Arredondar o fechamento da primeira vela finalizada para o bloco mais próximo para criar os níveis Renko superiores e inferiores iniciais.
- Manutenção de níveis
- Em cada vela finalizada o preço de fechamento é arredondado para o tamanho de bloco mais próximo.
- Quando o fechamento permanece dentro do bloco atual, os níveis armazenados permanecem inalterados.
- Quando o fechamento rompe abaixo do limite inferior, o algoritmo arredonda o preço para baixo e desloca o bloco para baixo (
lower = round, upper = round + size).
- Quando o fechamento rompe acima do limite superior, o bloco é deslocado para cima (
upper = round, lower = round - size).
- Geração de sinais
- Um nível superior crescente indica um rompimento altista do bloco Renko. Um nível superior decrescente indica um rompimento baixista.
- Se
Reverse estiver desabilitado, a estratégia compra em mudanças altistas e vende em mudanças baixistas. Quando Reverse está habilitado, as ações são trocadas.
- Quando um sinal é acionado, a exposição existente na direção oposta é eliminada automaticamente (ordem de compra fecha vendidas, ordem de venda fecha compradas). Se
Allow Increase estiver desabilitado, a estratégia recusa adicionar tamanho sobre uma posição já aberta na mesma direção.
- Execução de ordens
- As ordens são enviadas com a configuração
Volume da estratégia. Ao reverter uma posição existente, o tamanho da ordem é igual à posição absoluta mais o volume configurado para que a inversão ocorra imediatamente.
StartProtection() é chamado durante a inicialização para que as proteções de risco configuradas no Designer ou via composição estejam ativas.
Parâmetros
| Parâmetro |
Descrição |
Padrão |
Block Size |
Tamanho do bloco Renko em pips. A estratégia o multiplica pelo valor do pip do instrumento para obter o incremento de preço real. Valores maiores reduzem a frequência de trading. |
30 |
Reverse |
Quando true, inverte todos os sinais de trading (comprar em mudança baixista, vender em mudança altista). |
false |
Allow Increase |
Quando true, permite a piramidação adicionando ordens adicionais na mesma direção em cada sinal. Quando false, uma nova ordem só é enviada se a posição líquida for plana após fechar o lado oposto. |
false |
Candle Type |
Dados de velas fonte. Qualquer DataType suportado pode ser usado; por padrão a estratégia subscreve velas de 1 minuto. |
TimeFrame(1m) |
Volume (herdado) |
Tamanho da ordem ao enviar ordens a mercado. Defina esta propriedade na instância da estratégia antes de iniciá-la. |
Depende do portfólio |
Notas de uso
- Escolha o tamanho do bloco de acordo com a volatilidade do instrumento. Para os principais pares de moedas, 30–50 pips emulam o comportamento do EA original. Em índices ou ativos cripto use tamanhos de bloco maiores.
- A estratégia funciona com qualquer fonte de velas (tick, período, range) desde que o fechamento da vela reflita a amostragem de preço desejada. Para uma fonte Renko pura, pode-se mudar o tipo de vela para uma série de dados Renko.
- Habilite
Reverse para transformar o sistema de rompimento em um sistema de reversão à média que desvanece cada mudança de nível Renko.
Allow Increase pode ser ativado para imitar o parâmetro "Increase" do EA original que adiciona contratos em cada novo nível na mesma direção.
- O risco e a gestão monetária (stop-loss, take-profit, controle de drawdown) podem ser configurados através de proteções do StockSharp ou estratégias envelope. O exemplo mantém a lógica idêntica ao especialista MT5 e não impõe saídas fixas além das mudanças de nível.
Requisitos de dados
- Dados de velas históricos e em tempo real para o
Candle Type configurado.
- Os metadados do instrumento devem fornecer
PriceStep e Decimals para que a conversão de pip funcione corretamente. Quando esses valores não estão disponíveis, a estratégia recorre a um passo padrão de 0.0001.
Fluxo de trabalho sugerido
- Adicione a estratégia ao Designer ou crie-a programaticamente através da API do StockSharp.
- Defina
Security, Portfolio, Volume e, opcionalmente, ajuste os parâmetros listados acima.
- Inicie a estratégia. Ela aguardará a primeira vela finalizada para estabelecer o bloco Renko inicial.
- Monitore o gráfico de trades integrado ou subscreva aos logs para verificar que as ordens são acionadas apenas quando o nível arredondado muda.
Esta documentação reflete o comportamento do EA Renko Level original enquanto explica como está implementado dentro do StockSharp para que você possa personalizá-lo ou estendê-lo.
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>
/// Renko level breakout strategy. Emulates Renko bricks on time-based candles
/// and trades when the rounded level shifts up or down.
/// </summary>
public class RenkoLevelStrategy : Strategy
{
private readonly StrategyParam<int> _blockSize;
private readonly StrategyParam<DataType> _candleType;
private decimal _upperLevel;
private decimal _lowerLevel;
private bool _hasLevels;
public int BlockSize
{
get => _blockSize.Value;
set => _blockSize.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public RenkoLevelStrategy()
{
_blockSize = Param(nameof(BlockSize), 5000)
.SetGreaterThanZero()
.SetDisplay("Block Size", "Renko block size in price steps", "Renko");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_upperLevel = 0m;
_lowerLevel = 0m;
_hasLevels = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var brickSize = GetBrickSize();
if (brickSize <= 0m)
return;
var close = candle.ClosePrice;
if (!_hasLevels)
{
InitializeLevels(close, brickSize);
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
var previousUpper = _upperLevel;
var moved = false;
if (close < _lowerLevel)
{
var (round, _, ceil) = CalculateLevels(close, brickSize);
if (Math.Abs(round - _lowerLevel) > brickSize * 0.01m)
{
_lowerLevel = round;
_upperLevel = ceil;
moved = true;
}
}
else if (close > _upperLevel)
{
var (round, floor, _) = CalculateLevels(close, brickSize);
if (Math.Abs(round - _upperLevel) > brickSize * 0.01m)
{
_lowerLevel = floor;
_upperLevel = round;
moved = true;
}
}
if (!moved)
return;
if (_upperLevel > previousUpper && Position <= 0)
BuyMarket();
else if (_upperLevel < previousUpper && Position >= 0)
SellMarket();
}
private void InitializeLevels(decimal price, decimal brickSize)
{
var (round, floor, _) = CalculateLevels(price, brickSize);
_upperLevel = round;
_lowerLevel = floor;
_hasLevels = true;
}
private (decimal round, decimal floor, decimal ceil) CalculateLevels(decimal price, decimal brickSize)
{
var ratio = price / brickSize;
var rounded = Math.Round(ratio, 0, MidpointRounding.AwayFromZero);
var priceRound = rounded * brickSize;
var priceFloor = priceRound - brickSize;
var priceCeil = priceRound + brickSize;
return (priceRound, priceFloor, priceCeil);
}
private decimal GetBrickSize()
{
var step = Security?.PriceStep ?? 0.01m;
return step * BlockSize;
}
}
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.Strategies import Strategy
class renko_level_strategy(Strategy):
def __init__(self):
super(renko_level_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._block_size = self.Param("BlockSize", 5000) \
.SetDisplay("Block Size", "Renko block size in price steps", "Renko")
self._upper_level = 0.0
self._lower_level = 0.0
self._has_levels = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def BlockSize(self):
return self._block_size.Value
def OnReseted(self):
super(renko_level_strategy, self).OnReseted()
self._upper_level = 0.0
self._lower_level = 0.0
self._has_levels = False
def OnStarted2(self, time):
super(renko_level_strategy, self).OnStarted2(time)
self._upper_level = 0.0
self._lower_level = 0.0
self._has_levels = False
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
brick_size = self._get_brick_size()
if brick_size <= 0:
return
close = float(candle.ClosePrice)
if not self._has_levels:
self._initialize_levels(close, brick_size)
return
previous_upper = self._upper_level
moved = False
if close < self._lower_level:
rnd, _, ceil = self._calculate_levels(close, brick_size)
if abs(rnd - self._lower_level) > brick_size * 0.01:
self._lower_level = rnd
self._upper_level = ceil
moved = True
elif close > self._upper_level:
rnd, floor, _ = self._calculate_levels(close, brick_size)
if abs(rnd - self._upper_level) > brick_size * 0.01:
self._lower_level = floor
self._upper_level = rnd
moved = True
if not moved:
return
if self._upper_level > previous_upper and self.Position <= 0:
self.BuyMarket()
elif self._upper_level < previous_upper and self.Position >= 0:
self.SellMarket()
def _initialize_levels(self, price, brick_size):
rnd, floor, _ = self._calculate_levels(price, brick_size)
self._upper_level = rnd
self._lower_level = floor
self._has_levels = True
def _calculate_levels(self, price, brick_size):
ratio = price / brick_size
rounded = round(ratio)
price_round = rounded * brick_size
price_floor = price_round - brick_size
price_ceil = price_round + brick_size
return (price_round, price_floor, price_ceil)
def _get_brick_size(self):
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 0.01
return step * self.BlockSize
def CreateClone(self):
return renko_level_strategy()