# Open source FMZ Quant TA library, learn to use (with Javascript/Python/C++ versions)

Author: FMZ~Lydia, Created: 2023-07-12 15:55:21, Updated: 2024-01-03 21:04:30

# Open source FMZ Quant TA library, learn to use (with JavascriptPythonC++ versions)

The code is the best commentary, how can you say you know how to use an indicator if you don’t know how it is generated. The following code is handcrafted by myself when I transitioned from being a programmer to the finance industry, without any copy-pasting. I hope it will be helpful for beginners.

``````var Std = {
_skip: function(arr, period) {
var j = 0;
for (var k = 0; j < arr.length; j++) {
if (!isNaN(arr[j]))
k++;
if (k == period)
break;
}
return j;
},
_sum: function(arr, num) {
var sum = 0.0;
for (var i = 0; i < num; i++) {
if (!isNaN(arr[i])) {
sum += arr[i];
}
}
return sum;
},

_avg: function(arr, num) {
var n = 0;
var sum = 0.0;
for (var i = 0; i < num; i++) {
if (!isNaN(arr[i])) {
sum += arr[i];
n++;
}
}
return sum / n;
},

_zeros: function(len) {
var n = [];
for (var i = 0; i < len; i++) {
n.push(0.0);
}
return n;
},

_set: function(arr, start, end, value) {
var e = Math.min(arr.length, end);
for (var i = start; i < e; i++) {
arr[i] = value;
}
},

_diff: function(a, b) {
var d = [];
for (var i = 0; i < b.length; i++) {
if (isNaN(a[i]) || isNaN(b[i])) {
d.push(NaN);
} else {
d.push(a[i] - b[i]);
}
}
return d;
},
_move_diff: function(a) {
var d = [];
for (var i = 1; i < a.length; i++) {
d.push(a[i] - a[i - 1]);
}
return d;
},
_sma: function(S, period) {
var R = Std._zeros(S.length);
var j = Std._skip(S, period);
Std._set(R, 0, j, NaN);
if (j < S.length) {
var sum = 0;
for (var i = j; i < S.length; i++) {
if (i == j) {
sum = Std._sum(S, i + 1);
} else {
sum += S[i] - S[i - period];
}
R[i] = sum / period;
}
}
return R;
},

_smma: function(S, period) {
var R = Std._zeros(S.length);
var j = Std._skip(S, period);
Std._set(R, 0, j, NaN);
if (j < S.length) {
R[j] = Std._avg(S, j + 1);
for (var i = j + 1; i < S.length; i++) {
R[i] = (R[i - 1] * (period - 1) + S[i]) / period;
}
}
return R;
},
_ema: function(S, period) {
var R = Std._zeros(S.length);
var multiplier = 2.0 / (period + 1);
var j = Std._skip(S, period);
Std._set(R, 0, j, NaN);
if (j < S.length) {
R[j] = Std._avg(S, j + 1);
for (var i = j + 1; i < S.length; i++) {
R[i] = ((S[i] - R[i - 1]) * multiplier) + R[i - 1];
}
}
return R;
},
_cmp: function(arr, start, end, cmpFunc) {
var v = arr[start];
for (var i = start; i < end; i++) {
v = cmpFunc(arr[i], v);
}
return v;
},
_filt: function(records, n, attr, iv, cmpFunc) {
if (records.length < 2) {
return NaN;
}
var v = iv;
var pos = n !== 0 ? records.length - Math.min(records.length - 1, n) - 1 : 0;
for (var i = records.length - 2; i >= pos; i--) {
if (typeof(attr) !== 'undefined') {
v = cmpFunc(v, records[i][attr]);
} else {
v = cmpFunc(v, records[i]);
}
}
return v;
},
_ticks: function(records) {
if (records.length === 0) {
return [];
}
var ticks = [];
if (typeof(records[0].Close) !== 'undefined') {
for (var i = 0; i < records.length; i++) {
ticks.push(records[i].Close);
}
} else {
ticks = records;
}
return ticks;
},
};

