Skip to content

Instantly share code, notes, and snippets.

@spaced-out-mvc
Forked from flavioespinoza/WebsocketSample.js
Last active July 6, 2019 09:55
Show Gist options
  • Save spaced-out-mvc/a96e1c47594542873541e8e1b97ef15b to your computer and use it in GitHub Desktop.
Save spaced-out-mvc/a96e1c47594542873541e8e1b97ef15b to your computer and use it in GitHub Desktop.
Bittres Websocket Node.js Authentication Example
/**
* Node.js example
* https://github.com/Bittrex/beta
*
* By Flavio Espinoza
*
* Expanding on example from Adrian Soluch
*
* prerequisites:
* npm i signalr-client crypto lodash ololog
*
* tested on node v9.10.1
*/
const signalR = require('signalr-client')
const zlib = require('zlib')
const crypto = require('crypto')
const _ = require('lodash')
const log = require('ololog')
.configure({
locate: false
}) // console.log() in colors
const client = new signalR.client('wss://beta.bittrex.com/signalr', ['c2'])
let market = 'BTC-ZCL'
/** Bittrex Credentials */
const api_key = 'your_api_key'
const secret_key = 'your_secret_key'
const keyAlias = [
{
key: 'A',
val: 'Ask'
},
{
key: 'a',
val: 'Available'
},
{
key: 'B',
val: 'Bid'
},
{
key: 'b',
val: 'Balance'
},
{
key: 'C',
val: 'Closed'
},
{
key: 'c',
val: 'Currency'
},
{
key: 'D',
val: 'Deltas'
},
{
key: 'd',
val: 'Delta'
},
{
key: 'E',
val: 'Exchange'
},
{
key: 'e',
val: 'ExchangeDeltaType'
},
{
key: 'F',
val: 'FillType'
},
{
key: 'f',
val: 'Fills'
},
{
key: 'G',
val: 'OpenBuyOrders'
},
{
key: 'g',
val: 'OpenSellOrders'
},
{
key: 'H',
val: 'High'
},
{
key: 'h',
val: 'AutoSell'
},
{
key: 'I',
val: 'Id'
},
{
key: 'i',
val: 'IsOpen'
},
{
key: 'J',
val: 'Condition'
},
{
key: 'j',
val: 'ConditionTarget'
},
{
key: 'K',
val: 'ImmediateOrCancel'
},
{
key: 'k',
val: 'IsConditional'
},
{
key: 'L',
val: 'Low'
},
{
key: 'l',
val: 'Last'
},
{
key: 'M',
val: 'MarketName'
},
{
key: 'm',
val: 'BaseVolume'
},
{
key: 'N',
val: 'Nonce'
},
{
key: 'n',
val: 'CommissionPaid'
},
{
key: 'O',
val: 'Orders'
},
{
key: 'o',
val: 'Order'
},
{
key: 'P',
val: 'Price'
},
{
key: 'p',
val: 'CryptoAddress'
},
{
key: 'Q',
val: 'Quantity'
},
{
key: 'q',
val: 'QuantityRemaining'
},
{
key: 'R',
val: 'Rate'
},
{
key: 'r',
val: 'Requested'
},
{
key: 'S',
val: 'Sells'
},
{
key: 's',
val: 'Summaries'
},
{
key: 'T',
val: 'TimeStamp'
},
{
key: 't',
val: 'Total'
},
{
key: 'U',
val: 'Uuid'
},
{
key: 'u',
val: 'Updated'
},
{
key: 'V',
val: 'Volume'
},
{
key: 'W',
val: 'AccountId'
},
{
key: 'w',
val: 'AccountUuid'
},
{
key: 'X',
val: 'Limit'
},
{
key: 'x',
val: 'Created'
},
{
key: 'Y',
val: 'Opened'
},
{
key: 'y',
val: 'State'
},
{
key: 'Z',
val: 'Buys'
},
{
key: 'z',
val: 'Pending'
},
{
key: 'CI',
val: 'CancelInitiated'
},
{
key: 'FI',
val: 'FillId'
},
{
key: 'DT',
val: 'OrderDeltaType'
},
{
key: 'OT',
val: 'OrderType'
},
{
key: 'OU',
val: 'OrderUuid'
},
{
key: 'PD',
val: 'PrevDay'
},
{
key: 'TY',
val: 'Type'
},
{
key: 'PU',
val: 'PricePerUnit'
}
];
/** Authentication Signature Function */
function signature(secret_key, challenge) {
return crypto.createHmac('sha512', secret_key)
.update(challenge)
.digest('hex')
}
/** Websocket On Update Functions */
function on_public(__update) {
let raw = new Buffer.from(__update, 'base64');
zlib.inflateRaw(raw, function(err, inflated) {
if (!err) {
let obj = JSON.parse(inflated.toString('utf8'));
console.log(reformatKeys(obj));
}
})
}
function on_private(__update) {
let raw = new Buffer.from(__update, 'base64')
zlib.inflateRaw(raw, function(err, inflated) {
if (!err) {
let obj = JSON.parse(inflated.toString('utf8'))
if (obj.o) {
/** Order Updates */
let order = updated_order(obj)
if (order.side === 'buy') {
log.blue('buy_order_update', JSON.stringify(order, null, 2))
} else if (order.side === 'sell') {
log.lightRed('sell_order_update', JSON.stringify(order, null, 2))
}
} else {
/** Balance Updates */
let balance = updated_balance(obj.d)
log.green('updated_balance', JSON.stringify(balance, null, 2))
}
}
})
}
/** Websocket Client Connect */
client.serviceHandlers.connected = function(connection) {
console.log('connected')
/** Authentication Context */
client.call('c2', 'GetAuthContext', api_key)
.done(function(err, challenge) {
if (err) {
log.red(err)
}
/** Signature */
const signed_challenge = signature(secret_key, challenge)
/** Authenticate */
client.call('c2', 'Authenticate', api_key, signed_challenge)
.done(function(auth_err, auth_result) {
if (auth_err) {
log.red('auth_ERROR', auth_err)
}
log.yellow('auth_result', auth_result)
/** Balance Updated */
client.on('c2', 'uB', on_private)
/** Order Update */
client.on('c2', 'uO', on_private)
/** Exchange Update */
client.call('c2', 'SubscribeToExchangeDeltas', market)
.done(function(err, result) {
if (err) {
return console.error(err)
}
if (result === true) {
client.on('c2', 'uE', on_public)
}
})
/** Summary Update */
client.call('c2', 'SubscribeToSummaryDeltas')
.done(function(err, result) {
if (err) {
return console.error(err)
}
if (result === true) {
client.on('c2', 'uS', on_public)
}
})
})
})
}
/** Response Formatting Helper Functions */
function updated_order(__order) {
const map = _.map([__order], function(__obj) {
const order = __obj.o
const info = _.mapKeys(order, function(__val, __key) {
let key_long = map_keys(__key)
return key_long
})
return {
status: status(__obj.TY),
amount: __obj.o.Q,
remaining: __obj.o.q,
price: __obj.o.X,
average: __obj.o.PU,
uuid: __obj.o.U,
id: __obj.o.OU,
market_name: __obj.o.E,
symbol: symbol(__obj.o.E),
side: side(__obj.o.OT),
info: info
}
})
return map[0]
}
function updated_balance(__balance) {
return _.mapKeys(__balance, function(__val, __key) {
let key_long = map_keys(__key)
return key_long
})
}
function summary_current_market(__summary) {
return _.mapKeys(__summary, function(__val, __key) {
let key_long = map_keys(__key)
return key_long
})
}
function symbol (__market) {
let split = __market.split('-')
let base = split[0]
let comp = split[1]
return comp + '/' + base
}
function side(__order_type) {
if (__order_type === 'LIMIT_BUY') {
return 'buy'
} else if (__order_type === 'LIMIT_SELL') {
return 'sell'
}
}
function status(__id) {
const types = [{
id: 0,
status: 'open'
}, {
id: 1,
status: 'partial'
}, {
id: 2,
status: 'fill'
}, {
id: 3,
status: 'cancel'
}]
let filter = types.filter(function(__obj) {
return __obj.id === __id
})
return filter[0].status
}
function reformatKeys(data)
{
if (Array.isArray(data)) {
data.forEach(function(value, key) {
data[key] = reformatKeys(value);
});
} else if(typeof data === 'object') {
Object.keys(data).forEach(function(key) {
let newKey = getAlias(key);
let value = data[key];
data[newKey] = value;
delete data[key];
if(value instanceof Array) {
data[newKey] = reformatKeys(value);
} else if(typeof data === 'object') {
data[newKey] = reformatKeys(value);
}
});
}
return data;
}
function getAlias(key)
{
let response = null;
keyAlias.forEach(function(element) {
if(key === element.key) {
response = element.val;
}
});
if(response !== null) {
return response;
} else {
console.log('failed to match alias', key);
}
}
function map_keys(__key) {
const min_keys = [
{
key: 'A',
val: 'Ask'
},
{
key: 'a',
val: 'Available'
},
{
key: 'B',
val: 'Bid'
},
{
key: 'b',
val: 'Balance'
},
{
key: 'C',
val: 'Closed'
},
{
key: 'c',
val: 'Currency'
},
{
key: 'D',
val: 'Deltas'
},
{
key: 'd',
val: 'Delta'
},
{
key: 'E',
val: 'Exchange'
},
{
key: 'e',
val: 'ExchangeDeltaType'
},
{
key: 'F',
val: 'FillType'
},
{
key: 'f',
val: 'Fills'
},
{
key: 'G',
val: 'OpenBuyOrders'
},
{
key: 'g',
val: 'OpenSellOrders'
},
{
key: 'H',
val: 'High'
},
{
key: 'h',
val: 'AutoSell'
},
{
key: 'I',
val: 'Id'
},
{
key: 'i',
val: 'IsOpen'
},
{
key: 'J',
val: 'Condition'
},
{
key: 'j',
val: 'ConditionTarget'
},
{
key: 'K',
val: 'ImmediateOrCancel'
},
{
key: 'k',
val: 'IsConditional'
},
{
key: 'L',
val: 'Low'
},
{
key: 'l',
val: 'Last'
},
{
key: 'M',
val: 'MarketName'
},
{
key: 'm',
val: 'BaseVolume'
},
{
key: 'N',
val: 'Nonce'
},
{
key: 'n',
val: 'CommissionPaid'
},
{
key: 'O',
val: 'Orders'
},
{
key: 'o',
val: 'Order'
},
{
key: 'P',
val: 'Price'
},
{
key: 'p',
val: 'CryptoAddress'
},
{
key: 'Q',
val: 'Quantity'
},
{
key: 'q',
val: 'QuantityRemaining'
},
{
key: 'R',
val: 'Rate'
},
{
key: 'r',
val: 'Requested'
},
{
key: 'S',
val: 'Sells'
},
{
key: 's',
val: 'Summaries'
},
{
key: 'T',
val: 'TimeStamp'
},
{
key: 't',
val: 'Total'
},
{
key: 'U',
val: 'Uuid'
},
{
key: 'u',
val: 'Updated'
},
{
key: 'V',
val: 'Volume'
},
{
key: 'W',
val: 'AccountId'
},
{
key: 'w',
val: 'AccountUuid'
},
{
key: 'X',
val: 'Limit'
},
{
key: 'x',
val: 'Created'
},
{
key: 'Y',
val: 'Opened'
},
{
key: 'y',
val: 'State'
},
{
key: 'Z',
val: 'Buys'
},
{
key: 'z',
val: 'Pending'
},
{
key: 'CI',
val: 'CancelInitiated'
},
{
key: 'FI',
val: 'FillId'
},
{
key: 'DT',
val: 'OrderDeltaType'
},
{
key: 'OT',
val: 'OrderType'
},
{
key: 'OU',
val: 'OrderUuid'
},
{
key: 'PD',
val: 'PrevDay'
},
{
key: 'TY',
val: 'Type'
},
{
key: 'PU',
val: 'PricePerUnit'
}
]
return _.filter(min_keys, function(__obj) {
return __obj.key === __key
})[0].val
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment