Skip to content

Instantly share code, notes, and snippets.

@flavioespinoza
Last active September 13, 2021 19:31
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save flavioespinoza/a69dacfac67739c26b30dab4bbddaf6b to your computer and use it in GitHub Desktop.
Save flavioespinoza/a69dacfac67739c26b30dab4bbddaf6b 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'
/** 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'))
if (obj.f) {
log.lightGray('uE update...', JSON.stringify(obj, null, 2))
} else {
let current_market = _.filter(obj.D, function(__obj) {
return __obj.M === market
})
if (current_market.length > 0) {
const summary = summary_current_market(current_market[0])
log.lightBlue('uS update...', JSON.stringify(summary))
}
}
}
})
}
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 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
}
@spaced-out-mvc
Copy link

spaced-out-mvc commented Oct 29, 2018

Hi, This code is very good, i added an auto key formatter here - https://gist.github.com/space-mvc/a96e1c47594542873541e8e1b97ef15b

checkout the

  1. const keyAlias (which could be a function to condense the code)
  2. reformatKeys()
  3. getAlias()

I am working on an arbitrage trading gui if you would like to help, next i will need or create a script for Yobit which can do roughly the same thing at least for the last price summaries per currency pair. Do you have anything similar for any other exchanges?

send me an email if you are interested mrdaniellee2020@gmail.com

@npomfret
Copy link

Good work

@fmichaud
Copy link

Great. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment