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

The Bataylor

New member
VIP
@mashume Have there been any thoughts/attempts at projecting either the lower or the upper out into the future, or would that just be a fools-errand? I know the lower is already displaced by -1, but was wondering if either could be pushed out with an extension script.
 

tradegeek

Active member
2019 Donor
VIP
@mashume Have there been any thoughts/attempts at projecting either the lower or the upper out into the future, or would that just be a fools-errand? I know the lower is already displaced by -1, but was wondering if either could be pushed out with an extension script.
@mashume How about calculating the price at which the lower indicator will cross the zero line and have it as a label?
 

mashume

Well-known member
VIP
@mashume How about calculating the price at which the lower indicator will cross the zero line and have it as a label?
Reversing the calculation of the HMA is not a trivial task. One of these days, I'll post about the calculation of reverse moving averages, but it gets quite messy, quite quickly. I looked into the idea once upon a time and decided to move on. Perhaps it would be possible to have a set of labels that showed what would happen if the price were to move up or down a certain distance or set of distances (+- 5c, +- 10c, etc...) because that would simply be adding a fake (test) value to the array of values used in the calculation and testing to see whether the indicator had moved over or under the desired point. But that's an exercise for a very bored afternoon. Maybe tomorrow. ;-)

-mashume
 

mashume

Well-known member
VIP

Reverse Calculating Moving Averages -- LONG

There have been several (many) requests from the community for an indicator that would show at what price an indicator (usually the HMA concavity indicator) would flip. I will attempt to show how this is could be done, and why it isnt, and propose a 'close but not really' solution.

Let's begin with a simple moving average. The calculation for a 3 bar simple moving average would be:
Code:
SMA(CLOSE, 3) := (CLOSE + CLOSE[1] + CLOSE[2]) / 3
Adding some values, say (10, 10, 10) we can see that the resultant value is 10
Code:
(10 + 10 + 10) / 3 = 10
Let's complicate the matter a bit and make the values different. (9, 10, 11):
Code:
(9 + 10 + 11) /  3 = 10
So far, so good.
now let's ask what value would make the series (9, 10, x) return a value of 10. We already know from the previous exercise:
Code:
(9 + 10 + x) / 3 = 10
a little bit of algebra gets us to this:
Code:
x = -9 -10 + 30
which gives us a value of 11 for x, as we would expect.
we can gereralize this for a simple moving average of length 5 as such
Code:
x = -CLOSE[1] - CLOSE[2] - CLOSE[3] - CLOSE[4] + 5 * DESIRED_VALUE
and we could keep going. for simple moving averages.

The HMA formula is this:
Code:
HMA= WMA(2*WMA(n/2) − WMA(n)),sqrt(n))
where the formula for the WMA (for 5 periods) is:
Code:
WMA = (P1 * 5) + (P2 * 4) + (P3 * 3) + (P4 * 2) + (P5 * 1) / (5 + 4 + 3 + 2 + 1)

These formulas are taken from https://www.fidelity.com/learning-c...hnical-analysis/technical-indicator-guide/wma and https://www.fidelity.com/learning-c...technical-indicator-guide/hull-moving-average

Combining them then, we have a 4 period HMA as:

Code:
HMA(4) = 2 * ((2*((CLOSE[0] * 2) + (CLOSE[1] * 1) / (2 + 1)) − ((CLOSE[0] * 4) + (CLOSE[1] * 3) + (CLOSE[2] * 2) + (CLOSE[3] * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((CLOSE[1] * 2) + (CLOSE[2] * 1) / (2 + 1)) − ((CLOSE[1] * 4) + (CLOSE[2] * 3) + (CLOSE[3] * 2) + (CLOSE[4] * 1) / (4 + 3 + 2 + 1))) * 1)

Now, substitute in x and a bunch of letters so I can run it through a symbolic solver:
Code:
CLOSE[0] -> x
CLOSE[1] -> a
CLOSE[2] -> b
CLOSE[3] -> c
CLOSE[4] -> d

HMA = 2 * ((2*((x * 2) + (a * 1) / (2 + 1)) − ((x * 4) + (a * 3) + (b * 2) + (c * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((a * 2) + (b * 1) / (2 + 1)) − ((a * 4) + (b * 3) + (c * 2) + (d * 1) / (4 + 3 + 2 + 1))) * 1) + y

For those wishing to reproduce this, here is the python code I used to isolate (solve) that mess for x:
Python:
from sympy.solvers import solve
from sympy import Symbol
from sympy import Eq
x = Symbol('x')
y = Symbol('y')
a = Symbol('a')
b = Symbol('b')
c = Symbol('c')
d = Symbol('d')
eq = Eq((2 * ((2*((x * 2) + (a * 1) / (2 + 1)) - ((x * 4) + (a * 3) + (b * 2) + (c * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((a * 2) + (b * 1) / (2 + 1)) - ((a * 4) + (b * 3) + (c * 2) + (d * 1) / (4 + 3 + 2 + 1))) * 1) - y), x)
solve(eq, x)

It returns the following:
Code:
-28*a/3 - 31*b/3 - 12*c/5 - d/10 - y

which says, substituting back our replacements from above:
Code:
-28 * CLOSE[1] / 3 - 31 * CLOSE[2] / 3 - 12 * CLOSE[3] / 5 - CLOSE[4] / 10 - 10 = x

and we can plug in our values and see what happens.
That's for a 4 period Hull Moving Average. Now, lets see what the mess looks like for the default 55 period HMA used in the concavity study... NOT.

So what can be done?

we could try to create a series where the most recent value was increased or decreased by some amount and the equations solved the right-way-round, at which point we could look and see whether the result is above or below zero (or whatever point we wanted really) so it would look a bit like this:

for a four period simple moving average:
Code:
10 + 10 + 10 + (10 + 0.5) / 4 >= 10
10 + 10 + 10 + (10 + 1.0) / 4 >= 10
10 + 10 + 10 + (10 - 0.5) / 4 >= 10
10 + 10 + 10 + (10 - 1.0) / 4 >= 10

We can eyeball this one and say [true, true, false, false] to the four equations. The labels might show us those four +- values and a color code to tell us they are over or under 10 (in this case, or 0, or whatever target value).

So perhaps, though I haven't tested it, we can create a series where the last value only is changed:

Code:
def hl_v = if isNan(HL2[-1]) then HL2 - 10 else HL2;

The +- values could be set:
Code:
input over_under = 0.5;
def hl_over_a = if isNan(HL2[-1]) then HL2 + over_under else HL2;
def hl_over_b = if isNan(HL2[-1]) then HL2 + over_under * 2 else HL2;
def hl_under_a = if isNan(HL2[-1]) then HL2 - over_under else HL2;
def hl_under_b = if isNan(HL2[-1]) then HL2 - over_under  * 2 else HL2;

These arrays then get used in the calculation of the HMA (or whatever function you wish -- z scores etc...) and compared, as above to a value.

Conclusions

I don't really have any today. This is kind of preliminary, and sketchy, and hackey... I would welcome thoughts from others on the usefulness of this, the processor cycles required vs the benefit gained, and such ruminations as you all care to share.

Happy Trading,
Mashume
 

astro_phx

Member

Reverse Calculating Moving Averages -- LONG

There have been several (many) requests from the community for an indicator that would show at what price an indicator (usually the HMA concavity indicator) would flip. I will attempt to show how this is could be done, and why it isnt, and propose a 'close but not really' solution.

Let's begin with a simple moving average. The calculation for a 3 bar simple moving average would be:
Code:
SMA(CLOSE, 3) := (CLOSE + CLOSE[1] + CLOSE[2]) / 3
Adding some values, say (10, 10, 10) we can see that the resultant value is 10
Code:
(10 + 10 + 10) / 3 = 10
Let's complicate the matter a bit and make the values different. (9, 10, 11):
Code:
(9 + 10 + 11) /  3 = 10
So far, so good.
now let's ask what value would make the series (9, 10, x) return a value of 10. We already know from the previous exercise:
Code:
(9 + 10 + x) / 3 = 10
a little bit of algebra gets us to this:
Code:
x = -9 -10 + 30
which gives us a value of 11 for x, as we would expect.
we can gereralize this for a simple moving average of length 5 as such
Code:
x = -CLOSE[1] - CLOSE[2] - CLOSE[3] - CLOSE[4] + 5 * DESIRED_VALUE
and we could keep going. for simple moving averages.

The HMA formula is this:
Code:
HMA= WMA(2*WMA(n/2) − WMA(n)),sqrt(n))
where the formula for the WMA (for 5 periods) is:
Code:
WMA = (P1 * 5) + (P2 * 4) + (P3 * 3) + (P4 * 2) + (P5 * 1) / (5 + 4 + 3 + 2 + 1)

These formulas are taken from https://www.fidelity.com/learning-c...hnical-analysis/technical-indicator-guide/wma and https://www.fidelity.com/learning-c...technical-indicator-guide/hull-moving-average

Combining them then, we have a 4 period HMA as:

Code:
HMA(4) = 2 * ((2*((CLOSE[0] * 2) + (CLOSE[1] * 1) / (2 + 1)) − ((CLOSE[0] * 4) + (CLOSE[1] * 3) + (CLOSE[2] * 2) + (CLOSE[3] * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((CLOSE[1] * 2) + (CLOSE[2] * 1) / (2 + 1)) − ((CLOSE[1] * 4) + (CLOSE[2] * 3) + (CLOSE[3] * 2) + (CLOSE[4] * 1) / (4 + 3 + 2 + 1))) * 1)

Now, substitute in x and a bunch of letters so I can run it through a symbolic solver:
Code:
CLOSE[0] -> x
CLOSE[1] -> a
CLOSE[2] -> b
CLOSE[3] -> c
CLOSE[4] -> d

HMA = 2 * ((2*((x * 2) + (a * 1) / (2 + 1)) − ((x * 4) + (a * 3) + (b * 2) + (c * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((a * 2) + (b * 1) / (2 + 1)) − ((a * 4) + (b * 3) + (c * 2) + (d * 1) / (4 + 3 + 2 + 1))) * 1) + y

For those wishing to reproduce this, here is the python code I used to isolate (solve) that mess for x:
Python:
from sympy.solvers import solve
from sympy import Symbol
from sympy import Eq
x = Symbol('x')
y = Symbol('y')
a = Symbol('a')
b = Symbol('b')
c = Symbol('c')
d = Symbol('d')
eq = Eq((2 * ((2*((x * 2) + (a * 1) / (2 + 1)) - ((x * 4) + (a * 3) + (b * 2) + (c * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((a * 2) + (b * 1) / (2 + 1)) - ((a * 4) + (b * 3) + (c * 2) + (d * 1) / (4 + 3 + 2 + 1))) * 1) - y), x)
solve(eq, x)

It returns the following:
Code:
-28*a/3 - 31*b/3 - 12*c/5 - d/10 - y

which says, substituting back our replacements from above:
Code:
-28 * CLOSE[1] / 3 - 31 * CLOSE[2] / 3 - 12 * CLOSE[3] / 5 - CLOSE[4] / 10 - 10 = x

and we can plug in our values and see what happens.
That's for a 4 period Hull Moving Average. Now, lets see what the mess looks like for the default 55 period HMA used in the concavity study... NOT.

So what can be done?

we could try to create a series where the most recent value was increased or decreased by some amount and the equations solved the right-way-round, at which point we could look and see whether the result is above or below zero (or whatever point we wanted really) so it would look a bit like this:

for a four period simple moving average:
Code:
10 + 10 + 10 + (10 + 0.5) / 4 >= 10
10 + 10 + 10 + (10 + 1.0) / 4 >= 10
10 + 10 + 10 + (10 - 0.5) / 4 >= 10
10 + 10 + 10 + (10 - 1.0) / 4 >= 10

We can eyeball this one and say [true, true, false, false] to the four equations. The labels might show us those four +- values and a color code to tell us they are over or under 10 (in this case, or 0, or whatever target value).

So perhaps, though I haven't tested it, we can create a series where the last value only is changed:

Code:
def hl_v = if isNan(HL2[-1]) then HL2 - 10 else HL2;

The +- values could be set:
Code:
input over_under = 0.5;
def hl_over_a = if isNan(HL2[-1]) then HL2 + over_under else HL2;
def hl_over_b = if isNan(HL2[-1]) then HL2 + over_under * 2 else HL2;
def hl_under_a = if isNan(HL2[-1]) then HL2 - over_under else HL2;
def hl_under_b = if isNan(HL2[-1]) then HL2 - over_under  * 2 else HL2;

These arrays then get used in the calculation of the HMA (or whatever function you wish -- z scores etc...) and compared, as above to a value.

Conclusions

I don't really have any today. This is kind of preliminary, and sketchy, and hackey... I would welcome thoughts from others on the usefulness of this, the processor cycles required vs the benefit gained, and such ruminations as you all care to share.

Happy Trading,
Mashume
I just replying here for your great code. since I don't have any idea about coding I couldn't give my thoughts on it. I really thank you very much for putting some time into it. I am still using original code and it works great for me. Hopefully, if someone simplifies this I would really like to see how this works OR is it too much to ask to add an image or full code so I can try it out?
 

sunstar

New member
@mashume - I could follow very clearly until HMA[4] and then when the series example was provided, I'm having trouble connecting the dots, couple of questions
1. are you saying hl_over_a, b, hl_under_a, b should be used for calculating HMA itself or simply to indicate turning points?
2. HMA[4] itself presented a complex math equation, extending this to 55 or a higher number would present a very challenging equation, so thinking of using the series and using this series solve for x?
 

mashume

Well-known member
VIP
EDIT WARNING Watching this on /ES this afternoon, it is not behaving as I would have expected it to.

@sunstar
Something like this:
Code:
declare upper;

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

input stddev_len = 21;
input zlength = 13;
input threshold = 1.5;

input over_under = 0.5;
def a_price = if isNan(HL2[-1]) then (LOW + (HIGH + over_under) / 2) else HL2;
def b_price = if isNan(HL2[-1]) then (LOW + (HIGH + (over_under * 2 )) / 2) else HL2;
def c_price = if isNan(HL2[-1]) then (HIGH - (LOW - over_under) / 2) else HL2;
def d_price = if isNan(HL2[-1]) then (HIGH - (LOW - (over_under * 2 )) / 2) else HL2;

# A
def a_HMA             = HullMovingAvg(price = a_price, length = HMA_Length);
def a_delta         = a_HMA[1] - a_HMA[lookback + 1];
def a_delta_per_bar = a_delta / lookback;
def a_next_bar         = a_HMA[1] + a_delta_per_bar;
def a_divergence     = (a_HMA - a_next_bar) * 5000;
def a_Zscore         = ( (a_divergence - Average(a_divergence, zlength)) / StDev(a_divergence, zlength));

# B
def b_HMA             = HullMovingAvg(price = b_price, length = HMA_Length);
def b_delta         = b_HMA[1] - b_HMA[lookback + 1];
def b_delta_per_bar = b_delta / lookback;
def b_next_bar         = b_HMA[1] + b_delta_per_bar;
def b_divergence     = (b_HMA - b_next_bar) * 5000;
def b_Zscore         = ( (b_divergence - Average(b_divergence, zlength)) / StDev(b_divergence, zlength));

# C
def c_HMA             = HullMovingAvg(price = c_price, length = HMA_Length);
def c_delta         = c_HMA[1] - c_HMA[lookback + 1];
def c_delta_per_bar = c_delta / lookback;
def c_next_bar         = c_HMA[1] + c_delta_per_bar;
def c_divergence     = (c_HMA - c_next_bar) * 5000;
def c_Zscore         = ( (c_divergence - Average(c_divergence, zlength)) / StDev(c_divergence, zlength));

# D
def d_HMA             = HullMovingAvg(price = d_price, length = HMA_Length);
def d_delta         = d_HMA[1] - d_HMA[lookback + 1];
def d_delta_per_bar = d_delta / lookback;
def d_next_bar         = d_HMA[1] + d_delta_per_bar;
def d_divergence     = (d_HMA - d_next_bar) * 5000;
def d_Zscore         = ( (d_divergence - Average(d_divergence, zlength)) / StDev(d_divergence, zlength));


AddLabel(yes, if a_Zscore > 0 then "IF High > " + (HIGH + over_under) + " Z > 0" else "High + " + (High + over_under) + " NOT < 0", if a_Zscore > 0 then color.dark_green else color.gray);

AddLabel(yes, if b_Zscore > 0 then "IF High > " + (HIGH +  (2 * over_under)) + " Z > 0" else "High + " + (High + (2 * over_under)) + " NOT < 0", if a_Zscore > 0 then color.dark_green else color.gray);

AddLabel(yes, if c_Zscore > 0 then "IF Low < " + (LOW - over_under) + " Z < 0" else "Low - " + (LOW + over_under) + " NOT < 0", if a_Zscore > 0 then color.dark_red else color.gray);

AddLabel(yes, if d_Zscore > 0 then "IF Low < " + (LOW - (2 * over_under)) + " Z < 0" else "Low - " + (LOW + (2 * over_under)) + " NOT < 0", if a_Zscore > 0 then color.dark_red else color.gray);

-mashume

P.S. I have over_under set to 0.5 -- you may need to adjust it for the instrument you're trading. Or set it to ATR or some fraction thereof.
 
Last edited:

svencool

Member
VIP
Try this, you're script is a bit out of date

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

declare lower;

input price = HL2;

input HMA_length = 55;
input lookback = 2;

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

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

plot concavity = if HMA > next_bar then 1 else -1;
}

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


This is a hacked down version just for a scanner. Don't use this script as a plotting indicator.
-mashume
@mashume Do I just copy and paste this code into the scanner?
 

RickK

Active member

Reverse Calculating Moving Averages -- LONG

There have been several (many) requests from the community for an indicator that would show at what price an indicator (usually the HMA concavity indicator) would flip. I will attempt to show how this is could be done, and why it isnt, and propose a 'close but not really' solution.

Let's begin with a simple moving average. The calculation for a 3 bar simple moving average would be:
Code:
SMA(CLOSE, 3) := (CLOSE + CLOSE[1] + CLOSE[2]) / 3
Adding some values, say (10, 10, 10) we can see that the resultant value is 10
Code:
(10 + 10 + 10) / 3 = 10
Let's complicate the matter a bit and make the values different. (9, 10, 11):
Code:
(9 + 10 + 11) /  3 = 10
So far, so good.
now let's ask what value would make the series (9, 10, x) return a value of 10. We already know from the previous exercise:
Code:
(9 + 10 + x) / 3 = 10
a little bit of algebra gets us to this:
Code:
x = -9 -10 + 30
which gives us a value of 11 for x, as we would expect.
we can gereralize this for a simple moving average of length 5 as such
Code:
x = -CLOSE[1] - CLOSE[2] - CLOSE[3] - CLOSE[4] + 5 * DESIRED_VALUE
and we could keep going. for simple moving averages.

The HMA formula is this:
Code:
HMA= WMA(2*WMA(n/2) − WMA(n)),sqrt(n))
where the formula for the WMA (for 5 periods) is:
Code:
WMA = (P1 * 5) + (P2 * 4) + (P3 * 3) + (P4 * 2) + (P5 * 1) / (5 + 4 + 3 + 2 + 1)

These formulas are taken from https://www.fidelity.com/learning-c...hnical-analysis/technical-indicator-guide/wma and https://www.fidelity.com/learning-c...technical-indicator-guide/hull-moving-average

Combining them then, we have a 4 period HMA as:

Code:
HMA(4) = 2 * ((2*((CLOSE[0] * 2) + (CLOSE[1] * 1) / (2 + 1)) − ((CLOSE[0] * 4) + (CLOSE[1] * 3) + (CLOSE[2] * 2) + (CLOSE[3] * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((CLOSE[1] * 2) + (CLOSE[2] * 1) / (2 + 1)) − ((CLOSE[1] * 4) + (CLOSE[2] * 3) + (CLOSE[3] * 2) + (CLOSE[4] * 1) / (4 + 3 + 2 + 1))) * 1)

Now, substitute in x and a bunch of letters so I can run it through a symbolic solver:
Code:
CLOSE[0] -> x
CLOSE[1] -> a
CLOSE[2] -> b
CLOSE[3] -> c
CLOSE[4] -> d

HMA = 2 * ((2*((x * 2) + (a * 1) / (2 + 1)) − ((x * 4) + (a * 3) + (b * 2) + (c * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((a * 2) + (b * 1) / (2 + 1)) − ((a * 4) + (b * 3) + (c * 2) + (d * 1) / (4 + 3 + 2 + 1))) * 1) + y

For those wishing to reproduce this, here is the python code I used to isolate (solve) that mess for x:
Python:
from sympy.solvers import solve
from sympy import Symbol
from sympy import Eq
x = Symbol('x')
y = Symbol('y')
a = Symbol('a')
b = Symbol('b')
c = Symbol('c')
d = Symbol('d')
eq = Eq((2 * ((2*((x * 2) + (a * 1) / (2 + 1)) - ((x * 4) + (a * 3) + (b * 2) + (c * 1) / (4 + 3 + 2 + 1))) * 2) + ((2*((a * 2) + (b * 1) / (2 + 1)) - ((a * 4) + (b * 3) + (c * 2) + (d * 1) / (4 + 3 + 2 + 1))) * 1) - y), x)
solve(eq, x)

It returns the following:
Code:
-28*a/3 - 31*b/3 - 12*c/5 - d/10 - y

which says, substituting back our replacements from above:
Code:
-28 * CLOSE[1] / 3 - 31 * CLOSE[2] / 3 - 12 * CLOSE[3] / 5 - CLOSE[4] / 10 - 10 = x

and we can plug in our values and see what happens.
That's for a 4 period Hull Moving Average. Now, lets see what the mess looks like for the default 55 period HMA used in the concavity study... NOT.

So what can be done?

we could try to create a series where the most recent value was increased or decreased by some amount and the equations solved the right-way-round, at which point we could look and see whether the result is above or below zero (or whatever point we wanted really) so it would look a bit like this:

for a four period simple moving average:
Code:
10 + 10 + 10 + (10 + 0.5) / 4 >= 10
10 + 10 + 10 + (10 + 1.0) / 4 >= 10
10 + 10 + 10 + (10 - 0.5) / 4 >= 10
10 + 10 + 10 + (10 - 1.0) / 4 >= 10

We can eyeball this one and say [true, true, false, false] to the four equations. The labels might show us those four +- values and a color code to tell us they are over or under 10 (in this case, or 0, or whatever target value).

So perhaps, though I haven't tested it, we can create a series where the last value only is changed:

Code:
def hl_v = if isNan(HL2[-1]) then HL2 - 10 else HL2;

The +- values could be set:
Code:
input over_under = 0.5;
def hl_over_a = if isNan(HL2[-1]) then HL2 + over_under else HL2;
def hl_over_b = if isNan(HL2[-1]) then HL2 + over_under * 2 else HL2;
def hl_under_a = if isNan(HL2[-1]) then HL2 - over_under else HL2;
def hl_under_b = if isNan(HL2[-1]) then HL2 - over_under  * 2 else HL2;

These arrays then get used in the calculation of the HMA (or whatever function you wish -- z scores etc...) and compared, as above to a value.

Conclusions

I don't really have any today. This is kind of preliminary, and sketchy, and hackey... I would welcome thoughts from others on the usefulness of this, the processor cycles required vs the benefit gained, and such ruminations as you all care to share.

Happy Trading,
Mashume


@mashume
OMG... I bask in the shadow of your big brain. LOL.

Seriously though, thank you sincerely for your work and your contribution. It has taken me a few days to get thru the 26 pages of this thread, but I am left simply fascinated.

Best to you, my friend.
 

snahmed

New member
VIP
so my question after reading all 26 pages is the code on the 1st page updated to reflect all the changes and additions made to the orignal code and what are the best settings for a 5 minute chart.
 

mashume

Well-known member
VIP
As far as I know, the version shown at the BOTTOM of the first post (V4) is the latest. It's what I am running at any rate.

As for ideal timeframes, it depends on the instrument you're trading and your tolerance for noise vs early entries.

Happy Trading,
-mashume
 

RickK

Active member
Having traded this manually for a number of days on various (faster) timeframes, I'm liking it more and more. There haven't been a lot of posts to this thread in the last few months, so I hope that interest in it hasn't waned.

@tradegeek , quite some time ago you and @Aeteux had discussed porting this to Ninja. As I recall, you went ahead and did the lower with the help of a developer and @Aeteux was interested but not financially able at the time to invest in having the upper created for Ninja. @diazlaz and @kelso , I believe you also expressed some interest.

I don't know if any of you still trade this indicator. Possibly you've moved on to other things as I've seen many posts from you on many other topics over the last 6 months.

A trading partner and I are interested in creating an automated trading (scalping) tool for Ninja and are prepared to invest to get it done. I say scalping because our trading styles lend to being in and out of the market quickly to limit risk. I'm FULLY aware of all of the limitations that ToS strategies have with regards to PLs (disappearing entries, entering at unrealistic spots, no accounting for commissions, fees & slippage, etc.) So we've concluded that if this code performs only a fraction as well as what is displayed in the image below, then pursuing this will hopefully be worthwhile.

It is also currently being looked at in TradeStation, but I believe there is an issue with the referencing of a future candle.

I don't know if any of you would like to join in to defray the costs.

@tradegeek , if you ARE still using this indicator and still love it, I'd like to get your opinion on some of the limitations you've experienced using it on Ninja. Also, I'd be interested in any additional thoughts that you may have regarding hiring someone to get this done. On the other hand, if you have concluded that this type of thing would be a futile endeavour, I'd like to hear that too.

Please get in touch by messaging me directly either on the usethinkscript discord server ( invite: https://discord.gg/HQVZUxB ) where this nonVIP member I think can still message, or on the B4 Trading Community discord server ( invite: https://discord.gg/kD3pKE2CQd )

Best to you....

 
Last edited:

Fernwood

New member
VIP
I have manually tested this (eyeballed) on several positions for few days and really liking this and plan to use it in live trading. Problem I have is finding trades before they go green... Does anyone have a good breakout scanner or knows one on the forums they could point to? Maybe someone knows how to code a quick scan to search broad market NASDAQ COMP for stocks that are dark green (upper) with heavy volume and lower divergence signal cx_up pops the green dot?? Any help is much appreciated love the community :)
 

RickK

Active member
@mashume Hi Seth...

Do you have any thoughts on how this indicator might be built in a javascript environment? I'm trying to do it in Tradovate but I don't think there's a way to predict a future candle (like you did with concavity[-1])....unless I'm missing something. I know that you changed concavity[-1] to concavity[1], but when you did that the line

Code:
plot sell = if turning_point and concavity == 1 then high else Double.NaN;

was changed to

Code:
plot sell = if turning_point and concavity == -1 then high else Double.NaN;

What am I missing?

Anyone with javascript knowledge....thank you for chiming in.

In case it isn't obvious, I know very little about javascript.
 

mashume

Well-known member
VIP
@mashume Hi Seth...

Do you have any thoughts on how this indicator might be built in a javascript environment? I'm trying to do it in Tradovate but I don't think there's a way to predict a future candle (like you did with concavity[-1])....unless I'm missing something. I know that you changed concavity[-1] to concavity[1], but when you did that the line

Code:
plot sell = if turning_point and concavity == 1 then high else Double.NaN;

was changed to

Code:
plot sell = if turning_point and concavity == -1 then high else Double.NaN;

What am I missing?

Anyone with javascript knowledge....thank you for chiming in.

In case it isn't obvious, I know very little about javascript.
I'm no javascript expert (most of my professional dev work is python). Can you post whatever js code you do have though and I'll be happy to try to figure it out.

-Mashume
 

RickK

Active member
I'm no javascript expert (most of my professional dev work is python). Can you post whatever js code you do have though and I'll be happy to try to figure it out.

-Mashume

Thanks, but we don't actually have anything set up yet. We started out with a Hull crossover indicator, with one of the lines being displaced by 2 but havent proceeded any further because we couldn't figure out how to do the prediction. However, I do have the code for Tradovate's Hull Moving Average script. That is first below. Second and third below are the scripts for the constants noted in the parent script.

Thanks much Seth! It's very considerate of you.

HULL MOVING AVERAGE (JAVASCRIPT)
Code:
const predef = require("./tools/predef");
const WMA = require("./tools/WMA");

class hullMovingAverage {
    init() {
        const period = this.props.period;
        this.wmaLong = WMA(period);
        this.wmaShort = WMA(period / 2);
        this.wmaSqrt = WMA(Math.sqrt(period));
    }

    map(d) {
        const value = d.value();
        const wmaLong = this.wmaLong(value);
        const wmaShort = this.wmaShort(value) * 2;
        const wmaDiff = wmaShort - wmaLong;
        return this.wmaSqrt(wmaDiff);
    }
}

module.exports = {
    name: "hma",
    description: "Hull Moving Average",
    calculator: hullMovingAverage,
    params: {
        period: predef.paramSpecs.period(14)
    },
    tags: [predef.tags.MovingAverage],
    schemeStyles: predef.styles.solidLine("#8cecff")
};

CONSTANT "predef"
Code:
const lodash = require("lodash");

const { ParamType } = require("./meta");


function mkStyle(style) {

    if (typeof style === "string") {

        style = { color: style };

    }

    return {

        color: style.color || "gray",

        lineWidth: style.lineWidth || 1,

        opacity: style.opacity || 100,

        lineStyle: style.lineStyle || 1

    };

}


module.exports = {

    plotters: {

        line: { type: "line" },

        multiline(fields) {

            return {

                type: "multiline",

                fields

            };

        },

        singleline(field) {

            return {

                type: "multiline",

                fields: [field]

            };

        },

        pivotpoints(fields) {

            return {

                type: "pivotpoints",

                fields

            };

        },

        histogram: { type: "histogram" },

        cumulative: { type: "cumulative" },

        zigzag(fields) {

            return {

                type: "zigzag",

                fields

            };

        },

        macd: { type: "macd" },

        scatter: { type: "scatter" },

        dots(field) {

            return {

                type: "dots",

                field: field || "_"

            };

        },

        columns(field) {

            return {

                type: "columns",

                field

            };

        },

        custom(func) {

            return {

                type: "custom",

                function: func

            };

        },

        range(fieldFrom, fieldTo) {

            return {

                type: "range",

                fields: [fieldFrom, fieldTo]

            };

        }

    },

    scalers: {

        singlePath: { type: "singlePath" },

        multiPath(fields) {

            return {

                type: "multiPath",

                fields

            };

        }

    },

    paramSpecs: {

        period(defValue) {

            return {

                type: ParamType.NUMBER,

                def: defValue,

                restrictions: {

                    step: 1,

                    min: 1

                },

                validate(value) {

                    if (value < 1) {

                        return "Period should be a positive number";

                    }

                    return undefined;

                }

            };

        },

        number(defValue, step, min) {

            return {

                type: ParamType.NUMBER,

                def: defValue,

                restrictions: {

                    step: step || 1,

                    min: min || 0

                }

            };

        },

        percent(defValue, step, min, max) {

            return {

                type: ParamType.NUMBER,

                def: defValue,

                restrictions: {

                    step: step || 1,

                    min,

                    max

                }

            };

        },

        bool(defValue) {

            return {

                type: ParamType.BOOLEAN,

                def: defValue

            };

        },

        text(defValue) {

            return {

                type: ParamType.TEXT,

                def: defValue

            };

        },

        enum(enumSet, defValue) {

            return {

                type: ParamType.ENUM,

                enumSet,

                def: defValue,

                toSelectOptions() {

                    return lodash.toPairs(enumSet)

                        .map(p => ({

                            label: p[1],

                            value: p[0]

                        }));

                }

            };

        },

        color(defValue) {

            return {

                type: ParamType.COLOR,

                def: defValue

            };

        }

    },

    filters: {

        onlyNumberValue(d) {

            return d && (typeof d.value === "number" && !isNaN(d.value) || typeof d === "object");

        },

        isNumber(d) {

            return typeof d === "number" && !isNaN(d);

        }

    },

    tags: {

        MovingAverage: "Moving Averages",

        Channels: "Channels",

        Volatility: "Volatility",

        Oscillators: "Oscillators",

        Volumes: "Volume-based"

    },

    styles: {

        solidLine(plotName, dark, light) {

            if (arguments.length === 1) {

                dark = plotName;

                plotName = "_";

            }

            if (!light) {

                light = dark;

            }

            const result = {

                dark: {},

                light: {}

            };

            result.dark[plotName] = mkStyle(dark);

            result.light[plotName] = mkStyle(light);

            return result;

        },

        plot(partialStyle) {

            return mkStyle(partialStyle);

        },

        dashLine(plotName, dark, light) {

            if (arguments.length === 1) {

                dark = plotName;

                plotName = "_";

            }

            if (!light) {

                light = dark;

            }

            const result = {

                dark: {},

                light: {}

            };

            result.dark[plotName] = mkStyle(dark);

            result.dark[plotName].lineStyle = 3;

            result.light[plotName] = mkStyle(light);

            result.light[plotName].lineStyle = 3;

            return result;

        },

    }

};

CONSTANT "WMA"
Code:
function triangular(value) {
    return (value / 2) * (value + 1);
}

function WeightedMovingAverage(period) {
    function wma(value) {
        return wma.push(value);
    }

    wma.reset = () => {
        wma.state = {
            items: []
        };
    };

    wma.push = (value) => {
        wma.state.items.push(value);
        if (wma.state.items.length > period) {
            wma.state.items.shift();
        }
        return wma.avg();
    };

    wma.avg = () => {
        const items = wma.state.items;
        const denominator = triangular(items.length);

        function wmaAccumulator(sum, value, index) {
            return sum + (value * (index + 1) / denominator);
        }

        return items.reduce(wmaAccumulator, 0);
    };

    wma.reset();

    return wma;
}

module.exports = WeightedMovingAverage;
 

RickK

Active member
Well, possibly @rad14733 , although there is no real community there...not like here. Not at all an exchange of ideas from members who are enthusiastic about upping the game (and also helping each other). It's more of a request board where people who have used indicators on other platforms request that the dev team create them.

The only reason why I reached out to @mashume was because it was an indicator that was originally created by him so I knew he had formulated ideas about coming up with the concavity turning points. Knowing that he had a programming background, I wondered if he was also versed in javascript. Definitely not looking for a free lunch here... just a tip, if he has one, to achieve the same sort of thing on that platform. :)
 

Similar threads

Top