@@ -14,6 +14,7 @@ const _pick = require('lodash/pick')
1414const _isEqual = require ( 'lodash/isEqual' )
1515const { genAuthSig, nonce } = require ( 'bfx-api-node-util' )
1616const getMessagePayload = require ( '../util/ws2' )
17+ const LosslessJSON = require ( 'lossless-json' )
1718
1819const {
1920 BalanceInfo,
@@ -104,6 +105,7 @@ class WSv2 extends EventEmitter {
104105 this . _packetWDTimeout = null
105106 this . _packetWDLastTS = 0
106107 this . _orderBooks = { }
108+ this . _losslessOrderBooks = { }
107109 this . _candles = { }
108110
109111 /**
@@ -553,22 +555,22 @@ class WSv2 extends EventEmitter {
553555 }
554556
555557 /**
556- * @param {string } msgJSON
558+ * @param {string } rawMsg
557559 * @param {string } flags
558560 * @private
559561 */
560- _onWSMessage ( msgJSON , flags ) {
561- debug ( 'recv msg: %s' , msgJSON )
562+ _onWSMessage ( rawMsg , flags ) {
563+ debug ( 'recv msg: %s' , rawMsg )
562564
563565 this . _packetWDLastTS = Date . now ( )
564566 this . _resetPacketWD ( )
565567
566568 let msg
567569
568570 try {
569- msg = JSON . parse ( msgJSON )
571+ msg = JSON . parse ( rawMsg )
570572 } catch ( e ) {
571- this . emit ( 'error' , `invalid message JSON: ${ msgJSON } ` )
573+ this . emit ( 'error' , `invalid message JSON: ${ rawMsg } ` )
572574 return
573575 }
574576
@@ -585,19 +587,20 @@ class WSv2 extends EventEmitter {
585587 this . emit ( 'message' , msg , flags )
586588
587589 if ( Array . isArray ( msg ) ) {
588- this . _handleChannelMessage ( msg )
590+ this . _handleChannelMessage ( msg , rawMsg )
589591 } else if ( msg . event ) {
590- this . _handleEventMessage ( msg )
592+ this . _handleEventMessage ( msg , rawMsg )
591593 } else {
592594 debug ( 'recv unidentified message: %j' , msg )
593595 }
594596 }
595597
596598 /**
597599 * @param {array } msg
600+ * @param {string } rawMsg
598601 * @private
599602 */
600- _handleChannelMessage ( msg ) {
603+ _handleChannelMessage ( msg , rawMsg ) {
601604 const [ chanId , type ] = msg
602605 const channelData = this . _channelMap [ chanId ]
603606
@@ -614,7 +617,7 @@ class WSv2 extends EventEmitter {
614617 return this . _handleOBChecksumMessage ( msg , channelData )
615618 }
616619
617- return this . _handleOBMessage ( msg , channelData )
620+ return this . _handleOBMessage ( msg , channelData , rawMsg )
618621 } else if ( channelData . channel === 'trades' ) {
619622 return this . _handleTradeMessage ( msg , channelData )
620623 } else if ( channelData . channel === 'ticker' ) {
@@ -669,15 +672,17 @@ class WSv2 extends EventEmitter {
669672 *
670673 * @param {Array|Array[] } msg
671674 * @param {Object } chanData - entry from _channelMap
675+ * @param {string } rawMsg
672676 * @private
673677 */
674- _handleOBMessage ( msg , chanData ) {
678+ _handleOBMessage ( msg , chanData , rawMsg ) {
679+
675680 const { symbol, prec } = chanData
676681 const raw = prec === 'R0'
677682 let data = getMessagePayload ( msg )
678683
679684 if ( this . _manageOrderBooks ) {
680- const err = this . _updateManagedOB ( symbol , data , raw )
685+ const err = this . _updateManagedOB ( symbol , data , raw , rawMsg )
681686
682687 if ( err ) {
683688 this . emit ( 'error' , err )
@@ -710,11 +715,24 @@ class WSv2 extends EventEmitter {
710715 * @return {Error } err - null on success
711716 * @private
712717 */
713- _updateManagedOB ( symbol , data , raw ) {
718+ _updateManagedOB ( symbol , data , raw , rawMsg ) {
719+ // parse raw string with lossless parse which takes
720+ // the exact strict values rather than converting to floats
721+ // [0.00001, [1, 2, 3]] -> ['0.00001', ['1', '2', '3']]
722+ const rawLossless = LosslessJSON . parse ( rawMsg , ( key , value ) => {
723+ if ( value && value . isLosslessNumber ) {
724+ return value . toString ( )
725+ }
726+ else {
727+ return value ;
728+ }
729+ } )
730+ const losslessUpdate = rawLossless [ 1 ]
714731 // Snapshot, new OB. Note that we don't protect against duplicates, as they
715732 // could come in on re-sub
716733 if ( Array . isArray ( data [ 0 ] ) ) {
717734 this . _orderBooks [ symbol ] = data
735+ this . _losslessOrderBooks [ symbol ] = losslessUpdate
718736 return null
719737 }
720738
@@ -724,7 +742,7 @@ class WSv2 extends EventEmitter {
724742 }
725743
726744 OrderBook . updateArrayOBWith ( this . _orderBooks [ symbol ] , data , raw )
727-
745+ OrderBook . updateArrayOBWith ( this . _losslessOrderBooks [ symbol ] , losslessUpdate , raw )
728746 return null
729747 }
730748
@@ -735,7 +753,7 @@ class WSv2 extends EventEmitter {
735753 * @return {Error } err - null if none
736754 */
737755 _verifyManagedOBChecksum ( symbol , prec , cs ) {
738- const ob = this . _orderBooks [ symbol ]
756+ const ob = this . _losslessOrderBooks [ symbol ]
739757
740758 if ( ! ob ) return null
741759
@@ -762,6 +780,21 @@ class WSv2 extends EventEmitter {
762780 return new OrderBook ( this . _orderBooks [ symbol ] )
763781 }
764782
783+ /**
784+ * Returns an up-to-date lossless copy of the order book for the specified symbol, or
785+ * null if no OB is managed for that symbol. All amounts and prices are in original
786+ * string format.
787+ * Set `manageOrderBooks: true` in the constructor to use.
788+ *
789+ * @param {string } symbol
790+ * @return {OrderBook } ob - null if not found
791+ */
792+ getLosslessOB ( symbol ) {
793+ if ( ! this . _losslessOrderBooks [ symbol ] ) return null
794+
795+ return new OrderBook ( this . _losslessOrderBooks [ symbol ] )
796+ }
797+
765798 /**
766799 * @param {Array } msg
767800 * @param {Object } chanData
@@ -1045,9 +1078,10 @@ class WSv2 extends EventEmitter {
10451078
10461079 /**
10471080 * @param {Object } msg
1081+ * @param {string } rawMsg
10481082 * @private
10491083 */
1050- _handleEventMessage ( msg ) {
1084+ _handleEventMessage ( msg , rawMsg ) {
10511085 if ( msg . event === 'auth' ) {
10521086 return this . _handleAuthEvent ( msg )
10531087 } else if ( msg . event === 'subscribed' ) {
0 commit comments