The Fold Function Explained

Chuck

Moderator
Staff member
The Fold syntax is:
def <result> = fold <index> = <start> to <end> [ with <variable> [ = <init> ] ] [ while <condition> ] do <expression>;
Each component of the fold function will be explained separately. The function is not easy to use but understanding the purpose of the components will help you to feel comfortable with it.
General Comment:
  • The fold function is used to define the value for a named variable i.e. def <result>. You cannot operate on other variables or do anything within the fold. Studies may be used within a fold.
  • Rather than define a variable, the fold may be plotted directly i.e. def <result> = becomes Plot <result> =.
  • Remember that the fold calculation is executed at every bar as ThinkScript processes from bar 1 to the last bar.
  • As discussed in GetValue below, studies may be used in the Fold function especially in the do <expression>.
  • The names assigned <index> and <variable> are persistent variables. Hence, if you have two folds in a study and you assign 'idx' to <index> in the first fold you cannot assign 'idx' to <index> in the second fold. This will create an error.
  • Fold will normally work in a scan and custom columns. Complexity may become an issue especially if the servers are loaded up.
  • fold
  • A fixed word that identifies the following as a 'Fold' function.
  • <index> = <start> to <end>
  • This defines how many times the fold calculation loops on each bar. You need to figure out how many times "fold" needs to repeat itself, OR at what value it is going to stop churning away. Let’s say you want a calculation to repeat 5 times. If the <start> is at 0 and the <end> is at 5, then the calculation will repeat 5 times. <start> is inclusive but <end> is exclusive. When the counter gets to 5, fold stops and there is no results related to loop 5. <index> can be any name you want but 'i' or 'index' is commonly used e.g. i = 0 to 50. The value of the index can be used in the do <expression>. When <index> is used in the do statement, the last value of <index> is used and not the current value. The current value would be <index> + 1.
  • [ with <variable> [ = <init> ] ]
  • First of all, anything within brackets is optional. So when/why would you include this.?The answer lies in that this is an internal variable that fold uses. So when is it needed? If the 'do' section of the fold performs a activity like 'add to', 'multiply by' or similar, it must have a previous number to 'add to' for example. This 'with <variable>' is the value that will be added to when you see code like 'do nice + idx3'. This means that 'nice' is the with <variable> that fold has been keeping tract of internally and '+ idx3' is the current loop’s calculated value that is to be added to nice. 'nice + idx3' then becomes the new value of the internal variable nice and nice is available for the next loop’s calculation. <variable> can be any name you want to assign. In this example, 'nice' was used.
  • [ = <init> ] is the initial value of the 'with <variable>' and is optional. If it is omitted, then the default value of 0 is used. <init> is a number. Since it is in brackets, it is optional if there is a with <variable>.
  • [ while <condition> ]
  • This defines a condition, upon violation of which, the loop (not the fold itself) is terminated when calculating the fold function and TOS procedes to the next bar. The fold will do some action but that action may be subject to certain conditions. This [ while <condition> ] defines conditions/ limitations that are imposed on the actions that follow. The conditions may qualify the do-actions results or they may define conditions that terminate any further loops at the current bar. Conditions here do not preclude the 'do' statements from having an 'if' statement that may also set conditions but those conditions are used in getting the desired result from the 'do' statement. A example would look like
  • 'while close > 40'.
  • do <expression>
  • Defines an action to be performed, for each loop, when calculating the fold function. The do <expression> may be of numerous types. For example, if it is a true/false type then the fold results will be a true/false. Or it may be a arithmetic type like 'do nice * index' which multiplies fold’s internal variable, nice, by the index value. Another example is 'do nice + getValue(close, n, length - 1)) / length'(a simple moving average) which gets a close value; divides it by a length variable; and adds it to the internal variable, nice. Or it may be a more complicated fold such as: fold i = 0 to 100 with price = Double.NaN while !IsNaN(price) do if getValue(high, -i) > 40 then getValue(high, -i) else Double.NaN; This finds the next high price value greater than 40 among the following 100 bars and terminates looping if price is no longer a number.
  • GetValue function
  • The syntax for GetValue is: GetValue(IDataHolder data, IDataHolder dynamic offset, int max offset);
  • A discussion of fold would not be complete without discussing the GetValue function. This function goes and gets data used in the do <expression>.
  • The third parameter, int max offset,is a fail stop value to prevent an endless loop in the scripting engine. Ideally it should be set to the maximum number that the dynamic index is expected to be. Set it too small and the script engine stops the loop before all index values are processed. Set it too high and you may unnecessarily be wasting server capacity. It would be OK to set it a little higher than you know is needed. If the script engine hits the stop value you’ll get a run-time error message.
