MSTT Newsletter Feb 2005
MSTT Newsletter Feb 2005
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 )
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.
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()
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.
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).
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.
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
{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);
{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);
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.
Counter Construction
{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.
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.
{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.
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;
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.
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:
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.
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.
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
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
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
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.
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.
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.
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..
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 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.
{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.
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.