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

D

diazlaz

Well-known member
2019 Donor
VIP
Hi @Summies88 - can you share the intraday strategy. i would like to see how we can help in the backtesting and optimization.
 
S

Summies88

New member
@diazlaz - Here is the actual code for what I'm using, I believe it is the variation that @greentonic posted (#421) with the ability to change between the different moving averages, paint candles, and the plotting on ToS Mobile. I removed the painting function as I have a different study I use to paint my candles and added my own things to have all the functions in one study.

One thing to note is that this study has a major difference when it comes to this version and Version #4 that @mashume has posted in post #1. The "plot sell" and "plot buy" for some reason have different signals. Version #4 has "plot sell = if turning_point and concavity == -1 then high else double.nan;" while my version has "plot sell = if turning_point and concavity == 1 then high else double.nan;". I'm not sure why this is but when I reverse them to match Version #4 obviously the signals switch and everything ends up as a loss.

You can see in the code where I have tried the 5% take profit and MACD for confirmation. This is my current intraday setup (MA_Length = 21, MACD = 8, 21, 5, and 5% profit). The 5% never seemed to trigger on intraday testing but I have it on anyway just to see it plotted on the chart. EDIT: Today was my first day trading with this setup and I actually decided to use 55 as the MA_Length, not 21.

Also, load this as a strategy that way you can see the buy/sell signals plotted in real-time.

Code:
#
# Multiple Moving Average Concavity and Turning Points
#  or
# The Second Derivative of the A Moving Average
#
# via useThinkScript
# request from chillc15
# Added Arnaud Legoux MA and other Moving Averages
#
# Author: Seth Urion (Mahsume)
# Version: 2020-02-22 V2
#
# This code is licensed (as applicable) under the GPL v3
#
# ----------------------

declare upper;

input Price = HL2;
input MA_Length = 21;
input lookback = 2;

input MovingAverage = {default "HMA", "EMA", "SMA", "WMA", "ALMA"};

script ALMA {
# Attributed to Miket
# https://tos.mx/9mznij
# https://usethinkscript.com/threads/alma-arnaud-legoux-ma-indicator-for-thinkorswim.174/
    input Data = close;
    input Window = 9;
    input Sigma = 6;
    input Offset = 0.85;

    def m = (Offset * (Window - 1));
    def s = Window / Sigma;

    def SumVectorData = fold y = 0 to Window with WS do WS + Exp(-(Sqr(y - m)) / (2 * Sqr(s))) * GetValue(Data, (Window - 1) - y);
    def SumVector = fold z = 0 to Window with CW do CW + Exp(-(Sqr(z - m)) / (2 * Sqr(s)));

    plot ALMA = SumVectorData / SumVector;
}

plot MA;
switch (MovingAverage) {
case EMA:
    MA = MovAvgExponential(price, length = MA_Length);
case SMA:
    MA = SimpleMovingAvg(price, length = MA_Length);
case WMA:
    MA = WMA(price, length = MA_Length);
case ALMA:
    MA = ALMA(Data = price, window = MA_Length);
default:
    MA = HullMovingAvg(price = price, length = MA_Length);
}


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

def next_bar = MA[1] + delta_per_bar;

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

plot turning_point = if concavity[-1] != concavity then MA else Double.NaN;

MA.AssignValueColor(color = if concavity == -1 then
    if MA > MA[1] then Color.DARK_ORANGE else Color.RED else
    if MA < MA[1] then Color.DARK_GREEN else Color.GREEN);

MA.SetLineWeight(2);

turning_point.SetLineWeight(2);
turning_point.SetPaintingStrategy(paintingStrategy = PaintingStrategy.POINTS);
turning_point.SetDefaultColor(Color.WHITE);

plot MA_Max = if MA[-1] < MA and MA > MA[1] then MA else Double.NaN;
MA_Max.SetDefaultColor(Color.BLUE);
MA_Max.SetPaintingStrategy(PaintingStrategy.SQUARES);
MA_Max.SetLineWeight(4);

