// breakout-forward-study.afl
// Part 12 - Reality Check: Do High-Volume Breakouts Lead Anywhere?
//
// Measures what actually happened in the twenty bars after an objectively
// defined high-volume breakout, and - on a second run with one line changed -
// what happened in the twenty bars after EVERY eligible bar regardless of the
// condition. The second number is the base rate. Without it the first number
// means nothing at all, because a positive average after breakouts in a market
// that rose over the period is not evidence about breakouts.
//
// THIS FORMULA READS FUTURE BARS ON PURPOSE.
// Ref( Close, +20 ) looks twenty bars ahead. In a formula that produces Buy or
// Sell that would be look-ahead bias and the results would be worthless. Here
// we are measuring history that has already happened, which is a different
// activity: the study asks "what followed?", not "what should I do?". Never
// copy this line into a trading formula.
//
// DEFINITIONS - the whole point of the exercise is that these are arbitrary
// but explicit, so that somebody who disagrees can change one number and
// re-run rather than argue about words:
//   breakout    : today's close above the highest HIGH of the previous 50
//                 bars (the window is shifted back one bar, so today's own
//                 high cannot make the test trivially impossible)
//   high volume : today's volume at least twice the 50-bar average volume
//                 computed up to YESTERDAY, so today's volume does not inflate
//                 its own benchmark
//   eligible    : liquid and priced above a floor, measured to yesterday
//   outcome     : simple percentage change of the close over the next 20 bars
//
// ASSUMPTIONS AND KNOWN WEAKNESSES, stated before the numbers are seen:
//   - Daily bars, split- and dividend-adjusted. Unadjusted data manufactures
//     both breakouts and volume spikes on corporate-action bars.
//   - The database contains the symbols that exist today. Instruments that
//     were delisted are absent, so the measured outcomes are biased upward by
//     survivorship. This cannot be fixed from inside the formula; it can only
//     be reported.
//   - Rows are not independent observations. One symbol can contribute many
//     overlapping 20-bar windows, and symbols move together, so the effective
//     sample size is far smaller than the row count.
//   - No costs, no slippage, no position sizing, no risk model.

// ---- The one line you change between the two runs -----------------------
// False : measure only the bars where the condition was true
// True  : measure every eligible bar, which is the unconditional base rate
MeasureEveryBar = False;

Horizon       = 20;
BreakLookback = 50;
VolLookback   = 50;
VolMultiple   = 2;
MinTurnover   = 2000000;
MinPrice      = 2;

// Everything used to define the condition is shifted back one bar, so the
// condition is decidable from information available before today's close is
// compared against it.
PriorHigh   = Ref( HHV( High, BreakLookback ), -1 );
PriorAvgVol = Ref( MA( Volume, VolLookback ), -1 );
PriorTurn   = Ref( MA( Close * Volume, VolLookback ), -1 );

Eligible = Close > MinPrice AND PriorTurn > MinTurnover AND PriorAvgVol > 0;

Breakout   = Close > PriorHigh;
HighVolume = Volume > VolMultiple * PriorAvgVol;
Event      = Breakout AND HighVolume;

// The outcome. Ref() returns Null within Horizon bars of the end of the data,
// which is correct: those bars have no outcome yet and must not be counted as
// zero. Dropping them is what Measurable does.
FwdReturn  = 100 * ( Ref( Close, Horizon ) / Close - 1 );
Measurable = NOT IsNull( FwdReturn ) AND NOT IsNull( PriorHigh ) AND
             NOT IsNull( PriorAvgVol );

if( MeasureEveryBar )
    Population = Eligible;
else
    Population = Eligible AND Event;

Filter = IsTrue( Population AND Measurable );

AddColumn( Close, "Close", 1.2 );                                    // column 3
AddColumn( Volume / PriorAvgVol, "Vol / 50d avg", 1.2 );             // column 4
AddColumn( 100 * ( Close / PriorHigh - 1 ), "% above 50d high", 1.2 );// column 5
AddColumn( FwdReturn, "Fwd 20-bar %", 1.2 );                         // column 6
AddColumn( FwdReturn > 0, "Up in 20?", 1.0 );                        // column 7
AddColumn( DateTime(), "Bar (ISO)", formatDateTimeISO );             // column 8

// AVERAGE (2), MIN (4), MAX (8), STDEV (32) and COUNT (16) for the two
// outcome columns only. The average of column 7 is the proportion of
// observations that were positive; the count is the sample size; the standard
// deviation of column 6 is what tells you how little the average means.
// All of these rows appear at the TOP of the result list, not the bottom.
AddSummaryRows( 2 + 4 + 8 + 16 + 32, 1.2, 6, 7 );

SetSortColumns( -6 );
