bitget.rb 0.3.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9b1ea6c7d91ff48cbcd7e70f6dd5c3aaf43231bd6eac34050c6b0fc9c1c33cfd
4
+ data.tar.gz: d53e337eef7ed2b324ec29e64b36bcd82314c75f069761c13d5ef71f01a5abe9
5
+ SHA512:
6
+ metadata.gz: a66581d1bf3312c2826055eec6e4fdfc73a49ff1bcef8950ba7fe0f9ab067811983b960eecb979619a5e907c4561498525a15f4a113caf8d76826debf7543a18
7
+ data.tar.gz: 1f3b6e5288b51858423b470f7139be1bc7aba01e3719c54fe170833986d5784887ceda5c6719f2c5b43da795d5f0b54a26c6b2b4c0205cd8ef4b0d230ba181e1
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'http.rb'
4
+
5
+ group :development, :test do
6
+ gem 'minitest'
7
+ gem 'minitest-spec-context'
8
+ gem 'vcr'
9
+ end
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # bitget.rb
2
+
3
+ ## Description
4
+
5
+ Access the Bitget API with Ruby.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+ ```ruby
11
+ gem 'bitget.rb'
12
+ ```
13
+ And then execute:
14
+ ```bash
15
+ $ bundle
16
+ ```
17
+ Or install it yourself as:
18
+ ```bash
19
+ $ gem install bitget.rb
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Setup
25
+ ```ruby
26
+ bitget_client ||= Bitget::Client.new(
27
+ api_key: 'api_key0',
28
+ api_secret: 'api_secret0',
29
+ api_passphrase: 'api_passphrase0'
30
+ )
31
+ ```
32
+
33
+ ### Retrieve Info on All the Coins Traded
34
+ ```ruby
35
+ bitget_client.spot_public_coins
36
+ # =>
37
+ # {
38
+ # "code" => "00000",
39
+ # "msg" => "success",
40
+ # "requestTime" => 1743252600562,
41
+ # "data" => [
42
+ # {
43
+ # "coinId" => "1460",
44
+ # "coin" => "U2U",
45
+ # "transfer" => "false",
46
+ # "chains" => [
47
+ # {
48
+ # "chain" => "UnicornUltraSolaris",
49
+ # ...
50
+ # }
51
+ # ]
52
+ # },
53
+ # ...
54
+ # {...}
55
+ # ]
56
+ # "areaCoin" => "no"
57
+ # }
58
+ ```
59
+
60
+ ### Retrieve Info for One of the Coins Traded
61
+ ```ruby
62
+ bitget_client.spot_public_coins(coin: 'BTC')
63
+ # =>
64
+ # {
65
+ # "code" => "00000",
66
+ # "msg" => "success",
67
+ # "requestTime" => 1743252619082,
68
+ # "data" => [
69
+ # {
70
+ # "coinId" => "1",
71
+ # "coin" => "BTC",
72
+ # "transfer" => "true",
73
+ # "chains" => [
74
+ # {
75
+ # "chain" => "BTC",
76
+ # "needTag" => "false",
77
+ # "withdrawable" => "true",
78
+ # "rechargeable" => "true",
79
+ # "withdrawFee" => "0.00005",
80
+ # "extraWithdrawFee" => "0",
81
+ # "depositConfirm" => "1",
82
+ # "withdrawConfirm" => "1",
83
+ # "minDepositAmount" => "0.00001",
84
+ # "minWithdrawAmount" => "0.0005",
85
+ # "browserUrl" => "https://www.blockchain.com/explorer/transactions/btc/",
86
+ # "contractAddress" => nil,
87
+ # "withdrawStep" => "0",
88
+ # "withdrawMinScale" => "8",
89
+ # "congestion" => "normal"
90
+ # },
91
+ # ...
92
+ # {...}
93
+ # ]
94
+ # }
95
+ # ]
96
+ # "areaCoin" => "no"
97
+ # }
98
+ ```
99
+
100
+ ## Contributing
101
+
102
+ 1. Fork it ( https://github.com/thoran/bitget.rb/fork )
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create a new pull request
data/bitget.rb.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'bitget.rb'
3
+
4
+ spec.version = '0.3.0'
5
+ spec.date = '2025-03-29'
6
+
7
+ spec.summary = "Access the Bitget API with Ruby."
8
+ spec.description = "Access the Bitget API with Ruby."
9
+
10
+ spec.author = 'thoran'
11
+ spec.email = 'code@thoran.com'
12
+ spec.homepage = 'http://github.com/thoran/bitget.rb'
13
+ spec.license = 'Ruby'
14
+
15
+ spec.required_ruby_version = '>= 2.7'
16
+
17
+ spec.add_dependency('http.rb')
18
+ spec.files = [
19
+ 'bitget.rb.gemspec',
20
+ 'Gemfile',
21
+ Dir['lib/**/*.rb'],
22
+ 'README.md',
23
+ Dir['test/**/*.rb']
24
+ ].flatten
25
+ spec.require_paths = ['lib']
26
+ end
@@ -0,0 +1,8 @@
1
+ # Bitget/Client.rb
2
+ # Bitget::Client
3
+
4
+ require_relative './V2/Client'
5
+
6
+ module Bitget
7
+ class Client < Bitget::V2::Client; end
8
+ end
@@ -0,0 +1,16 @@
1
+ # Bitget/Error.rb
2
+ # Bitget::Error
3
+
4
+ module Bitget
5
+ class Error < RuntimeError
6
+ attr_reader :code, :message, :body
7
+
8
+ private
9
+
10
+ def initialize(code:, message:, body:)
11
+ @code = code
12
+ @message = message
13
+ @body = body
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,164 @@
1
+ # Bitget/V2/Client.rb
2
+ # Bitget::V2::Client
3
+
4
+ require 'Hash/to_parameter_string'
5
+ gem 'http.rb'; require 'http.rb'
6
+ require 'json'
7
+ require 'logger'
8
+ require 'openssl'
9
+
10
+ require_relative '../Error'
11
+ require_relative '../../Hash/x_www_form_urlencode'
12
+
13
+ module Bitget
14
+ module V2
15
+ class Client
16
+
17
+ API_HOST = 'api.bitget.com'
18
+
19
+ class << self
20
+ attr_writer :log_file_path
21
+
22
+ def path_prefix
23
+ '/api/v2'
24
+ end
25
+
26
+ def default_log_file_path
27
+ File.join(%w{~ log bitget log.txt})
28
+ end
29
+
30
+ def log_file_path
31
+ File.expand_path(@log_file_path || default_log_file_path)
32
+ end
33
+
34
+ def log_file
35
+ FileUtils.mkdir_p(File.dirname(log_file_path))
36
+ File.open(log_file_path, File::WRONLY | File::APPEND | File::CREAT)
37
+ end
38
+
39
+ def logger
40
+ @logger ||= Logger.new(log_file, 'daily')
41
+ end
42
+ end # class << self
43
+
44
+ def spot_public_coins(coin: nil)
45
+ response = get(
46
+ path: '/spot/public/coins',
47
+ args: {
48
+ coin: coin
49
+ }
50
+ )
51
+ handle_response(response)
52
+ end
53
+
54
+ private
55
+
56
+ def initialize(api_key:, api_secret:, api_passphrase:)
57
+ @api_key = api_key.encode('UTF-8')
58
+ @api_secret = api_secret.encode('UTF-8')
59
+ @api_passphrase = api_passphrase.encode('UTF-8')
60
+ end
61
+
62
+ def full_path(path)
63
+ self.class.path_prefix + path
64
+ end
65
+
66
+ def encoded_payload(args)
67
+ args.reject!{|k,v| v.nil?}
68
+ OpenSSL::Digest::SHA512.hexdigest(JSON.dump(args))
69
+ end
70
+
71
+ def timestamp
72
+ @timestamp ||= Time.now.to_i.to_s
73
+ end
74
+
75
+ def message(verb:, path:, args:)
76
+ query_string = (
77
+ case verb
78
+ when 'GET'
79
+ args.to_parameter_string
80
+ when 'POST'
81
+ nil
82
+ else
83
+ raise "The verb, #{verb}, is not acceptable."
84
+ end
85
+ )
86
+ body = JSON.dump(args)
87
+ if query_string.nil?
88
+ [timestamp, verb, full_path(path), body].join
89
+ else
90
+ [timestamp, verb, full_path(path), '?', query_string, body].join
91
+ end
92
+ end
93
+
94
+ def signature(message)
95
+ OpenSSL::HMAC.hexdigest('SHA512', @api_secret, message)
96
+ end
97
+
98
+ def request_string(path)
99
+ "https://#{API_HOST}#{self.class.path_prefix}#{path}"
100
+ end
101
+
102
+ def headers(signature)
103
+ {
104
+ 'ACCESS-KEY' => @api_key,
105
+ 'ACCESS-SIGN' => signature,
106
+ 'ACCESS-TIMESTAMP' => timestamp,
107
+ 'ACCESS-PASSPHRASE' => @api_passphrase,
108
+ 'Content-type' => 'application/json',
109
+ 'locale' =>'en-AU',
110
+ 'Accept' => 'application/json',
111
+ }
112
+ end
113
+
114
+ def log_args?(args)
115
+ !args.values.all?(&:nil?)
116
+ end
117
+
118
+ def log_request(verb:, request_string:, args:)
119
+ log_string = "#{verb} #{request_string}"
120
+ if log_args?(args)
121
+ log_string << "?#{args.x_www_form_urlencode}"
122
+ end
123
+ self.class.logger.info(log_string)
124
+ end
125
+
126
+ def log_error(code:, message:, body:)
127
+ log_string = "#{code}\n#{message}\n#{body}"
128
+ self.class.logger.error(log_string)
129
+ end
130
+
131
+ def do_request(verb:, path:, args: {})
132
+ log_request(verb: verb, request_string: request_string(path), args: args)
133
+ message = message(verb: verb, path: path, args: args)
134
+ signature = signature(message)
135
+ HTTP.send(verb.to_s.downcase, request_string(path), args, headers(signature))
136
+ end
137
+
138
+ def get(path:, args: {})
139
+ do_request(verb: 'GET', path: path, args: args)
140
+ end
141
+
142
+ def post(path:, args: {})
143
+ do_request(verb: 'POST', path: path, args: args)
144
+ end
145
+
146
+ def handle_response(response)
147
+ if response.success?
148
+ JSON.parse(response.body)
149
+ else
150
+ log_error(
151
+ code: response.code,
152
+ message: response.message,
153
+ body: response.body
154
+ )
155
+ raise Bitget::Error.new(
156
+ code: response.code,
157
+ message: response.message,
158
+ body: response.body
159
+ )
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,26 @@
1
+ # Hash#to_parameter_string
2
+
3
+ # 20120610
4
+ # 1.0.2
5
+
6
+ # Changes since 0:
7
+ # 1. I changed it from a class method to an instance method and hence removed references to any arguments and substituted self for those arguments.
8
+ # 0/1
9
+ # 2. I forget about needing String#url_encode, which for now I'll paste in here.
10
+ # 1/2
11
+ # 3. - String#url_encode.
12
+ # 4. + require 'String/url_encode'.
13
+
14
+ require 'String/url_encode'
15
+
16
+ class Hash
17
+
18
+ def to_parameter_string
19
+ parameters_string_parts = []
20
+ self.each do |k,v|
21
+ parameters_string_parts << (k.to_s + '=' + v.url_encode) unless v.nil?
22
+ end
23
+ parameters_string_parts.join('&')
24
+ end
25
+
26
+ end
@@ -0,0 +1,7 @@
1
+ # Hash/x_www_form_urlencode.rb
2
+ # Hash#x_www_form_urlencode
3
+
4
+ # 20241009
5
+ # 0.1.0
6
+
7
+ require 'Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode'
@@ -0,0 +1,24 @@
1
+ # String/url_encode.rb
2
+ # String#url_encode
3
+
4
+ # 20130310
5
+ # 0.2.1
6
+
7
+ # Description: When I went looking for a means to url encode and decode strings, I wanted something which used extensions to the String class. Of course there are other means to do url encoding and decoding including using the CGI class, but I liked the approach of having this 'built-in' to strings, rather than the string being passed as a parameter as does CGI. Hence this String extender...
8
+
9
+ # Acknowledgements: I've simply ripped off and refashioned the code from the CGI module!...
10
+
11
+ # Changes since 0.1:
12
+ # 1. The url_encode and url_decode methods now are strictly demarcated.
13
+ # 0/1
14
+ # 2. A small reformat.
15
+
16
+ class String
17
+
18
+ def url_encode
19
+ self.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
20
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
21
+ end.tr(' ', '+')
22
+ end
23
+
24
+ end
data/lib/bitget.rb ADDED
@@ -0,0 +1,10 @@
1
+ # bitget.rb
2
+ # Bitget
3
+
4
+ # 20250326
5
+ # 0.1.1
6
+
7
+ lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
9
+
10
+ require_relative './Bitget/Client'
@@ -0,0 +1,53 @@
1
+ require_relative '../helper'
2
+ require_relative '../../lib/Bitget/V2/Client'
3
+
4
+ describe Bitget::V2::Client do
5
+ let(:client) do
6
+ Bitget::V2::Client.new(
7
+ api_key: api_key,
8
+ api_secret: api_secret,
9
+ api_passphrase: api_passphrase
10
+ )
11
+ end
12
+
13
+ describe "#spot_public_coins" do
14
+ context "when a coin is NOT supplied" do
15
+ it "retrieves a list of all coins" do
16
+ VCR.use_cassette('v2/spot/public/coins-when_coin_is_not_supplied') do
17
+ response = client.spot_public_coins
18
+ _(response).must_include('data')
19
+ _(response['data'].first).must_include('coinId')
20
+ _(response['data'].first).must_include('coin')
21
+ assert_operator response['data'].count, :>, 1500
22
+ end
23
+ end
24
+ end
25
+
26
+ context "when a coin IS supplied" do
27
+ it "retrieves one coin" do
28
+ VCR.use_cassette('v2/spot/public/coins-when_coin_is_supplied') do
29
+ response = client.spot_public_coins(coin: 'BTC')
30
+ _(response).must_include('data')
31
+ _(response['data'].first).must_include('coinId')
32
+ _(response['data'].first).must_include('coin')
33
+ _(response['data'].count).must_equal(1)
34
+ end
35
+ end
36
+ end
37
+
38
+ context "when an error occurs" do
39
+ it "logs then raises an error" do
40
+ VCR.use_cassette('v2/spot/public/coins-when_an_error_occurs') do
41
+ assert_raises(Bitget::Error) do
42
+ mocked_method = Minitest::Mock.new
43
+ mocked_method.expect(:call, nil, [], code: '418', message: "I'm a teapot", body: '')
44
+ client.stub(:log_error, mocked_method) do
45
+ client.spot_public_coins
46
+ end
47
+ mocked_method.verify
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ require_relative './helper'
2
+ require_relative '../lib/Bitget/Client'
3
+
4
+ describe Bitget::Client do
5
+ let(:client) do
6
+ Bitget::Client.new(
7
+ api_key: api_key,
8
+ api_secret: api_secret,
9
+ api_passphrase: api_passphrase
10
+ )
11
+ end
12
+
13
+ describe "#spot_public_coins" do
14
+ context "when a coin is NOT supplied" do
15
+ it "retrieves a list of all coins" do
16
+ VCR.use_cassette('v2/spot/public/coins-when_coin_is_not_supplied') do
17
+ response = client.spot_public_coins
18
+ _(response).must_include('data')
19
+ _(response['data'].first).must_include('coinId')
20
+ _(response['data'].first).must_include('coin')
21
+ assert_operator response['data'].count, :>, 1500
22
+ end
23
+ end
24
+ end
25
+
26
+ context "when a coin IS supplied" do
27
+ it "retrieves one coin" do
28
+ VCR.use_cassette('v2/spot/public/coins-when_coin_is_supplied') do
29
+ response = client.spot_public_coins(coin: 'BTC')
30
+ _(response).must_include('data')
31
+ _(response['data'].first).must_include('coinId')
32
+ _(response['data'].first).must_include('coin')
33
+ _(response['data'].count).must_equal(1)
34
+ end
35
+ end
36
+ end
37
+
38
+ context "when an error occurs" do
39
+ it "logs then raises an error" do
40
+ VCR.use_cassette('v2/spot/public/coins-when_an_error_occurs') do
41
+ assert_raises(Bitget::Error) do
42
+ mocked_method = Minitest::Mock.new
43
+ mocked_method.expect(:call, nil, [], code: '418', message: "I'm a teapot", body: '')
44
+ client.stub(:log_error, mocked_method) do
45
+ client.spot_public_coins
46
+ end
47
+ mocked_method.verify
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest-spec-context'
3
+ require 'minitest/spec'
4
+ require 'ostruct'
5
+ require 'vcr'
6
+
7
+ VCR.configure do |config|
8
+ config.cassette_library_dir = File.expand_path('../fixtures/vcr_cassettes', __FILE__)
9
+ config.hook_into :webmock
10
+ end
11
+
12
+ def api_key
13
+ if ENV['BITGET_API_KEY']
14
+ ENV['BITGET_API_KEY']
15
+ else
16
+ 'api_key0'
17
+ end
18
+ end
19
+
20
+ def api_secret
21
+ if ENV['BITGET_API_SECRET']
22
+ ENV['BITGET_API_SECRET']
23
+ else
24
+ 'api_secret0'
25
+ end
26
+ end
27
+
28
+ def api_passphrase
29
+ if ENV['BITGET_API_PASSPHRASE']
30
+ ENV['BITGET_API_PASSPHRASE']
31
+ else
32
+ 'api_passphrase0'
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitget.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - thoran
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-03-29 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: http.rb
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: Access the Bitget API with Ruby.
27
+ email: code@thoran.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - Gemfile
33
+ - README.md
34
+ - bitget.rb.gemspec
35
+ - lib/Bitget/Client.rb
36
+ - lib/Bitget/Error.rb
37
+ - lib/Bitget/V2/Client.rb
38
+ - lib/Hash/to_parameter_string.rb
39
+ - lib/Hash/x_www_form_urlencode.rb
40
+ - lib/String/url_encode.rb
41
+ - lib/bitget.rb
42
+ - test/V2/client_test.rb
43
+ - test/client_test.rb
44
+ - test/helper.rb
45
+ homepage: http://github.com/thoran/bitget.rb
46
+ licenses:
47
+ - Ruby
48
+ metadata: {}
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '2.7'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 3.6.6
64
+ specification_version: 4
65
+ summary: Access the Bitget API with Ruby.
66
+ test_files: []