plot MA_Min = if MA[-1] > MA and MA < MA[1] then MA else Double.NaN;
MA_Min.SetDefaultColor(Color.GRAY);
MA_Min.SetPaintingStrategy(PaintingStrategy.TRIANGLES);
MA_Min.SetLineWeight(4);

plot sell = if turning_point and concavity == 1 then high else Double.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 Double.NaN;
buy.SetDefaultColor(Color.CYAN);
buy.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
buy.SetLineWeight(3);

def divergence = MA - next_bar;

AddLabel(yes, Concat("DIVERGENCE: " , divergence), 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);
AddLabel(yes, "MA Length: " + MA_Length, color = Color.CYAN);

#####################
#
## 5% PROFIT INPUT ##
#
#####################
input percentGainGoal = 5;
def percentChange = 100 * (high - EntryPrice()) / EntryPrice();
def exitGood = percentChange >= percentGainGoal;


#####################
#
## MACD (8, 21, 5, EXPONENTIAL, NO) ##
#
#####################
input MACD_fastLength = 8;
input MACD_slowLength = 21;
input MACD_Length = 5;
def averageType = AverageType.EXPONENTIAL;
def showBreakoutSignals = no;

def Value = MovingAverage(averageType, close, MACD_fastLength) - MovingAverage(averageType, close, MACD_slowLength);
def Avg = MovingAverage(averageType, Value, MACD_Length);


#####################
#
## ORIGINAL ORDERS ##
#
#####################
#AddOrder(OrderType.BUY_TO_OPEN, buy, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy", tradeSize = 1);
#AddOrder(OrderType.SELL_TO_CLOSE, sell, name = "Sell", tradeSize = 1);


##############
#
## 5% ORDER ##
#
##############
#addOrder(OrderType.SELL_TO_CLOSE, exitGood, name = "Sell 5%", tradeSize = 1);


