% Percentage Change for ThinkorSwim

Branch

Member
Can someone create a label that will tell me how much the current product (symbol) is up or down? TOS has it in the Symbol Menu but I would like to have it on the chart as a label also.

Code:
# =============================================================================
#Hint: <b>PM Percent Change</b>\nCalculates the percent change in value from now and N periods ago where<li>value: any data type enumerated by FundamentalData such as CLOSE, IV, etc.</li><li>N: user specified look back length</li><li>period: user or chart selected aggregation period such as FIVE_MIN, DAY, WEEK, etc.</li>
#
# PM Percent Change
#
# @author: Patrick Menard, @dranem05, [email protected]
#
# This script calculates the percent change in value from now and N periods ago
# where:
#
#     value: data type enumerated by FundamentalData := CLOSE, IV, VOLUME, etc.
#         N: user specified look back length
#    period: user or chart selected aggregation period := MIN, DAY, WEEK, etc.
#
# This script can be used as a plot and as a label. If displayed on a lower
# subgraph, the % chg and its hi and lo alert triggered instances will be
# shown as it occurred over time. If 'show_label' is enabled, the most recent
# % chg will be displayed on the chart. To use this script purely as a label
# on the main price chart, set 'label_only' to YES in the script settings window
#
# This script utilized ThinkOrSwim's built-in PercentChg script and the
# customizability provided in the PM_Rank script as inspiration.
#
# LICENSE ---------------------------------------------------------------------
#
# This PM_PercentChg script is free software distributed under the terms of the
# MIT license reproduced here:
#
# The MIT License (MIT)
#
# Copyright (c) 2013 Patrick Menard, http://www.dranem05.com, http://dranem05.blogspot.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# =============================================================================


input label_only       = YES; #Hint label_only: use this script only as a label (supercedes 'show_label')
input show_label       = YES; #Hint show_label: Toggle display of a label to display the % chg value (superceded by 'label_only')
input data_type        = FundamentalType.CLOSE; #Hint data_type: Data on which to compute a % chg
input length           = 10; #Hint length: 252 ~= 12 mth<br>189 ~= 9 mth<br>126 ~= 6 mth<br>63 ~= 3 mth
input period           = AggregationPeriod.DAY; #Hint period: Must be DAY or greater for IV computations
input use_chart_ap     = NO; #Hint use_chart_ap: Set to YES to utilize the chart's aggregation period
input high_alert       =  5.0; #Hint high_alert: Percent equal to or above which to change % chg display color
input low_alert        = -5.0; #Hint low_alert: Percent equal to or below which to change % chg display color
input multiplier       = 100; #Hint multiplier: 100 turns the % chg into a percentage, 1 leaves it as a decimal
input rounding         = 2; #Hint rounding: Number of decimal digits to which % chg value shall round
input no_nans          = YES; #Hint no_nans: If YES, return the previous % chg if current data is NaN

# -------------------------------------------------------------------------
# Ensure Aggregation Period is supported per the Fundamental Type specified
# -------------------------------------------------------------------------
#
# IMP_VOLATILITY does not support Aggregration periods less than 1 day.
# So, display at least the daily IV value

def ap_choice = if use_chart_ap then GetAggregationPeriod() else period;
def ap        = If( ap_choice < AggregationPeriod.DAY && FundamentalType.IMP_VOLATILITY == data_type, AggregationPeriod.DAY, ap_choice );

# -------------------------------------------------------------------------
# Adjust high and low alert thresholds to account for % chg multiplier
# -------------------------------------------------------------------------

plot pct_chg; # declare here so it appears first in strategy settings box on TOS
plot hi_alert = high_alert * multiplier / 100.0;
plot lo_alert = low_alert  * multiplier / 100.0;

# -------------------------------------------------------------------------
# Compute the percent change
# -------------------------------------------------------------------------

# If 'no_nans' is enabled, the previously computed % chg is returned when
# the fundamental data is NaN. If there is no previously computed % chg,
# the value returned will be what TOS uses to initialize variables:
#
#    0 as of 11/4/2013.
#
# Otherwise, NaN will be returned when a gap is encountered.
#
# TODO: may need to distinguish between gap meaning no data period vs. a temporary hole in the data (like a halt)

