crypto-nodes
Version:
394 lines (269 loc) • 8.87 kB
JavaScript
var md5 = require('md5');
var data_cache = {};
var moment = require('moment');
var last_write = {
minutes: -1,
hours: -1,
days: -1,
}
module.exports = function(RED) {
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function upsertData(model, data, callback) {
model.update({ _id: data._id }, { $set: data }, { upsert: true }, function(err,msg) {
if (err) {
console.log(err);
}
if (msg) {
if(callback) {
callback();
}
}
});
}
function getLineFormat(type, frequency, data, now) {
// TODO::
// Implement input of data type to pass via config
var line;
switch(type) {
default:
case 'crypto':
line = {
_id: getDataCode(type, frequency, data, now),
symbol: data.FROMSYMBOL + '/' + data.TOSYMBOL,
// TODO: Eliminate
//symbol_from: data.FROMSYMBOL,
//symbol_to: data.TOSYMBOL,
last_price: data.PRICE,
exchange: data.EXCHANGE,
provider: data.PROVIDER,
dir: data.DIR,
};
break;
}
return line;
}
function getDataModel(config, frequency) {
var dataModel;
if(config.model_name && app.models[config.model_name]) {
return app.models[config.model_name];
}
switch(frequency) {
default:
case 'latest':
dataModel = app.models.CryptoDataLatest;
break;
case 'minute':
dataModel = app.models.CryptoDataMinute;
break;
case 'hour':
dataModel = app.models.CryptoDataHour;
break;
case 'day':
dataModel = app.models.CryptoDataDay;
break;
}
return dataModel;
}
function getDataCode(type, frequency, data, now) {
var dataCode = false;
switch(type) {
default:
case 'crypto':
switch(frequency) {
default:
case 'latest':
dataCode = data.EXCHANGE + '/' + data.FROMSYMBOL + '/' + data.TOSYMBOL;
break;
case 'minute':
case 'hour':
case 'day':
if(!now) {
now = moment();
}
dataCode = now.unix() + '/' + data.EXCHANGE + '/' + data.FROMSYMBOL + '/' + data.TOSYMBOL;
}
break;
}
if(!dataCode) {
dataCode = uuidv4();
}
return md5(dataCode);
}
function cache_update(model, code, data) {
}
function cache_dump(config, now, frequency) {
//var model =
var data = data_cache[frequency];
var write_count = 0;
console.log(config);
console.log(JSON.stringify(data_cache));
for(var type in data) {
var tmp = type.split('/');
var module_name = tmp[0];
var model_name = tmp[1];
var model = getDataModel({ model_name: model_name }, frequency);
for(var provider in data[type]) {
for(var exchange in data[type][provider]) {
for(var symbol in data[type][provider][exchange]) {
var line = data[type][provider][exchange][symbol];
line.provider = provider;
line.exchange = exchange;
line.symbol = symbol;
line._id = md5(now.unix() + provider + exchange + symbol);
write_count++;
upsertData(model, line, function () {
//delete data_cache[frequency][type][provider][exchange][symbol];
});
}
}
}
delete data_cache[frequency];
// Handle data retention
if(config.model_name == model_name && config.module_name == module_name && config.retainer != 'forever') {
var delete_before = 0;
switch(config.retainer) {
case 'hour':
delete_before = moment().subtract(1, "hours");
break;
case 'day':
delete_before = moment().subtract(1, "days");
break;
case 'week':
delete_before = moment().subtract(7, "days");
break;
case 'month':
delete_before = moment().subtract(1, "months");
break;
}
model.deleteMany({ ts: { $lte: delete_before.unix() } }, function (err) {
console.log('DataWriter :: Delete of data before ' + delete_before.format("DD-MM-YYYY hh:mm:ss") + ' completed.');
});
}
//console.log(module_name);
//console.log(model_name);
//console.log(model);
//var id = getDataCode(data[symbol].type, frequency, data[symbol], now);
}
console.log('DataWriter :: Wrote ' + write_count + ' records');
config.node.status({fill: 'green', shape:"dot", text: moment().format("hh:mm:ss") + ' : Wrote ' + write_count + ' records' });
}
function system_tick(config) {
// console.log('Data dump tick');
var now = moment();
if(last_write.minutes != now.minutes() && config.frequency == 'minute') {
if(last_write.minutes == -1) {
} else {
cache_dump(config, now, 'minute');
}
last_write.minutes = now.minutes();
}
if(last_write.hours != now.hours() && config.frequency == 'hour') {
if(last_write.hours == -1) {
} else {
cache_dump(config, now, 'hour');
}
last_write.hours = now.hours();
}
if(last_write.days != now.days() && config.frequency == 'day') {
if(last_write.days == -1) {
} else {
cache_dump(config, now, 'day');
}
last_write.days = now.days();
}
}
function providerDataWriter(config) {
RED.nodes.createNode(this,config);
config.node = this;
setInterval(function () {
system_tick(config);
}, 1000);
// TODO:: Export controls
config.debug = true;
config.type = 'crypto';
config.setModel = false;
var node = this;
node.status({fill:"red",shape:"dot", text:"waiting for data"});
node.on('input', function(msg) {
var color = 'gray';
if(msg.DIR == 'up') {
color = 'green';
}
if(msg.DIR == 'down') {
color = 'red';
}
var dataModel = config.setModel || getDataModel(config, config.frequency);
var dataLine = getLineFormat(config.module_name, config.frequency, msg);
dataLine.type = config.module_name + '/' + config.model_name;
if(config.frequency == 'latest') {
// Not needed in data if specific model per type is set
delete dataLine.type;
upsertData(dataModel, dataLine, function () {
/*
if(dataLine.exchange == 'CCCAGG' && dataLine.symbol == 'BTC/USD') {
console.log('Latest update for CCCAGG ::');
console.log({
ts: new moment().unix(),
min: dataLine.last_price,
max: dataLine.last_price,
val: dataLine.last_price,
});
}
*/
if(config.debug) {
var text = (" " + dataLine.exchange).slice(-10) + ' ' + dataLine.symbol + ' ' + dataLine.last_price;
node.status({fill: color,shape:"dot", text: text });
}
});
} else {
if(!data_cache[config.frequency]) {
data_cache[config.frequency] = {};
}
if(!data_cache[config.frequency][dataLine.type]) {
data_cache[config.frequency][dataLine.type] = {};
}
if(!data_cache[config.frequency][dataLine.type][dataLine.provider]) {
data_cache[config.frequency][dataLine.type][dataLine.provider] = {};
}
if(!data_cache[config.frequency][dataLine.type][dataLine.provider][dataLine.exchange]) {
data_cache[config.frequency][dataLine.type][dataLine.provider][dataLine.exchange] = {};
}
if(!data_cache[config.frequency][dataLine.type][dataLine.provider][dataLine.exchange][dataLine.symbol]) {
// Create cache data for symbol
data_cache[config.frequency][dataLine.type][dataLine.provider][dataLine.exchange][dataLine.symbol] = {
ts: new moment().unix(),
min: dataLine.last_price,
max: dataLine.last_price,
val: dataLine.last_price,
}
/*
if(dataLine.exchange == 'CCCAGG' && dataLine.symbol == 'BTC/USD') {
console.log('Cache update for CCCAGG ::');
console.log({
ts: new moment().unix(),
min: dataLine.last_price,
max: dataLine.last_price,
val: dataLine.last_price,
});
}
*/
} else {
// Update cache data with min/max values + latest
var newLine = data_cache[config.frequency][dataLine.type][dataLine.provider][dataLine.exchange][dataLine.symbol];
newLine.min = Math.min(dataLine.last_price, newLine.min);
newLine.max = Math.max(dataLine.last_price, newLine.max);
newLine.val = dataLine.last_price;
}
}
});
}
RED.nodes.registerType("providerDataWriter",providerDataWriter);
RED.httpAdmin.get("/writerConfig", function(req,res) {
res.json({ models: Object.keys(global.app.models), modules: Object.keys(global.app.modules) });
});
}