var TA = {
Highest: function(records, n, attr) {
return Std._filt(records, n, attr, Number.MIN_VALUE, Math.max);
},
Lowest: function(records, n, attr) {
return Std._filt(records, n, attr, Number.MAX_VALUE, Math.min);
},

MA: function(records, period) {
period = typeof(period) === 'undefined' ? 9 : period;
return Std._sma(Std._ticks(records), period);
},
SMA: function(records, period) {
period = typeof(period) === 'undefined' ? 9 : period;
return Std._sma(Std._ticks(records), period);
},

EMA: function(records, period) {
period = typeof(period) === 'undefined' ? 9 : period;
return Std._ema(Std._ticks(records), period);
},

MACD: function(records, fastEMA, slowEMA, signalEMA) {
fastEMA = typeof(fastEMA) === 'undefined' ? 12 : fastEMA;
slowEMA = typeof(slowEMA) === 'undefined' ? 26 : slowEMA;
signalEMA = typeof(signalEMA) === 'undefined' ? 9 : signalEMA;
var ticks = Std._ticks(records);
var slow = Std._ema(ticks, slowEMA);
var fast = Std._ema(ticks, fastEMA);
// DIF
var dif = Std._diff(fast, slow);
// DEA
var signal = Std._ema(dif, signalEMA);
var histogram = Std._diff(dif, signal);
return [dif, signal, histogram];
},

BOLL: function(records, period, multiplier) {
period = typeof(period) === 'undefined' ? 20 : period;
multiplier = typeof(multiplier) === 'undefined' ? 2 : multiplier;
var S = Std._ticks(records);
for (var j = period - 1; j < S.length && isNaN(S[j]); j++);
var UP = Std._zeros(S.length);
var MB = Std._zeros(S.length);
var DN = Std._zeros(S.length);
Std._set(UP, 0, j, NaN);
Std._set(MB, 0, j, NaN);
Std._set(DN, 0, j, NaN);
var sum = 0;
for (var i = j; i < S.length; i++) {
if (i == j) {
for (var k = 0; k < period; k++) {
sum += S[k];
}
} else {
sum = sum + S[i] - S[i - period];
}
var ma = sum / period;
var d = 0;
for (var k = i + 1 - period; k <= i; k++) {
d += (S[k] - ma) * (S[k] - ma);
}
var stdev = Math.sqrt(d / period);
var up = ma + (multiplier * stdev);
var dn = ma - (multiplier * stdev);
UP[i] = up;
MB[i] = ma;
DN[i] = dn;
}
// upper, middle, lower
return [UP, MB, DN];
},

KDJ: function(records, n, k, d) {
n = typeof(n) === 'undefined' ? 9 : n;
k = typeof(k) === 'undefined' ? 3 : k;
d = typeof(d) === 'undefined' ? 3 : d;

var RSV = Std._zeros(records.length);
Std._set(RSV, 0, n - 1, NaN);
var K = Std._zeros(records.length);
var D = Std._zeros(records.length);
var J = Std._zeros(records.length);

var hs = Std._zeros(records.length);
var ls = Std._zeros(records.length);
for (var i = 0; i < records.length; i++) {
hs[i] = records[i].High;
ls[i] = records[i].Low;
}

for (var i = 0; i < records.length; i++) {
if (i >= (n - 1)) {
var c = records[i].Close;
var h = Std._cmp(hs, i - (n - 1), i + 1, Math.max);
var l = Std._cmp(ls, i - (n - 1), i + 1, Math.min);
RSV[i] = 100 * ((c - l) / (h - l));
K[i] = (1 * RSV[i] + (k - 1) * K[i - 1]) / k;
D[i] = (1 * K[i] + (d - 1) * D[i - 1]) / d;
} else {
K[i] = D[i] = 50;
RSV[i] = 0;
}
J[i] = 3 * K[i] - 2 * D[i];
}
// remove prefix
for (var i = 0; i < n - 1; i++) {
K[i] = D[i] = J[i] = NaN;
}
return [K, D, J];
},

RSI: function(records, period) {
period = typeof(period) === 'undefined' ? 14 : period;
var i;
var n = period;
var rsi = Std._zeros(records.length);
Std._set(rsi, 0, rsi.length, NaN);
if (records.length < n) {
return rsi;
}
var ticks = Std._ticks(records);
var deltas = Std._move_diff(ticks);
var seed = deltas.slice(0, n);
var up = 0;
var down = 0;
for (i = 0; i < seed.length; i++) {
if (seed[i] >= 0) {
up += seed[i];
} else {
down += seed[i];
}
}
up /= n;
down = -(down /= n);
var rs = down != 0 ? up / down : 0;
rsi[n] = 100 - 100 / (1 + rs);
var delta = 0;
var upval = 0;
var downval = 0;
for (i = n + 1; i < ticks.length; i++) {
delta = deltas[i - 1];
if (delta > 0) {
upval = delta;
downval = 0;
} else {
upval = 0;
downval = -delta;
}
up = (up * (n - 1) + upval) / n;
down = (down * (n - 1) + downval) / n;
rs = up / down;
rsi[i] = 100 - 100 / (1 + rs);
}
return rsi;
},
OBV: function(records) {
if (records.length === 0) {
return [];
}
if (typeof(records[0].Close) === 'undefined') {
throw "argument must KLine";
}
var R = [];
for (var i = 0; i < records.length; i++) {
if (i === 0) {
R[i] = records[i].Volume;
} else if (records[i].Close >= records[i - 1].Close) {
R[i] = R[i - 1] + records[i].Volume;
} else {
R[i] = R[i - 1] - records[i].Volume;
}
}
return R;
},
ATR: function(records, period) {
if (records.length === 0) {
return [];
}
if (typeof(records[0].Close) === 'undefined') {
throw "argument must KLine";
}
period = typeof(period) === 'undefined' ? 14 : period;
var R = Std._zeros(records.length);
var sum = 0;
var n = 0;
for (var i = 0; i < records.length; i++) {
var TR = 0;
if (i == 0) {
TR = records[i].High - records[i].Low;
} else {
TR = Math.max(records[i].High - records[i].Low, Math.abs(records[i].High - records[i - 1].Close), Math.abs(records[i - 1].Close - records[i].Low));
}
sum += TR;
if (i < period) {
n = sum / (i + 1);
} else {
n = (((period - 1) * n) + TR) / period;
}
R[i] = n;
}
return R;
},
Alligator: function(records, jawLength, teethLength, lipsLength) {
jawLength = typeof(jawLength) === 'undefined' ? 13 : jawLength;
teethLength = typeof(teethLength) === 'undefined' ? 8 : teethLength;
lipsLength = typeof(lipsLength) === 'undefined' ? 5 : lipsLength;
var ticks = [];
for (var i = 0; i < records.length; i++) {
ticks.push((records[i].High + records[i].Low) / 2);
}
return [
[NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN].concat(Std._smma(ticks, jawLength)), // jaw (blue)
[NaN, NaN, NaN, NaN, NaN].concat(Std._smma(ticks, teethLength)), // teeth (red)
[NaN, NaN, NaN].concat(Std._smma(ticks, lipsLength)), // lips (green)
];
},
CMF: function(records, periods) {
periods = periods || 20;
var ret = [];
var sumD = 0;
var sumV = 0;
var arrD = [];
var arrV = [];
for (var i = 0; i < records.length; i++) {
var d = (records[i].High == records[i].Low) ? 0 : (2 * records[i].Close - records[i].Low - records[i].High) / (records[i].High - records[i].Low) * records[i].Volume;
arrD.push(d);
arrV.push(records[i].Volume);
sumD += d;
sumV += records[i].Volume;
if (i >= periods) {
sumD -= arrD.shift();
sumV -= arrV.shift();
}
ret.push(sumD / sumV);
}
return ret;
}
};
``````
``````import math

