bitflyer 0.2.0 → 1.0.1
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 +5 -5
- data/.circleci/config.yml +54 -0
- data/.rubocop.yml +10 -0
- data/.rubocop_todo.yml +35 -0
- data/Gemfile.lock +57 -79
- data/README.md +5 -12
- data/bitflyer.gemspec +6 -4
- data/lib/bitflyer.rb +2 -0
- data/lib/bitflyer/http.rb +6 -4
- data/lib/bitflyer/http/private.rb +71 -47
- data/lib/bitflyer/http/public.rb +11 -9
- data/lib/bitflyer/realtime.rb +3 -1
- data/lib/bitflyer/realtime/client.rb +17 -36
- data/lib/bitflyer/realtime/websocket.rb +145 -0
- data/lib/bitflyer/version.rb +3 -1
- metadata +60 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3b414a7e2a6dd054ca0aff1d217dc8c8e712f8969daa03d91dbf795e33111e94
|
|
4
|
+
data.tar.gz: cb365b45701c9a23bf6d6f7b4818f04e6eeef1e709909802feca8d62779cb7f1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 66beabfdb8ba7893f44b16c41a6371b6eb0cfd131138055d6d72a02ac90af17ffa450a155c4dac0f19433705228319714111464af07159e56c7f0bd08f456b0e
|
|
7
|
+
data.tar.gz: 56ac4fe5e16694a3c4d2b4049c4fefa9b1fa7a9412133ce9c5596d9ec53dd664ccd5d155af54df3645cfab655f790105b62c18c3e73e562bd44e481f68a687d6
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
version: 2.1
|
|
2
|
+
|
|
3
|
+
update_bundler: &update_bundler
|
|
4
|
+
run:
|
|
5
|
+
name: update bundler
|
|
6
|
+
command: gem update bundler
|
|
7
|
+
|
|
8
|
+
bundle_install: &bundle_install
|
|
9
|
+
run:
|
|
10
|
+
name: bundle install
|
|
11
|
+
command: bundle update --bundler && bundle install --path vendor/bundle --jobs 4
|
|
12
|
+
|
|
13
|
+
restore_bundle_cache: &restore_bundle_cache
|
|
14
|
+
restore_cache:
|
|
15
|
+
key: cache-bundler-{{ checksum "Gemfile.lock" }}
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
docker:
|
|
20
|
+
- image: circleci/ruby
|
|
21
|
+
steps:
|
|
22
|
+
- checkout
|
|
23
|
+
- *restore_bundle_cache
|
|
24
|
+
- *update_bundler
|
|
25
|
+
- *bundle_install
|
|
26
|
+
- save_cache:
|
|
27
|
+
key: cache-bundler-{{ checksum "Gemfile.lock" }}
|
|
28
|
+
paths:
|
|
29
|
+
- vendor/bundle
|
|
30
|
+
rubocop:
|
|
31
|
+
docker:
|
|
32
|
+
- image: circleci/ruby
|
|
33
|
+
steps:
|
|
34
|
+
- checkout
|
|
35
|
+
- *restore_bundle_cache
|
|
36
|
+
- *update_bundler
|
|
37
|
+
- *bundle_install
|
|
38
|
+
- run: bundle exec rubocop
|
|
39
|
+
rspec:
|
|
40
|
+
docker:
|
|
41
|
+
- image: circleci/ruby
|
|
42
|
+
steps:
|
|
43
|
+
- checkout
|
|
44
|
+
- *restore_bundle_cache
|
|
45
|
+
- *update_bundler
|
|
46
|
+
- *bundle_install
|
|
47
|
+
- run: bundle exec rspec
|
|
48
|
+
workflows:
|
|
49
|
+
version: 2.1
|
|
50
|
+
rspec:
|
|
51
|
+
jobs:
|
|
52
|
+
- build
|
|
53
|
+
- rubocop
|
|
54
|
+
- rspec
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2020-08-08 12:17:16 UTC using RuboCop version 0.89.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 2
|
|
10
|
+
# Configuration parameters: IgnoredMethods.
|
|
11
|
+
Metrics/AbcSize:
|
|
12
|
+
Max: 32
|
|
13
|
+
|
|
14
|
+
# Offense count: 2
|
|
15
|
+
# Configuration parameters: CountComments, CountAsOne.
|
|
16
|
+
Metrics/ClassLength:
|
|
17
|
+
Max: 146
|
|
18
|
+
|
|
19
|
+
# Offense count: 2
|
|
20
|
+
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods.
|
|
21
|
+
Metrics/MethodLength:
|
|
22
|
+
Max: 20
|
|
23
|
+
|
|
24
|
+
# Offense count: 3
|
|
25
|
+
# Configuration parameters: CountKeywordArgs.
|
|
26
|
+
Metrics/ParameterLists:
|
|
27
|
+
Max: 7
|
|
28
|
+
|
|
29
|
+
# Offense count: 1
|
|
30
|
+
# Cop supports --auto-correct.
|
|
31
|
+
# Configuration parameters: EnforcedStyle.
|
|
32
|
+
# SupportedStyles: always, always_true, never
|
|
33
|
+
Style/FrozenStringLiteralComment:
|
|
34
|
+
Exclude:
|
|
35
|
+
- 'bin/console'
|
data/Gemfile.lock
CHANGED
|
@@ -1,99 +1,77 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
bitflyer (0.
|
|
5
|
-
faraday (
|
|
6
|
-
faraday_middleware (
|
|
7
|
-
|
|
4
|
+
bitflyer (1.0.1)
|
|
5
|
+
faraday (>= 0.14, < 1.2)
|
|
6
|
+
faraday_middleware (>= 0.12, < 1.1)
|
|
7
|
+
websocket-client-simple (~> 0.3.0)
|
|
8
8
|
|
|
9
9
|
GEM
|
|
10
10
|
remote: https://rubygems.org/
|
|
11
11
|
specs:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
celluloid-supervision
|
|
18
|
-
timers (>= 4.1.1)
|
|
19
|
-
celluloid-essentials (0.20.5)
|
|
20
|
-
timers (>= 4.1.1)
|
|
21
|
-
celluloid-extras (0.20.5)
|
|
22
|
-
timers (>= 4.1.1)
|
|
23
|
-
celluloid-fsm (0.20.5)
|
|
24
|
-
timers (>= 4.1.1)
|
|
25
|
-
celluloid-pool (0.20.5)
|
|
26
|
-
timers (>= 4.1.1)
|
|
27
|
-
celluloid-supervision (0.20.6)
|
|
28
|
-
timers (>= 4.1.1)
|
|
29
|
-
concurrent-ruby (1.0.5)
|
|
30
|
-
diff-lcs (1.3)
|
|
31
|
-
dry-configurable (0.7.0)
|
|
32
|
-
concurrent-ruby (~> 1.0)
|
|
33
|
-
dry-container (0.6.0)
|
|
34
|
-
concurrent-ruby (~> 1.0)
|
|
35
|
-
dry-configurable (~> 0.1, >= 0.1.3)
|
|
36
|
-
dry-core (0.4.2)
|
|
37
|
-
concurrent-ruby (~> 1.0)
|
|
38
|
-
dry-equalizer (0.2.0)
|
|
39
|
-
dry-logic (0.4.2)
|
|
40
|
-
dry-container (~> 0.2, >= 0.2.6)
|
|
41
|
-
dry-core (~> 0.2)
|
|
42
|
-
dry-equalizer (~> 0.2)
|
|
43
|
-
dry-types (0.12.2)
|
|
44
|
-
concurrent-ruby (~> 1.0)
|
|
45
|
-
dry-configurable (~> 0.1)
|
|
46
|
-
dry-container (~> 0.3)
|
|
47
|
-
dry-core (~> 0.2, >= 0.2.1)
|
|
48
|
-
dry-equalizer (~> 0.2)
|
|
49
|
-
dry-logic (~> 0.4, >= 0.4.2)
|
|
50
|
-
inflecto (~> 0.0.0, >= 0.0.2)
|
|
51
|
-
dry-validation (0.11.1)
|
|
52
|
-
concurrent-ruby (~> 1.0)
|
|
53
|
-
dry-configurable (~> 0.1, >= 0.1.3)
|
|
54
|
-
dry-core (~> 0.2, >= 0.2.1)
|
|
55
|
-
dry-equalizer (~> 0.2)
|
|
56
|
-
dry-logic (~> 0.4, >= 0.4.0)
|
|
57
|
-
dry-types (~> 0.12.0)
|
|
58
|
-
faraday (0.14.0)
|
|
12
|
+
ast (2.4.1)
|
|
13
|
+
coderay (1.1.3)
|
|
14
|
+
diff-lcs (1.4.4)
|
|
15
|
+
event_emitter (0.2.6)
|
|
16
|
+
faraday (1.1.0)
|
|
59
17
|
multipart-post (>= 1.2, < 3)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
rake (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
rspec-
|
|
77
|
-
|
|
78
|
-
rspec-
|
|
79
|
-
rspec-
|
|
18
|
+
ruby2_keywords
|
|
19
|
+
faraday_middleware (1.0.0)
|
|
20
|
+
faraday (~> 1.0)
|
|
21
|
+
method_source (1.0.0)
|
|
22
|
+
multipart-post (2.1.1)
|
|
23
|
+
parallel (1.19.2)
|
|
24
|
+
parser (2.7.2.0)
|
|
25
|
+
ast (~> 2.4.1)
|
|
26
|
+
pry (0.13.1)
|
|
27
|
+
coderay (~> 1.1)
|
|
28
|
+
method_source (~> 1.0)
|
|
29
|
+
rainbow (3.0.0)
|
|
30
|
+
rake (13.0.1)
|
|
31
|
+
regexp_parser (1.8.2)
|
|
32
|
+
rexml (3.2.4)
|
|
33
|
+
rspec (3.10.0)
|
|
34
|
+
rspec-core (~> 3.10.0)
|
|
35
|
+
rspec-expectations (~> 3.10.0)
|
|
36
|
+
rspec-mocks (~> 3.10.0)
|
|
37
|
+
rspec-core (3.10.0)
|
|
38
|
+
rspec-support (~> 3.10.0)
|
|
39
|
+
rspec-expectations (3.10.0)
|
|
80
40
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
81
|
-
rspec-support (~> 3.
|
|
82
|
-
rspec-mocks (3.
|
|
41
|
+
rspec-support (~> 3.10.0)
|
|
42
|
+
rspec-mocks (3.10.0)
|
|
83
43
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
84
|
-
rspec-support (~> 3.
|
|
85
|
-
rspec-support (3.
|
|
86
|
-
|
|
87
|
-
|
|
44
|
+
rspec-support (~> 3.10.0)
|
|
45
|
+
rspec-support (3.10.0)
|
|
46
|
+
rubocop (1.2.0)
|
|
47
|
+
parallel (~> 1.10)
|
|
48
|
+
parser (>= 2.7.1.5)
|
|
49
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
50
|
+
regexp_parser (>= 1.8)
|
|
51
|
+
rexml
|
|
52
|
+
rubocop-ast (>= 1.0.1)
|
|
53
|
+
ruby-progressbar (~> 1.7)
|
|
54
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
|
55
|
+
rubocop-ast (1.1.1)
|
|
56
|
+
parser (>= 2.7.1.5)
|
|
57
|
+
ruby-progressbar (1.10.1)
|
|
58
|
+
ruby2_keywords (0.0.2)
|
|
59
|
+
unicode-display_width (1.7.0)
|
|
60
|
+
websocket (1.2.8)
|
|
61
|
+
websocket-client-simple (0.3.0)
|
|
62
|
+
event_emitter
|
|
63
|
+
websocket
|
|
88
64
|
|
|
89
65
|
PLATFORMS
|
|
90
66
|
ruby
|
|
91
67
|
|
|
92
68
|
DEPENDENCIES
|
|
93
69
|
bitflyer!
|
|
94
|
-
bundler (~>
|
|
70
|
+
bundler (~> 2.0)
|
|
71
|
+
pry
|
|
95
72
|
rake
|
|
96
73
|
rspec
|
|
74
|
+
rubocop
|
|
97
75
|
|
|
98
76
|
BUNDLED WITH
|
|
99
|
-
|
|
77
|
+
2.0.2
|
data/README.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
[](https://badge.fury.io/rb/bitflyer)
|
|
3
3
|
[](https://circleci.com/gh/unhappychoice/bitflyer)
|
|
4
4
|
[](https://codeclimate.com/github/unhappychoice/bitflyer)
|
|
5
|
-
[](https://libraries.io/github/unhappychoice/bitflyer)
|
|
6
6
|

|
|
7
|
+

|
|
7
8
|
|
|
8
9
|
bitflyer is a wrapper interface of [Bitflyer lightning API](https://lightning.bitflyer.jp/docs)
|
|
9
10
|
|
|
@@ -19,6 +20,8 @@ See https://lightning.bitflyer.jp/docs for details.
|
|
|
19
20
|
|
|
20
21
|
### HTTP API
|
|
21
22
|
|
|
23
|
+
See [public.rb](./lib/bitflyer/http/public.rb) / [private.rb](./lib/bitflyer/http/private.rb) for method definition.
|
|
24
|
+
|
|
22
25
|
#### Example
|
|
23
26
|
|
|
24
27
|
```ruby
|
|
@@ -34,17 +37,7 @@ p private_client.positions # will print your positions
|
|
|
34
37
|
Accessor format is like `{event_name}_{product_code}`.
|
|
35
38
|
You can set lambda to get realtime events.
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
- board_snapshot
|
|
39
|
-
- board
|
|
40
|
-
- ticker
|
|
41
|
-
- executions
|
|
42
|
-
|
|
43
|
-
#### `product_code`
|
|
44
|
-
- btc_jpy
|
|
45
|
-
- fx_btc_jpy
|
|
46
|
-
- eth_btc
|
|
47
|
-
- bch_btc
|
|
40
|
+
`{event_name}` and `{product_code}` is defined at [client.rb](./lib/bitflyer/realtime/client.rb).
|
|
48
41
|
|
|
49
42
|
#### Example
|
|
50
43
|
|
data/bitflyer.gemspec
CHANGED
|
@@ -17,10 +17,12 @@ Gem::Specification.new do |spec|
|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
18
18
|
spec.require_paths = ['lib']
|
|
19
19
|
|
|
20
|
-
spec.add_dependency 'faraday', '
|
|
21
|
-
spec.add_dependency 'faraday_middleware', '
|
|
22
|
-
spec.add_dependency '
|
|
23
|
-
spec.add_development_dependency 'bundler', '~>
|
|
20
|
+
spec.add_dependency 'faraday', '>= 0.14', '< 1.2'
|
|
21
|
+
spec.add_dependency 'faraday_middleware', '>= 0.12', '< 1.1'
|
|
22
|
+
spec.add_dependency 'websocket-client-simple', '~> 0.3.0'
|
|
23
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
24
|
+
spec.add_development_dependency 'pry'
|
|
24
25
|
spec.add_development_dependency 'rake'
|
|
25
26
|
spec.add_development_dependency 'rspec'
|
|
27
|
+
spec.add_development_dependency 'rubocop'
|
|
26
28
|
end
|
data/lib/bitflyer.rb
CHANGED
data/lib/bitflyer/http.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'bitflyer'
|
|
2
4
|
require 'bitflyer/http/public'
|
|
3
5
|
require 'bitflyer/http/private'
|
|
@@ -13,7 +15,7 @@ module Bitflyer
|
|
|
13
15
|
def_delegators :@connection, :get, :post
|
|
14
16
|
|
|
15
17
|
def initialize(key, secret)
|
|
16
|
-
@connection = Faraday::Connection.new(:
|
|
18
|
+
@connection = Faraday::Connection.new(url: 'https://api.bitflyer.jp') do |f|
|
|
17
19
|
f.request :json
|
|
18
20
|
f.response :json
|
|
19
21
|
f.use Authentication, key, secret
|
|
@@ -34,9 +36,9 @@ module Bitflyer
|
|
|
34
36
|
|
|
35
37
|
timestamp = Time.now.to_i.to_s
|
|
36
38
|
method = env[:method].to_s.upcase
|
|
37
|
-
path = env[:url].path + (env[:url].query ?
|
|
39
|
+
path = env[:url].path + (env[:url].query ? "?#{env[:url].query}" : '')
|
|
38
40
|
body = env[:body] || ''
|
|
39
|
-
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @secret, timestamp +
|
|
41
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @secret, timestamp + method + path + body)
|
|
40
42
|
env[:request_headers]['ACCESS-KEY'] = @key if @key
|
|
41
43
|
env[:request_headers]['ACCESS-TIMESTAMP'] = timestamp
|
|
42
44
|
env[:request_headers]['ACCESS-SIGN'] = signature
|
|
@@ -44,4 +46,4 @@ module Bitflyer
|
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
48
|
end
|
|
47
|
-
end
|
|
49
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bitflyer
|
|
2
4
|
module HTTP
|
|
3
5
|
module Private
|
|
@@ -40,10 +42,10 @@ module Bitflyer
|
|
|
40
42
|
|
|
41
43
|
def withdraw(currency_code: 'JPY', bank_account_id: nil, amount: nil, code: nil)
|
|
42
44
|
body = {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
currency_code: currency_code,
|
|
46
|
+
bank_account_id: bank_account_id,
|
|
47
|
+
amount: amount,
|
|
48
|
+
code: code
|
|
47
49
|
}.delete_if { |_, v| v.nil? }
|
|
48
50
|
@connection.post('/v1/me/withdraw', body).body
|
|
49
51
|
end
|
|
@@ -52,102 +54,124 @@ module Bitflyer
|
|
|
52
54
|
@connection.get('/v1/me/getwithdrawals').body
|
|
53
55
|
end
|
|
54
56
|
|
|
55
|
-
def send_child_order(
|
|
57
|
+
def send_child_order(
|
|
58
|
+
product_code: 'BTC_JPY',
|
|
59
|
+
child_order_type: nil,
|
|
60
|
+
side: nil,
|
|
61
|
+
price: nil,
|
|
62
|
+
size: nil,
|
|
63
|
+
minute_to_expire: nil,
|
|
64
|
+
time_in_force: 'GTC'
|
|
65
|
+
)
|
|
56
66
|
body = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
product_code: product_code,
|
|
68
|
+
child_order_type: child_order_type,
|
|
69
|
+
side: side,
|
|
70
|
+
price: price,
|
|
71
|
+
size: size,
|
|
72
|
+
minute_to_expire: minute_to_expire,
|
|
73
|
+
time_in_force: time_in_force
|
|
64
74
|
}.delete_if { |_, v| v.nil? }
|
|
65
75
|
@connection.post('/v1/me/sendchildorder', body).body
|
|
66
76
|
end
|
|
67
77
|
|
|
68
78
|
def cancel_child_order(product_code: 'BTC_JPY', child_order_id: nil, child_order_acceptance_id: nil)
|
|
69
79
|
body = {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
product_code: product_code,
|
|
81
|
+
child_order_id: child_order_id,
|
|
82
|
+
child_order_acceptance_id: child_order_acceptance_id
|
|
73
83
|
}.delete_if { |_, v| v.nil? }
|
|
74
84
|
@connection.post('/v1/me/cancelchildorder', body).body
|
|
75
85
|
end
|
|
76
86
|
|
|
77
87
|
def send_parent_order(order_method: nil, minute_to_expire: nil, time_in_force: 'GTC', parameters: {})
|
|
78
88
|
body = {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
order_method: order_method,
|
|
90
|
+
minute_to_expire: minute_to_expire,
|
|
91
|
+
time_in_force: time_in_force,
|
|
92
|
+
parameters: parameters
|
|
83
93
|
}.delete_if { |_, v| v.nil? }
|
|
84
94
|
@connection.post('/v1/me/sendparentorder', body).body
|
|
85
95
|
end
|
|
86
96
|
|
|
87
97
|
def cancel_parent_order(product_code: 'BTC_JPY', parent_order_id: nil, parent_order_acceptance_id: nil)
|
|
88
98
|
body = {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
product_code: product_code,
|
|
100
|
+
parent_order_id: parent_order_id,
|
|
101
|
+
parent_order_acceptance_id: parent_order_acceptance_id
|
|
92
102
|
}.delete_if { |_, v| v.nil? }
|
|
93
103
|
@connection.post('/v1/me/cancelparentorder', body).body
|
|
94
104
|
end
|
|
95
105
|
|
|
96
106
|
def cancel_all_child_orders(product_code: 'BTC_JPY')
|
|
97
|
-
@connection.post('/v1/me/cancelallchildorders',
|
|
107
|
+
@connection.post('/v1/me/cancelallchildorders', product_code: product_code).body
|
|
98
108
|
end
|
|
99
109
|
|
|
100
|
-
def child_orders(
|
|
110
|
+
def child_orders(
|
|
111
|
+
product_code: 'BTC_JPY',
|
|
112
|
+
count: nil,
|
|
113
|
+
before: nil,
|
|
114
|
+
after: nil,
|
|
115
|
+
child_order_state: nil,
|
|
116
|
+
parent_order_id: nil
|
|
117
|
+
)
|
|
101
118
|
query = {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
product_code: product_code,
|
|
120
|
+
count: count,
|
|
121
|
+
before: before,
|
|
122
|
+
after: after,
|
|
123
|
+
child_order_state: child_order_state,
|
|
124
|
+
parent_order_id: parent_order_id
|
|
108
125
|
}.delete_if { |_, v| v.nil? }
|
|
109
126
|
@connection.get('/v1/me/getchildorders', query).body
|
|
110
127
|
end
|
|
111
128
|
|
|
112
129
|
def parent_orders(product_code: 'BTC_JPY', count: nil, before: nil, after: nil, parent_order_state: nil)
|
|
113
130
|
query = {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
product_code: product_code,
|
|
132
|
+
count: count,
|
|
133
|
+
before: before,
|
|
134
|
+
after: after,
|
|
135
|
+
parent_order_state: parent_order_state
|
|
119
136
|
}.delete_if { |_, v| v.nil? }
|
|
120
137
|
@connection.get('/v1/me/getparentorders', query).body
|
|
121
138
|
end
|
|
122
139
|
|
|
123
140
|
def parent_order(parent_order_id: nil, parent_order_acceptance_id: nil)
|
|
124
141
|
query = {
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
parent_order_id: parent_order_id,
|
|
143
|
+
parent_order_acceptance_id: parent_order_acceptance_id
|
|
127
144
|
}.delete_if { |_, v| v.nil? }
|
|
128
145
|
@connection.get('/v1/me/getparentorder', query).body
|
|
129
146
|
end
|
|
130
147
|
|
|
131
|
-
def executions(
|
|
148
|
+
def executions(
|
|
149
|
+
product_code: 'BTC_JPY',
|
|
150
|
+
count: nil,
|
|
151
|
+
before: nil,
|
|
152
|
+
after: nil,
|
|
153
|
+
child_order_id: nil,
|
|
154
|
+
child_order_acceptance_id: nil
|
|
155
|
+
)
|
|
132
156
|
query = {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
product_code: product_code,
|
|
158
|
+
count: count,
|
|
159
|
+
before: before,
|
|
160
|
+
after: after,
|
|
161
|
+
child_order_id: child_order_id,
|
|
162
|
+
child_order_acceptance_id: child_order_acceptance_id
|
|
139
163
|
}.delete_if { |_, v| v.nil? }
|
|
140
164
|
@connection.get('/v1/me/getexecutions', query).body
|
|
141
165
|
end
|
|
142
166
|
|
|
143
167
|
def positions(product_code: 'FX_BTC_JPY')
|
|
144
|
-
@connection.get('/v1/me/getpositions',
|
|
168
|
+
@connection.get('/v1/me/getpositions', product_code: product_code).body
|
|
145
169
|
end
|
|
146
170
|
|
|
147
171
|
def trading_commission(product_code: 'BTC_JPY')
|
|
148
|
-
@connection.get('v1/me/gettradingcommission',
|
|
172
|
+
@connection.get('v1/me/gettradingcommission', product_code: product_code).body
|
|
149
173
|
end
|
|
150
174
|
end
|
|
151
175
|
end
|
|
152
176
|
end
|
|
153
|
-
end
|
|
177
|
+
end
|
data/lib/bitflyer/http/public.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bitflyer
|
|
2
4
|
module HTTP
|
|
3
5
|
module Public
|
|
@@ -14,22 +16,22 @@ module Bitflyer
|
|
|
14
16
|
@connection.get('/v1/markets').body
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
def board(product_code
|
|
18
|
-
@connection.get('/v1/board',
|
|
19
|
+
def board(product_code: 'BTC_JPY')
|
|
20
|
+
@connection.get('/v1/board', product_code: product_code).body
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
def ticker(product_code
|
|
22
|
-
@connection.get('/v1/ticker',
|
|
23
|
+
def ticker(product_code: 'BTC_JPY')
|
|
24
|
+
@connection.get('/v1/ticker', product_code: product_code).body
|
|
23
25
|
end
|
|
24
26
|
|
|
25
|
-
def executions(product_code
|
|
26
|
-
@connection.get('/v1/executions',
|
|
27
|
+
def executions(product_code: 'BTC_JPY')
|
|
28
|
+
@connection.get('/v1/executions', product_code: product_code).body
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
def chats(from_date
|
|
30
|
-
@connection.get('/v1/getchats',
|
|
31
|
+
def chats(from_date: (Time.now - 5 * 24 * 60 * 60))
|
|
32
|
+
@connection.get('/v1/getchats', from_date: from_date).body
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
end
|
|
35
|
-
end
|
|
37
|
+
end
|
data/lib/bitflyer/realtime.rb
CHANGED
|
@@ -1,46 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './websocket'
|
|
2
4
|
|
|
3
5
|
module Bitflyer
|
|
4
6
|
module Realtime
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'lightning_board_snapshot_FX_BTC_JPY',
|
|
9
|
-
'lightning_board_snapshot_ETH_BTC',
|
|
10
|
-
'lightning_board_snapshot_BCH_BTC',
|
|
11
|
-
'lightning_board_BTC_JPY',
|
|
12
|
-
'lightning_board_FX_BTC_JPY',
|
|
13
|
-
'lightning_board_ETH_BTC',
|
|
14
|
-
'lightning_board_BCH_BTC',
|
|
15
|
-
'lightning_ticker_BTC_JPY',
|
|
16
|
-
'lightning_ticker_FX_BTC_JPY',
|
|
17
|
-
'lightning_ticker_ETH_BTC',
|
|
18
|
-
'lightning_ticker_BCH_BTC',
|
|
19
|
-
'lightning_executions_BTC_JPY',
|
|
20
|
-
'lightning_executions_FX_BTC_JPY',
|
|
21
|
-
'lightning_executions_ETH_BTC',
|
|
22
|
-
'lightning_executions_BCH_BTC'
|
|
23
|
-
].freeze
|
|
7
|
+
EVENT_NAMES = %w[lightning_board_snapshot lightning_board lightning_ticker lightning_executions].freeze
|
|
8
|
+
MARKETS = %w[BTC_JPY FX_BTC_JPY ETH_BTC BCH_BTC BTCJPY_MAT3M BTCJPY_MAT1WK BTCJPY_MAT2WK].freeze
|
|
9
|
+
CHANNEL_NAMES = EVENT_NAMES.product(MARKETS).map { |e, m| "#{e}_#{m}" }
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
attr_accessor *Realtime::CHANNEL_NAMES.map { |name| name.gsub('lightning_', '').downcase.to_sym }
|
|
11
|
+
SOCKET_HOST = 'https://io.lightstream.bitflyer.com'
|
|
27
12
|
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
class Client
|
|
14
|
+
attr_accessor :websocket_client, :ping_interval, :ping_timeout, :last_ping_at, :last_pong_at
|
|
30
15
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
presence: ->(envelope) {},
|
|
38
|
-
status: ->(envelope) {}
|
|
39
|
-
)
|
|
16
|
+
Realtime::CHANNEL_NAMES.each do |channel_name|
|
|
17
|
+
define_method "#{channel_name.gsub('lightning_', '').downcase.to_sym}=" do |callback|
|
|
18
|
+
@websocket_client.subscribe(channel_name: channel_name.to_sym, &callback)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
40
21
|
|
|
41
|
-
|
|
42
|
-
@
|
|
22
|
+
def initialize
|
|
23
|
+
@websocket_client = Bitflyer::Realtime::WebSocketClient.new(host: SOCKET_HOST)
|
|
43
24
|
end
|
|
44
25
|
end
|
|
45
26
|
end
|
|
46
|
-
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'websocket-client-simple'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Bitflyer
|
|
7
|
+
module Realtime
|
|
8
|
+
class WebSocketClient
|
|
9
|
+
attr_accessor :websocket_client, :channel_name, :channel_callbacks, :ping_interval, :ping_timeout,
|
|
10
|
+
:last_ping_at, :last_pong_at, :error
|
|
11
|
+
|
|
12
|
+
def initialize(host:, debug: false)
|
|
13
|
+
@host = host
|
|
14
|
+
@debug = debug
|
|
15
|
+
@error = nil
|
|
16
|
+
@channel_names = []
|
|
17
|
+
@channel_callbacks = {}
|
|
18
|
+
connect
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def subscribe(channel_name:, &block)
|
|
22
|
+
debug_log "Subscribe #{channel_name}"
|
|
23
|
+
@channel_names = (@channel_names + [channel_name]).uniq
|
|
24
|
+
@channel_callbacks[channel_name] = block
|
|
25
|
+
websocket_client.send "42#{['subscribe', channel_name].to_json}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def connect
|
|
29
|
+
@websocket_client = WebSocket::Client::Simple.connect "#{@host}/socket.io/?transport=websocket"
|
|
30
|
+
this = self
|
|
31
|
+
|
|
32
|
+
Thread.new do
|
|
33
|
+
loop do
|
|
34
|
+
sleep 1
|
|
35
|
+
if @websocket_client&.open?
|
|
36
|
+
send_ping
|
|
37
|
+
wait_pong
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Thread.new do
|
|
43
|
+
loop do
|
|
44
|
+
sleep 1
|
|
45
|
+
next unless @error
|
|
46
|
+
|
|
47
|
+
reconnect
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@websocket_client.on(:message) { |payload| this.handle_message(payload: payload) }
|
|
52
|
+
@websocket_client.on(:error) { |error| this.handle_error(error: error) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def send_ping
|
|
56
|
+
return unless @last_ping_at && @ping_interval
|
|
57
|
+
return unless Time.now.to_i - @last_ping_at > @ping_interval / 1000
|
|
58
|
+
|
|
59
|
+
debug_log 'Sent ping'
|
|
60
|
+
@websocket_client.send '2'
|
|
61
|
+
@last_ping_at = Time.now.to_i
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def wait_pong
|
|
65
|
+
return unless @last_pong_at && @ping_timeout
|
|
66
|
+
return unless Time.now.to_i - @last_pong_at > (@ping_interval + @ping_timeout) / 1000
|
|
67
|
+
|
|
68
|
+
debug_log 'Timed out waiting pong'
|
|
69
|
+
@websocket_client.close
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def reconnect
|
|
73
|
+
return unless @error
|
|
74
|
+
|
|
75
|
+
debug_log 'Reconnecting...'
|
|
76
|
+
|
|
77
|
+
@error = nil
|
|
78
|
+
@websocket_client.close if @websocket_client.open?
|
|
79
|
+
connect
|
|
80
|
+
@channel_names.each do |channel_name|
|
|
81
|
+
debug_log "42#{{ subscribe: channel_name }.to_json}"
|
|
82
|
+
websocket_client.send "42#{['subscribe', channel_name].to_json}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def handle_error(error:)
|
|
87
|
+
debug_log error
|
|
88
|
+
return unless error.is_a? Errno::ECONNRESET
|
|
89
|
+
|
|
90
|
+
reconnect
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def handle_message(payload:)
|
|
94
|
+
debug_log payload.data
|
|
95
|
+
return unless payload.data =~ /^\d+/
|
|
96
|
+
|
|
97
|
+
code, body = payload.data.scan(/^(\d+)(.*)$/)[0]
|
|
98
|
+
|
|
99
|
+
case code.to_i
|
|
100
|
+
when 0 then setup_by_response(json: body)
|
|
101
|
+
when 3 then receive_pong
|
|
102
|
+
when 41 then disconnect
|
|
103
|
+
when 42 then emit_message(json: body)
|
|
104
|
+
end
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
puts e
|
|
107
|
+
puts e.backtrace.join("\n")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def setup_by_response(json:)
|
|
111
|
+
body = JSON.parse json
|
|
112
|
+
@ping_interval = body['pingInterval'].to_i || 25_000
|
|
113
|
+
@ping_timeout = body['pingTimeout'].to_i || 60_000
|
|
114
|
+
@last_ping_at = Time.now.to_i
|
|
115
|
+
@last_pong_at = Time.now.to_i
|
|
116
|
+
channel_callbacks.each do |channel_name, _|
|
|
117
|
+
websocket_client.send "42#{['subscribe', channel_name].to_json}"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def receive_pong
|
|
122
|
+
debug_log 'Received pong'
|
|
123
|
+
@last_pong_at = Time.now.to_i
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def disconnect
|
|
127
|
+
debug_log 'Disconnecting from server...'
|
|
128
|
+
@error = true
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def emit_message(json:)
|
|
132
|
+
channel_name, *messages = JSON.parse json
|
|
133
|
+
return unless channel_name
|
|
134
|
+
|
|
135
|
+
messages.each { |message| @channel_callbacks[channel_name.to_sym]&.call(message) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def debug_log(message)
|
|
139
|
+
return unless @debug
|
|
140
|
+
|
|
141
|
+
p message
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
data/lib/bitflyer/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,71 +1,97 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bitflyer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuji Ueki
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-11-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.14'
|
|
20
|
+
- - "<"
|
|
18
21
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
22
|
+
version: '1.2'
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
26
|
requirements:
|
|
24
|
-
- - "
|
|
27
|
+
- - ">="
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.14
|
|
29
|
+
version: '0.14'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.2'
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: faraday_middleware
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
30
36
|
requirements:
|
|
31
|
-
- - "
|
|
37
|
+
- - ">="
|
|
32
38
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 0.12
|
|
39
|
+
version: '0.12'
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '1.1'
|
|
34
43
|
type: :runtime
|
|
35
44
|
prerelease: false
|
|
36
45
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
46
|
requirements:
|
|
38
|
-
- - "
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0.12'
|
|
50
|
+
- - "<"
|
|
39
51
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
52
|
+
version: '1.1'
|
|
41
53
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
54
|
+
name: websocket-client-simple
|
|
43
55
|
requirement: !ruby/object:Gem::Requirement
|
|
44
56
|
requirements:
|
|
45
57
|
- - "~>"
|
|
46
58
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
59
|
+
version: 0.3.0
|
|
48
60
|
type: :runtime
|
|
49
61
|
prerelease: false
|
|
50
62
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
63
|
requirements:
|
|
52
64
|
- - "~>"
|
|
53
65
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
66
|
+
version: 0.3.0
|
|
55
67
|
- !ruby/object:Gem::Dependency
|
|
56
68
|
name: bundler
|
|
57
69
|
requirement: !ruby/object:Gem::Requirement
|
|
58
70
|
requirements:
|
|
59
71
|
- - "~>"
|
|
60
72
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
73
|
+
version: '2.0'
|
|
62
74
|
type: :development
|
|
63
75
|
prerelease: false
|
|
64
76
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
77
|
requirements:
|
|
66
78
|
- - "~>"
|
|
67
79
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
80
|
+
version: '2.0'
|
|
81
|
+
- !ruby/object:Gem::Dependency
|
|
82
|
+
name: pry
|
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
type: :development
|
|
89
|
+
prerelease: false
|
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
69
95
|
- !ruby/object:Gem::Dependency
|
|
70
96
|
name: rake
|
|
71
97
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,6 +120,20 @@ dependencies:
|
|
|
94
120
|
- - ">="
|
|
95
121
|
- !ruby/object:Gem::Version
|
|
96
122
|
version: '0'
|
|
123
|
+
- !ruby/object:Gem::Dependency
|
|
124
|
+
name: rubocop
|
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '0'
|
|
130
|
+
type: :development
|
|
131
|
+
prerelease: false
|
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - ">="
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '0'
|
|
97
137
|
description: Bitflyer API wrapper
|
|
98
138
|
email:
|
|
99
139
|
- unhappychoice@gmail.com
|
|
@@ -101,6 +141,9 @@ executables: []
|
|
|
101
141
|
extensions: []
|
|
102
142
|
extra_rdoc_files: []
|
|
103
143
|
files:
|
|
144
|
+
- ".circleci/config.yml"
|
|
145
|
+
- ".rubocop.yml"
|
|
146
|
+
- ".rubocop_todo.yml"
|
|
104
147
|
- CODE_OF_CONDUCT.md
|
|
105
148
|
- Gemfile
|
|
106
149
|
- Gemfile.lock
|
|
@@ -114,6 +157,7 @@ files:
|
|
|
114
157
|
- lib/bitflyer/http/public.rb
|
|
115
158
|
- lib/bitflyer/realtime.rb
|
|
116
159
|
- lib/bitflyer/realtime/client.rb
|
|
160
|
+
- lib/bitflyer/realtime/websocket.rb
|
|
117
161
|
- lib/bitflyer/version.rb
|
|
118
162
|
homepage: https://github.com/unhappychoice/bitflyer
|
|
119
163
|
licenses:
|
|
@@ -134,8 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
178
|
- !ruby/object:Gem::Version
|
|
135
179
|
version: '0'
|
|
136
180
|
requirements: []
|
|
137
|
-
|
|
138
|
-
rubygems_version: 2.6.11
|
|
181
|
+
rubygems_version: 3.0.3
|
|
139
182
|
signing_key:
|
|
140
183
|
specification_version: 4
|
|
141
184
|
summary: Bitflyer API wrapper
|