Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
30 views18 pages

MSTT Newsletter Feb 2005

Uploaded by

rarosil528
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
30 views18 pages

MSTT Newsletter Feb 2005

Uploaded by

rarosil528
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

MetaStock Tips & Tricks IN THIS ISSUE

Visiting The Basics – 5 1


Thanks For The Memory – Part 2 3
Using a Counter 7
Questions and Answers 10
The System Tester – Part 2 12
Tip of the Month 16
Trailing Stops for ICE Users 18
Coming Next Month 18

VISITING THE BASICS - 5


This article is a continuation of "Visiting The Basics" from the
December 2004 issue.

Ceiling

Tools For The Ceiling() function returns the lowest integer that is greater than
the largest value in the data array.
MetaStock
Syntax Ceiling( Data Array )

According to the MetaStock Formula Reference document, this


function would return the lowest integer that is larger than the highest
high value loaded. For example, if you had three years of data loaded
and the highest value ever reached by the High during that time was
MetaStock Tips & Tricks 103 5/8ths the formula would return 104.
is published monthly by
This function is probably more useful in system tests and explorations,
R A Larsen & Associates but can help you plot and calculate the new highs of securities.
Palmerston North 5301
NEW ZEALAND
Caution
Ceiling() doesn’t work in quite the manner described above. The data
Tel: +64 6 358 3723
[email protected]
array doesn’t include all data loaded, but only data for the current
www.metastocktips.co.nz bar. You can easily see this by creating this indicator, Ceiling(H),
and dropping it onto a chart. See Figure 1. For an expression that
works as described above you would need Highest(Ceiling(H)).

The Ceiling() function can be used to "round up" when that might be a
Volume 1, Issue 6 more appropriate rounding option. See the Round() function.
February 2005
All illustrations in this newsletter are courtesy of MetaStock
Page 1 February 2005 Page 1
Floor

The Floor() function returns the highest integer that is lower than the
smallest value in the data array.

Syntax Floor( Data Array )

According to the MetaStock Formula Reference document, this


function would return the highest integer that is smaller than the
lowest low value loaded. For example, if you had three years of data
loaded and the lowest value ever reached by the low during that time
Figure 1.
was 83 1/4ths the formula would return 83. Ceiling() function.

This function is probably more useful in system tests and explorations,


but can help you plot and calculate the new highs of securities.

Caution
Floor() doesn’t work in quite the manner described above either. The
data array doesn’t include all data loaded, but only data for the
current bar. You can easily check this by creating this indicator,
Floor(L), and dropping it onto a chart. See Figure 2. For an expression
that works as described above you would need Lowest(Floor(L)).

The Floor() function can be used to "round down" when that might be
a more appropriate rounding option. See Round in the User Manual.

MACD Figure 2.
Floor() function.
This calculates the value of the MACD indicator.

Syntax MACD()

MACD measures the convergence or divergence between two


moving averages. It shows whether the difference between two
specific moving averages is increasing or decreasing, and
whether those changes are negative or positive.

MACD() nominally uses 12 and 26 period exponential moving


averages – Mov(C,12,E)-Mov(C,26,E). The MetaStock MACD()
function uses factors of 0.15 and 0.075 to determine the amount
of data added and discarded to and from each EMA.

