Ichimoku Volume 戦略
戦略の実装 - Ichimoku + Volume。価格が雲(Kumo)の上にあり、転換線(Tenkan-sen)が基準線(Kijun-sen)の上にあり、出来高が平均を上回るときに買い。価格が雲の下にあり、転換線が基準線の下にあり、出来高が平均を上回るときに売り。
テストでは年平均リターン約40%を示しています。暗号資産市場で最もパフォーマンスが高いです。
一目均衡表のコンポーネントが方向性バイアスを定義し、出来高の急増が関心を確認します。価格が雲と整合し出来高が増加したときにトレードが開かれます。
参加を伴う雲のブレイクアウトをフォローするトレーダーに適しています。リスクはパーセントストップによって制限されます。
詳細
- エントリー条件:
- ロング:
Price > Cloud && Tenkan > Kijun && Volume > AvgVolume - ショート:
Price < Cloud && Tenkan < Kijun && Volume > AvgVolume
- ロング:
- ロング/ショート: 両方
- エグジット条件:
- 反対方向への雲のブレイクアウト
- ストップ:
StopLossPercentを使用したパーセントベース - デフォルト値:
TenkanPeriod= 9KijunPeriod= 26SenkouSpanPeriod= 52VolumeAvgPeriod= 20StopLossPercent= 2CandleType= TimeSpan.FromMinutes(5).TimeFrame() AvgVolumeは直前VolumeAvgPeriod本の平均出来高です。ロングは価格が雲の下で引けたとき、ショートは雲の上で引けたときに決済します。 保有ポジションと逆方向のエントリーシグナルはドテンになります。
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: Ichimoku Cloud, Volume
- ストップ: はい
- 複雑さ: 中級
- 時間軸: 中期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Ichimoku Volume strategy.
/// A close above the cloud with Tenkan-sen above Kijun-sen on volume above the average of the previous VolumeAvgPeriod candles goes long;
/// a close below the cloud with Tenkan-sen below Kijun-sen on such volume goes short, reversing an opposite position.
/// A long closes when price closes below the cloud and a short when it closes above it, and a percent stop limits the loss.
/// </summary>
public class IchimokuVolumeStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanPeriod;
private readonly StrategyParam<int> _volumeAvgPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _volumes = [];
/// <summary>
/// Period of Tenkan-sen.
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Period of Kijun-sen.
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Period of Senkou Span B.
/// </summary>
public int SenkouSpanPeriod
{
get => _senkouSpanPeriod.Value;
set => _senkouSpanPeriod.Value = value;
}
/// <summary>
/// Previous candles the volume is averaged over.
/// </summary>
public int VolumeAvgPeriod
{
get => _volumeAvgPeriod.Value;
set => _volumeAvgPeriod.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 IchimokuVolumeStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Period of Tenkan-sen", "Ichimoku");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Period of Kijun-sen", "Ichimoku");
_senkouSpanPeriod = Param(nameof(SenkouSpanPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span Period", "Period of Senkou Span B", "Ichimoku");
_volumeAvgPeriod = Param(nameof(VolumeAvgPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Volume Average Period", "Previous candles the volume is averaged over", "Volume");
_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 ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, 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, ichimoku);
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 ichimokuValue)
{
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 (ichimokuValue is not IIchimokuValue { Tenkan: decimal tenkan, Kijun: decimal kijun, SenkouA: decimal senkouA, SenkouB: decimal senkouB })
return;
if (average is not decimal avgVolume || !IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var cloudTop = Math.Max(senkouA, senkouB);
var cloudBottom = Math.Min(senkouA, senkouB);
var surge = candle.TotalVolume > avgVolume;
if (close > cloudTop && tenkan > kijun && surge && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close < cloudBottom && tenkan < kijun && surge && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close < cloudBottom)
SellMarket(Position);
else if (Position < 0 && close > cloudTop)
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 Ichimoku
from StockSharp.Algo.Strategies import Strategy
class ichimoku_volume_strategy(Strategy):
"""
Ichimoku Volume strategy.
A close above the cloud with Tenkan-sen above Kijun-sen on volume above the average of the previous VolumeAvgPeriod candles goes long;
a close below the cloud with Tenkan-sen below Kijun-sen on such volume goes short, reversing an opposite position.
A long closes when price closes below the cloud and a short when it closes above it, and a percent stop limits the loss.
"""
def __init__(self):
super(ichimoku_volume_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9).SetGreaterThanZero().SetDisplay("Tenkan Period", "Period of Tenkan-sen", "Ichimoku")
self._kijun_period = self.Param("KijunPeriod", 26).SetGreaterThanZero().SetDisplay("Kijun Period", "Period of Kijun-sen", "Ichimoku")
self._senkou_span_period = self.Param("SenkouSpanPeriod", 52).SetGreaterThanZero().SetDisplay("Senkou Span Period", "Period of Senkou Span B", "Ichimoku")
self._volume_avg_period = self.Param("VolumeAvgPeriod", 20).SetGreaterThanZero().SetDisplay("Volume Average Period", "Previous candles the volume is averaged over", "Volume")
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(ichimoku_volume_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(ichimoku_volume_strategy, self).OnStarted2(time)
self._reset_state()
ichimoku = Ichimoku()
ichimoku.Tenkan.Length = self._tenkan_period.Value
ichimoku.Kijun.Length = self._kijun_period.Value
ichimoku.SenkouB.Length = self._senkou_span_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ichimoku, 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, ichimoku)
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, ichimoku_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)
tenkan = ichimoku_value.Tenkan
kijun = ichimoku_value.Kijun
senkou_a = ichimoku_value.SenkouA
senkou_b = ichimoku_value.SenkouB
if tenkan is None or kijun is None or senkou_a is None or senkou_b is None:
return
if average is None or not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
cloud_top = senkou_a if senkou_a > senkou_b else senkou_b
cloud_bottom = senkou_b if senkou_a > senkou_b else senkou_a
surge = candle.TotalVolume > average
if close > cloud_top and tenkan > kijun and surge and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close < cloud_bottom and tenkan < kijun and surge and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close < cloud_bottom:
self.SellMarket(self.Position)
elif self.Position < 0 and close > cloud_top:
self.BuyMarket(-self.Position)
def CreateClone(self):
return ichimoku_volume_strategy()