Page 1 of 5

MidPoint statistics - surprising results!

Posted: Sat May 27, 2006 12:55 pm
by michal.kreslik
NOTE: if you can't see the attached graphs, please LOG IN.

Hello,

Avery de Rumpledone :) asked me to run some stats on his MidPoint trading concept.

I tested:
- one NASDAQ stock (YHOO, Yahoo)
- one CME future (@ES, CME E-Mini S&P 500 futures continuous contract)
- one forex pair (EURUSD)

in timeframes:
- 1 min
- 5 min
- 15 min
- 60 min
- 240 min (4h)
- daily

For the purposes of this test I am using the term "Zone". This term relates to the price area either above or below the previous bar's midpoint.

midpoint is defined as (high + low)/2

The rules for conducting the research:
1. if the current bar closes above the previous bar's midpoint, it closed in the upper Zone
2. if the current bar closes below the previous bar's midpoint, it closed in the lower Zone
3. if the current bar closes at the very price of the previous bar's midpoint, we assume that it closed in the same Zone as the previous bar did

The basic question to be answered in our research with respect to the terms introduced above is:

What is the probability that the next bar’s close price will end up in the same Zone or in the opposite Zone than the current bar’s close price if we know how many consecutive bars, including the current bar, has had the close price in the present Zone?

Of course, the probability that the next bar will close in the present Zone and the probability that it will close in the opposite Zone always adds up to 100% since the bar has to close in one of the two Zones.

Independent variable here is the number of consecutive bars in the same Zone. Dependent variable is the probability that the next bar will close either in the same or in the opposite Zone (1 or -1 in the code, see below).

In the graphs I don't show probability in %, but rather the actual number of observations (number of consecutive bars' sequences) so that you may have a better idea of what was the testing sample size for each symbol/timeframe.

For the research to be valid with my algorithm that tracks only absolute Zone changes instead of upper to lower / lower to upper Zone change, we assume that the price behaviour in this respect is symmetric, i.e. there's no significant statistical difference between the tendency to change from the upper Zone to the lower Zone and the tendency to change from the lower Zone to the upper Zone.

Indeed, I was curious about the results. My working hypothesis was that:
1. the price action will resemble Brownian motion in Zone change with only one or several consecutive closes in the same Zone (i.e. the Zone selection will be random in this case)
2. with the higher value of consecutive bars closing in the same Zone there will be a significant tendency towards either staying in the current Zone or towards changing the current Zone.

But, as with many other studies, the results were quite surprising and I would say, strangely accurate! I double checked my code (I encourage you to do the same), but couldn't find any flaw that would explain the weird numerical accuracy of the results. I will be glad if you find the flaw in my code!

Anyway, have a look at the resulting graphs by yourself.

Notes:
- I only picked 10 highest consecutive bars sequences for every graph
- all the sequences/observations you will find in the attached zip file "MidPoint_stats.zip"
- from the graphs it is clear that the probability that the observation will occur decreases exponentially in inverse proportion with the number of consecutive bars in the sequence
- orange value: Zone changed after n consecutive bars in the Zone (x axis)
- green value: Zone didn't change after n consecutive bars in the Zone (x axis)
- look at the probability of Zone change (orange) versus Zone stay (green)!

The conclusion:
There is no evidence that there is a significant statistical relation between the number of consecutive bars in the same Zone and the probability that the Zone will change or not change on the following bar.

The code I used for calculations (you may re-run the experiment by yourself):

Code: Select all

{
   program name: !MidPoint statistics
   last change: May/27/2006
   version: 1.15
   author: Michal Kreslik
   email: michal.kreslik@kreslik.com
   website: http://kreslik.com
   comments: measures what is the probability
         that the next bar's close price winds up
      in the opposite Zone than the current
      bar's close price if we know how many
      consecutive bars, including the current
      bar, has had the close price
      in the present Zone
}

vars:
   iPrintToOutputBar( true ),
   iPrintToFile( true ),
   iFileFolder( "c:\data\temp" );

vars:
   vZoneBoundary( 0 ),
   vZone( 0 ),
   vCounter( 0 ),
   vMaxBarsInZone( 0 );

