irix 2.4.1 → 2.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: caedc8fae1848a9d4aae0465cf99219c179dddb3a94854e46692246448149b65
4
- data.tar.gz: c7889d940a00aea620b57c168e1ec87ae4807924a1f0cd6083a7e9d9ddc40b56
3
+ metadata.gz: 42f94194dc97ced688866fc10bbbb00bf990e15f4bc69b7166e6023bd6b7338a
4
+ data.tar.gz: 55f9bffab15c6dc7508d16da41aed1b264fd4f71c5d7928685f668e9906ab878
5
5
  SHA512:
6
- metadata.gz: 3421bc3612236d3a933ec9bb84e34fb6796d910f94cc71fca8e075b318ad0b39d6b533a4c625b73c28240b7f07fb7a70c87bbde5b2920fa7f4e6d97c7f6b10b9
7
- data.tar.gz: fcde730a14bf33dacc5df16302bc8c768043da35b32b2e769dd4eefedacf9d77d242f9f4fa49ab94365bf48ca81bf8dbd18ba34d648824e622bcc96d09c6ef54
6
+ metadata.gz: 5f97d6e1c3263401a6f2060f0f73e06caf7eab527cdc53f98717c714e167765474a59289575aaa571bbd4a5fce3ddfd0dccb71959aab7de4d05c5e2b8c6c7f5e
7
+ data.tar.gz: c68d6975275eb997d9e044f478bf0f6882ee3d655610fc58705c60d891a95a10a66610c123884f5581a113772cd21e74b27d3133e9ee3a493d8550ff303644fd
data/.drone.yml CHANGED
@@ -9,6 +9,21 @@ steps:
9
9
  - gem install bundler:2.0.2
10
10
  - bundle install --jobs=$(nproc) --retry=3 --path vendor/bundle
11
11
  - bundle exec rspec
12
+
13
+ - name: Release gem
14
+ image: ruby:2.6
15
+ environment:
16
+ GEM_CREDENTIALS:
17
+ from_secret: gem_credentials
18
+ commands:
19
+ - mkdir -p ~/.gem
20
+ - echo $GEM_CREDENTIALS | base64 -d > ~/.gem/credentials
21
+ - chmod 0600 ~/.gem/credentials
22
+ - gem build irix.gemspec
23
+ - gem push irix-*.gem
24
+ when:
25
+ branch:
26
+ - master
12
27
 
13
28
  trigger:
14
29
  event:
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- irix (2.4.1)
4
+ irix (2.6.0)
5
5
  em-synchrony (~> 1.0)
6
6
  em-websocket
7
7
  eventmachine
8
8
  faraday_middleware (~> 0.13.1)
9
9
  faye (~> 1.2)
10
- peatio (~> 2.4.2)
10
+ peatio (>= 2.4.2)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
@@ -22,13 +22,13 @@ GEM
22
22
  zeitwerk (~> 2.1, >= 2.1.8)
23
23
  addressable (2.7.0)
24
24
  public_suffix (>= 2.0.2, < 5.0)
25
- amq-protocol (2.3.0)
25
+ amq-protocol (2.3.1)
26
26
  amqp (1.8.0)
27
27
  amq-protocol (>= 2.2.0)
28
28
  eventmachine
29
29
  ast (2.4.0)
30
- bunny (2.14.4)
31
- amq-protocol (~> 2.3, >= 2.3.0)
30
+ bunny (2.15.0)
31
+ amq-protocol (~> 2.3, >= 2.3.1)
32
32
  byebug (11.1.1)
33
33
  clamp (1.3.1)
34
34
  coderay (1.1.2)
@@ -37,7 +37,7 @@ GEM
37
37
  daemons (1.3.1)
38
38
  diff-lcs (1.3)
39
39
  docile (1.3.2)
40
- em-http-request (1.1.5)
40
+ em-http-request (1.1.6)
41
41
  addressable (>= 2.3.4)
42
42
  cookiejar (!= 0.3.1)
43
43
  em-socksify (>= 0.3)
@@ -60,7 +60,7 @@ GEM
60
60
  multipart-post (>= 1.2, < 3)
61
61
  faraday_middleware (0.13.1)
62
62
  faraday (>= 0.7.4, < 1.0)
63
- faye (1.2.4)
63
+ faye (1.3.0)
64
64
  cookiejar (>= 0.3.0)
65
65
  em-http-request (>= 0.3.0)