class Std:
@staticmethod
def _skip(arr, period):
k = 0
for j in xrange(0, len(arr)):
if arr[j] is not None:
k+=1
if k == period:
break
return j

@staticmethod
def _sum(arr, num):
s = 0.0
for i in xrange(0, num):
if arr[i] is not None:
s += arr[i]
return s

@staticmethod
def _avg(arr, num):
if len(arr) == 0:
return 0
s = 0.0
n = 0
for i in xrange(0, min(len(arr), num)):
if arr[i] is not None:
s += arr[i]
n += 1
if n == 0:
return 0
return s / n

@staticmethod
def _zeros(n):
return [0.0] * n

@staticmethod
def _set(arr, start, end, value):
for i in xrange(start, min(len(arr), end)):
arr[i] = value

@staticmethod
def _diff(a, b):
d = [None] * len(b)
for i in xrange(0, len(b)):
if a[i] is not None and b[i] is not None:
d[i] = a[i] - b[i]
return d

@staticmethod
def _move_diff(a):
d = [None] * (len(a)-1)
for i in xrange(1, len(a)):
d[i-1] = a[i] - a[i-1]
return d

@staticmethod
def _cmp(arr, start, end, cmpFunc):
v = arr[start]
for i in xrange(start, end):
v = cmpFunc(arr[i], v)
return v