vars:
   sTimeFrameString( "" ),
   sFileName( "" );

vars:
   bZoneChanged( false ),
   bRunStats( false ),
   intrabarpersist bFirstRun( true ),

   tIndex( 0 );

arrays:
   aZoneChanged[]( 0 ),
   aZoneNotChanged[]( 0 );

bZoneChanged = false;
vZoneBoundary = (high + low)/2;

if close > vZoneBoundary[1] then vZone = 1;
if close < vZoneBoundary[1] then vZone = -1;

if vZone <> vZone[1] then
   begin
      bZoneChanged = true;
      vCounter = 1;
   end
else
   vCounter = vCounter + 1;

if bRunStats = false
   AND bZoneChanged
   AND   BarNumber > 1 then
      bRunStats = true;

if bRunStats[1] then
   begin
      vMaxBarsInZone = MaxList(vMaxBarsInZone, vCounter[1]);

      if vMaxBarsInZone > vMaxBarsInZone[1] then
         begin
            Array_SetMaxIndex(aZoneChanged, vMaxBarsInZone);
            Array_SetMaxIndex(aZoneNotChanged, vMaxBarsInZone);
         end;

      if bZoneChanged then
         aZoneChanged[vCounter[1]] = aZoneChanged[vCounter[1]] + 1
      else
         aZoneNotChanged[vCounter[1]] = aZoneChanged[vCounter[1]] + 1;
   end;

if LastBarOnChart AND bFirstRun then
   begin
      bFirstRun = false;

      if iPrintToOutputBar then
         begin
            ClearPrintLog;
            Print("cons. bars: zone changed: zone not changed:");
            for tIndex = 1 to vMaxBarsInZone
               begin
                  Print(NumToStr(tIndex, 0), " ",
                     NumToStr(aZoneChanged[tIndex], 0), " ",
                     NumToStr(aZoneNotChanged[tIndex], 0));
               end;
         end;

      if iPrintToFile then
         begin
            array: aTimeFrames[4]("");
            aTimeFrames[0] = NumToStr(BarInterval, 0) + "_ticks";
            aTimeFrames[1] = NumToStr(BarInterval, 0) + "_min";
            aTimeFrames[2] = "daily";
            aTimeFrames[3] = "weekly";
            aTimeFrames[4] = "monthly";
            sTimeFrameString = aTimeFrames[datacompression];

            sFileName = iFileFolder + "\"
               + GetSymbolName + "_"
               + sTimeFrameString + "_"
               + "statistics.csv";

            FileDelete(sFileName);
            FileAppend(sFileName, "cons. bars,zone changed,zone not changed" + NewLine);
            for tIndex = 1 to vMaxBarsInZone
               begin
                  FileAppend(sFileName, NumToStr(tIndex, 0) + ","
                     + NumToStr(aZoneChanged[tIndex], 0) + ","
                     + NumToStr(aZoneNotChanged[tIndex], 0) + NewLine);
               end;
         end;
   end;


Michal

Posted: Sat May 27, 2006 9:40 pm
by TheRumpledOne
Thanks Michal.

Now, can you explain the results in English? LOL

I want to know what the statistics are for when the price closes above/below the midpoint.

Posted: Sun May 28, 2006 10:03 am
by michal.kreslik
TheRumpledOne wrote:If I understand what you are saying then trading the middle crossover
is a coin toss?

Something doesn't feel right... perhaps I didn't communicate
accurately what I wanted.

To me, the results should be a 2D matrix showing the number of closes
above/below.

So if a stock closed above it's middle 3 bars in a row and closed
below on the fourth bar, it should be 1, 1, 1, 0 under the columns 1,
2, 3, 4 respectively.

Once you drop in enough results, it should give you an idea of how
long to look to stay in a trade. I know for daily, 3 is the "magic"
number. After 3 days above/below the middle, look for a reversal.


Avery,

given the fact that I know you for some time already, I think I can tell that you are always considering many different factors when you trade live. You never trade based on one single indicator. Since you are a professional trader, you are using your immense experience and, yes, some indicators, too. I know your style of trading and I know that you are using the indicators precisely as indicators. Not as a definite guide on when to place or close the trade. In other words, your indicator without YOU is not a complete trading system.


