Hull Moving Average Turning Points and Concavity (2nd Derivatives)

T

thebewb

New member
VIP
All - Wanted to post something that I thought may be helpful to give more information and less clutter on the upper chart. It is a script that shows the concavity colorings of different HMAs with different lengths and different aggregations that is customizable. I used OPEN as my price as I like how that won't repaint and it still backtests extremely well. Please let me know how you like the study and how you use it in trading... could be a good confirmation signal, but a lot of information there so easy to get mixed signals.
Code:
#
# Hull Moving Average Concavity and Turning Points
#  or
# The Second Derivative of the Hull Moving Average
#
# Author: Seth Urion (Mahsume)
# Version: 2020-02-23 V3
# Faster, but not necessarily mathematically as good as the first
#
# This code is licensed (as applicable) under the GPL v3
#
# ----------------------
# Update Author: TheBewb
# Created a study based of Mahsume's Hull Concavity study that shows the concavity of different aggregations and length HMAs as a lower study. It is a lot of information and not sure how actionable since you get mixed signals a lot but if the market is trending good confirmation


declare lower;
input aggperiod = AggregationPeriod.FIVE_MIN;
input aggperiod2 = AggregationPeriod.FIFTEEN_MIN;
def price = open(period = aggperiod);
def price2 = open(period = aggperiod2);
input HMA_Length = 21;
input HMA_length2 = 34;
input HMA_length3 = 55;
input HMA_Length4 = 21;
input HMA_length5 = 34;
input HMA_length6 = 55;
input lookback = 2;

def HMA = HullMovingAvg(price = price, length = HMA_Length);
def delta = HMA[1] - HMA[lookback + 1];
def delta_per_bar = delta / lookback;
def next_bar = HMA[1] + delta_per_bar;
def concavity = if HMA > next_bar then 1 else -1;
def turning_point = if concavity[1] != concavity then HMA else Double.NaN;
def preBull = concavity[1] == 1 and HMA < HMA[1];
def Bull = concavity[1] == 1 and HMA > HMA[1];
def preBear = concavity[1] == -1 and HMA > HMA[1];
def Bear = concavity[1] == -1 and HMA < HMA [1];
def MA_Max = if HMA[-1] < HMA and HMA > HMA[1] then HMA else Double.NaN;
def MA_Min = if HMA[-1] > HMA and HMA < HMA[1] then HMA else Double.NaN;



def HMA2 = HullMovingAvg(price = price, length = HMA_length2);
def delta2 = HMA2[1] - HMA2[lookback + 1];
def delta_per_bar2 = delta2 / lookback;
def next_bar2 = HMA2[1] + delta_per_bar2;
def concavity2 = if HMA2 > next_bar2 then 1 else -1;
def turning_point2 = if concavity2[1] != concavity2 then HMA2 else Double.NaN;
def preBull2 = concavity2[1] == 1 and HMA2 < HMA2[1];
def Bull2 = concavity2[1] == 1 and HMA2 > HMA2[1];
def preBear2 = concavity2[1] == -1 and HMA2 > HMA2[1];
def Bear2 = concavity2[1] == -1 and HMA2 < HMA2[1];
def MA_Max2 = if HMA2[-1] < HMA2 and HMA2 > HMA2[1] then HMA2 else Double.NaN;
def MA_Min2 = if HMA2[-1] > HMA2 and HMA2 < HMA2[1] then HMA2 else Double.NaN;
def HMA3 = HullMovingAvg(price = price, length = HMA_length3);
def delta3 = HMA3[1] - HMA3[lookback + 1];
def delta_per_bar3 = delta3 / lookback;
def next_bar3 = HMA3[1] + delta_per_bar3;
def concavity3 = if HMA3 > next_bar3 then 1 else -1;
def turning_point3 = if concavity3[1] != concavity3 then HMA3 else Double.NaN;
def preBull3 = concavity3[1] == 1 and HMA3 < HMA3[1];
def Bull3 = concavity3[1] == 1 and HMA3 > HMA3[1];
def preBear3 = concavity3[1] == -1 and HMA3 > HMA3[1];
def Bear3 = concavity3[1] == -1 and HMA3 < HMA3[1];
def MA_Max3 = if HMA3[-1] < HMA3 and HMA3 > HMA3[1] then HMA3 else Double.NaN;
def MA_Min3 = if HMA3[-1] > HMA3 and HMA3 < HMA3[1] then HMA3 else Double.NaN;
def HMA4 = HullMovingAvg(price = price2, length = HMA_Length4);
def delta4 = HMA4[1] - HMA4[lookback + 1];
def delta_per_bar4 = delta4 / lookback;
def next_bar4 = HMA4[1] + delta_per_bar4;
def concavity4 = if HMA4 > next_bar4 then 1 else -1;
def turning_point4 = if concavity4[1] != concavity4 then HMA4 else Double.NaN;
def preBull4 = concavity4[1] == 1 and HMA4 < HMA4[1];
def Bull4 = concavity4[1] == 1 and HMA4 > HMA4[1];
def preBear4 = concavity4[1] == -1 and HMA4 > HMA4[1];
def Bear4 = concavity4[1] == -1 and HMA4 < HMA4[1];
def MA_Max4 = if HMA4[-1] < HMA4 and HMA4 > HMA4[1] then HMA4 else Double.NaN;
def MA_Min4 = if HMA4[-1] > HMA4 and HMA4 < HMA4[1] then HMA4 else Double.NaN;

