Archive for the ‘Thinkscript Tips’ Category

Thinkscript Tip: Alert Code Block for Think or Swim

January 11, 2010

Here’s a block of code that I am going to be implementing in scripts in future. It’s a generic alert configuration code. It assumes that you have another line in your code that defines what the alert trigger is, like so:

def alerttrigger = high>high[1];

The line above says I want an alert if the bar’s high is above the last bar’s high, for example. You can replace it with whatever you want to be alerted about.

Here’s the alert code:

# Alerts:
def alerttrigger = 1; #replace the 1 with your definition
input alerttext = “Alert Text”;
input UseAlerts = {false, default true};
input AlertType = {default “BAR”, “ONCE”, “TICK”};
def at=AlertType;
input AlertSound = {“Bell”, “Chimes”, default “Ding”, “NoSound”, “Ring”};
alert(alerttrigger AND UseAlerts, alerttext, if at==1 then Alert.ONCE else if at==2 then Alert.TICK else Alert.BAR, AlertSound);

To use it in your scripts, change the “def alerttrigger = 1;” line in your code to what you want to alert about, and then drop the entire code block at the end of your script file. Instant configurable alerts!

Thinkscript Tip: Creating Alerts Using Thinkscript in Think or Swim

December 8, 2009

The newest Think or Swim release brings us the ability to create alerts from Thinkscript code! Here’s how to use them.

The syntax of the “alert” function is as below:

alert(condition, text, alert type, sound);

The required arguments are Condition and Text. The other two are optional, but have defaults if you don’t supply them:

alert type default value: Alert.ONCE
sound default value: Sound.NoSound

Condition is the logical value that will trigger the alert, and should be calculated to either 1 or 0 (true or false).
Text is the text string that will appear in the alert window when it is triggered.
Alert Type tells the alert how often it can trigger. The different ‘alert type’ parameters are:

Alert.ONCE – alert can be triggered only once after adding study
Alert.BAR – alert can be triggered only once per bar
Alert.TICK – alert can be triggered after each tick

Sound tells the alert what sound to play (if any). The different ‘sound’ parameters are:


Now some examples! This code will call an alert once on every single bar:


If you wanted an alert on RSI(2)>80 for example, you could use

def condition=if RSIWilder(2)>80 then 1 else 0;

If you had a plot of the NYSE TICK and wanted an alert every single time that a tick comes across as above 1000 or below -1000 (and didn’t value your sanity), you could use

alert(close>1000,”High TICK!”,alert.TICK,sound.DING);
alert(close<-1000,"Low TICK!",alert.TICK,sound.DING);

A slightly less ADD version would be to alert only once per bar, like so:

alert(close>1000,”High TICK!”,alert.BAR,sound.DING);
alert(close<-1000,"Low TICK!",alert.BAR,sound.DING);

And finally, if you only wanted the first +1000 tick of the day, you could use

alert(close>1000,”High TICK!”,alert.ONCE,sound.DING);

If you have any cool uses of Thinkscript alerts, feel free to share in the comments!

Paintbars Come to Think or Swim

September 12, 2009