In fact, there is a 2D matrix in the results. You should read the results of the research this way: for example, you may ask

"What is the probability that the next bar will close below the middle if we know that the last 4 bars (including the current one) on the EURUSD 15 min chart closed above the middle?"

You look at the appropriate file (in this case "EURUSD_15_min_statistics.csv", attached in the original post in the zip archive) or chart with the EURUSD 15 min statistics. You look up "4" in the "consecutive bars" row - this is the number of consecutive bars in the same Zone (in this case, 4 bars above the middle) and you look at the number of cases when the zone changed on the following bar in the "zone changed" column. It reads "3335". Then you look at the number of cases when the zone didn't change under the "zone not changed" column. It reads, "3335", too (you see, I told you the results were strangely accurate). This means that the probability that, after 4 consecutive closes on the same side of the middle, the probability of the next bar to close on the opposite side of the middle is exactly 50%.

Have a look at the attached excel table screenshot and graph below.

Maybe I am wrong and there's some severe flaw in my code, but I really did check it several times. Can you find the flaw?

Of course, this is only statistics. You are trading based on MANY OTHER different factors.

This statistics only shows that IF you were trading based on SOLELY this one factor, then you would indeed be better off tossing the coin :)

Have a very nice Sunday, Avery!
Michal

NOTE again: if you can't see the graph and attachements, please log in

Posted: Sun May 28, 2006 1:25 pm
by TheRumpledOne
Perhaps there's a better question...

What is the likelyhood that if the price closes above/below the previous middle that it will go higher/lower and by how much?

If we discover it goes higher 9 out of 10 times and usually by 10 pips or more, then we have a "money machine".

Posted: Mon May 29, 2006 12:54 pm
by TheRumpledOne
ghkramer found a bug in your code:


ghkramer


518 Posts
Posted - 05/28/2006 20:49:23

--------------------------------------------------------------------------------

Something doesn’t look right with your results – they are counter-initiative to trending markets, which in my experience do exist! And I've made money off of them! So I did a quick and dirty walk through of your code. There’s at least one error in your code:

Change:
aZoneNotChanged[vCounter[1]] = aZoneChanged[vCounter[1]] + 1;

To:
aZoneNotChanged[vCounter[1]] = aZoneNotChanged[vCounter[1]] + 1;

This is the result of this change for YHOO daily:

Posted: Mon May 29, 2006 2:01 pm
by michal.kreslik
Sure, I know :)

2 minutes ago, I have just thanked ghkramer for his effort on the TS forum.

I felt there was something fishy about the results :)

I've got several other interesting ideas to incorporate in this test now it gives sensible results.

So stay tuned, I will post the findings here in this thread.

So the tendency is indeed there!

Michal

Results

Posted: Tue May 30, 2006 8:49 pm
by misterclean999
So when are we going to see the real results?, Sorry that doesn't sound right and / or appreciative. that I am immensely. Anyway, I use the middle indicator, not as written about, but as a filter for a small list ( 20 ) of stocks to tell me whether to take only long os short trades on that particular issue. So I am very interested because this may help me tweark this for further accuracy.

Thanks

Mr Clean

Posted: Tue May 30, 2006 10:39 pm
by michal.kreslik
Mr Clean,

please give me some time :) I am trading and working on another projects, too :)

The rewritten code will produce results in 3D graph.

So stay tuned.

Middle

Posted: Wed May 31, 2006 1:10 am
by misterclean999
Certainly Mike, no rush didn't mean for it to seem that way. I appreciate all tha tyou, TRO and any of the posters do on these forums.

THx

3DZone stats

Posted: Sat Jun 03, 2006 3:35 am
by michal.kreslik
NOTE: if you can't see the 3D graphs and file attachements at the end of this post, please LOG IN

Well finally I have done it.

I had to rewrite the code from scratch to incorporate several new ideas into the concept that take the whole thing a littlebit further.

First of all, there are three figures that explain how the Zones are treated within the statistics. The Zone is defined as an area either above or below the particular percentage. The percentage is calculated from the extreme prices of the bar (high & low) to the inside of the bar.

Have a look at Figure 1 below:



