Fold Operation Not Updating

armybender

Active member
Hi. I have an "indicator" that uses a fold function to check if a bar would have achieved at least 1x risk:reward and colors bars green if they meet that condition for a long, and red if they meet that condition for a short. The bars tend to get out of synch as time goes, so you have to re-load the study or refresh the chart style to get it to correctly paint the bars.

I'm wondering if there's a way to "force" the indicator to refresh and recalculate the bar color.

What I'm saying is that even after the future bars close, it is not re-painting past bars. So, if a bar is an up bar and is 4 ticks tall, it needs to move 6 ticks above the bar's high at any point in the future to trigger a "green" condition.

When the next bar or two complete, it is not re-painting the original up bar even though the condition has been met. Once you re-load the study it paints it correctly.

I'm wondering how to make it re-paint as bars close without having to re-load the study.

I know it won't be green until that condition is met, but my concern is that because I'm using a fold function that it is only calculating the "front" bar and not actually recalculating all prior bars.

Lastly, in the past I have only used this for after-the-fact analysis. I'm trying to see IF I can use it as a real-time indicator to see which side is winning in a different way. That's why the live re-paint is now an issue I'm wondering about.

Any ideas?

Here's the code.


Ruby:
###  ACHIEVED_PROFIT_TARGET --- 2023-03-11 BY ARMYBENDER

###  License: I don't care - do what you want with it.

###  Determines whether a trade would have achieved a profit target before being stopped out.
###  Risk is based on the bar height + 2 ticks (1 tick for a stop entry price above/below the bar, and
###  1 tick for a stop exit price below the bar.  The study uses future bars, so it cannot be used in
###  realtime to make trade decisions, however, it is helpful for quick analysis or measurement
###  after the fact.  This will work with all bar types.


### DECLARATIONS
declare upper;
#declare once_per_bar;


### GLOBAL COLOR DEFINITIONS
DefineGlobalColor("Green", CreateColor(0, 155, 0));
DefineGlobalColor("Red", CreateColor(225, 105, 105));
DefineGlobalColor("Gray", CreateColor(181, 181, 181));


### INPUTS
input requireSignalBar = no;
input colorBars = yes;
input trailColors = yes;
input showArrows = no;
input requireTickThrough = yes;
input rewardMode = {default "Risk Reward Ratio", "Number of Ticks"};
input riskTargetRatio = 1.0;
input rewardNumberOfTicks = 4;
input numberOfBarsForward = 30;
input offsetBarsPlot = 0;
input arrowTickOffset = 2;
input arrowSize = 1;
input showHammer2EBars = no;


### DEFINITIONS
def isLastBar = !IsNaN(close) and IsNaN(close[-1]);

def rtRatio = riskTargetRatio;
def barsFwd = numberOfBarsForward;

def longEntry = high + TickSize();
def shortEntry = low - TickSize();

def riskAmount = if requireTickThrough then high - low + (3 * TickSize()) else high - low + (2 * TickSize());

def longStop = low - TickSize();
def shortStop = high + TickSize();

def longTarget =
    if rewardMode == rewardMode."Risk Reward Ratio" then
        longEntry + (rtRatio * riskAmount)
    else if rewardMode == rewardMode."Number of Ticks" then
        longEntry + rewardNumberOfTicks * TickSize()
    else
        Double.NaN
;
def shortTarget =
    if rewardMode == rewardMode."Risk Reward Ratio" then
        shortEntry - (rtRatio * riskAmount)
    else if rewardMode == rewardMode."Number of Ticks" then
        shortEntry - rewardNumberOfTicks * TickSize()
    else
        Double.NaN
;

def isUp = close > open;
def isDown = close < open;
def isBullDoji = IsDoji() and high == close;
def isBearDoji = IsDoji() and low == close;

def isBullSignalBar = if (isUp or isBullDoji) and low < low[1] and high[-1] > high then yes else no;
def isBearSignalBar = if (isDown or isBearDoji) and high > high[1] and low[-1] < low then yes else no;

