• LIMITED TIME ONLY: Take $40 off VIP with code: BTD40.

Manually calculated EMA not matching expected value?

new_spy123

New member
Hello All,

I am on a quest to work around the problem of referencing secondary aggregation in scan custom alerts.

What I am trying to solve: Receive alert when hourly candle crosses below 150 day ema in scan alert.

This is what I am doing in the below script without referencing aggregation constants and calculating the ema manually. I feel like I am close but missing something hence the values are not matching. Appreciate any input on this.

Ruby:
def smoothing = (2/150 + 1);

def today = GetDay() == GetLastDay();

def ydayClose = if today then ydayClose[1] else close;

input n = 150;
def emaForLast150Days = fold index = 1 to 150 with p = ydayClose do if index == 1 then ydayClose else (smoothing*ydayClose[index] + (1 - smoothing)*p);

plot emaForLast150DaysPlot = if today then emaForLast150Days else close;

Here is the chart, showing the gap with what I am computing vs what the actual value should be.

GkLMi9q.png
 
Solution
have a look at this page
https://school.stockcharts.com/doku.php?id=technical_indicators:moving_averages

...the first value in a EMA series is a simple moving average
maybe something like this?
def displace2 = n;
def SMA = Average(price2[displace2], n);
...adjust the loop count , start or stop , accordingly

...on the do formula, need to calc a reverse count of index, to generate an offset that points at the farthest bar first and ends with the closest. ( n - index )
actually the 2nd bar in the series. the first bar will be the SMA

changes i would make
...fold loop , index = 1 to n . replace constants (150) when possible
...move smoothing formula to be after the input, and use n instead of constant (150).
input n =
def...

halcyonguy

Well-known member
VIP
Lifetime
have a look at this page
https://school.stockcharts.com/doku.php?id=technical_indicators:moving_averages

...the first value in a EMA series is a simple moving average
maybe something like this?
def displace2 = n;
def SMA = Average(price2[displace2], n);
...adjust the loop count , start or stop , accordingly

...on the do formula, need to calc a reverse count of index, to generate an offset that points at the farthest bar first and ends with the closest. ( n - index )
actually the 2nd bar in the series. the first bar will be the SMA

changes i would make
...fold loop , index = 1 to n . replace constants (150) when possible
...move smoothing formula to be after the input, and use n instead of constant (150).
input n =
def smoothing = (2/n + 1);


misc
...fold loops don't process the end count number. if you need to use the end number, use index = 1 to ( n+1)
test code
def x = fold i = 1 to 20 with k do k + 1;
addlabel(1, "fold loop 20 " + x, color.cyan);
# x will equal 19
 
Last edited:
Solution

new_spy123

New member
have a look at this page
https://school.stockcharts.com/doku.php?id=technical_indicators:moving_averages

...the first value in a EMA series is a simple moving average
maybe something like this?
def displace2 = n;
def SMA = Average(price2[displace2], n);
...adjust the loop count , start or stop , accordingly

...on the do formula, need to calc a reverse count of index, to generate an offset that points at the farthest bar first and ends with the closest.
example, rev_cnt = ( n - index )
actually the 2nd bar in the series. the first bar will be the SMA

changes i would make
...fold loop , index = 1 to n . replace constants (150) when possible
...move smoothing formula to be after the input, and use n instead of constant (150).
input n =
def smoothing = (2/n + 1);


misc
...fold loops don't process the end count number. if you need to use the end number, use index = 1 to ( n+1)
test code
def x = fold i = 1 to 20 with k do k + 1;
addlabel(1, "fold loop 20 " + x, color.cyan);
# x will equal 19
Thank you for these inputs. Honestly, I was expecting someone to call me crazy for trying something like this :)
 

halcyonguy

Well-known member
VIP
Lifetime
Hello All,

I am on a quest to work around the problem of referencing secondary aggregation in scan custom alerts.

What I am trying to solve: Receive alert when hourly candle crosses below 150 day ema in scan alert.

This is what I am doing in the below script without referencing aggregation constants and calculating the ema manually. I feel like I am close but missing something hence the values are not matching. Appreciate any input on this.

Ruby:
def smoothing = (2/150 + 1);

