November 2000
TRADERS' TIPS 

Here is this month's selection of Traders' Tips, contributed by various developers of technical analysis software to help readers more easily implement some of the strategies presented in this issue.

You can copy these formulas and programs for easy use in your spreadsheet or analysis software. Simply "select" the desired text by highlighting as you would in any word processing program, then use your standard key command for copy or choose "copy" from the browser menu. The copied text can then be "pasted" into any open spreadsheet or other software by selecting an insertion point and executing a paste command. By toggling back and forth between an application window and the open Web page, data can be transferred with ease.
 

This month's tips include formulas and programs for:
 
 
HILBERT CHANNEL BREAKOUT INDICATOR
REFINING THE HILBERT INDICATOR
HILBERT CYCLE PERIOD FUNCTION
HILBERT CHANNEL BREAKOUT SIGNAL
EASYLANGUAGE
NEUROSHELL TRADER
SMARTTRADER
WAVEWI$E MARKET SPREADSHEET

or return to November 2000 Contents


HILBERT CHANNEL BREAKOUT INDICATOR

Here's a simple channel breakout system in EasyLanguage code, as discussed in my article in this issue, "Optimizing With Hilbert Indicators." This system incorporates adaptive filters that adjust moving averages to reflect actual market conditions. I got the idea from working with John Ehlers's Mesa software, but the idea works just as well with the indicators that he published in STOCKS & COMMODITIES.

