McGinley Dynamic (Verbessert)-Strategie
Implementiert den Indikator "McGinley Dynamic (Improved)" von John R. McGinley, Jr. und handelt, wenn der Schlusskurs die dynamische Linie kreuzt. Die Strategie unterstützt moderne, originale und benutzerdefinierte Koeffizientenformeln und kann optional die unkontrollierte Variante zum Vergleich anzeigen.
Details
- Einstieg Long: Schlusskurs kreuzt McGinley Dynamic nach oben.
- Einstieg Short: Schlusskurs kreuzt McGinley Dynamic nach unten.
- Indikatoren: McGinley Dynamic, optionales Unconstrained McGinley Dynamic, EMA als Referenz.
- Standardwerte: Period = 14, Formula = Modern, Custom k = 0.5, Exponent = 4.
- Richtung: Beide.
using System;
using System.Linq;
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;
public class McGinleyDynamicImprovedStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _signalThresholdPercent;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal? _mdPrev;
private ExponentialMovingAverage _ema;
private decimal _previousDiff;
private bool _hasPreviousDiff;
private int _barsFromSignal;
public int Period { get => _period.Value; set => _period.Value = value; }
public decimal SignalThresholdPercent { get => _signalThresholdPercent.Value; set => _signalThresholdPercent.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public McGinleyDynamicImprovedStrategy()
{
_period = Param(nameof(Period), 20)
.SetGreaterThanZero()
.SetDisplay("Period", "McGinley base period", "General");
_signalThresholdPercent = Param(nameof(SignalThresholdPercent), 0.25m)
.SetGreaterThanZero()
.SetDisplay("Signal Threshold %", "Minimum distance from McGinley in percent", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 10)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candles timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_mdPrev = null;
_ema = null;
_previousDiff = 0m;
_hasPreviousDiff = false;
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_ema = new ExponentialMovingAverage { Length = Period };
_mdPrev = null;
_previousDiff = 0m;
_hasPreviousDiff = false;
_barsFromSignal = SignalCooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_ema.IsFormed)
return;
var close = candle.ClosePrice;
// Calculate McGinley Dynamic
decimal md;
if (_mdPrev == null)
{
md = close;
}
else
{
var prev = _mdPrev.Value;
if (prev == 0m) prev = close;
var k = 0.6m;
var period = (decimal)Period;
var ratio = close / prev;
var pow = (decimal)Math.Pow((double)ratio, 4.0);
var denom = k * period * pow;
if (denom == 0m) denom = 1m;
md = prev + (close - prev) / denom;
}
_mdPrev = md;
if (close <= 0m)
return;
var diff = (close - md) / close * 100m;
var threshold = SignalThresholdPercent;
var crossedUp = _hasPreviousDiff && _previousDiff <= threshold && diff > threshold;
var crossedDown = _hasPreviousDiff && _previousDiff >= -threshold && diff < -threshold;
_previousDiff = diff;
_hasPreviousDiff = true;
_barsFromSignal++;
if (_barsFromSignal < SignalCooldownBars)
return;
if (crossedUp && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (crossedDown && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
}
}
import clr
import math
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class mcginley_dynamic_improved_strategy(Strategy):
def __init__(self):
super(mcginley_dynamic_improved_strategy, self).__init__()
self._period = self.Param("Period", 20) \
.SetGreaterThanZero() \
.SetDisplay("Period", "McGinley base period", "General")
self._signal_threshold_percent = self.Param("SignalThresholdPercent", 0.25) \
.SetGreaterThanZero() \
.SetDisplay("Signal Threshold %", "Minimum distance from McGinley in percent", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 10) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candles timeframe", "General")
self._md_prev = None
self._previous_diff = 0.0
self._has_previous_diff = False
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(mcginley_dynamic_improved_strategy, self).OnReseted()
self._md_prev = None
self._previous_diff = 0.0
self._has_previous_diff = False
self._bars_from_signal = 0
def OnStarted2(self, time):
super(mcginley_dynamic_improved_strategy, self).OnStarted2(time)
self._md_prev = None
self._previous_diff = 0.0
self._has_previous_diff = False
self._bars_from_signal = self._signal_cooldown_bars.Value
self._ema = ExponentialMovingAverage()
self._ema.Length = self._period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self.OnProcess).Start()
def OnProcess(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
return
close = float(candle.ClosePrice)
if self._md_prev is None:
md = close
else:
prev = self._md_prev
if prev == 0.0:
prev = close
k = 0.6
period = float(self._period.Value)
ratio = close / prev if prev != 0.0 else 1.0
pw = math.pow(ratio, 4.0)
denom = k * period * pw
if denom == 0.0:
denom = 1.0
md = prev + (close - prev) / denom
self._md_prev = md
if close <= 0.0:
return
diff = (close - md) / close * 100.0
threshold = float(self._signal_threshold_percent.Value)
crossed_up = self._has_previous_diff and self._previous_diff <= threshold and diff > threshold
crossed_down = self._has_previous_diff and self._previous_diff >= -threshold and diff < -threshold
self._previous_diff = diff
self._has_previous_diff = True
self._bars_from_signal += 1
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal < cd:
return
if crossed_up and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif crossed_down and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
def CreateClone(self):
return mcginley_dynamic_improved_strategy()