Estratégia Hancock RSI Volume
Esta estratégia calcula um Índice de Força Relativa (RSI) ponderado por volume, inspirado no script Hancock do TradingView. O RSI usa o volume altista e baixista para medir a força da tendência. Uma posição comprada é aberta quando a tendência do RSI vira para cima, e uma posição vendida quando vira para baixo.
Detalhes
- Critérios de entrada:
- Comprado: A tendência do RSI muda para cima.
- Vendido: A tendência do RSI muda para baixo.
- Comprado/Vendido: Ambos os lados.
- Critérios de saída:
- Sinal de tendência oposta.
- Stops: Nenhum.
- Valores padrão:
RSI Length= 14.Threshold= 0.1.Use Wicks= true.
- Filtros:
- Categoria: Momentum
- Direção: Ambos
- Indicadores: RSI, Volume
- Stops: Não
- Complexidade: Médio
- Período: Qualquer
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Hancock RSI Volume strategy.
/// Each candle's volume is split into bullish and bearish volume: with UseWicks by where the close sits inside the high-low range,
/// otherwise wholly by the candle colour. The RSI is 100 * bullish / (bullish + bearish) of the Wilder averages of both over
/// RsiLength candles. The trend turns up when the RSI rises by more than Threshold from the previous candle and down when it falls
/// by more than Threshold; a switch to up buys and a switch to down sells, reversing an opposite position.
/// </summary>
public class HancockRsiVolumeStrategy : Strategy
{
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _threshold;
private readonly StrategyParam<bool> _useWicks;
private readonly StrategyParam<DataType> _candleType;
private decimal _bullAvg;
private decimal _bearAvg;
private int _samples;
private decimal? _prevRsi;
private int _trend;
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Minimum RSI change that switches the trend.
/// </summary>
public decimal Threshold
{
get => _threshold.Value;
set => _threshold.Value = value;
}
/// <summary>
/// Split the volume by the close position inside the candle range including wicks.
/// </summary>
public bool UseWicks
{
get => _useWicks.Value;
set => _useWicks.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HancockRsiVolumeStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "Indicators");
_threshold = Param(nameof(Threshold), 0.1m)
.SetNotNegative()
.SetDisplay("Threshold", "Minimum RSI change that switches the trend", "Indicators");
_useWicks = Param(nameof(UseWicks), true)
.SetDisplay("Use Wicks", "Split the volume by the close position inside the candle range", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_bullAvg = 0m;
_bearAvg = 0m;
_samples = 0;
_prevRsi = null;
_trend = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
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 volume = candle.TotalVolume;
decimal bull;
decimal bear;
if (UseWicks)
{
var range = candle.HighPrice - candle.LowPrice;
if (range > 0)
{
bull = volume * (candle.ClosePrice - candle.LowPrice) / range;
bear = volume - bull;
}
else
{
bull = volume / 2;
bear = volume / 2;
}
}
else if (candle.ClosePrice > candle.OpenPrice)
{
bull = volume;
bear = 0m;
}
else if (candle.ClosePrice < candle.OpenPrice)
{
bull = 0m;
bear = volume;
}
else
{
bull = volume / 2;
bear = volume / 2;
}
// Wilder averages seeded with the simple mean of the first RsiLength candles.
var length = RsiLength;
if (_samples < length)
{
_samples++;
_bullAvg += (bull - _bullAvg) / _samples;
_bearAvg += (bear - _bearAvg) / _samples;
if (_samples < length)
return;
}
else
{
_bullAvg = (_bullAvg * (length - 1) + bull) / length;
_bearAvg = (_bearAvg * (length - 1) + bear) / length;
}
var total = _bullAvg + _bearAvg;
if (total <= 0)
return;
var rsi = 100m * _bullAvg / total;
var prevRsi = _prevRsi;
_prevRsi = rsi;
if (prevRsi is not decimal lastRsi)
return;
var prevTrend = _trend;
var change = rsi - lastRsi;
if (change > Threshold)
_trend = 1;
else if (change < -Threshold)
_trend = -1;
if (_trend == prevTrend)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_trend > 0 && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (_trend < 0 && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class hancock_rsi_volume_strategy(Strategy):
"""
Hancock RSI Volume strategy.
Each candle's volume is split into bullish and bearish volume: with UseWicks by where the close sits inside the high-low range,
otherwise wholly by the candle colour. The RSI is 100 * bullish / (bullish + bearish) of the Wilder averages of both over
RsiLength candles. The trend turns up when the RSI rises by more than Threshold from the previous candle and down when it falls
by more than Threshold; a switch to up buys and a switch to down sells, reversing an opposite position.
"""
def __init__(self):
super(hancock_rsi_volume_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "Indicators")
self._threshold = self.Param("Threshold", 0.1).SetNotNegative().SetDisplay("Threshold", "Minimum RSI change that switches the trend", "Indicators")
self._use_wicks = self.Param("UseWicks", True).SetDisplay("Use Wicks", "Split the volume by the close position inside the candle range", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._bull_avg = 0.0
self._bear_avg = 0.0
self._samples = 0
self._prev_rsi = None
self._trend = 0
def OnReseted(self):
super(hancock_rsi_volume_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(hancock_rsi_volume_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
volume = float(candle.TotalVolume)
open_price = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._use_wicks.Value:
rng = high - low
if rng > 0:
bull = volume * (close - low) / rng
bear = volume - bull
else:
bull = volume / 2.0
bear = volume / 2.0
elif close > open_price:
bull = volume
bear = 0.0
elif close < open_price:
bull = 0.0
bear = volume
else:
bull = volume / 2.0
bear = volume / 2.0
# Wilder averages seeded with the simple mean of the first RsiLength candles.
length = self._rsi_length.Value
if self._samples < length:
self._samples += 1
self._bull_avg += (bull - self._bull_avg) / self._samples
self._bear_avg += (bear - self._bear_avg) / self._samples
if self._samples < length:
return
else:
self._bull_avg = (self._bull_avg * (length - 1) + bull) / length
self._bear_avg = (self._bear_avg * (length - 1) + bear) / length
total = self._bull_avg + self._bear_avg
if total <= 0:
return
rsi = 100.0 * self._bull_avg / total
prev_rsi = self._prev_rsi
self._prev_rsi = rsi
if prev_rsi is None:
return
prev_trend = self._trend
change = rsi - prev_rsi
threshold = float(self._threshold.Value)
if change > threshold:
self._trend = 1
elif change < -threshold:
self._trend = -1
if self._trend == prev_trend:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._trend > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif self._trend < 0 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return hancock_rsi_volume_strategy()