@staticmethod
def _filt(records, n, attr, iv, cmpFunc):
if len(records) < 2:
return None
v = iv
pos = 0
if n != 0:
pos = len(records) - min(len(records)-1, n) - 1
for i in xrange(len(records)-2, pos-1, -1):
if records[i] is not None:
if attr is not None:
v = cmpFunc(v, records[i][attr])
else:
v = cmpFunc(v, records[i])
return v

@staticmethod
def _ticks(records):
if len(records) == 0:
return []
if 'Close' not in records[0]:
return records

ticks = [None] * len(records)
for i in xrange(0, len(records)):
ticks[i] = records[i]['Close']
return ticks

@staticmethod
def _sma(S, period):
R = Std._zeros(len(S))
j = Std._skip(S, period)
Std._set(R, 0, j, None)
if j < len(S):
s = 0
for i in xrange(j, len(S)):
if i == j:
s = Std._sum(S, i+1)
else:
s += S[i] - S[i-period]
R[i] = s / period
return R

@staticmethod
def _smma(S, period):
R = Std._zeros(len(S))
j = Std._skip(S, period)
Std._set(R, 0, j, None)
if j < len(S):
R[j] = Std._avg(S, j+1)
for i in xrange(j+1, len(S)):
R[i] = (R[i-1] * (period-1) + S[i]) / period
return R

@staticmethod
def _ema(S, period):
R = Std._zeros(len(S))
multiplier = 2.0 / (period + 1)
j = Std._skip(S, period)
Std._set(R, 0, j, None)
if j < len(S):
R[j] = Std._avg(S, j+1)
for i in xrange(j+1, len(S)):
R[i] = ((S[i] - R[i-1] ) * multiplier) + R[i-1]
return R

class TA:
@staticmethod
def Highest(records, n, attr=None):
return Std._filt(records, n, attr, 5e-324, max)

@staticmethod
def Lowest(records, n, attr=None):
return Std._filt(records, n, attr, 1.7976931348623157e+308, min)

@staticmethod
def MA(records, period=9):
return Std._sma(Std._ticks(records), period)

@staticmethod
def SMA(records, period=9):
return Std._sma(Std._ticks(records), period)

@staticmethod
def EMA(records, period=9):
return Std._ema(Std._ticks(records), period)

@staticmethod
def MACD(records, fastEMA=12, slowEMA=26, signalEMA=9):
ticks = Std._ticks(records)
slow = Std._ema(ticks, slowEMA)
fast = Std._ema(ticks, fastEMA)
# DIF
dif = Std._diff(fast, slow)
# DEA
signal = Std._ema(dif, signalEMA)
histogram = Std._diff(dif, signal)
return [ dif, signal, histogram]