def HMA5 = HullMovingAvg(price = price2, length = HMA_length5);
def delta5 = HMA5[1] - HMA5[lookback + 1];
def delta_per_bar5 = delta5 / lookback;
def next_bar5 = HMA5[1] + delta_per_bar5;
def concavity5 = if HMA5 > next_bar5 then 1 else -1;
def turning_point5 = if concavity5[1] != concavity5 then HMA5 else Double.NaN;
def preBull5 = concavity5[1] == 1 and HMA5 < HMA5[1];
def Bull5 = concavity5[1] == 1 and HMA5 > HMA5[1];
def preBear5 = concavity5[1] == -1 and HMA5 > HMA5[1];
def Bear5 = concavity5[1] == -1 and HMA5 < HMA5[1];
def MA_Max5 = if HMA5[-1] < HMA5 and HMA5 > HMA5[1] then HMA5 else Double.NaN;
def MA_Min5 = if HMA5[-1] > HMA5 and HMA5 < HMA5[1] then HMA5 else Double.NaN;

def HMA6 = HullMovingAvg(price = price2, length = HMA_length6);
def delta6 = HMA6[1] - HMA6[lookback + 1];
def delta_per_bar6 = delta6 / lookback;
def next_bar6 = HMA6[1] + delta_per_bar6;
def concavity6 = if HMA6 > next_bar6 then 1 else -1;
def turning_point6 = if concavity6[1] != concavity6 then HMA6 else Double.NaN;
def preBull6 = concavity6[1] == 1 and HMA6 < HMA6[1];
def Bull6 = concavity6[1] == 1 and HMA6 > HMA6[1];
def preBear6 = concavity6[1] == -1 and HMA6 > HMA6[1];
def Bear6 = concavity6[1] == -1 and HMA6 < HMA6[1];
def MA_Max6 = if HMA6[-1] < HMA6 and HMA6 > HMA6[1] then HMA6 else Double.NaN;
def MA_Min6 = if HMA6[-1] > HMA6 and HMA6 < HMA6[1] then HMA6 else Double.NaN;

plot Trend1 = If(IsNaN(Price[0]), Double.NaN, 6);
Trend1.AssignValueColor(if bull then color.green else if bear then color.red else if prebull then color.dark_green else if prebear then color.DARK_ORANGE else color.magenta);
Trend1.SetPaintingStrategy(PaintingStrategy.POINTS);
Trend1.SetLineWeight(3);

plot Trend2 = If(IsNaN(Price[0]), Double.NaN, 5);
Trend2.AssignValueColor(if bull2 then color.green else if bear2 then color.red else if prebull2 then color.dark_green else if prebear2 then color.DARK_ORANGE else color.magenta);
Trend2.SetPaintingStrategy(PaintingStrategy.POINTS);
Trend2.SetLineWeight(3);

plot Trend3 = If(IsNaN(Price[0]), Double.NaN, 4);
Trend3.AssignValueColor(if bull3 then color.green else if bear3 then color.red else if prebull3 then color.dark_green else if prebear3 then color.DARK_ORANGE else color.magenta);
Trend3.SetPaintingStrategy(PaintingStrategy.POINTS);
Trend3.SetLineWeight(3);

