{ Indicator: _LRChan Description: Draws linear regression channels with user specified Offset and Length. Uses OOEL timer for improved efficiency. User may drag the center trend line of the LR Channel to a new location and this indicator will redraw the associated deviation lines in the new location. Notes: UNCHECK "Update value intra-bar (tick by tick)" when formatting indicator for same performance with greater efficiency. Input parameter FreezeBeginPoint prevents the beginning point of the linear regression channel from moving once the indicator is applied to a chart, or from the time the user moves the indicator using the "user interactive" feature by dragging of the center trend line. Often _LinRegChan is used to monitor price action within a trend. A trend often begins at a pivot high or pivot low in the chart. If a user moves the beginning point of _LinRegChan to the point coinciding with the beginning of a trend, it is often desirable that this beginning point NOT be moved each time a new bar is added to the chart. Setting FreezeBeginPoint = true prevents the beginning point of the regression channel from shifting one bar to the right each time a new bar is added to the chart. The "length" of the channel therefore increases by one bar as each new bar is added to the chart. Setting FreezeBeginPoint = false causes the linear regression channel to shift one bar to the right (both begin and end points) as each new bar is added to the chart. To coordinate results with FxDeviation indicator on same chart, LinRegChan should use same offset but TgtBar should be left = 0. On FxDeviation, TgtBar = - Offset. LinRegChan.OOEL does not need to update each tick. The timer will generate a signal to check if The user has moved any trend lines intrabar. This is different from the non-OOEL version, LinRegChan, which DOES have to be formatted "Update each tick" for The user-interative functions To work. Alerts can be set on any of the deviation bands within the channel. The alert triggered is NOT The alert formatting of the _LRChan indicator. Instead it will be the preference setting Tradestation Message Center. This is because the alert is set on the trend lines that make up the deviation bands, and not by the EL reserved word ALERT. Warning: Maximum bars study will reference must be formatted for at least the number of bars visible in the chart window without scrolling PLUS the length of the center trend line. A variable defined in this routine, ForceMaxBarsBack, will force the maximum bars the study will reference to be at least 300 by default. If this indicator is used on a chart with more than 200 bars visible in the chart window (without scrolling), then the user should manually set "Maximum bars study will reference" to at least the number of bars that are visible in the chart window. The indicator will not perform corectly if used on tick or share bars when more than One bar has the same Date-Time stamp. This is because trend lines are positioned by declaring the Date-Time the TL begins and the Date-Time the TL ends. Author: MarkSanDiego Updated: 07/17/09 original version 08/06/09 move drawing code to function that both strategy and indicator can call 09/06/09 add user interactive feature 10/12/09 fixed bug in user interactive feature 02/05/10 fixed Marker_ID value assignment 04/30/10 allow user to specify if vertical marker will be drawn at offset point 07/30/10 Channel would not always follow center line when moved by user. Corrected. Force maximum bars back to be at least as many bars as visible on chart. 08/07/10 Corrected failure of distance between channel lines to recalculate when user moved center TL 08/15/10 Corrected failure of deviations lines to occasionally draw incorrectly after center line moved by user. 09/05/10 add ability to freeze beginning point of channel to startup default or any position the user moves the starting point to subsequently. 09/07/10 added error trapping with message if MaxBarsBack must be increased 10/13/10 fixed bug causing beginning point of trendline to move one bar to the left with each new bar instead of remaining "frozen" at original location when FreezeBeginPoint = true 03/15/11 Timer section converted to OOEL for improved efficiency. 05/09/11 Bug fixed in function _DrawLRChan 05/12/11 Removed initial drawing of indicator when LastBar was reached. This is unnecessary Using OOEL Timer code. 05/12/11 Bug fix. Some functions called by _DrawLRChan had variables that were not intrabarpersist. This caused erratic calculation of the distance between the parallel deviation lines. 05/12/11 Allow user to extend all trend lines except the center trend line to the right. Not extending the center TL makes it easier for The user to see which bars are included in the linear regression line calculation. 05/13/11 Add alerts to price crossing outer most deviation bands. When this feature is turned on, The alert band color will change to AlertBandColor. 05/18/11 Bug fix. With input parameter FreezeBeginPoint = false, channel would not advance one bar each time a new bar printed on chart. 07/11/11 Bug fix. FreezeBeginPoint was not working reliably. Logic changes corrected the bug. 07/14/11 Non-recursive functions used in deviation calculations so correct values are obtained if length of center trend line changed by user interactively. 07/14/11 Convert function DrawLRChan to a local Method for greater efficiency and to avoid passing large numbers of parameters to function. 09/20/11 Add marker dot at beginning and end of centerline. These markers remind user where beginning And end of analysis period is and also provide easy to see markers for dragging the ends of The center line. 09/28/11 Improve alert band feature to allow user to specify band number that will trigger alert. 12/03/11 Added BeginDate_YYYYMMDD. If user specifies, This over rides the begin date calculated from length and offset input parameters. Useful if user always wants LR channel to start on a specified date, such as the date of a major pivot point in the price action. 12/17/11 Remove input parameter LRTgtBar, since normally will always be zero. 03/02/12 Extend deviation lines to current bar. Keep center line end point at Offset to show user Range over which linear regression data is being calculated. Allow user to format indicator As "Update value intra-Bar (ticks by tick)" so deviation lines extend right up to current bar. This allows user to more easily see exactly where the deviation is for the current bar. A conditional IF In the main program prevents execution of the code with each tick for greater efficiency. 04/12/12 fix calculation so user can move center line endpoint such that EndOffset will be less than input parameter Offset. 07/30/12 adjust main program logic to draw channel immediately if real time data and with each new Bar after LastBarOnChart has been reached 08/09/12 fix bug in deviation calculations when user changes length of LR channel. 11/03/12 Function LinearReg replaced with faster algorithm in Method LinearRegFaster. } inputs: RefPrice(close), int Length(120), { linear regression channel length } int Offset(6), { linear regression channel offset } // TgtBar(0), { linear regression target bar, normally do not need to use non-zero values } DevID(2), { deviation function identifier } StdDevLength(0), { standard deviation length } StdErrLength(0), { standard error length } ATRLength(0), { average true range length } JATRLength(0), { Jurik moving average ATR - user must have Jurik routines installed } Percent(0), { percentage } Pts(1), { points } int NBands(3), { number of deviation bands drawn on both sides of center trend line } StartMult(1), { number of deviation units of innermost band from center trend line } Increment(1), { number of deviation units of subsequent bands from each other } FreezeBeginPoint(false), { freeze the beginning point of the TL channel to startup position or any position the center TL is moved to by the user } BeginDate_YYYYMMDD(0), { if specified , this begin date overrides calculated begin date } int AlertBand(0), { If non-zero, trigger alert when price hits this band number } AlertBandColor(red), { color of Alert band to remind user alert option is active } TLColor(cyan), { center line color } BandColor(DarkGray), { color of parallel bands around center line } LevelColor(yellow), { color of displayed band values } ShowMarker(true), { if true, show dot on center trend line to signify Offset point } MarkerColor(yellow), { vertical marker to signify Offset point } int MarkerPlotWidth(4), ExtendRight(true), { extend all regression channel lines to right } ExtendLeft(false), { extend all regression channel lines to left } int TLPlotWidth(0), { center line plotting width } int BandPlotWidth(0), { band lines plotting width } int CalcSeconds(2); { minimum interval in seconds that center line is check to see if it was moved by user } vars: intrabarpersist int BS(0), { Bar status } intrabarpersist Dev(0), { deviation } intrabarpersist DevLength(0), { length used for deviation function } Intrabarpersist MatchDevLength(false), { true if DevLength Not specified. In this case, match DevLength to linear regression Length } intrabarpersist LastBar(false), { true when LastBarOnChart reached } intrabarpersist int TL_ID(0), { trend line identification number } intrabarpersist Marker_ID(0), { trend line ID for drawing offset marker line } Intrabarpersist int CL_ID(0), { centerline TL ID } Intrabarpersist int CLBeginDot_ID(0), { centerline begin marker ID } Intrabarpersist int CLEndDot_ID(0), { centerline end marker ID } { output variables for LinearReg function } intrabarpersist oLRSlope(0), intrabarpersist oLRAngle(0), intrabarpersist oLRIntercept(0), intrabarpersist oLRValueRaw(0), intrabarpersist BeginValue(0), intrabarpersist EndValue(0), Intrabarpersist EndValue3(0), { variables for _StdDevMean } oMean(0), { variables for Method LinearRegFast } intrabarpersist TgtBar(0), intrabarpersist LastLength(0), // Intrabarpersist LROffset(0), intrabarpersist LastOffset(0), intrabarpersist double SumXY(0), intrabarpersist double SumY(0), intrabarpersist SumX(0), intrabarpersist SumXSqr(0), intrabarpersist Divisor(0), ForceMaxBarsBack(RefPrice[0]), { forces max bars back to be at least 400 if RefPrice[400] specified } { Method DrawLRChan variables } intrabarpersist int j(0), { miscellaneous looping variable } intrabarpersist int k(0), { miscellaneous looping variable } intrabarpersist x(0), { temporary variable } intrabarpersist BeginDate(0), intrabarpersist BeginTime(0), intrabarpersist BeginDate2(0), intrabarpersist BeginTime2(0), intrabarpersist EndDate(0), intrabarpersist EndTime(0), intrabarpersist EndDate2(0), intrabarpersist EndTime2(0), intrabarpersist EndDate3(0), intrabarpersist EndTime3(0), intrabarpersist int BeginOffset(0), intrabarpersist int EndOffset(0), Intrabarpersist int xLength(0), intrabarpersist int NDraw(0), intrabarpersist bool DrawOK(true), { switch to indicate when reg channel needs to be redrawn } Text1(""), { for future use labelling chart } Text2(""); { for future use labelling chart } arrays: int UBand[20](0), { stores trend line ID#'s of upper channels } int LBand[20](0); { stores trend line ID#'s of lower channels } // methods begin ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// { Methods declaration } var: elsystem.Timer Timer1( NULL ); Method override void InitializeComponent() begin Timer1 = new elsystem.Timer; Timer1.Interval = CalcSeconds * 1000; Timer1.AutoReset = true; Timer1.Enable = true; Timer1.Name = "Timer1"; Timer1.elapsed += Timer1_Elapsed; end; { run once every CalcSeconds seconds} {The Elapsed event handler is called when the timer counts down to zero} Method void Timer1_Elapsed( elsystem.Object sender, elsystem.TimerElapsedEventArgs args ) begin BS = -1; { all timer generated executions associated with BarStatus(1) = -1 (undefined) } DrawLRChan(); end; {--------------------------------------------------------------------------------------------------------------------------------} Method void CreateTL() begin { create center trendline } TL_ID = TL_New(Date, Time, 0, Date, Time, 0); if ExtendLeft then TL_SetExtLeft(TL_ID, ExtendLeft); // if ExtendRight then TL_SetExtRight(TL_ID, ExtendRight); { do not extend centerline to right so it's length can be easily seen } TL_SetSize(TL_ID, TLPlotWidth); TL_SetColor(TL_ID, TLColor); CL_ID = TL_ID; {create upper bands } for j = 1 to NBands begin TL_ID = TL_New(Date, Time, 0, Date, Time, 0); if ExtendLeft then TL_SetExtLeft(TL_ID, ExtendLeft); if ExtendRight then TL_SetExtRight(TL_ID, ExtendRight); TL_SetSize(TL_ID, BandPlotWidth); TL_SetColor(TL_ID, BandColor); UBand[j] = TL_ID; end; {create lower bands } for j = 1 to NBands begin TL_ID = TL_New(Date, Time, 0, Date, Time, 0); if ExtendLeft then TL_SetExtLeft(TL_ID, ExtendLeft); if ExtendRight then TL_SetExtRight(TL_ID, ExtendRight); TL_SetSize(TL_ID, BandPlotWidth); TL_SetColor(TL_ID, BandColor); LBand[j] = TL_ID; end; { if AlertBand <> 0 then set alert on this band number. Alert type is breakout intra/interbar } If AlertBand <> 0 then begin TL_SetAlert(UBand[AlertBand], 3); TL_SetAlert(LBand[AlertBand], 3); TL_SetColor(UBand[AlertBand], AlertBandColor); TL_SetColor(LBand[AlertBand], AlertBandColor); TL_SetStyle(UBand[AlertBand], Tool_Dotted); TL_SetStyle(LBand[AlertBand], Tool_Dotted); end; { create centerline end dot marker trendline to mark Offset point } TL_ID = TL_New(Date, Time, 0, Date, Time, 0); TL_SetSize(TL_ID, BandPlotWidth); TL_SetColor(TL_ID, MarkerColor); CLEndDot_ID = TL_ID; { create centerline begin dot marker trendline to mark beginning point of centerline } { this marker dot is a reminder that this is the point used to drag the centerline manually } TL_ID = TL_New(Date, Time, 0, Date, Time, 0); TL_SetSize(TL_ID, BandPlotWidth); TL_SetColor(TL_ID, MarkerColor); CLBeginDot_ID = TL_ID; end; { end Method CreateTL } {--------------------------------------------------------------------------------------------------------------------------------} Method void LinearRegFaster() begin if xLength > 1 then begin If xLength <> LastLength or EndOffset <> LastOffset then begin SumX = xLength * (xLength - 1) * .5; SumXSqr = xLength * (xLength - 1) * (2 * xLength - 1) / 6; Divisor = Square(SumX) - xLength * SumXSqr; SumXY = 0; for Value1 = 0 to xLength - 1 begin SumXY = SumXY + Value1 * RefPrice[Value1 + EndOffset] ; end ; SumY = Summation(RefPrice[EndOffset], xLength); End else begin SumY = SumY + RefPrice[EndOffset] - RefPrice[xLength + EndOffset]; SumXY = SumXY + (SumY - RefPrice[EndOffset]) - (xLength - 1) * RefPrice[xLength + EndOffset]; end; oLRSlope = ( xLength * SumXY - SumX * SumY) / Divisor ; // oLRAngle = ArcTangent( oLRSlope ) ; oLRIntercept = ( SumY - oLRSlope * SumX ) / xLength ; oLRValueRaw = oLRIntercept + oLRSlope * ( xLength - 1 - TgtBar ) ; LastLength = xLength; LastOffset = EndOffset; end; end; { end Method LinearRegFaster } { Method: DrawLRChan Description: Draw a linear regression trendline (center line) and deviation channels using trend lines drawn parallel to the center line. The regresion channel is user interactive. It is initially drawn according to the input parameters Length and Offset. Once drawn the user can move either end or both ends of the center trend line and withing CalcSeconds the regression channel will be redrawn with the Length and Offset matching the new location of the center trend line. To function correctly, MaxBarsBack should be at least equal to the number of bars visible within the chart window (without scrolling). Also, the number of bars loaded into the chart must be significantly more than the MaxBarsBack setting. Discussion: Created as a method so both strategy and indicator could use the same code without duplication. Having the stategy draw the LR channel is better than having an indicator and a strategy placed on the same chart since the parameters of the indicator do not have to be copied to the parameters of the strategy to ensure both are referring to the same LR channel. In this case, the parameters defined for the strategy are used to draw the LR channel directly, reducing the possibiliy of user error. Also, if the LR regression channel parameters are optimized, then at the completion of optimization the strategy will be set to the optimal values and the displayed regression channel will match these values without further user action, as would be required if an indicator was used to draw the regression channel. } Method void DrawLRChan() begin once begin { initialize deviations that never change } if DevID = 6 then begin Dev = Pts; Text2 = Text2 & "Points " & NumToStr(Pts, 2); end; { load appropriate deviation parameter } switch DevID begin Case 1: DevLength = StdDevLength; Case 2: DevLength = StdErrLength; Case 3: DevLength = ATRLength; Case 4: DevLength = JATRLength; end; if DevLength = 0 then begin DevLength = Length; { initialize DevLength to LR Length, if undefined } MatchDevLength = true; { if user changes LR Length, match DevLength to this new length } end; If BeginDate_YYYYMMDD > 0 then begin BeginDate = _YYYYMMDD.ToELDate(BeginDate_YYYYMMDD, "BeginDate_YYYYMMDD"); BeginOffset = _DateOffset(BeginDate); end else begin BeginOffset = Length + Offset; end; EndOffset = Offset; xLength = BeginOffset - EndOffset; BeginDate = Date[BeginOffset]; BeginTime = Time[BeginOffset]; EndDate = Date[EndOffset]; EndTime = Time[EndOffset]; DrawOK = true; { initialize to draw on first pass } end; { once initial trend lines are drawn, check for user moving trend lines } if NDraw > 0 then begin { determine if user moved end of center trend line } { test this with each Timer triggered function call AND with each new bar appearing on chart } TL_ID = CL_ID; EndDate = TL_GetEndDate(TL_ID); EndTime = TL_GetEndTime(TL_ID); if EndDate <> EndDate2 or EndTime <> EndTime2 then begin { user moved end of center line } EndOffset = _DateTimeOffset(EndDate, EndTime); if EndOffset < 0 then RaiseRunTimeError("MaxBarsBack must be increased to the number of bars visible on chart"); DrawOK = true; end; { determine if user moved beginning of center trend line } BeginDate = TL_GetBeginDate(TL_ID); BeginTime = TL_GetBeginTime(TL_ID); if BeginDate <> BeginDate2 or BeginTime <> BeginTime2 then begin { user moved beginning of center line } BeginOffset = _DateTimeOffset(BeginDate, BeginTime); if BeginOffset < 0 then RaiseRunTimeError("MaxBarsBack must be increased to the number of bars visible on chart"); DrawOK = true; end; { user moved one or both ends of trend line } If DrawOK then begin xLength = BeginOffset - EndOffset; end; end; { if Draw > 0 } { with each new bar on chart, move the trend line end points } If BS = 2 and NDraw > 0 then begin if FreezeBeginPoint = true then begin { BeginTime and BeginDate do not change, therefore offset and xLength must increase by 1 } BeginOffset = BeginOffset + 1; xLength = BeginOffset - EndOffset; BeginDate = Date[xLength + EndOffset]; BeginTime = Time[xLength + EndOffset]; BeginDate2 = BeginDate; BeginTime2 = BeginTime; end; DrawOK = true; end; { draw regression channel } if DrawOK then begin NDraw = NDraw + 1; DrawOK = false; { recalculate LR center line. Use function where length may vary. } { TgtBar is zero because channel projects to right to reach current bar. Its end values are calculated at Offset } // value1 = LinearReg(RefPrice[EndOffset], xLength, 0{TgtBar}, oLRSlope, oLRAngle, oLRIntercept, oLRValueRaw); // value1 = _LinearRegFaster(RefPrice[EndOffset], xLength, 0{TgtBar}, oLRSlope, oLRAngle, oLRIntercept, oLRValueRaw); LinearRegFaster(); BeginValue = oLRIntercept; EndValue = oLRValueRaw; EndValue3 = oLRValueRaw + oLRSlope * MinList(Offset, EndOffset); { recalculate deviation function. Use only functions where length may vary. } If MatchDevLength then DevLength = xLength; { adjust DevLength to xLength } switch DevID begin Case 1: Value1 = _StdDevMean(RefPrice[EndOffset], DevLength, 1, oMean, Dev); Text2 = Text2 & "Std Dev " & NumToStr(DevLength, 0); Case 2: { can not use _StdError, since length may not vary in this function } Dev = StdError(RefPrice[EndOffset], DevLength); Text2 = Text2 & "Std Err " & NumToStr(DevLength, 0); Case 3: Dev = _AvgTrueRange(DevLength)[EndOffset]; Text2 = Text2 & "ATR " & NumToStr(Dev, 0); { { uncomment if Jurik Moving Average add-on is installed } Case 4: Dev = _JMATrueRange(DevLength)[EndOffset]; Text2 = Text2 & "JURIC ATR " & NumToStr(DevLength, 0); } Case 5: Dev = RefPrice[EndOffset] * Percent / 100; Text2 = Text2 & "Percent " & NumToStr(Percent, 1); { Case 6 = Points. Since never changes, no recalculation required } end; { reposition LR channel } { reset Date/Time of TL beginning and end points} BeginDate = Date[xLength + EndOffset]; BeginTime = Time[xLength + EndOffset]; BeginDate2 = BeginDate; BeginTime2 = BeginTime; EndDate = Date[EndOffset]; EndTime = Time[EndOffset]; EndDate2 = EndDate; EndTime2 = EndTime; EndDate3 = Date[MaxList(EndOffset - Offset, 0)]; EndTime3 = Time[MaxList(EndOffset - Offset, 0)]; { draw center line only } TL_SetEnd(CL_ID, EndDate, EndTime, EndValue); TL_SetBegin(CL_ID, BeginDate, BeginTime, BeginValue); TL_SetEnd(CL_ID, EndDate, EndTime, EndValue); { draw deviation bands } for k = 1 to NBands begin x = (StartMult + (k - 1) * Increment) * Dev; TL_SetEnd (UBand[k], EndDate3, EndTime3, EndValue3 + x) ; TL_SetBegin (UBand[k], BeginDate, BeginTime, BeginValue + x) ; TL_SetEnd (LBand[k], EndDate3, EndTime3, EndValue3 - x) ; TL_SetBegin (LBand[k], BeginDate, BeginTime, BeginValue - x) ; { repeat TL_SetEnd to ensure deviation bands properly drawn } TL_SetEnd (UBand[k], EndDate3, EndTime3, EndValue3 + x) ; TL_SetEnd (LBand[k], EndDate3, EndTime3, EndValue3 - x) ; end; { draw dot on center trend line showing the offset when ExtendRight = true } { must repeat TL_SetEnd to ensure end marker is drawn correctly } if ShowMarker then begin TL_SetEnd (CLEndDot_ID, EndDate, EndTime, EndValue); TL_SetBegin (CLEndDot_ID, EndDate, EndTime, EndValue); TL_SetEnd (CLEndDot_ID, EndDate, EndTime, EndValue); TL_SetSize(CLEndDot_ID, MarkerPlotWidth); TL_SetEnd (CLBeginDot_ID, BeginDate, BeginTime, BeginValue); TL_SetBegin (CLBeginDot_ID, BeginDate, BeginTime, BeginValue); TL_SetEnd (CLBeginDot_ID, BeginDate, BeginTime, BeginValue); TL_SetSize (CLBeginDot_ID, MarkerPlotWidth); end; end; { Draw regression channel } end; { end Method DrawLRChan } // methods end //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// { Begin MAIN program } BS = BarStatus(1); { Find LastBarOnChart } Once (_LastBarOnChart) begin LastBar = true; if BS = 1 then DrawLRChan(); { draw immediately if real data } end; If BS = 2 then begin Once begin CreateTL(); { create trend lines } end; { call once every new bar after chart loads } if LastBar then begin DrawLRChan(); end; end;