When a strategy is using a custom indicator or custom function to calculate values for limit and stop orders, it is often desirable to plot the custom indicator on the same chart used by the strategy. This allows the user to verify that trades are occurring at the appropriate levels, since the trade signals generated by the strategy can be compared to the indicator values on the chart.

Here is an example of a reversion to the mean trading strategy that has a custom RibbonsPlotter indicator plotting a linear regression center line and deviation ribbons spaced ATR multiples away from the center line.

custom ribbon indicator inserted into strategy chart

Fig. 1.  Custom Ribbon Indicator Inserted into Strategy Chart

To make the values of the RibbonPlotter indicator available to the strategy, one could duplicate the custom indicator code in a function or method, and call this from the strategy to obtain the desired values.  This is a common method of making values of the indicator available to the strategy.  However, there are several problems with this method.

 Problems with Duplicating Indicator Code in Strategy

  1. Code duplication creates additional programming work when modifying and improving the code as changes have to be made in two places.
  2. Both versions of the code have to be verified to be calculating the same values.
  3. Some custom indicators involve entering many parameters. It is tedious for the user to continually have to check the input parameters of both the indicator and the strategy to verify that they are the same. Having to do so leaves the trading system vulnerable to inadvertent errors if the input parameters become out of sync.
  4. Each time the strategy is optimized and the optimal input parameters values are adopted by the strategy, the indicator input parameters must be updated by the user to match these. This requires opening the strategy formatting window to see what the optimized parameters are, then opening the indicator formatting window and copying all of these values into the corresponding input variables, and finally, closing both formatting windows.
  5. CPU has the additional workload of making the same calculations twice.
  6. The indicator does not know the trading state of the strategy, and cannot adjust for trading activity.

Life would be so much simpler if the strategy could plot the custom indicator values directly from within the strategy.

If that were possible, it would also ensure each time the strategy was optimized, the optimized values of the input parameters would be correctly plotted superimposed on the trading signals on the chart without having to separately adjust the chart indicator values to match the parameter values determined by strategy optimization.  This facilitates verification that the trades were being made at appropriate price levels. 

Unfortunately, Plot[n] statements cannot be used from within a strategy. They may only be used within an indicator. Therefore, an alternate method of generating plots from within a strategy is needed. However, trend lines CAN be drawn from within a strategy providing a solution to the dilemma.

 A Solution That Eliminates Code Duplication

  1. Create a custom plotting function, TLPlot, which allows the plotting of values calculated in a strategy directly on the strategy chart.
  2. Convert the RibbonPlotter indicator to an equivalent function that can be called from the strategy to delivery the necessary values for trading decisions
  3. Plot the RibbonPlotter values directly on the chart from within the strategy by calling custom function  TLPlot. 
  4. Remove the RibbonPlotter indicator from the chart, eliminating the code duplication.

Advantages of Plotting Directly From Within a Strategy

  1. Eliminates code duplication and the labor involved in maintaining two sets of code performing similar functions.
  2. More efficient CPU utilization.
  3. Eliminates the need to adjust indicator parameters after strategy optimization.
  4. Allows strategy to turn on and off plotting of some information according to Marketposition.  For example, trailing stops can be plotted below the price action if in a long position, and above the price action if in a short position.  There is no need to display both stops simultaneously, as an indicator would do.
  5. Ensures input values for custom functions using in strategy decisions are the same values used for plotting the custom functions.
  6. Allows the strategy logic as well as the custom function values to be verified by observation on the chart rather than extensive testing and debugging.
  7. No synchronization between Global Variable sender and receiver is necessary.

The TLPlot function, described below, reproduces the functionality of the Plot[n] statement creating a series of trend line segments to produce the plot. Such trend line plots can be formatted as solid, dotted and or various dashed lines in any color specified by the user.

Since the plotting is being done from within the strategy, the need for the user to keep indicator input parameters and strategy input parameters in sync is eliminated. Each time the strategy is optimized, the new optimized input parameters are automatically plotted correctly without further user intervention.

This next chart shows a linear regression ribbon plotted directly from the strategy, using the TLPlot function:

Ribbon function plotted directly from within strategy using function TLPlot

Fig. 2.  Ribbon function plotted directly from within strategy using function TLPlot

The strategy illustrated above is a reversion to the mean trading system.  When the price reaches 1 ATR deviation from the linear regression centerline, a trade is taken back toward the centerline.  When the centerline is reached, the trade exits.

The TLPlot function, used in the above chart, reproduces the functionality of the Plot[n] statement creating a series of trend line segments to produce the plot. Such trend line plots can be formatted as solid, dotted, and or various dashed lines in any color or line thickness specified by the user.  Additionally, the plotline type (solid, dashed, etc.) can be controlled programmatically.  This is not possible with the Plot[n] instructions of an indicator.  This setting is only accessible in indicator formatting.

