Slope That Isn't Slope

FreefallJM03

Active member
VIP
Is there a way to make a label with the degree of slope for moving averages? I need one for the 9 EMA and the 20 SMA. I can make a label that shows it rising or falling where it changes to red for falling and green for rising, but can't get the degree of slope.
 
Solution
Why Chart Slope Lies

Geometric slopes only work if both axes use the same scale.
Stock charts the units are different. Thus slope is an optical illusion.

The above scripts aren't measuring a real physical slope. It's taking a standard Rate of Change, running it through a math formula to squish it between -90 and +90, and slapping the word 'degrees' on the label.

Traders use momentum or true range if they want to analyze historical trajectory.
Is there a way to make a label with the degree of slope for moving averages? I need one for the 9 EMA and the 20 SMA. I can make a label that shows it rising or falling where it changes to red for falling and green for rising, but can't get the degree of slope.
Try this ,I didn't test it , but it should be ok , if not just let me know and I will fix it.

Code:
# 9 EMA + 20 SMA Slope Degree Labels
# Designed for an upper chart

declare upper;

input price = close;
input slopeLookbackBars = 3;
input useClosedBarOnly = yes;
input angleMode = {default RAW_POINTS_PER_BAR, ATR_NORMALIZED};
input atrLength = 14;
input flatThresholdDegrees = 1.0;
input showMovingAverageLines = no;
input showLabels = yes;

#-------------------------------------------------
# Moving averages
#-------------------------------------------------
def ema9Value = ExpAverage(price, 9);
def sma20Value = Average(price, 20);

# Closed-bar mode prevents the displayed angle from
# changing during the active candle.
def ema9SlopeValue =
if useClosedBarOnly then ema9Value[1]
else ema9Value;

def sma20SlopeValue =
if useClosedBarOnly then sma20Value[1]
else sma20Value;

# Average movement per bar
def ema9PointsPerBar =
(ema9SlopeValue -
ema9SlopeValue[slopeLookbackBars]) /
slopeLookbackBars;

def sma20PointsPerBar =
(sma20SlopeValue -
sma20SlopeValue[slopeLookbackBars]) /
slopeLookbackBars;

#-------------------------------------------------
# Optional ATR normalization
#-------------------------------------------------
def atrValue = MovingAverage(
AverageType.WILDERS,
TrueRange(high, close, low),
atrLength
);

def selectedATR =
if useClosedBarOnly then atrValue[1]
else atrValue;

def safeATR =
if selectedATR > 0 then selectedATR
else 1.0;

def ema9SlopeRatio =
if angleMode == angleMode.ATR_NORMALIZED
then ema9PointsPerBar / safeATR
else ema9PointsPerBar;

def sma20SlopeRatio =
if angleMode == angleMode.ATR_NORMALIZED
then sma20PointsPerBar / safeATR
else sma20PointsPerBar;

#-------------------------------------------------
# Convert slope to degrees
#-------------------------------------------------
def ema9Angle =
ATan(ema9SlopeRatio) * 180 / Double.Pi;

def sma20Angle =
ATan(sma20SlopeRatio) * 180 / Double.Pi;

def ema9Direction =
if ema9Angle > flatThresholdDegrees then 1
else if ema9Angle < -flatThresholdDegrees then -1
else 0;

def sma20Direction =
if sma20Angle > flatThresholdDegrees then 1
else if sma20Angle < -flatThresholdDegrees then -1
else 0;

#-------------------------------------------------
# Labels
#-------------------------------------------------
AddLabel(
showLabels,
"9 EMA: " +
(if ema9Direction == 1 then "RISING "
else if ema9Direction == -1 then "FALLING "
else "FLAT ") +
Round(ema9Angle, 1) + " deg",
if ema9Direction == 1 then Color.GREEN
else if ema9Direction == -1 then Color.RED
else Color.GRAY
);