From the construction of an EMA (See "Exponential Moving


Averages" in the January 2005 issue) we know that periods of 12
and 26 give factors of 2/13 and 2/27. Expressing these amounts
as decimal fractions we get approximately 0.1538 and 0.0741.
This explains why MACD() doesn’t plot quite the same value as Figure 3.
Red line: MACD().
Mov(C,12,E)-Mov(C,26,E). You can find more information on
Black line:
moving averages and MACD() in the User Manual. See Figure 3. Mov(C,12,E) - Mov(C,26,E).
MACD not scaled to price chart.

Page 2 February 2005 Page 2


THANKS FOR THE MEMORY - 2
Review the A PREV-based latch offers many more options than a simple latch
to MetaStock users. First, there are no limitations to the values it
examples and can store, and second, it allows a reset to be related to the set signal. A
stored value can be accessed from within the latch by using the PREV
try to grasp function. With the stored value of a latch able to be accessed by PREV

underlying
it’s possible to manage a range of different resets. Any reset signal can
be directly related to the set signal. That includes indicator values
principles. current at the time of the signal as well as prices.

The one drawback with PREV is that it’s resource hungry, and it slows
down the execution speed of any PREV-based code. This is most
apparent with explorations, and it gets worse as the number of scanned
bars increases. Each PREV increases the resource burden too. Some
users won’t notice a problem, and with today’s fast computers the
benefits of PREV-based code often outweigh any performance
disadvantage.

An "initialization" variable isn’t required with this form of latch – a


starting reference point isn’t necessary once a PREV is introduced into
the latch. Nevertheless it can still be useful to have an Init variable
ready for use with subsequent BarsSince() and ValueWhen() functions
to help reduce or eliminate unnecessary N/A plots.

{Exercise 1} {binary PREV-based latch}


Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Figure 4. Reset:=Cross(Mov(C,30,E),Mov(C,15,E));
Binary latch. Trade:=If(Set,1,If(Reset,0,PREV));
Trade;

Exercise 1 shows the simplest form of a PREV-based latch, and here


it’s used to give a binary result (zero or one). The basic formula has no
way of telling the difference between the first and any later set signal,
so it can’t be used to store prices reliably. The formula must test the
state of the previous bar’s Trade variable (by using PREV) to ensure
that only the first signal is sampled for the value to be stored.
Secondary set signals don’t pinpoint the correct values required for
locking into the latch – they only corrupt an existing sample.

The additional PREV in exercise 2 expands the role of the latch


variable to that of a fully functional price latch. It can be used to store
the CLOSE from the first bar of a set signal, or any price or indicator
value for that matter. The extra PREV inhibits secondary samplings.

{Exercise 2} {price latch}


Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Reset:=Cross(Mov(C,30,E),Mov(C,15,E));
Figure 5. Trade:=If(PREV=0,If(Set,CLOSE,0),If(Reset,0,PREV));
Price latch. Trade;

Page 3 February 2005 Page 3


{The three If() functions
The first If() function checks on Trade’s value from the previous bar. If in a price latch}
it was set to something other than zero then control is passed directly
to the third If() function. The third If() function is used to manage the Trade:=
latch reset. If the latch was already reset (zero) then control is passed to If(PREV=0, {OK to set?}
If(Set,CLOSE,0), {set signal?}
the second If() function. This is used to manage setting the latch.
If(Reset,0, {reset signal?}
PREV)); {keep same value}
If control is passed to the second If() function then any (Set) signal sets
the latch to the value of the current CLOSE. The absence of a signal
forces the latch to retain its previous value, zero, and it stays reset.

If control is passed to the third If() function then any reset signal forces
the latch to zero. The absence of a specific reset signal allows the latch
to retain its previous value, PREV, whatever that might have been.

Two conditions must be met for this latch to set to a new active value.
These conditions are that the previous bar of the latch must have been
zero (PREV=0), and the Set variable must be active (true).

What would happen if there was no preliminary check on the previous


state of the latch before each new signal was accepted? I’ve already Figure 6.
mentioned this situation once, and it needs to be repeated because this Multiple set signals enabled.
is an important issue. Without this check the latch would be left wide
open to secondary signals. This vulnerability is illustrated in Figure 6.

Each and every new signal would store a new value in the latch, and
the correct initial value would consequently be lost. The preliminary
check by the first If() function ensures that only the first Set signal is
effective in storing a value. Figure 7 illustrates how secondary signals
are masked out by the "If(PREV=0" test at the beginning of the latch
variable in exercise 2.

When the latch is used in binary mode there’s not really a need to
suppress secondary signals, but when storing prices or other values
within the latch it’s critical that secondary signals be inhibited.
Figure 7.
Now we’re ready to build a latch that monitors its own value so that it Multiple set signals inhibited.
can be self-resetting when specific set-related criteria are met. Exercise
3 shows how the latch can reset itself once the current CLOSE rises to
20% above the price at which it was first set. Either a conventional
reset or some price-related condition can trigger the latch reset
mechanism (Reset OR price condition met). Either condition forces the
latch to reset to zero. See Figure 8.

{Exercise 3}{price latch with entry related exit}


Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Reset:=Cross(Mov(C,30,E),Mov(C,15,E));
Trade:=If(PREV=0,If(Set,C,0),If(Reset OR C>=PREV*1.2,0,PREV));
Trade;
Figure 8.
An independent reset might not even be needed with some PREV- Exercise 3: Price latch
based latches. That’s because both positive and negative price with internal reset.

Page 4 February 2005 Page 4


movements can be checked against the stored latch value. One target or
the other will eventually trigger a reset, regardless of which way the
price moves. This situation is demonstrated in exercise 4. Either a 20%
rise or a 10% fall in the closing price will trigger a reset.

{Exercise 4} {price latch with entry-related exit only}


Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Trade:=If(PREV=0,If(Set,C,0),If(C>=PREV*1.2 OR
C<=PREV*0.9,0,PREV));
Trade;

Sometimes it’s desirable to plot a visible reset signal after an internally


generated reset has occurred. Obviously this must be done "after the
event" because it can’t be predefined. One way to simulate an internal
Figure 7.
reset is to use the resetting of the latch variable to generate a negative
Price latch with
negative spike on reset.
spike for one bar. The latch would then be restored to the normal reset
state on the following bar. The latch in exercise 5 plots a negative
spike at the reset, and you can see this by plotting the exercise on
almost any chart. See Figure 7.

{Exercise 5} {Price latch with negative spike on exit.}


Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Trade:=If(PREV<=0,If(Set,C,0),If(C>=PREV*1.2 OR
C<=PREV*0.9,-PREV,PREV));
Trade;

When a PREV latch is coded to spike negative on reset it’s necessary


to test the latch for a zero OR negative to enable the next set. Failing to
include a test for the negative condition (<=) inhibits the latch from
setting on the first bar after a reset. This is because one extra period is
needed for the latch to return to zero following a negative spike.

Figure 8. Exercise 6 shows how to use the ABS() function in conjunction with a
Negative spike used to negative spike to extend the plot of the latch-active state by one bar.
"extend" an active state. There are times when extending the latch signal across the reset bar is
desirable, and this technique does the job well enough. Whether or not
you need to extend the active side of the latch will depend on the needs
of subsequent variables.

{Exercise 6}
Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Trade:=If(PREV<=0,If(Set,C,0),If(C>=PREV*1.2 OR
C<=PREV*0.9,-PREV,PREV));
ABS(Trade);

One concern I have with the formulas for exercises 5 and 6 is that both
use five PREVs. This number can be reduced to three PREVs without
losing any essential functionality. This is achieved by converting the
Trade variable back to a "pseudo" binary form. A value of –1 is used
on the reset bar, while +1 and 0 are used for the nominal set and reset
states. The ability to store a wide range of values within the PREV

Page 5 February 2005 Page 5


variable is lost when this is done. However, you can still use PREV
with a ValueWhen() function inside the latch, and that gives you
access to values for the bar on which the latch was set.

Decision-making within the latch remains unaffected. The plot for


exercise 7 is the same as exercise 5, and the plot for exercise 8 is the
same as exercise 6. These two exercises show that a latch can operate
in binary mode and yet still effectively "store" prices or other values.
In reality the price has to be reconstructed "after the event", just as an
internally generated reset must be reconstructed when required later.

{Exercise 7}
Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Trade:=If(PREV<=0,If(Set,1,0),
If(C>=ValueWhen(1,PREV<=0,C)*1.2 OR
C<=ValueWhen(1,PREV<=0,C)*0.9,-1,1));
Trade*ValueWhen(1,Trade+Alert(Trade=0,2)=2,C);

{Exercise 8}
Set:=Cross(Mov(C,10,E),Mov(C,20,E));
Trade:=If(PREV<=0,If(Set,1,0),
If(C>=ValueWhen(1,PREV<=0,C)*1.2 OR
C<=ValueWhen(1,PREV<=0,C)*0.9,-1,1));
Abs(Trade)*ValueWhen(1,Ref(Trade=0,-1),C);

PREV is used inside ValueWhen(), also inside the Trade variable, to


identify the opening trade price. The result line (last line of code) uses
ValueWhen() to reconstruct the "stored" price. Keeping the number of
PREVs in your code to a minimum is essential. Even with today’s fast
computers any PREV-based code can still slow processing down to an
unacceptable crawl. This is most noticeable when processing large
amounts of historical data.

You might be asking yourself whether it’s still necessary to exclude


secondary signals when only binary values are stored in a latch. That’s
a good question. Strictly speaking there’s not really a need for such a
check. However, removing the first If() function (and the PREV inside
it too of course) makes it necessary to add a PREV in place of "1" at
the tail end of the variable. Exercise 9 shows how you could remove
the preliminary check on the state of the latch.

{Exercise 9}
Trade:=If(Set,1,
If(C>=ValueWhen(1,PREV=0,C)*1.2 OR
C<=ValueWhen(1,PREV=0,C)*0.9,0,PREV));
Trade*ValueWhen(1,Ref(Trade=0,-1),C);

If you’re using ValueWhen() to recover set-related prices or values,


you’ll need to take care in getting the entry timing correct regardless of

(Continued on page 17)

Page 6 February 2005 Page 6


USING A COUNTER
J ust what is a counter? It’s an accumulator for counting events,
price changes, or keeping tally of almost anything. Counters come
in two forms – those that use PREV, and those that don’t.

On Balance Volume, OBV(), is one example of a counting function,


and it can be constructed either with or without PREV. The manual
shows a PREV version of OBV(), and I’ve included one that works
correctly when the price doesn’t change from one bar to the next. Can
you see how my version differs from the one in the manual? Look it up
under PREV in the user manual.

The biggest difficulty you’ll face when building a counter without the
PREV function is how to force it to reset. If a reset isn’t needed then
you have less to worry about. You can probably just insert the
appropriate code into Cum() and the job is done. I’m assuming,
though, that a reset will be necessary for most purposes. The counter
presented here allows for a reset when it’s needed.

{On Balance Volume - PREV version}


V*If(C>ValueWhen(2,1,C),1,If(C<ValueWhen(2,1,C),-1,0))+PREV;

{On Balance Volume}


Cum(V*If(C>ValueWhen(2,1,C),1,If(C<ValueWhen(2,1,C),-1,0)));

Counter Construction

Figure 9 illustrates the counter’s operation, and a description of the


role of each variable follows the indicator code below.

{Counter}
D:=Input("Count, 0=Events, 1=Bars",0,1,0);
A:=DayOfMonth()>20;
B:=Month()=3 OR Month()=9;
I:=Cum(A+B>-1)=1;
A:=If(D,A,A*Alert(A=0,2));
X:=Cum(A);
F:=BarsSince(I+A)<BarsSince(I+B);
Z:=X-ValueWhen(1,I+(F=0),X); Z;

Variable Descriptions
Figure 9.
Counting signal leading-edges. D Selects whether to count all bars, or only signal leading edges.
A Signal to be counted (an example only).
B Reset signal (an example only).
I An Init creates pseudo signals to get the counter started.
A Applies the D option to the A variable.
X Accumulates all A signals.
F Creates a signal window when reset, B, is not active.

Page 7 February 2005 Page 7


Z Change in X since the last reset.

The operation of this counter differs from a PREV-based counter in


that all increments (and/or decrements) are accumulated, and the total
as at the last reset signal is subtracted from current total. I’ve attempted
to illustrate this principle in the following diagram. Take note of the
points at X, Y and Z.

X Y Z
<----A---->
<---------------B---->
<----C---->
Figure 10.
The length of line-A equals the difference in length between line-B and Counting signal bars.
line-C. The Cum() function allows the accumulation of all values from
each point along line-B. By subtracting the value as at some point in
the past (line-C at point Y) from the current value (line-B at point Z)
we get the actual length from the previous point to the current point
(line-A, from point Y to point Z).

To count the bars or periods in line A, the length of the line could be
represented in MetaStock as follows.

A:=Cum(1)-ValueWhen(1,Y,Cum(1));

If this seems a little complicated, relax, because it’s not. It’s as simple
as A=B–C. The distance from Y to Z is the same as the distance from
X to Y subtracted from the distance between X and Z. This indirect
method of accumulating values or bars along line-A might seem a little
odd, but the results are the same as counting from Y to Z directly.

Let’s review how a non-PREV counter works. The Cum()


function counts ALL events (prices or bars etc.) from the
beginning of the chart, and the ValueWhen() function returns the
counter value (the Cum() function tally) as at the time of the
most recent reset signal. The difference between the two is the
current counter value. In reality this counter is never reset to
zero; it’s simply made to appear that way by subtracting the
accumulated total from itself with each reset signal.

Putting the Counter to Work

A good example of a PREV-based counter (accumulator) can be found


in the Cumulative Price Change (CPC) variable. This is used in both
the Q and B indicators discussed by Jim in his "Filters" series of
articles. The counter section in these indicators is made up of four
variables – REV, PDS, DC and CPC. The REV and PDS variables
work together to generate a reset, DC gives the price change to be
accumulated, and CPC is the accumulated result.

Page 8 February 2005 Page 8


Here’s the Q indicator again. The counter section has been reduced to
three variables by removing some redundant code. REV is no longer
required. An asterisk marks the remaining three variables {*}.

{Q Indicator}
m:=Input("% Scalar trend period",1,25,4);
n:=Input("% Scalar noise period",1,500,250);
cf:=Input("% Scalar correction factor",0.1,20,1);
p1:=Input("First moving average periods",1,200,7);
p2:=Input("Second moving average periods",1,200,15);
{*} pds:=Mov(C,p1,E)>Mov(C,p2,E);
{*} dc:=ROC(C,1,$);
{*} cpc:=If(pds<>Ref(pds,-1),0,dc+PREV);
trend:=If(pds<>Ref(pds,-1),0,(cpc*(1/m))+(PREV*(1-(1/m))));
dt:=cpc-trend;
noise:=Sqrt(Mov(dt*dt,n,E));
(trend/noise)*cf;

You’ll notice that CPC is reset to zero whenever PDS differs from its
previous value. This particular accumulator can count both positive
and negative values, and it resets with each moving average crossover.
PDS checks the value of one moving average relative to the other. Any
crossover is reflected in a change of relative states, thus forcing CPC to
reset. Unless the reset is active, DC is added to the PREV value of
CPC on each bar. The accumulation of price changes continues until a
moving average crossover signals a change of trend. At that point CPC
resets to zero. You can plot the following code on any chart. You
might find it helpful to plot both moving averages too.

{cumulative price change}


pds:=Mov(C,7,E)>Mov(C,15,E);
dc:=ROC(C,1,$);
cpc:=If(pds<>Ref(pds,-1),0,dc+PREV);
cpc;

I want to adapt this counter and use it in place of the PREV-based


formula above. Why would I want to do that? Because I know that any
scan or test run across hundreds of securities is going to be processed
faster if my code has fewer PREVs. When working with only 20 to 30
securities any improvement is hardly worth the effort. However, when
scanning 7000 NASDAQ securities, any speed improvement is going
to make a significant difference.
Figure 11.
CPC, Cumulative price change. {cumulative price change - non-PREV}
pds:=Mov(C,7,E)>Mov(C,15,E); {reset setup}
pds:=pds<>ValueWhen(2,1,pds); {reset signal} {B}
dc:=ROC(C,1,$); {count this value on every bar} {A}
init:=Cum(pds>-1)=1; {I}
cdc:=Cum(dc); {cumulative count} {X}
cpc:=cdc-ValueWhen(1,init+pds,cdc); {counter} {Z}
cpc;

Page 9 February 2005 Page 9


This counter is intended as a general-purpose model, so it shouldn’t
come as any surprise that some changes were necessary to adapt it to
the CPC role.

Now look at the modified Q Indicator below. There’s only one PREV
left in the formula. Compare a plot from this adapted indicator with a
plot from the original Q code – the two plots should be identical.

{Q Indicator - revised}
m:=Input("% Scalar trend period",1,25,4);
n:=Input("% Scalar noise period",1,500,250);
cf:=Input("% Scalar correction factor",1,250,1);
p1:=Input("First moving average periods",1,200,7);
p2:=Input("Second moving average periods",1,200,15);
pds:=Mov(C,p1,E)>Mov(C,p2,E);
pds:=pds<>ValueWhen(2,1,pds);
dc:=ROC(C,1,$);
init:=Cum(pds>-1)=1;
cdc:=Cum(dc);
cpc:=cdc-ValueWhen(1,init+pds,cdc);
trend:=If(pds,0,(cpc*(1/m))+(PREV*(1-(1/m))));
trend:=If(Sum(trend,2)=Sum(trend,2),trend,trend); {add an invalid Figure 13.
Plotting the revised Q indicator.
bar}
dt:=cpc-trend;
noise:=Sqrt(Mov(dt*dt,n,E));
(trend/noise)*cf;

I’ve deliberately added a second TREND variable into this formula.


It’s there to insert one N/A bar to keep the plot from the revised
indicator the same as the original. The NOISE variable would be
slightly off for the first 250 bars (n periods) if this were not done, and
the two Q plots (before and after) would not be identical. The removal
of PREV from the CPC variable reduces its N/A periods by one, and
the flow–on effect is that NOISE calculates using a slightly different
data array for the first 250 bars. If you comment out the second
TREND variable you’ll see a minor but noticeable difference between
the two Q plots.

QUESTIONS AND ANSWERS


Qneed
In the January issue you gave a list of functions on page 6 that
special care when included in an exploration. What specific
Mov() functions should I look out for.

A The safe ones are S, W, T and TRI. The ones that can cause a
problem are E, VAR and VOL. Note that VAR is the same as
Tushar Chande’s VIDYA.

Page 10 February 2005 Page 10


(Continued from page 10)

Qwhere
Dec 2004 p.3 Abs(data array): Could you provide an example
the need to plot a negative spike as a continuation could
arise? I don't think it’s an issue I've seen, and I lack the imagination to
see where I might need it. This was the generic example you gave:

Tr:=If(PREV <=0, N>0, If( X OR


ValueWhen(1, PREV=0,N) <= C, -1, 1));
Tr:=Abs(Tr);

A The issue really has nothing to do with the ABS() function – it’s
about making adjustments to what you’ve got so you end up with
what you want. My example could perhaps have been chosen more
carefully.
Figure 13.
Open trade on Monday, Suppose you have a latch that tells you when a trade is active. If you
close trade on Friday.
enter the trade on Monday’s open, and exit on Friday’s close, the latch
is going to show the trade is only active for four days, when in fact it
spans five. Figure 13 demonstrates this behavior.

N:=DayOfWeek()=1;
X:=DayOfWeek()=5;
BarsSince(N)<BarsSince(X);

Is the latch telling a lie? No, but you should be aware that a reset forces
the latch to go from active on a Thursday to inactive on a Friday. This
plot might need to be adjusted if you want it to convey some other
Figure 14. message. What if you want to count the total number of open-trade
The apparently premature bars? Would you count four bars for each trade, or five?
reset of a trailing stop.
Events triggered by PREV-based code also occur one bar earlier than
might seem logical. If you plot a PREV-based auto-reset trailing stop
A you’ll seldom see the CLOSE cross below the plot. The PREV-based
code anticipates the CLOSE crossing below the trailing stop, and it
resets on the bar before that cross would be visible. See Figure 14.
B
A:=C-3*ATR(10);
B:=If(C<PREV,A,Max(A,PREV));
B;
C
The reset timing is correct, but our expectation is to see each cross
occur visually. A similar timing anomaly occurs with a PREV-based
Figure 15. latch reset. See Figure 15.
Using ABS() and a spike
to "extend" an active state. The object of the exercise, whatever mechanism we use, is to create the
A. Internal reset. timing signals that are essential for turning our ideas into code reality.
B. Spike on reset.
C. Active state "extended"
A negative spike is a means to an end, not the end in itself.
by spike.

Page 11 February 2005 Page 11


THE SYSTEM TESTER - 2
Y ou might recall that I only used formula names in various System
Tester windows in the December newsletter, not full formulas.
I’m going to look at the more common practice of placing all code
inside the System Tester this month. As part of the discussion you’ll
see how easy it should be to code price stops. Refinements might be
needed for some situations, but a price stop only has two basic
elements – a binary signal and a price mechanism.

One of the less agreeable features of the new System Tester is the
limited amount of space provided for placing entry, exit, price and
position-size code. That makes it necessary to keep code as short as
possible – there’s no room for long comments or flowery names. If
you’re like me and often call one or more filters for each system, you’ll
find it very difficult to squeeze both the primary code as well as related
filters into the limited space available. One reason that you might want
all code placed inside the system tester is so that your filters can be
optimized at the same time as the main code. If you can’t fit the filters
into the formula windows then you can’t optimize them.

Adobe tools
Actually that’s not quite true, and I’ll come back to this particular issue
next month. You can optimize filters and other code without placing it
all inside the System Tester. It takes a little preparation and thinking
through, but it can considerably reduce the amount of code that must allow text and
reside in the System Tester. code from this
How much room is there in the various formula windows anyway? My document to be
count puts it at 1023 characters per window. This figure is the same for
price and entry-size windows too. That’s only slightly more than 40% selected and
of a full-sized indicator. I’m sure you can see why there might be a
problem getting the code from several indicators to fit inside the copied.
System Tester.

Conserving Window Space

Here are some suggestions for conserving window space. These are
just ideas and are not meant to be set in stone.

• Assign variable names to all expressions that are used more than
once.
• Keep variable names to one or two characters in length. There are
14 letters of the alphabet available for use as variable names. Most
of the reserved letters are quite obvious, and MetaStock quickly
lets you know if you try to use one of them.
• Don’t use comments.
• Remove redundant code wherever and whenever you find it.
• Eliminate unnecessary brackets and spaces. Be careful with this
one. The manual lists operator precedence, so you can look it up
when you’re not sure what has precedence.
• Use the shortest method of writing expressions, but don’t take

Page 12 February 2005 Page 12


shortcuts when accuracy is at risk.
• Use abbreviations for price arrays names (C instead of CLOSE).
• Don’t separate variables with new-lines or spaces.

Many users believe that giving variables long self-explanatory names


is good coding practice. It’s not. One short comment is far preferable
to an often-repeated long name. Longer lines of code are harder to
analyze, and extra characters cause more confusion than clarity.

The last item in the list above touches on an important issue that some
of you will not be aware of. The single-line formula windows don’t
accept new-line characters directly, so placing each variable on a line
of its own doesn’t appear to be an option. However, a larger editing
window can be opened by clicking on the "f" button sitting to the right
of these windows. This button is enabled for any order type except
market orders, and any position size setting except default. Pasting
code into the editing window, complete with new-line characters, is
quite acceptable.

A Sample System

I’ve modified the entry for the PS Fractal system as an exercise, and
I’ve created a short-term breakout system from it. Two PS Fractal
systems are supplied with all later versions of MetaStock. My
{PS Fractal Trading System entry adaptation of the system should not be traded as shown here – it’s only
used as basis for a sample system}
presented to demonstrate aspects of the Enhanced System Tester.
F1:=ValueWhen(1,H<Ref(H,-2)
AND The essence of the Sample System is that it uses an EOD filter to select
Ref(H,-1)<Ref(H,-2) AND prospective trades for a watch list. It then enters the market as soon as
Ref(H,-3)<Ref(H,-2) AND the fractal price is touched, and exits two days later on the OPEN. The
Ref(H,-4)<Ref(H,-2),Ref(H,-2)); fractal (F1) sets the trigger price. This is based on a previous high, with
Cross(H,F1); two lower highs each side of it. My testing suggests that trades have a
higher probability of success when the trigger price is set higher than
the standard fractal. The original system triggers an entry when the
price crosses above the fractal. Short-term breakout systems can also
benefit from the use of an index filter to ensure that the trade is in
accord with the general market direction. The Ehler’s Distant
Coefficient Filter, as discussed in the September 2004 newsletter,
might make a useful index filter starting point. So might CoT data.

Trading prospects for this system are found by scanning for securities
where the sample filter is active. Typically, a good filter will kick in
when the price surges to a new short-term high and select securities
with strong short-term momentum. This approach may not work well
with high-volume / high-value securities.

A new trigger price is set once the security trades lower (lower than the
new high) for two bars. If the filter stays active and the price touches
the fractal then the trade is entered at the intraday fractal price (or
whatever price you wish to use). Slippage should be allowed for in real
life. Notice that the filter result is delayed by one bar, so it doesn’t use

Page 13 February 2005 Page 13


prices from the same day as the signal (using the current CLOSE to
validate an intraday entry is not a good idea).

Note: The use of "*" instead of "AND" is an


effective space-saving measure.

{Buy Order, Modified PS Fractal Entry}


Q:=Fml("Sample MSTT Filter");
F:=Ref(H,-2);
F1:=ValueWhen(1,H<F AND Ref(H,-1)<F
AND Ref(H,-3)<F AND Ref(H,-4)<F,F);
Buy:=H>=F1; Buy:=Buy AND
Ref(Buy,-1)=0;
Buy*Fml("Date Filter")*Q;
Figure 16.
The Limit Price code is almost the same as the Buy Order code, but it Buy Order window.
must give a price signal instead of a binary signal. Since the Buy Order
doesn’t inhibit trades when the OPEN gaps higher than the fractal, a
mechanism is needed to adjust the price to the higher entry price.
The Max() function fills this role nicely.

As I noted in December, an entry price deemed to be outside the


HIGH/LOW range forces the System Tester to enter the trade using
the CLOSE rather than the proper Limit Price. This shortcoming is
compensated for here by using the Min(Max()) combination seen
below in the Limit Price formula. It fractionally adjusts entry
prices when they are at one of the extremes (HIGH or LOW).
Figure 17.
The date filter need not be included with the price code as it’s not Order type and
necessary - a trade cannot be entered without the Buy Order signal position size settings.
being active. Also, the pricing expression, Max(O,F1), isn’t required
for the Buy Order code.

{Limit Price, Modified PS Fractal Entry}


Q:=Fml("Sample MSTT Filter"); F:=Ref(H,-2);
F1:=ValueWhen(1,H<F AND Ref(H,-1)<F
AND Ref(H,-3)<F AND Ref(H,-4)<F,F);
Buy:=H>=F1; Buy:=Buy AND Ref(Buy,-1)=0;
Buy*Fml("Date Filter")*Min(H*.99999,
Max(L*1.00001,Max(O,F1)))*Q;

The sell order is the same as the buy order except that it’s delayed by Figure 18.
Limit/Stop price
two bars. There are other ways to create a timed exit, but for such a
formula window.
short trade it’s hardly worth the effort. The inactivity stop offers an
alternative exit method if you want to try it. The inactivity stop would
be preferable to PREV-based code when longer timed-exit trades were
being tested.

It’s important to remember that all three pieces of code (Buy Order,
Sell order and Limit Price) should be changed simultaneously if, for
example, you were attempting to optimize additional code or use

Page 14 February 2005 Page 14


different filter values.

{Sell Order, Modified PS Fractal Entry}


Q:=Fml("Sample MSTT Filter"); F:= Ref(H,-2);
F1:=ValueWhen(1,H<F AND Ref(H,-1)<F
AND Ref(H,-3)<F AND Ref(H,-4)<F,F);
Buy:=H>=F1; Buy:=Buy AND Ref(Buy,-1)=0;
Ref(Buy*Fml("Date Filter")*Q,-2);

Figures 19. Create an indicator by pasting this code into the Indicator Builder. The
Sell Order window. filter was put together with the lower cap stocks in mind, not the
generals of the S&P 500. You could reduce the multipliers for higher
volume markets, but remember that the code is just for demonstration
purposes.

{Sample MSTT Filter}


Ref(Mov(C,15,E)>Mov(C,30,E)*1.05 AND
Mov(C,10,E)>Mov(C,20,E)*1.03,-1);

Position Sizing

There are two paths you can take to set up the System Tester
for "equal position sizing". Equal means that each trade
Figure 20. Default position size. invests the same nominated amount of capital. Both paths
use the "Transaction Cost" option setting. Equal position
sizing is an alternative to the more usual compounding of
Figure 21. Fixed position size. equity. I prefer to use equal position sizing because it gives
more conservative and credible results.
Compounding tends to emphasize
winners and play down losers.
Figures 22. Formula position size.
In the previous article I said to use "Default Size" for the Order Entry
Size, but it’s OK to change this setting to "Transaction Cost" if you
want equal position sizing. The windows shown in Figures 20–23 cater
for equal position sizing or position sizing managed by code.

Even if you stay with the "Use Default Size" option


shown in Figure 20, you can still change your mind
when you come to creating a new simulation. Under
"Trading Options" you can change the Default Size
from "% of Available Equity" to "Transaction Cost",
and set the required constant position size into the
small window just to the right. Notice in Figure 23 that
the transaction cost is set to zero. This is only
Figure 23. acceptable when the Transaction Cost is already defined in the system
Simulation position size is Buy Order window, as shown in Figures 21 and 22. The Buy Order
overridden when position size window takes precedence over the trading simulation settings. Why? I
is defined by the system rules. really don’t know.

Page 15 February 2005 Page 15


Entry and Exit on the Same Bar

Have you ever wondered whether one day of the week is more likely to
be profitable than any other day of the week? Data-mining tools may
be able to retrieve this sort of information for you – but which tools are
up to the task? The Explorer certainly is, but persuading the Enhanced
System Tester to enter and exit trades on the same bar requires a little
sleight-of-hand. Oddly enough, it’s the much-maligned forward
reference that makes same-bar trades possible.

The trick is to use a forward reference to


advance entry signals by one bar, and
leave the exit signals untouched. This
ploy forces the System Tester to
consider each entry signal one bar before
the exit signal goes active. Price signals
for entries must also be advanced by one
bar when used. The advanced signals are
retarded back to the proper timing by
adding a one-bar delay. The "Strategic Figure 24.
Delay" setting should be used to achieve this. Using the "Delay order The Buy Order is
opening" setting under the Trade Execution tab doesn’t appear to work. advanced by one bar to
facilitate same-bar entry
In the next article in this series I’ll look at optimization, stops, and and exit trades.
some other outstanding issues.

TIP OF THE MONTH


Last month I discussed exponential moving averages, and the effect
they sometimes have on the accuracy of exploration results. I was
reminded by one subscriber of a simple trick that forces an exploration
to scan a set minimum number of records.

This trick appends an expression such as the one below to an existing


column or the filter.

Suppose you want to ensure that the "Load Minimum Records" setting
always uses at least 250 bars. Enable the filter and place the following
code in it.

Sum(C,250)>0;

This expression forces the explorer to load at least 250 bars, and it’s
logically true whenever 250 bars are available. It can be used on its
own ,or added to an existing filter expression. It can just as easily be
used in one of the Explorer columns..

colA AND colB AND Sum(C,250)>0;

Page 16 February 2005 Page 16


(Continued from page 6)

the format you use for a PREV-based latch. It’s all too easy to sample
the wrong bar (too early or too late), so test your code thoroughly.

The timing of data sampling is difficult to get right when a PREV


function is used in any expression or formula. You might find that it
makes a little more sense if you think of PREV as being the same as
Ref("current variable value",-1).

The timing implications are even more obscure when PREV is used
with ValueWhen(). If you specify ValueWhen(1,PREV=0,"current
variable") you will usually be accessing the bar where the current
variable changed from zero to something else.

Remember that PREV always accesses the bar before the reference
point. Put another way, the reference point is always the bar after the
one pointed to by PREV. As you work with PREV you’ll find it
becomes easier to understand and use. Just looking at the exercises
won’t teach you much at all – you should pull each exercise apart and
put it back together again. Plot individual variables on their own, as
well as in combination with others. Most of your learning will come
from doing rather than reading.

I hope that you’ve taken the time to work


{Trade Stop Light}
through these exercises, and that the
{* user section *}
No:=Input("Entry Price 1=O 2=C 3=H 4=L",1,4,2); accompanying explanations have cleared away
Nd:=Input("Entry Delay",0,5,0); some of the mystery regarding the construction
Xd:=Input(" Exit Delay",0,5,0); and use of latches. PREV has always been one of
Pf:=1+Input("Profit Target %",1,999,25)/100; the more difficult MetaStock functions to get a
Lf:=1-Input("Stoploss %",1,99,10)/100; handle on, so don’t despair if you’re still
N:=Cross(Mov(C,10,E),Mov(C,20,E)); struggling with it. Persistence will get you there
X:=Cross(Mov(C,30,E),Mov(C,15,E)); eventually.
{* variable setup section *}
N:=N*Alert(N=0,2);
If you’ve survived this far then you might want
X:=X*Alert(X=0,2);
N:=ValueWhen(1+Nd,1,N);
to investigate the adjacent Trade Stop Light
N:=If(N>0,If(No=1,O,If(No=3,H,If(No=4,L,C))),0); indicator. It uses the same set and reset signals as
X:=ValueWhen(1+Xd,1,X); all other exercises, and it includes profit and
I:=Cum(N+X>-1)=1; stop-loss targets. The latch resets when these
M:=C<ValueWhen(2,1,C); limits are broken by the CLOSE. A feature of
{* latch and plot section *} this indicator is that both stops are managed by
R:=If(PREV=0,N>0,If(X OR just one PREV. The secret lies with the M
If(M,-1,1)*ValueWhen(1,PREV=0,N)<= variable, and the fact that the price must move up
If(M,-C/Lf,C/Pf),0,1)); to reach the profit target, or down to reach the
Z:=R=0 AND Alert(R,2);
stop-loss.
R:=Abs(R)*ValueWhen(1,I+Alert(R=0,2)*R,N);
R; {plot entry price}
R*Pf; {entry price + profit target}
R*Lf; {stop loss price}

Page 17 February 2005 Page 17


TRAILING STOPS FOR ICE USERS
The ICE IntelliStop formulas can easily be adapted to work as general
purpose trailing stops by using the Adaptick indicators, as shown in the
following examples. Anyone with ICE can create and use these stops.

{IntelliStop Long 4}
A:=Fml("zAdaptick - IntelliStop Buy 4");
L<ValueWhen(2,1,A);

{IntelliStop Short 3}
A:=Fml("zAdaptick - IntelliStop Short 3");
H>ValueWhen(2,1,A);

Note that there are difference between ICE 2.0x and ICE 2.5x stops.
Figure 25.
ICE IntelliStop signals.

PREMIUM DATA SERVICES


I f you are serious about getting the most from MetaStock, you will
need top quality data. Having bad data is worse than no data at all. I
use Premium Data, available from Norgate Investor Services.

They provide premium quality end-of-day data suitable for MetaStock,


covering stock markets (ASX, SGX, Amex, NYSE, NASDAQ, OTC),
futures markets (80 contracts) and Forex (78 currency pairs). The data Bad data is worse
is fully adjusted for all corporate actions such as consolidations, splits,
mergers, renames, code changes, and allows sophisticated groupings than no data.
such as industry, index participation and dividend-paying securities. A
free trial is available.

If you currently use another provider, let Norgate Investor Services


know and they'll give you a special offer. They really are worth your
consideration. http://www.premiumdata.net/

CUSTOM FORMULA SERVICES NEXT MONTH


Basics – Part 6
P ersonal trading system programming and testing, custom formula
writing for MetaStock and specialized data services are available
for AU$55/hr (US$40/hr, inc GST). Confidentiality and exclusivity
System Tester – Part 3
guaranteed by Jose Silva. ATR Ratchet Trailing Stop
Questions & Answers
http://www.metastocktools.com/index.html Explorations – Part 3

Disclaimer: There are significant risks associated with securities trading. Nothing in this newsletter should be considered investment
advice, trading advice or recommendations as to investment management. I am not an investment advisor, and the information
provided in this newsletter is for educational purposes only. Each subscriber uses the information published in MetaStock Tips &
Tricks at their own discretion and bears all risk associated with the application of that information to their trading or investment
decisions. Nothing in this newsletter implies that any formula, method or strategy is in fact correct, or reliable in all circumstances.

Page 18 February 2005 Page 18

You might also like