66
66
  eventmachine (>= 0.12.0)
@@ -72,20 +72,20 @@ GEM
72
72
  eventmachine (>= 0.12.0)
73
73
  websocket-driver (>= 0.5.1)
74
74
  http_parser.rb (0.6.0)
75
- i18n (1.8.2)
75
+ i18n (1.8.3)
76
76
  concurrent-ruby (~> 1.0)
77
77
  jaro_winkler (1.5.4)
78
78
  json (2.2.0)
79
79
  jwt (2.2.1)
80
80
  method_source (0.9.2)
81
- minitest (5.14.0)
81
+ minitest (5.14.1)
82
82
  multi_json (1.14.1)
83
83
  multipart-post (2.1.1)
84
84
  mysql2 (0.5.3)
85
85
  parallel (1.19.0)
86
86
  parser (2.6.5.0)
87
87
  ast (~> 2.4.0)
88
- peatio (2.4.2)
88
+ peatio (2.4.4)
89
89
  activemodel (> 5.2, <= 6.0.0)
90
90
  amqp
91
91
  bunny
@@ -106,7 +106,7 @@ GEM
106
106
  pry-byebug (3.8.0)
107
107
  byebug (~> 11.0)
108
108
  pry (~> 0.10)
109
- public_suffix (4.0.3)
109
+ public_suffix (4.0.5)
110
110
  rack (2.2.2)
111
111
  rainbow (3.0.0)
112
112
  rake (13.0.1)
@@ -151,13 +151,13 @@ GEM
151
151
  eventmachine (~> 1.0, >= 1.0.4)
152
152
  rack (>= 1, < 3)
153
153
  thread_safe (0.3.6)
154
- tzinfo (1.2.6)
154
+ tzinfo (1.2.7)
155
155
  thread_safe (~> 0.1)
156
156
  unicode-display_width (1.6.0)
157
157
  websocket (1.2.8)
158
- websocket-driver (0.7.1)
158
+ websocket-driver (0.7.2)
159
159
  websocket-extensions (>= 0.1.0)
160
- websocket-extensions (0.1.4)
160
+ websocket-extensions (0.1.5)
161
161
  zeitwerk (2.3.0)
162
162
 
163
163
  PLATFORMS
@@ -177,4 +177,4 @@ DEPENDENCIES
177
177
  simplecov-json
178
178
 
179
179
  BUNDLED WITH
180
- 2.0.2
180
+ 2.1.4
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency "eventmachine"
27
27
  spec.add_dependency "faraday_middleware", "~> 0.13.1"
28
28
  spec.add_dependency "faye", "~> 1.2"
29
- spec.add_dependency "peatio", "~> 2.4.2"
29
+ spec.add_dependency "peatio", ">= 2.4.2"
30
30
 
31
31
  spec.add_development_dependency "bundler", "~> 2.0"
32
32
  spec.add_development_dependency "em-spec"
@@ -8,6 +8,7 @@ require "faye"
8
8
 
9
9
  module Irix
10
10
  require "irix/bitfinex"
11
+ require "irix/huobi"
11
12
  require "irix/hooks"
12
13
  require "irix/version"
13
14
  end
@@ -2,6 +2,15 @@
2
2
 
3
3
  module Irix
4
4
  class Bitfinex < Peatio::Upstream::Base
5
+ require 'time'
6
+
7
+ MIN_INCREMENT_COUNT_TO_SNAPSHOT = 100
8
+ MIN_PERIOD_TO_SNAPSHOT = 5
9
+ MAX_PERIOD_TO_SNAPSHOT = 60
10
+
11
+ attr_accessor :snap, :snapshot_time, :increment_count, :sequence_number,
12
+ :open_channels, :asks, :bids
13
+
5
14
  def initialize(config)
6
15
  super
7
16
  @connection = Faraday.new(url: (config['rest']).to_s) do |builder|
@@ -12,15 +21,22 @@ module Irix
12
21
  builder.ssl[:verify] = config['verify_ssl']
13
22
  end
14
23
  end
24
+ @open_channels = {}
15
25
  @ping_set = false
16
26
  @rest = (config['rest']).to_s
17
- @ws_url = "#{config['websocket']}/public"
27
+ @ws_url = (config['websocket']).to_s
18
28
  end
19
29
 
20
30
  def ws_connect
21
31
  super
22
32
  return if @ping_set
23
33
 