This was the initially tested setup with the 50% Zones. The only difference is that now the area in between the upper Zone and the Lower Zone is considered to be of "no Zone" regardless of the previous bar's Zone.

Now have a look at Figure 2:



What we did was that we changed the setting from the 50% Zone (the originally tested MidPoint) to 30% Zone. The bar's close price is considered to be in this 30 % Zone only if it closes above the 30 % boundary (above 1.2770 price) or below the 30 % boundary (below 1.2730 price). So for example, if it closes at 1.2770, 1.2750 or 1.2730, it has closed in the no Zone area (I'm almost tempted to call it a twilight Zone :)

If you now look at Figure 3, then it starts to get really hot:



We are now examining whether the bar's close was outside the previous bar's range and at least 20% off of either previous bar's high or low. I call this a -20% Zone.

You can examine any percentage setting from minus infinity to +50%. Why the +50% is the maximum setting? Because above 50% the Zones would overlap. If you set the Zone % to zero (0 %), then you are examining whether the bar closed anywhere above or below the previous bar's range.

Now imagine that you will run stats with not only one Zone percentage setting, but with a whole range from -150% to +50%. This was what I did and you are encouraged to do further experimentation with my code (attached below). I've set the resolution (the smallest step) to be 10%, so the examined percentages were -150%, -140% ... to +50%.

Just a little refresher here, the statistics counts what is the probability of the next bar to close in the same Zone as the current bar did if we know how many consecutive bars (including the current one) has had the close in this Zone (either upper Zone or the lower Zone).

The statistics plots the Zone percentages on one axis, the number of consecutive bars so far on the second axis and the probability in % that the next bar will stay in the same Zone on the third axis. To illustrate, let's have a look at the following table and notice the highlighted cell in the middle:



It shows that in EURUSD 15 min statistics, there is a 26.96% probability that the next bar will close in the same Zone (0 % Zone - outside of the previous bar, row 0 in the table) if the bar has closed in this 0% Zone for 4 consecutive bars already (column 4 in the table). Keep in mind that the results are additive, so that if there is a 26.96% probability that the next bar will close in the same Zone, then there is also a 73.04% (100 - 26.96) probability that the next bar won't close in the same Zone after 4 consecutive closes in the current Zone. (Why? Well because the bar has to close somewhere after all and it only may close either in the present Zone or outside the present Zone).

For the bar's close to be considered in the same Zone, it has to close on the same side of the previous bar, i.e. either in the upper Zone or in the lower Zone.

Instead of the probabilities, you may also choose the program to print the number of cases (observations) by changing the iPrintProbOrCases parameter. This means that you will see in the output table how many times there was a case when the bar closed in the particular Zone for a particular number of consecutive bars. To illustrate, have a look at the following table:



It shows that there were 2018 cases in the data sample of EURUSD 15 min where the bar closed on the same side and outside the previous bar completely (0% Zone, row 0) for a total of 3 consecutive bars, including the current one (column 3). You see, using the statistics is pretty simple.

Of course, there's more to the stats than just plain boring excel sheets :) You wouldn't get a general view of what's going on behind the scenes by just observing the plain numbers in the spreadsheet. Thus, attached you will find 3D graphs with probabilities and number of cases for several example symbol/timeframe combinations. Go ahaed and use my program to export the data by yourself and create similar 3D graphs in excel with a single click of your mouse for the symbol/timeframe/settings of your wish.

Please keep in mind that if you want to change the program settings, you need to do this manually by hand right in the first section of the program labelled "global declarations". I'm sorry for this inconvenience, but unfortunately, EasyLanguage doesn't support dynamically allocated multidimensional arrays nor does it support assinging the static array boundaries via inputs :( All the parameters are well commented by me in the code, so you shouldn't have any problem changing them by hand. If so, feel free to contact me and I'll help you. After all, if you don't want to change any settings, just leave the code alone and apply it to the chart, it will work as is.

What's the conslusion from this research? There are clearly detectable and statistically signifiant tendencies in this concept. If you focus on refining and incorporating these statistics into your system development, it may bring you real money.

After all, everything you need as a foundation for a successful trading system is a phenomenon which occurs with other than 50% probability consistently and its occurence is statistically significant. This research uncovers such a phenomenon :)

