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.
@@ -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
@@ -0,0 +1,73 @@
1
+ # Ruby client for Chankura Exchange API
2
+
3
+ [![Build Status](https://travis-ci.org/ChankuraExchange/chankura-client-ruby.svg?branch=master)](https://travis-ci.org/ChankuraExchange/chankura-client-ruby)
4
+ [![Code Climate](https://codeclimate.com/github/ChankuraExchange/chankura-client-ruby/badges/gpa.svg)](https://codeclimate.com/github/ChankuraExchange/chankura-client-ruby)
5
+ [![Test Coverage](https://codeclimate.com/github/ChankuraExchange/chankura-client-ruby/badges/coverage.svg)](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,5 @@
1
+ module ChankuraAPI
2
+ class Client
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ module ChankuraAPI
2
+ class Config
3
+
4
+ def self.get_chankurarc(path)
5
+ if File.exist? path
6
+ lines = File.readlines path
7
+ [ lines[0].strip, lines[1].strip ]
8
+ end
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,2 @@
1
+ require 'chankura_api/core_ext/keys'
2
+ require 'chankura_api/core_ext/to_query'
@@ -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
@@ -0,0 +1,5 @@
1
+ require_relative 'chankura_api/core_ext'
2
+ require_relative 'chankura_api/config'
3
+ require_relative 'chankura_api/auth'
4
+ require_relative 'chankura_api/client'
5
+ require_relative 'chankura_api/streaming_client'
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: []