plot Trend4 = If(IsNaN(Price[0]), Double.NaN, 3);
Trend4.AssignValueColor(if bull4 then color.green else if bear4 then color.red else if prebull4 then color.dark_green else if prebear4 then color.DARK_ORANGE else color.magenta);
Trend4.SetPaintingStrategy(PaintingStrategy.POINTS);
Trend4.SetLineWeight(3);

plot Trend5 = If(IsNaN(Price[0]), Double.NaN, 2);
Trend5.AssignValueColor(if bull5 then color.green else if bear5 then color.red else if prebull5 then color.dark_green else if prebear5 then color.DARK_ORANGE else color.magenta);
Trend5.SetPaintingStrategy(PaintingStrategy.POINTS);
Trend5.SetLineWeight(3);

plot Trend6 = If(IsNaN(Price[0]), Double.NaN, 1);
Trend6.AssignValueColor(if bull6 then color.green else if bear6 then color.red else if prebull6 then color.dark_green else if prebear6 then color.DARK_ORANGE else color.magenta);
Trend6.SetPaintingStrategy(PaintingStrategy.POINTS);
Trend6.SetLineWeight(3);
 
G

Ginu09

Member
@thebewb Beauty. I interchanged the trends to get the agg1/agg2 of each moving average together, it helps. Thanks!
 
C

Cleartarget

New member
VIP
No - I use the TMO for confirmation of buy / sell zones
Hi can you share the TMO scanner that you use for signals or the hull scanner ,i have been little confused with this scanners.
Thanks
 
T

thebewb

New member
VIP
@mashume I took a stab at the math and "laborious" is putting it mildly. I tried to do a quick and dirty estimate of the price needed to turn concavity by taking the change in price used to calculate the HULL MA over the corresponding change in difference between actual & expected. Its close but running into the issue of differences in sign e.g., price goes up a minimal amount and Hull still decreases... Its definitely a fun exercise trying to figure out a lazy way to predict it.
 
M

mashume

Active member
VIP
@mashume I took a stab at the math and "laborious" is putting it mildly. I tried to do a quick and dirty estimate of the price needed to turn concavity by taking the change in price used to calculate the HULL MA over the corresponding change in difference between actual & expected. Its close but running into the issue of differences in sign e.g., price goes up a minimal amount and Hull still decreases... Its definitely a fun exercise trying to figure out a lazy way to predict it.
I started writing a python notebook to do the math using sympy for symbolic equation constructon and a few (many) loops to write the equations. Got about halfway before deciding I had better things to do. Perhaps some day, when I feel really bored I may drag it back out... but there are many projects before that.

It's and interesting exercise though, and certainly sheds light on the complexities of the mathematics we use every day. Glad you had a go of it.

-mashume
 
MerryDay

MerryDay

Member
VIP
Hi can you share the TMO scanner that you use for signals or the hull scanner ,i have been little confused with this scanners.
Thanks
@Cleartarget
A search of this site returned 58 results for the TMO scanner. I use the TMO scanner that you will find in the 1st result when you do your search.
In the post is titled: TMO with Higher Agg_Mobius, the scanner that I use is in post#23.

You will need to be able to do your own research and analysis and testing of the various tools on this site and on the internet to succeed at trading.
 
G

greentonic

New member
@mashume beautiful system you built here.
Is it possible to add VWAP as a filter for signals ?
If Hull is above vwap show Long entry signals only and if Hull is below vwap show Short entry signals only ?
 
Xiuying

Xiuying

New member
Love the indicator, I keep it on all of my charts.

I was wondering if you've had any comments/observations about the turning points and how well they work in being Support/Resistance levels.

I've been using it on the 15m/30m charts and have noticed how often they have saved me from making a huge mistake or have given me great opportunities to enter positions. Below when the Early Dark green showed it's head and ended up bouncing off one of the previous turning points, I was able to re-enter into some Amazon puts and ride them down quite a bit.

 
A

astro_phx

New member
@mashume Is it possible to get alert when MA_Max and MA_Min hits? also if possible can we get strategy buy-side MA_Min to MA_max and sell-side MA_Max to MA_min? Thank you in advance.
 
M

mashume

Active member
VIP
@mashume Is it possible to get alert when MA_Max and MA_Min hits? also if possible can we get strategy buy-side MA_Min to MA_max and sell-side MA_Max to MA_min? Thank you in advance.
perhaps... I have taken a bit of time off of late. I'll look at the code in the next couple of days

