Conditional Triggers Using the Fold Function

StoneMan

New member
I'm working on some logic in a custom strategy and need some help converting it to code. The basic flow is as follows: ConditionA = if ConditionB is true trigger a check to see if ConditionC is greater than ConditionC from 1 bar ago and if this is true iterate the loop, if it is not true or when it stops being true stop the loop and ConditionA is false. I want ConditionA to stay true even if ConditionB stops being so. ConditionB is just meant to trigger the check for ConditionC as I only care about that particular behavior if it began when ConditionB was true. I believe this logic of using a trigger condition to search for behaviors in indicators under specific circumstances could be a powerful tool. Any help or a general template would be greatly appreciated. Thank you.
 
Solution
I figured it out, the key was to compare the count of the last entry signal to the count of the last exit signal. Here is the code. @mashume @rad14733 thank you guys so much for all your help. This really was a collaborative effort and having some people to bounce ideas off of was key in the problem solving process. Consider this question answered :)

Code:
def BarsFromLongExit = if longexit[1] == 1 then 1 else BarsFromLongExit[1] + 1;

plot holdlong = if ((BarsFromLongExit > BarsFromLongEntry and VxIncreasing is true) or longentry is true, low, double.NaN);

StoneMan

New member
Of course. I have isolated the VxNet section from the Mobius Better Volume indicator and am using it to examine volume flows and trends. In this case, ConditionB is a trigger that detects momentum shifts at extreme levels and ConditionC is just monitoring the indicator for follow through by seeing the current reading is greater than the last. It would be used for mean reversion. For example, go long while ConditionA is true and exit the position when it is no longer true.

To elaborate, a momentum shift in the indicator at extremely low levels (ConditionB) triggers a check for follow through in that shift (ConditionC) so you can get a signal to go and stay long (ConditionA) while (ConditionC) holds true.

Let me know if you have other questions, and thank you for the help.
 

mashume

Well-known member
VIP
In my experience, the cleanest way to implement a set of conditions is NOT in a single statement but rather in a series of definitions:
Code:
def condA = if A is true then 1 else 0;
def condB = if B is true then 1 else 0;
def condC = if C is true then 1 else 0;
def entry = if condA == 1 then 
    if condC == 1 then 1
    else 0
else 0;
plot entry_signal = if entry then low else double.nan;
def exit = if condC[1] == 1 and condC == 0 then 1 else 0;
plot exit_signal = if exit == 1 then high else double.nan;
This is, of course, not real code. You can't run it. But it may give you ideas. I define conditions as 1 or 0 so that I can use the 0 value if I need to rather than a double.nan. Checking for nan gets ugly sometimes.

this may, or may not, be helpful
happy trading,
mashume
 

StoneMan

New member
Thank you mashume, this is helpful, I think we are on our way to figuring this out. Playing off what you posted I have a bit more logic to get the signal I am looking for.

If conditionC holds true (VxNet > VxNet[1]) iterate back, bar by bar, until conditionC breaks or until it reaches an "entry" signal. If the iteration reaches an entry signal trigger a "hold" condition, if it reaches a break in ConditionC return "hold" as false.

Because conditionC and conditionB must be true to return the "entry" signal iterateing back until that point will not return a misfired "hold" signal. The exit signal is given when "hold" is no longer true.

Therefore you can enter the position when "hold" triggers and exit when it is no longer true.

This is because VxNet > VxNet[1] in all sorts of conditions. But we only care about the falsification of that condition if it was triggered by our entry signal.

Let me know if you have any other questions, the assistance is greatly appreciated.
 

StoneMan

New member
@mashume @rad14733 I've made some progress on how I think this code could work out. Help with putting it all together would be greatly appreciated.

First you would need to identify the number of bars since the last entry signal

Def NumBarFromEntry = the number of bars since the last entry signal

You would use that variable as the limit counter in your fold condition which works its way backwards from the current bar. This condition would define "hold"

Def hold = fold i = 0 to NumBarFromEntry while VxNet > VxNet[1] (ConditionC)

As far as output goes hold returns true if it can successfully iterate back to an entry signal, false if it cannot.
 

mashume

Well-known member
VIP
You can count bars since the last time a condition was met with something like this:

Code:
def count = if condC[1] == 1 then 1 else count[1] + 1;

then you can do something with your count variable
 

StoneMan

New member
@mashume @rad14733 Pasted below is the code I have so far. It should be noted that VxRatioPlotSmoothed_agg3 uses a selectable aggregation period. The problems I am having are with the holdlong variable. I'd eventually like the truthfulness of this variable end up being what I use for entry and exits when I get it working. The compiler is telling me I need to use the GetValue() function and I honestly have no idea how to implement it here. I'll copy/paste the exact error codes I'm getting.

