Strategie Heatmap MACD
Heatmap MACD handelt, wenn MACD-Histogramme aus fünf Zeitrahmen übereinstimmen. Eine Long-Position wird eröffnet, wenn alle Histogramme positiv werden, und eine Short-Position, wenn alle negativ werden. Optional kann die Position geschlossen werden, wenn ein Histogramm gegen den Trade dreht.
Details
- Daten: Preiskerzen.
- Einstiegskriterien:
- Long: MACD-Histogramm > 0 auf allen fünf Zeitrahmen und zuvor nicht alle positiv.
- Short: MACD-Histogramm < 0 auf allen fünf Zeitrahmen und zuvor nicht alle negativ.
- Ausstiegskriterien: Entgegengesetztes Signal oder optionaler Schlusskurs bei Gegensignal.
- Stops: Standardmäßig keine.
- Standardwerte:
FastLength= 9SlowLength= 26SignalLength= 9TimeFrame1= tf(60)TimeFrame2= tf(120)TimeFrame3= tf(240)TimeFrame4= tf(240)TimeFrame5= tf(480)CloseOnOpposite= false
- Filter:
- Kategorie: Trend
- Richtung: Long & Short
- Indikatoren: MACD
- Stops: Nein
- Komplexität: Grundlegend
- Zeitrahmen: Multi-Zeitrahmen
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// Heatmap MACD strategy.
/// A MACD histogram (MACD minus signal) is calculated on each of five timeframes. When all five turn positive after not all
/// being positive the strategy goes long; when all five turn negative after not all being negative it goes short, reversing an
/// opposite position. With CloseOnOpposite a position is also closed as soon as any histogram flips against it.
/// </summary>
public class HeatmapMacdStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<DataType> _timeFrame1;
private readonly StrategyParam<DataType> _timeFrame2;
private readonly StrategyParam<DataType> _timeFrame3;
private readonly StrategyParam<DataType> _timeFrame4;
private readonly StrategyParam<DataType> _timeFrame5;
private readonly StrategyParam<bool> _closeOnOpposite;
private readonly decimal?[] _histograms = new decimal?[5];
private bool _wasAllPositive;
private bool _wasAllNegative;
/// <summary>
/// Fast EMA length of MACD.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Slow EMA length of MACD.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// Signal line length.
/// </summary>
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
/// <summary>
/// First timeframe.
/// </summary>
public DataType TimeFrame1
{
get => _timeFrame1.Value;
set => _timeFrame1.Value = value;
}
/// <summary>
/// Second timeframe.
/// </summary>
public DataType TimeFrame2
{
get => _timeFrame2.Value;
set => _timeFrame2.Value = value;
}
/// <summary>
/// Third timeframe.
/// </summary>
public DataType TimeFrame3
{
get => _timeFrame3.Value;
set => _timeFrame3.Value = value;
}
/// <summary>
/// Fourth timeframe.
/// </summary>
public DataType TimeFrame4
{
get => _timeFrame4.Value;
set => _timeFrame4.Value = value;
}
/// <summary>
/// Fifth timeframe.
/// </summary>
public DataType TimeFrame5
{
get => _timeFrame5.Value;
set => _timeFrame5.Value = value;
}
/// <summary>
/// Close the position when any histogram flips against it.
/// </summary>
public bool CloseOnOpposite
{
get => _closeOnOpposite.Value;
set => _closeOnOpposite.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HeatmapMacdStrategy()
{
_fastLength = Param(nameof(FastLength), 9)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast EMA length of MACD", "MACD");
_slowLength = Param(nameof(SlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow EMA length of MACD", "MACD");
_signalLength = Param(nameof(SignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("Signal Length", "Signal line length", "MACD");
_timeFrame1 = Param(nameof(TimeFrame1), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Timeframe 1", "First timeframe", "Timeframes");
_timeFrame2 = Param(nameof(TimeFrame2), TimeSpan.FromMinutes(120).TimeFrame())
.SetDisplay("Timeframe 2", "Second timeframe", "Timeframes");
_timeFrame3 = Param(nameof(TimeFrame3), TimeSpan.FromMinutes(240).TimeFrame())
.SetDisplay("Timeframe 3", "Third timeframe", "Timeframes");
_timeFrame4 = Param(nameof(TimeFrame4), TimeSpan.FromMinutes(240).TimeFrame())
.SetDisplay("Timeframe 4", "Fourth timeframe", "Timeframes");
_timeFrame5 = Param(nameof(TimeFrame5), TimeSpan.FromMinutes(480).TimeFrame())
.SetDisplay("Timeframe 5", "Fifth timeframe", "Timeframes");
_closeOnOpposite = Param(nameof(CloseOnOpposite), false)
.SetDisplay("Close On Opposite", "Close the position when any histogram flips against it", "Trading");
}
private DataType[] TimeFrames => [TimeFrame1, TimeFrame2, TimeFrame3, TimeFrame4, TimeFrame5];
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return TimeFrames.Distinct().Select(dt => (Security, dt));
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
Array.Clear(_histograms);
_wasAllPositive = false;
_wasAllNegative = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var timeFrames = TimeFrames;
for (var i = 0; i < timeFrames.Length; i++)
{
var index = i;
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength },
};
var subscription = SubscribeCandles(timeFrames[i]);
subscription
.BindEx(macd, (candle, value) => ProcessCandle(index, candle, value))
.Start();
if (i == 0)
{
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
}
}
private void ProcessCandle(int index, ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!value.IsFormed || value is not IMovingAverageConvergenceDivergenceSignalValue { Macd: decimal macd, Signal: decimal signal })
return;
_histograms[index] = macd - signal;
if (_histograms.Any(h => h is null))
return;
var allPositive = _histograms.All(h => h > 0);
var allNegative = _histograms.All(h => h < 0);
var anyPositive = _histograms.Any(h => h > 0);
var anyNegative = _histograms.Any(h => h < 0);
var wasAllPositive = _wasAllPositive;
var wasAllNegative = _wasAllNegative;
_wasAllPositive = allPositive;
_wasAllNegative = allNegative;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (allPositive && !wasAllPositive && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
}
else if (allNegative && !wasAllNegative && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
}
else if (CloseOnOpposite)
{
if (Position > 0 && anyNegative)
SellMarket(Position);
else if (Position < 0 && anyPositive)
BuyMarket(-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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class heatmap_macd_strategy(Strategy):
"""
Heatmap MACD strategy.
A MACD histogram (MACD minus signal) is calculated on each of five timeframes. When all five turn positive after not all
being positive the strategy goes long; when all five turn negative after not all being negative it goes short, reversing an
opposite position. With CloseOnOpposite a position is also closed as soon as any histogram flips against it.
"""
def __init__(self):
super(heatmap_macd_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 9).SetGreaterThanZero().SetDisplay("Fast Length", "Fast EMA length of MACD", "MACD")
self._slow_length = self.Param("SlowLength", 26).SetGreaterThanZero().SetDisplay("Slow Length", "Slow EMA length of MACD", "MACD")
self._signal_length = self.Param("SignalLength", 9).SetGreaterThanZero().SetDisplay("Signal Length", "Signal line length", "MACD")
self._time_frame1 = self.Param("TimeFrame1", DataType.TimeFrame(TimeSpan.FromMinutes(60))).SetDisplay("Timeframe 1", "First timeframe", "Timeframes")
self._time_frame2 = self.Param("TimeFrame2", DataType.TimeFrame(TimeSpan.FromMinutes(120))).SetDisplay("Timeframe 2", "Second timeframe", "Timeframes")
self._time_frame3 = self.Param("TimeFrame3", DataType.TimeFrame(TimeSpan.FromMinutes(240))).SetDisplay("Timeframe 3", "Third timeframe", "Timeframes")
self._time_frame4 = self.Param("TimeFrame4", DataType.TimeFrame(TimeSpan.FromMinutes(240))).SetDisplay("Timeframe 4", "Fourth timeframe", "Timeframes")
self._time_frame5 = self.Param("TimeFrame5", DataType.TimeFrame(TimeSpan.FromMinutes(480))).SetDisplay("Timeframe 5", "Fifth timeframe", "Timeframes")
self._close_on_opposite = self.Param("CloseOnOpposite", False).SetDisplay("Close On Opposite", "Close the position when any histogram flips against it", "Trading")
self._reset_state()
def _time_frames(self):
return [self._time_frame1.Value, self._time_frame2.Value, self._time_frame3.Value, self._time_frame4.Value, self._time_frame5.Value]
def GetWorkingSecurities(self):
result = []
for dt in self._time_frames():
if dt not in [x[1] for x in result]:
result.append((self.Security, dt))
return result
def _reset_state(self):
self._histograms = [None] * 5
self._was_all_positive = False
self._was_all_negative = False
def OnReseted(self):
super(heatmap_macd_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(heatmap_macd_strategy, self).OnStarted2(time)
self._reset_state()
for i, tf in enumerate(self._time_frames()):
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._fast_length.Value
macd.Macd.LongMa.Length = self._slow_length.Value
macd.SignalMa.Length = self._signal_length.Value
subscription = self.SubscribeCandles(tf)
subscription.BindEx(macd, self._make_handler(i)).Start()
if i == 0:
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _make_handler(self, index):
return lambda candle, value: self._process_candle(index, candle, value)
def _process_candle(self, index, candle, value):
if candle.State != CandleStates.Finished:
return
if not value.IsFormed or value.Macd is None or value.Signal is None:
return
self._histograms[index] = float(value.Macd) - float(value.Signal)
if any(h is None for h in self._histograms):
return
all_positive = all(h > 0 for h in self._histograms)
all_negative = all(h < 0 for h in self._histograms)
any_positive = any(h > 0 for h in self._histograms)
any_negative = any(h < 0 for h in self._histograms)
was_all_positive = self._was_all_positive
was_all_negative = self._was_all_negative
self._was_all_positive = all_positive
self._was_all_negative = all_negative
if not self.IsFormedAndOnlineAndAllowTrading():
return
if all_positive and not was_all_positive and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif all_negative and not was_all_negative and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self._close_on_opposite.Value:
if self.Position > 0 and any_negative:
self.SellMarket(self.Position)
elif self.Position < 0 and any_positive:
self.BuyMarket(-self.Position)
def CreateClone(self):
return heatmap_macd_strategy()