Mastering ThinkScript

A Comprehensive Guide to Coding in ThinkOrSwim's Proprietary Language

ThinkScript is the proprietary programming language used in Schwab's ThinkOrSwim (TOS) platform, designed specifically for creating custom indicators, strategies, and studies for technical analysis and trading. While it shares similarities with other programming languages, ThinkScript has unique syntax and features tailored for financial market analysis. The ThinkScript syntax is most similar to JavaScript, with elements of C/C++ mixed in. This guide will take you from the fundamentals to advanced techniques, helping you leverage the full power of the ThinkOrSwim platform through custom scripting.

Getting Started with ThinkScript

Unlike standalone programming languages, ThinkScript exists exclusively within the ThinkOrSwim platform. To begin writing ThinkScript you will need a Schwab account. If you have an account you can go here to download the ThinkOrSwim app. https://www.schwab.com/trading/thinkorswim. Once you have the app…

  • Open the ThinkOrSwim platform on your desktop.

  • Click on the “Charts” tab at the top.

  • In the upper-right area of the chart window, click the “Studies” button (beaker icon).

  • From the dropdown menu, choose “Edit Studies…”.

  • In the Edit Studies and Strategies window, click “Create” (at the bottom left) to open the ThinkScript Editor. From there, you can write or paste in your custom ThinkScript code and save it as a study.

  • NOTE: You can also create your ThinkScript files outside your editor and Import them. For this click “Import” instead of “Create”. Make sure you have saved your scripts with an extension of “.ts” and an name that ends with “STUDY.ts”. This is the filetype that the Import option is looking for.

ThinkScript Fundamentals

Comments and Code Structure

In ThinkScript comments are created by using hash (pound) sign as follows:

# Single line comment

#
Multi-line
comment block
#

# Begin study: MyCustomIndicator
# @author: Your Name
# @version: 1.0
# @description: This indicator measures price momentum
declare lower;  # Declares a separate plot below the price chart

Variables and Data Types

ThinkScript is a strongly typed language with the following primary data types:

# Numeric types
def price = close;           # Float (default for price data)
def period = 14;             # Integer
def isActive = 1;            # Boolean (1 = true, 0 = false)

# String (limited usage in ThinkScript)
def conditionMet = if close > open then "Bullish" else "Bearish";

# Special series-based types
def highestHigh = Highest(high, 20);  # Time series-based calculation

Basic Plotting

# Plot current price with custom appearance
plot Price = close;
Price.SetDefaultColor(GetColor(1));
Price.SetLineWeight(2);
Price.SetStyle(Curve);

# Plot a horizontal line
plot ZeroLine = 0;
ZeroLine.SetDefaultColor(Color.GRAY);
ZeroLine.SetStyle(Line);

Conditional Logic

# If-Then-Else structure
def direction = if close > close[1] 
                then 1        # Price rising
                else if close < close[1] 
                then -1       # Price falling
                else 0;       # Price unchanged

# Switch-case equivalent
def barColor = CompoundValue(
    1,
    Switch(
        direction,
        1, Color.GREEN,
        -1, Color.RED,
        Color.GRAY  # default case
    ),
    Color.GRAY  # default for first bar
);

Using Loops (Recursive Functions)

ThinkScript doesn't support traditional loops, but recursive functions can be used to simulate loops:

# Recursively calculate sum of last N values
rec sumOfRange = if IsNaN(sumOfRange[1]) then close else sumOfRange[1] + close;

# Self-referential loop with counter
def counter = if IsNaN(counter[1]) then 0 else counter[1] + 1;
def sumLast5 = if counter >= 5 then 0 else close + sumLast5[1];

Some Sample Scripts to learn from

Sometimes the easiest way to understand code is to look through a working example. As you read the following scripts pay attention to the comments. They will explain what each part of the code does.

# ===================================================================
# MULTI-INDICATOR TREND ANALYSIS - Sample ThinkScript
# ===================================================================
# This script combines multiple technical indicators to analyze market trends
# Great for learning how different indicators work together
# ===================================================================

# This tells ThinkorSwim to put this indicator in the lower panel (not on price chart)
declare lower;

# ==================================================================
# INPUT PARAMETERS - These can be adjusted in the study settings
# ==================================================================
input fastEMA = 12;        # Fast Exponential Moving Average period
input slowEMA = 26;        # Slow Exponential Moving Average period  
input rsiLength = 14;      # RSI calculation period (standard is 14)
input macdFastLength = 12; # MACD fast EMA period
input macdSlowLength = 26; # MACD slow EMA period
input macdLength = 9;      # MACD signal line period
input volumeLength = 20;   # Period for volume average calculation

# ==================================================================
# BASIC DATA VARIABLES
# ==================================================================
# 'def' creates variables in ThinkScript
# These grab the basic price and volume data we need
def price = close;  # Current bar's closing price
def vol = volume;   # Current bar's volume