Invalid statement: Arithmetic or logic operations cannot be used inside this indexer. Please use GetValue() instead at 171:137
No default value for parameter 'dynamic offset' on getvalue at 171:64
Expected double
Wrong type cast: different types after then and else: class java.lang.String vs double
No such variable: i at 171:73
Only constants expected here: i index in non-constant limits fold. Please use GetValue() instead at 171:107
Only constants expected here: i CL variable indexer of VxRatioPlotSmoothed_agg3 at 171:137

The goal is to have the holdlong variable achieve the outcome I described in my previous replies. Here is a link to a picture of my charts running the some of the code. (I accidently uploaded the same image twice). The goal is to go long on the blue arrow (entry signal) and exit on the red arrow. The problem is there are more exit signals than there are entry signals. That extraneous noise will mess up a part of a larger strategy. So the goal of this loop (holdlong) is to tie entry to exit so that for every entry there is only one exit signal. It does so by iterating BarsFromLongEntry back and checking to see that condC is true along the way. If holdlong can iterate back to a longentry signal holdlong will be true. If it cannot, holdlong will be false. The signal to trade would be go enter position when hold long becomes true, exit when it becomes false.

As always, any help is greatly appreciated and I cant thank you guys enough.

Code:
def condC = VxRatioPlotSmoothed_agg3 > VxRatioPlotSmoothed_agg3[1];
def longentry = MomentumShiftUp and condC is true;


def BarsFromLongEntry = if longentry[1] == 1 then 1 else BarsFromLongEntry[1] +1;



def holdlong = fold i = 0 to BarsFromLongEntry with lookback = GetValue(i) while VxRatioPlotSmoothed_agg3[i] > VxRatioPlotSmoothed_agg3[i+1] do holdlong == 1;


def longexit = if condC[1] == 1 and condC == 0 then 1 else 0;

plot entry_signal = if longentry then low else double.nan;
plot exit_signal = if longexit == 1 then high else double.nan;
 

rad14733

Well-known member
VIP
@StoneMan Is that the entire script or just a snippet of code...??? If it is only a snippet it will be impossible to debug... A couple of the errors are self-evident, like the following: Invalid statement: Arithmetic or logic operations cannot be used inside this indexer. Please use GetValue() instead at 9:137 is in reference to the code: VxRatioPlotSmoothed_agg3[i+1]... Obviously you haven't posted the entire script based on the line number of that particular error...
 

StoneMan

New member
@rad14733 @mashume Yes, these are just code snippets within a much larger project. The reason for me posting is that the coding techniques necessary to apply the logic I have described above is simply beyond where I currently am as a programmer. I have absolutely no idea how to apply the GetValue() function to the variables within a Fold function as I struggle enough Fold function as it is (loops where about as far as I got in my formal programming education). So, really, what I am looking for is a general template that lays out the logic I have described with the proper syntax so that I may apply it to this and future projects. I am far more interested in that than a debugging of my code within the project. I am well aware the logic/syntax within the holdlong variable are horribly wrong, and may not even be close to doing what I want it to. That's why I'm reaching out again. Hopefully, a conversion of the logic I have described in English and am fairly confident in can be converted to code which I am not at all confident in. The code I gave was just my attempt at that conversion and shows some of the particular variables I am working with. Perhaps someone in the community could do a better job than me. I'll reiterate that one of the quirks in this project is that VxRatioPlotSmoothed_agg3 used in assessing condC uses a selectable aggregatetion period. VxRatioPlotSmoothed_agg3 is also exclusively used to assess the MomentumShiftUp variable. Other than those outside factors, all variables needed to create a working holdlong variable should be present in the code snippet. Once again, thank you two for all your help, the feedback is greatly appreciated.
 

mashume

Well-known member
VIP
OK.

GetValue() requires at least something it can get.
Code:
GetValue(CLOSE, 1)
will return the close from the previous bar.

I don't think, imho, you really want to use the fold() function here since you aren't really using it recursively to do anything to your number.

I think you might be better served with a different method for the definition of you holdlong statement:
First, we're going to define an array of filled with 1s and 0s for true//false based on VxRatio...
Code:
def VxIncreasing = if VxRatioPlotSmoothed_agg3 > VxRatioPlotSmoothed_agg3[1] then 1 else 0;
Next, since we know that if all of the values in the last n bars are 1 the last n bars sum to n:
Code:
def hold_has_been_true_for_n_bars = if sum(VxIncreasing, BarsFromLongEntry) == BarsFromLongEntry then 1 else 0;
Finally, you can use that as your test for whether the holdlong variable is true or false.
Code:
def holdlong = if hold_has_been_true_for_n_bars == 1 then 1 else 0;

