169 lines
4.3 KiB
JavaScript
169 lines
4.3 KiB
JavaScript
const util = require('../../core/util.js');
|
|
const _ = require('lodash');
|
|
const moment = require('moment');
|
|
const log = require('../../core/log');
|
|
|
|
const config = util.getConfig();
|
|
|
|
const dirs = util.dirs();
|
|
|
|
const QUERY_DELAY = 350;
|
|
const BATCH_SIZE = 100;
|
|
const SCAN_ITER_SIZE = 50000;
|
|
const BATCH_ITER_SIZE = BATCH_SIZE * 10;
|
|
|
|
const Fetcher = require(dirs.exchanges + 'gdax');
|
|
const retry = require(dirs.exchanges + '../exchangeUtils').retry;
|
|
|
|
Fetcher.prototype.getTrades = function(sinceTid, callback) {
|
|
let lastScan = 0;
|
|
|
|
const handle = (err, data) => {
|
|
if (err) return callback(err);
|
|
|
|
let result = _.map(data, function(trade) {
|
|
return {
|
|
tid: trade.trade_id,
|
|
amount: parseFloat(trade.size),
|
|
date: moment.utc(trade.time).format("X"),
|
|
price: parseFloat(trade.price)
|
|
};
|
|
});
|
|
|
|
callback(null, result.reverse());
|
|
};
|
|
|
|
const fetch = cb => this.gdax_public.getProductTrades(this.pair, { after: sinceTid, limit: BATCH_SIZE }, this.processResponse('getTrades', cb));
|
|
retry(null, fetch, handle);
|
|
};
|
|
|
|
Fetcher.prototype.findFirstTrade = function(sinceTs, callback) {
|
|
let currentId = 0;
|
|
let sinceM = moment(sinceTs).utc();
|
|
|
|
log.info(`Scanning for the first trade ID to start batching requests, may take a few minutes ...`);
|
|
|
|
const handle = (err, data) => {
|
|
if (err) return callback(err);
|
|
|
|
let m = moment.utc(_.first(data).time);
|
|
let ts = m.valueOf();
|
|
if (ts < sinceTs) {
|
|
log.info(`First trade ID for batching found ${currentId - SCAN_ITER_SIZE}`);
|
|
return callback(undefined, currentId - SCAN_ITER_SIZE);
|
|
}
|
|
|
|
currentId = _.first(data).trade_id;
|
|
log.debug(`Have trade id ${currentId} for date ${_.first(data).time} ${sinceM.from(m, true)} to scan`);
|
|
|
|
let nextScanId = currentId - SCAN_ITER_SIZE;
|
|
if (nextScanId <= SCAN_ITER_SIZE) {
|
|
currentId = BATCH_ITER_SIZE;
|
|
log.info(`First trade ID for batching found ${currentId}`);
|
|
return callback(undefined, currentId);
|
|
}
|
|
|
|
setTimeout(() => {
|
|
const fetch = cb => this.gdax_public.getProductTrades(this.pair, { after: nextScanId, limit: 1 }, this.processResponse('getTrades', cb));
|
|
retry(null, fetch, handle);
|
|
}, QUERY_DELAY);
|
|
}
|
|
|
|
const fetch = cb => this.gdax_public.getProductTrades(this.pair, { limit: 1 }, this.processResponse('getTrades', cb));
|
|
retry(null, fetch, handle);
|
|
}
|
|
|
|
util.makeEventEmitter(Fetcher);
|
|
|
|
let end = false;
|
|
let done = false;
|
|
let from = false;
|
|
|
|
let batch = [];
|
|
let batchId = false; // Lowest ID for the current a batch
|
|
|
|
let lastId = false;
|
|
|
|
let latestId = false;
|
|
let latestMoment = false;
|
|
|
|
let fetcher = new Fetcher(config.watch);
|
|
|
|
let retryForever = {
|
|
forever: true,
|
|
factor: 1.2,
|
|
minTimeout: 10 * 1000,
|
|
maxTimeout: 120 * 1000
|
|
};
|
|
|
|
let fetch = () => {
|
|
fetcher.import = true;
|
|
|
|
// We are in the sub-iteration step for a given batch
|
|
if (lastId) {
|
|
setTimeout(() => {
|
|
fetcher.getTrades(lastId, handleFetch);
|
|
}, QUERY_DELAY);
|
|
}
|
|
// We are running the first query, and need to find the starting batch
|
|
else {
|
|
let process = (err, firstBatchId) => {
|
|
if (err) return handleFetch(err);
|
|
|
|
batchId = firstBatchId;
|
|
fetcher.getTrades(batchId + 1, handleFetch);
|
|
}
|
|
fetcher.findFirstTrade(from.valueOf(), process);
|
|
}
|
|
}
|
|
|
|
let handleFetch = (err, trades) => {
|
|
if (err) {
|
|
log.error(`There was an error importing from GDAX ${err}`);
|
|
fetcher.emit('done');
|
|
return fetcher.emit('trades', []);
|
|
}
|
|
|
|
if (trades.length) {
|
|
batch = trades.concat(batch);
|
|
|
|
let last = moment.unix(_.first(trades).date).utc();
|
|
lastId = _.first(trades).tid
|
|
|
|
let latestTrade = _.last(trades);
|
|
if (!latestId || latestTrade.tid > latestId) {
|
|
latestId = latestTrade.tid;
|
|
latestMoment = moment.unix(latestTrade.date).utc();
|
|
}
|
|
|
|
// still doing sub-iteration in the batch
|
|
if (lastId >= (batchId - BATCH_ITER_SIZE) && last >= from)
|
|
return fetch();
|
|
}
|
|
|
|
batchId += BATCH_ITER_SIZE;
|
|
lastId = batchId + 1;
|
|
|
|
if (latestMoment >= end) {
|
|
fetcher.emit('done');
|
|
}
|
|
|
|
let endUnix = end.unix();
|
|
let startUnix = from.unix();
|
|
batch = _.filter(batch, t => t.date >= startUnix && t.date <= endUnix);
|
|
|
|
fetcher.emit('trades', batch);
|
|
batch = [];
|
|
}
|
|
|
|
module.exports = function (daterange) {
|
|
|
|
from = daterange.from.utc().clone();
|
|
end = daterange.to.utc().clone();
|
|
|
|
return {
|
|
bus: fetcher,
|
|
fetch: fetch
|
|
}
|
|
}
|