Volume Supertrend 戦略
この戦略はVolume Supertrendインジケーターを使用してシグナルを生成します。 Volume > Avg(Volume) かつ Price > Supertrend(出来高急増と上昇トレンド)の場合にロングエントリー。Volume > Avg(Volume) かつ Price < Supertrend(出来高急増と下降トレンド)の場合にショートエントリー。 トレンド市場で機会を求めるトレーダーに適しています。
テストでは年間平均リターン約64%を示しています。外国為替市場で最もパフォーマンスが高いです。
詳細
- エントリー条件:
- ロング: Volume > Avg(Volume) && Price > Supertrend (volume surge with uptrend)
- ショート: Volume > Avg(Volume) && Price < Supertrend (volume surge with downtrend)
- ロング/ショート: 両方。
- エグジット条件:
- ロング: Supertrendが下向きに転じたときロングポジションを終了
- ショート: Supertrendが上向きに転じたときショートポジションを終了
- ストップ: はい。
- デフォルト値:
VolumeAvgPeriod= 20SupertrendPeriod= 10SupertrendMultiplier= 3CandleType= TimeSpan.FromMinutes(5)StopLossPercent= 2 Avg(Volume)は直前VolumeAvgPeriod本の平均出来高です。Supertrendが下向きに転じたらロングを、上向きに転じたらショートを閉じます。ストップはエントリー価格の固定StopLossPercentで、ローソク足の間も監視されます。 保有ポジションと逆方向のエントリーシグナルはドテンになります。
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: Volume Supertrend
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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()