View Full Version : Hurst Bands
yimbot
12-10-2008, 05:41 AM
I am looking at implementing a new indicator based on the Cycle Envelopes from JM Hursts "The Magic of Stock Transaction Timing"
In truth, the bands can be approximated fairly well by utilising the Keltner Channel with offsets. My current settings are:
1st Band
Offset Multiplier 1.5
Period 10
Displacement -5
2nd Band
Offset Multiplier 2.5
Period 30
Displacement -15
This draws fairly nice channels, but lacks the smoothness of a hand drawn envelope and also does not allow for projection to the current bar. Generally, the projection would be drawn by hand and follow an average of the cycles timing. This is where I am at a loss as to how to code this...
Has anybody tackled this so far?
I have seen that Omnitrader have recently released a plug in for it, but I am not convinced. A nice looking set of tools has been developed for Esignal and the info can be found here:
http://www.sr-analyst.com/cyclePackHelp/cyclePack.html
They point out that they are using double-smoothed averages for the envelopes with "linear projection" for the extension of the band to the current bar.
I am not sure if there is already a double-smoothed average that I could use to modify the Keltner bands as a start? Really, the big quetion is how to extend the bands to now taking into account an average of the previous highs/lows for the period and following the already visible curve.
Any ideas or advice would be great
NinjaTrader_Bertrand
12-10-2008, 06:07 AM
Hi yimbot,
Thanks for your post, great book you reference there.
There is a lot of stuff on the subject, because shifted back MA's look to sweet...what may work best for trading, is to code a shifted back MA and extrapolate using normal trendlines.
Linear prediction is a great concept and you find lots of algorithms in Numerical Recipes and I think also Ehlers published some on that topic.
I saw some people using Polynomial regression, too - but the noise can make it hard to justify the coding efforts.
For another Keltner base, I would suggest looking into the Triangular MA in NinjaTrader - http://www.ninjatrader-support.com/HelpGuideV6/MovingAverage-TriangularTMA.html
yimbot
12-10-2008, 06:39 AM
For another Keltner base, I would suggest looking into the Triangular MA in NinjaTrader - http://www.ninjatrader-support.com/HelpGuideV6/MovingAverage-TriangularTMA.html
Thanks Bertrand,
I set up the code to use the TMA and it is a beautiful thing - thanks for the tip!
To date, I have as you suggest just been using parallel trend lines for the projections and the fibonacci time tool set to multiples of 100% to try and get an average of the time cycles with. Obviously if I can project it with the indicator it would be much nicer, but am honestly way out of my depth.
Thanks again.
yimbot
12-10-2008, 06:46 AM
For anyone following this thread who may be interested in the code, here it is:
(Please forgive my poor modification of the original NT code - very lazy)
//
// Copyright (C) 2006, NinjaTrader LLC <www.ninjatrader.com>.
// NinjaTrader reserves the right to modify or overwrite this NinjaScript component with each release.
//
#region Using declarations
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Xml.Serialization;
using NinjaTrader.Data;
using NinjaTrader.Gui.Chart;
#endregion
// This namespace holds all indicators and is required. Do not change it.
namespace NinjaTrader.Indicator
{
/// <summary>
///
/// </summary>
[Description("The Hurst Envelope is a similar indicator to Bollinger Bands. Here the midline is a standard moving average with the upper and lower bands offset by the SMA of the difference between the high and low of the previous bars. The offset multiplier as well as the SMA period is configurable.")]
public class HurstEnvelope : Indicator
{
#region Variables
private int period = 10;
private double offsetMultiplier = 1.5;
private DataSeries diff;
#endregion
/// <summary>
/// This method is used to configure the indicator and is called once before any bar data is loaded.
/// </summary>
protected override void Initialize()
{
Add(new Plot(Color.Transparent, "Midline"));
Add(new Plot(Color.Gray, "Upper"));
Add(new Plot(Color.Gray, "Lower"));
diff = new DataSeries(this);
Overlay = true;
PriceTypeSupported = false;
}
/// <summary>
/// Called on each bar update event (incoming tick).
/// </summary>
protected override void OnBarUpdate()
{
diff.Set(High[0] - Low[0]);
double middle = TMA(Median, Period)[0];
double offset = TMA(diff, Period)[0] * offsetMultiplier;
double upper = middle + offset;
double lower = middle - offset;
Midline.Set(middle);
Upper.Set(upper);
Lower.Set(lower);
}
#region Properties
/// <summary>
/// </summary>
[Description("Numbers of bars used for calculations")]
[Category("Parameters")]
public int Period
{
get { return period; }
set { period = Math.Max(1, value); }
}
/// <summary>
/// </summary>
[Description("How much to expand the upper and lower band from the normal offset")]
[Category("Parameters")]
[Gui.Design.DisplayNameAttribute("Offset multiplier")]
public double OffsetMultiplier
{
get { return offsetMultiplier; }
set { offsetMultiplier = Math.Max(0.01, value); }
}
/// <summary>
/// </summary>
[Browsable(false)]
[XmlIgnore()]
public DataSeries Midline
{
get { return Values[0]; }
}
/// <summary>
/// </summary>
[Browsable(false)]
[XmlIgnore()]
public DataSeries Upper
{
get { return Values[1]; }
}
/// <summary>
/// </summary>
[Browsable(false)]
[XmlIgnore()]
public DataSeries Lower
{
get { return Values[2]; }
}
#endregion
}
}
#region NinjaScript generated code. Neither change nor remove.
// This namespace holds all indicators and is required. Do not change it.
namespace NinjaTrader.Indicator
{
public partial class Indicator : IndicatorBase
{
private HurstEnvelope[] cacheHurstEnvelope = null;
private static HurstEnvelope checkHurstEnvelope = new HurstEnvelope();
/// <summary>
/// The Hurst Envelope is a similar indicator to Bollinger Bands. Here the midline is a standard moving average with the upper and lower bands offset by the SMA of the difference between the high and low of the previous bars. The offset multiplier as well as the SMA period is configurable.
/// </summary>
/// <returns></returns>
public HurstEnvelope HurstEnvelope(double offsetMultiplier, int period)
{
return HurstEnvelope(Input, offsetMultiplier, period);
}
/// <summary>
/// The Hurst Envelope is a similar indicator to Bollinger Bands. Here the midline is a standard moving average with the upper and lower bands offset by the SMA of the difference between the high and low of the previous bars. The offset multiplier as well as the SMA period is configurable.
/// </summary>
/// <returns></returns>
public HurstEnvelope HurstEnvelope(Data.IDataSeries input, double offsetMultiplier, int period)
{
checkHurstEnvelope.OffsetMultiplier = offsetMultiplier;
offsetMultiplier = checkHurstEnvelope.OffsetMultiplier;
checkHurstEnvelope.Period = period;
period = checkHurstEnvelope.Period;
if (cacheHurstEnvelope != null)
for (int idx = 0; idx < cacheHurstEnvelope.Length; idx++)
if (Math.Abs(cacheHurstEnvelope[idx].OffsetMultiplier - offsetMultiplier) <= double.Epsilon && cacheHurstEnvelope[idx].Period == period && cacheHurstEnvelope[idx].EqualsInput(input))
return cacheHurstEnvelope[idx];
HurstEnvelope indicator = new HurstEnvelope();
indicator.BarsRequired = BarsRequired;
indicator.CalculateOnBarClose = CalculateOnBarClose;
indicator.Input = input;
indicator.OffsetMultiplier = offsetMultiplier;
indicator.Period = period;
indicator.SetUp();
HurstEnvelope[] tmp = new HurstEnvelope[cacheHurstEnvelope == null ? 1 : cacheHurstEnvelope.Length + 1];
if (cacheHurstEnvelope != null)
cacheHurstEnvelope.CopyTo(tmp, 0);
tmp[tmp.Length - 1] = indicator;
cacheHurstEnvelope = tmp;
Indicators.Add(indicator);
return indicator;
}
}
}
// This namespace holds all market analyzer column definitions and is required. Do not change it.
namespace NinjaTrader.MarketAnalyzer
{
public partial class Column : ColumnBase
{
/// <summary>
/// The Hurst Envelope is a similar indicator to Bollinger Bands. Here the midline is a standard moving average with the upper and lower bands offset by the SMA of the difference between the high and low of the previous bars. The offset multiplier as well as the SMA period is configurable.
/// </summary>
/// <returns></returns>
[Gui.Design.WizardCondition("Indicator")]
public Indicator.HurstEnvelope HurstEnvelope(double offsetMultiplier, int period)
{
return _indicator.HurstEnvelope(Input, offsetMultiplier, period);
}
/// <summary>
/// The Hurst Envelope is a similar indicator to Bollinger Bands. Here the midline is a standard moving average with the upper and lower bands offset by the SMA of the difference between the high and low of the previous bars. The offset multiplier as well as the SMA period is configurable.
/// </summary>
/// <returns></returns>
public Indicator.HurstEnvelope HurstEnvelope(Data.IDataSeries input, double offsetMultiplier, int period)
{
return _indicator.HurstEnvelope(input, offsetMultiplier, period);
}
}
}
// This namespace holds all strategies and is required. Do not change it.
namespace NinjaTrader.Strategy
{
public partial class Strategy : StrategyBase
{
/// <summary>
/// The Hurst Envelope is a similar indicator to Bollinger Bands. Here the midline is a standard moving average with the upper and lower bands offset by the SMA of the difference between the high and low of the previous bars. The offset multiplier as well as the SMA period is configurable.
/// </summary>
/// <returns></returns>
[Gui.Design.WizardCondition("Indicator")]
public Indicator.HurstEnvelope HurstEnvelope(double offsetMultiplier, int period)
{
return _indicator.HurstEnvelope(Input, offsetMultiplier, period);
}
/// <summary>
/// The Hurst Envelope is a similar indicator to Bollinger Bands. Here the midline is a standard moving average with the upper and lower bands offset by the SMA of the difference between the high and low of the previous bars. The offset multiplier as well as the SMA period is configurable.
/// </summary>
/// <returns></returns>
public Indicator.HurstEnvelope HurstEnvelope(Data.IDataSeries input, double offsetMultiplier, int period)
{
if (InInitialize && input == null)
throw new ArgumentException("You only can access an indicator with the default input/bar series from within the 'Initialize()' method");
return _indicator.HurstEnvelope(input, offsetMultiplier, period);
}
}
}
#endregion
NinjaTrader_Bertrand
12-10-2008, 06:56 AM
Thanks for the code and the screenshot, I feel our forum members will appreciate input on this interesting techniques.
Elliott Wave
12-10-2008, 06:58 AM
Here is a compiled version of the code posted below.
yimbot
12-11-2008, 05:16 AM
I wonder if someone may assist with a coding error here.
My aim is to draw a line between the current value of the displaced Hurst Envelope and the current value of a non-displaced Keltner Channel which is half the period of the Hurst Envelope but the same Offset multiplier.
So to clarify, at the end of the code for the Hurst Indicator above, I want to just draw a straight line between:
a) the current value of HurstEnvelope(Offset, Period, Displacement)
b) the current value of KeltnerChannel(Offset, (Period/2), no Displacement)
The code I have is as follows which compiles ok, but plots nothing at all. If I comment out the DrawLine Lines of code, the indicator once again displays. So I am not sure where the error is. All comments appreciated.
//
//
#region Using declarations
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Xml.Serialization;
using NinjaTrader.Data;
using NinjaTrader.Gui.Chart;
#endregion
// This namespace holds all indicators and is required. Do not change it.
namespace NinjaTrader.Indicator
{
/// <summary>
///
/// </summary>
[Description("The Hurst Envelope is described in J.M Hursts 'The Profit Magic of Stock Transaction Timing'")]
public class HurstEnvelope : Indicator
{
#region Variables
private int period = 10;
private double offsetMultiplier = 1.5;
private DataSeries diff;
#endregion
/// <summary>
/// This method is used to configure the indicator and is called once before any bar data is loaded.
/// </summary>
protected override void Initialize()
{
Add(new Plot(Color.Transparent, "Midline"));
Add(new Plot(Color.Gray, "Upper"));
Add(new Plot(Color.Gray, "Lower"));
diff = new DataSeries(this);
Overlay = true;
PriceTypeSupported = false;
}
/// <summary>
/// Called on each bar update event (incoming tick).
/// </summary>
protected override void OnBarUpdate()
{
diff.Set(High[0] - Low[0]);
double middle = TMA(Median, Period)[0];
double offset = TMA(diff, Period)[0] * offsetMultiplier;
double upper = middle + offset;
double lower = middle - offset;
KeltnerChannel env = KeltnerChannel(Median, offsetMultiplier, (period/2));
Midline.Set(middle);
Upper.Set(upper);
Lower.Set(lower);
///DrawLine(CurrentBar.ToString()+"UPPER", true, (period/2), upper, 0, env.Upper[0], Color.Cyan, DashStyle.Dot, 1);
///DrawLine(CurrentBar.ToString()+"LOWER", true, (period/2), lower, 0, env.Lower[0], Color.Cyan, DashStyle.Dot, 1);
}
#region Properties
/// <summary>
/// </summary>
[Description("Numbers of bars used for calculations")]
[Category("Parameters")]
public int Period
{
get { return period; }
set { period = Math.Max(1, value); }
}
/// <summary>
/// </summary>
[Description("How much to expand the upper and lower band from the normal offset")]
[Category("Parameters")]
[Gui.Design.DisplayNameAttribute("Offset multiplier")]
public double OffsetMultiplier
{
get { return offsetMultiplier; }
set { offsetMultiplier = Math.Max(0.01, value); }
}
/// <summary>
/// </summary>
[Browsable(false)]
[XmlIgnore()]
public DataSeries Midline
{
get { return Values[0]; }
}
/// <summary>
/// </summary>
[Browsable(false)]
[XmlIgnore()]
public DataSeries Upper
{
get { return Values[1]; }
}
/// <summary>
/// </summary>
[Browsable(false)]
[XmlIgnore()]
public DataSeries Lower
{
get { return Values[2]; }
}
#endregion
}
}
#region NinjaScript generated code. Neither change nor remove.
// This namespace holds all indicators and is required. Do not change it.
namespace NinjaTrader.Indicator
{
public partial class Indicator : IndicatorBase
{
private HurstEnvelope[] cacheHurstEnvelope = null;
private static HurstEnvelope checkHurstEnvelope = new HurstEnvelope();
/// <summary>
/// The Hurst Envelope is described in J.M Hursts 'The Profit Magic of Stock Transaction Timing'
/// </summary>
/// <returns></returns>
public HurstEnvelope HurstEnvelope(double offsetMultiplier, int period)
{
return HurstEnvelope(Input, offsetMultiplier, period);
}
/// <summary>
/// The Hurst Envelope is described in J.M Hursts 'The Profit Magic of Stock Transaction Timing'
/// </summary>
/// <returns></returns>
public HurstEnvelope HurstEnvelope(Data.IDataSeries input, double offsetMultiplier, int period)
{
checkHurstEnvelope.OffsetMultiplier = offsetMultiplier;
offsetMultiplier = checkHurstEnvelope.OffsetMultiplier;
checkHurstEnvelope.Period = period;
period = checkHurstEnvelope.Period;
if (cacheHurstEnvelope != null)
for (int idx = 0; idx < cacheHurstEnvelope.Length; idx++)
if (Math.Abs(cacheHurstEnvelope[idx].OffsetMultiplier - offsetMultiplier) <= double.Epsilon && cacheHurstEnvelope[idx].Period == period && cacheHurstEnvelope[idx].EqualsInput(input))
return cacheHurstEnvelope[idx];
HurstEnvelope indicator = new HurstEnvelope();
indicator.BarsRequired = BarsRequired;
indicator.CalculateOnBarClose = CalculateOnBarClose;
indicator.Input = input;
indicator.OffsetMultiplier = offsetMultiplier;
indicator.Period = period;
indicator.SetUp();
HurstEnvelope[] tmp = new HurstEnvelope[cacheHurstEnvelope == null ? 1 : cacheHurstEnvelope.Length + 1];
if (cacheHurstEnvelope != null)
cacheHurstEnvelope.CopyTo(tmp, 0);
tmp[tmp.Length - 1] = indicator;
cacheHurstEnvelope = tmp;
Indicators.Add(indicator);
return indicator;
}
}
}
// This namespace holds all market analyzer column definitions and is required. Do not change it.
namespace NinjaTrader.MarketAnalyzer
{
public partial class Column : ColumnBase
{
/// <summary>
/// The Hurst Envelope is described in J.M Hursts 'The Profit Magic of Stock Transaction Timing'
/// </summary>
/// <returns></returns>
[Gui.Design.WizardCondition("Indicator")]
public Indicator.HurstEnvelope HurstEnvelope(double offsetMultiplier, int period)
{
return _indicator.HurstEnvelope(Input, offsetMultiplier, period);
}
/// <summary>
/// The Hurst Envelope is described in J.M Hursts 'The Profit Magic of Stock Transaction Timing'
/// </summary>
/// <returns></returns>
public Indicator.HurstEnvelope HurstEnvelope(Data.IDataSeries input, double offsetMultiplier, int period)
{
return _indicator.HurstEnvelope(input, offsetMultiplier, period);
}
}
}
// This namespace holds all strategies and is required. Do not change it.
namespace NinjaTrader.Strategy
{
public partial class Strategy : StrategyBase
{
/// <summary>
/// The Hurst Envelope is described in J.M Hursts 'The Profit Magic of Stock Transaction Timing'
/// </summary>
/// <returns></returns>
[Gui.Design.WizardCondition("Indicator")]
public Indicator.HurstEnvelope HurstEnvelope(double offsetMultiplier, int period)
{
return _indicator.HurstEnvelope(Input, offsetMultiplier, period);
}
/// <summary>
/// The Hurst Envelope is described in J.M Hursts 'The Profit Magic of Stock Transaction Timing'
/// </summary>
/// <returns></returns>
public Indicator.HurstEnvelope HurstEnvelope(Data.IDataSeries input, double offsetMultiplier, int period)
{
if (InInitialize && input == null)
throw new ArgumentException("You only can access an indicator with the default input/bar series from within the 'Initialize()' method");
return _indicator.HurstEnvelope(input, offsetMultiplier, period);
}
}
}
#endregion
NinjaTrader_Bertrand
12-11-2008, 05:31 AM
Hi yimbot,
Please add this check at the start of your OnBarUpdate() -
if (CurrentBar < Period)
return;
This should work then, also please see this tip - http://www.ninjatrader-support2.com/vb/showthread.php?t=3170
yimbot
12-11-2008, 05:32 AM
I wonder if someone may assist with a coding error here.
My aim is to draw a line between the current value of the displaced Hurst Envelope and the current value of a non-displaced Keltner Channel which is half the period of the Hurst Envelope but the same Offset multiplier.
So to clarify, at the end of the code for the Hurst Indicator above, I want to just draw a straight line between:
a) the current value of HurstEnvelope(Offset, Period, Displacement)
b) the current value of KeltnerChannel(Offset, (Period/2), no Displacement)
Alternatively, even just drawing a ray from the HurstEnvelope 1 bar ago through the current HurstEnvelope that projects into the future (preferably only as far as the current bar) would still be useful and dynamic. This may be easier to code...
yimbot
12-11-2008, 05:53 AM
Thanks once again for your help Bertrand. That worked nicely
The indicator now has some level of projection and although not very sophisticated, it does provide some level of assistance.
I have attached this code for anyone interested.
NinjaTrader_Bertrand
12-11-2008, 05:59 AM
You are welcome yimbot, great it works!
yimbot
12-11-2008, 06:10 AM
Here is a screenshot on the 5min Euro with the indicator displayed, also shows a couple of trades taken...
ryker
12-11-2008, 11:33 AM
Got the following exception while trying to import the indicator, any ideas?
Thanks
NinjaTrader_Bertrand
12-11-2008, 11:52 AM
Hi ryker,
Which NinjaTrader version are you using? The .ntns format is not supported anymore, make sure you are on NinjaTrader 6.5.1000.8 (check under Help > About)
ryker
12-11-2008, 11:55 AM
Right, I'm using .7, I'll update my NT and see if it works afterwards.
Thanks
ryker
12-11-2008, 12:02 PM
I'm on .8 now and it still doesn't work unfortunately...
NinjaTrader_Bertrand
12-11-2008, 12:08 PM
Hi ryker,
Then please try to import the indicator manually.
Open the downloaded zip file and extract the .cs file to the MyDocuments\NinjaTrader 6.5\bin\Custom\Indicator folder.
ryker
12-11-2008, 01:59 PM
Doesn't work, there are lots of errors... Did you manage to import this indicator successfully?
Thanks
NinjaTrader_Josh
12-11-2008, 02:08 PM
ryker,
You will have to take up the issue with yimbot. You will want to request him to export it from the latest NinjaTrader version. It may be that he has exported from an older incompatible version of NT. If you want to view the code you will also want to request him to not export it as a compiled assembly.
styr_trader
12-11-2008, 02:21 PM
can't import to try
NinjaTrader_Josh
12-11-2008, 02:33 PM
There is nothing I can do. You guys need to contact yimbot to update his version to a compatible version.
ryker
12-11-2008, 02:55 PM
Ok thanks, I originally thought I was the only one that could not use it.
yimbot
12-12-2008, 05:04 PM
try one of these
ryker
12-13-2008, 10:51 AM
Thanks, first one is working ok.
J_o_s
07-05-2010, 09:44 AM
Thanks for sharing this indicator Yimbot, quite fascinating. :)
I'm just trying to use the 'DrawLine()' function to draw other "projecting" lines, such as for a Displaced EMA. So far, no good:
protected override void OnBarUpdate()
{
Value.Set(CurrentBar == 0 ? Input[0] : Input[0] * (2.0 / (1 + Period)) + (1 - (2.0 / (1 + Period))) * Value[1]);
DrawLine("Projection", true, (period/2), Value[1], 0, Value[1], Color.Cyan, DashStyle.Dot, 2);
}
This doesn't give an error message, but doesn't plot the indicator either. Anyone some tips or suggestions?
Regards,
eDanny
07-05-2010, 10:03 AM
Try this:
if(CurrentBar > period/2)
DrawLine("Projection", true, (period/2), Value[1], 0, Value[1], Color.Cyan, DashStyle.Dot, 2);
J_o_s
07-05-2010, 12:06 PM
Try this:
if(CurrentBar > period/2)
DrawLine("Projection", true, (period/2), Value[1], 0, Value[1], Color.Cyan, DashStyle.Dot, 2);
Thanks Danny! :) The lines (EMA and projection) are now both plotted, now I can go on with tweaking the DrawLine, still not correctly drawn. Thanks for helping me out!
Regards,
chriscolo
05-04-2011, 07:37 AM
Hello. Would it be possible to please share a template based on this compiled indicator or preferred settings for the indicator including forex pair and time frame? Alternatively, is there an improved version of the Hurst Cycle or Hurst Envelope indicator that someone could share or point me to?
Please see post 6 or 23 of this thread.
Thank you.
Chris
Here is a compiled version of the code posted below.
DavidHP
10-15-2011, 12:03 PM
The DrawLines are skewed from normal on this indicator.
Or is the normal display?
Is there an update for NT7?
Thanks