Refactored some exchange code

This commit is contained in:
José Oliveira 2021-01-21 11:08:20 +00:00 committed by Josh Harvey
parent 54a231ab60
commit 134eaaa518
20 changed files with 141 additions and 643 deletions

View file

@ -1,5 +1,5 @@
const configManager = require('./new-config-manager') const configManager = require('./new-config-manager')
const ph = require('./plugin-helper') const ccxt = require('./plugins/exchange/ccxt')
function lookupExchange (settings, cryptoCode) { function lookupExchange (settings, cryptoCode) {
const exchange = configManager.getWalletSettings(cryptoCode, settings.config).exchange const exchange = configManager.getWalletSettings(cryptoCode, settings.config).exchange
@ -10,23 +10,22 @@ function lookupExchange (settings, cryptoCode) {
function fetchExchange (settings, cryptoCode) { function fetchExchange (settings, cryptoCode) {
return Promise.resolve() return Promise.resolve()
.then(() => { .then(() => {
const plugin = lookupExchange(settings, cryptoCode) const exchangeName = lookupExchange(settings, cryptoCode)
if (!plugin) throw new Error('No exchange set') if (!exchangeName) throw new Error('No exchange set')
const exchange = ph.load(ph.EXCHANGE, plugin) const account = settings.accounts[exchangeName]
const account = settings.accounts[plugin]
return {exchange, account} return { exchangeName, account }
}) })
} }
function buy (settings, cryptoAtoms, fiatCode, cryptoCode) { function buy (settings, cryptoAtoms, fiatCode, cryptoCode) {
return fetchExchange(settings, cryptoCode) return fetchExchange(settings, cryptoCode)
.then(r => r.exchange.buy(r.account, cryptoAtoms, fiatCode, cryptoCode)) .then(r => ccxt.trade('buy', r.account, cryptoAtoms, fiatCode, cryptoCode, r.exchangeName))
} }
function sell (settings, cryptoAtoms, fiatCode, cryptoCode) { function sell (settings, cryptoAtoms, fiatCode, cryptoCode) {
return fetchExchange(settings, cryptoCode) return fetchExchange(settings, cryptoCode)
.then(r => r.exchange.sell(r.account, cryptoAtoms, fiatCode, cryptoCode)) .then(r => ccxt.trade('sell', r.account, cryptoAtoms, fiatCode, cryptoCode, r.exchangeName))
} }
function active (settings, cryptoCode) { function active (settings, cryptoCode) {

View file

@ -1,86 +0,0 @@
'use strict'
const querystring = require('querystring')
const axios = require('axios')
const crypto = require('crypto')
const _ = require('lodash/fp')
const API_ENDPOINT = 'https://www.bitstamp.net/api/v2'
let counter = -1
let lastTimestamp = Date.now()
function pad (num) {
const asString = num.toString(10)
if (num < 10) return '00' + asString
if (num < 100) return '0' + asString
return asString
}
function generateNonce () {
const timestamp = Date.now()
if (timestamp !== lastTimestamp) counter = -1
lastTimestamp = timestamp
counter = (counter + 1) % 1000
return timestamp.toString(10) + pad(counter)
}
function authRequest (config, path, data) {
if (!config.key || !config.secret || !config.clientId) {
const err = new Error('Must provide key, secret and client ID')
return Promise.reject(err)
}
data = data || {}
const nonce = generateNonce()
const msg = [nonce, config.clientId, config.key].join('')
const signature = crypto
.createHmac('sha256', Buffer.from(config.secret))
.update(msg)
.digest('hex')
.toUpperCase()
const signedData = _.merge(data, {
key: config.key,
signature: signature,
nonce: nonce
})
return request(path, 'POST', signedData)
}
function buildMarket (fiatCode, cryptoCode) {
if (!_.includes(cryptoCode, ['BTC', 'ETH', 'LTC', 'BCH'])) {
throw new Error('Unsupported crypto: ' + cryptoCode)
}
if (!_.includes(fiatCode, ['USD', 'EUR'])) {
throw new Error('Unsupported fiat: ' + fiatCode)
}
return `${cryptoCode.toLowerCase()}${fiatCode.toLowerCase()}`
}
function request (path, method, data) {
const options = {
method: method,
url: API_ENDPOINT + path + '/',
headers: {
'User-Agent': 'Mozilla/4.0 (compatible; Lamassu client)',
'Content-Type': 'application/x-www-form-urlencoded'
}
}
if (data) options.data = querystring.stringify(data)
return axios(options)
.then(r => r.data)
}
module.exports = {
authRequest,
request,
buildMarket
}

View file

@ -13,16 +13,16 @@ const FIAT = {
kraken: ['USD', 'EUR'] kraken: ['USD', 'EUR']
} }
module.exports = { verifyCurrencies }
function verifyCurrencies (exchangeName, fiatCode, cryptoCode) { function verifyCurrencies (exchangeName, fiatCode, cryptoCode) {
if (!_.includes(cryptoCode, CRYPTO[exchangeName])) { if (!_.includes(cryptoCode, CRYPTO[exchangeName])) {
throw new Error('Unsupported crypto: ' + cryptoCode) throw new Error('Unsupported crypto: ' + cryptoCode)
} }
if (!(exchangeName === 'coinbase')) { // coinbase is only used for ticker and it's expected to support most of the fiat if (exchangeName !== 'coinbase') {
if (!_.includes(fiatCode, FIAT[exchangeName])) { if (!_.includes(fiatCode, FIAT[exchangeName])) {
throw new Error('Unsupported fiat: ' + fiatCode) throw new Error('Unsupported fiat: ' + fiatCode)
} }
} }
return cryptoCode + '/' + fiatCode return cryptoCode + '/' + fiatCode
} }
module.exports = { verifyCurrencies, CRYPTO, FIAT }

View file

@ -1,92 +0,0 @@
'use strict'
const querystring = require('querystring')
const axios = require('axios')
const crypto = require('crypto')
const _ = require('lodash/fp')
const API_ENDPOINT = 'https://api.itbit.com/v1'
let counter = -1
let lastTimestamp = Date.now()
function generateNonce () {
const timestamp = Date.now()
if (timestamp !== lastTimestamp) counter = -1
lastTimestamp = timestamp
counter = (counter + 1) % 1000
return timestamp.toString() + counter.toString()
}
function authRequest (account, method, path, data) {
if (!account.userId || !account.walletId || !account.clientKey || !account.clientSecret) {
const err = new Error('Must provide user ID, wallet ID, client key, and client secret')
return Promise.reject(err)
}
const url = buildURL(method, path, data)
const dataString = method !== 'GET' && !_.isEmpty(data) ? JSON.stringify(data) : ''
const nonce = generateNonce()
const timestamp = Date.now()
const message = nonce + JSON.stringify([method, url, dataString, nonce.toString(), timestamp.toString()])
const hashBuffer = crypto
.createHash('sha256')
.update(message).digest()
const bufferToHash = Buffer.concat([Buffer.from(url), hashBuffer])
const signature = crypto
.createHmac('sha512', Buffer.from(account.clientSecret))
.update(bufferToHash)
.digest('base64')
return request(method, path, data, {
'Authorization': account.clientKey + ':' + signature,
'X-Auth-Timestamp': timestamp,
'X-Auth-Nonce': nonce
})
}
function request (method, path, data, auth) {
const options = {
method: method,
url: buildURL(method, path, data),
headers: {
'User-Agent': 'Lamassu itBit node.js client',
...(auth)
},
...(method !== 'GET' && { data: data })
}
return axios(options)
.then(r => r.data)
.catch(e => {
var description = _.get(e, 'response.data.description')
throw new Error(description || e.message)
})
}
const cryptoCodeTranslations = { 'BTC': 'XBT', 'ETH': 'ETH' }
function buildMarket (fiatCode, cryptoCode) {
const translatedCryptoCode = cryptoCodeTranslations[cryptoCode]
if (!translatedCryptoCode) {
throw new Error('Unsupported crypto: ' + cryptoCode)
}
if (!_.includes(fiatCode, ['USD', 'EUR', 'SGD'])) {
throw new Error('Unsupported fiat: ' + fiatCode)
}
return translatedCryptoCode + fiatCode
}
function buildURL (method, path, data) {
let url = API_ENDPOINT + path
if (method === 'GET' && !_.isEmpty(data)) {
url += '?' + querystring.stringify(data)
}
return url
}
module.exports = { authRequest, request, buildMarket }

View file

@ -1,28 +0,0 @@
const PAIRS = {
BTC: {
USD: 'XXBTZUSD',
EUR: 'XXBTZEUR'
},
ETH: {
USD: 'XETHZUSD',
EUR: 'XETHZEUR'
},
ZEC: {
USD: 'XZECZUSD',
EUR: 'XZECZEUR'
},
LTC: {
USD: 'XLTCZUSD',
EUR: 'XLTCZEUR'
},
DASH: {
USD: 'DASHUSD',
EUR: 'DASHEUR'
},
BCH: {
USD: 'BCHUSD',
EUR: 'BCHEUR'
}
}
module.exports = {PAIRS}

View file

@ -0,0 +1,21 @@
const common = require('../common/ccxt')
const _ = require('lodash/fp')
const consts = require('./consts')
const ORDER_TYPE = consts.ORDER_TYPES.MARKET
const FIAT = common.FIAT['kraken']
const CRYPTO = common.CRYPTO['kraken']
const loadConfig = (account) => {
const mapper = {
'key': 'apiKey',
'clientId': 'uid'
}
const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account)
return { ...mapped, timeout: 3000 }
}
const isConfigValid = ({ key, clientId, secret }) => key && secret && clientId
const amountPrecision = () => 8
module.exports = { loadConfig, isConfigValid, amountPrecision , CRYPTO, FIAT, ORDER_TYPE }

View file

@ -1,45 +0,0 @@
const common = require('../../common/bitstamp')
const coinUtils = require('../../../coin-utils')
function buy (account, cryptoAtoms, fiatCode, cryptoCode) {
return trade('buy', account, cryptoAtoms, fiatCode, cryptoCode)
}
function sell (account, cryptoAtoms, fiatCode, cryptoCode) {
return trade('sell', account, cryptoAtoms, fiatCode, cryptoCode)
}
function handleErrors (data) {
if (!data.reason || !data.reason.__all__) return data
const err = new Error(data.reason.__all__[0])
if (data.reason.__all__[0].indexOf('Minimum order size is') === 0) {
err.name = 'orderTooSmall'
}
throw err
}
function trade (type, account, cryptoAtoms, _fiatCode, cryptoCode) {
const fiatCode = _fiatCode === 'USD' ? 'USD' : 'EUR'
try {
const market = common.buildMarket(fiatCode, cryptoCode)
const options = {amount: coinUtils.toUnit(cryptoAtoms, cryptoCode).toFixed(8)}
return common.authRequest(account, '/' + type + '/market/' + market, options)
.catch(e => {
if (e.response) handleErrors(e.response.data)
throw e
})
.then(handleErrors)
} catch (e) {
return Promise.reject(e)
}
}
module.exports = {
buy,
sell
}

View file

@ -0,0 +1,51 @@
var ccxt = require('ccxt')
const coinUtils = require('../../coin-utils')
const common = require('../common/ccxt')
const kraken = require('./kraken')
const bitstamp = require('./bitstamp')
const itbit = require('./itbit')
const consts = require('./consts')
const ALL_EXCHANGES = {
kraken,
bitstamp,
itbit
}
function trade (side, account, cryptoAtoms, fiatCode, cryptoCode, exchangeName) {
try {
const exchangeConfig = ALL_EXCHANGES[exchangeName]
if (!exchangeConfig) throw Error('no exchange')
if (exchangeConfig.isConfigValid && !exchangeConfig.isConfigValid(account)) throw Error('Invalid config')
const exchange = new ccxt[exchangeName](exchangeConfig.loadConfig(account))
const symbol = common.verifyCurrencies(exchangeName, fiatCode, cryptoCode)
const precision = exchangeConfig.amountPrecision ? exchangeConfig.amountPrecision() : consts.DECIMAL_PRECISION.DEFAULT_AMOUNT
const amount = coinUtils.toUnit(cryptoAtoms, cryptoCode).toFixed(precision)
const options = exchangeConfig.loadOptions ? exchangeConfig.loadOptions(account) : {}
if (exchangeConfig.ORDER_TYPE === consts.ORDER_TYPES.MARKET) {
return exchange.createOrder(symbol, consts.ORDER_TYPES.MARKET, side, amount, null, options)
}
return exchange.fetchOrderBook(symbol)
.then(orderBook => {
const price = calculatePrice(side, amount, orderBook).toFixed(consts.DECIMAL_PRECISION.PRICE)
return exchange.createOrder(symbol, consts.ORDER_TYPES.LIMIT, side, amount, price, options)
})
} catch (e) {
return Promise.reject(e)
}
}
function calculatePrice (side, amount, orderBook) {
const book = side === 'buy' ? 'asks' : 'bids'
let collected = 0.0
for (const entry of orderBook[book]) {
collected += parseFloat(entry[1])
if (collected >= amount) return parseFloat(entry[0])
}
throw new Error('Insufficient market depth')
}
module.exports = { trade }

View file

@ -1,73 +0,0 @@
var ccxt = require('ccxt')
const coinUtils = require('../../../coin-utils')
const _ = require('lodash/fp')
const common = require('../../common/ccxt')
function trade (side, account, cryptoAtoms, fiatCode, cryptoCode, exchangeName) {
try {
const exchange = setUpExchange(account, exchangeName)
const symbol = common.verifyCurrencies(exchangeName, fiatCode, cryptoCode)
const amount = coinUtils.toUnit(cryptoAtoms, cryptoCode).toFixed(8)
if (exchangeName === 'itbit') {
return exchange.fetchOrderBook(symbol)
.then(orderBook => {
const price = calculatePrice(side, amount, orderBook)
return exchange.createOrder(symbol, 'limit', side, amount, price, { walletId: account.walletId })
})
}
return exchange.createOrder(symbol, 'market', side, amount)
} catch (e) {
return Promise.reject(e)
}
}
function setUpExchange (account, exchangeName) {
// map given credentials to cctx properties
if (!_.includes(exchangeName, ccxt.exchanges)) {
throw new Error(`Exchange ${exchangeName} not supported by ccxt.`)
}
switch (exchangeName) {
case 'itbit':
if (!account.clientKey || !account.clientSecret || !account.userId || !account.walletId) {
throw new Error('Must provide user ID, wallet ID, client key, and client secret')
}
return new ccxt[exchangeName](_.mapKeys((key) => {
if (key === 'clientKey') return 'apiKey'
if (key === 'clientSecret') return 'secret'
if (key === 'userId') return 'uid'
return key
}, _.omit(['walletId'], account)))
case 'kraken':
if (!account.apiKey || !account.privateKey) {
throw new Error('Must provide key and private key')
}
return new ccxt[exchangeName](_.mapKeys((key) => {
if (key === 'privateKey') return 'secret'
return key
}, account))
case 'bitstamp':
if (!account.key || !account.secret || !account.clientId) {
throw new Error('Must provide key, secret and client ID')
}
return new ccxt[exchangeName](_.mapKeys((key) => {
if (key === 'key') return 'apiKey'
if (key === 'clientId') return 'uid'
return key
}, account))
default:
throw new Error(`Exchange ${exchangeName} not supported.`)
}
}
function calculatePrice (side, amount, orderBook) {
const book = side === 'buy' ? 'asks' : 'bids'
let collected = 0.0
for (const entry of orderBook[book]) {
collected += parseFloat(entry[1])
if (collected >= amount) return parseFloat(entry[0])
}
throw new Error('Insufficient market depth')
}
module.exports = { trade }

View file

@ -0,0 +1,11 @@
const ORDER_TYPES = {
MARKET: 'market',
LIMIT: 'limit'
}
const DECIMAL_PRECISION = {
PRICE: 2,
DEFAULT_AMOUNT: 8
}
module.exports = { ORDER_TYPES, DECIMAL_PRECISION }

View file

@ -0,0 +1,22 @@
const common = require('../common/ccxt')
const _ = require('lodash/fp')
const consts = require('./consts')
const ORDER_TYPE = consts.ORDER_TYPES.LIMIT
const FIAT = common.FIAT['kraken']
const CRYPTO = common.CRYPTO['kraken']
const loadConfig = (account) => {
const mapper = {
'clientKey': 'apiKey',
'clientSecret': 'secret',
'userId': 'uid'
}
const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(_.omit(['walletId'], account))
return { ...mapped, timeout: 3000 }
}
const loadOptions = ({ walletId }) => ({ walletId })
const isConfigValid = ({ clientKey, clientSecret, userId, walletId }) => clientKey && clientSecret && userId && walletId
const amountPrecision = () => 4
module.exports = { amountPrecision, loadOptions, loadConfig, isConfigValid, CRYPTO, FIAT, ORDER_TYPE }

View file

@ -1,45 +0,0 @@
const common = require('../../common/itbit')
const coinUtils = require('../../../coin-utils')
exports.buy = function (account, cryptoAtoms, fiatCode, cryptoCode) {
return trade('buy', account, cryptoAtoms, fiatCode, cryptoCode)
}
exports.sell = function (account, cryptoAtoms, fiatCode, cryptoCode) {
return trade('sell', account, cryptoAtoms, fiatCode, cryptoCode)
}
function trade (type, account, cryptoAtoms, fiatCode, cryptoCode) {
try {
const instrument = common.buildMarket(fiatCode, cryptoCode)
const cryptoAmount = coinUtils.toUnit(cryptoAtoms, cryptoCode)
return calculatePrice(type, instrument, cryptoAmount)
.then(price => {
const args = {
side: type,
type: 'limit',
currency: cryptoCode,
amount: cryptoAmount.toFixed(4),
price: price.toFixed(2),
instrument: instrument
}
return common.authRequest(account, 'POST', '/wallets/' + account.walletId + '/orders', args)
})
} catch (e) {
return Promise.reject(e)
}
}
function calculatePrice (type, tickerSymbol, amount) {
return common.request('GET', '/markets/' + tickerSymbol + '/order_book')
.then(orderBook => {
const book = type == 'buy' ? 'asks' : 'bids'
let collected = 0.0
for (const entry of orderBook[book]) {
collected += parseFloat(entry[1])
if (collected >= amount) return parseFloat(entry[0])
}
throw new Error('Insufficient market depth')
})
}

View file

@ -0,0 +1,21 @@
const common = require('../common/ccxt')
const _ = require('lodash/fp')
const consts = require('./consts')
const ORDER_TYPE = consts.ORDER_TYPES.MARKET
const FIAT = common.FIAT['kraken']
const CRYPTO = common.CRYPTO['kraken']
const loadConfig = (account) => {
const mapper = {
'privateKey': 'secret'
}
const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account)
return { ...mapped, timeout: 3000 }
}
const loadOptions = () => ({ expiretm: '+60' })
const isConfigValid = ({ apiKey, privateKey }) => apiKey && privateKey
const amountPrecision = () => 6
module.exports = { amountPrecision, loadOptions, loadConfig, isConfigValid, CRYPTO, FIAT, ORDER_TYPE }

View file

@ -1,44 +0,0 @@
// Note: Using DeX3/npm-kraken-api to adjust timeout time
const Kraken = require('kraken-api')
const _ = require('lodash/fp')
const common = require('../../common/kraken')
const coinUtils = require('../../../coin-utils')
var PAIRS = common.PAIRS
module.exports = {buy, sell}
function buy (account, cryptoAtoms, fiatCode, cryptoCode) {
return trade(account, 'buy', cryptoAtoms, fiatCode, cryptoCode)
}
function sell (account, cryptoAtoms, fiatCode, cryptoCode) {
return trade(account, 'sell', cryptoAtoms, fiatCode, cryptoCode)
}
function trade (account, type, cryptoAtoms, fiatCode, cryptoCode) {
const kraken = new Kraken(account.apiKey, account.privateKey, {timeout: 30000})
const amount = coinUtils.toUnit(cryptoAtoms, cryptoCode)
const amountStr = amount.toFixed(6)
const pair = _.includes(fiatCode, ['USD', 'EUR'])
? PAIRS[cryptoCode][fiatCode]
: PAIRS[cryptoCode]['EUR']
var orderInfo = {
pair,
type,
ordertype: 'market',
volume: amountStr,
expiretm: '+60'
}
return new Promise((resolve, reject) => {
kraken.api('AddOrder', orderInfo, (error, response) => {
if (error) return reject(error)
return resolve()
})
})
}

View file

@ -1,49 +0,0 @@
const axios = require('axios')
const _ = require('lodash/fp')
const BN = require('../../../bn')
const common = require('../../common/bitstamp')
exports.NAME = 'Bitstamp'
exports.SUPPORTED_MODULES = ['ticker']
function findCurrency (fxRates, fiatCode) {
const rates = _.find(_.matchesProperty('code', fiatCode), fxRates)
if (!rates || !rates.rate) throw new Error(`Unsupported currency: ${fiatCode}`)
return BN(rates.rate.toString())
}
exports.ticker = function ticker (account, fiatCode, cryptoCode) {
if (fiatCode === 'USD' || fiatCode === 'EUR') {
return getCurrencyRates(fiatCode, cryptoCode)
}
return axios.get('https://bitpay.com/rates')
.then(response => {
const fxRates = response.data.data
const usdRate = findCurrency(fxRates, 'USD')
const fxRate = findCurrency(fxRates, fiatCode).div(usdRate)
return getCurrencyRates('USD', cryptoCode)
.then(res => ({
rates: {
ask: res.rates.ask.times(fxRate),
bid: res.rates.bid.times(fxRate)
}
}))
})
}
function getCurrencyRates (fiatCode, cryptoCode) {
return Promise.resolve()
.then(() => {
const market = common.buildMarket(fiatCode, cryptoCode)
return common.request('/ticker/' + market, 'GET')
})
.then(r => ({
rates: {
ask: BN(r.ask),
bid: BN(r.bid)
}
}))
}

View file

@ -1,8 +1,8 @@
const ccxt = require('ccxt') const ccxt = require('ccxt')
const BN = require('../../../bn') const BN = require('../../bn')
const axios = require('axios') const axios = require('axios')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const common = require('../../common/ccxt') const common = require('../common/ccxt')
function ticker (exchangeName, fiatCode, cryptoCode) { function ticker (exchangeName, fiatCode, cryptoCode) {
const exchange = new ccxt[exchangeName]() const exchange = new ccxt[exchangeName]()

View file

@ -1,54 +0,0 @@
const _ = require('lodash/fp')
const axios = require('axios')
const BN = require('../../../bn')
function getBuyPrice (obj) {
const currencyPair = obj.currencyPair
return axios({
method: 'get',
url: `https://api.coinbase.com/v2/prices/${currencyPair}/buy`,
headers: {'CB-Version': '2017-07-10'}
})
.then(r => r.data)
}
function getSellPrice (obj) {
const currencyPair = obj.currencyPair
return axios({
method: 'get',
url: `https://api.coinbase.com/v2/prices/${currencyPair}/sell`,
headers: {'CB-Version': '2017-07-10'}
})
.then(r => r.data)
}
function ticker (account, fiatCode, cryptoCode) {
return Promise.resolve()
.then(() => {
if (!_.includes(cryptoCode, ['BTC', 'ETH', 'LTC', 'BCH', 'ZEC', 'DASH'])) {
throw new Error('Unsupported crypto: ' + cryptoCode)
}
})
.then(() => {
const currencyPair = `${cryptoCode}-${fiatCode}`
const promises = [
getBuyPrice({currencyPair}),
getSellPrice({currencyPair})
]
return Promise.all(promises)
})
.then(([buyPrice, sellPrice]) => ({
rates: {
ask: BN(buyPrice.data.amount),
bid: BN(sellPrice.data.amount)
}
}))
}
module.exports = {
ticker
}

View file

@ -1,54 +0,0 @@
const axios = require('axios')
const _ = require('lodash/fp')
const BN = require('../../../bn')
const common = require('../../common/itbit')
exports.NAME = 'itBit'
exports.SUPPORTED_MODULES = ['ticker']
function findCurrency (fxRates, fiatCode) {
const rates = _.find(_.matchesProperty('code', fiatCode), fxRates)
if (!rates || !rates.rate) throw new Error(`Unsupported currency: ${fiatCode}`)
return BN(rates.rate.toString())
}
exports.ticker = function ticker (account, fiatCode, cryptoCode) {
if (_.includes(fiatCode, ['USD', 'EUR', 'SGD'])) {
return getCurrencyRates(fiatCode, cryptoCode)
}
return axios.get('https://bitpay.com/api/rates')
.then(response => {
const fxRates = response.data
try {
const usdRate = findCurrency(fxRates, 'USD')
const fxRate = findCurrency(fxRates, fiatCode).div(usdRate)
return getCurrencyRates('USD', cryptoCode)
.then(res => ({
rates: {
ask: res.rates.ask.times(fxRate),
bid: res.rates.bid.times(fxRate)
}
}))
} catch (e) {
return Promise.reject(e)
}
})
}
function getCurrencyRates (fiatCode, cryptoCode) {
try {
const market = common.buildMarket(fiatCode, cryptoCode)
return common.request('GET', '/markets/' + market + '/ticker')
.then(r => ({
rates: {
ask: BN(r.ask),
bid: BN(r.bid)
}
}))
} catch (e) {
return Promise.reject(e)
}
}

View file

@ -1,52 +0,0 @@
const axios = require('axios')
const _ = require('lodash/fp')
const BN = require('../../../bn')
const common = require('../../common/kraken')
exports.NAME = 'Kraken'
exports.SUPPORTED_MODULES = ['ticker']
const PAIRS = common.PAIRS
function findCurrency (fxRates, fiatCode) {
const rates = _.find(_.matchesProperty('code', fiatCode), fxRates)
if (!rates || !rates.rate) throw new Error(`Unsupported currency: ${fiatCode}`)
return BN(rates.rate.toString())
}
exports.ticker = function ticker (account, fiatCode, cryptoCode) {
if (fiatCode === 'USD' || fiatCode === 'EUR') {
return getCurrencyRates(fiatCode, cryptoCode)
}
return axios.get('https://bitpay.com/api/rates')
.then(response => {
const fxRates = response.data
const usdRate = findCurrency(fxRates, 'USD')
const fxRate = findCurrency(fxRates, fiatCode).div(usdRate)
return getCurrencyRates('USD', cryptoCode)
.then(res => ({
rates: {
ask: res.rates.ask.times(fxRate),
bid: res.rates.bid.times(fxRate)
}
}))
})
}
function getCurrencyRates (fiatCode, cryptoCode) {
const pair = PAIRS[cryptoCode][fiatCode]
return axios.get('https://api.kraken.com/0/public/Ticker?pair=' + pair)
.then(function (response) {
const rates = response.data.result[pair]
return {
rates: {
ask: BN(rates.a[0]),
bid: BN(rates.b[0])
}
}
})
}

View file

@ -1,8 +1,7 @@
const mem = require('mem') const mem = require('mem')
const configManager = require('./new-config-manager') const configManager = require('./new-config-manager')
const ph = require('./plugin-helper')
const logger = require('./logger') const logger = require('./logger')
const ccxt = require('./plugins/ticker/ccxt')
const lastRate = {} const lastRate = {}
const FETCH_INTERVAL = 60000 const FETCH_INTERVAL = 60000
@ -11,14 +10,10 @@ function _getRates (settings, fiatCode, cryptoCode) {
return Promise.resolve() return Promise.resolve()
.then(() => { .then(() => {
const config = settings.config const config = settings.config
const plugin = configManager.getWalletSettings(cryptoCode, config).ticker const exchangeName = configManager.getWalletSettings(cryptoCode, config).ticker
const account = settings.accounts[plugin]
const ticker = ph.load(ph.TICKER, plugin)
const market = [cryptoCode, fiatCode].join('-') const market = [cryptoCode, fiatCode].join('-')
return ticker.ticker(account, fiatCode, cryptoCode) return ccxt.ticker(exchangeName, fiatCode, cryptoCode)
.then(r => ({ .then(r => ({
rates: r.rates, rates: r.rates,
timestamp: Date.now() timestamp: Date.now()