def longRiskTargetMet =
    if !requireSignalBar or isBullSignalBar then
        fold l = 1 to barsFwd
        with long = Double.NaN
        while IsNaN(long)
        do
            if GetValue(low, -l) <= longStop then
                no
            else if GetValue(high, -l) >= longTarget then
                yes
            else if GetValue(isLastBar, -l) then
                no
            else
                Double.NaN
    else
        Double.NaN;

def shortRiskTargetMet =
    if !requireSignalBar or isBearSignalBar then
        fold s = 1 to barsFwd
        with short = Double.NaN
        while IsNaN(short)
        do
            if GetValue(high, -s) >= shortStop then
                no
            else if GetValue(low, -s) <= shortTarget then
                yes
            else if GetValue(isLastBar, -s) then
                no
            else
                Double.NaN
    else
        Double.NaN;

def longPlotData =
    if longRiskTargetMet then
        low - (arrowTickOffset * TickSize())
    else
        Double.NaN;

def shortPlotData =
    if shortRiskTargetMet then
        high + (arrowTickOffset * TickSize())
    else
        Double.NaN;

def colorValue =
    if longRiskTargetMet then 1               # Green
    else if shortRiskTargetMet then -1        # Red
    else if trailColors then colorValue[1]    # Last Color
    else 0;                                   # Gray

### PLOTS
plot longPlot = if showArrows then if longPlotData[offsetBarsPlot] then low - (arrowTickOffset * TickSize()) else Double.NaN else Double.NaN;

plot shortPlot = if showArrows then if shortPlotData[offsetBarsPlot] then high + (arrowTickOffset * TickSize()) else Double.NaN else Double.NaN;


### BAR COLORING
AssignPriceColor(
    if colorBars then
        if colorValue == 1 then GlobalColor("Green")
        else if colorValue == -1 then GlobalColor("Red")
        else if isLastBar then Color.CURRENT
        else GlobalColor("Gray")
    else GlobalColor("Gray")
);


### FORMATTING
longPlot.DefineColor("Long", CreateColor(0, 205, 0)); ### Custom green color
longPlot.SetDefaultColor(longPlot.Color("Long"));
longPlot.HideBubble();
longPlot.HideTitle();
longPlot.SetLineWeight(arrowSize);
longPlot.SetPaintingStrategy(PaintingStrategy.ARROW_UP);

shortPlot.DefineColor("Short", CreateColor(205, 0, 0)); ### Custom red color
shortPlot.SetDefaultColor(shortPlot.Color("Short"));
shortPlot.HideBubble();
shortPlot.HideTitle();
shortPlot.SetLineWeight(arrowSize);
shortPlot.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
 
Last edited by a moderator:
Solution
Historical bars have already been evaluated. They remain in what ever state they were left in post-evaluation. They're not supposed to update in the first place, that's not how thinkscript is structured. You can, however, force them to revaluate by including a generic forward offset. The forward offset must contribute to an output method though, because orphaned code is ignored under evaluation. Hence, why plotting the forward offset is required, but is also multiplied by double.nan so that it doesn't actually appear on the chart. The plot itself is irrelevant, the point is to activate the background mechanic.

Try this short script for a better understanding, it will repaint the last ten bars every tick. Needs to be on a live market...
I think that, because of your use of isLastBar in your calculations of longRiskTargetMet and shortRiskTargetMet, there is no way of making the indicator, as written, work without having to be reloaded, as it cannot call a bar in the middle of the chart the last bar when it is not.

-mashume

of course, this may be completely off the mark as well.
 

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

I think that, because of your use of isLastBar in your calculations of longRiskTargetMet and shortRiskTargetMet, there is no way of making the indicator, as written, work without having to be reloaded, as it cannot call a bar in the middle of the chart the last bar when it is not.

-mashume

