mirror of https://github.com/BTCPrivate/z-nomp.git
Merge pull request #232 from damianblack/add-bittrex-api
Add Bittrex API for profitability checks
This commit is contained in:
commit
81a0536606
|
@ -107,6 +107,7 @@
|
||||||
"depth": 0.90,
|
"depth": 0.90,
|
||||||
"usePoloniex": true,
|
"usePoloniex": true,
|
||||||
"useCryptsy": true,
|
"useCryptsy": true,
|
||||||
"useMintpal": true
|
"useMintpal": true,
|
||||||
|
"useBittrex": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
var request = require('request');
|
||||||
|
var nonce = require('nonce');
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Module dependencies
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
var version = '0.1.0',
|
||||||
|
PUBLIC_API_URL = 'https://bittrex.com/api/v1/public',
|
||||||
|
PRIVATE_API_URL = 'https://bittrex.com/api/v1/market',
|
||||||
|
USER_AGENT = 'nomp/node-open-mining-portal'
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
function Bittrex(key, secret){
|
||||||
|
// Generate headers signed by this user's key and secret.
|
||||||
|
// The secret is encapsulated and never exposed
|
||||||
|
this._getPrivateHeaders = function(parameters){
|
||||||
|
var paramString, signature;
|
||||||
|
|
||||||
|
if (!key || !secret){
|
||||||
|
throw 'Bittrex: Error. API key and secret required';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort parameters alphabetically and convert to `arg1=foo&arg2=bar`
|
||||||
|
paramString = Object.keys(parameters).sort().map(function(param){
|
||||||
|
return encodeURIComponent(param) + '=' + encodeURIComponent(parameters[param]);
|
||||||
|
}).join('&');
|
||||||
|
|
||||||
|
signature = crypto.createHmac('sha512', secret).update(paramString).digest('hex');
|
||||||
|
|
||||||
|
return {
|
||||||
|
Key: key,
|
||||||
|
Sign: signature
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a site uses non-trusted SSL certificates, set this value to false
|
||||||
|
Bittrex.STRICT_SSL = true;
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
function joinCurrencies(currencyA, currencyB){
|
||||||
|
return currencyA + '-' + currencyB;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prototype
|
||||||
|
Bittrex.prototype = {
|
||||||
|
constructor: Bittrex,
|
||||||
|
|
||||||
|
// Make an API request
|
||||||
|
_request: function(options, callback){
|
||||||
|
if (!('headers' in options)){
|
||||||
|
options.headers = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
options.headers['User-Agent'] = USER_AGENT;
|
||||||
|
options.json = true;
|
||||||
|
options.strictSSL = Bittrex.STRICT_SSL;
|
||||||
|
|
||||||
|
request(options, function(err, response, body) {
|
||||||
|
callback(err, body);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Make a public API request
|
||||||
|
_public: function(parameters, callback){
|
||||||
|
var options = {
|
||||||
|
method: 'GET',
|
||||||
|
url: PUBLIC_API_URL,
|
||||||
|
qs: parameters
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._request(options, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Make a private API request
|
||||||
|
_private: function(parameters, callback){
|
||||||
|
var options;
|
||||||
|
|
||||||
|
parameters.nonce = nonce();
|
||||||
|
options = {
|
||||||
|
method: 'POST',
|
||||||
|
url: PRIVATE_API_URL,
|
||||||
|
form: parameters,
|
||||||
|
headers: this._getPrivateHeaders(parameters)
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._request(options, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/////
|
||||||
|
|
||||||
|
|
||||||
|
// PUBLIC METHODS
|
||||||
|
|
||||||
|
getTicker: function(callback){
|
||||||
|
var options = {
|
||||||
|
method: 'GET',
|
||||||
|
url: PUBLIC_API_URL + '/getmarketsummaries',
|
||||||
|
qs: null
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._request(options, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
// getBuyOrderBook: function(currencyA, currencyB, callback){
|
||||||
|
// var options = {
|
||||||
|
// method: 'GET',
|
||||||
|
// url: PUBLIC_API_URL + '/orders/' + currencyB + '/' + currencyA + '/BUY',
|
||||||
|
// qs: null
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return this._request(options, callback);
|
||||||
|
// },
|
||||||
|
|
||||||
|
getOrderBook: function(currencyA, currencyB, callback){
|
||||||
|
var parameters = {
|
||||||
|
market: joinCurrencies(currencyA, currencyB),
|
||||||
|
type: 'buy',
|
||||||
|
depth: '50'
|
||||||
|
}
|
||||||
|
var options = {
|
||||||
|
method: 'GET',
|
||||||
|
url: PUBLIC_API_URL + '/getorderbook',
|
||||||
|
qs: parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._request(options, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
getTradeHistory: function(currencyA, currencyB, callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'returnTradeHistory',
|
||||||
|
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._public(parameters, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/////
|
||||||
|
|
||||||
|
|
||||||
|
// PRIVATE METHODS
|
||||||
|
|
||||||
|
myBalances: function(callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'returnBalances'
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._private(parameters, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
myOpenOrders: function(currencyA, currencyB, callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'returnOpenOrders',
|
||||||
|
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._private(parameters, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
myTradeHistory: function(currencyA, currencyB, callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'returnTradeHistory',
|
||||||
|
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._private(parameters, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
buy: function(currencyA, currencyB, rate, amount, callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'buy',
|
||||||
|
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||||
|
rate: rate,
|
||||||
|
amount: amount
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._private(parameters, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
sell: function(currencyA, currencyB, rate, amount, callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'sell',
|
||||||
|
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||||
|
rate: rate,
|
||||||
|
amount: amount
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._private(parameters, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelOrder: function(currencyA, currencyB, orderNumber, callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'cancelOrder',
|
||||||
|
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||||
|
orderNumber: orderNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._private(parameters, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
withdraw: function(currency, amount, address, callback){
|
||||||
|
var parameters = {
|
||||||
|
command: 'withdraw',
|
||||||
|
currency: currency,
|
||||||
|
amount: amount,
|
||||||
|
address: address
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._private(parameters, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Bittrex;
|
||||||
|
}();
|
|
@ -7,6 +7,7 @@ var util = require('stratum-pool/lib/util.js');
|
||||||
var Cryptsy = require('./apiCryptsy.js');
|
var Cryptsy = require('./apiCryptsy.js');
|
||||||
var Poloniex = require('./apiPoloniex.js');
|
var Poloniex = require('./apiPoloniex.js');
|
||||||
var Mintpal = require('./apiMintpal.js');
|
var Mintpal = require('./apiMintpal.js');
|
||||||
|
var Bittrex = require('./apiBittrex.js');
|
||||||
var Stratum = require('stratum-pool');
|
var Stratum = require('stratum-pool');
|
||||||
|
|
||||||
module.exports = function(logger){
|
module.exports = function(logger){
|
||||||
|
@ -77,6 +78,11 @@ module.exports = function(logger){
|
||||||
// 'API_SECRET'
|
// 'API_SECRET'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var bittrexApi = new Bittrex(
|
||||||
|
// 'API_KEY',
|
||||||
|
// 'API_SECRET'
|
||||||
|
);
|
||||||
|
|
||||||
//
|
//
|
||||||
// market data collection from Poloniex
|
// market data collection from Poloniex
|
||||||
//
|
//
|
||||||
|
@ -393,6 +399,118 @@ module.exports = function(logger){
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.getProfitDataBittrex = function(callback){
|
||||||
|
async.series([
|
||||||
|
function(taskCallback){
|
||||||
|
bittrexApi.getTicker(function(err, response){
|
||||||
|
if (err || !response.result){
|
||||||
|
taskCallback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(symbolToAlgorithmMap).forEach(function(symbol){
|
||||||
|
response.result.forEach(function(market){
|
||||||
|
var exchangeInfo = profitStatus[symbolToAlgorithmMap[symbol]][symbol].exchangeInfo;
|
||||||
|
if (!exchangeInfo.hasOwnProperty('Bittrex'))
|
||||||
|
exchangeInfo['Bittrex'] = {};
|
||||||
|
|
||||||
|
var marketData = exchangeInfo['Bittrex'];
|
||||||
|
var marketPair = market.MarketName.match(/([\w]+)-([\w-_]+)/)
|
||||||
|
market.exchange = marketPair[1]
|
||||||
|
market.code = marketPair[2]
|
||||||
|
if (market.exchange == 'BTC' && market.code == symbol) {
|
||||||
|
if (!marketData.hasOwnProperty('BTC'))
|
||||||
|
marketData['BTC'] = {};
|
||||||
|
|
||||||
|
marketData['BTC'].last = new Number(market.Last);
|
||||||
|
marketData['BTC'].baseVolume = new Number(market.BaseVolume);
|
||||||
|
marketData['BTC'].quoteVolume = new Number(market.BaseVolume / market.Last);
|
||||||
|
marketData['BTC'].ask = new Number(market.Ask);
|
||||||
|
marketData['BTC'].bid = new Number(market.Bid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (market.exchange == 'LTC' && market.code == symbol) {
|
||||||
|
if (!marketData.hasOwnProperty('LTC'))
|
||||||
|
marketData['LTC'] = {};
|
||||||
|
|
||||||
|
marketData['LTC'].last = new Number(market.Last);
|
||||||
|
marketData['LTC'].baseVolume = new Number(market.BaseVolume);
|
||||||
|
marketData['LTC'].quoteVolume = new Number(market.BaseVolume / market.Last);
|
||||||
|
marketData['LTC'].ask = new Number(market.Ask);
|
||||||
|
marketData['LTC'].bid = new Number(market.Bid);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
taskCallback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(taskCallback){
|
||||||
|
var depthTasks = [];
|
||||||
|
Object.keys(symbolToAlgorithmMap).forEach(function(symbol){
|
||||||
|
var marketData = profitStatus[symbolToAlgorithmMap[symbol]][symbol].exchangeInfo['Bittrex'];
|
||||||
|
if (marketData.hasOwnProperty('BTC') && marketData['BTC'].bid > 0){
|
||||||
|
depthTasks.push(function(callback){
|
||||||
|
_this.getMarketDepthFromBittrex('BTC', symbol, marketData['BTC'].bid, callback)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (marketData.hasOwnProperty('LTC') && marketData['LTC'].bid > 0){
|
||||||
|
depthTasks.push(function(callback){
|
||||||
|
_this.getMarketDepthFromBittrex('LTC', symbol, marketData['LTC'].bid, callback)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!depthTasks.length){
|
||||||
|
taskCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
async.series(depthTasks, function(err){
|
||||||
|
if (err){
|
||||||
|
taskCallback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
taskCallback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function(err){
|
||||||
|
if (err){
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.getMarketDepthFromBittrex = function(symbolA, symbolB, coinPrice, callback){
|
||||||
|
bittrexApi.getOrderBook(symbolA, symbolB, function(err, response){
|
||||||
|
if (err){
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var depth = new Number(0);
|
||||||
|
if (response.hasOwnProperty('result')){
|
||||||
|
var totalQty = new Number(0);
|
||||||
|
response['result'].forEach(function(order){
|
||||||
|
var price = new Number(order.Rate);
|
||||||
|
var limit = new Number(coinPrice * portalConfig.profitSwitch.depth);
|
||||||
|
var qty = new Number(order.Quantity);
|
||||||
|
// only measure the depth down to configured depth
|
||||||
|
if (price >= limit){
|
||||||
|
depth += (qty * price);
|
||||||
|
totalQty += qty;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var marketData = profitStatus[symbolToAlgorithmMap[symbolB]][symbolB].exchangeInfo['Bittrex'];
|
||||||
|
marketData[symbolA].depth = depth;
|
||||||
|
if (totalQty > 0)
|
||||||
|
marketData[symbolA].weightedBid = new Number(depth / totalQty);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.getCoindDaemonInfo = function(callback){
|
this.getCoindDaemonInfo = function(callback){
|
||||||
var daemonTasks = [];
|
var daemonTasks = [];
|
||||||
Object.keys(profitStatus).forEach(function(algo){
|
Object.keys(profitStatus).forEach(function(algo){
|
||||||
|
@ -525,6 +643,9 @@ module.exports = function(logger){
|
||||||
if (portalConfig.profitSwitch.useMintpal)
|
if (portalConfig.profitSwitch.useMintpal)
|
||||||
profitabilityTasks.push(_this.getProfitDataMintpal);
|
profitabilityTasks.push(_this.getProfitDataMintpal);
|
||||||
|
|
||||||
|
if (portalConfig.profitSwitch.useBittrex)
|
||||||
|
profitabilityTasks.push(_this.getProfitDataBittrex);
|
||||||
|
|
||||||
profitabilityTasks.push(_this.getCoindDaemonInfo);
|
profitabilityTasks.push(_this.getCoindDaemonInfo);
|
||||||
profitabilityTasks.push(_this.getMiningRate);
|
profitabilityTasks.push(_this.getMiningRate);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue