bot18
Version:
A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f
814 lines (736 loc) • 21.8 kB
JavaScript
'use strict'
const rp = require('request-promise')
const { URLSearchParams } = require('url')
const RESTv1 = require('./rest')
const { genAuthSig, nonce, isClass } = require('../util')
const {
FundingCredit,
FundingLoan,
FundingOffer,
FundingTrade,
MarginInfo,
Order,
Position,
Trade,
PublicTrade,
TradingTicker,
FundingTicker,
Wallet,
Alert,
Candle
} = require('../models')
const BASE_TIMEOUT = 15000
const API_URL = 'https://api.bitfinex.com'
/**
* Communicates with v2 of the Bitfinex HTTP API
*/
class RESTv2 {
/**
* Instantiate a new REST v2 transport.
*
* @param {Object} opts
* @param {string} opts.apiKey
* @param {string} opts.apiSecret
* @param {string} opts.url - endpoint URL
* @param {boolean} opts.transform - default false
* @param {Object} opts.agent - optional node agent for connection (proxy)
*/
constructor (opts = {
apiKey: '',
apiSecret: '',
url: API_URL,
transform: false,
agent: null
}) {
this._url = opts.url || API_URL
this._apiKey = opts.apiKey || ''
this._apiSecret = opts.apiSecret || ''
this._transform = !!opts.transform
this._agent = opts.agent
// Used for methods that are not yet implemented on REST v2
this._rest1 = new RESTv1(opts)
}
/**
* @param {string} path
* @param {Object} payload
* @param {Method} cb
* @param {Object|Function} transformer - model class or function
* @private
*/
_makeAuthRequest (path, payload = {}, cb = () => {}, transformer) {
if (!this._apiKey || !this._apiSecret) {
return cb(new Error('missing api key or secret'))
}
const url = `${this._url}/v2${path}`
const n = nonce()
const sigPayload = `/api/v2${path}${n}${JSON.stringify(payload)}`
const { sig } = genAuthSig(this._apiSecret, sigPayload)
return rp({
url,
method: 'POST',
headers: {
'bfx-nonce': n,
'bfx-apikey': this._apiKey,
'bfx-signature': sig
},
agent: this._agent,
body: payload,
json: true
}).then((data) => {
if (!this._transform) {
return data
} else if (isClass(transformer)) {
return this._doTransform(transformer, data)
} else if (typeof transformer === 'function') {
return transformer(data)
}
return data
}).then(res => {
cb(null, res)
return res
}).catch((err) => {
if (err.error && err.error[1] === 10114) {
err.message += ' see https://github.com/bitfinexcom/bitfinex-api-node/blob/master/README.md#nonce-too-small for help'
}
cb(new Error(err))
throw err // must be caught again
})
}
/**
* @param {string} path
* @param {Method} cb
* @param {Object|Function} transformer - model class or function
* @private
*/
_makePublicRequest (path, cb = () => {}, transformer) {
const url = `${this._url}/v2${path}`
return rp({
url,
method: 'GET',
timeout: BASE_TIMEOUT,
agent: this._agent,
json: true
}).then((data) => {
if (!this._transform) {
return data
} else if (isClass(transformer)) {
return this._doTransform(transformer, data)
} else if (typeof transformer === 'function') {
return transformer(data)
}
return data
}).then(res => {
cb(null, res)
return res
}).catch(err => {
cb(err)
throw err
})
}
/**
* NOTE: New API method, only returns a promise. Callback support will be
* deprecated!
*
* @param {string} path
* @param {Object} body
* @param {Object|Function} transformer - model class or function
* @return {Promise} p
* @private
*/
_makePublicPostRequest (path, body, transformer) {
const url = `${this._url}/v2${path}`
return rp({
url,
method: 'POST',
timeout: BASE_TIMEOUT,
agent: this._agent,
json: true,
body
}).then((data) => {
if (!this._transform) {
return data
} else if (isClass(transformer)) {
return this._doTransform(transformer, data)
} else if (typeof transformer === 'function') {
return transformer(data)
}
return data
})
}
/**
* Legacy REST1 public method wrapper, that also provides legacy cb
* support. Oh my!
*
* @deprecated
* @param {string} method - REST1 method name
* @param {Method?} cb - optional legacy cb
* @return {Promise} p - use this
*/
_makePublicLegacyRequest (method, cb = () => {}) {
return new Promise((resolve, reject) => {
this._rest1.make_public_request(method, (err, data) => {
if (err) {
cb(err)
return reject(err)
}
cb(null, data)
resolve(data)
})
})
}
/**
* See _makePublicLegacyRequest
* @param {string} method
* @param {Object?} params
* @param {Method?} cb
* @return {Promise} p
*/
_makeAuthLegacyRequest (method, params = {}, cb = () => {}) {
return new Promise((resolve, reject) => {
this._rest1.make_request(method, params, (err, data) => {
if (err) {
cb(err)
return reject(err)
}
cb(null, data)
resolve(data)
})
})
}
/**
* @param {Object} ModelClass
* @param {Object} data
* @return {Object|Object[]} finalData
* @private
*/
_doTransform (ModelClass, data) {
if (!data || data.length === 0) return []
if (!ModelClass || !this._transform) return data
if (Array.isArray(data[0])) {
return data.map(row => new ModelClass(row))
}
return new ModelClass(data)
}
/**
* @param {Method?} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-public-platform-status
*/
status (cb = () => {}) {
return this._makePublicRequest('/platform/status', cb)
}
/**
* @param {string} symbol
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-public-ticker
*/
ticker (symbol = 'tBTCUSD', cb) {
const Model = symbol[0] === 't' ? TradingTicker : FundingTicker
return this._makePublicRequest(`/ticker/${symbol}`, cb, (data) => {
return this._transform
? new Model([symbol, ...data])
: data
})
}
/**
* @param {string[]} symbols
* @param {Method?} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-public-tickers
*/
tickers (symbols = [], cb = () => {}) {
if (typeof symbols === 'function' || typeof cb === 'undefined') {
const err = new Error('symbols required')
cb(err)
return Promise.reject(err)
}
return this._makePublicRequest(`/tickers?symbols=${symbols.join(',')}`, cb, (data) => {
return data.map(ticker => (
(ticker[0] || '')[0] === 't'
? new TradingTicker(ticker)
: new FundingTicker(ticker)
))
})
}
/**
* @param {string} key
* @param {string} context
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-public-stats
*/
stats (key = 'pos.size:1m:tBTCUSD:long', context = 'hist', cb) {
return this._makePublicRequest(`/stats1/${key}/${context}`, cb)
}
/**
*
* @param {Object} opts
* @param {string} opts.timeframe - 1m, 5m, 15m, 30m, 1h, 3h, 6h, 12h, 1D, 7D, 14D, 1M
* @param {string} opts.symbol
* @param {string} opts.section - hist, last
* @param {Method} cb
* @return {Promise} p
* @see http://docs.bitfinex.com/v2/reference#rest-public-candles
*/
candles ({
timeframe = '1m',
symbol = 'tBTCUSD',
section = 'hist',
query = {}
}, cb) {
let url = `/candles/trade:${timeframe}:${symbol}/${section}`
if (Object.keys(query).length > 0) {
url += `?${new URLSearchParams(query).toString()}`
}
return this._makePublicRequest(url, cb, Candle)
}
/**
* @param {string} type
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-alert-list
*/
alertList (type = 'price', cb) {
return this._makeAuthRequest('/auth/r/alerts', { type }, cb, Alert)
}
/**
* @param {string} type
* @param {string} symbol
* @param {number} price
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-alert-set
*/
alertSet (type = 'price', symbol = 'tBTCUSD', price = 0, cb) {
return this._makeAuthRequest('/auth/w/alert/set', { type, symbol, price }, cb, Alert)
}
/**
* @param {string} symbol
* @param {number} price
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-alert-delete
*/
alertDelete (symbol = 'tBTCUSD', price = 0, cb) {
return this._makeAuthRequest('/auth/w/alert/del', { symbol, price }, cb)
}
/**
* @param {string} symbol
* @param {number?} start
* @param {number?} end
* @param {number?} limit
* @param {number?} sort - if 1, sorts results oldest first
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-public-trades
*/
trades (symbol = 'tBTCUSD', start = null, end = null, limit = null, sort = null, cb) {
const query = {}
if (start !== null) query.start = start
if (end !== null) query.end = end
if (limit !== null) query.limit = limit
if (sort !== null) query.sort = sort
let url = `/trades/${symbol}/hist`
if (Object.keys(query).length > 0) {
url += `?${new URLSearchParams(query).toString()}`
}
return this._makePublicRequest(url, cb, PublicTrade)
}
/**
* @param {string} symbol
* @param {number} start
* @param {number} end
* @param {number} limit
* @param {number} sort - if 1, sorts results oldest first
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-trades-hist
*/
accountTrades (symbol = 'tBTCUSD', start = null, end = null, limit = null, sort = null, cb) {
return this._makeAuthRequest(`/auth/r/trades/${symbol}/hist`, {
start, end, limit, sort
}, cb, Trade)
}
/**
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-wallets
*/
wallets (cb) {
return this._makeAuthRequest('/auth/r/wallets', {}, cb, Wallet)
}
/**
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-orders
*/
activeOrders (cb) {
return this._makeAuthRequest('/auth/r/orders', {}, cb, Order)
}
/**
* @param {string} symbol
* @param {number} start
* @param {number} end
* @param {number} limit
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#orders-history
*/
orderHistory (symbol = 'tBTCUSD', start = null, end = null, limit = null, cb) {
return this._makeAuthRequest(`/auth/r/orders/${symbol}/hist`, {
start, end, limit
}, cb, Order)
}
/**
* @param {string} symbol
* @param {number} start
* @param {number} end
* @param {number} limit
* @param {number} orderID
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-order-trades
*/
orderTrades (symbol = 'tBTCUSD', start = null, end = null, limit = null, orderID, cb) {
return this._makeAuthRequest(`/auth/r/order/${symbol}:${orderID}/trades`, {
start, end, limit
}, cb, Trade)
}
/**
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-positions
*/
positions (cb) {
return this._makeAuthRequest('/auth/r/positions', {}, cb, Position)
}
/**
* @param {string} symbol
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-funding-offers
*/
fundingOffers (symbol = 'fUSD', cb) {
return this._makeAuthRequest(`/auth/r/funding/offers/${symbol}`, {}, cb, FundingOffer)
}
/**
* @param {string} symbol
* @param {number} start
* @param {number} end
* @param {number} limit
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-funding-offers-hist
*/
fundingOfferHistory (symbol = 'tBTCUSD', start = null, end = null, limit = null, cb) {
return this._makeAuthRequest(`/auth/r/funding/offers/${symbol}/hist`, {
start, end, limit
}, cb, FundingOffer)
}
/**
* @param {string} symbol
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-funding-loans
*/
fundingLoans (symbol = 'fUSD', cb) {
return this._makeAuthRequest(`/auth/r/funding/loans/${symbol}`, {}, cb, FundingLoan)
}
/**
* @param {string} symbol
* @param {number} start
* @param {number} end
* @param {number} limit
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-funding-loans-hist
*/
fundingLoanHistory (symbol = 'tBTCUSD', start = null, end = null, limit = null, cb) {
return this._makeAuthRequest(`/auth/r/funding/loans/${symbol}/hist`, {
start, end, limit
}, cb, FundingLoan)
}
/**
* @param {string} symbol
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-funding-credits
*/
fundingCredits (symbol = 'fUSD', cb) {
return this._makeAuthRequest(`/auth/r/funding/credits/${symbol}`, {}, cb, FundingCredit)
}
/**
* @param {string} symbol
* @param {number} start
* @param {number} end
* @param {number} limit
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-funding-credits-hist
*/
fundingCreditHistory (symbol = 'tBTCUSD', start = null, end = null, limit = null, cb) {
return this._makeAuthRequest(`/auth/r/funding/credits/${symbol}/hist`, {
start, end, limit
}, cb, FundingCredit)
}
/**
* @param {string} symbol
* @param {number} start
* @param {number} end
* @param {number} limit
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-funding-trades-hist
*/
fundingTrades (symbol = 'tBTCUSD', start = null, end = null, limit = null, cb) {
return this._makeAuthRequest(`/auth/r/funding/trades/${symbol}/hist`, {
start, end, limit
}, cb, FundingTrade)
}
/**
* @param {string} key
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-info-margin
*/
marginInfo (key = 'base', cb) {
return this._makeAuthRequest(`/auth/r/info/margin/${key}`, {}, cb, MarginInfo)
}
/**
* @param {string} key
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-info-funding
*/
fundingInfo (key = 'fUSD', cb) {
return this._makeAuthRequest(`/auth/r/info/funding/${key}`, {}, cb)
}
/**
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-performance
*/
performance (cb) {
return this._makeAuthRequest('/auth/r/stats/perf:1D/hist', {}, cb)
}
/**
* @param {string} symbol
* @param {string} dir
* @param {number} rate
* @param {string} type
* @param {Method} cb
* @return {Promise} p
* @see https://docs.bitfinex.com/v2/reference#rest-auth-calc-bal-avail
*/
calcAvailableBalance (symbol = 'tBTCUSD', dir, rate, type, cb) {
return this._makeAuthRequest('/auth/calc/order/avail', {
symbol,
dir,
rate,
type
}, cb)
}
/**
* Get a list of valid symbol names
*
* @param {Method?} cb - legacy callback
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-public-symbols
*/
symbols (cb = () => {}) {
return this._makePublicLegacyRequest('symbols', cb)
}
/**
* Get a list of valid symbol names and details
*
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-public-symbol-details
*/
symbolDetails (cb) {
return this._makePublicLegacyRequest('symbols_details', cb)
}
/**
* Request information about your account
*
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-account-info
*/
accountInfo (cb) {
return this._makeAuthLegacyRequest('account_infos', {}, cb)
}
/**
* Request account withdrawl fees
*
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-fees
*/
accountFees (cb) {
return this._makeAuthLegacyRequest('account_fees', {}, cb)
}
/**
* Returns a 30-day summary of your trading volume and return on margin
* funding.
*
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-summary
*/
accountSummary (cb) {
return this._makeAuthLegacyRequest('summary', {}, cb)
}
/**
* Request a deposit address
*
* @param {Object} params
* @param {string} params.request
* @param {string} params.nonce
* @param {string} params.method - name of currency
* @param {string} params.wallet_name - 'trading', 'exchange' or 'deposit'
* @param {number} params.renew - 1 or 0
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-deposit
*/
deposit (params, cb) {
return this._makeAuthLegacyRequest('deposit/new', params, cb)
}
/**
* Requests a withdrawl from a wallet
*
* @param {Object} params
* @param {string} params.withdraw_type - name of currency
* @param {string} params.walletselected - 'trading', 'exchange, or 'deposit'
* @param {string} params.amount
* @param {string} params.address
* @param {string} params.payment_id - optional, for monero
* @param {string} params.account_name
* @param {string} params.account_number
* @param {string} params.swift
* @param {string} params.bank_name
* @param {string} params.bank_address
* @param {string} params.bank_city
* @param {string} params.bank_country
* @param {string} params.detail_payment - message to beneficiary
* @param {number} params.expressWire - 1 or 0
* @param {string} params.intermediary_bank_name
* @param {string} params.intermediary_bank_address
* @param {string} params.intermediary_bank_city
* @param {string} params.intermediary_bank_country
* @param {string} params.intermediary_bank_account
* @param {string} params.intermediary_bank_swift
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-withdrawal
*/
withdraw (params, cb) {
return this._makeAuthLegacyRequest('withdraw', params, cb)
}
/**
* Execute a balance transfer between wallets
*
* @param {Object} params
* @param {number} params.amount - amount to transfer
* @param {string} params.currency - currency of funds to transfer
* @param {string} params.walletFrom - wallet to transfer from
* @param {string} params.walletTo - wallet to transfer to
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-transfer-between-wallets
*/
transfer (params, cb) {
return this.make_request('transfer', params, cb)
}
/**
* Fetch the permissions of the key being used to generate this request
*
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#auth-key-permissions
*/
keyPermissions (cb) {
return this._makeAuthLegacyRequest('key_info', {}, cb)
}
/**
* Request your wallet balances
*
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-wallet-balances
*/
balances (cb) {
return this._makeAuthLegacyRequest('balances', {}, cb)
}
/**
* @param {Object} params
* @param {number} params.position_id
* @param {number} params.amount
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-claim-position
*/
claimPosition (params, cb) {
return this._makeAuthLegacyRequest('positions/claim', params, cb)
}
/**
* @param {Object} params
* @param {number} params.position_id
* @param {Method} cb
* @return {Promise} p
* @deprecated
* @see https://docs.bitfinex.com/v1/reference#rest-auth-close-position
*/
closePosition (params, cb) {
return this._rest1.make_request('positions/close', params, cb)
}
/**
* @param {Object} settings - key:value map
* @param {Method} cb
* @return {Promise} p
*/
updateSettings (settings, cb) {
return this._makeAuthRequest(`/auth/w/settings/set`, {
settings
}, cb)
}
/**
* @param {string[]} keys
* @param {Method} cb
* @return {Promise} p
*/
deleteSettings (keys, cb) {
return this._makeAuthRequest(`/auth/w/settings/del`, { keys }, cb)
}
/**
* @param {string[]} keys
* @param {Method} cb
* @return {Promise} p
*/
getSettings (keys, cb) {
return this._makeAuthRequest(`/auth/r/settings`, { keys }, cb)
}
/**
* @param {string} ccy1 - i.e. BTC
* @param {string} ccy2 - i.e. USD
* @return {Promise} p - resolves to currenct exchange rate
*/
exchangeRate (ccy1, ccy2) {
return this._makePublicPostRequest('/calc/fx', {
ccy1,
ccy2
}).then(res => res[0])
}
}
module.exports = RESTv2