UNPKG

crypto-nodes

Version:

563 lines (392 loc) 14.7 kB
const GeminiAPI = require('gemini-api').default; var gemeni_fix = { uuid_map: {}, init: function (node, config) { //console.log('LOADING GEMENI FIX'); this.node = node; this.config = config; // this.connect(); }, connect: function() { var that = this; if(!this.config.server_fix || !this.config.server_fix_port) { that.node.status({ fill: 'red', shape:'ring', text: "Missing FIX Config" }); return; } if(!this.config.api_key || !this.config.api_secret || !this.config.api_pass) { that.node.status({ fill: 'red', shape:'ring', text: "Missing FIX Auth" }); return; } this.stream = tls.connect({ host: 'fix-public.sandbox.gdax.com', port: '4198' }, function() { that.node.status({ fill: 'green', shape:'ring', text: "FIX Connected" }); }); this.client = fix.createClient(this.stream); this.session = this.client.session(this.config.api_key, 'Coinbase'); // Stream Events // this.stream.on('data', function(data) { console.log("FIX DATA :: " + data.toString()); }); this.stream.on('error', function(err) { console.log("FIX ERR :: " + err); }); this.stream.on('close', function () { console.log("FIX DISCONNECT"); }); this.stream.on('end', function() { console.log('FIX STREAM ENDED'); }); // Session Events this.session.on('send', function(msg) { /*console.log('sending message: %s', msg);*/ }); this.session.on('error', function(err) { console.error(err.stack); }); this.session.on('logout', function() { console.log('FIX SESSION :: Logged out'); }); this.session.on('end', function() { console.log('FIX SESSION :: Ended'); this.stream.end(); }); this.session.on('logon', function() { that.node.status({ fill: 'green', shape:'ring', text: "FIX Authenticated" }); }); // this.session.on('Reject', function(msg, next) { console.log('reject: %s', msg); next(); }); // this.session.on('OrderCancelReject', function(msg, next) { console.log('order cancel reject: %s', msg); next(); }); // The rest of these are handlers for various FIX keywords this.session.on('ExecutionReport', function(msg, next) { that.node.status({ fill: 'green', shape:'ring', text: "FIX Order Report" }); // console.log('FIX ORDER DATA :: ' + msg.toString()); var fnk_map = { '0': 'Added', '1': 'Filled', '3': 'Done', '4': 'Canceled', '7': 'Stopped', '8': 'Rejected', 'D': 'Changed', 'I': 'Info' }; // console.log(msg.ExecType); switch(msg.ExecType) { case '0': // NEW if(that.uuid_map[msg.ClOrdID]) { // Create mapping between REMOTE ORDER ID and LOCAL ORDER ID that.uuid_map[msg.OrderID] = that.uuid_map[msg.ClOrdID]; var order_log_obj = { op: 'order_log', order_id: that.uuid_map[msg.OrderID], status: 'ADDED', msg: 'Order Added', data: { exchange: 'gdax', protocol: 'fix', data: msg.toString() } } that.node.send({ payload: order_log_obj }); } break; case '1': // FILL var order_log_obj = { op: 'order_log', order_id: that.uuid_map[msg.OrderID], status: 'FILLED', filled_amount: parseFloat(msg.LastShares), msg: 'Order Filled', data: { exchange: 'gdax', protocol: 'fix', data: msg.toString() } } that.node.send({ payload: order_log_obj }); break; case '3': // DONE var order_log_obj = { op: 'order_log', order_id: that.uuid_map[msg.OrderID], status: 'DONE', msg: 'Order Done', data: { exchange: 'gdax', protocol: 'fix', data: msg.toString() } } that.node.send({ payload: order_log_obj }); break; case '4': // CANCELED var order_log_obj = { op: 'order_log', order_id: that.uuid_map[msg.OrderID], status: 'CANCEL', msg: 'Order Canceled', data: { exchange: 'gdax', protocol: 'fix', data: msg.toString() } } that.node.send({ payload: order_log_obj }); break; case '7': // STOPPED var order_log_obj = { op: 'order_log', order_id: that.uuid_map[msg.OrderID], status: 'CANCEL', msg: 'Order Stopped', data: { exchange: 'gdax', protocol: 'fix', data: msg.toString() } } that.node.send({ payload: order_log_obj }); break; case '8': // REJECTED var order_log_obj = { op: 'order_log', order_id: that.uuid_map[msg.OrderID], status: 'ERROR', msg: (msg.OrdRejReason && msg.OrdRejReason == 3 ? 'FUNDS': 'OTHER'), data: { exchange: 'gdax', protocol: 'fix', data: msg.toString() } } that.node.send({ payload: order_log_obj }); break; case 'D': // CHANGED break; case 'I': // STATUS var order_log_obj = { op: 'order_log', order_id: that.uuid_map[msg.OrderID], status: 'INFO', msg: 'Order Info', data: { exchange: 'gdax', protocol: 'fix', data: msg.toString() } } that.node.send({ payload: order_log_obj }); break; } //if(fnk_map[msg.ExecType]) { if(that.uuid_map[msg.OrderID]) { var text = 'Order ' + fnk_map[msg.ExecType] + '! Local ID ' + that.uuid_map[msg.OrderID] || ' NOT FOUND'; console.log(text); that.node.status({ fill: 'green', shape:'ring', text: text }); } //} next(); }); // DO LOGIN :: var logon = new fix.Msgs.Logon(); logon.SendingTime = new Date(); logon.HeartBtInt = 30; logon.EncryptMethod = 0; logon.set(554, this.config.api_pass); // FIX 4.4 Password tag var presign = [ logon.SendingTime, logon.MsgType, this.session.outgoing_seq_num, this.session.sender_comp_id, this.session.target_comp_id, this.config.api_pass ]; var what = presign.join('\x01'); logon.RawData = this.sign(what, this.config.api_secret); // console.log(logon); this.session.send(logon, true); }, status: function () { var orders = new fix.Msgs.OrderStatusRequest(); orders.OrderID = '*'; this.session.send(orders); }, sign: function(what, secret) { var key = Buffer(secret, 'base64'); var hmac = crypto.createHmac('sha256', key); //console.log("presign: " + what); var signature = hmac.update(what).digest('base64'); return signature; }, sendOrder(symbol, price, amount, is_buy, id) { var order = new fix.Msgs.NewOrderSingle(); var order_uuid = uuid(); // Map our numeric ID's to UUID to send to order this.uuid_map[order_uuid] = id; // Populate order object order.Symbol = symbol; order.ClOrdID = order_uuid; order.Side = is_buy ? 1 : 2; order.HandlInst = 1; order.OrdType = 2; // 2=Limit order.OrderQty = amount; order.Price = price; order.TimeInForce = '3'; // 1=GTC 3=IOC order.TransactTime = new Date(); order.set(7928, 'D'); // STP this.session.send(order); }, cancelOrder: function(ClOrdID, OrderID, symbol) { var cancel = new fix.Msgs.OrderCancelRequest(); cancel.Symbol = symbol; cancel.OrigClOrdID = ClOrdID; cancel.ClOrdID = 123456; cancel.OrderID = OrderID; this.session.send(cancel); } } var gemini = { fix: gemeni_fix, order_id_map: {}, order: function(data, node) { var that = this; gemini.order_node = node; // CCTX / FIX / WS gemini.utils.ccxt_order('gemini', data, node, function (err, res) { if(err) { node.status({ fill: 'red', shape:'ring', text: res }); // Emit to order log.. } else { node.status({ fill: 'green', shape:'ring', text: 'Sent order OK' }); console.log(res); /*{ info: { order_id: '94785411', id: '94785411', symbol: 'ethbtc', exchange: 'gemini', avg_execution_price: '0.07653', side: 'buy', type: 'exchange limit', timestamp: '1527511832', timestampms: 1527511832456, is_live: false, is_cancelled: false, is_hidden: false, was_forced: false, executed_amount: '0.01', remaining_amount: '0', client_order_id: '1527511832', options: [], price: '0.07653', original_amount: '0.01' },*/ that.order_id_map[res.info.order_id] = data.tx.acc; var order_log_obj = { op: 'order_log', order_id: data.tx.acc, status: 'ADDED', msg: 'Order added at exchange', data: { exchange: 'gemini', protocol: 'api', data: res } } node.send({ payload: order_log_obj }); } }); return; }, data: { setup: function(node, config) { // Initialize object with node and config which might contain AUTH and TESTNET params gemini.data.node = node; gemini.data.config = config; //console.log(gemini.ccxt); var that = this; // //console.log('INIT CONNECETIONS'); //console.log(this.config); that.websocketClient = new GeminiAPI.WebsocketClient({ key: this.config.api_key, secret: this.config.api_secret, sandbox: this.config.testnet }); that.websocketClient.openOrderSocket(); that.websocketClient.orderSocket.on('open', function (d) { }); that.websocketClient.orderSocket.on('message', function (msg) { try { msg = JSON.parse(msg); } catch(e) { return; } if(msg && msg.length) { for(x in msg) { var row = msg[x]; console.log(row); if(row.type) { if(row.order_id) { if(!gemini.order_id_map[row.order_id]) { if(row.client_order_id) { gemini.order_id_map[row.order_id] = row.client_order_id; } else { gemini.order_id_map[row.order_id] = ' unknown '; } } } switch(row.type) { case 'initial': // Not interesting.. // console.log('ORDER ' + gemini.order_id_map[row.order_id] + ' UPDATE ' + row.executed_amount + ' of ' + row.original_amount + ' filled ' ); break; case 'accepted': console.log('ORDER ' + gemini.order_id_map[row.order_id] + ' ACCEPTED'); // {"type":"accepted","order_id":"94785291","event_id":"94785292","client_order_id":"1527508720","api_session":"ggtwNc2VS01UTAAgYDy2","symbol":"ethbtc","side":"buy","order_type":"exchange limit","timestamp":"1527508720","timestampms":1527508720539,"is_live":true,"is_cancelled":false,"is_hidden":false,"original_amount":"0.01","price":"0.07653","socket_sequence":3} var order_log_obj = { op: 'order_log', order_id: gemini.order_id_map[row.order_id], status: 'ACCEPTED', msg: 'Order Accepted at exchange', data: { exchange: 'gemini', protocol: 'ws', data: msg.toString() } } if(gemini.order_node) { //console.log(order_log_obj); gemini.order_node.send({ payload: order_log_obj }); } break; case 'fill': console.log('ORDER ' + gemini.order_id_map[row.order_id] + ' FILLED WITH ' + row.executed_amount); // {"type":"fill","order_id":"94785291","client_order_id":"1527508720","api_session":"ggtwNc2VS01UTAAgYDy2","symbol":"ethbtc","side":"buy","order_type":"exchange limit","timestamp":"1527508720","timestampms":1527508720539,"is_live":false,"is_cancelled":false,"is_hidden":false,"avg_execution_price":"0.07653","executed_amount":"0.01","remaining_amount":"0","original_amount":"0.01","price":"0.07653","fill":{"trade_id":"94785293","liquidity":"Taker","price":"0.07653","amount":"0.01","fee":"0.00000191325","fee_currency":"BTC"},"socket_sequence":4} var order_log_obj = { op: 'order_log', order_id: gemini.order_id_map[row.order_id], status: 'FILLED', filled_amount: parseFloat(row.executed_amount), msg: 'Order Filled at exchange', data: { exchange: 'gemini', protocol: 'ws', data: row } } if(gemini.order_node) { //console.log(order_log_obj); gemini.order_node.send({ payload: order_log_obj }); } break; case 'closed': console.log('ORDER ' + gemini.order_id_map[row.order_id] + ' CLOSED ' + row.remaining_amount + ' REMAINING'); // {"type":"closed","order_id":"94785291","event_id":"94785295","client_order_id":"1527508720","api_session":"ggtwNc2VS01UTAAgYDy2","symbol":"ethbtc","side":"buy","order_type":"exchange limit","timestamp":"1527508720","timestampms":1527508720539,"is_live":false,"is_cancelled":false,"is_hidden":false,"avg_execution_price":"0.07653","executed_amount":"0.01","remaining_amount":"0","original_amount":"0.01","price":"0.07653","socket_sequence":5} var order_log_obj = { op: 'order_log', order_id: gemini.order_id_map[row.order_id], status: (row.remaining_amount == 0 ? 'DONE' : 'CANCEL'), msg: 'Order Closed at exchange', data: { exchange: 'gemini', protocol: 'ws', data: row } } if(gemini.order_node) { //console.log(order_log_obj); gemini.order_node.send({ payload: order_log_obj }); } break; } } } } }); return true; }, close: function () { console.log('CLOSE CONNECETIONS'); }, subscribe: function (symbols) { var that = this; // Either Pass it back to generic UTILS CCXT subscribe method // Or do something with the data .. gemini.utils.ccxt_subscribe('gemini', symbols, this.node); return; //console.log(that.websocketClient.marketSocket); for(x in symbols) { var symbol = symbols[x]; if(symbol in gemini.ccxt.markets) { that.websocketClient.openMarketSocket(gemini.ccxt.markets[symbol].id, () => { that.websocketClient.addMarketMessageListener(data => { if(data.type == 'update') { console.log('UP :: ' + data.events.length); } else { console.log(data); } }) }); } } that.websocketClient.marketSocket.on('message', function (d) { console.log('message!'); } ); // that.websocketClient.marketSocket.on('close', function () { console.log('CLOSED!'); } ); // that.websocketClient.marketSocket.on('error', function (e) { console.log('ERR!' + e); } ); // that.websocketClient.marketSocket.on('disconnect', function () { console.log('ERR!'); } ); }, init: function (obj, config, tickCB) { this.config = config; // Config containing authentication for creating out CCXT object this.tickCB = typeof(tickCB) == 'function' ? tickCB : false; }, connect: function () { // If this config.auth PRIVATE else PUBLIC else NOT SUPPORTED }, tick: function(data) { // CALLBACK FOR DATA if(this.tickCB) { this.tickCB(data); } } } } module.exports = gemini;