TradeStation EasyLanguage Tutorials: Multiple-Output Functions:

TradeStation EasyLanguage Tutorial
Multiple Output Functions
In this TradeStation EasyLanguage Tutorial we will show one of the most powerful methods in TradeStation EasyLanguage, which is developing multiple–output functions using by ref parameters. This concept allow you to write very reusable functions and we will use several TradeStation functions in our example.

A basic user function framework

User functions are used to make reusable calculations, for example to calculate indicator values, sorting, and so on. User functions have input parameters. These parameters can be either input parameters or input/output parameters. These parameters can also have various types. Some of these types are NumericSimple, NumericSeries, NumericRef, StringSimple, numericarrayref, numericarray, and StringRef.

A function can return a single value, but when we use byref, we can return many values from a function. Let’s look at several function shells. ByRef passes the memory address of a variable which we can then fill the contents of that memory address within the user functions. That is how byref works. Normal parameters, such as NumericSimple, NumericSeries, and Stringsimple all just copy the contents to an address on the function stack. This means it can copy data into the function but can not modify the values that are passed in. Only the variable types with Ref suffix can be modified.

Let’s first look at a simple moving average function:

inputs: 
	Price( numericseries ), 
	Length( numericsimple ) ; { will get an error message if Length = 0 }

{ check Length input;  raise an error if it is zero, since this Length would cause a
 divide-by-zero error }
if Length <> 0 then
	Average = Summation( Price, Length ) / Length
else
	RaiseRuntimeError( "Average function cannot be called with a Length input of " +
	 "zero." )

We can see that we can pass a price series. This series can be a scalar or a system for example Close, High or (Close+High+Low)/3 which would be a scalar which will be passed though and converted to a numeric series. We also have the Length parameter which is a simple numeric value. Another interesting thing about this function is the use of the error trapping routine for divide by zero.

Let’s now look at a simple example of using input/output variables.

 
Inputs:Price(Numericseries),Slen(Numericsimple),oSimpleMA(numericref),oXAver(numericref),oHMAAver(numericref);
Vars: SimpleMA(0),XAver(0),HMAAver(0);
if SLen <> 0 then begin
oSimpleMA=Average(Price,Slen);
oXAver=XAverage(Price,Slen);
oHMAAver=hma(price,slen);
AllMyMovingAverages=true;
end
else begin
	RaiseRuntimeError( "Average function cannot be called with a Length input of " +
	 "zero." ) ;
	 Allmymovingaverages=false;
end;

This function returns three different types of moving averages in 1 function call. Let’s now see how this type of function is handy. 
Inputs: Price(Close),SLen(20);
Vars: oSMA(0),oXMA(0),oHMAX(0);
vars:Retvalue(false);
retvalue=AllMyMovingAverages(Price,SLen,oSMA,oXMA,oHMAX);

Plot1(oSMA, "SMA" ) ;

Plot2(oXMA, "XMA" ) ;
Plot3(ohmax, "HMA" ) ;

This is a very powerful concept. In TradeStation, the best example of how to use this concept is in the linear regression functions:

{ Linear Regression multiple-output function; see MULTIPLE-OUTPUT FUNCTIONS note 
  below }

inputs: 
	Price( numericseries ), 
	Length( numericsimple ), { Length > 1 }
	TgtBar( numericsimple ), { use negative integer for future, positive for past }
	oLRSlope( numericref ), 
	oLRAngle( numericref ), 
	oLRIntercept( numericref ), { left intercept, at vertical through 
	 Price[ Length - 1 ] }
	oLRValue( numericref ) ;

variables: 
	SumXY( 0 ), 
	SumY( 0 ), 
	SumX( 0 ), 
	SumXSqr( 0 ), 
	OneSixth( 1 / 6 ), 
	Divisor( 0 ) ;

if Length > 1 then 
	begin
	SumX = Length * ( Length - 1 ) * .5 ;
	SumXSqr = Length * ( Length - 1 ) * ( 2 * Length - 1 ) * OneSixth ;
	Divisor = Square( SumX ) - Length * SumXSqr ;
	SumXY = 0;
	for Value1 = 0 to Length - 1 
		begin
		SumXY = SumXY + Value1 * Price[Value1] ;
		end ;
	SumY = Summation( Price, Length ) ;

	oLRSlope = ( Length * SumXY - SumX * SumY) / Divisor ;
	oLRAngle = ArcTangent( oLRSlope ) ;
	oLRIntercept = ( SumY - oLRSlope * SumX ) / Length ;
	oLRValue = oLRIntercept + oLRSlope * ( Length - 1 + ExecOffset - TgtBar ) ;
	LinearReg = 1 ;
	end
else
	LinearReg = -1 ;
{

MULTIPLE-OUTPUT FUNCTIONS

A multiple-output function has two types of parameters or "inputs" - input parameters and input/output parameters.  The values of the input parameters are passed into the multiple-output function, but not modified by the function.  The values of the input/output parameters are passed into the multiple-output function, modified by it, and the modified values are then inherited by - or output to - the calling routine.

The input/output parameters are often used for output purposes only, i.e., the incoming values are ignored.  The outputs are in addition to the function return.  In multiple-output functions, the function return is generally used to return an error code, though sometimes the return may simply be a dummy value.

The input/output parameters are declared with a "ref" suffix (such as "numericref") in the multiple-output function's declaration statements.  For further clarity, the names of the input/output parameters are generally prefixed with an "o" in the function as well as in all the routines that call the function.

The built-in single-return WRAPPER FUNCTIONS that call the multiple-output functions are specialized calling routines designed to offer simplified, alternate pathways to the functionality of the underlying multiple-output functions.  In the wrapper functions, the input/output parameters are declared as local variables and generally initialized to zero.  They are passed through to the multiple-output function without further modification.  After the call, the wrapper function picks out the single output of interest and assigns it as the return of the wrapper function.

}