AddLabel(
showLabels,
"20 SMA: " +
(if sma20Direction == 1 then "RISING "
else if sma20Direction == -1 then "FALLING "
else "FLAT ") +
Round(sma20Angle, 1) + " deg",
if sma20Direction == 1 then Color.GREEN
else if sma20Direction == -1 then Color.RED
else Color.GRAY
);

#-------------------------------------------------
# Optional moving-average plots
#-------------------------------------------------
plot EMA9 = ema9Value;
EMA9.SetLineWeight(2);
EMA9.SetHiding(!showMovingAverageLines);
EMA9.AssignValueColor(
if ema9Direction == 1 then Color.GREEN
else if ema9Direction == -1 then Color.RED
else Color.GRAY
);

plot SMA20 = sma20Value;
SMA20.SetLineWeight(2);
SMA20.SetStyle(Curve.SHORT_DASH);
SMA20.SetHiding(!showMovingAverageLines);
SMA20.AssignValueColor(
if sma20Direction == 1 then Color.GREEN
else if sma20Direction == -1 then Color.RED
else Color.GRAY
);
 
Last edited by a moderator:
Try this ,I didn't test it , but it should be ok , if not just let me know and I will fix it.

# 9 EMA + 20 SMA Slope Degree Labels
# Designed for an upper chart

declare upper;

input price = close;
input slopeLookbackBars = 3;
input useClosedBarOnly = yes;
input angleMode = {default RAW_POINTS_PER_BAR, ATR_NORMALIZED};
input atrLength = 14;
input flatThresholdDegrees = 1.0;
input showMovingAverageLines = no;
input showLabels = yes;

#-------------------------------------------------
# Moving averages
#-------------------------------------------------
def ema9Value = ExpAverage(price, 9);
def sma20Value = Average(price, 20);

# Closed-bar mode prevents the displayed angle from
# changing during the active candle.
def ema9SlopeValue =
if useClosedBarOnly then ema9Value[1]
else ema9Value;

def sma20SlopeValue =
if useClosedBarOnly then sma20Value[1]
else sma20Value;

# Average movement per bar
def ema9PointsPerBar =
(ema9SlopeValue -
ema9SlopeValue[slopeLookbackBars]) /
slopeLookbackBars;

def sma20PointsPerBar =
(sma20SlopeValue -
sma20SlopeValue[slopeLookbackBars]) /
slopeLookbackBars;

#-------------------------------------------------
# Optional ATR normalization
#-------------------------------------------------
def atrValue = MovingAverage(
AverageType.WILDERS,
TrueRange(high, close, low),
atrLength
);

def selectedATR =
if useClosedBarOnly then atrValue[1]
else atrValue;

def safeATR =
if selectedATR > 0 then selectedATR
else 1.0;

def ema9SlopeRatio =
if angleMode == angleMode.ATR_NORMALIZED
then ema9PointsPerBar / safeATR
else ema9PointsPerBar;

def sma20SlopeRatio =
if angleMode == angleMode.ATR_NORMALIZED
then sma20PointsPerBar / safeATR
else sma20PointsPerBar;

#-------------------------------------------------
# Convert slope to degrees
#-------------------------------------------------
def ema9Angle =
ATan(ema9SlopeRatio) * 180 / Double.Pi;

def sma20Angle =
ATan(sma20SlopeRatio) * 180 / Double.Pi;

def ema9Direction =
if ema9Angle > flatThresholdDegrees then 1
else if ema9Angle < -flatThresholdDegrees then -1
else 0;

def sma20Direction =
if sma20Angle > flatThresholdDegrees then 1
else if sma20Angle < -flatThresholdDegrees then -1
else 0;

#-------------------------------------------------
# Labels
#-------------------------------------------------
AddLabel(
showLabels,
"9 EMA: " +
(if ema9Direction == 1 then "RISING "
else if ema9Direction == -1 then "FALLING "
else "FLAT ") +
Round(ema9Angle, 1) + " deg",
if ema9Direction == 1 then Color.GREEN
else if ema9Direction == -1 then Color.RED
else Color.GRAY
);

