PureBytes Links
Trading Reference Links
|
My "more robust" code still has one source of error that I had
suspected was not important but turns out to be important. It is also
a problem in the original OddBall code.
So how could a such a simple system with basically two lines of code
include an error? Read on.
(The following explanation assumes 7 natural hour bars corresponding
to the 0930 to 1600 market hours. If you use the futures market hours
you might have 8 natural hour bars but the source of the error is the
same.)
The system is intended to compare the number of NYSE advancing issues
today with the number of advancing issues yesterday at the same time
of day. With 7 natural hour bars in a day, that would require:
RateOfChange(Close of data2, 7);
which is what is in the original code. We would thus expect the time
stamp on the current bar to be the same as the time stamp on the bar
7 bars ago. That is mostly true but not always. The worst source of
error is short trading days that contain fewer than 7 natural hour
bars such as the day after Thanksgiving. To test for this I tried
some experiments.
I just ran a quick test on my data and found that in 1000 days of
data there were 91 bars where the time stamps on the two bars being
compared were not the same. So I wondered if this was important.
Walk through one such case:
On 11/23/01 there are 5 natural hour bars. So at the 1000 bar on
11/26/01, the original code would compare that 1000 bar with the 1500
bar on 11/21/01 - clearly not what was intended.
I then tried two different new versions to test this. The code for my
experiment is appended at the end of this post in case you would like
to duplicate the experiment.
----
Version 1 = Original code (xMode = 1)
This was the original code. We know it includes comparisons on 91
bars with inconsistent time stamps for this data.
----
Version 2 (xMode = 2)
This version of the system restricted the RateOfChange calculation to
only those bars where the time stamps on the two bars were the same,
basically rejecting the 91 bars with the inconsistent time stamps.
(It held the RateOfChange value over bars where the time stamps were
different.) There were fewer trades and the results were poorer than
with the the original code.
----
Version 3 (xMode = 3)
This version does not assume anything about the number of bars back
to the corresponding time bar of yesterday. It saves the bar number
of each bar in an array based upon it's time stamp so that it can
find the corresponding past time bar if one exists. This allows the
code to use every bar available.
This version found only 26 bars with no corresponding time stamps the
previous day and produced better results than Version 2.
----
Results:
This is for trading 250 shares of $SPX (S&P500 Index) with no costs
for 1000 days of data ending 3/8/02:
Original Version 2 Version 3
Net Profit $511,000 $452,000 $503,000
# Trades 985 969 983
% Profitable 48% 47% 48%
Drawdown $56,000 $59,000 56,000
Profit Factor 1.44 1.38 1.43
ROA 900% 768% 895%
Sharpe Ratio 1.95 1.71 1.95
Original - So the spurious bars with the incorrect time stamp in the
original version were actually improving the results but is an
unpredictable way. It include 91 incorrect comparisons.
Version 2 - Leaving out all 91 mismatched time stamps reduced trades
and performance. It basically held previous positions for most days
following a short trading day.
Version 3 - Using all available time stamps accurately improved
profits to near the original and executes the strategy as accurately
as possible. There were only 26 bars for which no corresponding time
bar was available in the 1000 days of data.
I found this interesting - another example of how even simple code can
contain hidden errors that cause it to behave differently than
intended...
Bob Fulks
{ *******************************************************************
System : bf.OddBall.2
Last Edit : 3/17/02
Provided By : Bob Fulks
Description : This is three different versions of Mark Brown's
OddBall system. It is intended to test the effects of
comparing bars with different time stamps.
Data1 is the market being traded.
Data2 is the number of advancing issues on the NYSE.
Extra input:
xMode = 1 Original version for comparison
xMode = 2 Eliminates comparisons where the time stamps
on the two bars being compared are different.
xMode = 3 Uses an array to save the bar number of the
corresponding time bars of yesterday,
without assuming how many bars back it is,
so that it can use every available bar.
Use 60 minute natural hour bars day session only.
********************************************************************}
Input: RL(7), BZ(3), SZ(1), xMode(1);
Vars: ADV(Close of data2), ROC(0),
ErrCnt(0), DCount(0), j(0), k(0), LDate(0);
Array: aBar[10](0);
if Date <> Date[1] then begin {New day initialization}
DCount = DCount + 1; {Count days to allow 2 days of initialization}
LDate = Date[1]; {Save previous date}
end;
ADV = Close of data2; {Data series for advances}
if Time > 0930 and Time <= 1600 then begin
if xMode = 1 then ROC = RateOfChange(ADV, RL);
if xMode = 2 then begin {Calculate for matching bars only}
if Time[RL] = Time then ROC = RateOfChange(ADV, RL)
else begin
ErrCnt = ErrCnt + 1; {Count and print errors}
Print(ErrCnt:5:0, DCount:5:0, Date:8:0, LDate:8:0,
Date[RL]:8:0, Time:5:0, Time[RL]:5:0);
end;
end;
if xMode = 3 then begin
j = (TimeToMinutes(Time) - 600) / 60; {Calculate location in array}
k = MinList(CurrentBar - aBar[j], 50); {Get bars back to same bar yesterday}
if (Date[k] = LDate and Time[k] = Time) or DCount <= 2 then begin
ROC = RateOfChange(ADV, k); {Calculate rate of change}
end else begin
ErrCnt = ErrCnt + 1; {Count and print errors}
Print(ErrCnt:5:0, DCount:5:0, Date:8:0, LDate:8:0,
Date[k]:8:0, Time:5:0, Time[k]:5:0, j:4:0, k:4:0);
end;
aBar[j] = CurrentBar; {Save bar number in array}
end;
{--------------------System Code--------------------}
if DCount >= 3 then begin
if ROC > BZ then Buy on Close;
if ROC < SZ then Sell on Close;
end;
end;
{------------------Indicator Code--------------------}
{
Plot1(ROC, "1"); {Plot as a line}
if ErrCnt <> ErrCnt[1] then Plot3(ROC, "3"); {Plot as a fat Point}
Plot4(0, "4"); {Plot as a line}
end;
}
{--------------------End Code--------------------}
|