of course, this may be completely off the mark as well.


First... I really appreciate the thought on this. It's a unique thought I hadn't considered. However, I don't think that's what's causing it to not re-paint prior bars. I backed out the isLastBar from the entire script (commented out lines 88, 89, 105, 106, and 141) and tested it. I'm still facing the same problems.

My thoughts on your suggestion: Because the fold involves if/else, it will only reach the isLastBar for the bars within 30 bars of the end, otherwise it just reaches 30 bars and defaults to no. So, if it doesn't meet that condition (which it won't except for the last 29 bars) then it just gets skipped. And again, commenting out every reference to that doesn't fix the issue, so at this point I'm convinced it isn't it. So, I still need some help.

I'm wondering if TOS fundamentally doesn't re-paint prior bars live and instead only colors the last bar. The reason I think this is that it would have to re-evaluate hundreds or potentially thousands of bars each time, and given the fold function that would be 30 x 1000 bars = 30,000 calculations per tick???

I'm almost wondering if it's like the HighestAll() function where, once you have that function in a script it defaults to something like a once_per_bar mode and no longer recalculates previous bars?

Any thoughts @mashume, @BenTen, @halcyonguy, or @MerryDay ???
 
First... I really appreciate the thought on this. It's a unique thought I hadn't considered. However, I don't think that's what's causing it to not re-paint prior bars. I backed out the isLastBar from the entire script (commented out lines 88, 89, 105, 106, and 141) and tested it. I'm still facing the same problems.

My thoughts on your suggestion: Because the fold involves if/else, it will only reach the isLastBar for the bars within 30 bars of the end, otherwise it just reaches 30 bars and defaults to no. So, if it doesn't meet that condition (which it won't except for the last 29 bars) then it just gets skipped. And again, commenting out every reference to that doesn't fix the issue, so at this point I'm convinced it isn't it. So, I still need some help.

I'm wondering if TOS fundamentally doesn't re-paint prior bars live and instead only colors the last bar. The reason I think this is that it would have to re-evaluate hundreds or potentially thousands of bars each time, and given the fold function that would be 30 x 1000 bars = 30,000 calculations per tick???

I'm almost wondering if it's like the HighestAll() function where, once you have that function in a script it defaults to something like a once_per_bar mode and no longer recalculates previous bars?

Any thoughts @mashume, @BenTen, @halcyonguy, or @MerryDay ???

i was thinking the same as mashume, that the lastbar var could be causing an issue.
but after looking at some values, i don't think so.
but , with no live data till monday , i can't see what you are seeing.

here is a version that has bubbles with some values to help debugging.
also has points of the same color as the bar coloring.
can turn off bar coloring.


Code:
# future_issue_fold_lastbar_00

#https://usethinkscript.com/threads/fold-operation-not-updating.15337/
#Fold Operation Not Updating

### DECLARATIONS
declare upper;
#declare once_per_bar;

def na = double.nan;
def bn = barnumber();

### GLOBAL COLOR DEFINITIONS
DefineGlobalColor("Green", CreateColor(0, 155, 0));
DefineGlobalColor("Red", CreateColor(225, 105, 105));
DefineGlobalColor("Gray", CreateColor(181, 181, 181));


### INPUTS
input requireSignalBar = no;
input colorBars = yes;
input trailColors = yes;
input showArrows = no;
input requireTickThrough = yes;
input rewardMode = {default "Risk Reward Ratio", "Number of Ticks"};
input riskTargetRatio = 1.0;
input rewardNumberOfTicks = 4;
input numberOfBarsForward = 30;
input offsetBarsPlot = 0;
input arrowTickOffset = 2;
input arrowSize = 1;
input showHammer2EBars = no;


### DEFINITIONS
def isLastBar = !IsNaN(close) and IsNaN(close[-1]);

def rtRatio = riskTargetRatio;
def barsFwd = numberOfBarsForward;

def longEntry = high + TickSize();
def shortEntry = low - TickSize();