@staticmethod
def BOLL(records, period=20, multiplier=2):
S = Std._ticks(records)
j = period - 1
while j < len(S) and (S[j] is None):
j+=1
UP = Std._zeros(len(S))
MB = Std._zeros(len(S))
DN = Std._zeros(len(S))
Std._set(UP, 0, j, None)
Std._set(MB, 0, j, None)
Std._set(DN, 0, j, None)
n = 0.0
for i in xrange(j, len(S)):
if i == j:
for k in xrange(0, period):
n += S[k]
else:
n = n + S[i] - S[i - period]
ma = n / period
d = 0
for k in xrange(i+1-period, i+1):
d += (S[k] - ma) * (S[k] - ma)
stdev = math.sqrt(d / period)
up = ma + (multiplier * stdev)
dn = ma - (multiplier * stdev)
UP[i] = up
MB[i] = ma
DN[i] = dn
return [UP, MB, DN]

@staticmethod
def KDJ(records, n=9, k=3, d=3):
RSV = Std._zeros(len(records))
Std._set(RSV, 0, n - 1, None)
K = Std._zeros(len(records))
D = Std._zeros(len(records))
J = Std._zeros(len(records))

hs = Std._zeros(len(records))
ls = Std._zeros(len(records))
for i in xrange(0, len(records)):
hs[i] = records[i]['High']
ls[i] = records[i]['Low']

for i in xrange(0, len(records)):
if i >= (n - 1):
c = records[i]['Close']
h = Std._cmp(hs, i - (n - 1), i + 1, max)
l = Std._cmp(ls, i - (n - 1), i + 1, min)
RSV[i] = 100 * ((c - l) / (h - l))
K[i] = float(1 * RSV[i] + (k - 1) * K[i - 1]) / k
D[i] = float(1 * K[i] + (d - 1) * D[i - 1]) / d
else:
K[i] = D[i] = 50.0
RSV[i] = 0.0
J[i] = 3 * K[i] - 2 * D[i]
# remove prefix
for i in xrange(0, n-1):
K[i] = D[i] = J[i] = None
return [K, D, J]

@staticmethod
def RSI(records, period=14):
n = period
rsi = Std._zeros(len(records))
Std._set(rsi, 0, len(rsi), None)
if len(records) < n:
return rsi

ticks = Std._ticks(records)
deltas = Std._move_diff(ticks)
seed = deltas[:n]
up = 0.0
down = 0.0
for i in xrange(0, len(seed)):
if seed[i] >= 0:
up += seed[i]
else:
down += seed[i]
up /= n
down /= n
down = -down
if down != 0:
rs = up / down
else:
rs = 0
rsi[n] = 100 - 100 / (1 + rs)
delta = 0.0
upval = 0.0
downval = 0.0
for i in xrange(n+1, len(ticks)):
delta = deltas[i - 1]
if delta > 0:
upval = delta
downval = 0.0
else:
upval = 0.0
downval = -delta
up = (up * (n - 1) + upval) / n
down = (down * (n - 1) + downval) / n
rs = up / down
rsi[i] = 100 - 100 / (1 + rs)
return rsi
@staticmethod
def OBV(records):
if len(records) == 0:
return []

if 'Close' not in records[0]:
raise "TA.OBV argument must KLine"

R = Std._zeros(len(records))
for i in xrange(0, len(records)):
if i == 0:
R[i] = records[i]['Volume']
elif records[i]['Close'] >= records[i - 1]['Close']:
R[i] = R[i - 1] + records[i]['Volume']
else:
R[i] = R[i - 1] - records[i]['Volume']
return R

@staticmethod
def ATR(records, period=14):
if len(records) == 0:
return []
if 'Close' not in records[0]:
raise "TA.ATR argument must KLine"

R = Std._zeros(len(records))
m = 0.0
n = 0.0
for i in xrange(0, len(records)):
TR = 0
if i == 0:
TR = records[i]['High'] - records[i]['Low']
else:
TR = max(records[i]['High'] - records[i]['Low'], abs(records[i]['High'] - records[i - 1]['Close']), abs(records[i - 1]['Close'] - records[i]['Low']))
m += TR
if i < period:
n = m / (i + 1)
else:
n = (((period - 1) * n) + TR) / period
R[i] = n
return R

