bitfinex-rb 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bitfinex.rb +28 -30
  3. data/lib/{bitfinex/errors.rb → errors.rb} +2 -2
  4. data/lib/models/alert.rb +27 -0
  5. data/lib/models/balance_info.rb +25 -0
  6. data/lib/models/candle.rb +29 -0
  7. data/lib/models/currency.rb +27 -0
  8. data/lib/models/funding_credit.rb +41 -0
  9. data/lib/models/funding_info.rb +40 -0
  10. data/lib/models/funding_loan.rb +40 -0
  11. data/lib/models/funding_offer.rb +38 -0
  12. data/lib/models/funding_ticker.rb +50 -0
  13. data/lib/models/funding_trade.rb +31 -0
  14. data/lib/models/ledger_entry.rb +33 -0
  15. data/lib/models/margin_info.rb +67 -0
  16. data/lib/models/model.rb +56 -0
  17. data/lib/models/movement.rb +33 -0
  18. data/lib/models/notification.rb +30 -0
  19. data/lib/models/order.rb +197 -0
  20. data/lib/models/order_book.rb +265 -0
  21. data/lib/models/position.rb +33 -0
  22. data/lib/models/public_trade.rb +27 -0
  23. data/lib/models/trade.rb +34 -0
  24. data/lib/models/trading_ticker.rb +33 -0
  25. data/lib/models/user_info.rb +27 -0
  26. data/lib/models/wallet.rb +28 -0
  27. data/lib/rest/rest_client.rb +103 -0
  28. data/lib/rest/v1.rb +63 -0
  29. data/lib/rest/v1/account_info.rb +23 -0
  30. data/lib/{bitfinex → rest}/v1/deposit.rb +2 -3
  31. data/lib/{bitfinex → rest}/v1/funding_book.rb +2 -4
  32. data/lib/{bitfinex → rest}/v1/historical_data.rb +2 -2
  33. data/lib/{bitfinex → rest}/v1/lends.rb +1 -2
  34. data/lib/{bitfinex → rest}/v1/margin_funding.rb +1 -2
  35. data/lib/rest/v1/order_book.rb +17 -0
  36. data/lib/{bitfinex → rest}/v1/orders.rb +6 -8
  37. data/lib/{bitfinex → rest}/v1/positions.rb +6 -2
  38. data/lib/{bitfinex → rest}/v1/stats.rb +1 -3
  39. data/lib/{bitfinex → rest}/v1/symbols.rb +1 -2
  40. data/lib/rest/v1/ticker.rb +13 -0
  41. data/lib/{bitfinex → rest}/v1/trades.rb +1 -16
  42. data/lib/{bitfinex → rest}/v1/wallet.rb +1 -2
  43. data/lib/rest/v2.rb +47 -0
  44. data/lib/{bitfinex → rest}/v2/margin.rb +1 -2
  45. data/lib/{bitfinex → rest}/v2/personal.rb +2 -18
  46. data/lib/{bitfinex → rest}/v2/stats.rb +1 -4
  47. data/lib/rest/v2/ticker.rb +20 -0
  48. data/lib/{bitfinex → rest}/v2/trading.rb +3 -49
  49. data/lib/{bitfinex → rest}/v2/utils.rb +1 -3
  50. data/lib/ws/ws2.rb +529 -0
  51. metadata +74 -56
  52. data/lib/bitfinex-api-rb.rb +0 -1
  53. data/lib/bitfinex-rb.rb +0 -1
  54. data/lib/bitfinex/api_versions.rb +0 -7
  55. data/lib/bitfinex/authenticated_rest.rb +0 -57
  56. data/lib/bitfinex/client.rb +0 -38
  57. data/lib/bitfinex/configurable.rb +0 -48
  58. data/lib/bitfinex/connection.rb +0 -52
  59. data/lib/bitfinex/v1/account_info.rb +0 -38
  60. data/lib/bitfinex/v1/orderbook.rb +0 -36
  61. data/lib/bitfinex/v1/ticker.rb +0 -27
  62. data/lib/bitfinex/v2/ticker.rb +0 -58
  63. data/lib/bitfinex/version.rb +0 -3
  64. data/lib/bitfinex/websocket_connection.rb +0 -231