This indicator will show on the TradeStation screen how the adaptive filter is being applied to your trading strategy. The indicator works the same way the signal does (see Traders' Tip on page 108, "Hilbert Channel Breakout Signal"), except instead of generating buy and exit signals, it plots your strategy on the screen.

{***********************************************
Hilbert_Channel (Indicator)

This is the indicator for the Hilbert Channel Breakout System.

After the Hilbert Channel Breakout Signal is optimized, set the inputs for this indicator to match the corresponding inputs for the signal. Also set the "Max number of bars the study will reference" on the indicator's Properties tab to the corresponding value on the Properties tab for the signal.

Plot1 and plot2 should be lines; plot3 and plot 4 should be medium-thin points.
*********************************************** }

inputs: Price((H + L)/2),
        EntryVal(0),
        EntryK(0),
        ExitVal(0),
        ExitK(0);

vars:   Period(0),
        count(0),
        EntryLookBack(0),
        ExitLookBack(0),
        EntryChannel(0),
        ExitChannel(0);

Period = HilbertPeriod(Price);

If EntryVal <> 0 then EntryLookBack = EntryVal else EntryLookBack = EntryK*Period;
if EntryLookBack < 1 then EntryLookBack = 1;

If ExitVal <> 0 then ExitLookBack = ExitVal else ExitLookBack = ExitK*Period;
if ExitLookBack < 1 then ExitLookBack = 1;

EntryChannel = 0;
for count = 1 to EntryLookBack begin
        if EntryChannel < High[count] then EntryChannel = High[count];
end;

ExitChannel = 100000;
for count = 1 to ExitLookBack begin
        if ExitChannel > Low[count] then ExitChannel = Low[count];
end;

plot1(EntryChannel, "EntryChannel");
plot2(ExitChannel, "ExitChannel");

If High > EntryChannel then plot3(EntryChannel, "Enter");
If Low < ExitChannel then plot4(ExitChannel, "Exit");
-- Roger Darley
GO BACK

REFINING THE HILBERT INDICATOR

[Editor's note: In the March 2000 STOCKS & COMMODITIES, John Ehlers published the EasyLanguage code for the Hilbert cycle period, an indicator that plots the length of the current market cycle. Here, he explains how and why he updated the code for the indicator. The actual code in EasyLanguage appears in the next Traders' Tip. The points Ehlers makes here correspond to the code in the next Traders' Tip.]

My version of the Hilbert transform achieved computational efficiency by using a two-dimensional numbering system. Unfortunately, this introduces amplitude error in calculating the quadrature component. I can compensate for this error.

In the EasyLanguage code presented in the next Traders' Tip, "Hilbert Cycle Period Function," I have updated the method of compensating for the amplitude error by applying a straight-line compensation term using the frequency calculation from one bar ago. This is possible because the cycle period cannot change drastically from bar to bar. The slowly varying cycle period is adequate to do a good job of amplitude compensation.

In addition, I have used a different way to compute the cycle period. I currently use a homodyne discriminator because it exhibits superior performance in a low signal-to-noise environment. Homodyne means I use the signal multiplied by itself one bar ago to produce a zero-frequency beat note. This beat note carries the phase angle of the one-bar change. Still using the basic definition of a cycle, the one-bar rate of change of phase is exactly the cycle period.

With that overview, let's examine the Hilbert cycle period function to explain why each significant line of code is used.

1 The first thing I do is smooth the input data with a four-bar weighted moving average (WMA). I like to use WMAs because the lag is the "center of gravity" of the weighting coefficients. In the case of a four-bar moving average, the lag is only one bar.

2 The Hilbert transform only works with detrended signals, thus requiring the detrender variable. Since the calculation for the quadrature component is a difference, this same calculation is used to detrend the data before subsequent calculations are done. Note the detrended signal is corrected for the amplitude distortion --

3 Of the differencing process.

4 The quadrature component (Q1) of the Hilbert transform is then computed (and amplitude compensated). This computation has a three-bar lag. Therefore, the InPhase component (I1) is computed simply by using the detrended signal lagged by three bars. And that's it! The Hilbert transform is complete. The rest of the computations have to do with smoothing and computing the cycle period.

5 The first step of smoothing is done without introducing phase error by advancing the phase of I1 and Q1.

6 Next, I do some heavy-duty smoothing by using an exponential moving average (EMA) with an alpha of 0.15 in both components. I can use an EMA at this point because I am only interested in relative phase distortion between the two components and absolute phase distortion is irrelevant. Smoothing is crucial at this point because the next step involves multiplication and it is imperative to remove the noise component before the multiplication is performed.

7 Then I do a complex multiplication of the signal with the signal delayed by one bar.

8 I then smooth again to further remove any undesired cross-products.

9 Computing the period, I limit its change and smooth it with another EMA.

The result is the measured cycle period. This period is used in other calculations such as adaptive moving averages and sinewave indicators. Most important, the measured cycle period for today is used to compensate the amplitude distortions in tomorrow's Hilbert transform.
-- John F. Ehlers
GO BACK

HILBERT CYCLE PERIOD FUNCTION

The current version of John Ehlers's Hilbert cycle indicator has been put into an EasyLanguage function that returns the cycle period that will be used by our signal (see next Traders' Tip, "Hilbert Channel Breakout Signal") to generate buy and exit orders. Be sure that the name "HilbertPeriod" is assigned to the function.
{ ----------------------------------------------------------
HilbertPeriod (Function) by John Ehlers (7/22/00)
This is the Hilbert Cycle Period Function used by the Hilbert Channel Breakout System. It must be named "HilbertPeriod".
---------------------------------------------------------- }

Inputs: Price(numeric);

Vars:   Smoother(0), Detrender(0), I1(0), Q1(0), jI(0), jQ(0), I2(0),Q2(0), X1(0), X2(0), Y1(0), Y2(0), Re(0), Im(0), Period(0);

If CurrentBar >5 then begin
        Smoother = (4*Price + 3*Price[1] + 2*Price[2] + Price[3])/10;
        Detrender = (.25*Smoother + .75*Smoother[2] - .75*Smoother[4] - .25*Smoother[6])*(.046*Period[1] + .332);

{Compute InPhase and Quadrature components}
        Q1 = (.25*Detrender + .75*Detrender[2] - .75*Detrender[4] - .25*Detrender[6])*(.046*Period[1] + .332);
        I1 = Detrender[3];

{advance the phase of I1 and Q1 by 90 degrees}
        jI = .25*I1 + .75*I1[2] - .75*I1[4] - .25*I1[6];
        jQ = .25*Q1 + .75*Q1[2] - .75*Q1[4] - .25*Q1[6];
{Phasor addition to equalize amplitude due to quadrature calculations (and 3 bar averaging)}
        I2 = I1 - jQ;
        Q2 = Q1 + jI;

{Smooth the I and Q components before applying the discriminator}
        I2 = .15*I2 + .85*I2[1];
        Q2 = .15*Q2 + .85*Q2[1];

{Homodyne Discriminator}
        {Complex Conjugate Multiply}
        X1 = I2*I2[1];
        X2 = I2*Q2[1];
        Y1 = Q2*Q2[1];
        Y2 = Q2*I2[1];
        Re = X1 + Y1;
        Im = X2 - Y2;

{Smooth to remove undesired cross products}
        Re = .2*Re + .8*Re[1];
        Im = .2*Im + .8*Im[1];

{Compute Cycle Period}
        If Im <> 0 and Re <> 0 then Period = 360/ArcTangent(Im/Re);
        If Period > 1.5*Period[1] then Period = 1.5*Period[1];
        If Period < .67*Period[1] then Period = .67*Period[1];
        If Period <6 then Period = 6;
        If Period >50 then Period = 50;
        Period = .2*Period + .8*Period[1];

{END CORE CODE}

        HilbertPeriod = Period;

end;
-- Roger Darley
John F. Ehlers
GO BACK

HILBERT CHANNEL BREAKOUT SIGNAL

Here is the EasyLanguage code to generate the signals in the Hilbert channel breakout system, as discussed in my article in this issue, "Optimizing With Hilbert Indicators." The signal itself is a simple channel breakout system that generates buy and exit signals.

This code will also bring up a window in TradeStation, when you click on a bar with the TradeStation Expert Commentary cursor, that shows whether the system is long or flat; the high of the bar and the value of the entry channel; and the low of the bar and the value of the exit channel. This helps you see on a bar-by-bar basis exactly how the system is behaving.

This code works the same way as the code for the Hilbert channel breakout indicator, given on page 106, except the code for the indicator plots your strategy onscreen while this code actually generates the signals.

{**************************************************************************
 Hilbert_Channel (Signal)

 This is the signal for the Hilbert Channel Breakout System.

 If "EntryVal" or "ExitVal" have values greater than 0, then those values will be used for
 the entry / exit lookback periods. If either input "EntryVal" or "ExitVal" is 0, then the entry
 or exit lookback period will be the product of a fractional constant times the cycle period,
 "EntryK*Period" or "ExitK*Period."

 Optimize for highest Total Net Profit / Return on Account:
 1. Optimize EntryVal, and then ExitVal. (Optimum values are often between 10 and 45.)
 2. Change ExitVal to 0 and optimize ExitK. (Optimum value is often between 1.5 and 4.5.)
 3. Change EntryVal to 0 and optimize EntryK. (Optimum value is often between .1 and 1.)
 4. Repeat steps 2 and 3 until results of optimizing are stable.
***************************************************************************** }

inputs: Price((H + L)/2),
        EntryVal(15),
        EntryK(0),
        ExitVal(15),
        ExitK(0);

vars:   Period(0),
        count(0),
        EntryLookBack(0),
        ExitLookBack(0),
        EntryChannel(0),
        ExitChannel(0);

Period = HilbertPeriod(Price);

If EntryVal <> 0 then EntryLookBack = EntryVal else EntryLookBack = EntryK*Period;
if EntryLookBack < 1 then EntryLookBack = 1;

If ExitVal <> 0 then ExitLookBack = ExitVal else ExitLookBack = ExitK*Period;
if ExitLookBack < 1 then ExitLookBack = 1;

EntryChannel = 0;
for count = 1 to EntryLookBack begin
        if EntryChannel < High[count] then EntryChannel = High[count];
end;

ExitChannel = 100000;
for count = 1 to ExitLookBack begin
        if ExitChannel > Low[count] then ExitChannel = Low[count];
end;

If MarketPosition = 0 and High > EntryChannel then Buy;
If MarketPosition = 1 and Low < ExitChannel then ExitLong;

#BeginCmtry
        var: textString(" flat.");
        If CheckCommentary then BEGIN
                if MarketPosition = 1 then textString = " long.";
                if MarketPosition = 0 then textString = " flat.";
                commentary("Market position is", textString, NewLine);
                commentary("High is", high, ", channel", EntryChannel, ", lookback", ceiling(EntryLookBack), " bars.", NewLine);
                commentary("Low is", low, ", channel", ExitChannel, ", lookback", ceiling(ExitLookBack), " bars.", NewLine);
        End;
#End;
-- Roger Darley
GO BACK

EASYLANGUAGE

This month, we'll look back at Brian Bell's article "Normalization" from the October 2000 STOCKS & COMMODITIES. In his article, Bell defines four different techniques for normalizing oscillators. For this month's Traders' Tip, I have created an EasyLanguage function for normalizing oscillators, based on each normalization method described by Bell.

The EasyLanguage functions given here have been written specifically so that they can easily be used with any oscillator. The oscillator value is simply specified as one of the input parameters for the function. Following the EasyLanguage for the different functions, I have also created a sample indicator that utilizes all four of the normalization functions and allows the user to specify the oscillator and the type of normalization to use.

The first function to be created is one based on Bell's technique for normalizing to average price. This function has three input values. OscValue represents the value of the oscillator to be normalized. This input is included in each of the four functions. The NormPrice and NormLength inputs represent the price and length values used to calculate the average that is used in the normalization.

Name: NormAvg
Type: Function

Inputs: OscValue(Numeric), NormPrice(Numeric), NormLength(Numeric);
Variable: AvgValue(0);

AvgValue = Average(NormPrice, NormLength);

If AvgValue <> 0 Then
        NormAvg = OscValue / AvgValue
Else
        NormAvg = 0;
The second function is for Bell's technique for normalizing to standard deviation of prices. This function also has three input values. I already described the OscValue input. The NormPrice and NormLength inputs represent the price and length values used to calculate the standard deviation that is used in the normalization.
Name: NormStdDev
Type: Function

Inputs: OscValue(Numeric), NormPrice(Numeric), NormLength(Numeric);
Variable: StdDevVal(0);

StdDevVal = StdDev(NormPrice, NormLength);

If StdDevVal <> 0 Then
        NormStdDev = OscValue / StdDevVal
Else
        NormStdDev = 0;
The third function is for Bell's techniques for normalizing to average range of prices. Unlike the two previous functions, this function only has two inputs: the OscValue input, and the NormLength input, which represents the number of bars to use in the calculation of the average true range that is used in the normalization.
Name: NormATR
Type: Function

Inputs: OscValue(Numeric), NormLength(Numeric);
Variable: ATR(0);

ATR = AvgTrueRange(NormLength);

If ATR <> 0 Then
        NormATR = OscValue / ATR
Else
        NormATR = 0;
The fourth function is for Bell's technique for normalizing to range. This function also has only two inputs: the OscValue input, and a NormLength input, which represents the number of bars to use for the historical range that is used in the normalization.
Name: NormRange
Type: Function

Inputs: OscValue(Numeric), NormLength(Numeric);
Variable: HiHi(0), LoLo(0), HighLowDiff(0);

HiHi = Highest(OscValue, NormLength);
LoLo = Lowest(OscValue, NormLength);
HighLowDiff = HiHi - LoLo;

If HighLowDiff <> 0 Then
        NormRange = ((OscValue - LoLo) / HighLowDiff) * 100
Else
        NormRange = 0;
Finally, here is the EasyLanguage for the sample indicator that ties together all of the functions that we just created. This indicator allows a user to specify an oscillator and the type of normalization they wish to use for that oscillator. The NormType input is used to determine the oscillator normalization method (1=Avg, 2=Std Deviation, 3=Avg True Rng, 4=Hist Range). The oscillator that will be normalized is specified in the OscValue input. Please note that the value of the NormPrice input will not affect the results when NormType 3 or 4 are selected, since neither of these calculations references the price value. NormLength should be increased significantly when doing a range normalization (NormType = 4). Also note that while this indicator integrates all four functions that we created, it is in no way the only way in which these functions can be utilized. It is simply an example.
Name: Osc Normalization
Type: Indicator

Inputs: NormType(1), OscValue(Momentum(Close, 10)), NormPrice(Close), NormLength(8);
Variable: NormOsc(0);

If NormType = 1 Then Begin
        NormOsc = NormAvg(OscValue, NormPrice, NormLength);
        Plot1(NormOsc, "Norm Osc");
        Plot2(0, "Balance");
End;
If NormType = 2 Then Begin
        NormOsc = NormStdDev(OscValue, NormPrice, NormLength);
        Plot1(NormOsc, "Norm Osc");
        Plot2(0, "Balance");
End;
If NormType = 3 Then Begin
        NormOsc = NormATR(OscValue, NormLength);
        Plot1(NormOsc, "Norm Osc");
        Plot2(0, "Balance");
End;
If NormType = 4 Then Begin
        NormOsc = NormRange(OscValue, NormLength);
        Plot1(NormOsc, "Norm Osc");
        Plot2(50, "Balance");
End;
A sample chart is shown in Figure 1.

FIGURE 1: TRADESTATION, NORMALIZATION WORK AREA. This is a TradeStation chart of IBM with the momentum indicator applied in blue. The second indicator is the OSC normalization indicator, with the normalization based on historical range. The bottom window is the EasyLanguage document for the indicator.


The EasyLanguage for the normalized functions and sample indicator are available for download at Omega Research's Website. The filename is Normalize.ELS.

-- Gaston Sanchez, Omega Research Inc.

800 422-8587, 305 270-1095
Internet: https://www.omegaresearch.com
GO BACK

NEUROSHELL TRADER

If you own NeuroShell Trader Professional or NeuroShell DayTrader Professional, you can use the Genetic Optimizer to determine the best parameters for you in one step. Though you can iteratively determine the optimal parameters if you wish -- as Roger Darley did in his article in this issue, "Optimizing With Hilbert Indicators" -- the genetic optimizer overcomes the limitations of other optimizers by performing fast optimization of a large number of parameters.

The Hilbert indicators and their modifications by Darley and John Ehlers are quite complex, but with NeuroShell Trader's ability to call external code, we have the choice of building the custom indicators in C, C++, PowerBasic, or Delphi. Users of NeuroShell Trader can go to the STOCKS & COMMODITIES section of the NeuroShell Trader free technical support Website to download the Hilbert indicators we have created.

FIGURE 2: NEUROSHELL TRADER, HILBERT CHANNEL BREAKOUT SYSTEM. After downloading the custom Hilbert indicators for NeuroShell Trader, you can create a channel breakout system using the Hilbert channel indicators by selecting "New Trading Strategy..." from the Insert menu in the Trading Strategy Wizard to enter the entry/exit conditions.


After downloading the custom indicators, you can create a channel breakout system using the Hilbert channel indicators by selecting "New Trading Strategy..." from the Insert menu and enter the following entry/exit conditions in the appropriate locations of the Trading Strategy Wizard (Figure 2):

Long Entry:
Generate a buy long MARKET order if ALL of the following are true:
        HilbertHighBreakout( High, Low , 15, 0 )

Long Exit:
Generate a sell long MARKET order if ALL of the following are true:
        HilbertLowBreakout( High, Low, 15, 0 )
To view the Hilbert entry and exit channels on the chart, you can simply insert the HilbertEntryChannel and the HilbertExitChannel custom indicators using the Indicator Wizard.

In addition, you may find the Hilbert channel indicators interesting when using the neural nets in the Prediction Wizard to predict future price movements, highs, or lows, as well as in additional NeuroShell Trading Strategies that you can combine with different indicators to create your own unique system.

Ehlers's company, Mesa Software, sells additional indicators that are specifically designed for use with NeuroShell Trader. For more information on these indicators, please contact Mesa Software. For more information on NeuroShell Trader, visit www.NeuroShell.com.

-- Marge Sherald, Ward Systems Group, Inc.

301 662-7950, wardsystems@msn.com
https://www.neuroshell.com
GO BACK

SMARTTRADER

A handy tool to accompany any cycle analysis is the Detrend tool in SmarTrader. Simply put, Detrend removes the major cycle from a price chart to reveal shorter-term cycles that are otherwise hidden. All it takes to generate the detrended price chart is to define a simple moving average -- five bars in the case of this intraday chart -- then select the Detrend plot. Detrend defaults to the first moving average found in the price chart to create the Detrend plot (Figure 3). If there are multiple moving averages, the preferred moving average can be selected.

FIGURE 3: SMARTRADER, DETRENDED PRICE CHART. To generate the detrended price chart, simply define a simple moving average -- five bars, in the case of this intraday chart -- then select the Detrend plot. Detrend defaults to the first moving average found in the price chart to create the Detrend plot. If there are multiple moving averages, the preferred moving average can be selected.


For those who like to play with the arithmetic: the expression HIGH - moving average defines the high of the Detrend. LOW - moving average defines the low of the Detrend. To test this, use the User Formula Builder in SmarTrader to create these formulas, then plot it in a new window using the Range plot.

-- Jim Ritter, Stratagem Software International

504 885-7353, E-mail: Stratagem1@aol.com
Internet: https://www.stratagem1.com
GO BACK

WAVE WI$E MARKET SPREADSHEET

For this Traders' Tip, I'll update the Wave Wi$e tip that I presented in the September 2000 STOCKS & COMMODITIES. The following WaveWi$e formulas, originally given in the September S&C, show how to compute and chart fixed-length cycles using WaveWi$e.

Using the Cycles tool, two interesting cycles (34 days and 91 days) are identified for the S&P 500. These cycles are computed using the @CYCLE function and added to a chart. These cycles can also be added together (green line in Cycles chart) to see how they react with each other. Finally, the chart can be extended into the future to determine where future bottoms may occur.

FIGURE 4: WAVEWI$E, S&P 500 CYCLES. Here's an update of the chart that was presented in the September 2000 TradersÕ Tips for WAVEWI$E, now showing S&P 500 cycles through 9/14/2000. This WAVEWI$E technique for plotting cycles can be extended into the future to determine where future bottoms may occur. A cycle low appears to be on schedule for the October-to-November time frame.


We have updated the chart to show how the market has progressed through September 14 (Figure 4). A cycle low appears to be on schedule for the October-to-November time frame.

A: DATE         @TC2000(C:\TC2000V3\Data,SP-500,Standard & Poors 500,DB)
B: HIGH 
C: LOW 
D: CLOSE
E: OPEN 
F: VOL
G: 
H: Cycle1       @CYCLE(A, "01-28-1999", 91)
I: Cycle2       @CYCLE(A, "08-10-1999", 34)
J: CycSum        CYCLE1+CYCLE2
=========End Formulas
-- Peter Di Girolamo, Jerome Technology

908 369-7503, E-mail: jtiware@aol.com
https://members.aol.com/jtiware
GO BACK

All rights reserved. © Copyright 2000, Technical Analysis, Inc.

Return to November 2000 Contents