def today = GetDay() == GetLastDay();

def ydayClose = if today then ydayClose[1] else close;

input n = 150;
def emaForLast150Days = fold index = 1 to 150 with p = ydayClose do if index == 1 then ydayClose else (smoothing*ydayClose[index] + (1 - smoothing)*p);

plot emaForLast150DaysPlot = if today then emaForLast150Days else close;

Here is the chart, showing the gap with what I am computing vs what the actual value should be.


here is one way to calculate an EMA, instead of using the ExpAverage() function.
the first data point is derived from a simple average.
i cheated and used the average() function instead of using a fold loop for it.
barnumber 1 is the SMA value, then the following bars are the calculated EMA.

test bubbles can be displayed, while the price difference is greater that $0.005

this isn't perfect, but close. the first dozen bars may be off by 0.1% , on average or so.

i followed the rules on this page
https://school.stockcharts.com/doku.php?id=technical_indicators:moving_averages

Ruby:
# ema_create_01

# create formulas to find an EMA
# https://school.stockcharts.com/doku.php?id=technical_indicators:moving_averages
#
# 1.  Initial SMA: 10-period sum / 10
# 2.  Multiplier: (2 / (Time periods + 1) ) = (2 / (10 + 1) ) = 0.1818 (18.18%)
# 3.  EMA: {Close - EMA(previous day)} x multiplier + EMA(previous day).

# ---------------------------------------------
def na = double.nan;
def bn = barnumber();
# ---------------------------------------------
# ema parameters

# TD - MovAvgExponential
def price = close;
input exponential_avg_len = 9;
def ema_len = exponential_avg_len;

# default AME, for comparing to calculated value
def AvgExp = ExpAverage(price, ema_len);

input show_actual_exp_line = yes;
plot z1 = if show_actual_exp_line then AvgExp else na;
z1.SetStyle(Curve.MEDIUM_DASH);
z1.SetDefaultColor(Color.cyan);
# z1.setlineweight(1);
z1.hidebubble();

addlabel(1, "EMA length " + ema_len, color.yellow);

# ---------------------------------------------
# calc EMA - step 1
#  use SMA, from x bars back, for the 1st value in the EMA series

# SimpleMovingAvg
def sma_length = ema_len;
# this is ema length ,
#def sma_displace = ema_len;
def sma_displace = 0;
def SMA = Average(price[sma_displace], sma_length);

# ---------------------------------------------
# calc EMA - step 2
# 2.  Multiplier: (2 / (Time periods + 1) ) = (2 / (10 + 1) ) = 0.1818 (18.18%)

def multi = 2 / (ema_len + 1);

# ---------------------------------------------
# calc EMA - step 3
# 3.  EMA: {Close - EMA(previous day)} x multiplier + EMA(previous day).

def ema_calc;
if  bn == 1 then {
   ema_calc = sma;
} else {
   ema_calc = (( price - ema_calc[1] ) * multi ) + ema_calc[1];
}


plot z2 = ema_calc;
z2.SetDefaultColor(Color.yellow);
# z1.setlineweight(1);
z2.hidebubble();

# ------------
def ema_z = ema_calc;
# ---------------------------------------------

def diff_tol = 0.005;
def ema_diff = avgexp - ema_z;
def diff_big = (absvalue(ema_diff) > diff_tol);
input show_test_price_diff_from_actual = no;
def diff_ok_bn = if (diff_big[1] and !diff_big) then bn else diff_ok_bn[1];
def avg_diff_per = round((ema_diff / avgexp)*100, 3);

# test_price_diff_from_actual and diff_big
addchartbubble(show_test_price_diff_from_actual and diff_big[0], low*0.98, "bar# " + bn + "\n" + avgexp + "\n" + ema_z + "\n" + ema_diff + "\n" + avg_diff_per + "%", color.yellow, no);

addlabel(1, "calculated EMA is within $" + diff_tol + " after bar# " + diff_ok_bn, color.yellow);
#


a label shows at what barnumber, the calculated EMA is within $0.005 of the actual EMA.
a bubble lists the: barnumber, ema, calc ema, $ diff, % diff
Hd3LiTD.jpg
 
Top