Note that int max offset is a fixed integer value, while IDataHolder dynamic offset is an expression that defines the offset value. The expression used for the IDataHolder dynamic offset often has a length parameter in it and that length parameter is also the value used for int max offset. Two very popular expressiona for IDataHolder dynamic offset are LookUpHighest(price,'look up price',length) and LookUpLowest(price,'look up price',length). The length inthese two studies is often the value that int max offset is set to.
  • do <expression>
Examples of
  • The heart of the fold function is the 'do expression' which is crucial for success but is not naturally intuitive. A number
  • of examples may be helpful.
  • Example 1 :
  • Code:
    input n = 10;
    plot factorial = fold index = 1 to n + 1 with Var = 1 do Var * index;
  • Calculates the factorial of a number. 10 loops are executed and each loop is multiplied by the value of the previous loop.
  • The initial value for the start of the first loop is 1.
Example 2 :
Code:
input price = close;
input length = 9;
plot SMA = (fold n = 0 to length with Var_ma do Var_ma + getValue(price, n, length - 1)) / length;
Calculates the simple moving average using fold. 9 loops are run i.e. 0 thru 8 with the internal variable named Var_ma.
Note the importance of the index starting with 0. The first value is getValue(price,n) or price[0] . If the index was to be 1 thru 10, the current value of price would not be included in the average because the first value would be price[1].
Example 3:
Code:
input length = 10;
def Test = fold index = 0 to length + 1 with nice = 0 do nice + index;
AddLabel(1,"Test = " + test, color.green);
This simple fold sums the 'index' values. The AddLabel enables you to change any variable and predict what the label will show. If not determine where your thinking went astray.
Example 4:
Code:
input length = 10;
def bigCount = compoundValue( 1, fold idx = 1 to length with a = 0 do a + bigCount[1], 1 );
This is interesting because it illustrates the concept of the fold and def being applied to every bar. The def causes each bar to hold the value of bigCount and the fold’s 'do a + bigCount[1]' essentially causes each bar to be increased by a factor of 9 due to its looping. It is easy to see that the result will eventually reach infinity for a normal sized chart. It’s not likely that you will ever use a def value in a do statement of a fold like this. This is known as a runaway calculation.
Example 5:
Code:
input length = 10
def smlCount = compoundValue( 1, fold idx2 = 1 to length with b = 0 do if smlCount[1] >= 1000 and b >= 1000 then 1000 else b + smlCount[1], 1 );
This allows 'smlCount' to rise to 1000 and then it limits smlCount toa value of 1000.
Example6:
Code:
plot Test = fold i = 0 to 4 with x = 1 do x + i;
What is the value of test? If your answer was not 7, rethink it.
If we change it to:
[CODE]plot Test = fold i = 1 to 5 with x = 10 do x + i;[/CODE]
What is its value?
If your answer was not 20, rethink it.
Example7:
Code:
input period = 20; #hint period:Number of bars to look in
def Hi = fold i = 0 to period with n = high do Max(n, GetValue(high, i, period - 1));
def Lo = fold k = 0 to period with m = low do Min(m, GetValue(low, k, period - 1));
AddLabel(1, "High in last " + period + " bars = " + Round(Hi,2), Color.GREEN);
AddLabel(1, "Low in last " + period + " bars = " + Round(Lo,2), Color.GREEN);
Labels allow you to look at the chart and verify the values.
Example8:
Code:
input length = 21;
def SDr = StDev(r, length);
plot IVSwitch = ( fold i = 0 to length with count do count + if SDr[i] <= SDr then 1 else 0 ) / length;
This fold counts the number of times, in the last 20 bars, the SDr (std dev of the change ratio) has fallen below the SD.
Example9:
Code:
declare lower;
input volTarget        = 200000;
input length           = 20;
plot atLeastVolumeTgt = fold idx = 0 to length + 1 with s = yes while s == yes do if GetValue( volume, idx, length + 2 ) >volTarget then yes else no;
The above works as a study, omit the "declare lower;" if you want to use it directly in a scan or column.
This code that will check for "daily" average volume greater than 200,000 in the last 20 days, meaning that the stock should have traded at least 200,000 shares every single day for at least the last 20 days. If it complies, 1 is plotted if not 0 is plotted. In a study, it is more meaningful to put the 1 or 0 result in an clarifying label.
 