Simpler, a few more variables, but no convoluted fold statements

-mashume
 

StoneMan

New member
@mashume @rad14733 Thank you mashume. That's a great idea. However the logic does not quite seem to be working as intended. In this chart the holdlong variable's truthfulness is denoted by the blue line. It appears to be doing the opposite of what was intended. More specifically, it appears to be tracking when VxRatioPlot_agg3 (displayed in the lower chart) is LESS than the previous reading. The goal of the holdlong variable is to trace from the first red up arrow it hits (entry signal) to the first white down arrow (exit signal). And not do so again until another red up arrow occurs. In the code pasted below I have created a study that replicates the exact conditions shown in the posted chart that can be used to try to achieve that objective and debug. The lower study is just VxRatioPlot_agg3 plotted on lower. The chunk of code we have been working with falls under the #Mean Reversion label. Thank you guys so much for all your help, couldn't get this done without you.

Code:
############################ Signal

input VxNetagg3Length = 7;
input Volume_Bias_Based_on3 = { default "Close_Price" , "Trend" }; ## this makes a difference only in few bars.. it's not used in any calc
input VxNetSmooth_agg3 = 6;
input VolPressureCalc3 = { default "WAverage", "Sum" };
input VxNetagg3 = AggregationPeriod.FIVE_MIN;

def VxNetagg3_O = open(period = VxNetagg3);
def VxNetagg3_C = close(period = VxNetagg3);
def VxNetagg3_H = high(period = VxNetagg3);
def VxNetagg3_L = low(period = VxNetagg3);
def VxNetagg3_V = volume(period = VxNetagg3);

def VolState_agg3;
switch (Volume_Bias_Based_on3) {
case Trend:
    VolState_agg3 = if VxNetagg3_C > VxNetagg3_C[1] then 1 else if VxNetagg3_C == VxNetagg3_C [1] then 0 else -1;
default :
    VolState_agg3 = if VxNetagg3_C > VxNetagg3_O then 1 else if VxNetagg3_C == VxNetagg3_O then 0 else  -1;
}

def Vx_agg3 =  if VolPressureCalc3 == VolPressureCalc3.Sum then Sum(VxNetagg3_V, VxNetagg3Length) else WMA(VxNetagg3_V, VxNetagg3Length) ;

def Range_agg3 = VxNetagg3_H - VxNetagg3_L;
def VolUp_agg3 = if VxNetagg3_C > VxNetagg3_O and Range_agg3 <> 0 then (Range_agg3 / (2 * Range_agg3 + VxNetagg3_O - VxNetagg3_C)) * VxNetagg3_V
else if VxNetagg3_C < VxNetagg3_O and Range_agg3 <> 0 then ((Range_agg3 + VxNetagg3_C - VxNetagg3_O) / (2 * Range_agg3 + VxNetagg3_C - VxNetagg3_O)) * VxNetagg3_V
else 0.5 * VxNetagg3_V;

def VolDn_agg3 = VxNetagg3_V - VolUp_agg3;

def VxPlus_agg3 = if VolPressureCalc3 == VolPressureCalc3.Sum then Sum (VolUp_agg3, VxNetagg3Length) else WMA (VolUp_agg3, VxNetagg3Length);
def VxMinus_agg3 = if VolPressureCalc3 == VolPressureCalc3.Sum then Sum (VolDn_agg3, VxNetagg3Length) else WMA (VolDn_agg3, VxNetagg3Length);

def VxNetRaw_agg3 = VxPlus_agg3 - VxMinus_agg3 ;

## Smooth with a very short zero-lag to eliminate outliers
##### New Stuff #######
def VxRatioPlot_agg3 = 100 * VxNetRaw_agg3 / Vx_agg3;
def VxRatioPlotSmoothed_agg3 =  2 * WMA(VxRatioPlot_agg3, VxNetSmooth_agg3) - WMA(WMA(VxRatioPlot_agg3, VxNetSmooth_agg3), VxNetSmooth_agg3);

# Smart Momentum Tiers
def MomentumShiftDown = if((VxRatioPlotSmoothed_agg3 from 1 or 2 or 3 bars ago > 20) and (VxRatioPlotSmoothed_agg3 < ((VxRatioPlotSmoothed_agg3 from 1 or 2 or 3 bars ago) - 10)), 1, 0);

