Momentum Keltner Channels for ThinkorSwim

Pensar

Expert
VIP
Lifetime
I made this indicator some time ago based on the FW_MOBO code. After doing a few changes recently, I thought I might share it here. :) It is similar to Raghee Horner's GRaB indicator, but uses keltner channels instead of offset 34 EMAs. I feel it is best for showing trends in the market.

Code:
# MomentumKeltnerChannels
# Pensar
# 06/06/2020
# Based on the FW_MOBO code by TOS user davidinc (aka Firstwave, aka David Elliott)
# Modified code to use Keltner Channels, changed code structure.
# Added colored price and user-adjustable global colors.

#Inputs
input length       =     34;
input factor       =     0.5;
input displace     =     0;
input price        =     close;
input type         =     AverageType.SIMPLE;
input pricecolor   =     yes;
input fill         =     yes;
input arrows       =     yes;
input alerts       =     yes;
input sound        =     {default "Ding", "Bell", "Chimes", "NoSound", "Ring"};

#Variables
def nan      =   double.nan;
def shift    =   factor * MovingAverage(type, TrueRange(high, close, low), length);
def avg      =   MovingAverage(type, price, length);
def line1    =   avg[-displace] - shift[-displace];
def line2    =   avg[-displace] + shift[-displace];
def Chg      =   if(close > line2, 1, if(close < line1, -1, 0));
def Hold     =   CompoundValue(1,if(Hold[1] == Chg or Chg == 0, Hold[1], if(Chg == 1, 1, -1)), 0);
def ArUp     =   if !arrows or Hold[0] == Hold[1] then nan else if Hold[0] == 1 then line1 else nan;
def ArDn     =   if !arrows or Hold[0] == Hold[1] then nan else if Hold[0] == -1 then line2 else nan;
def LBUp     =   if fill and Hold[0] == 1 then line2 else nan;
def UBUp     =   if fill and Hold[0] == 1 then line1 else nan;
def LBDn     =   if fill and Hold[0] == -1 then line2 else nan;
def UBDn     =   if fill and Hold[0] == -1 then line1 else nan;
def AlertUp  =   alerts and Hold[1] ==  1 and (Hold[1] <> Hold[2]);
def AlertDn  =   alerts and Hold[1] == -1 and (Hold[1] <> Hold[2]);

#Colors
DefineGlobalColor("Cloud Up", color.dark_green);
DefineGlobalColor("Cloud Dn", color.dark_red);
DefineGlobalColor("Channel Up", color.green);
DefineGlobalColor("Channel Down", color.red);
DefineGlobalColor("Price Up", color.green);
DefineGlobalColor("Price Neutral", color.gray);
DefineGlobalColor("Price Down", color.red);

#Plots
plot UB = line1;
     UB.SetLineWeight(1);
     UB.AssignValueColor(if Hold[0] == 1 then GlobalColor("Channel Up")
                         else GlobalColor("Channel Down"));
plot LB = line2;
     LB.SetLineWeight(1);
     LB.AssignValueColor(if Hold[0] == 1 then GlobalColor("Channel Up")
                         else GlobalColor("Channel Down"));
plot BOA = ArUp;
     BOA.SetPaintingStrategy(PaintingStrategy.Arrow_Up);
     BOA.SetDefaultColor(color.green);
     BOA.SetLineWeight(2);
plot BDA = ArDn;
     BDA.SetPaintingStrategy(PaintingStrategy.Arrow_Down);
     BDA.SetDefaultColor(color.red);
     BDA.SetLineWeight(2);

#Clouds
AddCloud(LBUp, UBUp, GlobalColor("Cloud Up"), GlobalColor("Cloud Dn"));
AddCloud(LBDn, UBDn, GlobalColor("Cloud Dn"), GlobalColor("Cloud Up"));

#Price Color
AssignPriceColor(if pricecolor then if close > line2 then GlobalColor("Price Up")
                 else if close < line1 then GlobalColor("Price Down")
                 else GlobalColor("Price Neutral")
                 else color.current);

#Alerts
Alert(AlertUp, "BREAKOUT!",  Alert.Bar, Sound);
Alert(AlertDn, "BREAKDOWN!", Alert.Bar, Sound);

# --- End code ---

fqIN3pb.png
 
@Pensar, thanks for this indicator, it comes in handy. I have made a scanner out of it as seen below. Can you please help fine tune it so that it can have a lookback period of 1 or max 2 candles?

