Low Float "Heat Meter" For ThinkOrSwim

TRADERSAM

Member
VIP
This indicator, was designed to track momentum conditions often seen in volatile low-float stocks. It combines three key components that tend to drive explosive moves: gap percentage, relative volume (RVOL), and ATR expansion. Each factor contributes one point toward a composite “heat score” that ranges from 0 to 3. A score of 3 means all three conditions are firing at once: the stock is gapping up above a set threshold, trading with abnormal volume relative to its recent average, and showing volatility expansion (fast ATR significantly outpacing slow ATR). This creates a simple but powerful way to visualize when a stock is “heating up,” and it’s best applied to stocks that just released fresh news after hours or pre-market, since those catalysts often spark the exact kind of momentum this script is built to capture.

To keep things clear on the chart, the script smooths the score into a line called the, plotted at the bottom panel. Clouds help visually separate different levels of strength, while labels summarize the current readings for gap %, RVOL, and ATR ratio. A vertical green line will print when the Heat Line crosses into stronger territory, and an optional alert can be triggered when the score hits 3/3. This makes the Heat Meteor not just a momentum filter, but also a real-time scanner for conditions that often precede parabolic intraday moves. It’s especially useful for traders who like to focus on small-cap runners or news-driven plays and want a quick way to confirm when momentum is stacking in their favor.

Ruby:
declare lower;

# ========================
# Low Float "Heat Metor"
# Created By TraderSam
# Coded 08/23/25
# ========================

# === Inputs ===
input rvolLen = 20;  # Lookback length for avg volume
input rvolThresh = 3.0;  # RVOL threshold
input atrFastLen = 5;  # fast ATR length
input atrSlowLen = 20;  # slow ATR length
input atrRatioThresh = 2.0;  # ratio threshold (fast/slow)
input gapThreshPct = 7.0;  # gapMomentum % threshold
input useLiveGap = yes;
input AlertOn = yes;
input smoothLen = 3;  # smoothing length for Heat line

# === Basic Def ===
def c = close;
def h = high;
def o = open;
def l = low;
def v = volume;
def na = Double.NaN;

# === Daily references for Gap ===
def pcloseD = close(period = aggregationPeriod.DAY)[1];
def oD = open(period = aggregationPeriod.DAY);

# === Gap % ===
def lgpct = if pcloseD != 0 then 100 * (c - pcloseD) / pcloseD else 0;  # live gap
def ogpct = if pcloseD != 0 then 100 * (oD - pcloseD) / pcloseD else 0;  # open gap
def gpct = if useLiveGap then lgpct else ogpct;
def cGap = gpct >= gapThreshPct;  # Condition: Gap over threshold

# === Relative Volume (RVOL) ===
def avgv = Average(v, rvolLen);
def rvol = if avgv > 0 then v / avgv else 0;
def cRVOL = rvol >= rvolThresh;  # condition: rvol over threshold

# === ATR Expansion ===
def tr = TrueRange(h, c, l);
def atrf = MovingAverage(AverageType.WILDERS, tr, atrFastLen);  # fast ATR
def atrs = MovingAverage(AverageType.WILDERS, tr, atrSlowLen);  # slow ATR
def atrx = if atrs > 0 then atrf / atrs else 0;
def cATR = atrx >= atrRatioThresh;  # condition: atr ratio over threshold

# === Composite Score (0–3) ===
def score = (if cGap then 1 else 0) + (if cRVOL then 1 else 0) + (if cATR then 1 else 0);

# === Smoothed Heat Line ===
def smoothHeat = Average(score, smoothLen);
plot HeatLine = smoothHeat;
HeatLine.SetDefaultColor(Color.cyan);
HeatLine.SetLineWeight(3);

# === Clouds ===
addcloud(1, 0, color.gray);
addcloud(2, 1, color.green);
addcloud(3, 2, color.dark_green);

# === Verticle Lines ===
def crossUp3 = smoothHeat[1] < 2 and smoothHeat >= 2;
AddVerticalLine(crossUp3, "BUY", Color.GREEN);

# === Labels ===
AddLabel(yes, "Heat " + Round(smoothHeat,1) + " | Gap " + AsText(Round(gpct, 1)) + "%" + " | RVOL " + AsText(Round(rvol, 2)) + " | ATRx " + AsText(Round(atrx, 2)), if smoothHeat >= 3 then Color.GREEN else if smoothHeat >= 2 then Color.DARK_GREEN else if smoothHeat >= 1 then Color.YELLOW else Color.GRAY);

# === Alerts ===
Alert(AlertOn and score >= 3, "Heat 3/3 (Gap+RVOL+ATR)", Alert.BAR, Sound.Ding);
Screenshot 2025-08-25 at 5.38.36 PM.png
 