The latest Think Desktop software release has finally given us paintbars! Inside of your thinkscript code, you just use the function “AssignPriceColor(;”. You can also use conditional statements inside the function call, like this:

assignpricecolor(if close>ema then else;

That code will paint the bar green if its close is above the value of “ema”, or red if it is below. Here’s the result on a chart:


Thinkscript Tip: GetAggregationPeriod

March 25, 2009

In Thinkscript, there is a function called GetAggregationPeriod(). This function returns the current aggregation period in milliseconds. The aggregation period is defined as the number of milliseconds it takes to complete a candle on the current chart timeframe. Here are the aggregation period values for the different ToS chart timeframes:

Any Tick Chart = 0
1 min = 60,000
2 min = 120,000
3 min = 180,000
4 min = 240,000
5 min = 300,000
10 min = 600,000
15 min = 900,000
20 min = 1,200,000
30 min = 1,800,000
1 hr = 3,600,000
2 hr = 7,200,000
4 hr = 14,400,000
Daily = 86,400,000
Weekly = 604,800,000
Monthly = 2,592,000,000

There are a few built in constants for some of these aggregation periods, such as Aggregationperiod.MIN and Aggregationperiod.DAY. These constants would return the values for 1 min and Daily from the list above. But as you can see, there are many available values, more than there are defined constants.

If you want to tell the difference between intraday and end of day charts, you simply check if the GetAggregationPeriod() function returns a value of 86,400,000 or more. If so, it’s daily or greater. Less than that, it’s an intraday chart. You can also use it in any script that needs to know the duration of a candle. Just call the function and divide the answer by 60,000 to get the number of minutes, or divide by 1,000 to get the number of seconds.

In my earlier usage of GetAggregationPeriod in strategies, I used it incorrectly to try to indentify the differences in intraday and end of day charts. I was trapping specific values of aggregation period instead of just checking for daily or above. My check for the market close was also messed up; it only worked right if you showed aftermarket data. I will be updating all of my strategy codes over time to calculate these the correct way.

The following code will get the aggregation period, and check if that value is equal to or greater than the value for a daily candle. If it is, then the market is always considered to be “open” as far as the study/strategy is concerned. If the aggregation period value is less than the value for a daily candle, then we know we are on an intraday chart and the check is made to see if the market is open or not:

input opentime=0930;
input closetime=1600;
def AP=getaggregationperiod();
def daily=if AP>=aggregationPeriod.DAY then 1 else 0;
def isopen=if daily then 1 else if secondsFromTime(opentime)>=0 and secondstillTime(closetime)>=0 then 1 else 0;

Then you can use isopen as a boolean to check if the market is open before entering a strategy trade.

The next code will use aggregation period to check if we are on the last candle of the market session, which is useful for an “exit at close” strategy:

input closetime=1600;
def AP=getaggregationperiod();
def daily=if AP>=aggregationPeriod.DAY then 1 else 0;
def islastcandle=if daily then 0 else if secondstillTime(closetime)<=AP/1000 then 1 else 0;

Note that this doesn’t really work for tick charts, where the candle is not time-based. If you do testing with tick charts, I recommend showing all data, not just market hours. If I figure out a better way, I’ll post it.

Thinkscript Tip: Dealing With Added Bars On Charts

February 27, 2009

My Formatted Pivot Points Thinkscript was designed to dynamically hide faraway pivot levels based on where the price is currently. If you are down under S1, you don’t want to be looking at R3 all day long. The only problem was that whenever the current bar close would tick above or below the local pivot level, the respective hidden lines would appear and disappear on my chart, making the screen flash like something from YTMND. Easy fix, make the hiding depend on “close[1]” instead of “close”! Not so fast…

If you add extra bars to the right hand side of the chart (which I like to do), then “close[1]” IS NOT the prior bar! Neither is “close” the current bar. “Close” actually refers to the furthest right “bar” on the chart. If you added 5 bars, then “close” is the bar 5 periods into the future. Since this value doesn’t exsist yet, it is assigned the value N/A, and the value returned by calls to the “close” function are the last good value for close, which is the current bar closing price. Most of the time, this is fine since all you care about is the current bar anyway. But if you add bars, and then want to reference bars in the recent past, you won’t get the values you think you will. If I added 5 bars to the right, to get the bar one previous to the current one, I’d have to reference “close[6]”. But you can get around this by using this code:

# Define lastclose = last completed bar close:
rec lastclose=if isnan(close[-1]) then lastclose[1] else close;

Then you use “lastclose” in your code instead of “close”, and you will get the right values no matter how many extra bars you have on the right. I updated the Formatted Pivot Points code with this change, so no more seizures. You’re welcome.