Have a very nice day!
Michal

Attached:
- ELD file
- 3D graphs
- sample output statistics in zip file

code:

Code: Select all

{
   program name: 3DZone statistics
   last change: Jun/03/2006
   version: 5.23
   author: Michal Kreslik
   email: michal.kreslik @ kreslik.com
   website: http:// kreslik.com
   comments: prints 3D Zone statistics
}


{ ***** begin global declarations ***** }
   
vars: { inputs }
   iZonePercentStart( -150 ),
   { first   % Zone to be examined, you may use negative % }
   iZonePercentEnd( 50 ),
   { last % Zone to be examined, max: 50% }   
   iTotalNumberOfZones( 21 ),
   { how many Zones }   
   iMaxConsecutiveBars( 12 ),
   { max number of consecutive bars in the same Zone }   

   iMinNumOfCases( 50 ),
   { minumim number of observations to deem the results statistically significant }   

   iFileFolder( "c:\data\temp" ),
   { directory to save the resulting files(s) to }
   iPercentDecPrecision( 2 ),
   { number of decimal places to use for the export of % proabbilities }   
   iStatInsignChar( -1 ),
   { character or number to use for a statistically insignificant value }   
   
   iAppendMode( false ),
   { if set to true, then append the results to the existing file
   if set to false, create a brand new file for every test }
   iRepeatCells( true ),
   { repeat columns in the output file to allow for more homogenous 3D display }   
   iPrintProbOrCases( 0 );
   { print the restuls either as
   - % probabilities (set this variable to 0)
   - the number of cases/observations (set this variable to 1) }


{ ***** begin array inputs
WARNING! you must set the following array boundaries by hand since EasyLanguage
does not allow for array boundaries to be set by an input variable! ***** }

arrays: { inputs }
   anCasesOfStay[21, 12]( 0 ), { main array }
   anCasesOfAbandonment[21, 12]( 0 ), { main array }

   anUpperZoneBndaries[21]( 0 ),
   anLowerZoneBndaries[21]( 0 ),

   anZoneState[21]( 0 ), { -1 = lower Zone, 0 = no Zone, 1 = upper Zone}
   anZoneStayCounter[21]( 0 ), { always >= 1 }

   anZonePercentages[21]( 0 ),
      
   abRunStatsFThisZone[21]( false ),
   abZoneAbandoned[21]( false );

{ ***** end array inputs **** }


vars: { numerical }
   nZonePercentageStep( 0 ),
   nTradingDays( 0 ),
   nRealMaxConsecBars( 0 ),
   nRepeatCellCount( 0 );

vars: { string & temporary}
   sStartDate( "" ),
   tZoneIndex( 0 );

vars: { boolean }
   bMaxBarsExceeded( false ),
   intrabarpersist bFirstRun( true );

{ ***** end global declarations ***** }


{ ***** begin program ***** }
   