sam4cok

Member
The Fold syntax is:
def <result> = fold <index> = <start> to <end> [ with <variable> [ = <init> ] ] [ while <condition> ] do <expression>;
Each component of the fold function will be explained separately. The function is not easy to use but understanding the purpose of the components will help you to feel comfortable with it.
General Comment:
  • The fold function is used to define the value for a named variable i.e. def <result>. You cannot operate on other variables or do anything within the fold. Studies may be used within a fold.
  • Rather than define a variable, the fold may be plotted directly i.e. def <result> = becomes Plot <result> =.
  • Remember that the fold calculation is executed at every bar as ThinkScript processes from bar 1 to the last bar.
  • As discussed in GetValue below, studies may be used in the Fold function especially in the do <expression>.
  • The names assigned <index> and <variable> are persistent variables. Hence, if you have two folds in a study and you assign 'idx' to <index> in the first fold you cannot assign 'idx' to <index> in the second fold. This will create an error.
  • Fold will normally work in a scan and custom columns. Complexity may become an issue especially if the servers are loaded up.
  • fold
  • A fixed word that identifies the following as a 'Fold' function.
  • <index> = <start> to <end>
  • This defines how many times the fold calculation loops on each bar. You need to figure out how many times "fold" needs to repeat itself, OR at what value it is going to stop churning away. Let’s say you want a calculation to repeat 5 times. If the <start> is at 0 and the <end> is at 5, then the calculation will repeat 5 times. <start> is inclusive but <end> is exclusive. When the counter gets to 5, fold stops and there is no results related to loop 5. <index> can be any name you want but 'i' or 'index' is commonly used e.g. i = 0 to 50. The value of the index can be used in the do <expression>. When <index> is used in the do statement, the last value of <index> is used and not the current value. The current value would be <index> + 1.
  • [ with <variable> [ = <init> ] ]
  • First of all, anything within brackets is optional. So when/why would you include this.?The answer lies in that this is an internal variable that fold uses. So when is it needed? If the 'do' section of the fold performs a activity like 'add to', 'multiply by' or similar, it must have a previous number to 'add to' for example. This 'with <variable>' is the value that will be added to when you see code like 'do nice + idx3'. This means that 'nice' is the with <variable> that fold has been keeping tract of internally and '+ idx3' is the current loop’s calculated value that is to be added to nice. 'nice + idx3' then becomes the new value of the internal variable nice and nice is available for the next loop’s calculation. <variable> can be any name you want to assign. In this example, 'nice' was used.
  • [ = <init> ] is the initial value of the 'with <variable>' and is optional. If it is omitted, then the default value of 0 is used. <init> is a number. Since it is in brackets, it is optional if there is a with <variable>.
  • [ while <condition> ]
  • This defines a condition, upon violation of which, the loop (not the fold itself) is terminated when calculating the fold function and TOS procedes to the next bar. The fold will do some action but that action may be subject to certain conditions. This [ while <condition> ] defines conditions/ limitations that are imposed on the actions that follow. The conditions may qualify the do-actions results or they may define conditions that terminate any further loops at the current bar. Conditions here do not preclude the 'do' statements from having an 'if' statement that may also set conditions but those conditions are used in getting the desired result from the 'do' statement. A example would look like
  • 'while close > 40'.
  • do <expression>
  • Defines an action to be performed, for each loop, when calculating the fold function. The do <expression> may be of numerous types. For example, if it is a true/false type then the fold results will be a true/false. Or it may be a arithmetic type like 'do nice * index' which multiplies fold’s internal variable, nice, by the index value. Another example is 'do nice + getValue(close, n, length - 1)) / length'(a simple moving average) which gets a close value; divides it by a length variable; and adds it to the internal variable, nice. Or it may be a more complicated fold such as: fold i = 0 to 100 with price = Double.NaN while !IsNaN(price) do if getValue(high, -i) > 40 then getValue(high, -i) else Double.NaN; This finds the next high price value greater than 40 among the following 100 bars and terminates looping if price is no longer a number.
  • GetValue function
  • The syntax for GetValue is: GetValue(IDataHolder data, IDataHolder dynamic offset, int max offset);
  • A discussion of fold would not be complete without discussing the GetValue function. This function goes and gets data used in the do <expression>.
  • The third parameter, int max offset,is a fail stop value to prevent an endless loop in the scripting engine. Ideally it should be set to the maximum number that the dynamic index is expected to be. Set it too small and the script engine stops the loop before all index values are processed. Set it too high and you may unnecessarily be wasting server capacity. It would be OK to set it a little higher than you know is needed. If the script engine hits the stop value you’ll get a run-time error message.
