<commit_before>"use strict";

var xchange = require("xchange.js")
	,async = require("async")
	,config = require("../config.js");

function BtcStats(exchanges){
	this.exchanges(exchanges);
}

BtcStats.prototype.exchanges = function(excs) {
	excs = excs || config.defaultExchanges;
	
	this._exchanges = excs.reduce(function(exchange, name, idx){
		exchange[name] = xchange[name].ticker;
		return exchange;
	}, {});
};

function tickersResp(callback) {
	async.map(Object.keys(this._exchanges), function(item, cb){
		xchange[item].ticker(function(error, resp){
			if (error || !resp) {
				return cb(error, null);
			} else if(resp) {
				resp.ticker = item;
				return cb(error, resp);
			} else {
				return cb(error, null);
			}
		});
	}, callback);
}

function midpoint(a) {
	return (a.ask + a.bid) / 2;
}

BtcStats.prototype.avg = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err && !resp) callback(err,null)
		if(!err && !resp) callback(null, null)
		var res = resp.reduce(function(a, b){
			return {
				price: (a && b) ? a.price + midpoint(b): 0   		
			};
		}, {price: 0});
		
		res.price = (res.price / resp.length).toFixed(2);
		callback(null, res);
	});
};

BtcStats.prototype.weightedAvg = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var totalVol = resp.reduce(function(a, b){
				return a + ((b.volume) ? b.volume : 0);
			}, 0);

			var res = resp.reduce(function(a, b){
				return {
					price: a.price + midpoint(b) * ((b.volume) ? b.volume : 0) / totalVol 	    		
				};
			}, {price: 0});
		
			res.price = (res.price).toFixed(2);
		    callback(null, res);
		}
	});	
};

BtcStats.prototype.min = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return (midpoint(a) < midpoint(b)) ? a : b;
			});
			
			callback(null, {price: midpoint(min).toFixed(2), exchange: min.ticker});
		}
	});
};

BtcStats.prototype.max = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return (midpoint(a) > midpoint(b)) ? a : b;
			});
			
			callback(null, {price: midpoint(max).toFixed(2), exchange: max.ticker});
		}
	});
};

BtcStats.prototype.minVolume = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return (a.volume < b.volume) ? a : b;
			});
			
			callback(null, {volume: (min.volume).toFixed(2), exchange: min.ticker});
		}
	});
};

BtcStats.prototype.maxVolume = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return (a.volume > b.volume) ? a : b;
			});
			
			callback(null, {volume: (max.volume).toFixed(2), exchange: max.ticker});
		}
	});
};

BtcStats.prototype.maxSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return (a.bid < b.bid) ? a : b;
			});
			
			var max = resp.reduce(function(a, b){
				return (a.ask > b.ask) ? a : b;
			});
			
			var spread = {spread: (max.ask - min.bid).toFixed(2), 
						  bid: min.bid, 
						  ask: max.ask, 
						  bidExchange: min.ticker, 
						  askExchange: max.ticker};
			
			callback(null, spread);
		}
	});
};

BtcStats.prototype.minSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return (a.bid > b.bid) ? a : b;
			});
			
			var min = resp.reduce(function(a, b){
				return (a.ask < b.ask) ? a : b;
			});
			
			var spread = {spread: (min.ask - max.bid).toFixed(2), 
					bid: max.bid, 
					ask: min.ask, 
					bidExchange: max.ticker, 
					askExchange: min.ticker};
			
			callback(null, spread);
		}
	});
};

BtcStats.prototype.exchangeMaxSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return ((a.ask - a.bid) > (b.ask - b.bid)) ? a : b;
			});
			
			var spread = {spread: (max.ask - max.bid).toFixed(2), 
					bid: max.bid, 
					ask: max.ask, 
					exchange: max.ticker};
			
			callback(null, spread);
		}
	});
};

BtcStats.prototype.exchangeMinSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return ((a.ask - a.bid) < (b.ask - b.bid)) ? a : b;
			});
			
			var spread = {spread: (min.ask - min.bid).toFixed(2), 
					bid: min.bid, 
					ask: min.ask, 
					exchange: min.ticker};
			
			callback(null, spread);
		}
	});
};

module.exports = new BtcStats();
<commit_msg>fixes error in midpoint calculation with NaN ask or bid<commit_after>"use strict";

var xchange = require("xchange.js")
	,async = require("async")
	,config = require("../config.js");

function BtcStats(exchanges){
	this.exchanges(exchanges);
}

