// replay-workspace.afl
// Capstone Component 5 - the Level A path (no data subscription required)
//
// GOAL
//   The same decision workspace as the real-time version, driven by Bar Replay
//   instead of by a live feed. Everything about the DECISION is identical; only
//   the source of the next bar changes.
//
//   This is the path the course guarantees: it needs no subscription, no
//   Professional edition and no live feed. Real-time data requires the
//   Professional edition, and this course is completable without it.
//
// WHAT IT SHOWS
//   The trigger level, the context state, the distance to the level, whether
//   the newest bar is complete, and a running count of decision points. It
//   raises an alert on a qualifying bar and stops there.
//
//   ALERT -> HUMAN REVIEW -> DECISION. Nothing in this course places an order.
//
// HOW TO RUN
//   1. Apply this to a chart pane at your trading interval.
//   2. Tools -> Bar Replay. Set the replay to your trading interval and step
//      forward one bar at a time, or run it at a slow speed.
//   3. At each step, before advancing, write your decision in the log described
//      in the lesson. The point of the exercise is the record, not the chart.
//
// ASSUMPTIONS
//   Interval    any. On intraday intervals the session filter does real work.
//   Bar stamps  TimeNum() returns the START or the END of the interval
//               depending on Tools -> Preferences -> Intraday. Confirm which
//               convention your database uses before trusting the session gate.
//   Alerts      appear only if "custom indicators" is ticked under
//               Tools -> Preferences -> Alerts, "Enable alerts from".
//   Not modelled  costs, fills, size, and everything after the decision.

_SECTION_BEGIN( "Capstone replay workspace" );

SetChartOptions( 2, chartWrapTitle );

TriggerPeriod = Param( "Trigger look-back (bars)", 20, 2, 400, 1 );
AtrPeriod     = Param( "ATR period", 14, 1, 100, 1 );
BufferAtr     = Param( "Trigger buffer (x ATR)", 0.10, 0, 2, 0.05 );
TrendPeriod   = Param( "Trend average (bars)", 50, 2, 400, 1 );
SessionStart  = Param( "Session start (HHMMSS)", 0, 0, 235959, 100 );
SessionEnd    = Param( "Session end (HHMMSS)", 235959, 0, 235959, 100 );
UseSession    = ParamToggle( "Session filter", "Off|On", 0 );
AlertsOn      = ParamToggle( "Alerts", "Off|On", 1 );
AlertLookback = Param( "Alert lookback (bars)", 2, 1, 20, 1 );

// ----------------------------------------------------------- 1. the level
// Shifted back one bar, so the current bar is never part of the level it is
// being measured against.
PriorHigh = Ref( HHV( High, TriggerPeriod ), -1 );
Level     = PriorHigh + BufferAtr * ATR( AtrPeriod );
HaveLevel = NOT IsNull( Level );

TrendMa   = MA( Close, TrendPeriod );
ContextUp = Close > TrendMa;

Distance = SafeDivide( Level - Close, ATR( AtrPeriod ), Null );

// --------------------------------------------------------- 2. the event
// Cross() fires on the one bar where High first exceeded the level. The state
// "High > Level" stays true while price remains up there, which is the
// difference between one decision point and forty.
RawBreak = HaveLevel AND Cross( High, Level );

InSession = NOT UseSession
            OR ( TimeNum() >= SessionStart AND TimeNum() <= SessionEnd );

// The newest bar is still being built by Bar Replay: its high can still grow.
// Only completed bars are decision points.
BarComplete = BarIndex() < LastValue( BarIndex() );

NewSession = Day() != Ref( Day(), -1 );
FirstBreak = ExRem( RawBreak AND InSession, NewSession );
Trigger    = FirstBreak AND BarComplete;

// -------------------------------------------------------- 3. replay state
// GetPlaybackDateTime() returns the playback position, or zero when Bar Replay
// is not running. That is what lets a practice run be labelled as one and kept
// out of a real decision log.
Playback   = GetPlaybackDateTime();
InReplay   = Playback > 0;
ModeText   = WriteIf( InReplay, "[REPLAY] ", "[LIVE OR STATIC] " );

// ------------------------------------------------------------- 4. alert
AlertText = ModeText + "DECISION POINT  " + Name()
          + "  " + Interval( 2 )
          + "  bar "   + DateTimeToStr( LastValue( DateTime() ) )
          + "  high "  + NumToStr( LastValue( High ),  1.4 )
          + "  level " + NumToStr( LastValue( Level ), 1.4 );

// Lookback of 2 puts the most recent COMPLETED bar inside the window that
// AlertIf examines; the default of 1 is exactly the bar BarComplete excludes.
// Flag bit 8 - do not repeat alerts with the same date/time - is what keeps one
// completed-bar signal from being re-reported on every refresh.
if( AlertsOn )
{
    AlertIf( Trigger, "", AlertText, 1, 1 + 2 + 4 + 8, AlertLookback );
}

// ---------------------------------------------------------- 5. the chart
Plot( Close, "Close", colorDefault, styleCandle );
Plot( TrendMa, "Trend average", colorBlue, styleLine );
Plot( Level, "Trigger level", colorOrange, styleLine | styleStaircase );

PlotShapes( Trigger * shapeUpArrow, colorGreen, 0, Low, -20 );

RibbonTint = IIf( IsNull( TrendMa ), colorLightGrey,
                  IIf( ContextUp, colorPaleGreen, colorRose ) );
Plot( 1, "Context", RibbonTint, styleArea | styleOwnScale | styleNoLabel, 0, 100 );

// ---------------------------------------------------------- 6. the title
ContextWord = WriteIf( IsNull( TrendMa ), "no trend yet",
                       WriteIf( ContextUp, "above trend average",
                                "below trend average" ) );

_N( Title = StrFormat(
      "%s%s   %s\n"
      + "Context: %s   Trigger level %g   distance to level %g ATR\n"
      + "Newest bar complete: %s   raw breaks %g   decision points %g\n"
      + "This raises an alert. It does not place an order. "
      + "Alert -> your review -> your decision.",
      ModeText, Name(), Interval( 2 ),
      ContextWord, SelectedValue( Level ), SelectedValue( Distance ),
      WriteIf( BarComplete, "yes", "no - still forming" ),
      LastValue( Cum( RawBreak ) ), LastValue( Cum( Trigger ) ) ) );

_SECTION_END();