Note that int max offset is a fixed integer value, while IDataHolder dynamic offset is an expression that defines the offset value. The expression used for the IDataHolder dynamic offset often has a length parameter in it and that length parameter is also the value used for int max offset. Two very popular expressiona for IDataHolder dynamic offset are LookUpHighest(price,'look up price',length) and LookUpLowest(price,'look up price',length). The length inthese two studies is often the value that int max offset is set to.
  • do <expression>
Examples of
  • The heart of the fold function is the 'do expression' which is crucial for success but is not naturally intuitive. A number
  • of examples may be helpful.
  • Example 1 :
  • Code:
    input n = 10;
    plot factorial = fold index = 1 to n + 1 with Var = 1 do Var * index;
  • Calculates the factorial of a number. 10 loops are executed and each loop is multiplied by the value of the previous loop.
  • The initial value for the start of the first loop is 1.
Example 2 :
Code:
input price = close;
input length = 9;
plot SMA = (fold n = 0 to length with Var_ma do Var_ma + getValue(price, n, length - 1)) / length;
Calculates the simple moving average using fold. 9 loops are run i.e. 0 thru 8 with the internal variable named Var_ma.
Note the importance of the index starting with 0. The first value is getValue(price,n) or price[0] . If the index was to be 1 thru 10, the current value of price would not be included in the average because the first value would be price[1].
Example 3:
Code:
input length = 10;
def Test = fold index = 0 to length + 1 with nice = 0 do nice + index;
AddLabel(1,"Test = " + test, color.green);
This simple fold sums the 'index' values. The AddLabel enables you to change any variable and predict what the label will show. If not determine where your thinking went astray.
Example 4:
Code:
input length = 10;
def bigCount = compoundValue( 1, fold idx = 1 to length with a = 0 do a + bigCount[1], 1 );
This is interesting because it illustrates the concept of the fold and def being applied to every bar. The def causes each bar to hold the value of bigCount and the fold’s 'do a + bigCount[1]' essentially causes each bar to be increased by a factor of 9 due to its looping. It is easy to see that the result will eventually reach infinity for a normal sized chart. It’s not likely that you will ever use a def value in a do statement of a fold like this. This is known as a runaway calculation.
Example 5:
Code:
input length = 10
def smlCount = compoundValue( 1, fold idx2 = 1 to length with b = 0 do if smlCount[1] >= 1000 and b >= 1000 then 1000 else b + smlCount[1], 1 );
This allows 'smlCount' to rise to 1000 and then it limits smlCount toa value of 1000.
Example6:
Code:
plot Test = fold i = 0 to 4 with x = 1 do x + i;
What is the value of test? If your answer was not 7, rethink it.
If we change it to:
[CODE]plot Test = fold i = 1 to 5 with x = 10 do x + i;[/CODE]
What is its value?
If your answer was not 20, rethink it.
Example7:
Code:
input period = 20; #hint period:Number of bars to look in
def Hi = fold i = 0 to period with n = high do Max(n, GetValue(high, i, period - 1));
def Lo = fold k = 0 to period with m = low do Min(m, GetValue(low, k, period - 1));
AddLabel(1, "High in last " + period + " bars = " + Round(Hi,2), Color.GREEN);
AddLabel(1, "Low in last " + period + " bars = " + Round(Lo,2), Color.GREEN);
Labels allow you to look at the chart and verify the values.
Example8:
Code:
input length = 21;
def SDr = StDev(r, length);
plot IVSwitch = ( fold i = 0 to length with count do count + if SDr[i] <= SDr then 1 else 0 ) / length;
This fold counts the number of times, in the last 20 bars, the SDr (std dev of the change ratio) has fallen below the SD.
Example9:
Code:
declare lower;
input volTarget        = 200000;
input length           = 20;
plot atLeastVolumeTgt = fold idx = 0 to length + 1 with s = yes while s == yes do if GetValue( volume, idx, length + 2 ) >volTarget then yes else no;
The above works as a study, omit the "declare lower;" if you want to use it directly in a scan or column.
This code that will check for "daily" average volume greater than 200,000 in the last 20 days, meaning that the stock should have traded at least 200,000 shares every single day for at least the last 20 days. If it complies, 1 is plotted if not 0 is plotted. In a study, it is more meaningful to put the 1 or 0 result in an clarifying label.
can TOS support multiple variables inside one loop?
 
Top