34
+ @ws.on(:open) do |_e|
35
+ subscribe_trades(@target, @ws)
36
+ subscribe_orderbook(@target, @ws)
37
+ logger.info { 'Websocket connected' }
38
+ end
39
+
24
40
  Fiber.new do
25
41
  EM::Synchrony.add_periodic_timer(80) do
26
42
  @ws.send('{"event":"ping"}')
@@ -30,6 +46,8 @@ module Irix
30
46
  end
31
47
 
32
48
  def subscribe_trades(market, ws)
49
+ return unless @config['trade_proxy']
50
+
33
51
  sub = {
34
52
  event: 'subscribe',
35
53
  channel: 'trades',
@@ -42,9 +60,40 @@ module Irix
42
60
  end
43
61
  end
44
62
 
63
+ def subscribe_orderbook(market, ws)
64
+ return unless @config['orderbook_proxy']
65
+
66
+ @sequence_number = 0
67
+ @increment_count = 0
68
+ @bids = []
69
+ @asks = []
70
+ @snap = { 'asks' => [], 'bids' => [] }
71
+ sub = {
72
+ event: 'subscribe',
73
+ channel: 'book',
74
+ symbol: market.upcase,
75
+ len: 25
76
+ }
77
+ Rails.logger.info 'Open event' + sub.to_s
78
+ EM.next_tick do
79
+ ws.send(JSON.generate(sub))
80
+ end
81
+ Fiber.new do
82
+ EM::Synchrony.add_periodic_timer(0.2) do
83
+ publish_increment
84
+ end
85
+ end.resume
86
+ end
87
+
45
88
  def ws_read_public_message(msg)
46
89
  if msg.is_a?(Array)
47
- detect_trade(msg)
90
+ if msg[1] == 'hb'
91
+ @ws.send('{"event":"ping"}')
92
+ elsif @open_channels[msg[0]] == 'trades'
93
+ detect_trade(msg)
94
+ elsif @open_channels[msg[0]] == 'book'
95
+ detect_order(msg)
96
+ end
48
97
  elsif msg.is_a?(Hash)
49
98
  message_event(msg)
50
99
  end
@@ -65,10 +114,85 @@ module Irix
65
114
  end
66
115
  end
67
116
 
117
+ # [
118
+ # CHANNEL_ID,
119
+ # [
120
+ # PRICE,
121
+ # COUNT,
122
+ # AMOUNT
123
+ # ]
124
+ # ]
125
+ def detect_order(msg)
126
+ if msg[1][0].is_a?(Array)
127
+ msg[1].each do |point|
128
+ if point[2] > 0
129
+ @snap['bids'] << [point[0].to_s, point[2].to_s]
130
+ else
131
+ @snap['asks'] << [point[0].to_s, point[2].abs.to_s]
132
+ end
133
+ end
134
+ publish_snapshot
135
+ else
136
+ if @increment_count < MIN_INCREMENT_COUNT_TO_SNAPSHOT && @snapshot_time <= Time.now - MAX_PERIOD_TO_SNAPSHOT
137
+ publish_snapshot
138
+ @increment_count = 0
139
+ elsif @increment_count >= MIN_INCREMENT_COUNT_TO_SNAPSHOT && @snapshot_time < Time.now - MIN_PERIOD_TO_SNAPSHOT
140
+ publish_snapshot
141
+ @increment_count = 0
142
+ end
143
+
144
+ fill_increment(msg[1])
145
+ end
146
+ end
147
+
148
+ def fill_increment(order)
149
+ side = order[2].positive? ? 'bid' : 'ask'
150
+ price = order[0].to_s
151
+ if order[1].zero?
152
+ amount = 0
153
+ @snap["#{side}s"].delete_if { |point| point[0] == price }
154
+ else
155
+ amount = order[2].abs.to_s
156
+ @snap["#{side}s"].delete_if { |point| point[0] == price }
157
+ @snap["#{side}s"] << [price.to_s, amount.to_s]
158
+ end
159
+ if side == 'bid'
160
+ @bids.delete_if { |point| point[0] == price }
161
+ @bids << [price.to_s, amount.to_s]
162
+ elsif side == 'ask'
163
+ @asks.delete_if { |point| point[0] == price }
164
+ @asks << [price.to_s, amount.to_s]
165
+ end
166
+ @increment_count += 1
167
+ end
168
+
169
+ def publish_increment
170
+ inc = {}
171
+ inc['bids'] = @bids.sort.reverse if @bids.present?
172
+ inc['asks'] = @asks.sort if @asks.present?
173
+ if inc.present?
174
+ @sequence_number += 1
175
+ @peatio_mq.enqueue_event('public', @market, 'ob-inc',
176
+ 'bids' => inc['bids'], 'asks' => inc['asks'],
177
+ 'sequence' => @sequence_number)
178
+ end
179
+ @bids = []
180
+ @asks = []
181
+ end
182
+
183
+ def publish_snapshot
184
+ @snapshot_time = Time.now
185
+ @peatio_mq.enqueue_event('public', @market, 'ob-snap',
186
+ 'bids' => @snap['bids'].sort.reverse,
187
+ 'asks' => @snap['asks'].sort,
188
+ 'sequence' => @sequence_number)
189
+ end
190
+
68
191
  def message_event(msg)
69
192
  case msg['event']
70
193
  when 'subscribed'
71
194
  Rails.logger.info "Event: #{msg}"
195
+ @open_channels[msg['chanId']] = msg['channel']
72
196
  when 'error'
73
197
  Rails.logger.info "Event: #{msg} ignored"
74
198
  end
@@ -5,6 +5,7 @@ module Irix
5
5
  class << self
6
6
  def register
7
7
  Peatio::Upstream.registry[:bitfinex] = Irix::Bitfinex
8
+ Peatio::Upstream.registry[:huobi] = Irix::Huobi
8
9
  end
9
10
  end
10
11
 
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Irix
4
+ class Huobi < Peatio::Upstream::Base
5
+ require 'time'
6
+
7
+ MIN_INCREMENT_COUNT_TO_SNAPSHOT = 100
8
+ MIN_PERIOD_TO_SNAPSHOT = 5
9
+ MAX_PERIOD_TO_SNAPSHOT = 60
10
+
11
+ attr_accessor :snap, :snapshot_time, :increment_count, :sequence_number,
12
+ :asks, :bids
13
+ # WS huobi global
14
+ # websocket: "wss://api.huobi.pro/ws/"
15
+ # WS for krw markets
16
+ # websocket: "wss://api-cloud.huobi.co.kr/ws/"
17
+
18
+ def initialize(config)
19
+ super
20
+ @connection = Faraday.new(url: (config['rest']).to_s) do |builder|
21
+ builder.response :json
22
+ builder.response :logger if config['debug']
23
+ builder.adapter(@adapter)
24
+ unless config['verify_ssl'].nil?
25
+ builder.ssl[:verify] = config['verify_ssl']
26
+ end
27
+ end
28
+ @ping_set = false
29
+ @rest = (config['rest']).to_s
30
+ @ws_url = (config['websocket']).to_s
31
+ end
32
+
33
+ def ws_read_message(msg)
34
+ data = Zlib::GzipReader.new(StringIO.new(msg.data.map(&:chr).join)).read
35
+ Rails.logger.debug { "received websocket message: #{data}" }
36
+
37
+ object = JSON.parse(data)
38
+ ws_read_public_message(object)
39
+ end
40
+
41
+ def ws_read_public_message(msg)
42
+ if msg['ping'].present?
43
+ @ws.send(JSON.dump('pong': msg['ping']))
44
+ return
45
+ end
46
+
47
+ case msg['ch']
48
+ when /market\.#{@target}\.trade\.detail/
49
+ detect_trade(msg.dig('tick', 'data'))
50
+ when /market\.#{@target}\.mbp\.150/
51
+ detect_order(msg.dig('tick'))
52
+ end
53
+ end
54
+
55
+ def detect_order(msg)
56
+ if @increment_count < MIN_INCREMENT_COUNT_TO_SNAPSHOT && @snapshot_time <= Time.now - MAX_PERIOD_TO_SNAPSHOT
57
+ publish_snapshot
58
+ @increment_count = 0
59
+ elsif @increment_count >= MIN_INCREMENT_COUNT_TO_SNAPSHOT && @snapshot_time < Time.now - MIN_PERIOD_TO_SNAPSHOT
60
+ publish_snapshot
61
+ @increment_count = 0
62
+ end
63
+ fill_increment(msg)
64
+ end
65
+
66
+ def fill_increment(inc)
67
+ fill_side(inc, "bids")
68
+ fill_side(inc, "asks")
69
+ @increment_count += 1
70
+ end
71
+
72
+ def fill_side(inc, side)
73
+ inc[side].each do |price_point|
74
+ price = price_point[0]
75
+ amount = price_point[1]
76
+ if amount.zero?
77
+ @snap[side].delete_if { |point| point[0] == price.to_s }
78
+ else
79
+ @snap[side].delete_if { |point| point[0] == price.to_s }
80
+ @snap[side] << [price.to_s, amount.to_s]
81
+ end
82
+ if side == "bids"
83
+ @bids.delete_if { |point| point[0] == price }
84
+ @bids << [price.to_s, amount.to_s]
85
+ elsif side == "asks"
86
+ @asks.delete_if { |point| point[0] == price }
87
+ @asks << [price.to_s, amount.to_s]
88
+ end
89
+ end
90
+ end
91
+
92
+ def publish_increment
93
+ inc = {}
94
+ inc['bids'] = @bids.sort.reverse if @bids.present?
95
+ inc['asks'] = @asks.sort if @asks.present?
96
+ if inc.present?
97
+ @sequence_number += 1
98
+ @peatio_mq.enqueue_event('public', @market, 'ob-inc',
99
+ 'bids' => inc['bids'], 'asks' => inc['asks'],
100
+ 'sequence' => @sequence_number)
101
+ end
102
+ @bids = []
103
+ @asks = []
104
+ end
105
+
106
+ def publish_snapshot
107
+ @snapshot_time = Time.now
108
+ @peatio_mq.enqueue_event('public', @market, 'ob-snap',
109
+ 'bids' => @snap['bids'].sort.reverse,
110
+ 'asks' => @snap['asks'].sort,
111
+ 'sequence' => @sequence_number)
112
+ end
113
+
114
+ def detect_trade(msg)
115
+ msg.map do |t|
116
+ trade =
117
+ {
118
+ 'tid' => t['tradeId'],
119
+ 'amount' => t['amount'].to_d,
120
+ 'price' => t['price'].to_d,
121
+ 'date' => t['ts'] / 1000,
122
+ 'taker_type' => t['direction']
123
+ }
124
+ notify_public_trade(trade)
125
+ end
126
+ end
127
+
128
+ def ws_connect
129
+ super
130
+ return if @ping_set
131
+
132
+ Fiber.new do
133
+ EM::Synchrony.add_periodic_timer(80) do
134
+ @ws.send(JSON.dump('ping' => Time.now.to_i))
135
+ end
136
+ end.resume
137
+ @ping_set = true
138
+ end
139
+
140
+ def subscribe_trades(market, ws)
141
+ return unless @config['trade_proxy']
142
+
143
+ sub = {
144
+ 'sub' => "market.#{market}.trade.detail"
145
+ }
146
+
147
+ Rails.logger.info 'Open event' + sub.to_s
148
+ EM.next_tick do
149
+ ws.send(JSON.generate(sub))
150
+ end
151
+ end
152
+
153
+ def subscribe_orderbook(market, ws)
154
+ return unless @config['orderbook_proxy']
155
+
156
+ @sequence_number = 0
157
+ @increment_count = 0
158
+ @snapshot_time = Time.now
159
+ @bids = []
160
+ @asks = []
161
+ @snap = { 'asks' => [], 'bids' => [] }
162
+ sub = {
163
+ 'sub' => "market.#{market}.mbp.150"
164
+ }
165
+
166
+ Rails.logger.info 'Open event' + sub.to_s
167
+ EM.next_tick do
168
+ ws.send(JSON.generate(sub))
169
+ end
170
+ Fiber.new do
171
+ EM::Synchrony.add_periodic_timer(0.2) do
172
+ publish_increment
173
+ end
174
+ end.resume
175
+ end
176
+ end
177
+ end
@@ -1,3 +1,3 @@
1
1
  module Irix
2
- VERSION = "2.4.1"
2
+ VERSION = "2.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irix
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.1
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Naichuk M.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-11 00:00:00.000000000 Z
11
+ date: 2020-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-synchrony
@@ -84,14 +84,14 @@ dependencies:
84
84
  name: peatio
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: 2.4.2
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: 2.4.2
97
97
  - !ruby/object:Gem::Dependency
@@ -256,6 +256,7 @@ files:
256
256
  - lib/irix.rb
257
257
  - lib/irix/bitfinex.rb
258
258
  - lib/irix/hooks.rb
259
+ - lib/irix/huobi.rb
259
260
  - lib/irix/railtie.rb
260
261
  - lib/irix/version.rb
261
262
  homepage: https://www.openware.com