Ichimoku Daily Candle X Hull MA X MACD 戦略
この戦略は、Ichimokuの先行線、日足ローソク足の方向、Hull Moving Averageのトレンド、およびHMAベースのMACDを組み合わせています。すべてのコンポーネントが強気に揃ったときにロングポジションを建て、すべての条件が弱気に転じたときにショートを行います。
詳細
- エントリー条件:
- ロング: HMAが上昇、現在の価格が前のHMAを上回る、現在の日足ローソク足が前より高い、SenkouA > SenkouB、MACDライン > シグナル。
- ショート: HMAが下落、価格が前のHMAを下回る、現在の日足ローソク足が前より低い、SenkouA < SenkouB、MACDライン < シグナル。
- ロング/ショート: 両方向。
- エグジット条件: 反対のシグナル。
- ストップ: なし。
- デフォルト値:
HmaPeriod= 14ConversionPeriod= 9BasePeriod= 26SpanPeriod= 52MacdFastLength= 12MacdSlowLength= 26MacdSignalLength= 9PriceSource= OpenCandleType= TimeSpan.FromMinutes(1).TimeFrame()
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: Ichimoku, Hull MA, MACD
- ストップ: なし
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Ichimoku daily candle X Hull MA X MACD strategy.
/// A Hull moving average of HmaPeriod and an HMA-based MACD (HMA(MacdFastLength) - HMA(MacdSlowLength), signal HMA(MacdSignalLength))
/// are computed on the PriceSource price. A long requires a rising HMA, the close above the previous HMA value, the current daily
/// candle's PriceSource price above the previous day's, Senkou A above Senkou B and the MACD line above its signal; a short requires
/// all of them reversed. The opposite signal reverses the position; there are no stops.
/// </summary>
public class IchimokuDailyCandleXHullMaXMacdStrategy : Strategy
{
/// <summary>
/// Candle price used by the averages and the daily comparison.
/// </summary>
public enum PriceSources
{
/// <summary>
/// Open price.
/// </summary>
Open,
/// <summary>
/// High price.
/// </summary>
High,
/// <summary>
/// Low price.
/// </summary>
Low,
/// <summary>
/// Close price.
/// </summary>
Close,
}
private readonly StrategyParam<int> _hmaPeriod;
private readonly StrategyParam<int> _conversionPeriod;
private readonly StrategyParam<int> _basePeriod;
private readonly StrategyParam<int> _spanPeriod;
private readonly StrategyParam<int> _macdFastLength;
private readonly StrategyParam<int> _macdSlowLength;
private readonly StrategyParam<int> _macdSignalLength;
private readonly StrategyParam<PriceSources> _priceSource;
private readonly StrategyParam<DataType> _candleType;
private HullMovingAverage _hma;
private HullMovingAverage _macdFast;
private HullMovingAverage _macdSlow;
private HullMovingAverage _macdSignal;
private decimal? _prevHma;
private DateTime? _currentDay;
private decimal _currentDayPrice;
private decimal? _previousDayPrice;
/// <summary>
/// Hull moving average period.
/// </summary>
public int HmaPeriod
{
get => _hmaPeriod.Value;
set => _hmaPeriod.Value = value;
}
/// <summary>
/// Ichimoku conversion line period.
/// </summary>
public int ConversionPeriod
{
get => _conversionPeriod.Value;
set => _conversionPeriod.Value = value;
}
/// <summary>
/// Ichimoku base line period.
/// </summary>
public int BasePeriod
{
get => _basePeriod.Value;
set => _basePeriod.Value = value;
}
/// <summary>
/// Ichimoku leading span B period.
/// </summary>
public int SpanPeriod
{
get => _spanPeriod.Value;
set => _spanPeriod.Value = value;
}
/// <summary>
/// Fast HMA period of the MACD.
/// </summary>
public int MacdFastLength
{
get => _macdFastLength.Value;
set => _macdFastLength.Value = value;
}
/// <summary>
/// Slow HMA period of the MACD.
/// </summary>
public int MacdSlowLength
{
get => _macdSlowLength.Value;
set => _macdSlowLength.Value = value;
}
/// <summary>
/// Signal HMA period of the MACD.
/// </summary>
public int MacdSignalLength
{
get => _macdSignalLength.Value;
set => _macdSignalLength.Value = value;
}
/// <summary>
/// Candle price used by the averages and the daily comparison.
/// </summary>
public PriceSources PriceSource
{
get => _priceSource.Value;
set => _priceSource.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public IchimokuDailyCandleXHullMaXMacdStrategy()
{
_hmaPeriod = Param(nameof(HmaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("HMA Period", "Hull moving average period", "Indicators");
_conversionPeriod = Param(nameof(ConversionPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Conversion Period", "Ichimoku conversion line period", "Ichimoku");
_basePeriod = Param(nameof(BasePeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Base Period", "Ichimoku base line period", "Ichimoku");
_spanPeriod = Param(nameof(SpanPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Span Period", "Ichimoku leading span B period", "Ichimoku");
_macdFastLength = Param(nameof(MacdFastLength), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast HMA period of the MACD", "MACD");
_macdSlowLength = Param(nameof(MacdSlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow HMA period of the MACD", "MACD");
_macdSignalLength = Param(nameof(MacdSignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal HMA period of the MACD", "MACD");
_priceSource = Param(nameof(PriceSource), PriceSources.Open)
.SetDisplay("Price Source", "Candle price used by the averages and the daily comparison", "General");
_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()
{
_prevHma = null;
_currentDay = null;
_currentDayPrice = 0m;
_previousDayPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ichimoku = new Ichimoku
{
Tenkan = { Length = ConversionPeriod },
Kijun = { Length = BasePeriod },
SenkouB = { Length = SpanPeriod },
};
_hma = new HullMovingAverage { Length = HmaPeriod };
_macdFast = new HullMovingAverage { Length = MacdFastLength };
_macdSlow = new HullMovingAverage { Length = MacdSlowLength };
_macdSignal = new HullMovingAverage { Length = MacdSignalLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ichimoku);
DrawIndicator(area, _hma);
DrawOwnTrades(area);
}
}
private decimal GetPrice(ICandleMessage candle)
{
return PriceSource switch
{
PriceSources.High => candle.HighPrice,
PriceSources.Low => candle.LowPrice,
PriceSources.Close => candle.ClosePrice,
_ => candle.OpenPrice,
};
}
private void UpdateDaily(ICandleMessage candle)
{
var day = candle.OpenTime.Date;
if (_currentDay != day)
{
if (_currentDay != null)
_previousDayPrice = _currentDayPrice;
_currentDay = day;
_currentDayPrice = GetPrice(candle);
return;
}
// The daily candle is built from the intraday candles of the same day.
switch (PriceSource)
{
case PriceSources.Close:
_currentDayPrice = candle.ClosePrice;
break;
case PriceSources.High:
_currentDayPrice = Math.Max(_currentDayPrice, candle.HighPrice);
break;
case PriceSources.Low:
_currentDayPrice = Math.Min(_currentDayPrice, candle.LowPrice);
break;
}
}
private static decimal? ProcessValue(IIndicator indicator, decimal input, DateTime time)
{
var value = indicator.Process(new DecimalIndicatorValue(indicator, input, time) { IsFinal = true });
return indicator.IsFormed && !value.IsEmpty ? value.GetValue<decimal>() : null;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue)
{
if (candle.State != CandleStates.Finished)
return;
UpdateDaily(candle);
var price = GetPrice(candle);
var time = candle.OpenTime;
var hmaResult = ProcessValue(_hma, price, time);
var fast = ProcessValue(_macdFast, price, time);
var slow = ProcessValue(_macdSlow, price, time);
decimal? macd = fast is decimal f && slow is decimal s ? f - s : null;
var signal = macd is decimal m ? ProcessValue(_macdSignal, m, time) : null;
if (hmaResult is not decimal hma)
return;
var prevHma = _prevHma;
_prevHma = hma;
if (prevHma is not decimal previousHma || macd is not decimal macdLine || signal is not decimal signalLine)
return;
if (!ichimokuValue.IsFormed || ichimokuValue is not IchimokuValue { SenkouA: decimal senkouA, SenkouB: decimal senkouB })
return;
if (_previousDayPrice is not decimal previousDayPrice)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var longSignal = hma > previousHma && close > previousHma && _currentDayPrice > previousDayPrice && senkouA > senkouB && macdLine > signalLine;
var shortSignal = hma < previousHma && close < previousHma && _currentDayPrice < previousDayPrice && senkouA < senkouB && macdLine < signalLine;
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Ichimoku, HullMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
PRICE_OPEN = 0
PRICE_HIGH = 1
PRICE_LOW = 2
PRICE_CLOSE = 3
class ichimoku_daily_candle_x_hull_ma_x_macd_strategy(Strategy):
"""
Ichimoku daily candle X Hull MA X MACD strategy.
A Hull moving average of HmaPeriod and an HMA-based MACD (HMA(MacdFastLength) - HMA(MacdSlowLength), signal HMA(MacdSignalLength))
are computed on the PriceSource price. A long requires a rising HMA, the close above the previous HMA value, the current daily
candle's PriceSource price above the previous day's, Senkou A above Senkou B and the MACD line above its signal; a short requires
all of them reversed. The opposite signal reverses the position; there are no stops.
"""
def __init__(self):
super(ichimoku_daily_candle_x_hull_ma_x_macd_strategy, self).__init__()
self._hma_period = self.Param("HmaPeriod", 14).SetGreaterThanZero().SetDisplay("HMA Period", "Hull moving average period", "Indicators")
self._conversion_period = self.Param("ConversionPeriod", 9).SetGreaterThanZero().SetDisplay("Conversion Period", "Ichimoku conversion line period", "Ichimoku")
self._base_period = self.Param("BasePeriod", 26).SetGreaterThanZero().SetDisplay("Base Period", "Ichimoku base line period", "Ichimoku")
self._span_period = self.Param("SpanPeriod", 52).SetGreaterThanZero().SetDisplay("Span Period", "Ichimoku leading span B period", "Ichimoku")
self._macd_fast_length = self.Param("MacdFastLength", 12).SetGreaterThanZero().SetDisplay("MACD Fast", "Fast HMA period of the MACD", "MACD")
self._macd_slow_length = self.Param("MacdSlowLength", 26).SetGreaterThanZero().SetDisplay("MACD Slow", "Slow HMA period of the MACD", "MACD")
self._macd_signal_length = self.Param("MacdSignalLength", 9).SetGreaterThanZero().SetDisplay("MACD Signal", "Signal HMA period of the MACD", "MACD")
self._price_source = self.Param("PriceSource", PRICE_OPEN).SetDisplay("Price Source", "0 Open, 1 High, 2 Low, 3 Close", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._hma = None
self._macd_fast = None
self._macd_slow = None
self._macd_signal = None
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_hma = None
self._current_day = None
self._current_day_price = None
self._previous_day_price = None
def OnReseted(self):
super(ichimoku_daily_candle_x_hull_ma_x_macd_strategy, self).OnReseted()
self._reset_state()
@staticmethod
def _make_hma(length):
hma = HullMovingAverage()
hma.Length = length
return hma
def OnStarted2(self, time):
super(ichimoku_daily_candle_x_hull_ma_x_macd_strategy, self).OnStarted2(time)
self._reset_state()
ichimoku = Ichimoku()
ichimoku.Tenkan.Length = int(self._conversion_period.Value)
ichimoku.Kijun.Length = int(self._base_period.Value)
ichimoku.SenkouB.Length = int(self._span_period.Value)
self._hma = self._make_hma(int(self._hma_period.Value))
self._macd_fast = self._make_hma(int(self._macd_fast_length.Value))
self._macd_slow = self._make_hma(int(self._macd_slow_length.Value))
self._macd_signal = self._make_hma(int(self._macd_signal_length.Value))
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ichimoku, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ichimoku)
self.DrawIndicator(area, self._hma)
self.DrawOwnTrades(area)
def _get_price(self, candle):
source = int(self._price_source.Value)
if source == PRICE_HIGH:
return candle.HighPrice
if source == PRICE_LOW:
return candle.LowPrice
if source == PRICE_CLOSE:
return candle.ClosePrice
return candle.OpenPrice
def _update_daily(self, candle):
day = candle.OpenTime.Date
if self._current_day != day:
if self._current_day is not None:
self._previous_day_price = self._current_day_price
self._current_day = day
self._current_day_price = self._get_price(candle)
return
# The daily candle is built from the intraday candles of the same day.
source = int(self._price_source.Value)
if source == PRICE_CLOSE:
self._current_day_price = candle.ClosePrice
elif source == PRICE_HIGH:
self._current_day_price = max(self._current_day_price, candle.HighPrice)
elif source == PRICE_LOW:
self._current_day_price = min(self._current_day_price, candle.LowPrice)
@staticmethod
def _process(indicator, value, time):
result = process_value(indicator, value, time, True)
if indicator.IsFormed and not result.IsEmpty:
return to_decimal(result)
return None
def _process_candle(self, candle, ichimoku_value):
if candle.State != CandleStates.Finished:
return
self._update_daily(candle)
price = self._get_price(candle)
t = candle.OpenTime
hma = self._process(self._hma, price, t)
fast = self._process(self._macd_fast, price, t)
slow = self._process(self._macd_slow, price, t)
macd = fast - slow if fast is not None and slow is not None else None
signal = self._process(self._macd_signal, macd, t) if macd is not None else None
if hma is None:
return
prev_hma = self._prev_hma
self._prev_hma = hma
if prev_hma is None or macd is None or signal is None:
return
if not ichimoku_value.IsFormed or ichimoku_value.SenkouA is None or ichimoku_value.SenkouB is None:
return
if self._previous_day_price is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
senkou_a = ichimoku_value.SenkouA
senkou_b = ichimoku_value.SenkouB
close = candle.ClosePrice
day_price = self._current_day_price
prev_day_price = self._previous_day_price
long_signal = hma > prev_hma and close > prev_hma and day_price > prev_day_price and senkou_a > senkou_b and macd > signal
short_signal = hma < prev_hma and close < prev_hma and day_price < prev_day_price and senkou_a < senkou_b and macd < signal
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return ichimoku_daily_candle_x_hull_ma_x_macd_strategy()