# ==================================================================
# EXPONENTIAL MOVING AVERAGES (EMA)
# ==================================================================
# EMAs give more weight to recent prices than simple moving averages
# When fast EMA crosses above slow EMA = bullish signal
# When fast EMA crosses below slow EMA = bearish signal

def emaFast = ExpAverage(price, fastEMA);  # 12-period EMA
def emaSlow = ExpAverage(price, slowEMA);  # 26-period EMA

# Boolean (true/false) variable: true when fast EMA > slow EMA
def emaCross = emaFast > emaSlow;

# ==================================================================
# RELATIVE STRENGTH INDEX (RSI)
# ===================================================================
# RSI measures momentum on a scale of 0-100
# Above 70 = potentially overbought (might fall)
# Below 30 = potentially oversold (might rise)
# Between 30-70 = neutral zone (healthier for trend continuation)

def rsi = RSI(length = rsiLength);

# Create boolean conditions for RSI levels
def rsiOverbought = rsi > 70;          # RSI too high
def rsiOversold = rsi < 30;            # RSI too low  
def rsiNeutral = rsi >= 30 and rsi <= 70;  # RSI in healthy range

# ===================================================================
# MACD (Moving Average Convergence Divergence)
# ===================================================================
# MACD shows relationship between two moving averages
# MACD Line = (12-day EMA - 26-day EMA)
# Signal Line = 9-day EMA of MACD Line
# When MACD > Signal = bullish momentum
# When MACD < Signal = bearish momentum

def macdValue = MACD(fastLength = macdFastLength, slowLength = macdSlowLength, MACDLength = macdLength);
def macdAvg = MACD(fastLength = macdFastLength, slowLength = macdSlowLength, MACDLength = macdLength).Avg;

# Calculate difference between MACD line and signal line
def macdDiff = macdValue - macdAvg;

# Boolean: true when MACD is above its signal line (bullish momentum)
def macdBullish = macdValue > macdAvg;

# ===================================================================
# VOLUME ANALYSIS
# ===================================================================
# Volume confirms price movements
# High volume + price movement = stronger signal
# Low volume + price movement = weaker signal

def avgVolume = Average(vol, volumeLength);  # 20-period average volume

# Boolean: true when current volume is 50% higher than average
# This indicates increased interest/activity
def volumeSpike = vol > avgVolume * 1.5;

# ===================================================================
# COMPOSITE SIGNALS - Combining Multiple Indicators
# ===================================================================
# Strong signals occur when multiple indicators agree
# This reduces false signals that happen with single indicators

# BULLISH SIGNAL: All conditions must be true
# 1. Fast EMA above slow EMA (uptrend)
# 2. RSI in neutral zone (not overbought)
# 3. MACD above signal line (momentum up)
# 4. Volume spike (confirmation)
def bullishSignal = emaCross and rsiNeutral and macdBullish and volumeSpike;

# BEARISH SIGNAL: All conditions must be true
# 1. Fast EMA below slow EMA (downtrend)
# 2. RSI overbought (potential reversal)
# 3. MACD below signal line (momentum down)
# 4. Volume spike (confirmation)
def bearishSignal = !emaCross and rsiOverbought and !macdBullish and volumeSpike;

# ===================================================================
# TREND STRENGTH CALCULATION (0-100 Scale)
# ===================================================================
# This creates a single number showing overall trend strength
# Each indicator contributes points to the total score
# Higher score = stronger bullish trend
# Lower score = stronger bearish trend

def trendStrength = 
    (if emaCross then 25 else 0) +           # EMA trend: 25 points if bullish
    (if rsiNeutral then 25 else if rsiOversold then 15 else 0) + # RSI: 25 for neutral, 15 for oversold, 0 for overbought
    (if macdBullish then 25 else 0) +        # MACD: 25 points if bullish
    (if volumeSpike then 25 else 10);        # Volume: 25 for spike, 10 for normal

# ===================================================================
# PLOT STATEMENTS - What Actually Shows on Chart
# ===================================================================
# 'plot' statements create the lines and points you see on the indicator

# Main trend strength line (0-100 scale)
plot TrendStrengthLine = trendStrength;

# Alert dots - only appear when strong signals trigger
plot BullishAlert = if bullishSignal then 90 else Double.NaN;  # Green dot at 90 level
plot BearishAlert = if bearishSignal then 10 else Double.NaN;  # Red dot at 10 level

# Reference line at 50 (middle of 0-100 range)
plot Midline = 50;

# RSI line and reference levels (for comparison)
plot RSI_Line = rsi;
plot RSI_70 = 70;  # Overbought line
plot RSI_30 = 30;  # Oversold line

# ===================================================================
# FORMATTING - Making the Chart Look Good and Easy to Read
# ===================================================================