BtcStats.prototype.exchanges = function(excs) {
	excs = excs || config.defaultExchanges;
	
	this._exchanges = excs.reduce(function(exchange, name, idx){
		exchange[name] = xchange[name].ticker;
		return exchange;
	}, {});
};

function tickersResp(callback) {
	async.map(Object.keys(this._exchanges), function(item, cb){
		xchange[item].ticker(function(error, resp){
			if (error || !resp) {
				return cb(error, null);
			} else if(resp) {
				resp.ticker = item;
				return cb(error, resp);
			} else {
				return cb(error, null);
			}
		});
	}, callback);
}

function midpoint(a) {
	var ask = a.ask ? a.ask : 0;
	var bid = a.bid ? a.bid : 0;
	return (ask + bid) / 2;
}

BtcStats.prototype.avg = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err && !resp) callback(err,null)
		if(!err && !resp) callback(null, null)
		var res = resp.reduce(function(a, b){
			return {
				price: (a && b) ? a.price + midpoint(b): 0   		
			};
		}, {price: 0});
		
		res.price = (res.price / resp.length).toFixed(2);
		callback(null, res);
	});
};

BtcStats.prototype.weightedAvg = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var totalVol = resp.reduce(function(a, b){
				return a + ((b.volume) ? b.volume : 0);
			}, 0);

			var res = resp.reduce(function(a, b){
				return {
					price: a.price + midpoint(b) * ((b.volume) ? b.volume : 0) / totalVol 	    		
				};
			}, {price: 0});
		
			res.price = (res.price).toFixed(2);
		    callback(null, res);
		}
	});	
};

BtcStats.prototype.min = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return (midpoint(a) < midpoint(b)) ? a : b;
			});
			
			callback(null, {price: midpoint(min).toFixed(2), exchange: min.ticker});
		}
	});
};

BtcStats.prototype.max = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return (midpoint(a) > midpoint(b)) ? a : b;
			});
			
			callback(null, {price: midpoint(max).toFixed(2), exchange: max.ticker});
		}
	});
};

BtcStats.prototype.minVolume = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return (a.volume < b.volume) ? a : b;
			});
			
			callback(null, {volume: (min.volume).toFixed(2), exchange: min.ticker});
		}
	});
};

BtcStats.prototype.maxVolume = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return (a.volume > b.volume) ? a : b;
			});
			
			callback(null, {volume: (max.volume).toFixed(2), exchange: max.ticker});
		}
	});
};

BtcStats.prototype.maxSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return (a.bid < b.bid) ? a : b;
			});
			
			var max = resp.reduce(function(a, b){
				return (a.ask > b.ask) ? a : b;
			});
			
			var spread = {spread: (max.ask - min.bid).toFixed(2), 
						  bid: min.bid, 
						  ask: max.ask, 
						  bidExchange: min.ticker, 
						  askExchange: max.ticker};
			
			callback(null, spread);
		}
	});
};

BtcStats.prototype.minSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return (a.bid > b.bid) ? a : b;
			});
			
			var min = resp.reduce(function(a, b){
				return (a.ask < b.ask) ? a : b;
			});
			
			var spread = {spread: (min.ask - max.bid).toFixed(2), 
					bid: max.bid, 
					ask: min.ask, 
					bidExchange: max.ticker, 
					askExchange: min.ticker};
			
			callback(null, spread);
		}
	});
};

BtcStats.prototype.exchangeMaxSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var max = resp.reduce(function(a, b){
				return ((a.ask - a.bid) > (b.ask - b.bid)) ? a : b;
			});
			
			var spread = {spread: (max.ask - max.bid).toFixed(2), 
					bid: max.bid, 
					ask: max.ask, 
					exchange: max.ticker};
			
			callback(null, spread);
		}
	});
};

BtcStats.prototype.exchangeMinSpread = function(callback) {
	tickersResp.call(this, function(err, resp){
		if(err){
			callback(err);
		} else {
			var min = resp.reduce(function(a, b){
				return ((a.ask - a.bid) < (b.ask - b.bid)) ? a : b;
			});
			
			var spread = {spread: (min.ask - min.bid).toFixed(2), 
					bid: min.bid, 
					ask: min.ask, 
					exchange: min.ticker};
			
			callback(null, spread);
		}
	});
};

module.exports = new BtcStats();

{'fraction_non_alphanumeric': 0.1764819837272375, 'fraction_numerical': 0.003487020534676482, 'mean_word_length': 2.552993805918789, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1598881', 'n_tokens_mistral': 4503, 'n_tokens_neox': 3758, 'n_words': 737}