#def data      = Fundamental(data_type, period=ap); #TODO: creates an array or maps 'data' to this fundamental array?
#def data      = close(period=ap); #TODO: creates an array or maps 'data' to this fundamental array?
#def pct_chg_v = if   no_nans && (IsNaN(data) or IsNaN(data[length]))
#                then pct_chg_v[1]
#                else Round(multiplier * (data / data[length] - 1), rounding);

# Using 'data', whether def or plot, to reference Fundamental() does not
# work as one would assume. During market hours (OnDemand or Live), the
# result returned for data[N] is the same as data[0]. Whereas explicitly
# doing Fundamental(dt,pd)[N] returns the actual value for N periods ago.
# The same is true if we reference directly 'close', 'open', etc.
#
# So, we have to be explicit in code until this ThinkScript error is fixed.

def pct_chg_v = if   no_nans && (IsNaN(Fundamental(data_type, period=ap)) or IsNaN(Fundamental(data_type, period=ap)[length]))
                then pct_chg_v[1]
                else Round(multiplier * (Fundamental(data_type, period=ap) / Fundamental(data_type, period=ap)[length] - 1), rounding);
pct_chg       = pct_chg_v;

# DEBUGGING TOOLS
#AddLabel(1, Concat("d[0]: ", data[0]), Color.CYAN);
#AddLabel(1, Concat("d[" + length + "]: ", data[length]), Color.CYAN);
#AddLabel(1, Concat("c[0]: ", close(period=ap)[0]), Color.CYAN);
#AddLabel(1, Concat("c[" + length + "]: ", close(period=ap)[length]), Color.CYAN);
#AddLabel(1, Concat("f[0]: ", Fundamental(data_type, period=ap)[0]), Color.CYAN);
#AddLabel(1, Concat("f[" + length + "]: ", Fundamental(data_type, period=ap)[length]), Color.CYAN);

# -------------------------------------------------------------------------
# Create visual effects, display label if requested
# -------------------------------------------------------------------------

# set colors based on hi and lo alert thresholds --------------------------

pct_chg.DefineColor("HiAlert", Color.UPTICK);
pct_chg.DefineColor("Normal" , Color.GRAY);
pct_chg.DefineColor("LoAlert", Color.DOWNTICK);
pct_chg.AssignValueColor( if pct_chg >= hi_alert then pct_chg.Color("HiAlert") else if pct_chg <= lo_alert then pct_chg.Color("LoAlert") else pct_chg.Color("Normal") );
hi_alert.SetDefaultColor( Color.YELLOW );
lo_alert.SetDefaultColor( Color.YELLOW );

# select the label's prefix based on the fundamental type -----------------

# cannot use switch/case as ThinkScript's fundamental types are not enums

AddLabel(show_label or label_only,
              Concat( if data_type == FundamentalType.IMP_VOLATILITY then "IV "
         else if data_type == FundamentalType.OPEN           then "$O "
         else if data_type == FundamentalType.HIGH           then "$H "
         else if data_type == FundamentalType.LOW            then "$L "
         else if data_type == FundamentalType.CLOSE          then "PRICE "
         else if data_type == FundamentalType.HL2            then "$HL2 "
         else if data_type == FundamentalType.HLC3           then "$HLC3 "
         else if data_type == FundamentalType.OHLC4          then "$OHLC4 "
         else if data_type == FundamentalType.VWAP           then "VWAP "
         else if data_type == FundamentalType.VOLUME         then "VOLUME "
         else if data_type == FundamentalType.OPEN_INTEREST  then "OI "
         else                                                     "",
         "%CHG("+length+") " + pct_chg),
         pct_chg.TakeValueColor() );

# hide plots if user wants labels only ------------------------------------

hi_alert.SetHiding( label_only );
lo_alert.SetHiding( label_only );
pct_chg.SetHiding( label_only );
 
