bitfinex-rb 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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