Home > Technical Topics > Programming > ThinkScript Recursive Data Help

ThinkScript Recursive Data Help

  1. So I am building a study that will act as a scanner for horizontal channels. I have everything programmed up to it detecting whenever there is a high and low within a channel. It plots 1 when the price hits resistance and a 0 when it isn't. There is a separate plot that plots 1 when the price hits support and 0 when it isn't. I am trying to figure out how to go through each plot and add 1 to a separate variable each time the corresponding plot shows 1. So if the support plot hits one 6 times then the support accumulation variable would equal 6. And if the resistance plot hits one 4 times then the support accumulation variable would equal 4.

    This is my code so far:
    def offset = 0;
    input length = 30;
    input channelAccuracy = 3;

    #Converting the accuracy input into usable data
    def accuracy = (channelAccuracy / 2) * 0.01;

    #Set Lower Line
    def lowersma = SimpleMovingAvg(Lowest(low[1], length), 1, length);
    rec lowerline = if IsNaN(lowersma) then lowerline[1] else lowersma[offset];
    def lowerpriceline=if isnan(lowersma) then lowerline else double.nan;

    #Lower Line Accuracy
    def bottomloweraccuracy = lowerpriceline * (1 - accuracy);
    def toploweraccuracy = lowerpriceline * (1 + accuracy);


    #Set Upper Line
    def uppersma = SimpleMovingAvg(Highest(high[1], length), 1, length);
    rec upperline = if IsNaN(uppersma) then upperline[1] else uppersma[offset];
    def upperpriceline=if isnan(uppersma) then upperline else double.nan;

    #Upper Line Accuracy
    def bottomupperaccuracy = upperpriceline * (1 - accuracy);
    def topupperaccuracy = upperpriceline * (1 + accuracy);


    #Is Stock Within Upper Accuracy Enough
    plot NextHigh = fold i = 0 to length with price = Double.NaN while IsNaN(price) do if getValue(high, -i) >= bottomupperaccuracy && getValue(high, -i) <= topupperaccuracy then 1 else 0;
    NextHigh.setdefaultcolor(color.green);

    #Is Stock Within Lower Accuracy Enough
    plot NextLow = fold m = 0 to length with price2 = Double.NaN while IsNaN(price2) do if getValue(low, -m) >= bottomloweraccuracy && getValue(low, -m) <= toploweraccuracy then 1 else 0;
    NextLow.setdefaultcolor(color.red);
     
  2. Dunno the language, but just increment counter whenever High exceeds upper bound and increment another counter whenever Low exceeds lower bound. This is usually accomplished in programming languages by assigning the data variable's value to itself and then by modifying the final value. Ie.:

    X = X + 1

    As algebra, this is nonsense. However, as an imperative (state changing) instruction, this is how most imperative programming languages do state changes.

    Something like:
    def uppercount = 0;
    def lowercount = 0;
    ...
    ...then (uppercount+1) else uppercount;
    ...then (lowercount+1) else lowercount;​

    You're already using the language, just need to begin to "speak it" instead of "copy & paste" it.
     
  3. I've tried that but the language, ThinkScript, doesn't allow it.
    It believes every time it goes through the fold (a type of loop) the then statement is executed as 0 + 1. Not uppercount = uppercount + 1. This leads to it displaying 1 and 0 not a compounded number.
    I'm trying to figure out if there is a way to get around this problem that someone with more knowledge of thinkscript knows.
     
  4. Worst case scenario: Write indicators/plots for counting and then use the counts from previous bar, ie. (UpperCount[1] + 1). Can try a simple hardcoded indicator first.

    Then maybe you figure out later how to simplify it.

    Such scripting languages makes me glad NT uses C#.
     
  5. Wish I could use a different language. :/
    Tried that as well but it just moves the plotted numbers up 1 on the Y axis...
    Tried using multiple variables to get around the fact that a variable can't equal itself in Think script but it doesn't let you define variables in if statements:
    plot compoundHigh;
    if NextHigh == 1 {
    def compHigh = compHigh + 1;
    compoundHigh = compHigh;
    } else {
    compoundHigh = compHigh;
    }
    That didn't work either...
    It looks like for now i'm screwed over trying to compound data.
     
  6. I'm not trying to move the plotted number up 1 but for every time the count chart = 1 the compound count will add 1. So if it counts 5 highs (the count chart hits the number 1 5 times), the compound count will equal 5.
     
  7. It would be extremely easy if I had the ability to have a variable such as x = x + 1 but that is not possible.
     
  8. It helps to think in terms of arrays of values vs. discrete variables.

    Thinkscript def variables are evaluated for each bar on the chart -- just like price, volume, study values, etc. -- and you can access past and future values using [] offset terminology.

    So... Your counter variable is actually an array in which you either store the previous array element value, or increment the previous array element value by one and store it.

    Function template:
    def Counter = if([Test Case] is [True], Counter[1] + 1, Counter[1]);​

    Statement template:
    def Counter = if [Test Case] is [True] then Counter[1] + 1 else Counter[1];

    Two things to remember:
    1. You must test for all possible cases where your variable could evaluate to double.NaN. This is required because any arithmetic or boolean operation with double.NaN always results in double.NaN.
    2. Take care in using variables that may propagate the double.NaN value.
    Testing for the double.NaN value can get tedious, and it unnecessarily complicates the if-then-else logic, especially as the number of tests increase!

    Here are the counter variables using the plots you created:

    def SCount = if IsNaN(NextLow) then SCount[1] else if NextLow == 1 then SCount[1] + 1 else SCount[1];
    def RCount = if IsNaN(NextHigh) then RCount[1] else if NextHigh == 1 then RCount[1] + 1 else RCount[1];​


    Compare the code below with your original. Changes to the lowerpriceline and upperpriceline variables test for the last bar with data [IsNaN(close)]. Otherwise, everything below the double line is new.

    ***

    input length = 30;
    input channelAccuracy = 3;

    #Converting the accuracy input into usable data
    def accuracy = (channelAccuracy / 2) * 0.01;

    #Set Lower Line
    def lowersma = SimpleMovingAvg(Lowest(low[1], length), 1, length);
    def lowerline = if IsNaN(lowersma) then lowerline[1] else lowersma;
    def lowerpriceline = if IsNaN(close) then
    double.nan # no more data
    else if IsNaN(lowersma) then
    lowerline # record this entry
    else
    double.NaN; # default entry

    #Lower Line Accuracy
    def bottomloweraccuracy = lowerpriceline * (1 - accuracy);
    def toploweraccuracy = lowerpriceline * (1 + accuracy);

    #Set Upper Line
    def uppersma = SimpleMovingAvg(Highest(high[1], length), 1, length);
    def upperline = if IsNaN(uppersma) then upperline[1] else uppersma;
    def upperpriceline = if IsNaN(close) then
    double.NaN
    else if IsNaN(uppersma) then
    upperline
    else
    double.nan;

    #Upper Line Accuracy
    def bottomupperaccuracy = upperpriceline * (1 - accuracy);
    def topupperaccuracy = upperpriceline * (1 + accuracy);


    #Is Stock Within Upper Accuracy Enough
    plot NextHigh = fold i = 0 to length with price = Double.NaN while IsNaN(price) do if getValue(high, -i) >= bottomupperaccuracy && getValue(high, -i) <= topupperaccuracy then 1 else 0;
    NextHigh.setdefaultcolor(color.green);


    #Is Stock Within Lower Accuracy Enough
    plot NextLow = fold m = 0 to length with price2 = Double.NaN while IsNaN(price2) do if getValue(low, -m) >= bottomloweraccuracy && getValue(low, -m) <= toploweraccuracy then 1 else 0;
    NextLow.setdefaultcolor(color.red);

    ===========================

    #### Show the channel on a chart
    input showChannel = yes;
    def SLL;# Support lower limit
    def SUL;# Support upper limit
    def RLL;# Resistance lower limit
    def RUL;# Resistance lower limit
    if showChannel == yes then {
    SLL = bottomloweraccuracy;
    SUL = toploweraccuracy;
    RLL = bottomupperaccuracy;
    RUL = topupperaccuracy;
    }else{
    SLL = double.NaN;
    SUL = double.NaN;
    RLL = double.NaN;
    RUL = double.NaN;
    }
    DefineGlobalColor("Support Level", Color.Red);
    DefineGlobalColor("Resistance Level", Color.Blue);
    addcloud(SUL, SLL, GlobalColor("Support Level"), color.red);
    addcloud(RUL, RLL, GlobalColor("Resistance Level"), color.red);

    #### Support and Resistance Tag Counters
    # define counters
    def SCount = if IsNaN(NextLow) then SCount[1] else if NextLow == 1 then SCount[1] + 1 else SCount[1];
    def RCount = if IsNaN(NextHigh) then RCount[1] else if NextHigh == 1 then RCount[1] + 1 else RCount[1];

    # show tag counter info on charts
    input showInfo = yes;
    addLabel(showInfo, concat(concat(length,
    if getAggregationPeriod() == AggregationPeriod.DAY then " Day"
    else if getAggregationPeriod() == AggregationPeriod.WEEK then " Week"
    else if getAggregationPeriod() == AggregationPeriod.MONTH then " Month"
    else " Period"), " Channel Tags ->"), Color.Black);
    addlabel(showInfo, "S: " + SCount,GlobalColor("Support Level"));
    addLabel(showInfo, "R: " + RCount,GlobalColor("Resistance Level"));

    ####
     
  9. Does anybody know how a ThinkScript is executed? Is it executed all the way from the very first line of the script or is it executed from below all the "input" and "def" statements? Are all of the variables, once declared in the def statement with a value is set to stone and cannot be changed?

    I am trying to use variables recursively and it doesn't work either. I had the following codes just to test:

    def Test_Num = 0;

    Test_Num = Test_Num[1] + 100;

    It gives me the error: Already assigned: Test_Num at XXX

    And then when I got rid of the first "def" statement and just uses "def Test_Num = Test_Num[1] + 100;"

    then it gives me another error message that states "Too early to access Test_Num" which makes sense because Test_Num was never declared so Test_Num[1] would have no value but WHY doesn't it allow me to use it recursively to increment its previous value once it was declared with a value in the beginning like any other programming or scripting languages? It states clearly in their manual that recursive variable is allowed. http://tlc.thinkorswim.com/center/reference/thinkScript/Reserved-Words/def.html

    I used it exactly as how it was set up in the manual and it does not work. I have wasted so much time and effort on this stupid useless crap. Anybody who can help me is appreciated.
     
  10. Did you get your answer?
    I typically kludge the initialization, with something like

    def Test_Num=if (barnumber()<1) then 0 else Test_Num[1]+1;
    plot tn=Test_Num;

    Which gets the job done. -- However, you may find that this "initialization" is not always required!

    NOTE: barnumber() increments with each bar from left to right of chart.