Renko トレンドリバーサル戦略 V2
Renko トレンドリバーサル戦略 V2 は、RenkoのオープンがRenkoのクローズをクロスしたときにトレードします。ATRベースのRenkoブロックを使用し、ストップロスとテイクプロフィットのレベルを設定します。ショートは無効にできます。
詳細
- エントリー条件: 時間ウィンドウ内でのRenkoオープン/クロースのクロス
- ロング/ショート: 両方(ショートはオプション)
- エグジット条件: ストップロスまたはテイクプロフィット
- ストップ: あり
- デフォルト値:
RenkoAtrLength= 10StopLossPct= 3TakeProfitPct= 20
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: ATR
- ストップ: あり
- 複雑さ: 基本
- 時間軸: Renko
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Adaptive Renko reversal strategy. Brick size follows ATR of the source candles.
/// </summary>
public class RenkoTrendReversalV2Strategy : Strategy
{
private readonly StrategyParam<int> _renkoAtrLength;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<bool> _allowShorts;
private readonly StrategyParam<TimeSpan> _tradeStart;
private readonly StrategyParam<TimeSpan> _tradeEnd;
private readonly StrategyParam<DataType> _candleType;
private decimal? _brickClose;
private int _lastBrickDirection;
public int RenkoAtrLength { get => _renkoAtrLength.Value; set => _renkoAtrLength.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public bool AllowShorts { get => _allowShorts.Value; set => _allowShorts.Value = value; }
public TimeSpan TradeStart { get => _tradeStart.Value; set => _tradeStart.Value = value; }
public TimeSpan TradeEnd { get => _tradeEnd.Value; set => _tradeEnd.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RenkoTrendReversalV2Strategy()
{
_renkoAtrLength = Param(nameof(RenkoAtrLength), 10).SetGreaterThanZero()
.SetDisplay("Renko ATR Length", "ATR period used as adaptive brick size.", "Renko");
_stopLossPct = Param(nameof(StopLossPct), 3m).SetNotNegative()
.SetDisplay("Stop Loss %", "Protective stop in percent.", "Protection");
_takeProfitPct = Param(nameof(TakeProfitPct), 20m).SetNotNegative()
.SetDisplay("Take Profit %", "Profit target in percent.", "Protection");
_allowShorts = Param(nameof(AllowShorts), true)
.SetDisplay("Allow Shorts", "Allow bearish Renko reversals to open short positions.", "Trading");
_tradeStart = Param(nameof(TradeStart), TimeSpan.Zero)
.SetDisplay("Trade Start", "Start of the allowed entry window.", "Timing");
_tradeEnd = Param(nameof(TradeEnd), new TimeSpan(23, 59, 59))
.SetDisplay("Trade End", "End of the allowed entry window.", "Timing");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Source Candle Type", "Source candles used to calculate ATR and build adaptive Renko bricks.", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_brickClose = null;
_lastBrickDirection = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (TakeProfitPct > 0m || StopLossPct > 0m)
{
StartProtection(
TakeProfitPct > 0m ? new Unit(TakeProfitPct, UnitTypes.Percent) : null,
StopLossPct > 0m ? new Unit(StopLossPct, UnitTypes.Percent) : null);
}
var atr = new AverageTrueRange { Length = RenkoAtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(atr, (candle, atrValue) =>
{
if (candle.State != CandleStates.Finished || !atr.IsFormed || atrValue <= 0m)
return;
ProcessAdaptiveRenko(candle, atrValue);
}).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessAdaptiveRenko(ICandleMessage candle, decimal brickSize)
{
if (_brickClose is null)
{
_brickClose = candle.ClosePrice;
return;
}
// A large source bar can form several Renko bricks. Each brick has an explicit
// open/close relationship; a change of that relationship is the reversal signal.
while (candle.ClosePrice >= _brickClose.Value + brickSize)
{
var open = _brickClose.Value;
var close = open + brickSize;
ProcessBrick(candle.OpenTime, open, close);
_brickClose = close;
}
while (candle.ClosePrice <= _brickClose.Value - brickSize)
{
var open = _brickClose.Value;
var close = open - brickSize;
ProcessBrick(candle.OpenTime, open, close);
_brickClose = close;
}
}
private void ProcessBrick(DateTimeOffset time, decimal open, decimal close)
{
var direction = close > open ? 1 : -1;
var reversal = _lastBrickDirection != 0 && direction != _lastBrickDirection;
_lastBrickDirection = direction;
if (!reversal || !InTradeWindow(time.TimeOfDay))
return;
if (direction > 0 && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (direction < 0 && AllowShorts && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
private bool InTradeWindow(TimeSpan time)
=> TradeStart <= TradeEnd
? time >= TradeStart && time <= TradeEnd
: time >= TradeStart || time <= TradeEnd;
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class renko_trend_reversal_v2_strategy(Strategy):
def __init__(self):
super(renko_trend_reversal_v2_strategy, self).__init__()
self._renko_atr_length = self.Param("RenkoAtrLength", 10).SetGreaterThanZero()
self._stop_loss_pct = self.Param("StopLossPct", 3.0).SetNotNegative()
self._take_profit_pct = self.Param("TakeProfitPct", 20.0).SetNotNegative()
self._allow_shorts = self.Param("AllowShorts", True)
self._trade_start = self.Param("TradeStart", TimeSpan.Zero)
self._trade_end = self.Param("TradeEnd", TimeSpan(23, 59, 59))
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._brick_close = None
self._last_brick_direction = 0
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnReseted(self):
super(renko_trend_reversal_v2_strategy, self).OnReseted()
self._brick_close = None
self._last_brick_direction = 0
def OnStarted2(self, time):
super(renko_trend_reversal_v2_strategy, self).OnStarted2(time)
tp = float(self._take_profit_pct.Value)
sl = float(self._stop_loss_pct.Value)
if tp > 0 or sl > 0:
self.StartProtection(
Unit(tp, UnitTypes.Percent) if tp > 0 else None,
Unit(sl, UnitTypes.Percent) if sl > 0 else None)
atr = AverageTrueRange()
atr.Length = int(self._renko_atr_length.Value)
def on_candle(candle, atr_value):
if candle.State != CandleStates.Finished or not atr.IsFormed or float(atr_value) <= 0:
return
self._process_adaptive_renko(candle, float(atr_value))
subscription = self.SubscribeCandles(self._candle_type.Value)
subscription.Bind(atr, on_candle).Start()
def _process_adaptive_renko(self, candle, brick_size):
close_price = float(candle.ClosePrice)
if self._brick_close is None:
self._brick_close = close_price
return
while close_price >= self._brick_close + brick_size:
open_price = self._brick_close
brick_close = open_price + brick_size
self._process_brick(candle.OpenTime, open_price, brick_close)
self._brick_close = brick_close
while close_price <= self._brick_close - brick_size:
open_price = self._brick_close
brick_close = open_price - brick_size
self._process_brick(candle.OpenTime, open_price, brick_close)
self._brick_close = brick_close
def _process_brick(self, time, open_price, close_price):
direction = 1 if close_price > open_price else -1
reversal = self._last_brick_direction != 0 and direction != self._last_brick_direction
self._last_brick_direction = direction
if not reversal or not self._in_window(time.TimeOfDay):
return
if direction > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif direction < 0 and bool(self._allow_shorts.Value) and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
def _in_window(self, value):
start = self._trade_start.Value
end = self._trade_end.Value
if start <= end:
return value >= start and value <= end
return value >= start or value <= end
def CreateClone(self):
return renko_trend_reversal_v2_strategy()