UNPKG

ohlc-aggregator

Version:

Aggregates ohlcv values into coarse grain intervals

867 lines (824 loc) 22.5 kB
"use strict"; var expect = require("chai").expect; var ohlc_aggregate = require("../lib/index"); var moment = require("moment"); function basicTests() { it("should return null if input is null", function() { var result = ohlc_aggregate(); expect(result).to.deep.equal(null); }); it("should return [] if input length is 0", function() { var result = ohlc_aggregate([]); expect(result).to.deep.equal([]); }); it("should return an array of length 1 if input is of length 1", function() { var result = ohlc_aggregate( [ { time: 1525651200, close: 9377.81, high: 9662.23, low: 9202.13, open: 9643.99, volume: 73842.44 } ], /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { time: 1525651200, close: 9377.81, high: 9662.23, low: 9202.13, open: 9643.99, volume: 73842.44 } ]); }); } function lengthTwoTests() { it("should return an array of length 1 if input is of length 2 and one is divisible by intervalInSeconds", function() { var result = ohlc_aggregate( [ { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:01", "M/D/YYYY H:mm").valueOf(), open: 3, high: 10, low: 0, close: 2, volume: 200 } ], /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 10, low: 0, close: 2, volume: 300 } ]); }); } function lengthThreeTests() { it("should return an array of length 2 if input is of length 3 and middle one is divisible by intervalInSeconds", function() { var result = ohlc_aggregate( [ { time: moment("10/15/2017 8:59", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:01", "M/D/YYYY H:mm").valueOf(), open: 3, high: 10, low: 0, close: 6, volume: 200 } ], /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { time: moment("10/15/2017 8:55", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 10, low: 0, close: 6, volume: 300 } ]); }); it("should return an array of length 1 if input is of length 3 and last one is divisible by intervalInSeconds", function() { var result = ohlc_aggregate( [ { time: moment("10/15/2017 8:58", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 8:59", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 3, high: 10, low: 0, close: 6, volume: 200 } ], /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { time: moment("10/15/2017 8:55", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 200 }, { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 3, high: 10, low: 0, close: 6, volume: 200 } ]); }); it("should return an array of length 1 if input is of length 3 and first one is divisible by intervalInSeconds", function() { var result = ohlc_aggregate( [ { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:01", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:02", "M/D/YYYY H:mm").valueOf(), open: 3, high: 10, low: 0, close: 6, volume: 200 } ], /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 10, low: 0, close: 6, volume: 400 } ]); }); it("should return an array of length 1 if input is of length 3 and none is divisible by intervalInSeconds", function() { var result = ohlc_aggregate( [ { time: moment("10/15/2017 9:01", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 }, { time: moment("10/15/2017 9:02", "M/D/YYYY H:mm").valueOf(), open: 4, high: 16, low: 1, close: 3, volume: 100 }, { time: moment("10/15/2017 9:03", "M/D/YYYY H:mm").valueOf(), open: 3, high: 10, low: 0, close: 6, volume: 200 } ], /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 16, low: 0, close: 6, volume: 400 } ]); }); } function lengthTenTests() { it("should return an array of length 2 if input is of length 10 and first one is divisible by intervalInSeconds", function() { let data = [ { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 } ]; for (let index = 1; index < 10; index++) { let rand = Math.floor(Math.random() * 10); let newData = { time: data[0].time + index * 60 * 1000, open: data[0].open + rand, high: data[0].high + rand, low: data[0].low + rand, close: data[0].close + rand, volume: data[0].volume }; data.push(newData); } var result = ohlc_aggregate( data, /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { open: data[0].open, close: data[4].close, low: data .slice(0, 5) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(0, 5) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data.slice(0, 5).reduce((acm, e) => e.volume + acm, 0), time: data[0].time }, { open: data[5].open, close: data[9].close, low: data .slice(5, 10) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(5, 10) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data.slice(5, 10).reduce((acm, e) => e.volume + acm, 0), time: data[5].time } ]); }); it("should return an array of length 3 if input is of length 10 and first one is one after the aggregate time", function() { let data = [ { time: moment("10/15/2017 9:01", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 } ]; for (let index = 1; index < 10; index++) { let rand = Math.floor(Math.random() * 10); let newData = { time: data[0].time + index * 60 * 1000, open: data[0].open + rand, high: data[0].high + rand, low: data[0].low + rand, close: data[0].close + rand, volume: data[0].volume }; data.push(newData); } var result = ohlc_aggregate( data, /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { open: data[0].open, close: data[3].close, low: data .slice(0, 4) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(0, 4) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data.slice(0, 4).reduce((acm, e) => e.volume + acm, 0), time: moment("10/15/2017 9:0", "M/D/YYYY H:mm").valueOf() }, { open: data[4].open, close: data[8].close, low: data .slice(4, 9) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(4, 9) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data.slice(4, 9).reduce((acm, e) => e.volume + acm, 0), time: data[4].time }, data[9] ]); }); it("should return an array of length 3 if input is of length 10 and first one is one before the aggregate time", function() { let data = [ { time: moment("10/15/2017 8:59", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 } ]; for (let index = 1; index < 10; index++) { let rand = Math.floor(Math.random() * 10); let newData = { time: data[0].time + index * 60 * 1000, open: data[0].open + rand, high: data[0].high + rand, low: data[0].low + rand, close: data[0].close + rand, volume: data[0].volume }; data.push(newData); } var result = ohlc_aggregate( data, /*intervalRatio=*/ 5, /*intervalInSeconds=*/ 5 * 60 ); expect(result).to.deep.equal([ { open: data[0].open, high: data[0].high, low: data[0].low, close: data[0].close, volume: data[0].volume, time: moment("10/15/2017 8:55", "M/D/YYYY H:mm").valueOf() }, { open: data[1].open, close: data[5].close, low: data .slice(1, 6) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(1, 6) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data.slice(1, 6).reduce((acm, e) => e.volume + acm, 0), time: data[1].time }, { open: data[6].open, close: data[9].close, low: data .slice(6, 10) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(6, 10) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data.slice(6, 10).reduce((acm, e) => e.volume + acm, 0), time: data[6].time } ]); }); } //----------------------------------------------------------------------------- describe("#OHLC-Aggregate-1m-to-5m", function() { basicTests(); lengthTwoTests(); lengthThreeTests(); lengthTenTests(); }); describe("#OHLC-Aggregate-1m-to-10m", function() { it("should return an array of length 2 if input is of length 10 and first one is divisible by intervalInSeconds", function() { let intervalRatio = 10; let data = [ { time: moment("10/15/2017 9:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 } ]; for (let index = 1; index < intervalRatio * 2; index++) { let rand = Math.floor(Math.random() * 10); let newData = { time: data[0].time + index * 60 * 1000, open: data[0].open + rand, high: data[0].high + rand, low: data[0].low + rand, close: data[0].close + rand, volume: data[0].volume }; data.push(newData); } var result = ohlc_aggregate( data, /*intervalRatio=*/ intervalRatio, /*intervalInSeconds=*/ intervalRatio * 60 ); expect(result).to.deep.equal([ { open: data[0].open, close: data[intervalRatio - 1].close, low: data .slice(0, intervalRatio) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(0, intervalRatio) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data .slice(0, intervalRatio) .reduce((acm, e) => e.volume + acm, 0), time: data[0].time }, { open: data[intervalRatio].open, close: data[2 * intervalRatio - 1].close, low: data .slice(intervalRatio, intervalRatio * 2) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(intervalRatio, intervalRatio * 2) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data .slice(intervalRatio, intervalRatio * 2) .reduce((acm, e) => e.volume + acm, 0), time: data[intervalRatio].time } ]); }); }); describe("#OHLC-Aggregate-1h-to-4h", function() { it("should return an array of length 2 if input is of length 8 and first one is divisible by intervalInSeconds", function() { let intervalRatio = 4; let data = [ { time: moment("10/15/2017 8:00", "M/D/YYYY H:mm").valueOf(), open: 1, high: 5, low: 1, close: 2, volume: 100 } ]; for (let index = 1; index < intervalRatio * 2; index++) { let rand = Math.floor(Math.random() * 10); let newData = { time: data[0].time + index * 60 * 60 * 1000, open: data[0].open + rand, high: data[0].high + rand, low: data[0].low + rand, close: data[0].close + rand, volume: data[0].volume }; data.push(newData); } var result = ohlc_aggregate( data, /*intervalRatio=*/ intervalRatio, /*intervalInSeconds=*/ intervalRatio * 60 * 60 ); expect(result).to.deep.equal([ { open: data[0].open, close: data[intervalRatio - 1].close, low: data .slice(0, intervalRatio) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(0, intervalRatio) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data .slice(0, intervalRatio) .reduce((acm, e) => e.volume + acm, 0), time: data[0].time }, { open: data[intervalRatio].open, close: data[2 * intervalRatio - 1].close, low: data .slice(intervalRatio, intervalRatio * 2) .reduce((acm, e) => Math.min(e.low, acm), Number.MAX_SAFE_INTEGER), high: data .slice(intervalRatio, intervalRatio * 2) .reduce((acm, e) => Math.max(e.high, acm), Number.MIN_SAFE_INTEGER), volume: data .slice(intervalRatio, intervalRatio * 2) .reduce((acm, e) => e.volume + acm, 0), time: data[intervalRatio].time } ]); }); }); // describe("#OHLC-Aggregate-1m-to-5m-cc", function() { // console.log("Salam"); // it("should return an array of length 2 if input is of length 8 and first one is divisible by intervalInSeconds", async function() { // // let data = await cc.histoMinute("BTC", "USD", { limit: 10 }); // // let dataAgg = await cc.histoMinute("BTC", "USD", { limit: 2, aggregate: "5" }); // let data1; // let data1Agg; // let data = []; // let dataAgg = []; // let exchange; // try { // exchange = new ccxt["bitfinex"](); // } catch (error) { // console.log("Error: ", error); // } // const min = 60 * 1000; // let since = exchange.milliseconds() - 20 * min; // let sinceAgg = exchange.milliseconds() - 20 * min; // try { // data1 = await exchange.fetchOHLCV(`BTC/USD`, "1m", since, 10); // } catch (error) { // console.log("Error: ", error); // } // try { // data1Agg = await exchange.fetchOHLCV(`BTC/USD`, "5m", sinceAgg, 2); // } catch (error) { // console.log("Error: ", error); // } // for (const d of data1) { // data.push({ // time: d[0], // open: d[1], // high: d[2], // low: d[3], // close: d[4], // volume: d[5] // }); // } // for (const d of data1Agg) { // dataAgg.push({ // time: d[0], // open: d[1], // high: d[2], // low: d[3], // close: d[4], // volume: d[5] // }); // } // // for (const d of dataAgg) { // // d["volume"]= d.volumeto; // // d["time"]= d.time * 1000; // // } // let intervalRatio = 5; // console.log("data: ", JSON.stringify(data, null, 2)); // console.log("dataAgg: ", JSON.stringify(dataAgg, null, 2)); // var result = ohlc_aggregate( // data, // /*intervalRatio=*/ intervalRatio, // /*intervalInSeconds=*/ intervalRatio * 60 // ); // expect(result).to.deep.equal(dataAgg); // }); // }); describe("#OHLC-Aggregate-1m-to-5m-ccxt-sample", function() { it("should return an array of length 3 for this sample", function() { let data = [ { time: 1567361880000, open: 9626.981274, high: 9626.981274, low: 9626.9, close: 9626.9, volume: 0.94270682 }, { time: 1567361940000, open: 9626.9, high: 9626.90964075, low: 9626.9, close: 9626.90964075, volume: 0.45494175 }, { time: 1567362000000, open: 9626.9, high: 9626.90963142, low: 9616.1, close: 9616.3, volume: 1.01075833 }, { time: 1567362060000, open: 9624.9, high: 9626.7, low: 9624.9, close: 9626.040496, volume: 0.9088545299999999 }, { time: 1567362120000, open: 9626, high: 9626, low: 9622.1, close: 9622.1, volume: 0.195106 }, { time: 1567362180000, open: 9622.6, high: 9624.1, low: 9622, close: 9624.1, volume: 0.21193979999999998 }, { time: 1567362240000, open: 9623.2, high: 9624.81764813, low: 9622.2, close: 9622.2, volume: 0.19835183 }, { time: 1567362300000, open: 9623.2, high: 9624.81764813, low: 9622.2, close: 9622.2, volume: 0.19835183 }, { time: 1567362360000, open: 9623.2, high: 9625.9, low: 9623.2, close: 9625.9, volume: 0.01 }, { time: 1567362420000, open: 9622.1, high: 9628.8, low: 9622.1, close: 9628.8, volume: 3.65199 }, { time: 1567362480000, open: 9628.8, high: 9628.8, low: 9628.7, close: 9628.75605418, volume: 0.0211 } ]; let expectedData = [ { close: 9626.90964075, high: 9626.981274, low: 9626.9, open: 9626.981274, time: 1567361700000, volume: 1.3976485699999999 }, { time: 1567362000000, open: 9626.9, high: 9626.90963142, low: 9616.1, close: 9622.2, volume: 2.52501049 }, { time: 1567362300000, open: 9623.2, high: 9628.8, low: 9622.1, close: 9628.75605418, volume: 3.8814418300000004 } ]; let intervalRatio = 5; var result = ohlc_aggregate( data, /*intervalRatio=*/ intervalRatio, /*intervalInSeconds=*/ intervalRatio * 60 ); // console.log("result: ", JSON.stringify(result, null, 2)); expect(result).to.deep.equal(expectedData); }); }); describe("#OHLC-Aggregate-1m-to-2m-ccxt-sample", function() { it("should convert 1m to 2m", function() { let data = [ { time: 1563625680000, open: 0.00024824, high: 0.00024851, low: 0.00024798, close: 0.00024831, volume: 2264 }, { time: 1563625740000, open: 0.00024817, high: 0.00024832, low: 0.00024795, close: 0.00024828, volume: 3145 }, { time: 1563625800000, open: 0.00024824, high: 0.00024831, low: 0.00024789, close: 0.00024825, volume: 2956 }, { time: 1563625860000, open: 0.00024829, high: 0.00024841, low: 0.0002479, close: 0.00024841, volume: 3742 } ]; let expectedData = [ { open: 0.00024824, close: 0.00024828, low: 0.00024795, high: 0.00024851, volume: 5409, time: 1563625680000 }, { open: 0.00024824, close: 0.00024841, low: 0.00024789, high: 0.00024841, volume: 6698, time: 1563625800000 } ]; let intervalRatio = 2; var result = ohlc_aggregate( data, /*intervalRatio=*/ intervalRatio, /*intervalInSeconds=*/ intervalRatio * 60 ); // console.log("result: ", JSON.stringify(result, null, 2)); expect(result).to.deep.equal(expectedData); }); });