From the February 01, 2010 issue of Futures Magazine • Subscribe!

2010: A trader's odyssey

MEASURE OF TREND

It’s no surprise that trend-following systems perform well when the market is trending. When the market is in a strong trend, the chance that the trend will continue is higher than if the market trend is weaker. One classic measure of trend is ADX and its momentum.

We could use these raw values as inputs to the neural networks or develop a fuzzy trend indicator which gives us a measure of trend strength. However, a well-designed component that provides a single measure will work better than the raw data.

The ADX indicates a trend above a given level, normally 25. We will create a continuous trend indicator with the direction based on ADX by subtracting an offset value from ADX. We also will use a five-period difference of ADX to remove the lag and make the indicator respond faster. Next, we normalize the next value over a look back period that is normally 1.5 to 2.0 times the length of the period used. We assign a direction component by subtracting DMIPlus and DMIMinus. We use the sign of the resulting result and rescale our results between -1 and 1.

Another definition of trend is based on cycle. Price should remain above a full cycle length moving average for 50% of the cycle. It should increase its distance above that moving average for 25% of the cycle and then move closer to it for the next 25% of the cycle until it crosses the moving average and repeats this in the opposite direction.

Based on this concept, if we remain above a full cycle length moving average for more than 25% of the cycle and price continues to move further away from the moving average, we are in a price trend. This gives us a 0, -1 trend indicator.

Another helpful piece of data is the hypothetical equity curve of each trading system component. We can pass the output of our signal component or any new component we are building to this function and we will get the current equity for that signal. We can do this for each component and save the return value in a bar array and then take a difference of them. This will produce a change in equity.

You can find the function code for the ADX function, the cycle function and the system equity curve function below.

DEVELOPING AN OUTPUT

Output filtering could be based on human input, looking at all three systems and future price action and adjusting signals based on that. We could develop a voting scheme for our three systems and then produce an equity curve of this voting scheme. Next, we can look at this curve in the future and used it to modify our voting signals. There are many ways to create a target.

After we develop our target, we can use the output of the target, create trading rules and build an equity curve for our target. We should also use our target and normal trading buy and sells and see how well our target does. An important note: We can’t expect our model to do better than our target.

Once we have decided on our target, we need to find things that are predictive of the target. Because our target is based at least partly on our three system signals, we need to include these three signals as inputs. In terms of the equity curves, we need to take a difference of equity and find a way to normalize it. For example, we can divide by the average range over the same period used in calculating a difference.

IMPLEMENTING THE MODEL

Kernel regression or neural network models must be retrained to remain accurate. The best way to do this is walk-forward testing. We would train the model on 2,500 bars, for example, and then predict 250 bars and then roll the window forward.

When implementing these types of solutions, a trading set is used to predict an output. The output normally has some knowledge of future data, which in real life we don’t have. The way to implement this is to shift our training set back in time as many bars as the output is required to look into the future.

Consider this code fragment:

Comp1 = TrendComponent1Feb10Art(SLen1,MLen,LLen)

Comp2 = TrendComponent2Feb10(SLen,BuyLev,SellLev)

EquityComp1 = ShadowSysStats(Comp1)

EquityComp2 = ShadowSysStats(Comp2)

EquityComp3 = ShadowSysStats(Comp3)

NNInput1 = ShiftBack(Comp1,DaysPredict)

NNInput2 = ShiftBack(Comp2,DaysPredict)

NNInput3 = ShiftBack(Comp3,DaysPredict)

We have three neural network inputs here. If we had an output, we would calculate it using unshifted data. For example, if we wanted to predict percent change, our output would be:

Output = (Close – Close[DaysPredict])/Close[DaysPredict]. We could then feed the shift data by “DaysPredict” bars to predict a target using unshifted data.

Neural Networks require training multiple networks, saving results and then selecting the one with the best performance. Also, when using neural networks, the initial weights start with different values so your results will not be duplicated. This makes implementation trickier because to use them in real life, all historical networks selected are trained on older data that was used in walk-forward and must be saved so that the traded history can be reproduced. Otherwise, it’s possible that you could be long for the past two weeks and when a new network trains on that data you would have been short for the past week. You can’t have this trading real money. The saving of these networks, after they have been selected and reusing them on each test, allows this technology to be used in real life. Kernel regression doesn’t have these issues; the primary downside to this approach is it has trouble training with more than a dozen inputs.

NEXT STEP

This article presented the pieces of a supervised learning model that uses a composite of many different systems to create a master system that works better than any of the individual components. These types of composite systems can greatly outperform single systems, but some type of safety method needs to be applied in case the model fails during a walk-forward period.