@staticmethod
def Alligator(records, jawLength=13, teethLength=8, lipsLength=5):
ticks = []
for i in xrange(0, len(records)):
ticks.append((records[i]['High'] + records[i]['Low']) / 2)
return [
[None]*8+Std._smma(ticks, jawLength), # // jaw (blue)
[None]*5+Std._smma(ticks, teethLength), # teeth (red)
[None]*3+Std._smma(ticks, lipsLength) # lips (green)
]

@staticmethod
def CMF(records, periods=20):
ret = []
sumD = 0.0
sumV = 0.0
arrD = []
arrV = []
for i in xrange(0, len(records)):
d = 0.0
if records[i]['High'] != records[i]['Low']:
d = (2 * records[i]['Close'] - records[i]['Low'] - records[i]['High']) / (records[i]['High'] - records[i]['Low']) * records[i]['Volume']
arrD.append(d)
arrV.append(records[i]['Volume'])
sumD += d
sumV += records[i]['Volume']
if i >= periods:
sumD -= arrD.pop(0)
sumV -= arrV.pop(0)
ret.append(sumD / sumV)
return ret
``````
``````double _cmp_min(double a, double b) {
return a < b ? a : b;
}

double _cmp_max(double a, double b) {
return a > b ? a : b;
}

double _cmp_max(double a, double b, double c) {
double d = a > b ? a : b;
return d > c ? d : c;
}

