/*
 *  Multi-indicator analysis panel - Part 10 project.
 *
 *  What it does
 *    Puts three separate readings in one pane, each in its own horizontal band:
 *      top    - trend: how far price sits from a long average, in ATR units;
 *      middle - momentum: RSI, with its 30/50/70 reference lines;
 *      bottom - volatility: where ATR-as-a-percentage-of-price sits inside its
 *               own recent history.
 *
 *  What it deliberately does not do
 *    It does not add the three readings together, score them, or call their
 *    agreement "confirmation". All three are transformations of the same close
 *    series, so they agree with each other for arithmetic reasons far more often
 *    than three independent measurements would. The panel puts them side by side
 *    so you can see the disagreement, which is the part that carries information.
 *
 *  Assumptions and limits
 *    - Insert as its OWN pane, below the price chart.
 *    - Nothing here is a signal, a score or a forecast.
 *    - RSI is Null for its first periods bars; ATR and the averages are
 *      seed-contaminated for longer than that. Read the left edge of the chart
 *      with suspicion.
 */

_SECTION_BEGIN( "Analysis panel" );

TrendPeriod = Param( "Trend average periods", 100, 20, 400, 5 );
AtrPeriod   = Param( "ATR periods", 20, 2, 100, 1 );
RsiPeriod   = Param( "RSI periods", 14, 2, 100, 1 );
RankPeriod  = Param( "Volatility look-back (bars)", 252, 20, 1000, 1 );
StretchCap  = Param( "Trend band limit (ATR units)", 4, 1, 12, 0.5 );
Compact     = ParamToggle( "Compact (small screens)", "No|Yes", 0 );

UpTint    = ParamColor( "Positive colour", colorBrightGreen );
DownTint  = ParamColor( "Negative colour", colorRed );
QuietTint = ParamColor( "Neutral colour", colorBlueGrey );
GuideTint = ParamColor( "Guide line colour", colorLightGrey );

/*
 *  Draws one series inside a horizontal slice of the pane.
 *
 *  minvalue and maxvalue are documented as being used by styleOwnScale plots
 *  only, and they are the sole control AFL gives over a plot's own Y range. So a
 *  band is produced by choosing bounds that place DataMin at BandBottom and
 *  DataMax at BandTop, where both band figures are fractions of the pane height
 *  running 0 at the foot to 1 at the top.
 *
 *  With scale bounds ScaleMin..ScaleMin+Span, a value v sits at the pane
 *  fraction ( v - ScaleMin ) / Span. Substituting v = DataMin gives BandBottom
 *  and v = DataMax gives BandTop, which is the whole trick.
 */
function PlotInBand( DataSeries, PlotName, PlotTint, StyleBits,
                     DataMin, DataMax, BandBottom, BandTop )
{
    local Span, ScaleMin;

    Span     = ( DataMax - DataMin ) / ( BandTop - BandBottom );
    ScaleMin = DataMin - BandBottom * Span;

    Plot( DataSeries, PlotName, PlotTint,
          StyleBits | styleOwnScale | styleNoLabel,
          ScaleMin, ScaleMin + Span );

    return Span;
}

// ---------------------------------------------------------------- trend band

TrendLine  = MA( Close, TrendPeriod );
Volatility = ATR( AtrPeriod );

// Distance from the average measured in ATR units, so the number means the same
// thing on any instrument, then clipped so one extreme bar cannot squash the
// rest of the band flat.
Stretch = ( Close - TrendLine ) / Volatility;
Stretch = Max( Min( Stretch, StretchCap ), -StretchCap );

StretchTint = IIf( Stretch > 0, UpTint, DownTint );

PlotInBand( 0, "", GuideTint, styleLine | styleNoTitle,
            -StretchCap, StretchCap, 0.70, 1.00 );

PlotInBand( Stretch, "Distance from trend (ATR units)", StretchTint,
            styleHistogram | styleThick,
            -StretchCap, StretchCap, 0.70, 1.00 );

// ------------------------------------------------------------- momentum band

Momentum = RSI( RsiPeriod );

PlotInBand( 30, "", GuideTint, styleLine | styleDashed | styleNoTitle, 0, 100, 0.36, 0.64 );
PlotInBand( 50, "", GuideTint, styleLine | styleNoTitle,               0, 100, 0.36, 0.64 );
PlotInBand( 70, "", GuideTint, styleLine | styleDashed | styleNoTitle, 0, 100, 0.36, 0.64 );

PlotInBand( Momentum, "RSI " + NumToStr( RsiPeriod, 1.0 ), QuietTint,
            styleLine | styleThick, 0, 100, 0.36, 0.64 );

// ----------------------------------------------------------- volatility band

AtrPercent = 100 * ATR( AtrPeriod ) / Close;
Rank       = PercentRank( AtrPercent, RankPeriod );

RankTint = IIf( Rank >= 80, DownTint, IIf( Rank <= 20, UpTint, QuietTint ) );

PlotInBand( 20, "", GuideTint, styleLine | styleDashed | styleNoTitle, 0, 100, 0.00, 0.28 );
PlotInBand( 80, "", GuideTint, styleLine | styleDashed | styleNoTitle, 0, 100, 0.00, 0.28 );

PlotInBand( Rank, "Volatility percentile", RankTint,
            styleHistogram, 0, 100, 0.00, 0.28 );

// ------------------------------------------------------------------- reading

SetChartOptions( 2, chartWrapTitle );

TrendWord = WriteIf( Stretch > 0.5, "above trend",
                     WriteIf( Stretch < -0.5, "below trend", "at trend" ) );

VolatilityWord = WriteIf( Rank >= 80, "wide range",
                          WriteIf( Rank <= 20, "narrow range", "ordinary range" ) );

if( Compact )
    _N( Title = StrFormat( "%s   %s   RSI %g   vol pct %g",
                           Name(), TrendWord,
                           SelectedValue( Momentum ), SelectedValue( Rank ) ) );
else
    _N( Title = StrFormat(
            "%s   trend: %s (%g ATR)   momentum: RSI(%g) = %g   volatility: %s, percentile %g\n"
            + "Three views of one price series. Agreement between them is not independent evidence.",
            Name(), TrendWord, SelectedValue( Stretch ),
            RsiPeriod, SelectedValue( Momentum ),
            VolatilityWord, SelectedValue( Rank ) ) );

_SECTION_END();