-mashume
 
A

astro_phx

New member
can some one share this indicator with the Alert code on it. plus the scanner please
I have code with the alert but not the scanner.

Code:
declare upper;

input price = HL2;
input HMA_Length = 55;
input lookback = 2;

# I read somewhere that it's faster to define nan's and then use the def'd var rather than call double.nan every time.
def nan = double.nan;

plot HMA = HullMovingAvg(price = price, length = HMA_Length);

def delta = HMA[1] - HMA[lookback + 5];
def delta_per_bar = delta / lookback;

def next_bar = HMA[1] + delta_per_bar;

def concavity = if HMA > next_bar then 1 else -1;

plot turning_point = if concavity[1] != concavity then HMA else nan;

HMA.AssignValueColor(color = if concavity[1] == -1 then
    if HMA > HMA[1] then color.dark_orange else color.red else
    if HMA < HMA[1] then color.dark_green else color.green);

HMA.SetLineWeight(3);

turning_point.SetLineWeight(4);
turning_point.SetPaintingStrategy(paintingStrategy = PaintingStrategy.POINTS);
turning_point.SetDefaultColor(color.white);

plot MA_Max = if HMA[-1] < HMA and HMA > HMA[1] then HMA else NaN;
MA_Max.SetDefaultColor(Color.WHITE);
MA_Max.SetPaintingStrategy(PaintingStrategy.SQUARES);
MA_Max.SetLineWeight(3);

plot MA_Min = if HMA[-1] > HMA and HMA < HMA[1] then HMA else Nan;
MA_Min.SetDefaultColor(Color.WHITE);
MA_Min.SetPaintingStrategy(PaintingStrategy.TRIANGLES);
MA_Min.SetLineWeight(3);

plot sell = if turning_point and concavity == -1 then high else nan;
sell.SetDefaultColor(Color.DARK_ORANGE);
sell.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
sell.SetLineWeight(3);

plot buy = if turning_point and concavity == 1 then low else nan;
buy.SetDefaultColor(Color.CYAN);
buy.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
buy.SetLineWeight(3);

def divergence = HMA - next_bar;

addLabel(no, concat("DIVERGENCE: " , divergence * 10000), color = if concavity < 0 then if divergence[1] > divergence then Color.RED else color.PINK else if divergence[1] < divergence then color.green else color.yellow);

###################
#
# ALERTS
#
###################

Alert(condition = buy, text = "Buy", "alert type" = Alert.BAR, sound = Sound.Bell);

Alert(condition = sell, text = "Sell", "alert type" = Alert.BAR, sound = Sound.Chimes);

###################
#
# 2020-05-01
#
# MOBILE TOS SUPPORT
#
# Each color of the HMA needs to be a separate plot as ToS Mobile
# lacks the ability to assign colors the way ToS Desktop does.
# I recommend a plain colored HMA behind the line
# Set the line color of the HMA above to gray or some neutral
#
# CCD_D -> ConCave Down and Decreasing
# CCD_I -> ConCave Down and Increasing
# CCU_D -> ConCave Up and Decreasing
# CCU_I -> ConCave Up and Increasing
#
###################
plot CCD_D = if concavity == -1 and HMA < HMA[1] then HMA else nan;
CCD_D.SetDefaultColor(Color.RED);
CCD_D.SetLineWeight(3);

plot CCD_I = if concavity == -1 and HMA >= HMA[1] then HMA else nan;
CCD_I.SetDefaultColor(Color.DARK_ORANGE);
CCD_I.SetLineWeight(3);

plot CCU_D = if concavity == 1 and HMA <= HMA[1] then HMA else nan;
CCU_D.SetDefaultColor(COLOR.DARK_GREEN);
CCU_D.SetLineWeight(3);

plot CCU_I = if concavity == 1 and HMA > HMA[1] then HMA else nan;
CCU_I.SetDefaultColor(COLOR.GREEN);
CCU_I.SetLineWeight(3);
 
M

mashume

Active member
VIP
Thank you very much mashume. It would be a great help!!!
If you feel like hacking code, replace the ALERTS section in the code with this block:
Code:
###################
#
# ALERTS
#
###################

Alert(condition = buy, text = "Buy", "alert type" = Alert.BAR, sound = Sound.Bell);