def riskAmount = if requireTickThrough then high - low + (3 * TickSize()) else high - low + (2 * TickSize());

def longStop = low - TickSize();
def shortStop = high + TickSize();

def longTarget =
    if rewardMode == rewardMode."Risk Reward Ratio" then longEntry + (rtRatio * riskAmount)
    else if rewardMode == rewardMode."Number of Ticks" then longEntry + rewardNumberOfTicks * TickSize()
    else Double.NaN;

def shortTarget =
    if rewardMode == rewardMode."Risk Reward Ratio" then shortEntry - (rtRatio * riskAmount)
    else if rewardMode == rewardMode."Number of Ticks" then shortEntry - rewardNumberOfTicks * TickSize()
    else Double.NaN;

def isUp = close > open;
def isDown = close < open;
def isBullDoji = IsDoji() and high == close;
def isBearDoji = IsDoji() and low == close;

def isBullSignalBar = if (isUp or isBullDoji) and low < low[1] and high[-1] > high then 1 else 0;
def isBearSignalBar = if (isDown or isBearDoji) and high > high[1] and low[-1] < low then 1 else 0;

def longRiskTargetMet =
    if !requireSignalBar or isBullSignalBar then
        fold l = 1 to barsFwd
        with long = Double.NaN
        while IsNaN(long)
        do if GetValue(low, -l) <= longStop then 0
            else if GetValue(high, -l) >= longTarget then 1
            else if GetValue(isLastBar, -l) then 0
            else Double.NaN
    else Double.NaN;

def shortRiskTargetMet =
    if !requireSignalBar or isBearSignalBar then
        fold s = 1 to barsFwd
        with short = Double.NaN
        while IsNaN(short)
        do if GetValue(high, -s) >= shortStop then 0
            else if GetValue(low, -s) <= shortTarget then 1
            else if GetValue(isLastBar, -s) then 0
            else Double.NaN
    else Double.NaN;



#----------------------
#  test stuff
def testlonglast = fold l2 = 1 to barsFwd
        with t2
        while t2 == 0
        do if GetValue(isLastBar, -l2) then l2 else 0;



input bubbles1 = yes;
addchartbubble(bubbles1, low*0.996,
 bn + "\n" +
 isLastBar + "\n" +
 isBullSignalBar + "  bull\n" +
 isBearSignalBar + "  bear\n" +
 longRiskTargetMet + "  L\n" +
 shortRiskTargetMet + "  S\n"   +
 testlonglast
, (if isnan(longRiskTargetMet) then color.gray else if longRiskTargetMet then color.green else if shortRiskTargetMet then color.red else color.gray), no);

#---------------------------


def longPlotData =
    if longRiskTargetMet then low - (arrowTickOffset * TickSize())
    else Double.NaN;

def shortPlotData =
    if shortRiskTargetMet then high + (arrowTickOffset * TickSize())
    else Double.NaN;


# long term trend
def colorValue =
    if longRiskTargetMet then 1               # Green
    else if shortRiskTargetMet then -1        # Red
    else if trailColors then colorValue[1]    # Last Color
    else 0;                                   # Gray


### PLOTS
plot longPlot = 
  if showArrows then
     if longPlotData[offsetBarsPlot] then low - (arrowTickOffset * TickSize())
     else Double.NaN
  else Double.NaN;

plot shortPlot =
  if showArrows then
     if shortPlotData[offsetBarsPlot] then high + (arrowTickOffset * TickSize())
     else Double.NaN
  else Double.NaN;


### FORMATTING
longPlot.DefineColor("Long", CreateColor(0, 205, 0)); ### Custom green color
longPlot.SetDefaultColor(longPlot.Color("Long"));
longPlot.HideBubble();
longPlot.HideTitle();
longPlot.SetLineWeight(arrowSize);
longPlot.SetPaintingStrategy(PaintingStrategy.ARROW_UP);