class TAHelper {
public:
array<vector<double>, 3> MACD(Records &records, size_t fastEMA = 12, size_t slowEMA = 26, size_t signalEMA = 9) {
vector<double> ticks = records.Close();
vector<double> dif = _diff(_ema(ticks, fastEMA), _ema(ticks, slowEMA));
vector<double> signal = _ema(dif, signalEMA);
vector<double> histogram = _diff(dif, signal);
return {{ dif, signal, histogram }};
}

array<vector<double>, 3> KDJ(Records &records, size_t n = 9, size_t k = 3, size_t d = 3) {
size_t length = records.size();
vector<double> RSV(length, 0);
_set(RSV, 0, n - 1, NAN);
vector<double> K(length, 0);
vector<double> D(length, 0);
vector<double> J(length, 0);

vector<double> hs = records.High();
vector<double> ls = records.Low();

for (size_t i = 0; i < length; i++) {
if (i >= size_t(n - 1)) {
double c = records[i].Close;
double h = _cmp(hs, i - (n - 1), i + 1, _cmp_max);
double l = _cmp(ls, i - (n - 1), i + 1, _cmp_min);
RSV[i] = h != l ? (100 * ((c - l) / (h - l))) : 100;
K[i] = (1 * RSV[i] + (k - 1) * K[i - 1]) / k;
D[i] = (1 * K[i] + (d - 1) * D[i - 1]) / d;
} else {
K[i] = D[i] = 50;
RSV[i] = 0;
}
J[i] = 3 * K[i] - 2 * D[i];
}
for (size_t i = 0; i < n - 1; i++) {
K[i] = D[i] = J[i] = NAN;
}
return{{ K, D, J }};
}

vector<double> RSI(Records &records, size_t period = 14) {
size_t i = 0;
size_t n = period;
vector<double> rsi(records.size(), 0);
_set(rsi, 0, rsi.size(), NAN);
if (records.size() < n) {
return rsi;
}
vector<double> ticks = records.Close();
vector<double> deltas = _move_diff(ticks);
vector<double> seed(deltas.begin(), deltas.begin() + n);
double up = 0.0;
double down = 0.0;
for (i = 0; i < seed.size(); i++) {
if (seed[i] >= 0) {
up += seed[i];
} else {
down += seed[i];
}
}
up /= n;
down /= n;
down = -down;
double rs = down != 0 ? up / down : 0;
rsi[n] = 100 - 100 / (1 + rs);
double delta = 0.0;
double upval = 0.0;
double downval = 0.0;
for (i = n + 1; i < ticks.size(); i++) {
delta = deltas[i - 1];
if (delta > 0) {
upval = delta;
downval = 0;
} else {
upval = 0;
downval = -delta;
}
up = (up * (n - 1) + upval) / n;
down = (down * (n - 1) + downval) / n;
rs = up / down;
rsi[i] = 100 - 100 / (1 + rs);
}
return rsi;
}

vector<double> ATR(Records &records, size_t period = 14) {
vector<double> ret;
if (records.size() == 0) {
return ret;
}
vector<double> R(records.size(), 0);
double sum = 0.0;
double n = 0.0;
for (size_t i = 0; i < records.size(); i++) {
double TR = 0.0;
if (i == 0) {
TR = records[i].High - records[i].Low;
} else {
TR = _cmp_max(records[i].High - records[i].Low, abs(records[i].High - records[i - 1].Close), abs(records[i - 1].Close - records[i].Low));
}
sum += TR;
if (i < period) {
n = sum / (i + 1);
} else {
n = (((period - 1) * n) + TR) / period;
}
R[i] = n;
}
return R;
}

vector<double> OBV(Records &records) {
vector<double> R;
if (records.size() == 0) {
return R;
}
for (size_t i = 0; i < records.size(); i++) {
if (i == 0) {
R.push_back(records[i].Volume);
} else if (records[i].Close >= records[i - 1].Close) {
R.push_back(R[i - 1] + records[i].Volume);
} else {
R.push_back(R[i - 1] - records[i].Volume);
}
}
return R;
}

vector<double> MA(Records &records, size_t period = 9) {
return _sma(records.Close(), period);
}

vector<double> EMA(Records &records, size_t period = 9) {
return _ema(records.Close(), period);
}

array<vector<double>, 3> BOLL(Records &records, size_t period = 20, double multiplier = 2) {
vector<double> S = records.Close();
size_t j = 0;
for (j = period - 1; j < S.size() && isnan(S[j]); j++);
vector<double> UP(S.size(), 0);
vector<double> MB(S.size(), 0);
vector<double> DN(S.size(), 0);
_set(UP, 0, j, NAN);
_set(MB, 0, j, NAN);
_set(DN, 0, j, NAN);
double sum = 0;
for (size_t i = j; i < S.size(); i++) {
if (i == j) {
for (size_t k = 0; k < period; k++) {
sum += S[k];
}
} else {
sum = sum + S[i] - S[i - period];
}
double ma = sum / period;
double d = 0.0;
for (size_t k = i + 1 - period; k <= i; k++) {
d += (S[k] - ma) * (S[k] - ma);
}
double stdev = sqrt(d / period);
double up = ma + (multiplier * stdev);
double dn = ma - (multiplier * stdev);
UP[i] = up;
MB[i] = ma;
DN[i] = dn;
}
return {{ UP, MB, DN }};
}

array<vector<double>, 3> Alligator(Records &records, size_t jawLength = 13, size_t teethLength = 8, size_t lipsLength = 5) {
vector<double> ticks;
for (size_t i = 0; i < records.size(); i++) {
ticks.push_back((records[i].High + records[i].Low) / 2);
}
vector<double> jaw = _smma(ticks, jawLength);
jaw.insert(jaw.begin(), 8, NAN);
vector<double> teeth = _smma(ticks, teethLength);
teeth.insert(teeth.begin(), 5, NAN);
vector<double> lips = _smma(ticks, lipsLength);
lips.insert(lips.begin(), 3, NAN);
return{{ jaw, teeth, lips }};
}

vector<double> CMF(Records &records, size_t periods = 20) {
vector<double> ret;
double sumD = 0.0;
double sumV = 0.0;
vector<double> arrD;
vector<double> arrV;
for (size_t i = 0; i < records.size(); i++) {
double d = (records[i].High == records[i].Low) ? 0 : (2 * records[i].Close - records[i].Low - records[i].High) / (records[i].High - records[i].Low) * records[i].Volume;
arrD.push_back(d);
arrV.push_back(records[i].Volume);
sumD += d;
sumV += records[i].Volume;
if (i >= periods) {
sumD -= arrD.front();
arrD.erase(arrD.begin());
sumV -= arrV.front();
arrV.erase(arrV.begin());
}
ret.push_back(sumD / sumV);
}
return ret;
}

double Highest(vector<double> records, size_t n) {
return _filt(records, n, NAN, _cmp_max);
}

double Lowest(vector<double> records, size_t n) {
return _filt(records, n, NAN, _cmp_min);
}

double _filt(vector<double> records, double n, double iv, double(*pfun) (double a, double b)) {
if (records.size() < 2) {
return NAN;
}
double v = iv;
double pos = n != 0 ? records.size() - _cmp_min(records.size() - 1, n) - 1 : 0;
for (size_t i = records.size() - 2; i >= pos; i--) {
v = pfun(v, records[i]);
}
return v;
}

vector<double> _smma(vector<double> S, size_t period) {
size_t length = S.size();
vector<double> R(length, 0);
size_t j = _skip(S, period);
_set(R, 0, j, NAN);
if (j < length) {
R[j] = _avg(S, j + 1);
for (size_t i = j + 1; i < length; i++) {
R[i] = (R[i - 1] * (period - 1) + S[i]) / period;
}
}
return R;
}

vector<double> _move_diff(vector<double> a) {
vector<double> d;
for (size_t i = 1; i < a.size(); i++) {
d.push_back(a[i] - a[i - 1]);
}
return d;
}

vector<double> _ema(vector<double> S, size_t period) {
size_t length = S.size();
vector<double> R(length, 0);
double multiplier = 2.0 / (period + 1);
size_t j = _skip(S, period);
_set(R, 0, j, NAN);
if (j < length) {
R[j] = _avg(S, j + 1);
for (size_t i = j + 1; i < length; i++) {
R[i] = (S[i] - R[i - 1]) * multiplier + R[i - 1];
}
}
return R;
}

vector<double> _sma(vector<double> S, size_t period) {
vector<double> R(S.size(), 0);
size_t j = _skip(S, period);
_set(R, 0, j, NAN);
if (j < S.size()) {
double sum = 0;
for (size_t i = j; i < S.size(); i++) {
if (i == j) {
sum = _sum(S, i + 1);
} else {
if (i < period) {
R[i] = NAN;
continue;
}
sum += S[i] - S[i - period];
}
R[i] = sum / period;
}
}
return R;
}

double _sum(vector<double> arr, size_t num) {
double sum = 0.0;
for (size_t i = 0; i < num; i++) {
if (!isnan(arr[i])) {
sum += arr[i];
}
}
return sum;
}

vector<double> _diff(vector<double> a, vector<double> b) {
vector<double> d;
for (size_t i = 0; i < b.size(); i++) {
if (isnan(a[i]) || isnan(b[i])) {
d.push_back(NAN);
} else {
d.push_back(a[i] - b[i]);
}
}
return d;
}

double _avg(vector<double> arr, double num) {
size_t n = 0;
double sum = 0.0;
for (size_t i = 0; i < num; i++) {
if (!isnan(arr[i])) {
sum += arr[i];
n++;
}
}
return sum / n;
}

void _set(vector<double> &arr, size_t start, size_t end, double value) {
size_t e = _cmp_min(arr.size(), end);
for (size_t i = start; i < e; i++) {
arr[i] = value;
}
}

size_t _skip(vector<double> arr, size_t period) {
size_t j = 0;
for (size_t k = 0; j < arr.size(); j++) {
if (!isnan(arr[j])) {
k++;
}
if (k == period) {
break;
}
}
return j;
}

double _cmp(vector<double> arr, size_t start, size_t end, double(*pfun) (double a, double b)) {
double v = arr[start];
for (size_t i = start; i < end; i++) {
v = pfun(arr[i], v);
}
return v;
}
};

TAHelper TA;
``````

More