UNPKG

crypto-nodes

Version:

527 lines (397 loc) 11.3 kB
/* GENERIC CCXT 3 TYPES OF RESPONCES REQUIRED TO HANDLE:: { _msgid: '3d3003de.c0249c', topic: '', payload: { key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '120000', price: 84.55, symbol: 'ETH/USD', is_buy: true } } } { _msgid: '3d3003de.c0249c', topic: '', payload: { key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '120000', price: 84.55, symbol: 'ETH/USD', is_buy: true } }, key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '120000', price: 84.55, symbol: 'ETH/USD', is_buy: true } } ExchangeError gemini POST https://api.sandbox.gemini.com/v1/order/new 406 Not Acceptable {"result":"error","reason":"InsufficientFunds","message":"Failed to place buy order on symbol 'ETHUSD' for price $84.55 and quantity 120,000 ETH due to insufficient funds"} RESET :: GDAX :: WebSocket connection disconnected GDAX :: CONNECTED Already have socket.. { _msgid: '20f30417.9c7cfc', topic: '', payload: { key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '0.19', price: 484.55, symbol: 'ETH/USD', is_buy: false } } } { _msgid: '20f30417.9c7cfc', topic: '', payload: { key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '0.19', price: 484.55, symbol: 'ETH/USD', is_buy: false } }, key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '0.19', price: 484.55, symbol: 'ETH/USD', is_buy: false } } false { info: { order_id: '99068078', id: '99068078', symbol: 'ethusd', exchange: 'gemini', avg_execution_price: '0.00', side: 'sell', type: 'exchange limit', timestamp: '1530023603', timestampms: 1530023603284, is_live: true, is_cancelled: false, is_hidden: false, was_forced: false, executed_amount: '0', remaining_amount: '0.19', client_order_id: '5b228e720ea83970b58648aa', options: [], price: '484.55', original_amount: '0.19' }, id: '99068078' } { _msgid: '1fd361c3.2902ae', topic: '', payload: { key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '0.19', price: 484.55, symbol: 'ETH/USD', is_buy: true } } } { _msgid: '1fd361c3.2902ae', topic: '', payload: { key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '0.19', price: 484.55, symbol: 'ETH/USD', is_buy: true } }, key: 'fill', data: { _id: '5b228e720ea83970b58648aa', provider: 'gemini', quantity: '0.19', price: 484.55, symbol: 'ETH/USD', is_buy: true } } false { info: { order_id: '99068081', id: '99068081', symbol: 'ethusd', exchange: 'gemini', avg_execution_price: '181.91', side: 'buy', type: 'exchange limit', timestamp: '1530023613', timestampms: 1530023613115, is_live: false, is_cancelled: false, is_hidden: false, was_forced: false, executed_amount: '0.19', remaining_amount: '0', client_order_id: '5b228e720ea83970b58648aa', options: [], price: '484.55', original_amount: '0.19' }, id: '99068081' } */ // var moment = require('moment'); var ccxt = require('ccxt'); const utils = require('./lib/utils'); var exchange_manager = { process_order_result(node, msg, err, res, source) { console.log(node); console.log(msg); console.log(err); console.log(res); console.log(source); if(err) { console.log(err); var o_id = msg._id; // Check presense of word to see if funds specific error var f = err.toLowerCase().indexOf('insufficient') > -1 ? 'FUNDS' : err; var order_log_obj = { payload: { order_id: o_id, status: 'ERROR', msg: f, data: { response: res, tx: msg } } } console.log(order_log_obj); //this.emit(node, 'errors', order_log_obj); this.emit(node, 'orderlog', order_log_obj); return; } if(res) { // We have a result, lets figure the status // CANCEL = 0 fill // FILL = some fill // DONE = 100% filled var o_ammount = parseFloat(res.info.original_amount); var e_ammount = parseFloat(res.info.executed_amount); // var o_id = res.info.client_order_id; var o_id = msg._id; var status = ''; if(!e_ammount) { status = 'CANCEL'; } else { if(o_ammount == e_ammount) { status = 'DONE'; } else { status = 'FILLED'; } } var order_log_obj = { payload: { order_id: o_id, status: status, filled_amount: e_ammount, msg: 'OK', data: { res: res, tx: msg } } } console.log(order_log_obj); this.emit(node, 'orderlog', order_log_obj); return; } }, exchanges: {}, set_exchange(exchange_id, config) { var opts = { enableRateLimit: false, urls: {} }; // console.log(config); // Set USER / PASS CONFIG if(config.key) { opts.apiKey = config.key; } if(config.secret) { opts.secret = config.secret; } if(config.password) { opts.password = config.password; } var tmp = new ccxt[exchange_id] (opts); // Set TESTNET CONFIG // console.log(exchange_id); if(config.testnet && tmp.urls.test) { opts.urls.api = tmp.urls.test; } if(exchange_id == 'gemini' && config.testnet) { opts.urls.api = 'https://api.sandbox.gemini.com'; } //console.log(opts); this.exchanges[exchange_id] = new ccxt[exchange_id] (opts); }, async create_order(data, callback) { if(!this.exchanges[data.provider]) { callback(true, 'exchange not found'); return false; } var ccxt_obj = this.exchanges[data.provider]; try { var opt = { 'timeInForce': 'IOC', 'client_order_id': data._id.toString(), }; // Exchange specific fix if(data.provider == 'gemeni') { opt.options = [ 'immediate-or-cancel' ]; } const response = await ccxt_obj.createOrder( data.symbol, 'limit', (data.is_buy ? 'buy' : 'sell'), data.quantity, data.price, opt ); callback(false, response); } catch (e) { callback(e.constructor.name, e.message); } }, async get_balances(exchanges, callback) { var out = {}; if(exchanges == 'all') { exchanges = Object.keys(this.exchanges); } for(x in exchanges) { if(!this.exchanges[exchanges[x]]) { callback(true, 'Misconfigured Exchange :: ' + exchanges[x]); } } for(x in exchanges) { var ex = exchanges[x]; var cctx_obj = this.exchanges[ex]; let balances = await cctx_obj.fetchBalance().catch(function (e) { out[ex] = { ERR: { balance: 0, err: e }}; //console.log(e); }); if(!out[ex] && balances && balances.free) { out[ex] = {}; for(x in balances.free) { out[ex][x] = { balance: balances.free[x] }; } } } //console.log(out); callback(false, out); }, emit(node, type, msg) { var outputs = [ 'exchanges', 'balances', 'http', 'orderlog', 'errors' ]; var out = []; for(x in outputs) { if(type == outputs[x]) { out.push(msg); } else { out.push(null); } } node.send(out); } } module.exports = function(RED) { function exchangeManager(config) { RED.nodes.createNode(this,config); var node = this; var conf = { exchanges: {} }; if(config.conf && config.conf != '') { try { conf = JSON.parse(config.conf); } catch(e) { // } } // Set exchange in our helper object for(ex_id in conf.exchanges) { var ex_conf = conf.exchanges[ex_id]; if(ex_conf.balance) { ex_conf.auth.testnet = ex_conf.testnet; exchange_manager.set_exchange(ex_id, ex_conf.auth); } } node.on('input', function(msg) { console.log(msg); if(msg.req) { switch(req.route.path) { case '/create_order': msg.payload.key = 'fill'; msg.payload.data = msg.req.query; break; case '/balances': default: msg.payload.op = 'get_balance'; break; } } // Handle OrderBook specific incoming messages // Change mapping a bit if(msg.key && msg.data) { msg.payload.key = msg.key; msg.payload.data = msg.data; } if(msg.payload.key) { console.log(msg.payload); switch(msg.payload.key) { case 'fill': exchange_manager.create_order(msg.payload.data, function(err, res) { exchange_manager.process_order_result(node, msg.payload.data, err, res, 'orderbook'); }); break; } return; } // Nadda if(!msg.payload || !msg.payload.op) { // Make sure to release the request if(msg.req) { exchange_manager.emit(node, 'http', msg); } return false; } if(msg.payload.op == 'order_log') { console.log(msg); exchange_manager.emit(node, 'orderlog', { payload: msg.payload }); return; } if(msg.payload.op == 'get_balance') { if(!msg.payload.exchanges) { msg.payload.exchanges = 'all'; } exchange_manager.get_balances(msg.payload.exchanges, function(err, data) { var payload = { op: 'get_balance', exchanges: msg.payload.exchanges }; if(err) { payload.err = data; } else { payload.data = data; } msg.payload = payload; exchange_manager.emit(node, 'balances', msg); if(msg.req) { exchange_manager.emit(node, 'http', msg); } }); return; } if(msg.payload.op == 'create_order' && msg.payload.data) { exchange_manager.create_order(msg.payload.data, function(err, res) { exchange_manager.process_order_result(node, msg, err, res, 'rest'); }); return; } // Release hanging requests.. if(msg.req) { exchange_manager.emit(node, 'http', msg); } console.log(msg); }); } RED.nodes.registerType("exchangeManager", exchangeManager); }