# Trend strength line formatting
TrendStrengthLine.SetDefaultColor(Color.CYAN);  # Light blue color
TrendStrengthLine.SetLineWeight(2);             # Thick line

# Bullish alert formatting
BullishAlert.SetDefaultColor(Color.GREEN);                    # Green color
BullishAlert.SetPaintingStrategy(PaintingStrategy.POINTS);    # Show as dots
BullishAlert.SetLineWeight(5);                               # Large dots

# Bearish alert formatting  
BearishAlert.SetDefaultColor(Color.RED);                     # Red color
BearishAlert.SetPaintingStrategy(PaintingStrategy.POINTS);   # Show as dots
BearishAlert.SetLineWeight(5);                              # Large dots

# Midline formatting
Midline.SetDefaultColor(Color.GRAY);      # Gray color
Midline.SetStyle(Curve.SHORT_DASH);       # Dashed line

# RSI line formatting
RSI_Line.SetDefaultColor(Color.YELLOW);   # Yellow RSI line
RSI_70.SetDefaultColor(Color.RED);        # Red overbought line
RSI_70.SetStyle(Curve.SHORT_DASH);        # Dashed
RSI_30.SetDefaultColor(Color.GREEN);      # Green oversold line
RSI_30.SetStyle(Curve.SHORT_DASH);        # Dashed

# ===================================================================
# BACKGROUND CLOUDS - Visual Trend Zones
# ===================================================================
# AddCloud creates colored background areas to highlight trend strength

# Strong bullish zone (trend strength > 75%)
AddCloud(if trendStrength > 75 then 100 else Double.NaN, 0, Color.DARK_GREEN, Color.DARK_GREEN);

# Strong bearish zone (trend strength < 25%)  
AddCloud(if trendStrength < 25 then 100 else Double.NaN, 0, Color.DARK_RED, Color.DARK_RED);

# ===================================================================
# LABEL - Text Display on Chart
# ===================================================================
# Shows current trend strength percentage with color coding

AddLabel(yes, "Trend: " + trendStrength + "%", 
    if trendStrength > 75 then Color.GREEN        # Green for strong bullish
    else if trendStrength < 25 then Color.RED     # Red for strong bearish
    else Color.YELLOW);                           # Yellow for neutral

# ===================================================================
# HOW TO USE THIS INDICATOR:
# ===================================================================
# 1. Trend Strength 75-100% = Strong bullish trend
# 2. Trend Strength 25-75% = Neutral/mixed signals  
# 3. Trend Strength 0-25% = Strong bearish trend
# 4. Green dots = Strong buy signals (multiple confirmations)
# 5. Red dots = Strong sell signals (multiple confirmations)
# 6. Watch for trend strength changes at key levels
# ===================================================================

Another Sample Script that monitors the bid and ask and alerts when the spread is more than 25 cents

# =============================================================
# Wide Bid-Ask Spread Alert Study
# Purpose: Identifies stocks with bid-ask spreads greater than 25 cents
# 
# Learning Notes:
# - This study demonstrates key ThinkScript concepts including variables,
#   conditions, plotting, alerts, labels, and conditional formatting
# =============================================================

# DECLARE STATEMENT
# This tells ThinkScript where to display the study on the chart
# "lower" means it appears in a separate panel below the main price chart
declare lower;

# =============================================================
# INPUT PARAMETERS
# =============================================================

# INPUT DECLARATION
# Creates a user-adjustable parameter that appears in the study settings
# Users can change this value without editing the code
# Default value is 0.25 (representing 25 cents)
input spreadThreshold = 0.25;

# =============================================================
# VARIABLE DEFINITIONS - Getting Real-Time Market Data
# =============================================================

# BUILT-IN MARKET DATA VARIABLES
# ThinkScript provides built-in variables for real-time market data
# These values update continuously during market hours
# "close(priceType = PriceType.BID)" = current highest price buyers are willing to pay
# "close(priceType = PriceType.ASK)" = current lowest price sellers are willing to accept
# NOTE: The bid/ask spread is calculated and monitored in real-time
def currentBid = close(priceType = PriceType.BID);
def currentAsk = close(priceType = PriceType.ASK);

# CALCULATED VARIABLES
# The spread is the difference between ask and bid prices at the same moment
# This represents the cost of immediate execution (market impact)
# Real-time monitoring: spread updates whenever bid or ask changes
def spread = currentAsk - currentBid;

# BOOLEAN CONDITIONS
# Creates a true/false condition that we can use throughout the script
# Returns "yes" when current spread is greater than our threshold, "no" otherwise
# This condition is evaluated in real-time as market prices change
def wideSpread = spread > spreadThreshold;

# =============================================================
# PLOTTING AND VISUALIZATION
# =============================================================

