Skip to content

Commit c69ebfe

Browse files
committed
(feature) document ws2
1 parent 8166df9 commit c69ebfe

File tree

1 file changed

+133
-23
lines changed

1 file changed

+133
-23
lines changed

lib/ws/ws2.rb

Lines changed: 133 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@
2727
require_relative '../models/wallet'
2828

2929
module Bitfinex
30+
###
31+
# Implements version 2 of the Bitfinex WebSocket API, taking an evented
32+
# approach. Incoming packets trigger event broadcasts with names relevant to
33+
# the individual packets. Provides order manipulation methods that support
34+
# callback blocks, which are called when the relevant confirmation
35+
# notifications are received
36+
###
3037
class WSv2
3138
include Emittr::Events
3239

@@ -40,6 +47,18 @@ class WSv2
4047
FLAG_SEQ_ALL = 65536, # enable sequencing
4148
FLAG_CHECKSUM = 131072 # enable OB checksums, top 25 levels per side
4249

50+
###
51+
# Creates a new instance of the class
52+
#
53+
# @param [Hash] params
54+
# @param [string] params.url - connection URL
55+
# @param [string] params.api_key
56+
# @param [string] params.api_secret
57+
# @param [boolean] params.manage_order_books - if true, order books are persisted internally, allowing for automatic checksum verification
58+
# @param [boolean] params.transform - if true, full models are returned in place of array data
59+
# @param [boolean] params.seq_audit - enables automatic seq number verification
60+
# @param [boolean] params.checksum_audit - enables automatic OB checksum verification (requires manage_order_books)
61+
###
4362
def initialize (params = {})
4463
@l = Logger.new(STDOUT)
4564
@l.progname = 'ws2'
@@ -62,7 +81,7 @@ def initialize (params = {})
6281
@last_auth_seq = nil
6382
end
6483

65-
def on_open (e)
84+
def on_open (e) # :nodoc:
6685
@l.info 'client open'
6786
@is_open = true
6887
emit(:open)
@@ -71,7 +90,7 @@ def on_open (e)
7190
enable_ob_checksums if @checksum_audit
7291
end
7392

74-
def on_message (e)
93+
def on_message (e) # :nodoc:
7594
@l.info "recv #{e.data}"
7695

7796
msg = JSON.parse(e.data)
@@ -80,12 +99,15 @@ def on_message (e)
8099
emit(:message, msg)
81100
end
82101

83-
def on_close (e)
102+
def on_close (e) # :nodoc:
84103
@l.info 'client closed'
85104
@is_open = false
86105
emit(:close)
87106
end
88107

108+
###
109+
# Opens the websocket client inside an eventmachine run block
110+
###
89111
def open!
90112
if @is_open
91113
raise Exception, 'already open'
@@ -108,11 +130,14 @@ def open!
108130
}
109131
end
110132

133+
###
134+
# Closes the websocket client
135+
###
111136
def close!
112137
@ws.close
113138
end
114139

115-
def process_message (msg)
140+
def process_message (msg) # :nodoc:
116141
if @seq_audit
117142
validate_message_seq(msg)
118143
end
@@ -124,7 +149,7 @@ def process_message (msg)
124149
end
125150
end
126151

127-
def validate_message_seq (msg)
152+
def validate_message_seq (msg) # :nodoc:
128153
return unless @seq_audit
129154
return unless msg.kind_of?(Array)
130155
return unless msg.size > 2
@@ -171,7 +196,7 @@ def validate_message_seq (msg)
171196
@last_auth_seq = auth_seq
172197
end
173198

174-
def process_channel_message (msg)
199+
def process_channel_message (msg) # :nodoc:
175200
if !@channel_map.include?(msg[0])
176201
@l.error "recv message on unknown channel: #{msg[0]}"
177202
return
@@ -202,7 +227,7 @@ def process_channel_message (msg)
202227
end
203228
end
204229

205-
def handle_ticker_message (msg, chan)
230+
def handle_ticker_message (msg, chan) # :nodoc:
206231
payload = msg[1]
207232

208233
if chan['symbol'][0] === 't'
@@ -212,7 +237,7 @@ def handle_ticker_message (msg, chan)
212237
end
213238
end
214239

215-
def handle_trades_message (msg, chan)
240+
def handle_trades_message (msg, chan) # :nodoc:
216241
if msg[1].kind_of?(Array)
217242
payload = msg[1]
218243
emit(:public_trades, chan['symbol'], @transform ? payload.map { |t| Models::PublicTrade.new(t) } : payload)
@@ -230,7 +255,7 @@ def handle_trades_message (msg, chan)
230255
end
231256
end
232257

