UNPKG

gdax-assistant

Version:

A library of useful tools and classes for working with information gathered via the Coinbase-Pro (Gdax) API

604 lines (518 loc) 14.7 kB
let BandControl = require("./BandControl"); function PivotPoints(candles) { let high = candles[0].high, low = candles[0].low, close = candles[0].close; let P = (high + low + close) / 3; let R1 = P * 2 - low; let R2 = P + (high - low); let S1 = P * 2 - high; let S2 = P - (high - low); return { s1: S1, s2: S2, r1: R1, r2: R2, }; } function Sma(candles, lhoc) { let sum = 0; lhoc = lhoc === undefined ? "close" : lhoc; for (let i = 0; i < candles.length; i++) { sum += candles[i][lhoc]; } return sum / candles.length; } function Atr(candles, length, offset) { return SmoothedAtr(candles, length, offset); } function Highest(candles, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; let points = candles.map((candle) => { return candle[lhoc]; }); return max(points); } function IndexOfHighest(candles, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; let points = candles.map((candle) => { return candle[lhoc]; }); let indexOfMax = -1; let max = -Infinity; points.forEach((value, index) => { if (value >= max) { indexOfMax = index; max = value; } }); return indexOfMax; } function Lowest(candles, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; let points = candles.map((candle) => { return candle[lhoc]; }); return min(points); } function IndexOfLowest(candles, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; let points = candles.map((candle) => { return candle[lhoc]; }); let indexOfMin = -1; let min = Infinity; points.forEach((value, index) => { if (value <= min) { indexOfMin = index; min = value; } }); return indexOfMin; } function Aroon(candles) { let up = (100 * (candles.length - IndexOfHighest(candles, "high"))) / candles.length; let down = (100 * (candles.length - IndexOfLowest(candles, "low"))) / candles.length; let oscillator = up - down; return { up: up, down: down, oscillator: oscillator, }; } function Vwap(candles) { let sum = 0; let totalVolume = 0; for (let i = 0; i < candles.length; i++) { sum += ((candles[i].high + candles[i].low + candles[i].close) / 3) * candles[i].volume; totalVolume += candles[i].volume; } return sum / totalVolume; } function Rms(candles, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; let total = 0, mean = 0, squaredMean = 0; let points = candles.map((candle) => { return candle[lhoc]; }); points.forEach((value) => { total += value; }); mean = total / points.length; points.forEach((value) => { squaredMean += Math.pow(value - mean, 2); }); squaredMean /= points.length; return Math.sqrt(squaredMean); } function Roi(candles, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; return ( 100 * ((candles[0][lhoc] - candles[candles.length - 1][lhoc]) / candles[candles.length - 1][lhoc]) ); } function Ichimoku( candles, tenkanLength, kijunLength, periodLength, cloudOffset, ) { let cloudOffsetIndex = cloudOffset - 1; let tenkansen = (Highest(candles.slice(0, tenkanLength), "high") + Lowest(candles.slice(0, tenkanLength), "low")) / 2; let kijunsen = (Highest(candles.slice(0, kijunLength), "high") + Lowest(candles.slice(0, kijunLength), "low")) / 2; let senkouSpanB = (Highest( candles.slice(cloudOffsetIndex, cloudOffsetIndex + periodLength), "high", ) + Lowest( candles.slice(cloudOffsetIndex, cloudOffsetIndex + periodLength), "low", )) / 2; let oldTenkansen = (Highest( candles.slice(cloudOffsetIndex, cloudOffsetIndex + tenkanLength), "high", ) + Lowest( candles.slice(cloudOffsetIndex, cloudOffsetIndex + tenkanLength), "low", )) / 2; let oldKijunsen = (Highest( candles.slice(cloudOffsetIndex, cloudOffsetIndex + kijunLength), "high", ) + Lowest( candles.slice(cloudOffsetIndex, cloudOffsetIndex + kijunLength), "low", )) / 2; let senkouSpanA = (oldTenkansen + oldKijunsen) / 2; let forward = []; for (let i = 1; i < cloudOffset; i++) { let cofs = cloudOffsetIndex - i; let temp_senkouSpanB = (Highest(candles.slice(cofs, cofs + periodLength), "high") + Lowest(candles.slice(cofs, cofs + periodLength), "low")) / 2; let temp_oldTenkansen = (Highest(candles.slice(cofs, cofs + tenkanLength), "high") + Lowest(candles.slice(cofs, cofs + tenkanLength), "low")) / 2; let temp_oldKijunsen = (Highest(candles.slice(cofs, cofs + kijunLength), "high") + Lowest(candles.slice(cofs, cofs + kijunLength), "low")) / 2; let temp_senkouSpanA = (temp_oldTenkansen + temp_oldKijunsen) / 2; forward.push({ top: Math.max(temp_senkouSpanA, temp_senkouSpanB), bottom: Math.min(temp_senkouSpanA, temp_senkouSpanB), bullish: temp_senkouSpanA > temp_senkouSpanB, bearish: temp_senkouSpanB > temp_senkouSpanA, }); } return { cloud: { top: Math.max(senkouSpanA, senkouSpanB), bottom: Math.min(senkouSpanA, senkouSpanB), bullish: senkouSpanA > senkouSpanB, bearish: senkouSpanB > senkouSpanA, forward: forward, }, tkCross: tenkansen > kijunsen, ktCross: kijunsen > tenkansen, kijunsen: kijunsen, tenkansen: tenkansen, }; } function KnowSureThing(candles, lhoc, short, long, signal) { //need to have atleast 45 candles lhoc = lhoc === undefined ? "close" : lhoc; short = short === undefined ? 10 : short; long = long === undefined ? 15 : long; signal = signal === undefined ? 9 : signal; let cc = candles.slice(0, candles.length); let kstArray = []; for (let i = 0; i < signal; i++) { kstArray.push(_KST(cc, lhoc, short, long)); cc.shift(); } return { kst: kstArray[0], signal: average(kstArray) }; } function Rsi(candles, length, look_back) { let { avg_gains, avg_losses } = SmoothedAverageLossAndGains( candles, length, look_back, ); if (avg_gains === 0) { return 0; } if (avg_losses === 0) { return 100; } else { let rs = avg_gains / avg_losses; return 100 - 100 / (1 + rs); } } function Percentile(candles, lhoc, percentile) { lhoc = lhoc === undefined ? "close" : lhoc; let arr = candles.map((candle) => { return candle[lhoc]; }); arr.sort(); let index = Math.round(arr.length * percentile); return arr[index]; } /** * * @param {Array} candles * @param {Number} length */ function Adx(candles, length, look_back) { //get the atr let adx = 0; let atr = SmoothedAtr(candles, length, look_back); let pDi = (100 * SmoothedPositiveMovemnet(candles, length, look_back)) / atr; let nDi = (100 * SmoothedNegativeMovemnet(candles, length, look_back)) / atr; let sum = Math.abs(pDi + nDi); let dx = Math.abs(pDi - nDi) / (sum === 0 ? 1 : sum); if (candles.length > length && look_back > 0) { //keep it accurate as we reach the end let N = length; //get the previous value let prime = Adx(candles.slice(1), length, look_back - 1).dx; dx = ((N - 1) * prime + dx) / N; } adx = dx * 100; return { adx: adx, pDi: pDi, nDi: nDi, dx: dx, }; } function Ema(candles, length, look_back, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; let ret = 0; if (candles.length > length && look_back > 0) { //keep it accurate as we reach the end let N = length; let k = 2 / (N + 1); let price = candles[0][lhoc]; //get the previous value let prime = Ema(candles.slice(1), length, look_back - 1, lhoc); ret = k * (price - prime) + prime; } else { ret = Sma(candles.slice(0, length), lhoc); } return ret; } function BollingerBands(candles, stdv, lhoc) { stdv = stdv === undefined ? 2 : stdv; lhoc = lhoc === undefined ? "close" : lhoc; let sma = Sma(candles, lhoc); let rms = Rms(candles, lhoc); return { sma: sma, top: sma + rms * stdv, bottom: sma - rms * stdv, }; } function Macd(candles, shortLength, longLength, smoothing, look_back, lhoc) { let macdArray = [], long, short; for (let i = 0; i < candles.length - longLength; i++) { let c = candles.slice(i, candles.length); let _short = Ema(c, shortLength, look_back, lhoc), _long = Ema(c, longLength, look_back, lhoc); if (i === 0) { short = _short; long = _long; } macdArray.push({ macd: _short - _long, }); } let signal = Ema(macdArray, smoothing, 100, "macd"), macd = macdArray[0].macd; return { short: short, long: long, macd: macd, signal: signal, histogram: macd - signal, }; } function SrLevels(candles, smoothing, bandSize, lhoc) { lhoc = lhoc === undefined ? "close" : lhoc; smoothing = smoothing === undefined ? 10 : smoothing; bandSize = bandSize === undefined ? Atr(candles, smoothing, smoothing * 5) : bandSize; let sp = []; //smoothed points let dx_sp = []; //rate of change between each smoothed point for (let i = 0; i < candles.length - smoothing; i++) { sp.push(Sma(candles.slice(i, i + smoothing), lhoc)); if (i > 0) { dx_sp.push(sp[i - 1] - sp[i]); } } sp.pop(); //get rid of odd man out who has no rate of change let SandR = []; for (let i = 1; i < dx_sp.length; i++) { if ( (dx_sp[i] >= 0 && dx_sp[i - 1] < 0) || (dx_sp[i] <= 0 && dx_sp[i - 1] > 0) ) { SandR.push(sp[i]); } } let minVal = min(SandR); let maxVal = max(SandR); let bl = new BandControl.BandList(minVal, maxVal, bandSize); bl.analyze(SandR); return bl; } module.exports = { Adx: Adx, Atr: Atr, Aroon: Aroon, BollingerBands: BollingerBands, Ema: Ema, Highest: Highest, KST: KnowSureThing, Ichimoku: Ichimoku, IndexOfHighest: IndexOfHighest, IndexOfLowest: IndexOfLowest, Lowest: Lowest, Macd: Macd, PivotPoints: PivotPoints, Rms: Rms, Rsi: Rsi, Roi: Roi, Sma: Sma, SrLevels: SrLevels, Vwap: Vwap, Percentile: Percentile, }; function ROC(currentValue, previousValue) { return (currentValue / previousValue - 1) * 100; } function _KST(candles, lhoc, short, long) { //Calc RCMA #1 10-Period SMA of 10-period ROC let rcm1 = [], //Calc RCMA #2 10-Period SMA of 15-period ROC rcm2 = [], //Calc RCMA #3 10-Period SMA of 20-period ROC rcm3 = [], //Calc RCMA #4 15-Period SMA of 30-period ROC rcm4 = []; for (let i = 0; i < long; i++) { if (i < 10) { rcm1.push(ROC(candles[i][lhoc], candles[i + short][lhoc])); rcm2.push(ROC(candles[i][lhoc], candles[i + long][lhoc])); rcm3.push(ROC(candles[i][lhoc], candles[i + short * 2][lhoc])); } rcm4.push(ROC(candles[i][lhoc], candles[i + long * 2][lhoc])); } let rmca1 = average(rcm1), rmca2 = average(rcm2), rmca3 = average(rcm3), rmca4 = average(rcm4); return rmca1 + rmca2 * 2 + rmca3 * 3 + rmca4 * 4; } function average(array) { let total = 0; array.forEach((val) => { total += val; }); return total / array.length; } function max(array) { let max = -Infinity; array.forEach((element) => { if (element > max) { max = element; } }); return max; } function min(array) { let min = Infinity; array.forEach((element) => { if (element < min) { min = element; } }); return min; } function SmoothedAverageLossAndGains(candles, length, look_back) { let current = candles[0].close - candles[1].close; let ret = { avg_gains: current > 0 ? current : 0, avg_losses: current < 0 ? -1 * current : 0, }; //keep it accurate as we reach the end let N = length; if (candles.length > length && look_back > 0) { //get the previous value let prime = SmoothedAverageLossAndGains( candles.slice(1), length, look_back - 1, ); ret.avg_gains = ((N - 1) * prime.avg_gains + ret.avg_gains) / N; ret.avg_losses = ((N - 1) * prime.avg_losses + ret.avg_losses) / N; } else { let sumGains = 0, sumLoss = 0; for (let i = 0; i < length - 1; i++) { let diff = candles[i].close - candles[i + 1].close; if (diff > 0) { sumGains += diff; } else { sumLoss -= diff; } } ret.avg_gains = sumGains / N; ret.avg_losses = sumLoss / N; } return ret; } function SmoothedPositiveMovemnet(candles, length, look_back) { let ret = null; //get the current dm (eazy) let up_move = candles[0].high - candles[1].high; let down_move = candles[1].low - candles[0].low; let cdm = 0; if (up_move > down_move && up_move > 0) cdm = up_move; if (candles.length > length && look_back > 0) { //keep it accurate as we reach the end let N = length; //get the previous value let prime = SmoothedPositiveMovemnet( candles.slice(1), length, look_back - 1, ); ret = ((N - 1) * prime + cdm) / N; } else { //base case ret = cdm; } //Yo no me gusta multiple return values return ret; } function SmoothedNegativeMovemnet(candles, length, look_back) { let ret = null; //get the current dm (eazy) let up_move = candles[0].high - candles[1].high; let down_move = candles[1].low - candles[0].low; let cdm = 0; if (down_move > up_move && down_move > 0) cdm = down_move; if (candles.length > length && look_back > 0) { //keep it accurate as we reach the end let N = length; //get the previous value let prime = SmoothedNegativeMovemnet( candles.slice(1), length, look_back - 1, ); ret = ((N - 1) * prime + cdm) / N; } else { //base case ret = cdm; } //Yo no me gusta multiple return values return ret; } function SmoothedAtr(candles, length, look_back) { let c = candles[0]; let ret = c.tr; if (candles.length > length && look_back > 0) { let N = length; let prime = SmoothedAtr(candles.slice(1), length, look_back - 1); ret = ((N - 1) * prime + ret) / N; } return ret; }