2014-04-19 16:08:52 -07:00
var async = require ( 'async' ) ;
var net = require ( 'net' ) ;
var bignum = require ( 'bignum' ) ;
var algos = require ( 'stratum-pool/lib/algoProperties.js' ) ;
var util = require ( 'stratum-pool/lib/util.js' ) ;
2014-04-13 09:44:12 -07:00
2014-04-19 16:08:52 -07:00
var Cryptsy = require ( './apiCryptsy.js' ) ;
2014-04-13 09:44:12 -07:00
var Poloniex = require ( './apiPoloniex.js' ) ;
2014-04-19 16:08:52 -07:00
var Mintpal = require ( './apiMintpal.js' ) ;
2014-05-27 14:05:12 -07:00
var Bittrex = require ( './apiBittrex.js' ) ;
2014-04-19 16:08:52 -07:00
var Stratum = require ( 'stratum-pool' ) ;
2014-04-13 09:44:12 -07:00
module . exports = function ( logger ) {
var _this = this ;
var portalConfig = JSON . parse ( process . env . portalConfig ) ;
var poolConfigs = JSON . parse ( process . env . pools ) ;
var logSystem = 'Profit' ;
2014-04-13 09:46:06 -07:00
//
// build status tracker for collecting coin market information
//
var profitStatus = { } ;
2014-04-16 18:53:40 -07:00
var symbolToAlgorithmMap = { } ;
2014-04-13 09:44:12 -07:00
Object . keys ( poolConfigs ) . forEach ( function ( coin ) {
var poolConfig = poolConfigs [ coin ] ;
2014-04-13 16:17:40 -07:00
var algo = poolConfig . coin . algorithm ;
if ( ! profitStatus . hasOwnProperty ( algo ) ) {
profitStatus [ algo ] = { } ;
}
var coinStatus = {
name : poolConfig . coin . name ,
symbol : poolConfig . coin . symbol ,
difficulty : 0 ,
reward : 0 ,
2014-04-16 18:53:40 -07:00
exchangeInfo : { }
2014-04-13 16:17:40 -07:00
} ;
profitStatus [ algo ] [ poolConfig . coin . symbol ] = coinStatus ;
2014-04-16 18:53:40 -07:00
symbolToAlgorithmMap [ poolConfig . coin . symbol ] = algo ;
2014-04-13 09:46:06 -07:00
} ) ;
//
// ensure we have something to switch
//
Object . keys ( profitStatus ) . forEach ( function ( algo ) {
2014-04-17 15:55:54 -07:00
if ( Object . keys ( profitStatus [ algo ] ) . length <= 1 ) {
delete profitStatus [ algo ] ;
2014-04-18 05:48:59 -07:00
Object . keys ( symbolToAlgorithmMap ) . forEach ( function ( symbol ) {
if ( symbolToAlgorithmMap [ symbol ] === algo )
delete symbolToAlgorithmMap [ symbol ] ;
} ) ;
2014-04-13 16:17:40 -07:00
}
2014-04-13 09:46:06 -07:00
} ) ;
2014-04-17 15:55:54 -07:00
if ( Object . keys ( profitStatus ) . length == 0 ) {
2014-04-13 16:17:40 -07:00
logger . debug ( logSystem , 'Config' , 'No alternative coins to switch to in current config, switching disabled.' ) ;
return ;
2014-04-13 09:46:06 -07:00
}
//
// setup APIs
//
var poloApi = new Poloniex (
2014-04-13 09:44:12 -07:00
// 'API_KEY',
2014-04-13 16:17:40 -07:00
// 'API_SECRET'
2014-04-13 09:46:06 -07:00
) ;
2014-04-16 18:53:40 -07:00
var cryptsyApi = new Cryptsy (
// 'API_KEY',
// 'API_SECRET'
) ;
2014-04-16 19:42:38 -07:00
var mintpalApi = new Mintpal (
// 'API_KEY',
// 'API_SECRET'
) ;
2014-04-13 09:44:12 -07:00
2014-05-27 14:05:12 -07:00
var bittrexApi = new Bittrex (
2014-05-28 07:17:32 -07:00
// 'API_KEY',
2014-05-27 14:05:12 -07:00
// 'API_SECRET'
) ;
2014-04-13 09:46:06 -07:00
//
// market data collection from Poloniex
//
2014-04-13 09:44:12 -07:00
this . getProfitDataPoloniex = function ( callback ) {
2014-04-13 16:17:40 -07:00
async . series ( [
function ( taskCallback ) {
poloApi . getTicker ( function ( err , data ) {
if ( err ) {
taskCallback ( err ) ;
return ;
}
2014-04-16 18:53:40 -07:00
Object . keys ( symbolToAlgorithmMap ) . forEach ( function ( symbol ) {
var exchangeInfo = profitStatus [ symbolToAlgorithmMap [ symbol ] ] [ symbol ] . exchangeInfo ;
2014-04-18 05:48:59 -07:00
if ( ! exchangeInfo . hasOwnProperty ( 'Poloniex' ) )
exchangeInfo [ 'Poloniex' ] = { } ;
var marketData = exchangeInfo [ 'Poloniex' ] ;
2014-04-13 16:17:40 -07:00
if ( data . hasOwnProperty ( 'BTC_' + symbol ) ) {
2014-04-18 05:48:59 -07:00
if ( ! marketData . hasOwnProperty ( 'BTC' ) )
marketData [ 'BTC' ] = { } ;
2014-04-16 18:53:40 -07:00
2014-04-18 05:48:59 -07:00
var btcData = data [ 'BTC_' + symbol ] ;
2014-04-16 18:53:40 -07:00
marketData [ 'BTC' ] . ask = new Number ( btcData . lowestAsk ) ;
marketData [ 'BTC' ] . bid = new Number ( btcData . highestBid ) ;
marketData [ 'BTC' ] . last = new Number ( btcData . last ) ;
marketData [ 'BTC' ] . baseVolume = new Number ( btcData . baseVolume ) ;
marketData [ 'BTC' ] . quoteVolume = new Number ( btcData . quoteVolume ) ;
2014-04-13 16:17:40 -07:00
}
if ( data . hasOwnProperty ( 'LTC_' + symbol ) ) {
2014-04-18 05:48:59 -07:00
if ( ! marketData . hasOwnProperty ( 'LTC' ) )
marketData [ 'LTC' ] = { } ;
2014-04-13 16:17:40 -07:00
2014-04-18 05:48:59 -07:00
var ltcData = data [ 'LTC_' + symbol ] ;
2014-04-16 18:53:40 -07:00
marketData [ 'LTC' ] . ask = new Number ( ltcData . lowestAsk ) ;
marketData [ 'LTC' ] . bid = new Number ( ltcData . highestBid ) ;
marketData [ 'LTC' ] . last = new Number ( ltcData . last ) ;
marketData [ 'LTC' ] . baseVolume = new Number ( ltcData . baseVolume ) ;
marketData [ 'LTC' ] . quoteVolume = new Number ( ltcData . quoteVolume ) ;
2014-04-13 16:17:40 -07:00
}
2014-04-18 05:48:59 -07:00
// save LTC to BTC exchange rate
2014-04-17 15:55:54 -07:00
if ( marketData . hasOwnProperty ( 'LTC' ) && data . hasOwnProperty ( 'BTC_LTC' ) ) {
2014-04-18 05:48:59 -07:00
var btcLtc = data [ 'BTC_LTC' ] ;
2014-04-17 15:55:54 -07:00
marketData [ 'LTC' ] . ltcToBtc = new Number ( btcLtc . highestBid ) ;
2014-04-18 05:48:59 -07:00
}
2014-04-13 16:17:40 -07:00
} ) ;
2014-04-17 15:55:54 -07:00
2014-04-13 16:17:40 -07:00
taskCallback ( ) ;
} ) ;
} ,
function ( taskCallback ) {
var depthTasks = [ ] ;
2014-04-16 18:53:40 -07:00
Object . keys ( symbolToAlgorithmMap ) . forEach ( function ( symbol ) {
var marketData = profitStatus [ symbolToAlgorithmMap [ symbol ] ] [ symbol ] . exchangeInfo [ 'Poloniex' ] ;
if ( marketData . hasOwnProperty ( 'BTC' ) && marketData [ 'BTC' ] . bid > 0 ) {
depthTasks . push ( function ( callback ) {
2014-04-18 05:48:59 -07:00
_this . getMarketDepthFromPoloniex ( 'BTC' , symbol , marketData [ 'BTC' ] . bid , callback )
} ) ;
2014-04-16 18:53:40 -07:00
}
if ( marketData . hasOwnProperty ( 'LTC' ) && marketData [ 'LTC' ] . bid > 0 ) {
depthTasks . push ( function ( callback ) {
2014-04-18 05:48:59 -07:00
_this . getMarketDepthFromPoloniex ( 'LTC' , symbol , marketData [ 'LTC' ] . bid , callback )
} ) ;
2014-04-13 16:17:40 -07:00
}
} ) ;
2014-04-16 18:53:40 -07:00
if ( ! depthTasks . length ) {
taskCallback ( ) ;
2014-04-13 16:17:40 -07:00
return ;
}
2014-04-16 18:53:40 -07:00
async . series ( depthTasks , function ( err ) {
2014-04-13 16:17:40 -07:00
if ( err ) {
taskCallback ( err ) ;
return ;
}
taskCallback ( ) ;
} ) ;
}
] , function ( err ) {
if ( err ) {
2014-04-13 09:46:06 -07:00
callback ( err ) ;
2014-04-13 16:17:40 -07:00
return ;
}
callback ( null ) ;
} ) ;
2014-04-13 09:44:12 -07:00
} ;
this . getMarketDepthFromPoloniex = function ( symbolA , symbolB , coinPrice , callback ) {
2014-04-13 16:17:40 -07:00
poloApi . getOrderBook ( symbolA , symbolB , function ( err , data ) {
if ( err ) {
callback ( err ) ;
return ;
}
2014-04-13 09:44:12 -07:00
var depth = new Number ( 0 ) ;
2014-04-17 15:55:54 -07:00
var totalQty = new Number ( 0 ) ;
2014-04-13 16:17:40 -07:00
if ( data . hasOwnProperty ( 'bids' ) ) {
data [ 'bids' ] . forEach ( function ( order ) {
var price = new Number ( order [ 0 ] ) ;
2014-04-18 05:48:59 -07:00
var limit = new Number ( coinPrice * portalConfig . profitSwitch . depth ) ;
2014-04-13 16:17:40 -07:00
var qty = new Number ( order [ 1 ] ) ;
// only measure the depth down to configured depth
2014-04-16 18:53:40 -07:00
if ( price >= limit ) {
2014-04-13 16:17:40 -07:00
depth += ( qty * price ) ;
2014-04-18 05:48:59 -07:00
totalQty += qty ;
2014-04-13 16:17:40 -07:00
}
} ) ;
}
2014-04-16 18:53:40 -07:00
var marketData = profitStatus [ symbolToAlgorithmMap [ symbolB ] ] [ symbolB ] . exchangeInfo [ 'Poloniex' ] ;
marketData [ symbolA ] . depth = depth ;
2014-04-18 05:48:59 -07:00
if ( totalQty > 0 )
2014-04-17 15:55:54 -07:00
marketData [ symbolA ] . weightedBid = new Number ( depth / totalQty ) ;
2014-04-13 09:44:12 -07:00
callback ( ) ;
2014-04-13 16:17:40 -07:00
} ) ;
2014-04-13 09:46:06 -07:00
} ;
2014-04-13 09:44:12 -07:00
2014-04-16 18:53:40 -07:00
2014-04-13 09:44:12 -07:00
this . getProfitDataCryptsy = function ( callback ) {
2014-04-16 18:53:40 -07:00
async . series ( [
function ( taskCallback ) {
cryptsyApi . getTicker ( function ( err , data ) {
if ( err || data . success != 1 ) {
taskCallback ( err ) ;
return ;
}
Object . keys ( symbolToAlgorithmMap ) . forEach ( function ( symbol ) {
var exchangeInfo = profitStatus [ symbolToAlgorithmMap [ symbol ] ] [ symbol ] . exchangeInfo ;
2014-04-18 05:48:59 -07:00
if ( ! exchangeInfo . hasOwnProperty ( 'Cryptsy' ) )
exchangeInfo [ 'Cryptsy' ] = { } ;
2014-04-16 18:53:40 -07:00
2014-04-18 05:48:59 -07:00
var marketData = exchangeInfo [ 'Cryptsy' ] ;
var results = data . return . markets ;
2014-04-16 18:53:40 -07:00
2014-04-17 15:55:54 -07:00
if ( results && results . hasOwnProperty ( symbol + '/BTC' ) ) {
2014-04-18 05:48:59 -07:00
if ( ! marketData . hasOwnProperty ( 'BTC' ) )
marketData [ 'BTC' ] = { } ;
2014-04-16 18:53:40 -07:00
2014-04-18 05:48:59 -07:00
var btcData = results [ symbol + '/BTC' ] ;
2014-04-16 18:53:40 -07:00
marketData [ 'BTC' ] . last = new Number ( btcData . lasttradeprice ) ;
marketData [ 'BTC' ] . baseVolume = new Number ( marketData [ 'BTC' ] . last / btcData . volume ) ;
marketData [ 'BTC' ] . quoteVolume = new Number ( btcData . volume ) ;
2014-04-18 05:48:59 -07:00
if ( btcData . sellorders != null )
2014-04-16 18:53:40 -07:00
marketData [ 'BTC' ] . ask = new Number ( btcData . sellorders [ 0 ] . price ) ;
2014-04-18 05:48:59 -07:00
if ( btcData . buyorders != null ) {
2014-04-16 18:53:40 -07:00
marketData [ 'BTC' ] . bid = new Number ( btcData . buyorders [ 0 ] . price ) ;
2014-04-18 05:48:59 -07:00
var limit = new Number ( marketData [ 'BTC' ] . bid * portalConfig . profitSwitch . depth ) ;
2014-04-16 18:53:40 -07:00
var depth = new Number ( 0 ) ;
2014-04-17 15:55:54 -07:00
var totalQty = new Number ( 0 ) ;
2014-04-16 18:53:40 -07:00
btcData [ 'buyorders' ] . forEach ( function ( order ) {
var price = new Number ( order . price ) ;
var qty = new Number ( order . quantity ) ;
if ( price >= limit ) {
depth += ( qty * price ) ;
2014-04-18 05:48:59 -07:00
totalQty += qty ;
2014-04-16 18:53:40 -07:00
}
2014-04-18 05:48:59 -07:00
} ) ;
2014-04-16 18:53:40 -07:00
marketData [ 'BTC' ] . depth = depth ;
2014-04-18 05:48:59 -07:00
if ( totalQty > 0 )
2014-04-17 15:55:54 -07:00
marketData [ 'BTC' ] . weightedBid = new Number ( depth / totalQty ) ;
2014-04-18 05:48:59 -07:00
}
}
2014-04-16 18:53:40 -07:00
2014-04-17 15:55:54 -07:00
if ( results && results . hasOwnProperty ( symbol + '/LTC' ) ) {
2014-04-18 05:48:59 -07:00
if ( ! marketData . hasOwnProperty ( 'LTC' ) )
marketData [ 'LTC' ] = { } ;
2014-04-16 18:53:40 -07:00
2014-04-18 05:48:59 -07:00
var ltcData = results [ symbol + '/LTC' ] ;
2014-04-16 18:53:40 -07:00
marketData [ 'LTC' ] . last = new Number ( ltcData . lasttradeprice ) ;
marketData [ 'LTC' ] . baseVolume = new Number ( marketData [ 'LTC' ] . last / ltcData . volume ) ;
marketData [ 'LTC' ] . quoteVolume = new Number ( ltcData . volume ) ;
2014-04-18 05:48:59 -07:00
if ( ltcData . sellorders != null )
2014-04-16 18:53:40 -07:00
marketData [ 'LTC' ] . ask = new Number ( ltcData . sellorders [ 0 ] . price ) ;
2014-04-18 05:48:59 -07:00
if ( ltcData . buyorders != null ) {
2014-04-16 18:53:40 -07:00
marketData [ 'LTC' ] . bid = new Number ( ltcData . buyorders [ 0 ] . price ) ;
2014-04-18 05:48:59 -07:00
var limit = new Number ( marketData [ 'LTC' ] . bid * portalConfig . profitSwitch . depth ) ;
2014-04-16 18:53:40 -07:00
var depth = new Number ( 0 ) ;
2014-04-17 15:55:54 -07:00
var totalQty = new Number ( 0 ) ;
2014-04-16 18:53:40 -07:00
ltcData [ 'buyorders' ] . forEach ( function ( order ) {
var price = new Number ( order . price ) ;
var qty = new Number ( order . quantity ) ;
if ( price >= limit ) {
depth += ( qty * price ) ;
2014-04-18 05:48:59 -07:00
totalQty += qty ;
2014-04-16 18:53:40 -07:00
}
2014-04-18 05:48:59 -07:00
} ) ;
2014-04-16 18:53:40 -07:00
marketData [ 'LTC' ] . depth = depth ;
2014-04-18 05:48:59 -07:00
if ( totalQty > 0 )
2014-04-17 15:55:54 -07:00
marketData [ 'LTC' ] . weightedBid = new Number ( depth / totalQty ) ;
2014-04-18 05:48:59 -07:00
}
2014-04-16 18:53:40 -07:00
}
} ) ;
taskCallback ( ) ;
} ) ;
}
] , function ( err ) {
if ( err ) {
callback ( err ) ;
return ;
}
callback ( null ) ;
} ) ;
2014-04-13 09:44:12 -07:00
} ;
2014-04-16 18:53:40 -07:00
2014-04-16 19:42:38 -07:00
this . getProfitDataMintpal = function ( callback ) {
async . series ( [
function ( taskCallback ) {
mintpalApi . getTicker ( function ( err , response ) {
2014-04-17 15:55:54 -07:00
if ( err || ! response . data ) {
2014-04-16 19:42:38 -07:00
taskCallback ( err ) ;
return ;
}
2014-04-18 05:48:59 -07:00
Object . keys ( symbolToAlgorithmMap ) . forEach ( function ( symbol ) {
response . data . forEach ( function ( market ) {
var exchangeInfo = profitStatus [ symbolToAlgorithmMap [ symbol ] ] [ symbol ] . exchangeInfo ;
if ( ! exchangeInfo . hasOwnProperty ( 'Mintpal' ) )
exchangeInfo [ 'Mintpal' ] = { } ;
var marketData = exchangeInfo [ 'Mintpal' ] ;
if ( market . exchange == 'BTC' && market . code == symbol ) {
if ( ! marketData . hasOwnProperty ( 'BTC' ) )
marketData [ 'BTC' ] = { } ;
marketData [ 'BTC' ] . last = new Number ( market . last _price ) ;
marketData [ 'BTC' ] . baseVolume = new Number ( market [ '24hvol' ] ) ;
marketData [ 'BTC' ] . quoteVolume = new Number ( market [ '24hvol' ] / market . last _price ) ;
marketData [ 'BTC' ] . ask = new Number ( market . top _ask ) ;
marketData [ 'BTC' ] . bid = new Number ( market . top _bid ) ;
}
if ( market . exchange == 'LTC' && market . code == symbol ) {
if ( ! marketData . hasOwnProperty ( 'LTC' ) )
marketData [ 'LTC' ] = { } ;
marketData [ 'LTC' ] . last = new Number ( market . last _price ) ;
marketData [ 'LTC' ] . baseVolume = new Number ( market [ '24hvol' ] ) ;
marketData [ 'LTC' ] . quoteVolume = new Number ( market [ '24hvol' ] / market . last _price ) ;
marketData [ 'LTC' ] . ask = new Number ( market . top _ask ) ;
marketData [ 'LTC' ] . bid = new Number ( market . top _bid ) ;
}
} ) ;
} ) ;
2014-04-16 19:42:38 -07:00
taskCallback ( ) ;
} ) ;
2014-04-16 20:29:37 -07:00
} ,
function ( taskCallback ) {
var depthTasks = [ ] ;
Object . keys ( symbolToAlgorithmMap ) . forEach ( function ( symbol ) {
var marketData = profitStatus [ symbolToAlgorithmMap [ symbol ] ] [ symbol ] . exchangeInfo [ 'Mintpal' ] ;
if ( marketData . hasOwnProperty ( 'BTC' ) && marketData [ 'BTC' ] . bid > 0 ) {
depthTasks . push ( function ( callback ) {
2014-04-18 05:48:59 -07:00
_this . getMarketDepthFromMintpal ( 'BTC' , symbol , marketData [ 'BTC' ] . bid , callback )
} ) ;
2014-04-16 20:29:37 -07:00
}
if ( marketData . hasOwnProperty ( 'LTC' ) && marketData [ 'LTC' ] . bid > 0 ) {
depthTasks . push ( function ( callback ) {
2014-04-18 05:48:59 -07:00
_this . getMarketDepthFromMintpal ( 'LTC' , symbol , marketData [ 'LTC' ] . bid , callback )
} ) ;
2014-04-16 20:29:37 -07:00
}
} ) ;
if ( ! depthTasks . length ) {
taskCallback ( ) ;
return ;
}
async . series ( depthTasks , function ( err ) {
if ( err ) {
taskCallback ( err ) ;
return ;
}
taskCallback ( ) ;
} ) ;
2014-04-16 19:42:38 -07:00
}
] , function ( err ) {
if ( err ) {
callback ( err ) ;
return ;
}
callback ( null ) ;
} ) ;
2014-04-16 20:29:37 -07:00
} ;
this . getMarketDepthFromMintpal = function ( symbolA , symbolB , coinPrice , callback ) {
mintpalApi . getBuyOrderBook ( symbolA , symbolB , function ( err , response ) {
if ( err ) {
callback ( err ) ;
return ;
}
var depth = new Number ( 0 ) ;
if ( response . hasOwnProperty ( 'data' ) ) {
2014-04-17 15:55:54 -07:00
var totalQty = new Number ( 0 ) ;
2014-04-16 20:29:37 -07:00
response [ 'data' ] . forEach ( function ( order ) {
var price = new Number ( order . price ) ;
2014-04-18 05:48:59 -07:00
var limit = new Number ( coinPrice * portalConfig . profitSwitch . depth ) ;
2014-04-16 20:29:37 -07:00
var qty = new Number ( order . amount ) ;
// only measure the depth down to configured depth
if ( price >= limit ) {
depth += ( qty * price ) ;
2014-04-18 05:48:59 -07:00
totalQty += qty ;
2014-04-16 20:29:37 -07:00
}
} ) ;
}
var marketData = profitStatus [ symbolToAlgorithmMap [ symbolB ] ] [ symbolB ] . exchangeInfo [ 'Mintpal' ] ;
marketData [ symbolA ] . depth = depth ;
2014-04-18 05:48:59 -07:00
if ( totalQty > 0 )
2014-04-17 15:55:54 -07:00
marketData [ symbolA ] . weightedBid = new Number ( depth / totalQty ) ;
2014-04-16 20:29:37 -07:00
callback ( ) ;
} ) ;
2014-04-16 19:42:38 -07:00
} ;
2014-05-27 14:05:12 -07:00
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 ( ) ;
} ) ;
} ;
2014-04-13 16:17:40 -07:00
this . getCoindDaemonInfo = function ( callback ) {
var daemonTasks = [ ] ;
Object . keys ( profitStatus ) . forEach ( function ( algo ) {
Object . keys ( profitStatus [ algo ] ) . forEach ( function ( symbol ) {
var coinName = profitStatus [ algo ] [ symbol ] . name ;
var poolConfig = poolConfigs [ coinName ] ;
2014-05-02 14:59:46 -07:00
var daemonConfig = poolConfig . paymentProcessing . daemon ;
2014-04-13 16:17:40 -07:00
daemonTasks . push ( function ( callback ) {
_this . getDaemonInfoForCoin ( symbol , daemonConfig , callback )
} ) ;
} ) ;
} ) ;
if ( daemonTasks . length == 0 ) {
callback ( ) ;
return ;
}
2014-04-16 18:53:40 -07:00
async . series ( daemonTasks , function ( err ) {
2014-04-13 16:17:40 -07:00
if ( err ) {
callback ( err ) ;
return ;
}
callback ( null ) ;
} ) ;
} ;
this . getDaemonInfoForCoin = function ( symbol , cfg , callback ) {
2014-05-06 19:30:31 -07:00
var daemon = new Stratum . daemon . interface ( [ cfg ] , function ( severity , message ) {
logger [ severity ] ( logSystem , symbol , message ) ;
2014-04-19 16:08:52 -07:00
callback ( null ) ; // fail gracefully for each coin
2014-05-06 19:30:31 -07:00
} ) ;
2014-04-28 14:04:12 -07:00
daemon . cmd ( 'getblocktemplate' , [ { "capabilities" : [ "coinbasetxn" , "workid" , "coinbase/append" ] } ] , function ( result ) {
if ( result [ 0 ] . error != null ) {
logger . error ( logSystem , symbol , 'Error while reading daemon info: ' + JSON . stringify ( result [ 0 ] ) ) ;
callback ( null ) ; // fail gracefully for each coin
return ;
}
var coinStatus = profitStatus [ symbolToAlgorithmMap [ symbol ] ] [ symbol ] ;
var response = result [ 0 ] . response ;
// some shitcoins dont provide target, only bits, so we need to deal with both
var target = response . target ? bignum ( response . target , 16 ) : util . bignumFromBitsHex ( response . bits ) ;
coinStatus . difficulty = parseFloat ( ( diff1 / target . toNumber ( ) ) . toFixed ( 9 ) ) ;
logger . debug ( logSystem , symbol , 'difficulty is ' + coinStatus . difficulty ) ;
coinStatus . reward = response . coinbasevalue / 100000000 ;
callback ( null ) ;
} ) ;
2014-04-13 16:17:40 -07:00
} ;
2014-04-13 09:44:12 -07:00
2014-04-17 15:55:54 -07:00
this . getMiningRate = function ( callback ) {
var daemonTasks = [ ] ;
Object . keys ( profitStatus ) . forEach ( function ( algo ) {
Object . keys ( profitStatus [ algo ] ) . forEach ( function ( symbol ) {
2014-04-18 05:48:59 -07:00
var coinStatus = profitStatus [ symbolToAlgorithmMap [ symbol ] ] [ symbol ] ;
2014-04-28 14:04:12 -07:00
coinStatus . blocksPerMhPerHour = 86400 / ( ( coinStatus . difficulty * Math . pow ( 2 , 32 ) ) / ( 1 * 1000 * 1000 ) ) ;
coinStatus . coinsPerMhPerHour = coinStatus . reward * coinStatus . blocksPerMhPerHour ;
2014-04-17 15:55:54 -07:00
} ) ;
} ) ;
callback ( null ) ;
} ;
2014-04-18 05:48:59 -07:00
this . switchToMostProfitableCoins = function ( ) {
Object . keys ( profitStatus ) . forEach ( function ( algo ) {
var algoStatus = profitStatus [ algo ] ;
2014-04-17 15:55:54 -07:00
var bestExchange ;
var bestCoin ;
2014-04-28 14:04:12 -07:00
var bestBtcPerMhPerHour = 0 ;
2014-04-17 15:55:54 -07:00
2014-04-18 05:48:59 -07:00
Object . keys ( profitStatus [ algo ] ) . forEach ( function ( symbol ) {
var coinStatus = profitStatus [ algo ] [ symbol ] ;
Object . keys ( coinStatus . exchangeInfo ) . forEach ( function ( exchange ) {
var exchangeData = coinStatus . exchangeInfo [ exchange ] ;
if ( exchangeData . hasOwnProperty ( 'BTC' ) && exchangeData [ 'BTC' ] . hasOwnProperty ( 'weightedBid' ) ) {
2014-04-28 14:04:12 -07:00
var btcPerMhPerHour = exchangeData [ 'BTC' ] . weightedBid * coinStatus . coinsPerMhPerHour ;
2014-04-18 05:48:59 -07:00
if ( btcPerMhPerHour > bestBtcPerMhPerHour ) {
bestBtcPerMhPerHour = btcPerMhPerHour ;
bestExchange = exchange ;
bestCoin = profitStatus [ algo ] [ symbol ] . name ;
}
coinStatus . btcPerMhPerHour = btcPerMhPerHour ;
2014-04-19 16:08:52 -07:00
logger . debug ( logSystem , 'CALC' , 'BTC/' + symbol + ' on ' + exchange + ' with ' + coinStatus . btcPerMhPerHour . toFixed ( 8 ) + ' BTC/day per Mh/s' ) ;
2014-04-18 05:48:59 -07:00
}
if ( exchangeData . hasOwnProperty ( 'LTC' ) && exchangeData [ 'LTC' ] . hasOwnProperty ( 'weightedBid' ) ) {
2014-04-28 14:04:12 -07:00
var btcPerMhPerHour = ( exchangeData [ 'LTC' ] . weightedBid * coinStatus . coinsPerMhPerHour ) * exchangeData [ 'LTC' ] . ltcToBtc ;
2014-04-18 05:48:59 -07:00
if ( btcPerMhPerHour > bestBtcPerMhPerHour ) {
bestBtcPerMhPerHour = btcPerMhPerHour ;
bestExchange = exchange ;
bestCoin = profitStatus [ algo ] [ symbol ] . name ;
}
coinStatus . btcPerMhPerHour = btcPerMhPerHour ;
2014-04-19 16:08:52 -07:00
logger . debug ( logSystem , 'CALC' , 'LTC/' + symbol + ' on ' + exchange + ' with ' + coinStatus . btcPerMhPerHour . toFixed ( 8 ) + ' BTC/day per Mh/s' ) ;
2014-04-18 05:48:59 -07:00
}
} ) ;
} ) ;
2014-04-19 16:08:52 -07:00
logger . debug ( logSystem , 'RESULT' , 'Best coin for ' + algo + ' is ' + bestCoin + ' on ' + bestExchange + ' with ' + bestBtcPerMhPerHour . toFixed ( 8 ) + ' BTC/day per Mh/s' ) ;
2014-04-28 14:04:12 -07:00
var client = net . connect ( portalConfig . cliPort , function ( ) {
client . write ( JSON . stringify ( {
command : 'coinswitch' ,
params : [ bestCoin ] ,
options : { algorithm : algo }
} ) + '\n' ) ;
} ) . on ( 'error' , function ( error ) {
if ( error . code === 'ECONNREFUSED' )
logger . error ( logSystem , 'CLI' , 'Could not connect to NOMP instance on port ' + portalConfig . cliPort ) ;
else
logger . error ( logSystem , 'CLI' , 'Socket error ' + JSON . stringify ( error ) ) ;
} ) ;
2014-04-18 05:48:59 -07:00
} ) ;
} ;
2014-04-17 15:55:54 -07:00
2014-04-13 09:44:12 -07:00
var checkProfitability = function ( ) {
2014-04-17 15:55:54 -07:00
logger . debug ( logSystem , 'Check' , 'Collecting profitability data.' ) ;
2014-04-13 09:46:06 -07:00
2014-04-18 05:48:59 -07:00
profitabilityTasks = [ ] ;
if ( portalConfig . profitSwitch . usePoloniex )
profitabilityTasks . push ( _this . getProfitDataPoloniex ) ;
2014-04-16 19:42:38 -07:00
2014-04-18 05:48:59 -07:00
if ( portalConfig . profitSwitch . useCryptsy )
profitabilityTasks . push ( _this . getProfitDataCryptsy ) ;
2014-04-16 19:42:38 -07:00
2014-04-18 05:48:59 -07:00
if ( portalConfig . profitSwitch . useMintpal )
profitabilityTasks . push ( _this . getProfitDataMintpal ) ;
2014-04-16 19:42:38 -07:00
2014-05-27 14:05:12 -07:00
if ( portalConfig . profitSwitch . useBittrex )
profitabilityTasks . push ( _this . getProfitDataBittrex ) ;
2014-04-18 05:48:59 -07:00
profitabilityTasks . push ( _this . getCoindDaemonInfo ) ;
profitabilityTasks . push ( _this . getMiningRate ) ;
2014-04-16 19:42:38 -07:00
2014-04-17 15:55:54 -07:00
// has to be series
async . series ( profitabilityTasks , function ( err ) {
2014-04-13 16:17:40 -07:00
if ( err ) {
2014-04-13 09:46:06 -07:00
logger . error ( logSystem , 'Check' , 'Error while checking profitability: ' + err ) ;
2014-04-13 16:17:40 -07:00
return ;
}
2014-04-18 05:48:59 -07:00
//
// TODO offer support for a userConfigurable function for deciding on coin to override the default
//
_this . switchToMostProfitableCoins ( ) ;
2014-04-13 16:17:40 -07:00
} ) ;
2014-04-13 09:46:06 -07:00
} ;
2014-04-13 09:44:12 -07:00
setInterval ( checkProfitability , portalConfig . profitSwitch . updateInterval * 1000 ) ;
} ;