shortPlot.DefineColor("Short", CreateColor(205, 0, 0)); ### Custom red color
shortPlot.SetDefaultColor(shortPlot.Color("Short"));
shortPlot.HideBubble();
shortPlot.HideTitle();
shortPlot.SetLineWeight(arrowSize);
shortPlot.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);



### BAR COLORING
input change_bar_color = yes;
AssignPriceColor(
 if !change_bar_color then color.current else 
    if colorBars then
        if colorValue == 1 then GlobalColor("Green")
        else if colorValue == -1 then GlobalColor("Red")
        else if isLastBar then Color.CURRENT
        else GlobalColor("Gray")
    else GlobalColor("Gray")
);


plot zup = if colorValue == 1 then high*1.002 else na;
zup.SetPaintingStrategy(PaintingStrategy.POINTS);
zup.SetDefaultColor(Color.green);
zup.setlineweight(3);
zup.hidebubble();

plot zdwn = if colorValue == -1 then low*0.998 else na;
zdwn.SetPaintingStrategy(PaintingStrategy.POINTS);
zdwn.SetDefaultColor(Color.red);
zdwn.setlineweight(3);
zdwn.hidebubble();


#x.AssignValueColor( 

#  if colorValue == 1 then GlobalColor("Green")
#  else if colorValue == -1 then GlobalColor("Red")
#  else if isLastBar then Color.CURRENT
#  else GlobalColor("Gray")
#  else GlobalColor("Gray")
#
 
I haven't thoroughly read this thread, but on a quick glance; if you just want to force repainting, plotting a forward offset should repaint the offset's length in bars from the right edge of the chart. There are some tricks to prevent the plot from appearing while still activating the repaint.

Try adding this to your script:

Plot X = Close[-numberOfBarsForward] * Double.NaN;
 
I haven't thoroughly read this thread, but on a quick glance; if you just want to force repainting, plotting a forward offset should repaint the offset's length in bars from the right edge of the chart. There are some tricks to prevent the plot from appearing while still activating the repaint.

Try adding this to your script:

Plot X = Close[-numberOfBarsForward] * Double.NaN;

You should read the original question. There are no plots in question - we are trying to figure out why bar coloring does not update for historical (not the last) bars.
 
Historical bars have already been evaluated. They remain in what ever state they were left in post-evaluation. They're not supposed to update in the first place, that's not how thinkscript is structured. You can, however, force them to revaluate by including a generic forward offset. The forward offset must contribute to an output method though, because orphaned code is ignored under evaluation. Hence, why plotting the forward offset is required, but is also multiplied by double.nan so that it doesn't actually appear on the chart. The plot itself is irrelevant, the point is to activate the background mechanic.

Try this short script for a better understanding, it will repaint the last ten bars every tick. Needs to be on a live market or nothing will happen.

Code:
def Rand = Random() * 20;
assignpriceColor(getcolor(Rand));
plot Repaint = Close[-10] * Double.NaN;
 
Solution
Historical bars have already been evaluated. They remain in what ever state they were left in post-evaluation. They're not supposed to update in the first place, that's not how thinkscript is structured. You can, however, force them to revaluate by including a generic forward offset. The forward offset must contribute to an output method though, because orphaned code is ignored under evaluation. Hence, why plotting the forward offset is required, but is also multiplied by double.nan so that it doesn't actually appear on the chart. The plot itself is irrelevant, the point is to activate the background mechanic.

Try this short script for a better understanding, it will repaint the last ten bars every tick. Needs to be on a live market or nothing will happen.

Code:
def Rand = Random() * 20;
assignpriceColor(getcolor(Rand));
plot Repaint = Close[-10] * Double.NaN;


FYI @Joshua, I was able to test it this morning. I added this in the plot section and it repaints perfectly in real time. This is exactly what was needed.

plot forceBarRepaint = close[-barsFwd] * Double.NaN;
 

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
323 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top