chankura_client 0.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 +7 -0
- data/README.md +73 -0
- data/lib/chankura_api/auth.rb +36 -0
- data/lib/chankura_api/client.rb +79 -0
- data/lib/chankura_api/client/version.rb +5 -0
- data/lib/chankura_api/config.rb +12 -0
- data/lib/chankura_api/core_ext.rb +2 -0
- data/lib/chankura_api/core_ext/keys.rb +16 -0
- data/lib/chankura_api/core_ext/to_query.rb +60 -0
- data/lib/chankura_api/streaming_client.rb +48 -0
- data/lib/chankura_client.rb +5 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a6ec17e84eb2423228db73a189a2046c45940830
|
4
|
+
data.tar.gz: d3e597c3eea88b47f0b94c2433c4edea4fa3bbe9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c41bd64a72f1fd9afef3f26ed9207279dc86c5f1355e9604091268b02e217ba24b4f44c380941b7fccfcd94b9d98de3d33e6fcf8ce171971e5540e79a681b096
|
7
|
+
data.tar.gz: ed46d09776ef588957465fb138d30cb3180fd94da0390f3b1e8f24956ff0f802904581cfe80939ed30cf3984d863549d9df20913fddd01469d991e8f94ed4d2f
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Ruby client for Chankura Exchange API
|
2
|
+
|
3
|
+
[](https://travis-ci.org/ChankuraExchange/chankura-client-ruby)
|
4
|
+
[](https://codeclimate.com/github/ChankuraExchange/chankura-client-ruby)
|
5
|
+
[](https://codeclimate.com/github/ChankuraExchange/chankura-client-ruby/coverage)
|
6
|
+
|
7
|
+
`chankura-client-ruby` is a client for Chankura Exchange API, support all Chankura Exchange API functions like submit order, get tickers, etc. It's also a reference client implementation, where you can find how to authenticate private Chankura Exchange API.
|
8
|
+
|
9
|
+
### Requirements
|
10
|
+
|
11
|
+
* ruby 2.0.0 or higher (if you want to run 'rake test' in this gem you'll need ruby 2.1.0 or higher)
|
12
|
+
* openssl
|
13
|
+
|
14
|
+
### Install
|
15
|
+
|
16
|
+
```shell
|
17
|
+
$ gem install chankura_client
|
18
|
+
```
|
19
|
+
|
20
|
+
### Usage
|
21
|
+
|
22
|
+
#### Command line tool
|
23
|
+
|
24
|
+
TBD
|
25
|
+
|
26
|
+
#### REST API client
|
27
|
+
|
28
|
+
Use `#get` or `#post` to access API after you created a `ChankuraAPI::Client`:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require 'chankura_client'
|
32
|
+
|
33
|
+
# Client can be initialized not providing key and sercet, but this client can only access public APIs
|
34
|
+
client_public = ChankuraAPI::Client.new endpoint: 'https://trading.chanura.com'
|
35
|
+
|
36
|
+
# GET public api /api/v2/markets
|
37
|
+
client_public.get_public '/api/v2/markets'
|
38
|
+
|
39
|
+
# To build a full functional client which can access both public/private api, access_key/secret_key
|
40
|
+
# are required.
|
41
|
+
#
|
42
|
+
# If there's no data received in `timeout` seconds, Net::OpenTimeout will be raised. Default to 60.
|
43
|
+
#
|
44
|
+
client = ChankuraAPI::Client.new access_key: 'your_access_key', secret_key: 'your_secret_key', endpoint: 'https://trading.chankura.com', timeout: 60
|
45
|
+
|
46
|
+
# GET private api /api/v2/orders with 'market=btczar'
|
47
|
+
client.get '/api/v2/orders', market: 'btczar'
|
48
|
+
|
49
|
+
# POST to create an order
|
50
|
+
client.post '/api/v2/orders', market: 'btczar', side: 'sell', volume: '0.11', price: '2955.0'
|
51
|
+
|
52
|
+
# POST to create multiple orders at once
|
53
|
+
client.post '/api/v2/orders/multi', market: 'btczar', orders: [{side: 'buy', volume: '0.15', price: '2955.0'}, {side: 'sell', volume: '0.16', price: '2956'}]
|
54
|
+
```
|
55
|
+
|
56
|
+
Check [Chankura API v2 Documents](https://trading.chankura.com/documents/api_v2) for details on Chankura API.
|
57
|
+
|
58
|
+
### Streaming API client
|
59
|
+
|
60
|
+
Streaming API client is built upon eventmachine, it will start an endless loop to accept updates from server side, you only need to provide a callback block:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
require 'chankura_client'
|
64
|
+
|
65
|
+
client = ChankuraAPI::StreamingClient.new access_key: 'your_access_key', secret_key: 'your_secret_key', endpoint: 'wss://trading.chankura.com:8080'
|
66
|
+
client.run do |message|
|
67
|
+
# do whatever you want with message
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
### How To Contribute
|
72
|
+
|
73
|
+
Just create an issue or open a pull request :)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ChankuraAPI
|
2
|
+
class Auth
|
3
|
+
|
4
|
+
def initialize(access_key, secret_key)
|
5
|
+
@access_key = access_key
|
6
|
+
@secret_key = secret_key
|
7
|
+
end
|
8
|
+
|
9
|
+
def signed_challenge(challenge)
|
10
|
+
signature = OpenSSL::HMAC.hexdigest 'SHA256', @secret_key, "#{@access_key}#{challenge}"
|
11
|
+
{auth: {access_key: @access_key, answer: signature}}
|
12
|
+
end
|
13
|
+
|
14
|
+
def signed_params(verb, path, params={})
|
15
|
+
params = format_params params
|
16
|
+
signature = sign verb, path, URI.unescape(params.to_query)
|
17
|
+
params.merge(signature: signature)
|
18
|
+
end
|
19
|
+
|
20
|
+
def sign(verb, path, params)
|
21
|
+
OpenSSL::HMAC.hexdigest 'SHA256', @secret_key, payload(verb, path, params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def payload(verb, path, params)
|
25
|
+
"#{verb.upcase}|#{path}|#{params}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def format_params(params)
|
29
|
+
params = params.symbolize_keys
|
30
|
+
params[:access_key] ||= @access_key
|
31
|
+
params[:tonce] ||= (Time.now.to_f*1000).to_i
|
32
|
+
params
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require_relative 'client/version'
|
4
|
+
|
5
|
+
module ChankuraAPI
|
6
|
+
class Client
|
7
|
+
attr_reader :auth
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
options = options.symbolize_keys
|
11
|
+
setup_auth_keys options
|
12
|
+
@endpoint = options[:endpoint] || 'https://trading.chankura.com'
|
13
|
+
@timeout = options[:timeout] || 60
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_public(path, params = {})
|
17
|
+
uri = URI("#{@endpoint}#{path}")
|
18
|
+
uri.query = URI.encode_www_form params
|
19
|
+
|
20
|
+
request(:get, path, nil, params) do |http, _|
|
21
|
+
http.request_get(uri.request_uri)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(path, params = {})
|
26
|
+
check_auth!
|
27
|
+
|
28
|
+
uri = URI("#{@endpoint}#{path}")
|
29
|
+
|
30
|
+
request(:get, path, @auth, params) do |http, signed_params|
|
31
|
+
uri.query = URI.encode_www_form signed_params
|
32
|
+
http.request_get(uri.request_uri)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def post(path, params = {})
|
37
|
+
check_auth!
|
38
|
+
|
39
|
+
request(:post, path, @auth, params) do |http, signed_params|
|
40
|
+
http.request_post(path, signed_params.to_query)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def request(action, path, auth, params = {})
|
47
|
+
uri = URI("#{@endpoint}#{path}")
|
48
|
+
params = auth.signed_params action.to_s.upcase, path, params if auth
|
49
|
+
|
50
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
51
|
+
http.open_timeout = @timeout
|
52
|
+
http.use_ssl = true if @endpoint.start_with?('https://')
|
53
|
+
|
54
|
+
http.start do |http|
|
55
|
+
parse yield(http, params)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse(response)
|
60
|
+
JSON.parse response.body
|
61
|
+
rescue JSON::ParserError
|
62
|
+
{ http_error: { code: response.code, body: response.body } }
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup_auth_keys(options)
|
66
|
+
if options[:access_key] && options[:secret_key]
|
67
|
+
@access_key = options[:access_key]
|
68
|
+
@secret_key = options[:secret_key]
|
69
|
+
@auth = Auth.new @access_key, @secret_key
|
70
|
+
else
|
71
|
+
raise ArgumentError, 'Missing access key and/or secret key'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_auth!
|
76
|
+
raise ArgumentError, 'Missing access key and/or secret key' if @auth.nil?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
unless {}.respond_to? :symbolize_keys
|
2
|
+
class Hash
|
3
|
+
def transform_keys
|
4
|
+
return enum_for(:transform_keys) unless block_given?
|
5
|
+
result = self.class.new
|
6
|
+
each_key do |key|
|
7
|
+
result[yield(key)] = self[key]
|
8
|
+
end
|
9
|
+
result
|
10
|
+
end
|
11
|
+
|
12
|
+
def symbolize_keys
|
13
|
+
transform_keys{ |key| key.to_sym rescue key }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
unless Object.new.respond_to? :to_query and Object.new.respond_to? :to_param
|
2
|
+
|
3
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
4
|
+
|
5
|
+
class Object
|
6
|
+
def to_param
|
7
|
+
to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_query(key)
|
11
|
+
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NilClass
|
16
|
+
def to_param
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class TrueClass
|
22
|
+
def to_param
|
23
|
+
self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FalseClass
|
28
|
+
def to_param
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Array
|
34
|
+
def to_param
|
35
|
+
collect(&:to_param).join '/'
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_query(key)
|
39
|
+
prefix = "#{key}[]"
|
40
|
+
|
41
|
+
if empty?
|
42
|
+
nil.to_query(prefix)
|
43
|
+
else
|
44
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Hash
|
50
|
+
def to_query(namespace = nil)
|
51
|
+
collect do |key, value|
|
52
|
+
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
|
53
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
54
|
+
end
|
55
|
+
end.compact.sort! * '&'
|
56
|
+
end
|
57
|
+
|
58
|
+
alias_method :to_param, :to_query
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'json'
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'faye/websocket'
|
5
|
+
|
6
|
+
module ChankuraAPI
|
7
|
+
class StreamingClient < Client
|
8
|
+
def initialize(options = {})
|
9
|
+
super
|
10
|
+
@endpoint = options[:endpoint] || 'wss://trading.chankura.com:8080'
|
11
|
+
@logger = options[:logger] || Logger.new(STDOUT)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(&callback)
|
15
|
+
EM.run do
|
16
|
+
ws = Faye::WebSocket::Client.new(@endpoint)
|
17
|
+
|
18
|
+
ws.on(:open) do |event|
|
19
|
+
@logger.info 'Connected.'
|
20
|
+
end
|
21
|
+
|
22
|
+
ws.on(:message) do |event|
|
23
|
+
msg = JSON.parse(event.data)
|
24
|
+
|
25
|
+
key = msg.keys.first
|
26
|
+
data = msg[key]
|
27
|
+
case key
|
28
|
+
when 'challenge'
|
29
|
+
ws.send JSON.dump(@auth.signed_challenge(data))
|
30
|
+
else
|
31
|
+
begin
|
32
|
+
callback.call msg
|
33
|
+
rescue
|
34
|
+
@logger.error "Failed to process message: #{payload}"
|
35
|
+
@logger.error $!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
ws.on(:close) do |event|
|
41
|
+
@logger.info "Closed. Code: #{event.code}, Reason: #{event.reason || 'none'}"
|
42
|
+
ws = nil
|
43
|
+
EM.stop
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chankura_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chankura Exchange
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faye-websocket
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.9.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.9.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 5.5.1
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 5.5.1
|
41
|
+
description: A ruby client which can access all Chankura Exchange's API.
|
42
|
+
email:
|
43
|
+
- support@chankura.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- README.md
|
49
|
+
- lib/chankura_api/auth.rb
|
50
|
+
- lib/chankura_api/client.rb
|
51
|
+
- lib/chankura_api/client/version.rb
|
52
|
+
- lib/chankura_api/config.rb
|
53
|
+
- lib/chankura_api/core_ext.rb
|
54
|
+
- lib/chankura_api/core_ext/keys.rb
|
55
|
+
- lib/chankura_api/core_ext/to_query.rb
|
56
|
+
- lib/chankura_api/streaming_client.rb
|
57
|
+
- lib/chankura_client.rb
|
58
|
+
homepage: https://github.com/ChankuraExchange/chankura-client-ruby
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.5.2
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: A ruby client to access Chankura Exchange's API.
|
82
|
+
test_files: []
|