def MomentumShiftUp = if((VxRatioPlotSmoothed_agg3 from 1 or 2 or 3 bars ago < -20) and (VxRatioPlotSmoothed_agg3 > ((VxRatioPlotSmoothed_agg3  from 1 or 2 or 3 bars ago) + 10)), 1, 0);

#Mean Reversion

#The logic I am hoping to eventually use
def VxIncreasing = if VxRatioPlotSmoothed_agg3 > VxRatioPlotSmoothed_agg3[1] then 1 else 0;

def longentry = MomentumShiftUp and VxIncreasing is true;

def BarsFromLongEntry = if longentry[1] == 1 then 1 else BarsFromLongEntry[1] + 1;

def hold_has_been_true_for_n_bars_long = if (VxIncreasing + BarsFromLongEntry) == BarsFromLongEntry then 1 else 0;

def holdlong = if hold_has_been_true_for_n_bars_long == 1 then 1 else 0;

plot holdlongplot = if holdlong is true then low else Double.NaN;


#The signals that are avalible
def longexit = if VxIncreasing[1] == 1 and VxIncreasing == 0 then 1 else 0;

plot entry_signal = if longentry then high else double.nan;

plot exit_signal = if longexit == 1 then high else double.nan;
 

StoneMan

New member
can you post an image, please?
Yes, I have been attempting to upload using the imgur link but it has not been working. So I instead copy and pasted the URL and attached it to a hot link. The word "chart" in the last reply is that link. If it is not working let me know, I will also share it via a tweet here for redundancy I really don't know what's up with the imgur uploading system, it has been quite annoying.
 

rad14733

Well-known member
VIP
Yes, I have been attempting to upload using the imgur link but it has not been working. So I instead copy and pasted the URL and attached it to a hot link. The word "chart" in the last reply is that link. If it is not working let me know, I will also share it via a tweet here for redundancy I really don't know what's up with the imgur uploading system, it has been quite annoying.
What I have started doing is to upload a new image, Click on the image, and select the BBCode option... It can then be Pasted directly into a post and will display as an image without all the extra crap...
 

mashume

Well-known member
VIP
Hmmm.
Got this error trying to implement the sum function
Only constants expected here: BarsFromLongEntry CL constant function parameter 'length' at 56:63
So you're not going to be able to use the variable BarsFromLongEntry in a sum clause. Bummer. I hadn't tried that approach before.
I'll think about some other way of doing this...
Seems you may have bumped into a limit of thinkscript. Remember that it is really a functional language, not a procedural one. Folds don't really like dynamic lengths either.
If the range of hold length were reasonable, it might be ok to do a case kind of switch, but it's not really (range is too large for individual statements).

-mashume

At times like this, I usually just switch back to python. ;-)
 
Last edited:

StoneMan

New member
@mashume Understood, the one way thinkscript can successfully implement this logic, in a way, is to use the strategy code to buy to open on longentry and to sell to close on longexit. I wanted holdlong to mimic that hold period but in a variable that doesn't use the strategy logic. Perhaps this helps but if not and if you can't figure out a way to do it I would be interested in a python conversion. Thanks for everything.
 

StoneMan

New member
Thinking of a way to work around the need for a constant. Just spit balling here, but perhaps you could just set the look back period to a very high constant number, say 100, one that "guarantees" the condition either breaks or is met in that time frame. Then, because we know the value of that constant, we can do math with it after the sum function to achieve our desired result.
 

StoneMan

New member
@mashume @rad14733 A bit more development with that idea...

Change the number in the sum function to 100, call the variable "Guarantees_Condition_Is_Met" use ConditionMath=sum(VxIncreasing, Guarantees_Condtion_Is_Met), so now we know BarsFromLongEntry, ConditionMath, Guarantees_Condtion_Is_Met, and VxIncreasing to try to parse out a useful hold_has_been_true_for_n_bars variable.

Using Guarantees_Condtion_Is_Met we could also potentially bring the use of the fold function back to the table. I was thinking that because of the iterative nature of the fold function using a massive number for Guarantees_Condtion_Is_Met wouldn't be inefficient because it stops when the condition is broken or met anyway.
 

StoneMan

New member
Some more development with the idea, more theory then anything else...

Do all calculations in the context of the "Guarantees_Condtion_Is_Met" variable. Essentially shifting your perspective in the calculations. Sort of like how when you are in a car you feel at rest and the world is moving around you but from outside the car from a fixed point the car is moving. I remember this sort of thing being done in physics problems, moving your axis to a new zero point to make solving the problem easier. In order to do these calculations I think we may need make the "Guarantees_Condtion_Is_Met" variable our new zero point so to speak. Apologies for spamming btw. And thank you again for all the stuff you guys have done for me to try to solve this.
 

Similar threads

Top