AddLabel(
showLabels,
"20 SMA: " +
(if sma20Direction == 1 then "RISING "
else if sma20Direction == -1 then "FALLING "
else "FLAT ") +
Round(sma20Angle, 1) + " deg",
if sma20Direction == 1 then Color.GREEN
else if sma20Direction == -1 then Color.RED
else Color.GRAY
);

#-------------------------------------------------
# Optional moving-average plots
#-------------------------------------------------
plot EMA9 = ema9Value;
EMA9.SetLineWeight(2);
EMA9.SetHiding(!showMovingAverageLines);
EMA9.AssignValueColor(
if ema9Direction == 1 then Color.GREEN
else if ema9Direction == -1 then Color.RED
else Color.GRAY
);

plot SMA20 = sma20Value;
SMA20.SetLineWeight(2);
SMA20.SetStyle(Curve.SHORT_DASH);
SMA20.SetHiding(!showMovingAverageLines);
SMA20.AssignValueColor(
if sma20Direction == 1 then Color.GREEN
else if sma20Direction == -1 then Color.RED
else Color.GRAY
);
Thanks, can this be done for the 100 and 200 SMA averages?
 
Thanks, can this be done for the 100 and 200 SMA averages?
Try this:-------
Code:
# 100 EMA + 200 SMA Slope and Alignment
# Thinkorswim upper study
# Angles are mathematical, not visual screen angles.

declare upper;

input price = close;

input firstLength = 100;
input firstAverageType = AverageType.EXPONENTIAL;

input secondLength = 200;
input secondAverageType = AverageType.SIMPLE;

input slopeLookbackBars = 3;
input useClosedBarOnly = yes;

input angleMode = {default ATR_NORMALIZED, RAW_POINTS_PER_BAR};
input atrLength = 14;

# Starting threshold only; not a validated trading threshold.
# Set to 0 for strictly rising/falling classification.
input flatThresholdDegrees = 1.0;

input showSlopeLabels = yes;
input showAlignmentLabel = yes;
input showMovingAverageLines = no;

Assert(firstLength >= 1, "First length must be at least 1.");
Assert(secondLength >= 1, "Second length must be at least 1.");
Assert(slopeLookbackBars >= 1, "Slope lookback must be at least 1.");
Assert(atrLength >= 1, "ATR length must be at least 1.");
Assert(flatThresholdDegrees >= 0, "Flat threshold cannot be negative.");

#------------------------------------------
# Moving averages
#------------------------------------------
def firstMA = MovingAverage(
firstAverageType, price, firstLength
);

def secondMA = MovingAverage(
secondAverageType, price, secondLength
);

def firstSelected =
if useClosedBarOnly then firstMA[1]
else firstMA;

def secondSelected =
if useClosedBarOnly then secondMA[1]
else secondMA;

def firstPointsPerBar =
(firstSelected -
firstSelected[slopeLookbackBars]) /
slopeLookbackBars;

def secondPointsPerBar =
(secondSelected -
secondSelected[slopeLookbackBars]) /
slopeLookbackBars;

#------------------------------------------
# ATR normalization
#------------------------------------------
def atrValue = MovingAverage(
AverageType.WILDERS,
TrueRange(high, close, low),
atrLength
);

def selectedATR =
if useClosedBarOnly then atrValue[1]
else atrValue;

def denominator =
if angleMode == angleMode.ATR_NORMALIZED
then (
if selectedATR > 0
then selectedATR
else Double.NaN
)
else 1.0;

#------------------------------------------
# Angles
#------------------------------------------
def firstAngle =
ATan(firstPointsPerBar / denominator) *
180 / Double.Pi;

def secondAngle =
ATan(secondPointsPerBar / denominator) *
180 / Double.Pi;

def ready =
!IsNaN(firstAngle) and
!IsNaN(secondAngle);

def firstDirection =
if firstAngle > flatThresholdDegrees then 1
else if firstAngle < -flatThresholdDegrees then -1
else 0;