{ ** Copyright (c) 2001 - 2010 TradeStation Technologies, Inc. All rights reserved. ** 
  ** TradeStation reserves the right to modify or overwrite this analysis technique 
     with each release. ** }

This function gives us all of the variables that are used in regression models. Let’s look at the inputs for this function:

Price( numericseries ), 
	Length( numericsimple ), { Length > 1 }
	TgtBar( numericsimple ), { use negative integer for future, positive for past }
	oLRSlope( numericref ), 
	oLRAngle( numericref ), 
	oLRIntercept( numericref ), { left intercept, at vertical through 
	 Price[ Length - 1 ] }
	oLRValue( numericref ) ;

We can see that we can predict any number of bars in the past or future, we can return the slope, angle, intercept and the value. This allows this one piece of code to be used for many wrapper functions as well as in more complex models and systems and only do these calculations once. Let’s look at an example of how TradeStation used this in a wrapper. Let’s first look at the complete linear regression function.

{ Linear Regression multiple-output function; see MULTIPLE-OUTPUT FUNCTIONS note 
  below }

inputs: 
	Price( numericseries ), 
	Length( numericsimple ), { Length > 1 }
	TgtBar( numericsimple ), { use negative integer for future, positive for past }
	oLRSlope( numericref ), 
	oLRAngle( numericref ), 
	oLRIntercept( numericref ), { left intercept, at vertical through 
	 Price[ Length - 1 ] }
	oLRValue( numericref ) ;

variables: 
	SumXY( 0 ), 
	SumY( 0 ), 
	SumX( 0 ), 
	SumXSqr( 0 ), 
	OneSixth( 1 / 6 ), 
	Divisor( 0 ) ;

if Length > 1 then 
	begin
	SumX = Length * ( Length - 1 ) * .5 ;
	SumXSqr = Length * ( Length - 1 ) * ( 2 * Length - 1 ) * OneSixth ;
	Divisor = Square( SumX ) - Length * SumXSqr ;
	SumXY = 0;
	for Value1 = 0 to Length - 1 
		begin
		SumXY = SumXY + Value1 * Price[Value1] ;
		end ;
	SumY = Summation( Price, Length ) ;

	oLRSlope = ( Length * SumXY - SumX * SumY) / Divisor ;
	oLRAngle = ArcTangent( oLRSlope ) ;
	oLRIntercept = ( SumY - oLRSlope * SumX ) / Length ;
	oLRValue = oLRIntercept + oLRSlope * ( Length - 1 + ExecOffset - TgtBar ) ;
	LinearReg = 1 ;
	end
else
	LinearReg = -1 ;

We will now create a linear regression slope function which uses the multiple output function from above. This is an example of creating these super functions and using wrappers to use them for multiple purposes.

{ Single-return wrapper function calling multiple-output function; see MULTIPLE-OUTPUT 
  FUNCTIONS note below }

inputs: Price( numericseries ), Length( numericsimple ) ;
variables: oLRSlope( 0 ), oLRAngle( 0 ), oLRIntercept( 0 ), oLRValue( 0 ) ;

Value1 = LinearReg( Price, Length, 0, oLRSlope, oLRAngle, oLRIntercept, oLRValue ) ;

LinearRegSlope = oLRSlope ;

{
MULTIPLE-OUTPUT FUNCTIONS

A multiple-output function has two types of parameters or "inputs" - input parameters 
and input/output parameters.  The values of the input parameters are passed into the 
multiple-output function, but not modified by the function.  The values of the input/
output parameters are passed into the multiple-output function, modified by it, and 
the modified values are then inherited by - or output to - the calling routine.

The input/output parameters are often used for output purposes only, i.e., the 
incoming values are ignored.  The outputs are in addition to the function return.  In 
multiple-output functions, the function return is generally used to return an error 
code, though sometimes the return may simply be a dummy value.

The input/output parameters are declared with a "ref" suffix (such as "numericref") in 
the multiple-output function's declaration statements.  For further clarity, the names 
of the input/output parameters are generally prefixed with an "o" in the function as 
well as in all the routines that call the function.

The built-in single-return WRAPPER FUNCTIONS that call the multiple-output functions 
are specialized calling routines designed to offer simplified, alternate pathways to 
the functionality of the underlying multiple-output functions.  In the wrapper 
functions, the input/output parameters are declared as local variables and generally 
initialized to zero.  They are passed through to the multiple-output function without 
further modification.  After the call, the wrapper function picks out the single 
output of interest and assigns it as the return of the wrapper function.
}

{ ** Copyright (c) 2001 - 2010 TradeStation Technologies, Inc. All rights reserved. ** 
  ** TradeStation reserves the right to modify or overwrite this analysis technique 
     with each release. ** }

I hope this tutorial has explained how to use byref and make functions which return multiple outputs and how they can be used to simplify developing complex code and make valuable reusable functions.