# frozen_string_literal: true require('openssl') require('base64') require('curb') require('json') # @author Hernani Rodrigues Vaz module Cex # classe para processar dados no kraken class Client # @return [String] API key attr_reader :aky # @return [String] API secret attr_reader :asc # @return [String] API public url attr_reader :puu # @return [String] API private url attr_reader :pru # @return [String] API private path attr_reader :pth # @param [String] pky API key # @param [String] psc API secret # @return [Client] API kraken base def initialize(pky: ENV['KRAKEN_API_KEY'], psc: ENV['KRAKEN_API_SECRET'], options: {}) b = options.fetch(:base_uri, 'https://api.kraken.com') v = options.fetch(:version, 0) @aky = pky @asc = psc @puu = "#{b}/#{v}/public/" @pru = "#{b}/#{v}/private/" @pth = "/#{v}/private/" end # @return [Hash] resultados ultimas transacoes trades no kraken # @example # { # 'trades' => { # 'TLADFV-QIUTN-QDNCBR' => { # 'ordertxid' => 'O2CRFG-5PCJA-EMX7VC', # 'pair' => 'XETHXXBT', # 'time' => 1_463_422_494.7069, # 'type' => 'buy', # 'ordertype' => 'market', # 'price' => '0.024400', # 'cost' => '1.358424', # 'fee' => '0.003532', # 'vol' => '55.67311475', # 'margin' => '0.000000', # 'misc' => '' # }, # ... # }, # 'count' => 156 # } def trades_history post_private('TradesHistory')['result'] end # @return [Hash] resultados ultimas transacoes ledger no kraken # @example # { # 'ledger' => { # 'LUK3QC-QW6TA-27B7NI' => { # 'refid' => 'A2BNCQS-CHCETZ-JYCER6', # 'time' => 1_584_349_306.9479, # 'type' => 'withdrawal', # 'subtype' => '', # 'aclass' => 'currency', # 'asset' => 'XETH', # 'amount' => '-29.2659039100', # 'fee' => '0.0050000000', # 'balance' => '1.1806988100' # } # , # ... # }, # 'count' => 373 # } def ledger post_private('Ledgers')['result'] end # @return [Hash] saldos no kraken # @example # { # 'ZEUR' => '0.0000', # 'XXBT' => '0.0000000000', # 'XETH' => '1.1806988100', # 'XETC' => '0.0000000000', # 'EOS' => '0.0000001700', # 'BCH' => '0.0000000000' # } def balance post_private('Balance')['result'] end # @return [Hash] data hora kraken # @example # { # 'unixtime' => 1_598_956_727, # 'rfc1123' => 'Tue, 1 Sep 20 10:38:47 +0000' # } def server_time get_public('Time')['result'] end private # HTTP GET request for public API queries. def get_public(method, **opts) parse_response(Curl.get("#{puu}#{method}", opts)) end # HTTP POST request for private API queries involving user credentials. def post_private(method, **opts) # Generate a continually-increasing unsigned 51-bit integer nonce from the current Unix Time. opts.merge!({ nonce: Integer(Time.now) * 1_000_000 }) parse_response(Curl.post("#{pru}#{method}", opts) do |r| r.headers = { 'api-key': aky, 'api-sign': authenticate(method, opts[:nonce], opts.map { |p| p.join('=') }.join('&')) } end) end def authenticate(method, nonce, params) raise(ArgumentError, 'API Key is not set') unless aky raise(ArgumentError, 'API Secret is not set') unless asc Base64.strict_encode64(OpenSSL::HMAC.digest( 'sha512', Base64.decode64(asc), "#{pth}#{method}#{Digest::SHA256.digest("#{nonce}#{params}")}" )) end def parse_response(http) # HTTP_SUCCESS = 200 http.response_code == 200 ? JSON.parse(http.body) : http.status rescue JSON::ParserError, EOFError, Errno::ECONNRESET, Errno::EINVAL, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Timeout::Error => e "Erro da API #{e.inspect}" end end end