Advanced Features Only Available When Plotting from Within A Strategy

  1. Notice that outermost cyan bands in Fig. 2 above have several flat sections, indicated by the yellow arrows. The outer band is being used as a stop loss level once a trade is entered.  It is held constant after the entry to ensure the stop loss maintains a constant value from the entry price.  Once the trade is exited, the outer bands resume values that are +2 and -2 ATR units from the linear regression centerline.  This type of control of plotting is only possible if the plot is generated within the strategy.
  2. Another advanced feature would be plotting a trailing stop only for the duration of a trade and placing it below or above the price action depending upon whether the trade was long or short.

Function TLPlot

Function TLPlot is called using the following syntax:

value1 = _TLPlot ( PlotNr, Price, Length, Offset, Style, Color, Size );

 This is similar to the Plot[n] statement syntax:

Plot(n) [Offset] (Price, “PlotName”, Color, default, Width );

The TLPlot function can also programmatically set the line style to solid, dotted or one of three dashed formats, making it somewhat more versatile than the Plot[n] statement, whose line style can only be changed by manually formatting the indicator.

The plot is created by a series of bar-to-bar trend lines.  To limit the number of trend line segments used, the input parameter Length specifies how many historical bars up to the current bar will show the plot.  Trend line segments used to create the plot are recycled by storing their IDs in a circular array.  There is one circular array for each PlotNR being used.  Up to 10 independent plots can be accommodated by the function.  All plots must be on the main price chart.  Plotting cannot be done on sub-charts.

Function  TLPlot input parameters

Function TLPlot Input Parameter Description

Typical Usage In MA CrossOver Strategy

MA Crossover Strategy Plotting Moving Averages From Within Strategy

Fig. 3.  MA CrossOver Strategy Plotting Moving Averages From Within the Strategy

Function TLPlotVec  (3/15/17)

The original TLPlot function was developed in 2009.  A version using vectors rather than arrays to store trendline IDs,  TLPlotVec, was developed by David Pedersen (dbf16 on the TS Forum) in 2017 and generously contributed to this post.

TLPlotVec has a similar structure to TLPlot, except for the replacements of arrays with vectors, uses more efficient trendline objects for plotting, uses less memory, and runs more efficiently.  Dave’s efforts are much appreciated.

Input parameters and usage for TLPlotVec are identical to those of TLPlot.

 Function TLPlotFast (10/14/21)

TLPlotFast is an updated version of the original code.  It is a complete rewrite, notably leaner, faster, and uses less memory than previous versions.  It is recommended that users of the original TLPlot function replace these with the function TLPlotFast.

Circular arrays and circular vector stacks have been eliminated.  TLPlotFast contains only 28 lines of code and uses trend line objects to perform its plotting.  In comparison, the original TLPlot function contained 253 lines of code.

An additional benefit is plotting can be performed across the entire chart and not just for the most recent Length bars.  PlotStyle has an additional plotting style of plotting points (dots) instead of lines, by setting the input parameter PlotStyle = 0.  This works by setting the beginning and ending point of the trendline to the same value.  For the point to be large enough to display on the chart, the PlotWidth must be at least 1.

Function TLPlotFast Input Parameters

PriceValue of function to be plotted
PriceOffsetNumber of bars Price is shifted to the right
PlotOffsetNumber of bars plot is shifted to the right
PlotStylePlot style (0=point (dot), 1=Tool_Solid, 2=Tool_Dotted, 3=Tool_Dashed, 4=Tool_Dashed2, 5=Tool_Dashed3
PlotColorPlot color (yellow, red, green, blue, cyan, white, etc.)
PlotWidthWidth of plot line

Usage

 value1 = _TLPlotFast( price, PriceOffset, PlotOffset, PlotStyle, PlotColor, PlotSize )

Typical Usage in MACrossover Strategy

inputs:
     Price(close),
     Length1(10),
     Length2(20),
     PriceOffset(0),
     PlotOffset(0),
     PlotStyle(1),
     PlotColor1(cyan),
     PlotColor2(yellow),
     PlotSize(1);

vars:
     MA1(0),
     MA2(0),
     bool Optimizing(GetAppInfo(aiOptimizing)=1);

MA1 = Average(Price, Length1);
MA2 = Average(Price, Length2);

if MA1 crosses above MA2 then Buy Next Bar at Market;
if MA1 crosses below MA2 then SellShort Next Bar at Market;

{ plot only when NOT optimizing }
if Optimizing = false then begin
     value1 = _TLPlotFast(MA1, PriceOffset, PlotOffset, PlotStyle, PlotColor1, PlotSize);
     value1 = _TLPlotFast(MA2, PriceOffset, PlotOffset, PlotStyle, PlotColor2, PlotSize);
end;

Downloads

Initial posted version: 10/03/09

Latest Update: 10/15/21

*.ELD files are compiled for TS 9.5

All ELD and code text files packaged here:

TLPlotFast.zip  (10/15/21)  Recommended Version

The original code may be visualized here:

Function TLPlot  (10/3/09)

A version generously contributed by David Pedersen (dpf16 on TS Forum) using vectors rather than arrays:

Function TLPlotVec  (3/15/17)

The latest version of the code:

Function TLPlotFast  (10/15/21)  (Recommended Version)

How to call this function from your strategy:

Sample Strategy Code Plotting with  TLPlotFast