Renkoレベル戦略
概要
Renkoレベル戦略はMetaTrader 5エキスパートアドバイザー「Renko Level EA」の忠実な変換です。StockSharp内でインジケーター駆動型のロジックを再構築し、丸められたRenkoレベルが新しいブロックにジャンプするたびに取引します。戦略は各レベルのシフトを合成Renkoレンガのブレイクアウトとして解釈し、ブレイクアウトの方向、または逆モードが有効な場合は反対方向にエントリーします。
システムは通常の時間ベースのロウソク足(デフォルトでは1分)をデータフィードとしてのみ使用します。ロウソク足のクローズはRenkoデータサブスクリプションを必要とせずにRenkoレンガをエミュレートする設定可能なブロックサイズに丸められます。丸められたブロックが変わるたびに、戦略は反対方向のエクスポージャーを決済し、検出された動きに合わせた新しいポジションを開きます。
取引ロジック
- 初期化
- 銘柄からpipサイズを検出します(
PriceStep)。
Block Sizeパラメーターをpipから価格単位に変換します(3桁および5桁の銘柄はpip値を自動的に10倍にします)。
- 最初の完了したロウソク足のクローズを最も近いブロックに丸めて初期の上部と下部のRenkoレベルを作成します。
- レベルの維持
- 各完了したロウソク足でクローズ価格が最も近いブロックサイズに丸められます。
- クローズが現在のブロック内に留まる場合、保存されたレベルは変更されません。
- クローズが下限を下回ると、アルゴリズムは価格を切り捨ててブロックを下にシフトします(
lower = round、upper = round + size)。
- クローズが上限を上回ると、ブロックは上にシフトされます(
upper = round、lower = round - size)。
- シグナル生成
- 上昇する上部レベルはRenkoブロックの強気のブレイクアウトを示します。下降する上部レベルは弱気のブレイクアウトを示します。
Reverseが無効な場合、戦略は強気シフトで買い、弱気シフトで売ります。Reverseが有効な場合、アクションが入れ替わります。
- シグナルがトリガーされると、反対方向の既存のエクスポージャーが自動的に清算されます(買い注文はショートを決済、売り注文はロングを決済)。
Allow Increaseが無効な場合、戦略は同じ方向のすでに開いているポジションの上にサイズを追加することを拒否します。
- 注文実行
- 注文は戦略の
Volume設定で送信されます。既存のポジションを反転する場合、注文サイズは絶対ポジションに設定されたボリュームを加えた値に等しく、フリップが即座に発生します。
StartProtection()は起動時に呼び出され、Designerまたはコンポジションを通じて設定されたリスク保護がアクティブになります。
パラメーター
| パラメーター |
説明 |
デフォルト |
Block Size |
pipでのRenkoブロックサイズ。戦略はこれを銘柄のpip値で乗算して実際の価格増分を得ます。値が大きいほど取引頻度が減ります。 |
30 |
Reverse |
trueの場合、すべての取引シグナルを反転します(弱気シフトで買い、強気シフトで売り)。 |
false |
Allow Increase |
trueの場合、各シグナルで同じ方向に追加注文を追加することでピラミッドを許可します。falseの場合、反対側を決済した後にネットポジションがフラットな場合にのみ新しい注文が送信されます。 |
false |
Candle Type |
ソースロウソク足データ。サポートされている任意のDataTypeを使用できます;デフォルトでは戦略は1分ロウソク足をサブスクライブします。 |
TimeFrame(1m) |
Volume (継承) |
マーケット注文を送信する際に使用する注文サイズ。開始前にストラテジーインスタンスでこのプロパティを設定してください。 |
ポートフォリオによる |
使用上の注意
- 銘柄のボラティリティに応じてブロックサイズを選択します。主要な外為ペアでは30〜50 pipが元のEAの動作をエミュレートします。指数または暗号資産ではより大きなブロックサイズを使用します。
- 戦略はロウソク足のクローズが望ましい価格サンプリングを反映している限り、任意のロウソク足フィード(ティック、時間軸、レンジ)で動作します。純粋なRenkoフィードには、ロウソク足タイプをRenkoデータシリーズに切り替えることができます。
Reverseを有効にして、各Renkoレベル変化をフェードするブレイクアウトシステムを平均回帰システムに変換します。
Allow Increaseをオンにして、同じ方向の各新しいレベルでコントラクトを追加する元のEAの「Increase」パラメーターを模倣できます。
- リスクとマネーマネジメント(ストップロス、テイクプロフィット、ドローダウン制御)はStockSharpの保護またはラッパー戦略を通じて設定できます。サンプルはMT5エキスパートと同一のロジックを維持し、レベルフリップを超えた固定出口を課しません。
データ要件
- 設定された
Candle Typeの過去および リアルタイムのロウソク足データ。
- pip変換が正しく機能するために、銘柄のメタデータが
PriceStepとDecimalsを提供する必要があります。これらの値が利用できない場合、戦略はデフォルトのステップ0.0001にフォールバックします。
推奨ワークフロー
- Designer に戦略を追加するか、StockSharp APIを通じてプログラムで作成します。
Security、Portfolio、Volumeを設定し、オプションで上記のパラメーターを調整します。
- 戦略を開始します。最初の完了したロウソク足を待って初期のRenkoブロックを確立します。
- 内蔵のトレードチャートを監視するかログをサブスクライブして、丸められたレベルが変化したときにのみ注文がトリガーされることを確認します。
このドキュメントは元のRenko Level EAの動作を反映しながら、さらにカスタマイズまたは拡張できるようにStockSharp内での実装方法を説明しています。
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()