Code:
# MomentumKeltnerChannels
# Pensar
# 06/06/2020
# Based on the FW_MOBO code by TOS user davidinc (aka Firstwave, aka David Elliott)
# Modified code to use Keltner Channels, changed code structure.
# Added colored price and user-adjustable global colors.

#Inputs
input length       =     34;
input factor       =     0.5;
input displace     =     0;
input price        =     close;
input type         =     AverageType.SIMPLE;
input pricecolor   =     yes;
input fill         =     yes;
input arrows       =     yes;
input alerts       =     yes;


#Variables
def nan      =   double.nan;
def shift    =   factor * MovingAverage(type, TrueRange(high, close, low), length);
def avg      =   MovingAverage(type, price, length);
def line1    =   avg[-displace] - shift[-displace];
def line2    =   avg[-displace] + shift[-displace];
def Chg      =   if(close > line2, 1, if(close < line1, -1, 0));
def Hold     =   CompoundValue(1,if(Hold[1] == Chg or Chg == 0, Hold[1], if(Chg == 1, 1, -1)), 0);
def ArUp     =   if !arrows or Hold[0] == Hold[1] then nan else if Hold[0] == 1 then line1 else nan;
def ArDn     =   if !arrows or Hold[0] == Hold[1] then nan else if Hold[0] == -1 then line2 else nan;
def LBUp     =   if fill and Hold[0] == 1 then line2 else nan;
def UBUp     =   if fill and Hold[0] == 1 then line1 else nan;
def LBDn     =   if fill and Hold[0] == -1 then line2 else nan;
def UBDn     =   if fill and Hold[0] == -1 then line1 else nan;
def AlertUp  =   alerts and Hold[1] ==  1 and (Hold[1] <> Hold[2]);
def AlertDn  =   alerts and Hold[1] == -1 and (Hold[1] <> Hold[2]);

#plot Buy = AlertUp;
plot Sell = AlertDn;
 
@camerdr Check if this provides the results you wish -

Scanner Code -
Code:
# Momentum Keltner Channels Scan
# Based on the FW_MOBO code by TOS user davidinc (aka Firstwave, aka David Elliott)

input length       =     34;
input factor       =     0.5;
input displace     =     0;
input price        =     close;
input type         =     AverageType.SIMPLE;

def nan      =   double.nan;
def shift    =   factor * MovingAverage(type, TrueRange(high, close, low), length);
def avg      =   MovingAverage(type, price, length);
def line1    =   avg[-displace] - shift[-displace];
def line2    =   avg[-displace] + shift[-displace];
def Chg      =   if(close > line2, 1, if(close < line1, -1, 0));
def Hold     =   CompoundValue(1,if(Hold[1] == Chg or Chg == 0, Hold[1], if(Chg == 1, 1, -1)), 0);

def n1 = Hold[0] == 1;
def n2 = Hold[0] == -1;
def countup = if n1 and !n1[1] then 1 else countup[1]+1;
def countdn = if n2 and !n2[1] then 1 else countdn[1]+1;

plot scan_for_break_up = if n1 then if(countup < 3, 1, 0) else 0;
#plot scan_for_break_down = if n2 then if(countdn < 3, 1, 0) else 0;

#End of code

Also, here is a watchlist column for anyone interested. Figured might as well make one. :)

Watchlist Code -
Code:
# Momentum Keltner Channels Watchlist Column
# Based on the FW_MOBO code by TOS user davidinc (aka Firstwave, aka David Elliott)
# The count displayed in the watchlist shows the number of bars since a break up or break down

input length       =     34;
input factor       =     0.5;
input displace     =     0;
input price        =     close;
input type         =     AverageType.SIMPLE;

def nan      =   double.nan;
def shift    =   factor * MovingAverage(type, TrueRange(high, close, low), length);
def avg      =   MovingAverage(type, price, length);
def line1    =   avg[-displace] - shift[-displace];
def line2    =   avg[-displace] + shift[-displace];
def Chg      =   if(close > line2, 1, if(close < line1, -1, 0));
def Hold     =   CompoundValue(1,if(Hold[1] == Chg or Chg == 0, Hold[1], if(Chg == 1, 1, -1)), 0);

def n1 = Hold[0] == 1;
def n2 = Hold[0] == -1;
def count1 = if n1 and !n1[1] then 1 else count1[1]+1;
def count2 = if n2 and !n2[1] then 1 else count2[1]+1;