Last edited:

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

I’d love to hear any thoughts on how it could be improved — whether that’s cleaner logic, better visuals, or even new conditions to add in. If you see a way to make it more useful, please feel free to share your ideas!!
 
Put this into chatGPT and had it create me a version that gives bearish signals. After tweaking it a little I think I have it to a place that I like. I will say though the bearish signals do not fire nearly as much as the bullish ones. I don't know why this is but if anyone does please let me know. Here is the code:
Ruby:
declare lower;

# ==============================
# Low Float "Heat Meteor" (Bidirectional)
# Created By TraderSam
# Updated: 08/23/25
# ==============================

# === Inputs ===
input rvolLen       = 20;    # Lookback length for avg volume
input rvolThresh    = 3.0;   # RVOL threshold
input atrFastLen    = 5;     # Fast ATR length
input atrSlowLen    = 20;    # Slow ATR length
input atrRatioThresh= 2.0;   # Ratio threshold (fast/slow)
input gapThreshPct  = 7.0;   # Gap % threshold
input useLiveGap    = yes;   # Use live gap or open gap
input AlertOn       = yes;   # Enable alerts
input smoothLen     = 3;     # Smoothing length for Heat line

# === Basic Definitions ===
def c = close;
def h = high;
def o = open;
def l = low;
def v = volume;

# === Daily References for Gap ===
def pcloseD = close(period = aggregationPeriod.DAY)[1];
def oD      = open(period  = aggregationPeriod.DAY);

# === Gap % Calculation ===
def lgpct = if pcloseD != 0 then 100 * (c - pcloseD) / pcloseD else 0;  # Live gap
def ogpct = if pcloseD != 0 then 100 * (oD - pcloseD) / pcloseD else 0; # Open gap
def gpct  = if useLiveGap then lgpct else ogpct;

# === Relative Volume (RVOL) ===
def avgv  = Average(v, rvolLen);
def rvol  = if avgv > 0 then v / avgv else 0;
def cRVOL = rvol >= rvolThresh;

# === ATR Expansion ===
def tr    = TrueRange(h, c, l);
def atrf  = MovingAverage(AverageType.WILDERS, tr, atrFastLen);
def atrs  = MovingAverage(AverageType.WILDERS, tr, atrSlowLen);
def atrx  = if atrs > 0 then atrf / atrs else 0;
def cATR  = atrx >= atrRatioThresh;

# === Composite Score (-3 to +3) ===
def gapScore =
    if gpct >=  gapThreshPct then  1
    else if gpct <= -gapThreshPct then -1
    else 0;

def rvolScore = if cRVOL then 1 else 0;
def atrScore  = if cATR  then 1 else 0;

def score = gapScore + rvolScore + atrScore;

# === Smoothed Heat Line ===
def smoothHeat = Average(score, smoothLen);

plot HeatLine = smoothHeat;
HeatLine.SetDefaultColor(Color.CYAN);
HeatLine.SetLineWeight(3);

plot ZeroLine = 0;
ZeroLine.SetDefaultColor(Color.WHITE);
ZeroLine.SetLineWeight(2);

# === Clouds (Bullish & Bearish) ===
AddCloud(1,   0, Color.GRAY);
AddCloud(2,   1, Color.GREEN);
AddCloud(3,   2, Color.DARK_GREEN);

AddCloud(0,  -1, Color.GRAY);
AddCloud(-1, -2, Color.RED);
AddCloud(-2, -3, Color.DARK_RED);

# === Vertical Signals ===
def crossBull = smoothHeat[1] < 2 and smoothHeat >= 2;
AddVerticalLine(crossBull, "BUY", Color.GREEN);

def crossBear = smoothHeat[1] > -2 and smoothHeat <= -2;
AddVerticalLine(crossBear, "SELL", Color.RED);

# === Labels ===
AddLabel(yes,
    "Heat " + Round(smoothHeat, 1) +
    " | Gap " + AsText(Round(gpct, 1)) + "%" +
    " | RVOL " + AsText(Round(rvol, 2)) +
    " | ATRx " + AsText(Round(atrx, 2)),
    if smoothHeat >=  2 then Color.DARK_GREEN
    else if smoothHeat >  0 then Color.GREEN
    else if smoothHeat <= -2 then Color.RED
    else if smoothHeat <  0 then Color.ORANGE
    else Color.GRAY
);

# === Alerts ===
Alert(AlertOn and score >=  3, "Bull Heat 3/3 (GapUp+RVOL+ATR)",  Alert.BAR, Sound.Ding);
Alert(AlertOn and score <= -3, "Bear Heat -3/3 (GapDown+RVOL+ATR)", Alert.BAR, Sound.Bell);
 

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

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