Estratégia de Reversão VRS Vegas
Estratégia de reversão que utiliza as mechas dos candles.
Os testes indicam um retorno anual médio de aproximadamente 37%. Funciona melhor no mercado de criptomoedas.
O sistema procura grandes picos em relação ao preço de fechamento. Uma mecha inferior grande aciona uma entrada comprada, enquanto uma mecha superior grande aciona uma entrada vendida. As posições são fechadas quando o preço avança o dobro do tamanho do pico em lucro.
Detalhes
- Critérios de entrada:
- Comprado: mecha inferior ≥ Spike% * close e sem pico superior.
- Vendido: mecha superior ≥ Spike% * close e sem pico inferior.
- Comprado/Vendido: Ambos os lados.
- Critérios de saída: Alvo na entrada ± (pico * 2).
- Stops: Não.
- Valores padrão:
SpikePercent= 0.025CandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoria: Reversão
- Direção: Ambos
- Indicadores: Price action
- Stops: Não
- Complexidade: Básico
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Alto
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>
/// Vegas Reversal strategy based on spike percentage.
/// Enters long on large lower wick and short on large upper wick.
/// Exits when price moves twice the spike length in favor.
/// </summary>
public class VrsVegasReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _spikePercent;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _spikeSize;
private bool _isLong;
/// <summary>
/// Spike percentage relative to close price.
/// </summary>
public decimal SpikePercent
{
get => _spikePercent.Value;
set => _spikePercent.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="VrsVegasReversalStrategy"/>.
/// </summary>
public VrsVegasReversalStrategy()
{
_spikePercent = Param(nameof(SpikePercent), 0.025m)
.SetGreaterThanZero()
.SetDisplay("Spike %", "Spike percentage threshold", "Reversal")
.SetOptimize(0.01m, 0.05m, 0.005m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_spikeSize = 0;
_isLong = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = NormalizeVolume(1m);
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 upperSpike = candle.HighPrice - Math.Max(candle.ClosePrice, candle.OpenPrice);
var lowerSpike = Math.Min(candle.ClosePrice, candle.OpenPrice) - candle.LowPrice;
var validUpper = upperSpike >= candle.ClosePrice * SpikePercent;
var validLower = lowerSpike >= candle.ClosePrice * SpikePercent;
var valid = (validUpper && !validLower) || (validLower && !validUpper);
var enterLong = valid && validLower;
var enterShort = valid && validUpper;
if (enterLong && Position <= 0)
{
_entryPrice = candle.ClosePrice;
_spikeSize = lowerSpike;
_isLong = true;
BuyMarket(NormalizeVolume(Volume + Math.Abs(Position)));
}
else if (enterShort && Position >= 0)
{
_entryPrice = candle.ClosePrice;
_spikeSize = upperSpike;
_isLong = false;
SellMarket(NormalizeVolume(Volume + Math.Abs(Position)));
}
if (Position > 0 && _isLong)
{
var target = _entryPrice + _spikeSize * 2m;
if (candle.ClosePrice >= target)
SellMarket(Position);
}
else if (Position < 0 && !_isLong)
{
var target = _entryPrice - _spikeSize * 2m;
if (candle.ClosePrice <= target)
BuyMarket(-Position);
}
}
private decimal NormalizeVolume(decimal volume)
{
var step = Security?.VolumeStep ?? 1m;
if (step <= 0m)
step = 1m;
var minimum = Security?.MinVolume ?? step;
if (volume < minimum)
volume = minimum;
var rounded = Math.Ceiling(volume / step) * step;
return rounded < minimum ? minimum : rounded;
}
}
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 vrs_vegas_reversal_strategy(Strategy):
def __init__(self):
super(vrs_vegas_reversal_strategy, self).__init__()
self._spike_percent = self.Param("SpikePercent", 0.025) \
.SetDisplay("Spike %", "Spike percentage threshold", "Reversal")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._entry_price = 0.0
self._spike_size = 0.0
self._is_long = False
@property
def spike_percent(self):
return self._spike_percent.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vrs_vegas_reversal_strategy, self).OnReseted()
self._entry_price = 0.0
self._spike_size = 0.0
self._is_long = False
def OnStarted2(self, time):
super(vrs_vegas_reversal_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
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
upper_spike = float(candle.HighPrice) - max(float(candle.ClosePrice), float(candle.OpenPrice))
lower_spike = min(float(candle.ClosePrice), float(candle.OpenPrice)) - float(candle.LowPrice)
close = float(candle.ClosePrice)
sp = float(self.spike_percent)
valid_upper = upper_spike >= close * sp
valid_lower = lower_spike >= close * sp
valid = (valid_upper and not valid_lower) or (valid_lower and not valid_upper)
enter_long = valid and valid_lower
enter_short = valid and valid_upper
if enter_long and self.Position <= 0:
self._entry_price = close
self._spike_size = lower_spike
self._is_long = True
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif enter_short and self.Position >= 0:
self._entry_price = close
self._spike_size = upper_spike
self._is_long = False
self.SellMarket(self.Volume + Math.Abs(self.Position))
if self.Position > 0 and self._is_long:
target = self._entry_price + self._spike_size * 2.0
if close >= target:
self.SellMarket(self.Position)
elif self.Position < 0 and not self._is_long:
target = self._entry_price - self._spike_size * 2.0
if close <= target:
self.BuyMarket(-self.Position)
def CreateClone(self):
return vrs_vegas_reversal_strategy()