// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Electrified (electrifiedtrading)
// @version=6
// @description Contains utilities for generating moving average values including getting a moving average by name and a function for generating a Volume-Adjusted WMA.
library('MovingAverages', true)
isLenInvalid(simple int len, simple string name = 'len') =>
if na(len)
runtime.error("The '"+name+"' param cannot be NA.")
true
else if len < 0
runtime.error("The '"+name+"' param cannot be negative.")
true
else if len == 0
true
else
false
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// VAWMA = VWMA and WMA combined.
// Simply put, this attempts to determine the average price per share over time weighted heavier for recent values.
// Uses triangular algorithm to taper off values in the past (same as WMA does).
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////
// @function VAWMA = VWMA and WMA combined. Simply put, this attempts to determine the average price per share over time weighted heavier for recent values. Uses a triangular algorithm to taper off values in the past (same as WMA does).
// @param len The number of bars to measure with.
// @param src The series to measure from. Default is 'hlc3'.
// @param volumeDefault The default value to use when a chart has no (N/A) volume.
// @returns The volume adjusted triangular weighted moving average of the series.
export vawma(
simple int len,
series float src = hlc3,
simple float volumeDefault = na,
simple int startingWeight = 1) =>
var float notAvailable = na
if isLenInvalid(len)
notAvailable
else
sum = 0.0
vol = 0.0
last = len - 1
for i = 0 to last
s = src[i]
v = volume[i]
if na(v) and not na(volumeDefault)
v := volumeDefault
if not na(s) and not na(v)
m = last - i + startingWeight
v *= m
vol += v
sum += s * v
vol == 0 ? na : sum/vol
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// @function Coefficient Moving Avereage (CMA) is a variation of a moving average that can simulate SMA or WMA with the advantage of previous data.
// @param n The number of bars to measure with.
// @param D The series to measure from. Default is 'close'.
// @param C The coefficient to use when averaging. 0 behaves like SMA, 1 behaves like WMA.
// @param compound When true (default is false) will use a compounding method for weighting the average.
export cma(
simple int n,
series float D = close,
simple float C = 1,
simple bool compound = false) =>
var float notAvailable = na
if isLenInvalid(n, 'n')
notAvailable
else
var float result = na
sum = 0.0
weight = 1.0
weightTotal = 0.0
for m = 1 to n
s = D[n - m]
if not na(s)
sum := sum + s * weight
weightTotal += weight
if compound
weight *= 1 + C
else
weight += C
prev = result[n]
if na(prev)
result := sum / weightTotal
else
result := (prev + sum) / (weightTotal + 1)
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// @function Same as ta.ema(src,len) but properly ignores NA values.
// @param len The number of samples to derive the average from.
// @param src The series to measure from. Default is 'close'.
export ema(
simple int len,
series float src = close) =>
var float notAvailable = na
if isLenInvalid(len)
notAvailable
else
var float alpha = 2 / (len + 1)
var float a1 = 1 - alpha
var float s = na
var float ema = na
if not na(src)
s := src
ema := alpha * s + a1 * nz(ema)
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// @function Same as ta.wma(src,len) but properly ignores NA values.
// @param len The number of samples to derive the average from.
// @param src The series to measure from. Default is 'close'.
// @param startingWeight The weight to begin with when calculating the average. Higher numbers will decrease the bias.
export wma(
simple int len,
series float src = close,
simple int startingWeight = 1) =>
var float notAvailable = na
if isLenInvalid(len)
notAvailable
else
sum = 0.0
total = 0
last = len - 1
for i = 0 to last
s = src[i]
if not na(s)
m = last - i + startingWeight // triangular multiple
total += m
sum += s * m
total == 0 ? na : sum/total
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// @function Same as ta.vwma(src,len) but properly ignores NA values.
// @param len The number of bars to measure with.
// @param src The series to measure from. Default is 'hlc3'.
// @param volumeDefault The default value to use when a chart has no (N/A) volume.
export vwma(
simple int len,
series float src = hlc3,
simple float volumeDefault = na) =>
var float notAvailable = na
if isLenInvalid(len)
notAvailable
else
var sum = 0.0
var vol = 0.0
float datum = na
v = volume
if na(v) and not na(volumeDefault)
v := volumeDefault
if not na(src) and not na(v)
vol += v
datum := src * v
sum += datum
else
v := na
vStart = v[len]
dStart = datum[len]
if not na(vStart)
vol -= vStart
if not na(dStart)
sum -= dStart
vol == 0 ? na : sum/vol
///////////////////////////////////////////////////
type VWAP
float sum
float volume
float value
vwap(float sum, float vol, float ratio = 1) =>
VWAP.new(ratio * sum, ratio * vol, sum/vol)
sum(VWAP a, VWAP b, float ratio = 1) =>
s = a.sum + b.sum
v = a.volume + b.volume
vwap(s, v, ratio)
import Electrified/Time/7
///////////////////////////////////////////////////
// @function This is experimental moving average doesn't use a period/length but instead buffers the price per share and transfers that price per share at a given ratio per bar while also releasing the previous values at a decay ratio.
// @param transferRatio The ratio at which buffered data is applied to the average.
// @param releaseRatio The ratio at which data is released from the average.
// @param useTime When true will tend to make the values consistent across timeframes.
// @param src The series to measure from. Default is 'hlc3'.
// @param vol The series to represent volume. The default is 'volume'.
export rsvwma(
simple float transferRatio,
simple float releaseRatio,
simple bool useTime,
series float src = hlc3,
float vol = volume) =>
var retainRatio = 1 - releaseRatio
var minutesPerBar = Time.bar() / 1000 / 60
var m = useTime ? minutesPerBar : 1
var r = math.pow(1 - transferRatio, m)
var active = VWAP.new(0, 0, 0)
var surplus = VWAP.new(0, 0, 0)
s = surplus.sum + nz(src * vol)
v = surplus.volume + (na(src) ? 0 : nz(vol))
transfer = vwap(s, v, 1 - r)
surplus := vwap(s, v, r)
active := sum(active, transfer, math.pow(retainRatio, m))
active.value
///////////////////////////////////////////////////
const string SMA = 'SMA'
const string EMA = 'EMA'
const string WMA = 'WMA'
const string CMA = 'CMA'
const string VWMA = 'VWMA'
const string VAWMA = 'VAWMA'
///////////////////////////////////////////////////
// Moving Average Types
///////////////////////////////////////////////////
export enum Type
SMA = 'SMA'
EMA = 'EMA'
WMA = 'WMA'
CMA = 'CMA'
VWMA = 'VWMA'
VAWMA = 'VAWMA'
///////////////////////////////////////////////////
// @function Generates a moving average based upon a 'type'.
// @param type The type of moving average to generate. Values allowed are: SMA, EMA, WMA, VWMA and VAWMA.
// @param len The number of bars to measure with.
// @param src The series to measure from. Default is 'close'.
// @returns The moving average series requested.
export get(
simple string type,
simple int len,
series float src = close) =>
switch type
WMA => wma(len, src)
EMA => ema(len, src)
VWMA => vwma(len, src)
VAWMA => vawma(len, src)
CMA => cma(len, src)
SMA => ta.sma(src, len)
=>
runtime.error("No matching MA type found.")
na
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// @function Generates a moving average based upon a 'type' (enum overload).
// @param type The type of moving average to generate as a Type enum. Values allowed are: Type.SMA, Type.EMA, Type.WMA, Type.VWMA, Type.VAWMA, Type.CMA.
// @param len The number of bars to measure with.
// @param src The series to measure from. Default is 'close'.
// @returns The moving average series requested.
export get(
simple Type type,
simple int len,
series float src = close) =>
get(str.tostring(type), len, src)
///////////////////////////////////////////////////
getLevels(float[] values) =>
avg = math.abs(array.avg(values))
stdev = array.stdev(values)
[avg + stdev, stdev]
///////////////////////////////////////////////////
// @function The slope of the source (change over time) is measured over the mLen to determine what is considered 'normal'. A positive value less than 1 is considered within 1 standard deviation. A value less than 2 is within 2 standard deviations and so-on. This allows for the slope of a value to be standardized regardless of the symbol.
// @param source The series (typically a moving average) to meaasue the slope.
// @param mlen The number of bars to measure the standard deviation of the slope.
// @param slopeLen The number of bars to measure the slope. A higher number will smooth out the curve.
// @returns The nromalized value of the slope.
export normalizeSlope(series float source, simple int mLen = 400, simple int slopeLen = 1) =>
var pos = array.new_float()
var neg = array.new_float()
change = ta.change(source, slopeLen)
if(change > 0)
array.push(pos, change)
if array.size(pos) > mLen
array.shift(pos)
if(change < 0)
array.push(neg, change)
if array.size(neg) > mLen
array.shift(neg)
[pos_level1, pos_stdev] = getLevels(pos)
[neg_level1, neg_stdev] = getLevels(neg)
if change > 0
if(change < pos_level1)
change / pos_level1
else
(change - pos_level1) / pos_stdev + 1
else if change < 0
if(change > -neg_level1)
change / neg_level1
else
(change + neg_level1) / neg_stdev - 1
else
0
///////////////////////////////////////////////////
// Demo
plot(ema(20), 'ema(20)', color.red)
plot(wma(20), 'wma(20)', color.yellow)
plot(vwma(20), 'vwma(20)', color.blue)
plot(vawma(20), 'vawma(20)', color.purple)
Show all 320 lines