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.
- checksums.yaml +4 -4
- data/lib/bitfinex.rb +28 -30
- data/lib/{bitfinex/errors.rb → errors.rb} +2 -2
- data/lib/models/alert.rb +27 -0
- data/lib/models/balance_info.rb +25 -0
- data/lib/models/candle.rb +29 -0
- data/lib/models/currency.rb +27 -0
- data/lib/models/funding_credit.rb +41 -0
- data/lib/models/funding_info.rb +40 -0
- data/lib/models/funding_loan.rb +40 -0
- data/lib/models/funding_offer.rb +38 -0
- data/lib/models/funding_ticker.rb +50 -0
- data/lib/models/funding_trade.rb +31 -0
- data/lib/models/ledger_entry.rb +33 -0
- data/lib/models/margin_info.rb +67 -0
- data/lib/models/model.rb +56 -0
- data/lib/models/movement.rb +33 -0
- data/lib/models/notification.rb +30 -0
- data/lib/models/order.rb +197 -0
- data/lib/models/order_book.rb +265 -0
- data/lib/models/position.rb +33 -0
- data/lib/models/public_trade.rb +27 -0
- data/lib/models/trade.rb +34 -0
- data/lib/models/trading_ticker.rb +33 -0
- data/lib/models/user_info.rb +27 -0
- data/lib/models/wallet.rb +28 -0
- data/lib/rest/rest_client.rb +103 -0
- data/lib/rest/v1.rb +63 -0
- data/lib/rest/v1/account_info.rb +23 -0
- data/lib/{bitfinex → rest}/v1/deposit.rb +2 -3
- data/lib/{bitfinex → rest}/v1/funding_book.rb +2 -4
- data/lib/{bitfinex → rest}/v1/historical_data.rb +2 -2
- data/lib/{bitfinex → rest}/v1/lends.rb +1 -2
- data/lib/{bitfinex → rest}/v1/margin_funding.rb +1 -2
- data/lib/rest/v1/order_book.rb +17 -0
- data/lib/{bitfinex → rest}/v1/orders.rb +6 -8
- data/lib/{bitfinex → rest}/v1/positions.rb +6 -2
- data/lib/{bitfinex → rest}/v1/stats.rb +1 -3
- data/lib/{bitfinex → rest}/v1/symbols.rb +1 -2
- data/lib/rest/v1/ticker.rb +13 -0
- data/lib/{bitfinex → rest}/v1/trades.rb +1 -16
- data/lib/{bitfinex → rest}/v1/wallet.rb +1 -2
- data/lib/rest/v2.rb +47 -0
- data/lib/{bitfinex → rest}/v2/margin.rb +1 -2
- data/lib/{bitfinex → rest}/v2/personal.rb +2 -18
- data/lib/{bitfinex → rest}/v2/stats.rb +1 -4
- data/lib/rest/v2/ticker.rb +20 -0
- data/lib/{bitfinex → rest}/v2/trading.rb +3 -49
- data/lib/{bitfinex → rest}/v2/utils.rb +1 -3
- data/lib/ws/ws2.rb +529 -0
- metadata +74 -56
- data/lib/bitfinex-api-rb.rb +0 -1
- data/lib/bitfinex-rb.rb +0 -1
- data/lib/bitfinex/api_versions.rb +0 -7
- data/lib/bitfinex/authenticated_rest.rb +0 -57
- data/lib/bitfinex/client.rb +0 -38
- data/lib/bitfinex/configurable.rb +0 -48
- data/lib/bitfinex/connection.rb +0 -52
- data/lib/bitfinex/v1/account_info.rb +0 -38
- data/lib/bitfinex/v1/orderbook.rb +0 -36
- data/lib/bitfinex/v1/ticker.rb +0 -27
- data/lib/bitfinex/v2/ticker.rb +0 -58
- data/lib/bitfinex/version.rb +0 -3
- 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
|
data/lib/models/model.rb
ADDED
@@ -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
|
data/lib/models/order.rb
ADDED
@@ -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
|