Skip to content
This repository has been archived by the owner on Feb 15, 2022. It is now read-only.

Commit

Permalink
Bitfinex extension PR (#245)
Browse files Browse the repository at this point in the history
* Add files via upload

* Delete bitfinex-master.zip

* Create package.json

* final version

* Bitfinex API credentials

* Bitfinex deps

* // fix

* add takerFee

Support ref #233.
Did not test after update as master is not update to #233

* move to /
  • Loading branch information
nedievas authored and DeviaVir committed Jun 9, 2017
1 parent edbf800 commit e7b5703
Show file tree
Hide file tree
Showing 6 changed files with 452 additions and 0 deletions.
6 changes: 6 additions & 0 deletions conf-sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ c.bittrex.secret = 'YOUR-SECRET'
// please note that this might change in the future.
// please note that bittrex API is limited, you cannot use backfills or sims (paper/live trading only)

// to enable Bitfinex trading, enter your API credentials:
c.bitfinex = {}
c.bitfinex.key = 'YOUR-API-KEY'
c.bitfinex.secret = 'YOUR-SECRET'
// May use 'exchange' or 'trading' wallet balances. However margin trading may not work...read the API documentation.
c.bitfinex.wallet = 'exchange'

// Optional stop-order triggers:

Expand Down
6 changes: 6 additions & 0 deletions extensions/exchanges/bitfinex/_codemap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
_ns: 'zenbot',

'exchanges.bitfinex': require('./exchange'),
'exchanges.list[]': '#exchanges.bitfinex'
}
241 changes: 241 additions & 0 deletions extensions/exchanges/bitfinex/exchange.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
const BFX = require('bitfinex-api-node')
var _ = require('lodash')
, path = require('path')
, n = require('numbro')