During this and next decade as computers become faster and CPUs and software are developed to take advantage of that speed, advanced neural networks and kernel regression will finally become part of the average trader’s toolbox. If traders are going to be in position to take advantage of those tools, however, they need to take the time to learn how they work now. Developing component-based trading systems as described here is not easy, but the payoff that can result from a complete understanding of the approach can be substantial — if not in this decade, then the next.

Murray A. Ruggiero Jr. is the author of "Cybernetic Trading Strategies" (Wiley). E-mail him at ruggieroassoc@aol.com.

SYSTEM CODE:

Function MARTrendLevels(LookBack,Offset,Period) As BarArray

Dim AdjustADXValue As BarArray

Dim RawADX As BarArray

Dim ADXDiff As BarArray

Dim LongShort As BarArray

Dim MARRaw As BarArray

RawADX = ADXOld(Period,0)

ADXDiff = RawADX - RawADX[5]

AdjustADXValue = (RawADX - Offset) + (0.5*ADXDiff)

MARRaw = (AdjustADXValue – Lowest(AdjustADXValue,LookBack,0))/(Highest(AdjustADXValue,LookBack,0) - Lowest(AdjustADXValue,LookBack,0))

LongShort = Sign(DMIPlusOld(Period) - DMIMinusOld(Period))

MARTrendLevels = (2*MARRaw*LongShort)-1

End Function

Function CycleTrend() As BarArray

Dim DCycle

Dim ResultVal

Dim DatSeries As BarArray

Dim InstantTrend As BarArray

Dim SmoothPeriod1

Dim SmoothPrice1

DatSeries = (H + L)/2

CycleTrend = 0

DCycle = JE_DomCycPeriodClassic(SmoothPeriod1,SmoothPrice1)

InstantTrend = Average(DatSeries,CInt(DCycle),0)

ResultVal = scriptmro_2(Sign(DatSeries - InstantTrend) <> Sign(DatSeries[1] - InstantTrend[1]),CInt(0.5*DCycle),1)

If ResultVal = -1 Then

CycleTrend = 1

End If

If ((ResultVal > (0.25*DCycle)) And (Abs(DatSeries - InstantTrend) > Abs(DatSeries[1] - InstantTrend[1]))) Then

CycleTrend=1

End If

End Function

Function ShadowSysStats(TradeFlag as bararray) As BarArray

Dim VirEntryPrice As BarArray

Dim VirExitPrice As BarArray

Dim OpenEquity As BarArray

Dim CloseTradeEquity As BarArray

Dim TProfit

If ((TradeFlag = 0) And (TradeFlag[1] <> 0)) Then

If TradeFlag[1] = 1 Then

TProfit = NextOpen(0) - VirEntryPrice

ShadowSysStats = ShadowSysStats + TProfit

End If

If TradeFlag[1] = -1 Then

TProfit = VirEntryPrice - NextOpen(0)

CloseTradeEquity = CloseTradeEquity + TProfit

End If

VirEntryPrice = -999999

End If

If ((TradeFlag = 1) And (TradeFlag[1] <> 1)) Then

If TradeFlag[1] = -1 Then

TProfit = VirEntryPrice - NextOpen(0)

CloseTradeEquity = CloseTradeEquity + TProfit

'Print FormatDateTime(Date)," ",VirEntryPrice," ",NextOpen(0)

End If

VirEntryPrice = NextOpen(0)

End If

If ((TradeFlag = -1) And (TradeFlag[1] <> -1)) Then

If TradeFlag[1] = 1 Then

TProfit = Open - VirEntryPrice

CloseTradeEquity = CloseTradeEquity + TProfit

'Print FormatDateTime(Date)," ",VirEntryPrice," ",NextOpen(0)

End If

VirEntryPrice = NextOpen(0)

End If

If ((TradeFlag = 1) And (TradeFlag[1] = 1) And (VirEntryPrice <> -999999)) Then

ShadowSysStats = CloseTradeEquity + Close - VirEntryPrice

End If

If ((TradeFlag = -1) And (TradeFlag[1] = -1) And (VirEntryPrice <> -999999)) Then

ShadowSysStats = CloseTradeEquity + VirEntryPrice - Close

End If

If (TradeFlag <> TradeFlag[1]) Then

ShadowSysStats = CloseTradeEquity

End If

End Function

(Go to next page for more codes)

<< Page 3 of 4 >>
Comments
comments powered by Disqus
Check out Futures Magazine - Polls on LockerDome on LockerDome