def secondDirection =
if secondAngle > flatThresholdDegrees then 1
else if secondAngle < -flatThresholdDegrees then -1
else 0;

# Alignment refers to slopes, not MA crossover/order.
def bullAligned =
firstDirection == 1 and
secondDirection == 1;

def bearAligned =
firstDirection == -1 and
secondDirection == -1;

def bothFlat =
firstDirection == 0 and
secondDirection == 0;

#------------------------------------------
# Slope labels
#------------------------------------------
AddLabel(
showSlopeLabels and ready,
firstLength +
(if firstAverageType == AverageType.EXPONENTIAL then " EMA"
else if firstAverageType == AverageType.SIMPLE then " SMA"
else " MA") +
": " +
(if firstDirection == 1 then "RISING "
else if firstDirection == -1 then "FALLING "
else "FLAT ") +
Round(firstAngle, 1) + " deg",
if firstDirection == 1 then Color.GREEN
else if firstDirection == -1 then Color.RED
else Color.GRAY
);

AddLabel(
showSlopeLabels and ready,
secondLength +
(if secondAverageType == AverageType.EXPONENTIAL then " EMA"
else if secondAverageType == AverageType.SIMPLE then " SMA"
else " MA") +
": " +
(if secondDirection == 1 then "RISING "
else if secondDirection == -1 then "FALLING "
else "FLAT ") +
Round(secondAngle, 1) + " deg",
if secondDirection == 1 then Color.GREEN
else if secondDirection == -1 then Color.RED
else Color.GRAY
);

#------------------------------------------
# Alignment label
#------------------------------------------
AddLabel(
showAlignmentLabel and ready,
"SLOPES: " +
(if bullAligned then "BULL ALIGNED"
else if bearAligned then "BEAR ALIGNED"
else if bothFlat then "FLAT"
else "MIXED") +
(if useClosedBarOnly then " | CLOSED"
else " | LIVE"),
if bullAligned then Color.GREEN
else if bearAligned then Color.RED
else if bothFlat then Color.GRAY
else Color.YELLOW
);

AddLabel(
(showSlopeLabels or showAlignmentLabel) and !ready,
"SLOPES: INSUFFICIENT DATA",
Color.GRAY
);

#------------------------------------------
# Optional MA lines
# These lines display current MA values.
#------------------------------------------
plot FirstAverage = firstMA;
FirstAverage.SetDefaultColor(Color.CYAN);
FirstAverage.SetLineWeight(2);
FirstAverage.SetHiding(!showMovingAverageLines);

plot SecondAverage = secondMA;
SecondAverage.SetDefaultColor(Color.YELLOW);
SecondAverage.SetLineWeight(2);
SecondAverage.SetStyle(Curve.SHORT_DASH);
SecondAverage.SetHiding(!showMovingAverageLines);
 
Last edited by a moderator:
Why Chart Slope Lies

Geometric slopes only work if both axes use the same scale.
Stock charts the units are different. Thus slope is an optical illusion.

The above scripts aren't measuring a real physical slope. It's taking a standard Rate of Change, running it through a math formula to squish it between -90 and +90, and slapping the word 'degrees' on the label.

Traders use momentum or true range if they want to analyze historical trajectory.
 
Last edited:
Solution
Slope is ok as a reference of its own past performance or as a heads up set point (cross over at .2 slope has always been a good reference for action) I find the spread of the two EMA's to be more of a tell than slopes, narrow or braiding EMA's could elude to squeezes and or consolidation; a wide spread could lead to sign of expansion or maturity or exhaustion, and widening spread of the EMA plots could tell if the slope or rate of change is increasing and price action is starting to take off... I would use the spread more than the slope.... for instance, this script below combines slope and spread - yellow above zero line is bullish - thermal denotes expansion or contraction and momentum

https://usethinkscript.com/threads/swing-sharpe-market-type-trending-or-choppy.22240/#post-164432
 
Last edited by a moderator:

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