[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Even More Robust OddBall



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--------------------}