233-
def handle_candles_message (msg, chan)
258+
def handle_candles_message (msg, chan) # :nodoc:
234259
payload = msg[1]
235260

236261
if payload[0].kind_of?(Array)
@@ -240,7 +265,7 @@ def handle_candles_message (msg, chan)
240265
end
241266
end
242267

243-
def handle_order_book_checksum_message (msg, chan)
268+
def handle_order_book_checksum_message (msg, chan) # :nodoc:
244269
key = "#{chan['symbol']}:#{chan['prec']}:#{chan['len']}"
245270
emit(:checksum, chan['symbol'], msg)
246271

@@ -259,7 +284,7 @@ def handle_order_book_checksum_message (msg, chan)
259284
end
260285
end
261286

262-
def handle_order_book_message (msg, chan)
287+
def handle_order_book_message (msg, chan) # :nodoc:
263288
ob = msg[1]
264289

265290
if @manage_obs
@@ -282,7 +307,7 @@ def handle_order_book_message (msg, chan)
282307
end
283308

284309
# Resolves/rejects any pending promise associated with the notification
285-
def handle_notification_promises (n)
310+
def handle_notification_promises (n) # :nodoc:
286311
type = n[1]
287312
payload = n[4]
288313
status = n[6]
@@ -331,7 +356,7 @@ def handle_notification_promises (n)
331356
end
332357
end
333358

334-
def handle_auth_message (msg, chan)
359+
def handle_auth_message (msg, chan) # :nodoc:
335360
type = msg[1]
336361
return if type == 'hb'
337362
payload = msg[2]
@@ -401,6 +426,16 @@ def handle_auth_message (msg, chan)
401426
end
402427
end
403428

429+
###
430+
# Subscribes to the specified channel; params dictate the channel filter
431+
#
432+
# @param [string] channel - i.e. 'trades', 'candles', etc
433+
# @param [Hash] params
434+
# @param [string?] params.symbol
435+
# @param [string?] params.prec - for order book channels
436+
# @param [string?] params.len - for order book channels
437+
# @param [string?] params.key - for candle channels
438+
###
404439
def subscribe (channel, params = {})
405440
@l.info 'subscribing to channel %s [%s]' % [channel, params]
406441
@ws.send(JSON.generate(params.merge({
@@ -409,18 +444,40 @@ def subscribe (channel, params = {})
409444
})))
410445
end
411446

447+
###
448+
# Subscribes to a ticker channel by symbol
449+
#
450+
# @param [string] sym - i.e. tBTCUSD
451+
###
412452
def subscribe_ticker (sym)
413453
subscribe('ticker', { :symbol => sym })
414454
end
415455

456+
###
457+
# Subscribes to a trades channel by symbol
458+
#
459+
# @param [string] sym - i.e. tBTCUSD
460+
###
416461
def subscribe_trades (sym)
417462
subscribe('trades', { :symbol => sym })
418463
end
419464

465+
###
466+
# Subscribes to a candle channel by key
467+
#
468+
# @param [string] key - i.e. trade:1m:tBTCUSD
469+
###
420470
def subscribe_candles (key)
421471
subscribe('candles', { :key => key })
422472
end
423473

474+
###
475+
# Subscribes to an order book channel
476+
#
477+
# @param [string] sym - i.e. tBTCUSD
478+
# @param [string] prec - i.e. R0, P0, etc
479+
# @param [string] len - i.e. 25, 100, etc
480+
###
424481
def subscribe_order_book (sym, prec, len)
425482
subscribe('book', {
426483
:symbol => sym,
@@ -429,7 +486,7 @@ def subscribe_order_book (sym, prec, len)
429486
})
430487
end
431488

432-
def process_event_message (msg)
489+
def process_event_message (msg) # :nodoc:
433490
case msg['event']
434491
when 'auth'
435492
handle_auth_event(msg)
@@ -446,7 +503,7 @@ def process_event_message (msg)
446503
end
447504
end
448505

449-
def handle_auth_event (msg)
506+
def handle_auth_event (msg) # :nodoc:
450507
if msg['status'] != 'OK'
451508
@l.error "auth failed: #{msg['message']}"
452509
return
@@ -459,7 +516,7 @@ def handle_auth_event (msg)
459516
@l.info 'authenticated'
460517
end
461518

462-
def handle_info_event (msg)
519+
def handle_info_event (msg) # :nodoc:
463520
if msg.include?('version')
464521
if msg['version'] != 2
465522
close!
@@ -488,11 +545,11 @@ def handle_info_event (msg)
488545
end
489546
end
490547

491-
def handle_error_event (msg)
548+
def handle_error_event (msg) # :nodoc:
492549
@l.error msg
493550
end
494551