####################
#
## ORIGINAL WITH MACD CONFIRM ORDER ##
#
####################
AddOrder(OrderType.BUY_TO_OPEN, buy and value > avg, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy", tradeSize = 1);
AddOrder(OrderType.SELL_TO_CLOSE, sell, name = "Sell", tradeSize = 1);


####################
#
## ORIGINAL BUY AND MA_MIN WITH MACD CONFIRM ORDER ##
#
####################
#AddOrder(OrderType.BUY_TO_OPEN, buy and value > avg or MA_Min and value > avg, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy", tradeSize = 1);
#AddOrder(OrderType.SELL_TO_CLOSE, sell, name = "Sell", tradeSize = 1);


###################
#
# 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 MA < MA[1] then MA else double.nan;
CCD_D.SetDefaultColor(Color.RED);
CCD_D.SetLineWeight(2);

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

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

plot CCU_I = if concavity == 1 and MA > MA[1] then MA else double.nan;
CCU_I.SetDefaultColor(COLOR.GREEN);
CCU_I.SetLineWeight(2);

ToS Strategy Link:
http://tos.mx/oYrHt3C
 
Last edited:
D

diazlaz

Well-known member
2019 Donor
VIP
@Summies88 nice work.. have you backtest with ALMA? could be a good initial optimization to start.
 
S

Summies88

New member
@Summies88 nice work.. have you backtest with ALMA? could be a good initial optimization to start.
I have not backtested anything other than the 13, 21, 34, 55, 89, & 144 HMAs on the 5D/5m, 5Yr/Daily, and 360Day/1h. I only stumbled across this Friday night and that's all I was able to do over the busy weekend that I had. I want to also look at the major SMA/EMAs (20, 50, 100, 200) and I can look into the ALMA which I have never looked at before.
 
S

Summies88

New member
@Summies88 nice work.. have you backtest with ALMA? could be a good initial optimization to start.
I was able to do some backtesting using the 13, 21, 34, 55, 89, 144, 20, 50, 100, & 200 ALMAs last night along with adding the 20, 50, 100, & 200 HMAs on the 5 Year / Daily chart.

Again, anything below a 65% win rate, I made red because it's not an indicator length that I would use. On the ALMA charts, Blue represents my choice of MA Length while on the HMA chart Blue is MA Lengths that I find interesting, namely the P/L total compared to the other lengths.

5 Year / Daily HMA w/ MACD (8,21,5) confirmation:


5 Year / Daily HMA w/ MACD (8,21,5) confirmation:


5 Year / Daily ALMA:


I found that there are some articles that show that trading the 20 ALMA along with the 5 & 10 EMA help find entries and exits, however I didn’t have the time to coding that setup so I will look into that sometime this week, hopefully.
 
Z

Zoomer

New member
VIP
@Summies88 This looks very good. Do you have a scan for UP & Down arrows and also the Buy & Sell signals? Thanks
 
S

Summies88

New member
@Summies88 This looks very good. Do you have a scan for UP & Down arrows and also the Buy & Sell signals? Thanks
@Zoomer Below is the code that I use for my watchlist scans. This particular one was setup for the 5 min chart. The only difference between my intraday scan and my swing scan is that "5m" or "D" box and obviously I change what the length of my study is to match the length I want to scan for. For my intraday scan, I have one setup for 21 HMA and 55 HMA, and for my swings, I have the 20 HMA as my primary and the 89 & 144 HMA as my alternates right now

I am also looking at seeing how some of the longer-term MA lengths might play out with options. But that will require manual backtesting and that's not always the most reliable. Early assessment on $SPY using the 20 HMA on the Daily chart is that for the highest loss it would have been about a 15-25% for options and for the highest win trade it was about 50-115% profit depending on the options. But way more factors are required to figure what would be the best option chain, strike price, and so on.

"SCAN_Swing_TurningPoint_MACD" is what I named the study that mashume provided and the "MACD" is just the original MACD study in ToS.

Code:
SCAN_Swing_TurningPoint_MACD()."buy" is true within 2 bars and MACD("fast length" = 8, "slow length" = 21, "macd length" = 5)."Value" is greater than MACD("fast length" = 8, "slow length" = 21, "macd length" = 5)."Avg" within 2 bars
 
M

mansor

Member
@Summies88 @mashume

I got 2 question:

I am using this version on 5M 1D & hma lenght is 20 & lookback is 2 & price setting is (h+l)/2. It gives me buy support & sell resistance in bubbles. How can I make this setting better if it's not in your opinion good enough for 5M 1D.

Code:
#
# Hull Moving Average Concavity and Turning Points
#  or
# The Second Derivative of the Hull Moving Average
#
# Author: Seth Urion (Mahsume)
# Version: 2020-05-01 V4
#
# Now with support for ToS Mobile
#
# Faster, but not necessarily mathematically as good as the first
#
# This code is licensed (as applicable) under the GPL v3
#
# ----------------------


declare upper;

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

plot 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;

plot turning_point = if concavity[1] != concavity then HMA else double.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 Double.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 Double.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 double.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 double.nan;
buy.SetDefaultColor(Color.CYAN);
buy.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
buy.SetLineWeight(3);

def divergence = HMA - next_bar;

addLabel(yes, concat("DIVERGENCE: " , divergence), 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 double.nan;
CCD_D.SetDefaultColor(Color.RED);
CCD_D.SetLineWeight(3);

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

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

plot CCU_I = if concavity == 1 and HMA > HMA[1] then HMA else double.nan;
CCU_I.SetDefaultColor(COLOR.GREEN);
CCU_I.SetLineWeight(3);
AddChartBubble(MA_MAX == MA_MAX , MA_MAX , "SELL RESISTANCE" , Color.RED,no);

AddChartBubble(MA_MIN == MA_MIN , MA_MIN , "BUY SUPPORT" , Color.GREEN, yes);


Second question:

I got this setting for 1M1D

The setting is hma lenght 21, lookback 2, price setting is (h+l)/2. How would you set it up for 1M1D? this one gives buy & sell bubbles - but way too many & it confuses me. Any help to cut down the noise of bubbles? Thanks in advance - you guys understand this script better than anyone here.

Code:
# blackFLAG FTS BUY AND SELL CONFIRMATION LABELS FOR SwingArms
# 5-24-2020

# Hull Moving Average Concavity and Turning Points
#  or
# The Second Derivative of the Hull Moving Average
#
# Author: Seth Urion (Mahsume)
# Version: 2020-05-01 V4
#
# Now with support for ToS Mobile
#
# This code is licensed (as applicable) under the GPL v3
# UseThinkScript.com
# https://usethinkscript.com/threads/hull-moving-average-turning-points-and-concavity-2nd-derivatives.1803/page-14#post-23080 
# ----------------------
# ************** Settings Update: 5-26-2020 **************
# INSTRUCTIONS: 
# SETTINGS TO SET UP ALERT NOTIFICATIONS - JAzcarate -
# BUY / SELL CHART ALERTS WITH EMAIL / TEXT
# STUDY NAME: HULL MOVING AVERAGE TURNING POINTS
# 1 MINUTE CHART SETTING 255 period HMA
# 5 MINUTE CHART SETTING 255 period HMA
# 4 Hour CHART SETTING 255 PERIOD HMA
# DAILY CHART SETTING 75 PERIOD HMA ( BEST ESTIMATE )

# SELL FROM RESISTANCE - BUY FROM SUPPORT


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 + 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;

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);

# NOTE: I PREFER TO TURN OFF UP ARROWS WHEN IN DOWN TREND.  IF IN DOWNTRENDING SWINGARM, THE TURN ON ONLY DOWN SELL ARROWS. YOU CAN DO THIS USING THE INPUT SETTINGS SCREEN.

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(yes, concat("DIVERGENCE: " , divergence), 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);


# --------------------------------------
#Edited by: Jose Azcarate
#May 25, 2020
#Reason: Adding Bubble - Labels

addChartBubble(MA_MAX == MA_MAX , MA_MAX , "SELL Confirmed" , Color.YELLOW,no);


AddChartBubble(MA_MIN == MA_MIN , MA_MIN , "BUY Confirmed" , Color.YELLOW, yes);
 
S

Summies88

New member
@mansor Being that I am just the one that added an extra indicator to it to provide a better entrance, that's all that I can really talk about.

For your first question, I noticed that there were a lot of signals when using the indicator by itself. So I looked into adding another simple indicator for using it as a confirmation of entry. Below are the results that I have for a 5M1D backtest of the 20HMA alone next to the 20HMA with MACD confirmation. These are on SPY from May-Oct 2020, the first trade being signaled on 7 May for the 20HMA and on 3 Jun for the 20HMA with MACD.

Over 5 months, 13 trades were made with the original code with a 62% win rate, however, when using the MACD it's a 100% win rate on 3 trades. I understand that a total of 16 trades isn't enough to actually base any data off of, which is why I did my backtesting on a 5Y/Daily chart to see how it faired over the years.



For Question 2, Again, if you are getting too many signals you can either increase your MA length or use another indicator that you like to help gauge your entries and only make them once all of our buy signals match up. It just comes down to figuring out what signals you like best for making your entries and sticking to that. That's why I have my code as a strategy and not a study that way once the "buy" and my MACD confirmation are lined up it will plot a buy signal for me, making my entries dummy-proof.

Don't get me wrong, when you wait for the MACD for confirmation you do miss out on some trades. Below is the $SPY and you can see that the last signal with MACD was on 21 Jul. Even though the Original code gave more buy signals since then, but a couple were just false breakouts which ended in losses because you would have entered the day after the cyan arrow and exited after the orange arrows.

 
M

mailbagman2000

Member
@mansor Being that I am just the one that added an extra indicator to it to provide a better entrance, that's all that I can really talk about.

For your first question, I noticed that there were a lot of signals when using the indicator by itself. So I looked into adding another simple indicator for using it as a confirmation of entry. Below are the results that I have for a 5M1D backtest of the 20HMA alone next to the 20HMA with MACD confirmation. These are on SPY from May-Oct 2020, the first trade being signaled on 7 May for the 20HMA and on 3 Jun for the 20HMA with MACD.

Over 5 months, 13 trades were made with the original code with a 62% win rate, however, when using the MACD it's a 100% win rate on 3 trades. I understand that a total of 16 trades isn't enough to actually base any data off of, which is why I did my backtesting on a 5Y/Daily chart to see how it faired over the years.



For Question 2, Again, if you are getting too many signals you can either increase your MA length or use another indicator that you like to help gauge your entries and only make them once all of our buy signals match up. It just comes down to figuring out what signals you like best for making your entries and sticking to that. That's why I have my code as a strategy and not a study that way once the "buy" and my MACD confirmation are lined up it will plot a buy signal for me, making my entries dummy-proof.

Don't get me wrong, when you wait for the MACD for confirmation you do miss out on some trades. Below is the $SPY and you can see that the last signal with MACD was on 21 Jul. Even though the Original code gave more buy signals since then, but a couple were just false breakouts which ended in losses because you would have entered the day after the cyan arrow and exited after the orange arrows.

Great job could you post a link and share this set-up with us?
 
S

Summies88

New member
Hi there,
How do I remove the buy and sell arrows?
The arrows are the indication of where to buy or sell, not sure why you would want to get rid of those. But if you scroll through the actual code, you will see 4 lines starting with "plot sell" and "plot buy". Just put a # before that and that potion of the code won't be used in the indicator anymore.
 
T

TraderKevin

Member
@mansor Being that I am just the one that added an extra indicator to it to provide a better entrance, that's all that I can really talk about.

For your first question, I noticed that there were a lot of signals when using the indicator by itself. So I looked into adding another simple indicator for using it as a confirmation of entry. Below are the results that I have for a 5M1D backtest of the 20HMA alone next to the 20HMA with MACD confirmation. These are on SPY from May-Oct 2020, the first trade being signaled on 7 May for the 20HMA and on 3 Jun for the 20HMA with MACD.

Over 5 months, 13 trades were made with the original code with a 62% win rate, however, when using the MACD it's a 100% win rate on 3 trades. I understand that a total of 16 trades isn't enough to actually base any data off of, which is why I did my backtesting on a 5Y/Daily chart to see how it faired over the years.



For Question 2, Again, if you are getting too many signals you can either increase your MA length or use another indicator that you like to help gauge your entries and only make them once all of our buy signals match up. It just comes down to figuring out what signals you like best for making your entries and sticking to that. That's why I have my code as a strategy and not a study that way once the "buy" and my MACD confirmation are lined up it will plot a buy signal for me, making my entries dummy-proof.

Don't get me wrong, when you wait for the MACD for confirmation you do miss out on some trades. Below is the $SPY and you can see that the last signal with MACD was on 21 Jul. Even though the Original code gave more buy signals since then, but a couple were just false breakouts which ended in losses because you would have entered the day after the cyan arrow and exited after the orange arrows.

Hate to burst your bubble, but your backtest results are so good because you have it set to buy one bar before is possible in real life. As is, your buy/sell signals will appear/disappear during candle creation depending on price action. Then, when it closes, it will act like you could have bought/sold at the open price. As was discussed previously in this thread, you need to either set the buy/sell orders to include the [1] index, or eliminate the [-1] index from the variables used in the logic. I went ahead and did this for you and the results are still decent, but you'll have to re-do your analysis to tweak it with the new (realistic) delay.

Code:
##
# Multiple Moving Average Concavity and Turning Points
#  or
# The Second Derivative of the A Moving Average
#
# via useThinkScript
# request from chillc15
# Added Arnaud Legoux MA and other Moving Averages
#
# Author: Seth Urion (Mahsume)
# Version: 2020-02-22 V2
#
# This code is licensed (as applicable) under the GPL v3
#
# ----------------------

declare upper;

input Price = HL2;
input MA_Length = 21;
input lookback = 2;

input MovingAverage = {default "HMA", "EMA", "SMA", "WMA", "ALMA"};

script ALMA {
# Attributed to Miket
# https://tos.mx/9mznij
# https://usethinkscript.com/threads/alma-arnaud-legoux-ma-indicator-for-thinkorswim.174/
    input Data = close;
    input Window = 9;
    input Sigma = 6;
    input Offset = 0.85;

    def m = (Offset * (Window - 1));
    def s = Window / Sigma;

    def SumVectorData = fold y = 0 to Window with WS do WS + Exp(-(Sqr(y - m)) / (2 * Sqr(s))) * GetValue(Data, (Window - 1) - y);
    def SumVector = fold z = 0 to Window with CW do CW + Exp(-(Sqr(z - m)) / (2 * Sqr(s)));

    plot ALMA = SumVectorData / SumVector;
}

plot MA;
switch (MovingAverage) {
case EMA:
    MA = MovAvgExponential(price, length = MA_Length);
case SMA:
    MA = SimpleMovingAvg(price, length = MA_Length);
case WMA:
    MA = WMA(price, length = MA_Length);
case ALMA:
    MA = ALMA(Data = price, window = MA_Length);
default:
    MA = HullMovingAvg(price = price, length = MA_Length);
}


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

def next_bar = MA[1] + delta_per_bar;

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

plot turning_point = if concavity[-1] != concavity then MA else Double.NaN;

MA.AssignValueColor(color = if concavity == -1 then
    if MA > MA[1] then Color.DARK_ORANGE else Color.RED else
    if MA < MA[1] then Color.DARK_GREEN else Color.GREEN);

MA.SetLineWeight(2);

turning_point.SetLineWeight(2);
turning_point.SetPaintingStrategy(paintingStrategy = PaintingStrategy.POINTS);
turning_point.SetDefaultColor(Color.WHITE);

plot MA_Max = if MA[-1] < MA and MA > MA[1] then MA else Double.NaN;
MA_Max.SetDefaultColor(Color.BLUE);
MA_Max.SetPaintingStrategy(PaintingStrategy.SQUARES);
MA_Max.SetLineWeight(4);

plot MA_Min = if MA[-1] > MA and MA < MA[1] then MA else Double.NaN;
MA_Min.SetDefaultColor(Color.GRAY);
MA_Min.SetPaintingStrategy(PaintingStrategy.TRIANGLES);
MA_Min.SetLineWeight(4);

plot sell = if turning_point and concavity == 1 then high else Double.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 Double.NaN;
buy.SetDefaultColor(Color.CYAN);
buy.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
buy.SetLineWeight(3);

def divergence = MA - next_bar;

AddLabel(yes, Concat("DIVERGENCE: " , divergence), 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);
AddLabel(yes, "MA Length: " + MA_Length, color = Color.CYAN);

#####################
#
## 5% PROFIT INPUT ##
#
#####################
input percentGainGoal = 5;
def percentChange = 100 * (high - EntryPrice()) / EntryPrice();
def exitGood = percentChange >= percentGainGoal;


#####################
#
## MACD (8, 21, 5, EXPONENTIAL, NO) ##
#
#####################
input MACD_fastLength = 8;
input MACD_slowLength = 21;
input MACD_Length = 5;
def averageType = AverageType.EXPONENTIAL;
def showBreakoutSignals = no;

def Value = MovingAverage(averageType, close, MACD_fastLength) - MovingAverage(averageType, close, MACD_slowLength);
def Avg = MovingAverage(averageType, Value, MACD_Length);


#####################
#
## ORIGINAL ORDERS ##
#
#####################
#AddOrder(OrderType.BUY_TO_OPEN, buy, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy", tradeSize = 1);
#AddOrder(OrderType.SELL_TO_CLOSE, sell, name = "Sell", tradeSize = 1);


##############
#
## 5% ORDER ##
#
##############
#addOrder(OrderType.SELL_TO_CLOSE, exitGood, name = "Sell 5%", tradeSize = 1);


####################
#
## ORIGINAL WITH MACD CONFIRM ORDER ##
#
####################
AddOrder(OrderType.BUY_TO_OPEN, buy[1] and value > avg, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy", tradeSize = 1);
AddOrder(OrderType.SELL_TO_CLOSE, sell[1], name = "Sell", tradeSize = 1);


####################
#
## ORIGINAL BUY AND MA_MIN WITH MACD CONFIRM ORDER ##
#
####################
#AddOrder(OrderType.BUY_TO_OPEN, buy and value > avg or MA_Min and value > avg, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy", tradeSize = 1);
#AddOrder(OrderType.SELL_TO_CLOSE, sell, name = "Sell", tradeSize = 1);


###################
#
# 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 MA < MA[1] then MA else double.nan;
#CCD_D.SetDefaultColor(Color.RED);
#CCD_D.SetLineWeight(2);

#plot CCD_I = if concavity == -1 and MA >= MA[1] then MA else double.nan;
#CCD_I.SetDefaultColor(Color.DARK_ORANGE);
#CCD_I.SetLineWeight(2);

#plot CCU_D = if concavity == 1 and MA <= MA[1] then MA else double.nan;
#CCU_D.SetDefaultColor(COLOR.DARK_GREEN);
#CCU_D.SetLineWeight(2);

#plot CCU_I = if concavity == 1 and MA > MA[1] then MA else double.nan;
#CCU_I.SetDefaultColor(COLOR.GREEN);
#CCU_I.SetLineWeight(2);
 
C

cswu1211

New member
VIP
Thank you all for your comments and encouragement.

I have a laundry list of things in this post:
  1. Variations to scripts
  2. Scanner
  3. Testing

Script Variations

@chillc15, I'm intrigued by the Arnaud Legoux MA, and so here is a variant of the upper study that will allow you to choose between Hull, Simple, Exponential, Williams, and ALMA. I haven't gone through the various permutations, but bear in mind that the 'overshoot' and smoothing of the Hull is part of what makes the turning point signal so attractive.

@Miket Used your posted code for the ALMA. Hope you're good with this use. Thanks!

Code:
#
# Multiple Moving Average Concavity and Turning Points
#  or
# The Second Derivative of the A Moving Average
#
# via useThinkScript
# request from chillc15
# Added Arnaud Legoux MA and other Moving Averages
#
# Author: Seth Urion (Mahsume)
# Version: 2020-02-22 V2
#
# This code is licensed (as applicable) under the GPL v3
#
# ----------------------

declare upper;

input price = HL2;
input MA_Length = 21;
input lookback = 2;

input MovingAverage = {default "HMA", "EMA", "SMA", "WMA", "ALMA"};

script ALMA {
# Attributed to Miket
# https://tos.mx/9mznij
# https://usethinkscript.com/threads/alma-arnaud-legoux-ma-indicator-for-thinkorswim.174/
input Data = close;
input Window = 9;
input Sigma = 6;
input Offset = 0.85;

def m = (Offset * (Window - 1));
def s = Window/Sigma;

def SumVectorData = fold y = 0 to Window with WS do WS + Exp(-(sqr(y-m))/(2*sqr(s))) * getvalue(Data, (Window-1)-y);
def SumVector = fold z = 0 to Window with CW do CW + Exp(-(sqr(z-m))/(2*sqr(s)));

plot ALMA = SumVectorData / SumVector;
}

plot MA;
switch (MovingAverage) {
case EMA:
    MA = MovAvgExponential(price, length = MA_Length);
case SMA:
    MA = simpleMovingAvg(price, length = MA_Length);
case WMA:
    MA = wma(price, length = MA_Length);
case ALMA:
    MA = ALMA(Data = price, window = MA_Length);
default:
    MA = HullMovingAvg(price = price, length = MA_Length);
}


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

def next_bar = MA[1] + delta_per_bar;

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

plot turning_point = if concavity[-1] != concavity then MA else double.nan;

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

MA.SetLineWeight(3);

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

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

plot MA_Min = if MA[-1] > MA and MA < MA[1] then MA else Double.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 double.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 double.nan;
buy.SetDefaultColor(Color.CYAN);
buy.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
buy.SetLineWeight(3);

def divergence = MA - next_bar;

addLabel(yes, concat("DIVERGENCE: " , divergence), 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);

SCANS

@mailbagman2000 , Scans, yes...
Scan 1 -- Buy signals.

This scan relies on the upper study being called "Concavity" and is entered on the scan page as a custom.
Code:
Concavity("hma length" = 55, price = CLOSE)."buy" is true within 2 bars

Scan 2 -- Stocks approaching possible Buy signals
I thought that, since the lower indicator crossovers can generate signals, that an 'early warning' scan might be of interest. We can scan for any HMA that has a negative convergence, and look for a decrease in the distance below zero. It may be a long way off, it may never get there, but we are alerted before the trade entry.

Code:
script ConcavityDivergence {
#
# Hull Moving Average Concavity Divergence
#  or
# The Second Derivative of the Hull Moving Average
#
# Author: Seth Urion (Mahsume)
# Version: 2020-02-21 Initial Public
#
# This code is licensed (as applicable) under the GPL v3
#
# ----------------------

declare lower;

input price = HL2;

input HMA_length = 34;
input lookback = 2;

def HMA = HullMovingAvg(length = HMA_length, price = price);

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;

plot zero = 0;
zero.setdefaultcolor(color.gray);
zero.setpaintingstrategy(PaintingStrategy.DASHES);

plot divergence = displacer(-1, HMA - next_bar);
divergence.setDefaultColor(Color.LIME);
divergence.SetLineweight(2);

plot cx_up = if divergence crosses above zero then 0 else double.nan;
cx_up.SetPaintingStrategy(PaintingStrategy.POINTS);
cx_up.SetDefaultColor(Color.LIGHT_GREEN);
cx_up.SetLineWeight(4);

plot cx_down = if divergence crosses below zero then 0 else double.nan;
cx_down.SetPaintingStrategy(PaintingStrategy.POINTS);
cx_down.SetDefaultColor(Color.RED);
cx_down.SetLineWeight(4);

}

def signal = ConcavityDivergence("hma length" = 55)."concavity";
def HMA = ConcavityDivergence("hma length" = 55)."HMA";
def next = ConcavityDivergence("hma length" = 55)."next_bar";

plot buy = signal < 0 and (HMA - next) > (HMA[1] - next[1]);

TESTING

I used the built-in strategy feature of thinkorswim, with block sizes of 100. All tests were run on 1 year Daily charts, with an HMA length of 55 and set price to CLOSE

First SPX
Buy and Hold: $55,600
Strategy: $77,372
Delta + $21772



Next ADI
Buy and Hold: $1718
Strategy: $5202
Delta: + $3483


Last BA
Buy and Hold: ($9322)
Strategy: $14393
Delta: + $23715


SHORT TIME FRAMES
I did not take time today to test shorter timeframes, though I imagine the results would be good. If there is interest, I'll try to post up some another time.

If you've read this far, thank you.
Happy trading and good luck.
I copy it to the Scan, and got a message for invalid symbol at line 47, pls advise, tks
 
S

Summies88

New member
@TraderKevin I thought that I had my buy signals setup to buy at market open the day following the buy signal because I thought the buy signal was plotted based on the HL2 of the candle and not needing the next candle for the signal. I haven't had the time to actually see if that was correct or not. Thank you for the updated code, I will use this to continue my backtesting.
 
Last edited:
T

TraderKevin

Member
@TraderKevin I thought that I had my buy signals setup to buy at market open the day following the buy signal because I thought the buy signal was plotted based on the HL2 of the candle and not needing the next candle for the signal. I haven't had the time to actually see if that was correct or not. Thank you for the updated code, I will use this to continue my backtesting.
Using HL2 and waiting until midway through candle creation will allow you to guess that the candle won't make a move that changes the buy/sell signal, but based on my experience, that guess will be wrong as often as it's right. Half the time, then, you'll find yourself in trades that had no signal, and the price will have moved against you substantially already by the time the candle closes (since moving against you is what causes the signal to disappear). Even then you wouldn't be entering at the candle's open price like your strategy indicates, but somewhere worse (since it would have to be to create the signal in the first place).

In general, if results look too good to be true, they almost always are. When creating a strategy and backtesting it, your first step after creating it is to figure out everything that could possibly be wrong with the strategy that makes it look better than it actually is. In my experience, there are always many and they are difficult to find. Also keep in mind that TOS's backtesting doesn't account for transaction costs or slippage, which will both have a much larger effect than you might realize.
 

Similar threads

Top