module.exports = function container (get, set, clear) {
var c = get('conf')

var public_client, authed_client

function publicClient () {
if (!public_client) public_client = new BFX(null,null, {version: 1}).rest
return public_client
}

function authedClient () {
if (!authed_client) {
if (!c.bitfinex || !c.bitfinex.key || c.bitfinex.key === 'YOUR-API-KEY') {
throw new Error('please configure your Bitfinex credentials in ' + path.resolve(__dirname, 'conf.js'))
}
authed_client = new BFX(c.bitfinex.key, c.bitfinex.secret, {version: 1}).rest
}
return authed_client
}

function joinProduct (product_id) {
return product_id.split('-')[0] + '' + product_id.split('-')[1]
}

function retry (method, args) {
if (method !== 'getTrades') {
console.error(('\nBitfinex API is down! unable to call ' + method + ', retrying in 10s').red)
}
setTimeout(function () {
exchange[method].apply(exchange, args)
}, 10000)
}

var orders = {}
var exchange = {
name: 'bitfinex',
// historyScan: 'backward',
makerFee: 0.1,
takerFee: 0.2,

getProducts: function () {
return require('./products.json')
},

getTrades: function (opts, cb) {
var func_args = [].slice.call(arguments)
var client = publicClient()
var args = joinProduct(opts.product_id)
client.trades(args, function (err, body) {
if (err) return retry('getTrades', func_args, err)
var trades = body.map(function(trade) {
return {
trade_id: trade.tid,
time: n(trade.timestamp).multiply(1000).value(),
size: Number(trade.amount),
price: Number(trade.price),
side: trade.type
}
})
cb(null, trades)
})
},

getBalance: function (opts, cb) {
var func_args = [].slice.call(arguments)
var client = authedClient()
client.wallet_balances(function (err, body) {
if (err) return retry('getBalance', func_args, err)
var balance = {asset: 0, currency: 0}
var accounts = _(body).filter(function (body) { return body.type === c.bitfinex.wallet }).forEach(function (account) {
if (account.currency.toUpperCase() === opts.currency) {
balance.currency = account.amount
balance.currency_hold = (account.amount - account.available)
}
else if (account.currency.toUpperCase() === opts.asset) {
balance.asset = account.amount
balance.asset_hold = (account.amount - account.available)
}
})
cb(null, balance)
})
},

getQuote: function (opts, cb) {
var func_args = [].slice.call(arguments)
var client = publicClient()
var pair = joinProduct(opts.product_id)
client.ticker(pair, function (err, body) {
if (err) return retry('getQuote', func_args, err)
cb(null, { bid : body.bid, ask : body.ask })
})
},

cancelOrder: function (opts, cb) {
var func_args = [].slice.call(arguments)
var client = authedClient()
client.cancel_order(opts.order_id, function (err, body) {
if (err) return retry('cancelOrder', func_args, err)
cb()
})
},

buy: function (opts, cb) {
var func_args = [].slice.call(arguments)
var client = authedClient()
if (c.bitfinex.wallet === 'exchange' && typeof opts.order_type === 'maker') {
opts.type = 'exchange limit'
}
else if (c.bitfinex.wallet === 'exchange' && typeof opts.order_type === 'taker') {
opts.type = 'exchange market'
}
if (typeof opts.post_only === 'undefined') {
opts.post_only = true
}
var symbol = joinProduct(opts.product_id)
var amount = opts.size
var price = opts.price
var exchange = 'bitfinex'
var side = 'buy'
var type = opts.type
var is_hidden = false
var is_postonly = opts.post_only
var params = {
symbol,
amount,
price,
exchange,
side,
type,
is_hidden,
is_postonly
}
client.make_request('order/new', params, function (err, body) {
var order = {
id: body && body.is_live === true ? body.id : null,
status: 'open',
price: opts.price,
size: opts.size,
post_only: !!opts.post_only,
created_at: new Date().getTime(),
filled_size: '0'
}
if (err && err.match(/Error: Invalid order: not enough exchange balance$/)) {
status: 'rejected'
reject_reason: 'balance'
return cb(null, order)
}
if (err) return retry('buy', func_args, err)
orders['~' + body.id] = order
cb(null, order)
})
},

sell: function (opts, cb) {
var func_args = [].slice.call(arguments)
var client = authedClient()
if (c.bitfinex.wallet === 'exchange' && typeof opts.order_type === 'maker') {
opts.type = 'exchange limit'
}
else if (c.bitfinex.wallet === 'exchange' && typeof opts.order_type === 'taker') {
opts.type = 'exchange market'
}
if (typeof opts.post_only === 'undefined') {
opts.post_only = true
}
var symbol = joinProduct(opts.product_id)
var amount = opts.size
var price = opts.price
var exchange = 'bitfinex'
var side = 'sell'
var type = opts.type
var is_hidden = false
var is_postonly = opts.post_only
var params = {
symbol,
amount,
price,
exchange,
side,
type,
is_hidden,
is_postonly
}
client.make_request('order/new', params, function (err, body) {
var order = {
id: body && body.is_live === true ? body.id : null,
status: 'open',
price: opts.price,
size: opts.size,
post_only: !!opts.post_only,
created_at: new Date().getTime(),
filled_size: '0'
}
if (err && err.match(/Error: Invalid order: not enough exchange balance$/)) {
status: 'rejected'
reject_reason: 'balance'
return cb(null, order)
}
if (err) return retry('sell', func_args, err)
orders['~' + body.id] = order
cb(null, order)
})
},

getOrder: function (opts, cb) {
var func_args = [].slice.call(arguments)
var order = orders['~' + opts.order_id]
var client = authedClient()
client.order_status(opts.order_id, function (err, body) {
if (err) return retry('getOrder', func_args, err)
if (!body.id) {
return cb('Order not found')
}
if (body.is_cancelled === true && body.is_live === false) {
order.status = 'rejected'
order.reject_reason = 'post only'
order.done_at = new Date().getTime()
return cb(null, order)
}
if (body.is_live === false) {
order.status = 'done'
order.done_at = new Date().getTime()
order.filled_size = body.original_amount - body.executed_amount
return cb(null, order)
}
cb(null, order)
})
},

// return the property used for range querying.
getCursor: function (trade) {
return trade.trade_id
}
}
return exchange
}
Loading

0 comments on commit e7b5703

Please sign in to comment.