@@ -0,0 +1,33 @@
1
+ require_relative './model'
2
+
3
+ module Bitfinex
4
+ module Models
5
+ class LedgerEntry < Model
6
+ BOOL_FIELDS = []
7
+ FIELDS = {
8
+ :id => 0,
9
+ :currency => 1,
10
+ :mts => 3,
11
+ :amount => 5,
12
+ :balance => 6,
13
+ :description => 8,
14
+ :wallet => nil
15
+ }
16
+
17
+ FIELDS.each do |key, index|
18
+ attr_accessor key
19
+ end
20
+
21
+ def initialize (data)
22
+ super(data, FIELDS, BOOL_FIELDS)
23
+
24
+ spl = self.description.split('wallet')
25
+ self.wallet = (spl && spl[1]) ? spl[1].trim() : nil
26
+ end
27
+
28
+ def self.unserialize (data)
29
+ return Model.unserialize(data, FIELDS, BOOL_FIELDS)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,67 @@
1
+ require_relative './model'
2
+
3
+ module Bitfinex
4
+ module Models
5
+ class MarginInfo < Model
6
+ attr_accessor :user_pl, :user_swaps, :margin_balance, :margin_net, :type
7
+ attr_accessor :symbol, :tradable_balance, :gross_balance, :buy, :sell
8
+
9
+ def initialize (data)
10
+ super(data, {}, [])
11
+ end
12
+
13
+ def serialize ()
14
+ if self.type == 'base'
15
+ return [
16
+ self.type,
17
+ [
18
+ self.user_pl,
19
+ self.user_swaps,
20
+ self.margin_balance,
21
+ self.margin_net
22
+ ]
23
+ ]
24
+ else
25
+ return [
26
+ self.type,
27
+ self.symbol,
28
+ [
29
+ self.tradable_balance,
30
+ self.gross_balance,
31
+ self.buy,
32
+ self.sell
33
+ ]
34
+ ]
35
+ end
36
+ end
37
+
38
+ def self.unserialize (arr)
39
+ type = arr[0]
40
+
41
+ if type == 'base'
42
+ payload = arr[1]
43
+
44
+ return {
45
+ :type => type,
46
+ :user_pl => payload[0],
47
+ :user_swaps => payload[1],
48
+ :margin_balance => payload[2],
49
+ :margin_net => payload[3]
50
+ }
51
+ else
52
+ symbol = arr[1]
53
+ payload = arr[2]
54
+
55
+ return {
56
+ :type => type,
57
+ :symbol => symbol,
58
+ :tradable_balance => payload[0],
59
+ :gross_balance => payload[1],
60
+ :buy => payload[2],
61
+ :sell => payload[3]
62
+ }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,56 @@
1
+ module Bitfinex
2
+ module Models
3
+ class Model
4
+ def initialize (data, fields, boolFields)
5
+ @fields = fields
6
+ @boolFields = boolFields
7
+
8
+ if data.kind_of?(Array)
9
+ apply(self.class.unserialize(data))
10
+ elsif data.kind_of?(Hash)
11
+ apply(data)
12
+ end
13
+ end
14
+
15
+ def serialize
16
+ arr = []
17
+
18
+ @fields.each do |key, index|
19
+ return if index.nil?
20
+
21
+ if @boolFields.include?(key)
22
+ arr[index] = instance_variable_get("@#{key}") ? 1 : 0
23
+ else
24
+ arr[index] = instance_variable_get("@#{key}")
25
+ end
26
+ end
27
+
28
+ arr
29
+ end
30
+
31
+ def apply (obj)
32
+ @fields.each do |key, index|
33
+ return if index.nil?
34
+
35
+ instance_variable_set("@#{key}", obj[key])
36
+ end
37
+ end
38
+
39
+ def self.unserialize (data, fields, boolFields)
40
+ obj = {}
41
+
42
+ fields.each do |key, index|
43
+ return if index.nil?
44
+
45
+ if boolFields.include?(key)
46
+ obj[key] = data[index] == 1
47
+ else
48
+ obj[key] = data[index]
49
+ end
50
+ end
51
+
52
+ return obj
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ require_relative './model'
2
+
3
+ module Bitfinex
4
+ module Models
5
+ class Movement < Model
6
+ BOOL_FIELDS = []
7
+ FIELDS = {
8
+ :id => 0,
9
+ :currency => 1,
10
+ :currency_name => 2,
11
+ :mts_started => 5,
12
+ :mts_updated => 6,
13
+ :status => 9,
14
+ :amount => 12,
15
+ :fees => 13,
16
+ :destination_address => 16,
17
+ :transaction_id => 20
18
+ }
19
+
20
+ FIELDS.each do |key, index|
21
+ attr_accessor key
22
+ end
23
+
24
+ def initialize (data)
25
+ super(data, FIELDS, BOOL_FIELDS)
26
+ end
27
+
28
+ def self.unserialize (data)
29
+ return Model.unserialize(data, FIELDS, BOOL_FIELDS)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ require_relative './model'
2
+
3
+ module Bitfinex
4
+ module Models
5
+ class Notification < Model
6
+ BOOL_FIELDS = []
7
+ FIELDS = {
8
+ :mts => 0,
9
+ :type => 1,
10
+ :message_id => 2,
11
+ :notify_info => 4,
12
+ :code => 5,
13
+ :status => 6,
14
+ :text => 7
15
+ }
16
+
17
+ FIELDS.each do |key, index|
18
+ attr_accessor key
19
+ end
20
+
21
+ def initialize (data)
22
+ super(data, FIELDS, BOOL_FIELDS)
23
+ end
24
+
25
+ def self.unserialize (data)
26
+ return Model.unserialize(data, FIELDS, BOOL_FIELDS)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,197 @@
1
+ require 'bigdecimal'
2
+ require_relative './model'
3
+
4
+ module Bitfinex
5
+ module Models
6
+ class Order < Model
7
+ BOOL_FIELDS = []
8
+ FIELDS = {
9
+ :id => 0,
10
+ :gid => 1,
11
+ :cid => 2,
12
+ :symbol => 3,
13
+ :mts_create => 4,
14
+ :mts_update => 5,
15
+ :amount => 6,
16
+ :amount_orig => 7,
17
+ :type => 8,
18
+ :type_prev => 9,
19
+ :flags => 12,
20
+ :status => 13,
21
+ :price => 16,
22
+ :price_avg => 17,
23
+ :price_trailing => 18,
24
+ :price_aux_limit => 19,
25
+ :notify => 23,
26
+ :placed_id => 25
27
+ }
28
+
29
+ FLAG_OCO = 2 ** 14 # 16384
30
+ FLAG_POSTONLY = 2 ** 12 # 4096
31
+ FLAG_HIDDEN = 2 ** 6 # 64
32
+ FLAG_NO_VR = 2 ** 19 # 524288
33
+ FLAG_POS_CLOSE = 2 ** 9 # 512
34
+ FLAG_REDUCE_ONLY = 2 ** 10 # 1024
35
+
36
+ @@last_cid = Time.now.to_i
37
+
38
+ FIELDS.each do |key, index|
39
+ attr_accessor key
40
+ end
41
+
42
+ attr_accessor :last_amount, :meta
43
+
44
+ def self.gen_cid
45
+ @@last_cid += 1
46
+ @@last_cid
47
+ end
48
+
49
+ def initialize (data)
50
+ super(data, FIELDS, BOOL_FIELDS)
51
+
52
+ @flags = 0 unless @flags.is_a?(Numeric)
53
+ @amount_orig = @amount if @amount_orig.nil? && !@amount.nil?
54
+ @last_amount = @amount
55
+
56
+ if data.kind_of?(Hash)
57
+ set_oco(data[:oco]) if data.has_key?(:oco)
58
+ set_hidden(data[:hidden]) if data.has_key?(:hidden)
59
+ set_post_only(data[:post_only]) if data.has_key?(:post_only)
60
+ end
61
+ end
62
+
63
+ def self.unserialize (data)
64
+ return Model.unserialize(data, FIELDS, BOOL_FIELDS)
65
+ end
66
+
67
+ def modify_flag (flag, active)
68
+ return if (@flags & flag != 0) == active
69
+
70
+ @flags += active ? flag : -flag
71
+ end
72
+
73
+ def set_oco (v, stop_price = @price_aux_limit)
74
+ @price_aux_limit = stop_price if v
75
+
76
+ modify_flag(FLAG_OCO, v)
77
+ end
78
+
79
+ def set_hidden (v)
80
+ modify_flag(FLAG_HIDDEN, v)
81
+ end
82
+
83
+ def set_post_only (v)
84
+ modify_flag(FLAG_POSTONLY, v)
85
+ end
86
+
87
+ def set_no_variable_rates (v)
88
+ modify_flag(FLAG_NO_VR, v)
89
+ end
90
+
91
+ def set_position_close (v)
92
+ modify_flag(FLAG_POS_CLOSE, v)
93
+ end
94
+
95
+ def set_reduce_only (v)
96
+ modify_flag(FLAG_REDUCE_ONLY, v)
97
+ end
98
+
99
+ def is_oco
100
+ return !!(@flags & FLAG_OCO)
101
+ end
102
+
103
+ def is_hidden
104
+ return !!(@flags & FLAG_HIDDEN)
105
+ end
106
+
107
+ def is_post_only
108
+ return !!(@flags & FLAG_POSTONLY)
109
+ end
110
+
111
+ def includes_variable_rates
112
+ return !!(@flags & FLAG_NO_VR)
113
+ end
114
+
115
+ def is_position_close
116
+ return !!(@flags & FLAG_POS_CLOSE)
117
+ end
118
+
119
+ def is_reduce_only
120
+ return !!(@flags & FLAG_REDUCE_ONLY)
121
+ end
122
+
123
+ def get_last_fill_amount
124
+ @last_amount - @amount
125
+ end
126
+
127
+ def reset_fill_amount
128
+ @last_amount = @amount
129
+ end
130
+
131
+ def get_base_currency
132
+ @symbol[1...4]
133
+ end
134
+
135
+ def get_quote_currency
136
+ @symbol[4..-1]
137
+ end
138
+
139
+ def get_notional_value
140
+ (@amount * @price).abs
141
+ end
142
+
143
+ def is_partially_filled
144
+ a = @amount.abs
145
+ a > 0 && a < @amount_orig.abs
146
+ end
147
+
148
+ def to_new_order_packet
149
+ if !@cid.nil?
150
+ cid = @cid
151
+ else
152
+ cid = Order.gen_cid
153
+ end
154
+
155
+ data = {
156
+ :gid => @gid,
157
+ :cid => cid,
158
+ :symbol => @symbol,
159
+ :type => @type,
160
+ :amount => BigDecimal.new(@amount, 8).to_s,
161
+ :flags => @flags || 0,
162
+ :meta => @meta
163
+ }
164
+
165
+ data[:price] = BigDecimal.new(@price, 5).to_s if !@price.nil?
166
+ data[:price_trailing] = BigDecimal.new(@price_trailing, 5).to_s if !@price_trailing.nil?
167
+
168
+ if !@price_aux_limit.nil?
169
+ if is_oco
170
+ data[:price_oco_stop] = BigDecimal.new(@price_aux_limit, 5).to_s
171
+ else
172
+ data[:price_aux_limit] = BigDecimal.new(@price_aux_limit, 5).to_s
173
+ end
174
+ end
175
+
176
+ data
177
+ end
178
+
179
+ def update (changes = {})
180
+ changes.each do |k, v|
181
+ return if k == 'id'
182
+
183
+ if FIELDS.has_key?(k)
184
+ instance_variable_set(k, v)
185
+ elsif k == 'price_trailing'
186
+ @price_trailing = v.to_f
187
+ elsif k == 'price_oco_stop' || k == 'price_aux_limit'
188
+ @price_aux_limit = v.to_f
189
+ elsif k == 'delta' && v.is_a?(Numeric) && @amount.is_a?(Numeric)
190
+ @amount += v.to_f
191
+ @last_amount = @amount
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,265 @@
1
+ require 'zlib'
2
+
3
+ module Bitfinex
4
+ module Models
5
+ class OrderBook
6
+ attr_reader :raw, :bids, :asks
7
+
8
+ def initialize (snap = [], raw = false)
9
+ @raw = raw
10
+
11
+ if snap.instance_of?(OrderBook)
12
+ @bids = snap.bids.dup
13
+ @asks = snap.asks.dup
14
+ elsif snap.kind_of?(Array)
15
+ update_from_snapshot(snap)
16
+ elsif snap.kind_of?(Hash)
17
+ @bids = snap[:bids].dup
18
+ @asks = snap[:asks].dup
19
+ else
20
+ @bids = []
21
+ @asks = []
22
+ end
23
+ end
24
+
25
+ def update_from_snapshot (snap = [])
26
+ @bids = []
27
+ @asks = []
28
+
29
+ snap = [snap] if !snap[0].kind_of?(Array)
30
+
31
+ snap.each do |entry|
32
+ if entry.size == 4
33
+ if entry[3] < 0
34
+ @bids.push(entry)
35
+ else
36
+ @asks.push(entry)
37
+ end
38
+ else
39
+ if entry[2] < 0
40
+ @asks.push(entry)
41
+ else
42
+ @bids.push(entry)
43
+ end
44
+ end
45
+ end
46
+
47
+ if self.raw
48
+ priceI = snap[0].size == 4 ? 2 : 1
49
+ else
50
+ priceI = 0
51
+ end
52
+
53
+ @bids.sort! { |a, b| b[priceI] <=> a[priceI]}
54
+ @asks.sort! { |a, b| a[priceI] <=> b[priceI]}
55
+ end
56
+
57
+ def top_bid_level
58
+ @bids[0] || nil
59
+ end
60
+
61
+ def top_bid
62
+ if self.raw
63
+ priceI = (@bids[0].size == 4 || @asks[0].size == 4) ? 2 : 1
64
+ else
65
+ priceI = 0
66
+ end
67
+
68
+ (top_bid_level || [])[priceI] || nil
69
+ end
70
+
71
+ def top_ask_level
72
+ @asks[0] || nil
73
+ end
74
+
75
+ def top_ask
76
+ if self.raw
77
+ priceI = (@bids[0].size == 4 || @asks[0].size == 4) ? 2 : 1
78
+ else
79
+ priceI = 0
80
+ end
81
+
82
+ (top_ask_level || [])[priceI] || nil
83
+ end
84
+
85
+ def mid_price
86
+ ask = top_ask || 0
87
+ bid = top_bid || 0
88
+
89
+ return bid if ask == 0
90
+ return ask if bid == 0
91
+
92
+ return (bid + ask) / 2
93
+ end
94
+
95
+ def spread
96
+ ask = top_ask || 0
97
+ bid = top_bid || 0
98
+
99
+ return 0 if ask == 0 || bid == 0
100
+
101
+ return ask - bid
102
+ end
103
+
104
+ def bid_amount
105
+ amount = 0
106
+
107
+ @bids.each do |entry|
108
+ amount += entry.size == 4 ? entry[3] : entry[2]
109
+ end
110
+
111
+ amount.abs
112
+ end
113
+
114
+ def ask_amount
115
+ amount = 0
116
+
117
+ @asks.each do |entry|
118
+ amount += entry.size == 4 ? entry[3] : entry[2]
119
+ end
120
+
121
+ amount.abs
122
+ end
123
+
124
+ def serialize
125
+ @asks + @bids
126
+ end
127
+
128
+ def checksum
129
+ data = []
130
+
131
+ for i in 0...25
132
+ bid = @bids[i]
133
+ ask = @asks[i]
134
+
135
+ if !bid.nil?
136
+ price = bid[0]
137
+ data.push(price)
138
+ data.push(bid.size == 4 ? bid[3] : bid[2])
139
+ end
140
+
141
+ if !ask.nil?
142
+ price = ask[0]
143
+ data.push(price)
144
+ data.push(ask.size == 4 ? ask[3] : ask[2])
145
+ end
146
+ end
147
+
148
+ [Zlib::crc32(data.join(':'))].pack("I").unpack("i")[0]
149
+ end
150
+
151
+ def update_with (entry)
152
+ if @raw
153
+ priceI = entry.size == 4 ? 2 : 1
154
+ count = -1
155
+ else
156
+ priceI = 0
157
+ count = entry.size == 4 ? entry[2] : entry[1]
158
+ end
159
+
160
+ price = entry[priceI]
161
+ oID = entry[0] # only for raw books
162
+ amount = entry.size == 4 ? entry[3] : entry[2]
163
+
164
+ if entry.size == 4
165
+ dir = amount < 0 ? 1 : -1
166
+ side = amount < 0 ? @bids : @asks
167
+ else
168
+ dir = amount < 0 ? -1 : 1
169
+ side = amount < 0 ? @asks : @bids
170
+ end
171
+
172
+ insertIndex = -1
173
+
174
+ # apply insert directly if empty
175
+ if (side.size == 0 && (@raw || count > 0))
176
+ side.push(entry)
177
+ return true
178
+ end
179
+
180
+ # match by price level, or order ID for raw books
181
+ side.each_with_index do |pl, i|
182
+ if (!@raw && pl[priceI] == price) || (@raw && pl[0] == oID)
183
+ if (!@raw && count == 0) || (@raw && price == 0)
184
+ side.slice!(i, 1)
185
+ return true
186
+ elsif !@raw || (@raw && price > 0)
187
+ side.slice!(i, 1)
188
+ break
189
+ end
190
+ end
191
+ end
192
+
193
+ if (@raw && price == 0) || (!@raw && count == 0)
194
+ return false
195
+ end
196
+
197
+ side.each_with_index do |pl, i|
198
+ if (insertIndex == -1 && (
199
+ (dir == -1 && price < pl[priceI]) || # by price
200
+ (dir == -1 && price == pl[priceI] && (raw && entry[0] < pl[0])) || # by order ID
201
+ (dir == 1 && price > pl[priceI]) ||
202
+ (dir == 1 && price == pl[priceI] && (raw && entry[0] < pl[0]))
203
+ ))
204
+ insertIndex = i
205
+ break
206
+ end
207
+ end
208
+
209
+ # add
210
+ if (insertIndex == -1)
211
+ side.push(entry)
212
+ else
213
+ side.insert(insertIndex, entry)
214
+ end
215
+
216
+ return true
217
+ end
218
+
219
+ def self.unserialize (arr, raw = false)
220
+ if arr[0].kind_of?(Array)
221
+ entries = arr.map { |e| OrderBook.unserialize(e, raw) }
222
+ bids = entries.select { |e| (e[:rate] ? -e[:amount] : e[:amount]) > 0 }
223
+ asks = entries.select { |e| (e[:rate] ? -e[:amount] : e[:amount]) < 0 }
224
+
225
+ return {
226
+ :bids => bids,
227
+ :asks => asks
228
+ }
229
+ end
230
+
231
+ if arr.size == 4
232
+ if raw
233
+ return {
234
+ :order_id => arr[0],
235
+ :period => arr[1],
236
+ :rate => arr[2],
237
+ :amount => arr[3]
238
+ }
239
+ else
240
+ return {
241
+ :rate => arr[0],
242
+ :period => arr[1],
243
+ :count => arr[2],
244
+ :amount => arr[3]
245
+ }
246
+ end
247
+ else
248
+ if raw
249
+ return {
250
+ :order_id => arr[0],
251
+ :price => arr[1],
252
+ :amount => arr[2]
253
+ }
254
+ else
255
+ return {
256
+ :price => arr[0],
257
+ :count => arr[1],
258
+ :amount => arr[2]
259
+ }
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end