Alert(condition = sell, text = "Sell", "alert type" = Alert.BAR, sound = Sound.Chimes);

Alert(condition = MA_Max, text = "MA Maximum", "alert type" = Alert.BAR, sound = Sound.Ding);

Alert(condition = MA_Min, text = "MA Minumum", "alert type" = Alert.BAR, sound = Sound.Ding);
happy trading
-mashume
 
A

astro_phx

New member
If you feel like hacking code, replace the ALERTS section in the code with this block:
Code:
###################
#
# ALERTS
#
###################

Alert(condition = buy, text = "Buy", "alert type" = Alert.BAR, sound = Sound.Bell);

Alert(condition = sell, text = "Sell", "alert type" = Alert.BAR, sound = Sound.Chimes);

Alert(condition = MA_Max, text = "MA Maximum", "alert type" = Alert.BAR, sound = Sound.Ding);

Alert(condition = MA_Min, text = "MA Minumum", "alert type" = Alert.BAR, sound = Sound.Ding);
happy trading
-mashume

Thank you mashume. Is there any chance that you can put this in strategy so we can test out how it works in the backtest. I am sorry if I am asking too much. always appreciate your help. Thank you again.
 
M

mashume

Active member
VIP
Thank you mashume. Is there any chance that you can put this in strategy so we can test out how it works in the backtest. I am sorry if I am asking too much. always appreciate your help. Thank you again.
@astro_phx :
I could, and you may easily. However, remember that the original entry end exit points for this indicator are the turning points, not the local maxima and minima. Using local extrema as entry and exit signals would be a completely different strategy. I can't say that's not a good one, or one you or anyone else might find profitable, but the turning points was the original intent.

Documentation for adding buy and sell orders may be found here:
https://tlc.thinkorswim.com/center/reference/thinkScript/Functions/Others/AddOrder
and you can use the MA_MAX and MA_MIN as the condition.

so your order might look like this:
Code:
AddOrder(OrderType.BUY_AUTO, MA_MIN, open[-1], 50);
AddOrder(OrderType.SELL_AUTO, MA_MAX, open[-1], 50);
Of course, there is much more you can do with the orders, and be careful where you set the buy/sell price as it can affect your P/L dramatically. This indicator repaints, so for a true test, you might want to test at open[-2]. Up to you and how you trade it. Make it a realistic emulation/simulation of your implementation of the strategy or it won't tell you very much about how it would perform for you.

Good luck, and have fun.

-mashume
 
A

astro_phx

New member
@astro_phx :
I could, and you may easily. However, remember that the original entry end exit points for this indicator are the turning points, not the local maxima and minima. Using local extrema as entry and exit signals would be a completely different strategy. I can't say that's not a good one, or one you or anyone else might find profitable, but the turning points was the original intent.

Documentation for adding buy and sell orders may be found here:
https://tlc.thinkorswim.com/center/reference/thinkScript/Functions/Others/AddOrder
and you can use the MA_MAX and MA_MIN as the condition.

so your order might look like this:
Code:
AddOrder(OrderType.BUY_AUTO, MA_MIN, open[-1], 50);
AddOrder(OrderType.SELL_AUTO, MA_MAX, open[-1], 50);
Of course, there is much more you can do with the orders, and be careful where you set the buy/sell price as it can affect your P/L dramatically. This indicator repaints, so for a true test, you might want to test at open[-2]. Up to you and how you trade it. Make it a realistic emulation/simulation of your implementation of the strategy or it won't tell you very much about how it would perform for you.

Good luck, and have fun.

-mashume
@mashume Thank you again. I will try it out and watch how it does on Monday.
 
D

dm10

New member
VIP
@mashume How do the negative array indexes work in your post above? Does open[-1], open[-2], and HMA[-1] in the original script refer to values in the future or is it the first tick in the bar?
 
M

mashume

Active member
VIP
@mashume How do the negative array indexes work in your post above? Does open[-1], open[-2], and HMA[-1] in the original script refer to values in the future or is it the first tick in the bar?
the bracket indicates the bar relative to current you wish to get a value for. open[0] is the same as open. open[1] is the open of the immediately proceeding bar. open[-1] is the open of the next bar. I if open[-2] would work or not, and I don't have ToS open to check right now. alternatively, you could use the buy condition as MA_MIN[1] paired with OPEN[-1] to the same effect.

-mashume
 

Similar threads

Top