plot n = if n1 then count1 else if n2 then count2 else double.nan;
     n.setdefaultcolor(color.black);
assignbackgroundcolor(if n1 and count1 then color.green else color.red);
#End of code
 
Last edited:
@Pensar, regarding the values in the watch list, is there any significance to the numerical values displayed in the watch list column? Meaning that anything over a certain value would equate to a better trade etc? Higher numbers vs lower numbers equate to better trades?
 
@dinodotcom The only significance of the numbers in the watchlist column is to count how many bars since a breakout/breakdown.

It can help in knowing which instruments have been running for a while vs those that are just breaking out/down.
 
Wow, it looks great. For the watchlist, can you please explain what the number mean, what does it represent?
Thanks Pensar!
 
@Pensar great indicator, looks great. Do you mind explaining what time frame is best used for this? Also how does one follow a trend based on this? As long as its above green channel, means trending upward correct? Thanks in advance!
 
@Pensar I'm very new to all this scripting and the settings for TOS. So I added the above script for the study and it displays correctly on the chart. I'm trying to add the scan script and watchlist scripts. What do I enter as the conditions? Like crosses, crosses above and more and within ? bars, etc? Are those values the same for both the scan and the watchlist's custom conditions? Thank you.
 
@camerdr @Pensar

Thanks alot for the great script, wrt to scanner & watch list having few Queries

Watch list custom column -> what settings to use like "true' or false" or value?
scanner - similar what exactly to use? if possible can you share it as export link for both?

After use Value i see the number of days in watch list column, hope its the right way i'm using correct me. Thanks in advance
 
Last edited:
@JGilde @raghavag2004 The scan code, as currently written, finds breakouts or breakdowns less than three bars ago (depending on which side you are scanning for). The watchlist shows the count of the number of bars since a breakout or breakdown. No "crosses" or "crosses below" or "true/false settings" should be needed, just copy the code and paste into the TOS scan/watchlist code editor.
 
This is a great indicator, especially when you use it with a relative volume column on you watchlist/scanner. I was using a supertrend for the longest time until I came across this, this is quicker and more reliable
 
@Pensar Is there a way to make a label for this with anytime frame?
@K_O_Trader Try out this label -

Code:
input length       =     34;
input factor       =     0.5;
input displace     =     0;
input agg          =     aggregationperiod.FIVE_MIN;
input type         =     AverageType.SIMPLE;
input label_text   =     "5m";

def h        =   high(period = agg);
def c        =   close(period = agg);
def l        =   low(period = agg);
def shift    =   factor * MovingAverage(type, TrueRange(h,c,l), length);
def avg      =   MovingAverage(type, c, length);
def line1    =   avg[-displace] - shift[-displace];
def line2    =   avg[-displace] + shift[-displace];
def Chg      =   if(c > line2, 1, if(c < line1, -1, 0));
def Hold     =   CompoundValue(1,if(Hold[1] == Chg or Chg == 0, Hold[1], if(Chg == 1, 1, -1)), 0);

def n1 = Hold[0] == 1;
def n2 = Hold[0] == -1;
def count1 = if n1 and !n1[1] then 1 else count1[1]+1;
def count2 = if n2 and !n2[1] then 1 else count2[1]+1;

AddLabel(1,label_text + " - " + if n1 then count1 else count2,
     if n1 then color.green
     else color.red);

#End of code
 
Last edited:
@K_O_Trader Try out this label -

Code:
input length       =     34;
input factor       =     0.5;
input displace     =     0;
input agg          =     aggregationperiod.FIVE_MIN;
input type         =     AverageType.SIMPLE;
input label_text   =     "5m";

def h        =   high(period = agg);
def c        =   close(period = agg);
def l        =   low(period = agg);
def shift    =   factor * MovingAverage(type, TrueRange(h,c,l), length);
def avg      =   MovingAverage(type, c, length);
def line1    =   avg[-displace] - shift[-displace];
def line2    =   avg[-displace] + shift[-displace];
def Chg      =   if(c > line2, 1, if(c < line1, -1, 0));
def Hold     =   CompoundValue(1,if(Hold[1] == Chg or Chg == 0, Hold[1], if(Chg == 1, 1, -1)), 0);

AddLabel(1,label_text,
     if Hold == 1 then color.green
     else color.red);
That works great, is it possible to get the label with the number in it too?
 

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
333 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