495-
def handle_config_event (msg)
552+
def handle_config_event (msg) # :nodoc:
496553
if msg['status'] != 'OK'
497554
@l.error "config failed: #{msg['message']}"
498555
else
@@ -501,18 +558,23 @@ def handle_config_event (msg)
501558
end
502559
end
503560

504-
def handle_subscribed_event (msg)
561+
def handle_subscribed_event (msg) # :nodoc:
505562
@l.info "subscribed to #{msg['channel']} [#{msg['chanId']}]"
506563
@channel_map[msg['chanId']] = msg
507564
emit(:subscribed, msg['chanId'])
508565
end
509566

510-
def handle_unsubscribed_event (msg)
567+
def handle_unsubscribed_event (msg) # :nodoc:
511568
@l.info "unsubscribed from #{msg['chanId']}"
512569
@channel_map.delete(msg['chanId'])
513570
emit(:unsubscribed, msg['chanId'])
514571
end
515572

573+
###
574+
# Enable an individual flag (see FLAG_* constants)
575+
#
576+
# @param [number] flag
577+
###
516578
def enable_flag (flag)
517579
return unless @is_open
518580

@@ -522,19 +584,43 @@ def enable_flag (flag)
522584
}))
523585
end
524586

587+
###
588+
# Checks if an individual flag is enabled (see FLAG_* constants)
589+
#
590+
# @param [number] flag
591+
# @return [boolean] enabled
592+
###
525593
def is_flag_enabled (flag)
526594
(@enabled_flags & flag) == flag
527595
end
528596

597+
###
598+
# Sets the flag to activate sequence numbers on incoming packets
599+
#
600+
# @param [boolean] audit - if true (default), incoming seq numbers will be checked for consistency
601+
###
529602
def enable_sequencing (audit = true)
530603
@seq_audit = audit
531604
enable_flag(FLAG_SEQ_ALL)
532605
end
533606

534-
def enable_ob_checksums
607+
###
608+
# Sets the flag to activate order book checksums. Managed order books are
609+
# required for automatic checksum audits.
610+
#
611+
# @param [boolean] audit - if true (default), incoming checksums will be compared to local checksums
612+
###
613+
def enable_ob_checksums (audit = true)
614+
@checksum_audit = audit
535615
enable_flag(FLAG_CHECKSUM)
536616
end
537617

618+
###
619+
# Authenticates the socket connection
620+
#
621+
# @param [number] calc
622+
# @param [number] dms - dead man switch, active 4
623+
###
538624
def auth! (calc = 0, dms = 0)
539625
if @is_authenticated
540626
raise Exception, 'already authenticated'
@@ -555,18 +641,30 @@ def auth! (calc = 0, dms = 0)
555641
}))
556642
end
557643

558-
def new_nonce
644+
def new_nonce # :nodoc:
559645
Time.now.to_i.to_s
560646
end
561647

562-
def sign (payload)
648+
def sign (payload) # :nodoc:
563649
OpenSSL::HMAC.hexdigest('sha384', @api_secret, payload)
564650
end
565651

652+
###
653+
# Requests a calculation to be performed
654+
# @see https://docs.bitfinex.com/v2/reference#ws-input-calc
655+
#
656+
# @param [Array] prefixes - i.e. ['margin_base']
657+
###
566658
def request_calc (prefixes)
567659
@ws.send(JSON.generate([0, 'calc', nil, prefixes.map { |p| [p] }]))
568660
end
569661

662+
###
663+
# Update an order with a changeset by ID
664+
#
665+
# @param [Hash] changes - must contain ID
666+
# @param [Block] cb - triggered upon receipt of confirmation notification
667+
###
570668
def update_order (changes, &cb)
571669
id = changes[:id] || changes['id']
572670
@ws.send(JSON.generate([0, 'ou', nil, changes]))
@@ -576,6 +674,12 @@ def update_order (changes, &cb)
576674
end
577675
end
578676

677+
###
678+
# Cancel an order by ID
679+
#
680+
# @param [Hash|Array|Order|number] order - must contain or be ID
681+
# @param [Block] cb - triggered upon receipt of confirmation notification
682+
###
579683
def cancel_order (order, &cb)
580684
return if !@is_authenticated
581685

@@ -598,6 +702,12 @@ def cancel_order (order, &cb)
598702
end
599703
end
600704

705+
###
706+
# Submit a new order
707+
#
708+
# @param [Hash|Array|Order] order
709+
# @param [Block] cb - triggered upon receipt of confirmation notification
710+
###
601711
def submit_order (order, &cb)
602712
return if !@is_authenticated
603713

0 commit comments

Comments
 (0)