if iZonePercentEnd <= 50 AND iMinNumOfCases > 0 then
   begin


      { ***** begin data collection ***** }
   
      if BarNumber = 1 then
         begin
            sStartDate = DateToString(DateToJulian(date));   
            nZonePercentageStep = (iZonePercentEnd - iZonePercentStart)/(iTotalNumberOfZones - 1);
            for tZoneIndex = 1 to iTotalNumberOfZones
               begin
                  anZonePercentages[tZoneIndex] = iZonePercentStart + (tZoneIndex - 1)*nZonePercentageStep;
               end;
         end;
      
      if date <> date[1] then
         nTradingDays = nTradingDays + 1;
      
      for tZoneIndex = 1 to iTotalNumberOfZones
         begin
            abZoneAbandoned[tZoneIndex] = false;
      
            anUpperZoneBndaries[tZoneIndex] = high - (high - low)*anZonePercentages[tZoneIndex]/100;
            anLowerZoneBndaries[tZoneIndex] = low + (high - low)*anZonePercentages[tZoneIndex]/100;
      
            if close > anUpperZoneBndaries[tZoneIndex][1] then
                  anZoneState[tZoneIndex] = 1
               else if close < anLowerZoneBndaries[tZoneIndex][1] then
                  anZoneState[tZoneIndex] = -1
               else if close >=anLowerZoneBndaries[tZoneIndex][1] AND
                  close <= anUpperZoneBndaries[tZoneIndex][1] then
                  anZoneState[tZoneIndex] = 0;
      
            if anZoneState[tZoneIndex] <> anZoneState[tZoneIndex][1] then
               begin
                  abZoneAbandoned[tZoneIndex] = true;
                  anZoneStayCounter[tZoneIndex] = 1;
               end
            else
               anZoneStayCounter[tZoneIndex] = anZoneStayCounter[tZoneIndex] + 1;
      
            if abRunStatsFThisZone[tZoneIndex] = false
               AND abZoneAbandoned[tZoneIndex]
               AND anZoneState[tZoneIndex] <> 0
               AND BarNumber > 1 then
                  abRunStatsFThisZone[tZoneIndex] = true;
                  
            if abRunStatsFThisZone[tZoneIndex][1]
               AND anZoneState[tZoneIndex][1] <> 0 then
                  begin
                     nRealMaxConsecBars = MaxList(nRealMaxConsecBars, anZoneStayCounter[tZoneIndex][1]);
            
                     if anZoneStayCounter[tZoneIndex][1] <= iMaxConsecutiveBars then
                        begin
                           if abZoneAbandoned[tZoneIndex] then
                              anCasesOfAbandonment[tZoneIndex, anZoneStayCounter[tZoneIndex][1]]
                              = anCasesOfAbandonment[tZoneIndex, anZoneStayCounter[tZoneIndex][1]] + 1
                           else
                              anCasesOfStay[tZoneIndex, anZoneStayCounter[tZoneIndex][1]]
                              = anCasesOfStay[tZoneIndex, anZoneStayCounter[tZoneIndex][1]] + 1;
                        end
                     else
                        bMaxBarsExceeded = true;
                  end;
         
         end; { for tZoneIndex = 1 to iTotalNumberOfZones }
      
      { ***** end data collecion ***** }
      
      
      { ***** begin analysis **** }
      
      if LastBarOnChart AND bFirstRun then
         begin
            bFirstRun = false;
            if iRepeatCells then
               nRepeatCellCount = round(iTotalNumberOfZones/iMaxConsecutiveBars, 0)
            else
               nRepeatCellCount = 1;


            { ***** begin local declarations ***** }
            
            vars: { string }
               sHeader( "" ),
               sTimeFrame( "" ),
               sFileName( "" ),
               sPrintCell( "" ),

               sExtension( "" ),
               sFileNameProb( "" ),
               sFileNameCases( "" );

            vars: { numerical & temporary }
               nTotalCases( 0 ),

               tConsecBarIndex( 0 ),
               tRepeatCell( 0 );
      
            arrays: { string arrays }
               asTimeFrames[4]( "" ),
               asHeaderRows[]( "" );
      
            { ***** end local declarations ***** }               

      
            { ***** begin set up the strings *****}
      
            sExtension = ".csv";
            sFileNameProb = "3D_Zone_Stat_Probabilities";
            sFileNameCases = "3D_Zone_Stat_Cases";

            asTimeFrames[0] = NumToStr(BarInterval, 0) + "_ticks";
            asTimeFrames[1] = NumToStr(BarInterval, 0) + "_min";
            asTimeFrames[2] = "daily";
            asTimeFrames[3] = "weekly";
            asTimeFrames[4] = "monthly";
            sTimeFrame = asTimeFrames[datacompression];
         
            sHeader = "";
            sHeader = sHeader + "3DZone statistics by michal.kreslik @ kreslik.com" + NewLine;
            sHeader = sHeader + "website: www.kreslik.com" + NewLine;
            sHeader = sHeader + "*****" + NewLine;
            sHeader = sHeader + "Zone start: " + NumToStr(iZonePercentStart, 0) + "%" + NewLine;
            sHeader = sHeader + "Zone end: " + NumToStr(iZonePercentEnd, 0) + "%" + NewLine;
            sHeader = sHeader + "Zone count: " + NumToStr(iTotalNumberOfZones, 0) + " zones" + NewLine;
            sHeader = sHeader + "Zone step: " + NumToStr(nZonePercentageStep, 0) + "%" + NewLine;
            sHeader = sHeader + "*****" + NewLine;
            sHeader = sHeader + "Max consecutive bars setting: " + NumToStr(iMaxConsecutiveBars, 0) + " bars" + NewLine;
            sHeader = sHeader + "Max consecutive bars exceeded: " + IffString(bMaxBarsExceeded, "yes", "no") + NewLine;
            sHeader = sHeader + "Max consecutive bars necessary: " + NumToStr(nRealMaxConsecBars, 0) + NewLine;
            sHeader = sHeader + "*****" + NewLine;
            sHeader = sHeader + "Total bars examined: " + NumToStr(BarNumber, 0) + NewLine;
            sHeader = sHeader + "Time period: " + sStartDate + " to " + DateToString(DateToJulian(date)) + NewLine;
            sHeader = sHeader + "Trading days: " + NumToStr(nTradingDays, 0) + NewLine;
            sHeader = sHeader + "*****" + NewLine;
            sHeader = sHeader + "Exchange: " + GetExchangeName + NewLine;
            sHeader = sHeader + "Symbol: " + GetSymbolName + NewLine;
            sHeader = sHeader + "Timeframe: " + sTimeFrame + NewLine;
            sHeader = sHeader + "*****" + NewLine;
            sHeader = sHeader + "Repeat cells: " + IffString(iRepeatCells, "yes", "no") + NewLine;
            sHeader = sHeader + "Stat. validity - min # of cases setting: "   + NumToStr(iMinNumOfCases, 0) + NewLine;
            sHeader = sHeader + "Printing: " + IffString(iPrintProbOrCases = 0, "probabilities of stay", "number of cases") + NewLine;
            sHeader = sHeader + NewLine;

            { ***** end set up the strings *****}
      
      
            { ***** begin print to file section ***** }

            if iAppendMode then
               if iPrintProbOrCases = 0 then
                     sFileName = iFileFolder + "\" + sFileNameProb + sExtension
                  else
                     sFileName = iFileFolder + "\" + sFileNameCases + sExtension
               else
                  begin
                     sFileName = iFileFolder + "\"
                        + GetSymbolName + "_"
                        + sTimeFrame + "_"
                        + IffString(iPrintProbOrCases = 0, sFileNameProb + sExtension, sFileNameCases + sExtension);
                     FileDelete(sFileName);
                  end;

            FileAppend(sFileName, sHeader);
            for tZoneIndex = 1 to iMaxConsecutiveBars
               begin
                  for tRepeatCell = 1 to nRepeatCellCount
                     begin
                        FileAppend(sFileName, "," + NumToStr(tZoneIndex, 0));
                     end;
               end;
      
            for tZoneIndex = 1 to iTotalNumberOfZones
               begin
                  FileAppend(sFileName, NewLine + NumToStr(anZonePercentages[tZoneIndex], 0));
      
                  for tConsecBarIndex = 1 to iMaxConsecutiveBars
                     begin
                        nTotalCases = anCasesOfStay[tZoneIndex, tConsecBarIndex]
                           + anCasesOfAbandonment[tZoneIndex, tConsecBarIndex];
                        if nTotalCases >= iMinNumOfCases then
                           if iPrintProbOrCases = 0 then
                                 sPrintCell = "," + NumToStr(anCasesOfStay[tZoneIndex, tConsecBarIndex]/nTotalCases*100, iPercentDecPrecision)
                              else sPrintCell = "," + NumToStr(nTotalCases, 0)
                           else
                              sPrintCell = "," + NumToStr(iStatInsignChar, 0);               
      
                        for tRepeatCell = 1 to nRepeatCellCount
                           begin
                              FileAppend(sFileName, sPrintCell);
                           end;
                     end;
               end;
      
            FileAppend(sFileName, NewLine + NewLine);
      
            { ***** end print to file section ***** }
      
      
         end; { if LastBarOnChart AND bFirstRun then }
      end; { if iZonePercentEnd <= 50 AND iMinNumOfCases > 0 then }

{ ***** end program ***** }