Solution
I have updated the Percent Change Label (from Previous Day's Close) that should work both on a daily as well as intraday aggregations. Since the equities market is currently closed I have tested this against /ES. It seems to work fine as far as I can tell.

Code:
# Percent Change Label (from Yesterday's Close)
# tomsk
# 1.9.2020

# V1.0 - 01.08.2020 - tomsk - Initial release of Percent Change Label, to be used for daily aggregation
# V1.1 - 01.09.2020 - tomsk - Updated to work on intraday aggregation as well as daily

def PC = close(period = AggregationPeriod.DAY)[1];
def PctChange = (close - PC) / PC;
AddLabel(1, "Percent Change = " + AsPercent(PctChange), if PctChange > 0 then Color.Green else if PctChange < 0 then Color.Red else...
@number9 You did a good job on the changes. Try this code, I should have put .25 instead of 2.5, my fault.
Code:
def DailyClose = close(period = AggregationPeriod.DAY);
def PctChange = (DailyClose - DailyClose[1]) / DailyClose[1];
AddLabel(1, "Percent Change = " + AsPercent(PctChange),
         if PctChange > .25 then Color.White
         else if PctChange < 0 then Color.Red
         else Color.Green);
Again, I really appreciate your efforts, but it still isn't working. It still behaves just as the original script did, whereas everything greater than 0 is labeled green, and everything less than 0 is red. From what little bit I do know, it seems the suggestions you've provided should work. I don't understand why it does not.
 
It is odd. I tried a few different things to no avail. For some reason, ToS on my end is not recognizing any value over "0". For example, I simplified the script as follows to test my theory, and this particular script works as it should, whereas PctChange greater than 0 displays white, and less than 0 displays red. However, when I change the value of the 0 to anything else (1, 2, 2.5, etc.), it does not display white above that new value, and everything gets displayed as red. 0 is the only value that is working.


Code:
def DailyClose = close(period = AggregationPeriod.DAY);
def PctChange = (close - DailyClose[1]) / DailyClose[1];
AddLabel(1, "Percent Change = " + AsPercent(PctChange), if PctChange > 0 then Color.White else Color.Red);
PctChange is defined in decimal format so
1% is .01
100% would be 1.00
 
@number9 That's odd, when I ran it on TOS it seemed to work fine. I'll look into it tomorrow.
Figured it out!
PctChange is defined in decimal format so
1% is .01
100% would be 1.00
Haha! I was just responding to tell you when I tried .025 it worked. Thanks again!

Edit: In case you're confused, I thought XeoNox was you replying earlier, and saw that message briefly as I refreshed my screen as I was about to reply. Nonetheless, thanks for all your help!
 
Last edited:
TOS provides several placeholder columns you can customize, named Custom1, Custom2, etc. Search for how to customize a watchlist column and then set the column's name at the top to whatever you want, set the time period to Yr and paste in this code.

Ruby:
plot ytd = (close / open - 1) * 100;
 
This worked like a charm, thank you!
Is there any way to format the returned value with green/red and a % at the end so that it displays similar to the existing %change field?
 
This worked like a charm, thank you!
Is there any way to format the returned value with green/red and a % at the end so that it displays similar to the existing %change field?
You can either have the % symbol or you can have it sort correctly, not both. Adding the % turns the value into a string of text instead of a number which breaks the sorting. You get 1, 10, 100, 2, 3, 4, etc. Here's colors without the %. The red/orange color doesn't match TOS's other columns. I tried their named colors Color.DOWNTICK, Color.DARK_ORANGE and Color.LIGHT_RED and light red was the closest. If you care enough to put more effort into it you can use the CreateColor function to create a better match.

Ruby:
plot ytd = (close / open - 1) * 100;
ytd.AssignValueColor(if ytd > 0 then Color.UPTICK else Color.LIGHT_RED);
 
You would have to change the code to use a label instead of a plot and, as I mentioned, you will no longer be able to sort using this column.

Ruby:
def ytd = (close / open - 1);
AddLabel(yes, AsPercent(ytd), if ytd > 0 then Color.UPTICK else Color.LIGHT_RED);
 
I just tried to test this using the code from above (found below)

# Get percent change from Open

input agg = AggregationPeriod.DAY;

def currentPrice = ask;
def openPrice = open(period = agg); # Fixed: I didn't set the agg as a parameter to the open price


def priceDifference = ask - open;

plot percentChange = priceDifference / open;

AddLabel(yes, asPercent(round(percentChange,4)), if ask < open then color.RED else color.Green); # changed close to ask

#Edit - fixed the label

and I got the errors:
Expected double at 5:5
Expected double
No such variable: ask at 5:20
No such variable: ask at 9:23
No such variable: ask at 13:53
Expected double at 5:5
No such variable: ask at 5:20
No such variable: ask at 9:23
No such variable: ask at 13:53

it looks like all the errors are related to the ask. Can you possibly tell me why it won't run for me when it appears the other folks had theirs run? Finally, I wanted to set this to notify me of intraday price changes amounting to a 3% or more increase increase or decrease and have had no luck. I thought this code could help get me there but I need a little help. If anyone can correct the above that would be wonderful.

Thank you.
 
@homeplate2 @MerryDay After looking it up ( https://tlc.thinkorswim.com/center/reference/thinkScript/Constants/PriceType/PriceType-ASK ) this is what I figured out using the supplied code. Its two labels, one uses "close" and the other "pricetype.ASK" . . . have no idea if this gives proper results, first time I've used this pricetype.ASK method and I didnt test or check anything. @rad14733 What do you think?

Code:
input agg = AggregationPeriod.DAY;
def openPrice = open(period = agg);
def priceDifference = close(pricetype = PriceType.ASK) - open;
def percentChange = priceDifference / open;
AddLabel(yes, "ASK %Change: " + asPercent(round(percentChange,4)), if close(pricetype = PriceType.ASK) < open then color.RED else color.Green);

input agg1 = AggregationPeriod.DAY;
def openPrice1 = open(period = agg1);
def priceDifference1 = close - open;
def percentChange1 = priceDifference1 / open;
AddLabel(yes, "CLOSE %Change: " + asPercent(round(percentChange1,4)), if close < open then color.RED else color.Green);
 
@Pensar I tested your code and it gives different results for different timeframes, which it shouldn't for a daily aggregation... Both %Change's are the same, but vary... The first calculation doesn't display results on a daily timeframe... Odd results...
 
@rad14733 Thanks, I suspicioned as much. Looking it over myself, it doesn't appear that the extra trouble for using BID and ASK is worth the results. The documentation says that BID and ASK pricetypes are not supported on daily or higher, so that's probably why it didn't work on daily.
 
I want to find "the dip" but only those that happen before the first 45 minutes with a minimum of 13% from the open.... I tried the basic price change/open at least 13% less than 20 bars, but it is already giving me results and the open hasn't happened yet... Shouldn't the scan get nothing at least until the twenty minute mark?
 
You would have to change the code to use a label instead of a plot and, as I mentioned, you will no longer be able to sort using this column.

Ruby:
def ytd = (close / open - 1);
AddLabel(yes, AsPercent(ytd), if ytd > 0 then Color.UPTICK else Color.LIGHT_RED);
This is just what I need. Thank you!
 
How to add the % sign at the end of the value. Currently, it is showing a value like 10.50 but it would be nice to see 10.50%.

plot x=round(100*((close/open)-1),1);
x.assignValueColor( if x < 0 then color.RED else color.GREEN);
I realize this is an old post but for those new to searching this here is all I did:

Code:
plot x=round(100*((close/open)-1),1);
AddLabel (yes, x + "% " , if x > 0 then Color.UPTICK else Color.DOWNTICK);
 
Thanks @tomsk for the updated version, works great!

I also found this in my collection of studies which displays both % change & net change based on the prior day close.

Code:
#HINT: This study plots a chart label for Net Change and Percent Change from prior close (regardless of time period of the chart.

input period_Type = AggregationPeriod.DAY;

def begin = close(period = period_Type)[1];
def end = close(period = period_Type);
def NetChg = end - begin;
def PctChg = (end / begin) - 1;

AddLabel(yes, "Change from Prior close:  " + AsDollars(NetChg) + "  " + AsPercent(PctChg), if NetChg > 0 then CreateColor(100,200,100) else if NetChg < 0 then Color.Dark_Orange else color.LIGHT_GRAY);


The code above works perfect to match percent change on the TOS platform for stocks, but when you load a futures symbol it don't match. I realized futures market have different open/close times then stock for most likely this is the issue. Is there a code or can this be modified to show the correct percent change to match what TOS has for FUTURES ?
 

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

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
403 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