# PRIMARY PLOT - The main spread line
# This draws the actual bid-ask spread value over time
plot SpreadLine = spread;
SpreadLine.SetDefaultColor(Color.CYAN);        # Sets line color to cyan
SpreadLine.SetLineWeight(2);                   # Makes line thicker (1-5 scale)

# REFERENCE LINE - Shows our threshold
# This horizontal line helps visualize when spreads cross our alert level
plot ThresholdLine = spreadThreshold;
ThresholdLine.SetDefaultColor(Color.YELLOW);   # Yellow for easy identification
ThresholdLine.SetStyle(Curve.LONG_DASH);       # Dashed line style for distinction

# CONDITIONAL HIGHLIGHTING - Only shows when condition is met
# Uses conditional logic: "if condition then value else NaN"
# NaN (Not a Number) means "don't plot anything" for those bars
plot WideSpreads = if wideSpread then spread else Double.NaN;
WideSpreads.SetDefaultColor(Color.RED);                      # Red for alerts
WideSpreads.SetLineWeight(3);                                # Extra thick line
WideSpreads.SetPaintingStrategy(PaintingStrategy.HISTOGRAM); # Draws as bars from zero

# =============================================================
# ALERTS AND NOTIFICATIONS
# =============================================================

# ALERT FUNCTION
# Triggers when the boolean condition becomes true
# Parameters: (condition, message, alert_type, sound)
# Alert.BAR means it alerts once per bar when condition is first met
Alert(wideSpread, "Wide Spread Alert: " + spread + " cents", Alert.BAR, Sound.DING);

# =============================================================
# INFORMATION LABELS
# =============================================================

# DYNAMIC LABELS - Show current market information
# AddLabel syntax: (show_condition, text, color)
# "yes" means always show the label
# Color can be conditional using "if-then-else" logic

# Current spread with conditional coloring (red if wide, green if normal)
AddLabel(yes, "Current Spread: " + AsDollars(spread), 
         if wideSpread then Color.RED else Color.GREEN);

# Current bid price (always white)
AddLabel(yes, "Bid: " + AsDollars(currentBid), Color.WHITE);

# Current ask price (always white)  
AddLabel(yes, "Ask: " + AsDollars(currentAsk), Color.WHITE);

# CONDITIONAL LABEL - Only shows when spread is wide
# This label only appears when our alert condition is true
AddLabel(wideSpread, "WIDE SPREAD ALERT!", Color.RED);

# =============================================================
# BACKGROUND HIGHLIGHTING
# =============================================================

# CLOUD FUNCTION - Creates colored background areas
# AddCloud fills the area between two plot lines
# Here we fill between the spread value and zero, but only when spread is wide
# This creates a red background highlight during alert periods
AddCloud(if wideSpread then spread else Double.NaN, 0, Color.RED, Color.RED);

# =============================================================
# OPTIONAL FEATURES
# =============================================================

# SCAN CONDITION - For use in ThinkOrSwim stock scanners
# Uncomment this line to use this study in a custom stock scan
# The scanner will find all stocks where this condition is currently true
# plot ScanCondition = wideSpread;

# =============================================================
# HOW TO USE THIS STUDY:
# 1. Copy this entire script
# 2. In ThinkOrSwim: Studies > Edit Studies > New > Paste this code
# 3. Save with a name like "Wide Spread Alert"
# 4. Apply to any chart - it will appear in the lower panel
# 5. Adjust the spreadThreshold input as needed for different alert levels
# =============================================================

Note: the ThinkScript editor within ThinkOrSwim will show you errors in your script in a windows just below the script. Pay attention to these as you will need to fix them before the script will run. On the right is a list of Reserved Words, Declarations, Functions, Constants, and Data Types. These are great resources.

Also Note: Clicking on “Help on thinkScript” button will load the thinkScript documentation is a much larger window so that is is easier to read.

In my opinion, one of the drawbacks of ThinkScript is the web-based editor so I have created a ThinkScript syntax highlighter for Sublime Text Editor. If you use Sublime Text and also write ThinkScript here are the steps to add this extension:

  • Open Sublime Text, go to Preferences > Browse Packages.. This will open up an explorer window.

  • Download the extension at https://www.wasql.com/ThinkScript_Sublime_Text.zip

  • Unzip this file into the window that opened up from Sublime.

  • Restart Sublime Text.

Conclusion

ThinkScript is a very extensive scripting language that allows you to create powerful custom indicators and strategies in the ThinkOrSwim platform. By mastering ThinkScript, you can significantly enhance your trading analysis capabilities and create tools tailored to your specific trading approach.

Remember to test your ThinkScript code thoroughly on historical data before using it for actual trading decisions. The ThinkOrSwim platform provides excellent backtesting capabilities to help validate your custom indicators and strategies.

Happy coding, and may your custom indicators bring clarity to your trading!