Estratégia Volume Supertrend
Esta estratégia utiliza os indicadores Volume Supertrend para gerar sinais. A entrada comprada ocorre quando Volume > Avg(Volume) && Price > Supertrend (surto de volume com tendência de alta). A entrada vendida ocorre quando Volume > Avg(Volume) && Price < Supertrend (surto de volume com tendência de baixa). É adequada para traders que buscam oportunidades em mercados de tendência.
Os testes indicam um retorno anual médio de aproximadamente 64%. Funciona melhor no mercado de câmbio.
Detalhes
- Critérios de entrada:
- Comprado: Volume > Avg(Volume) && Price > Supertrend (volume surge with uptrend)
- Vendido: Volume > Avg(Volume) && Price < Supertrend (volume surge with downtrend)
- Comprado/Vendido: Ambos os lados.
- Critérios de saída:
- Comprado: Sair da posição comprada quando Supertrend vira para baixo
- Vendido: Sair da posição vendida quando Supertrend vira para cima
- Stops: Sim.
- Valores padrão:
VolumeAvgPeriod= 20SupertrendPeriod= 10SupertrendMultiplier= 3CandleType= TimeSpan.FromMinutes(5)StopLossPercent= 2 Avg(Volume) é o volume médio dos VolumeAvgPeriod candles anteriores. Uma compra fecha assim que o Supertrend vira para baixo e uma venda quando vira para cima; o stop é um StopLossPercent fixo do preço de entrada, vigiado também entre os candles. Um sinal de entrada contra uma posição aberta a inverte.
- Filtros:
- Categoria: Tendência
- Direção: Ambos
- Indicadores: Volume Supertrend
- Stops: Sim
- Complexidade: Intermediário
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Volume Supertrend strategy.
/// A close above the Supertrend line on volume above the average of the previous VolumeAvgPeriod candles goes long and a close below it
/// on such volume goes short, reversing an opposite position. A long closes once the Supertrend turns down and a short once it turns up,
/// and a percent stop limits the loss.
/// </summary>
public class VolumeSupertrendStrategy : Strategy
{
private readonly StrategyParam<int> _volumeAvgPeriod;
private readonly StrategyParam<int> _supertrendPeriod;
private readonly StrategyParam<decimal> _supertrendMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _volumes = [];
/// <summary>
/// Previous candles the volume is averaged over.
/// </summary>
public int VolumeAvgPeriod
{
get => _volumeAvgPeriod.Value;
set => _volumeAvgPeriod.Value = value;
}
/// <summary>
/// ATR period of Supertrend.
/// </summary>
public int SupertrendPeriod
{
get => _supertrendPeriod.Value;
set => _supertrendPeriod.Value = value;
}
/// <summary>
/// ATR multiplier of Supertrend.
/// </summary>
public decimal SupertrendMultiplier
{
get => _supertrendMultiplier.Value;
set => _supertrendMultiplier.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public VolumeSupertrendStrategy()
{
_volumeAvgPeriod = Param(nameof(VolumeAvgPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Volume Avg Period", "Previous candles the volume is averaged over", "Volume");
_supertrendPeriod = Param(nameof(SupertrendPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Supertrend Period", "ATR period of Supertrend", "Supertrend");
_supertrendMultiplier = Param(nameof(SupertrendMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("Supertrend Multiplier", "ATR multiplier of Supertrend", "Supertrend");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_volumes.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_volumes.Clear();
var supertrend = new SuperTrend { Length = SupertrendPeriod, Multiplier = SupertrendMultiplier };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(supertrend, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, supertrend);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue supertrendValue)
{
if (candle.State != CandleStates.Finished)
return;
// Volume is compared with the candles before this one.
var average = _volumes.Count == VolumeAvgPeriod ? _volumes.Average() : (decimal?)null;
_volumes.Add(candle.TotalVolume);
if (_volumes.Count > VolumeAvgPeriod)
_volumes.RemoveAt(0);
if (!supertrendValue.IsFormed || supertrendValue is not SuperTrendIndicatorValue value || average is not decimal avgVolume)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var line = value.Value;
var close = candle.ClosePrice;
var surge = candle.TotalVolume > avgVolume;
if (surge && close > line && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (surge && close < line && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && !value.IsUpTrend)
SellMarket(Position);
else if (Position < 0 && value.IsUpTrend)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import SuperTrend
from StockSharp.Algo.Strategies import Strategy
class volume_supertrend_strategy(Strategy):
"""
Volume Supertrend strategy.
A close above the Supertrend line on volume above the average of the previous VolumeAvgPeriod candles goes long and a close below it
on such volume goes short, reversing an opposite position. A long closes once the Supertrend turns down and a short once it turns up,
and a percent stop limits the loss.
"""
def __init__(self):
super(volume_supertrend_strategy, self).__init__()
self._volume_avg_period = self.Param("VolumeAvgPeriod", 20).SetGreaterThanZero().SetDisplay("Volume Avg Period", "Previous candles the volume is averaged over", "Volume")
self._supertrend_period = self.Param("SupertrendPeriod", 10).SetGreaterThanZero().SetDisplay("Supertrend Period", "ATR period of Supertrend", "Supertrend")
self._supertrend_multiplier = self.Param("SupertrendMultiplier", 3.0).SetGreaterThanZero().SetDisplay("Supertrend Multiplier", "ATR multiplier of Supertrend", "Supertrend")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).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._volumes = []
def OnReseted(self):
super(volume_supertrend_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(volume_supertrend_strategy, self).OnStarted2(time)
self._reset_state()
supertrend = SuperTrend()
supertrend.Length = self._supertrend_period.Value
supertrend.Multiplier = Decimal(self._supertrend_multiplier.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(supertrend, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, supertrend)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, supertrend_value):
if candle.State != CandleStates.Finished:
return
# Volume is compared with the candles before this one.
period = self._volume_avg_period.Value
average = None
if len(self._volumes) == period:
total = Decimal(0)
for volume in self._volumes:
total += volume
average = total / Decimal(period)
self._volumes.append(candle.TotalVolume)
if len(self._volumes) > period:
self._volumes.pop(0)
if not supertrend_value.IsFormed or average is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
line = supertrend_value.Value
close = candle.ClosePrice
surge = candle.TotalVolume > average
if surge and close > line and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif surge and close < line and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and not supertrend_value.IsUpTrend:
self.SellMarket(self.Position)
elif self.Position < 0 and supertrend_value.IsUpTrend:
self.BuyMarket(-self.Position)
def CreateClone(self):
return volume_supertrend_strategy()