bybit-connector-ruby 0.1.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 +7 -0
- data/CHANGELOG.md +259 -0
- data/LICENSE +21 -0
- data/README.md +173 -0
- data/examples/quickstart.rb +35 -0
- data/lib/bybit/authentication.rb +16 -0
- data/lib/bybit/client.rb +71 -0
- data/lib/bybit/configuration.rb +74 -0
- data/lib/bybit/error.rb +67 -0
- data/lib/bybit/rest_api/account_service.rb +332 -0
- data/lib/bybit/rest_api/affiliate_service.rb +42 -0
- data/lib/bybit/rest_api/asset_service.rb +638 -0
- data/lib/bybit/rest_api/base_service.rb +11 -0
- data/lib/bybit/rest_api/bot_service.rb +386 -0
- data/lib/bybit/rest_api/broker_service.rb +109 -0
- data/lib/bybit/rest_api/crypto_loan_service.rb +375 -0
- data/lib/bybit/rest_api/earn_service.rb +1047 -0
- data/lib/bybit/rest_api/market_service.rb +359 -0
- data/lib/bybit/rest_api/p2p_service.rb +256 -0
- data/lib/bybit/rest_api/position_service.rb +202 -0
- data/lib/bybit/rest_api/rfq_service.rb +221 -0
- data/lib/bybit/rest_api/spot_margin_service.rb +62 -0
- data/lib/bybit/rest_api/trade_service.rb +289 -0
- data/lib/bybit/rest_api/user_service.rb +254 -0
- data/lib/bybit/session.rb +199 -0
- data/lib/bybit/utils/wire_keys.rb +66 -0
- data/lib/bybit/version.rb +5 -0
- data/lib/bybit.rb +10 -0
- metadata +158 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'timeout'
|
|
6
|
+
require 'uri'
|
|
7
|
+
|
|
8
|
+
module Bybit
|
|
9
|
+
# Session owns the Faraday connection + auth-header assembly. Every service
|
|
10
|
+
# class receives a Session instance and dispatches through public_request /
|
|
11
|
+
# sign_request. Modeled after binance-connector-ruby's Session pattern.
|
|
12
|
+
#
|
|
13
|
+
# Signing invariant: the payload signed and the query string sent on the
|
|
14
|
+
# wire MUST be byte-identical. We enforce this by building ONE query_str
|
|
15
|
+
# (via encode_query after key-sort) and using it verbatim for both
|
|
16
|
+
# the signature payload AND the request URL — never letting Faraday's
|
|
17
|
+
# NestedParamsEncoder re-serialize the params. This avoids the classic
|
|
18
|
+
# array-value / nested-hash reorder bug.
|
|
19
|
+
class Session
|
|
20
|
+
PAYLOAD_QUERY_METHODS = %i[get delete].freeze
|
|
21
|
+
|
|
22
|
+
def initialize(config)
|
|
23
|
+
@config = config
|
|
24
|
+
@conn = config.faraday_connection || build_connection
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Public unsigned endpoint — no X-BAPI-* headers attached.
|
|
28
|
+
# session.public_request(path: '/v5/market/kline', params: {...})
|
|
29
|
+
def public_request(path:, method: :get, params: nil, body: nil)
|
|
30
|
+
dispatch(method: method, path: path, signed: false, params: params, body: body)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Signed endpoint — X-BAPI-* headers computed via Authentication.
|
|
34
|
+
def sign_request(method:, path:, params: nil, body: nil)
|
|
35
|
+
dispatch(method: method, path: path, signed: true, params: params, body: body)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def dispatch(method:, path:, signed:, params:, body:)
|
|
41
|
+
# Only GET / DELETE carry params on the query string. Silently dropping
|
|
42
|
+
# params on POST / PUT / PATCH used to leave codegen typos undiagnosed
|
|
43
|
+
# for weeks — now they raise loudly.
|
|
44
|
+
if params && !PAYLOAD_QUERY_METHODS.include?(method) && !body.nil?
|
|
45
|
+
raise Bybit::ConfigurationError,
|
|
46
|
+
"params: is only valid on GET/DELETE — #{method.to_s.upcase} must pass data via body:"
|
|
47
|
+
end
|
|
48
|
+
clean_params = compact(params)
|
|
49
|
+
clean_body = compact(body)
|
|
50
|
+
query_str = clean_params ? encode_query(clean_params) : ''
|
|
51
|
+
body_str = clean_body ? JSON.generate(clean_body) : ''
|
|
52
|
+
headers = build_headers(signed: signed, method: method, query_str: query_str, body_str: body_str)
|
|
53
|
+
|
|
54
|
+
# Build URL manually with our own query_str so the wire bytes match
|
|
55
|
+
# what we signed. `req.params =` would re-serialize through
|
|
56
|
+
# Faraday::NestedParamsEncoder which key-orders and array-brackets
|
|
57
|
+
# differently, breaking the signature.
|
|
58
|
+
full_url = if !query_str.empty? && PAYLOAD_QUERY_METHODS.include?(method)
|
|
59
|
+
"#{path}?#{query_str}"
|
|
60
|
+
else
|
|
61
|
+
path
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
resp = @conn.send(method) do |req|
|
|
65
|
+
req.url full_url
|
|
66
|
+
req.headers.update(headers)
|
|
67
|
+
req.body = body_str if clean_body
|
|
68
|
+
end
|
|
69
|
+
parse_response(resp)
|
|
70
|
+
rescue Faraday::TimeoutError => e
|
|
71
|
+
raise Bybit::TimeoutError, e.message
|
|
72
|
+
rescue Faraday::ConnectionFailed => e
|
|
73
|
+
raise Bybit::TimeoutError, e.message if connect_timeout?(e)
|
|
74
|
+
|
|
75
|
+
raise Bybit::NetworkError, e.message
|
|
76
|
+
rescue Faraday::SSLError => e
|
|
77
|
+
raise Bybit::NetworkError, e.message
|
|
78
|
+
rescue Faraday::Error => e
|
|
79
|
+
# Catch-all for Faraday::ParsingError / ClientError / ServerError etc.
|
|
80
|
+
# that surface when the caller wires their own error-raising middleware.
|
|
81
|
+
raise Bybit::TransportError, e.message
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Faraday 2's net_http adapter wraps Net::OpenTimeout / Timeout::Error /
|
|
85
|
+
# Errno::ETIMEDOUT in ConnectionFailed (only Net::ReadTimeout becomes
|
|
86
|
+
# Faraday::TimeoutError). Callers still want these surfaced as TimeoutError.
|
|
87
|
+
def connect_timeout?(err)
|
|
88
|
+
cause = err.respond_to?(:wrapped_exception) ? err.wrapped_exception : nil
|
|
89
|
+
cause.is_a?(Timeout::Error) || cause.is_a?(Errno::ETIMEDOUT)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Deterministic `&`-joined encoding, keys sorted, values URI-escaped.
|
|
93
|
+
# Arrays become repeated keys (`symbol=BTCUSDT&symbol=ETHUSDT`) — this
|
|
94
|
+
# matches Bybit V5's flat-list expectation.
|
|
95
|
+
def encode_query(params)
|
|
96
|
+
params.sort_by { |k, _| k.to_s }.flat_map do |k, v|
|
|
97
|
+
key_enc = URI.encode_www_form_component(k.to_s)
|
|
98
|
+
Array(v).map { |single| "#{key_enc}=#{URI.encode_www_form_component(single.to_s)}" }
|
|
99
|
+
end.join('&')
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def build_headers(signed:, method:, query_str:, body_str:)
|
|
103
|
+
h = {}
|
|
104
|
+
h['Content-Type'] = 'application/json' unless body_str.empty?
|
|
105
|
+
return h unless signed
|
|
106
|
+
raise Bybit::ConfigurationError, 'signed endpoint requires api_key + api_secret' if @config.api_key.nil? || @config.api_secret.nil?
|
|
107
|
+
|
|
108
|
+
ts = (Time.now.to_f * 1000).to_i.to_s
|
|
109
|
+
# payload for GET/DELETE is query string; for POST/PUT/PATCH it's the
|
|
110
|
+
# JSON body. Both branches use the SAME predicate as dispatch above.
|
|
111
|
+
payload = PAYLOAD_QUERY_METHODS.include?(method) ? query_str : body_str
|
|
112
|
+
h['X-BAPI-API-KEY'] = @config.api_key
|
|
113
|
+
h['X-BAPI-TIMESTAMP'] = ts
|
|
114
|
+
h['X-BAPI-RECV-WINDOW'] = @config.recv_window.to_s
|
|
115
|
+
h['X-BAPI-SIGN'] = Authentication.sign_v5(
|
|
116
|
+
@config.api_secret, ts, @config.api_key, @config.recv_window.to_s, payload
|
|
117
|
+
)
|
|
118
|
+
h['X-BAPI-SIGN-TYPE'] = '2'
|
|
119
|
+
h
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def parse_response(response)
|
|
123
|
+
status = response.status
|
|
124
|
+
raw = response.body
|
|
125
|
+
body = raw.is_a?(String) ? safe_parse_json(raw) : raw
|
|
126
|
+
body = normalize_legacy_body(body) if body.is_a?(Hash)
|
|
127
|
+
|
|
128
|
+
# HTTP status wins over retCode when the body isn't a valid ApiResponse.
|
|
129
|
+
# 5xx / non-auth 4xx get their own class so retries and pager logic can
|
|
130
|
+
# tell them apart from client / auth errors.
|
|
131
|
+
if !body.is_a?(Hash) || !body['retCode'].is_a?(Integer)
|
|
132
|
+
preview = truncate_for_error(raw)
|
|
133
|
+
if status >= 500
|
|
134
|
+
raise Bybit::ServerError, "Bybit server error (status=#{status}): #{preview}"
|
|
135
|
+
elsif status >= 400
|
|
136
|
+
raise Bybit::ClientError, "Bybit client error (status=#{status}): #{preview}"
|
|
137
|
+
else
|
|
138
|
+
raise Bybit::ParseError.new(
|
|
139
|
+
"Response is not a valid Bybit V5 ApiResponse (status=#{status}): #{preview}",
|
|
140
|
+
body: raw, http_status: status
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
return body if body['retCode'].zero?
|
|
145
|
+
|
|
146
|
+
raise Bybit.api_error_from(body, http_status: status)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# P2P endpoints (and a few legacy V3-derived paths) return the pre-V5
|
|
150
|
+
# envelope: { ret_code, ret_msg, ext_code, ext_info, time_now, result }.
|
|
151
|
+
# Alias the legacy keys into the V5 shape so the rest of the parser and
|
|
152
|
+
# error mapper works uniformly. If the response is already V5, no-op.
|
|
153
|
+
LEGACY_KEY_MAP = {
|
|
154
|
+
'ret_code' => 'retCode',
|
|
155
|
+
'ret_msg' => 'retMsg',
|
|
156
|
+
'ext_info' => 'retExtInfo',
|
|
157
|
+
'time_now' => 'time'
|
|
158
|
+
}.freeze
|
|
159
|
+
|
|
160
|
+
def normalize_legacy_body(body)
|
|
161
|
+
return body if body.key?('retCode')
|
|
162
|
+
return body unless body.key?('ret_code')
|
|
163
|
+
|
|
164
|
+
LEGACY_KEY_MAP.each do |legacy, v5|
|
|
165
|
+
body[v5] = body.delete(legacy) if body.key?(legacy)
|
|
166
|
+
end
|
|
167
|
+
# `time_now` is a Bybit-legacy float-string; V5 exposes `time` as an
|
|
168
|
+
# integer millisecond epoch. Best-effort coerce so downstream code that
|
|
169
|
+
# compares against V5 `time` doesn't hit a type mismatch.
|
|
170
|
+
body['time'] = (body['time'].to_f * 1000).to_i if body['time'].is_a?(String)
|
|
171
|
+
body
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def safe_parse_json(str)
|
|
175
|
+
JSON.parse(str)
|
|
176
|
+
rescue JSON::ParserError
|
|
177
|
+
nil
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def truncate_for_error(raw)
|
|
181
|
+
return '(nil body)' if raw.nil?
|
|
182
|
+
|
|
183
|
+
s = raw.is_a?(String) ? raw : raw.inspect
|
|
184
|
+
s.length > 2048 ? "#{s[0, 2048]}…(truncated)" : s
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Shallow compact only — inner Hash / Array-of-Hash values pass through.
|
|
188
|
+
# Recursion happens on the wire-key side via WireKeys.camelize.
|
|
189
|
+
def compact(hash)
|
|
190
|
+
return nil if hash.nil?
|
|
191
|
+
|
|
192
|
+
hash.reject { |_, v| v.nil? }
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def build_connection
|
|
196
|
+
Faraday.new(url: @config.resolved_base_url, request: { timeout: @config.timeout })
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bybit
|
|
4
|
+
module Utils
|
|
5
|
+
# snake_case <-> camelCase key translation for wire payloads.
|
|
6
|
+
# Bot-family endpoints keep snake_case (identity mapping) — call sites
|
|
7
|
+
# skip WireKeys.camelize. Everyone else runs camelize on the params/body
|
|
8
|
+
# hash right before dispatch.
|
|
9
|
+
#
|
|
10
|
+
# Recursion: batch endpoints (trade#batch_amend_orders,
|
|
11
|
+
# account#batch_set_collateral, etc.) carry Array<Hash> or nested Hash
|
|
12
|
+
# payloads whose inner keys must ALSO be camelized. A non-recursive
|
|
13
|
+
# camelize was the PR#1 blocker (retCode 10001/10004 on every batch call).
|
|
14
|
+
module WireKeys
|
|
15
|
+
# Ruby-reserved-word arg aliases: :end_ → :end on the wire. Codegen
|
|
16
|
+
# emits arg names with the trailing '_'; camelize normalizes them back
|
|
17
|
+
# to the spec name so service methods no longer need per-method
|
|
18
|
+
# `params[:end] = params.delete(:end_)` shims.
|
|
19
|
+
RESERVED_ALIASES = {
|
|
20
|
+
end_: :end, begin_: :begin, class_: :class, next_: :next,
|
|
21
|
+
return_: :return, do_: :do, if_: :if, else_: :else
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
# Convert every key of a hash from :some_key / 'some_key' to camelCase.
|
|
27
|
+
# Recurses into Hash and Array-of-Hash values. Non-hash/array values
|
|
28
|
+
# pass through unchanged. Reserved-word aliases (:end_ etc.) get
|
|
29
|
+
# rewritten to their bare form.
|
|
30
|
+
def camelize(hash)
|
|
31
|
+
return hash if hash.nil?
|
|
32
|
+
|
|
33
|
+
hash.each_with_object({}) do |(k, v), out|
|
|
34
|
+
out[to_camel(unalias(k))] = camelize_value(v)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def camelize_value(value)
|
|
39
|
+
case value
|
|
40
|
+
when Hash then camelize(value)
|
|
41
|
+
when Array then value.map { |el| el.is_a?(Hash) ? camelize(el) : el }
|
|
42
|
+
else value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def unalias(key)
|
|
47
|
+
return key unless key.is_a?(Symbol) || key.is_a?(String)
|
|
48
|
+
|
|
49
|
+
aliased = RESERVED_ALIASES[key.to_s.to_sym]
|
|
50
|
+
return key unless aliased
|
|
51
|
+
|
|
52
|
+
key.is_a?(Symbol) ? aliased : aliased.to_s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def to_camel(key)
|
|
56
|
+
return key unless key.is_a?(Symbol) || key.is_a?(String)
|
|
57
|
+
|
|
58
|
+
parts = key.to_s.split('_')
|
|
59
|
+
return key if parts.size < 2
|
|
60
|
+
|
|
61
|
+
camel = parts[0] + parts[1..].reject(&:empty?).map(&:capitalize).join
|
|
62
|
+
key.is_a?(Symbol) ? camel.to_sym : camel
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/bybit.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bybit/version'
|
|
4
|
+
require 'bybit/error'
|
|
5
|
+
require 'bybit/configuration'
|
|
6
|
+
require 'bybit/authentication'
|
|
7
|
+
require 'bybit/session'
|
|
8
|
+
require 'bybit/utils/wire_keys'
|
|
9
|
+
require 'bybit/rest_api/base_service'
|
|
10
|
+
require 'bybit/client'
|
metadata
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bybit-connector-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Bybit
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-07-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: faraday
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.12'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.12'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rubocop
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.60'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.60'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: simplecov
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0.22'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0.22'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: webmock
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: yard
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0.9'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0.9'
|
|
97
|
+
description: Ruby connector for the Bybit V5 REST API — keyword-arg method signatures,
|
|
98
|
+
HMAC-SHA256 signing, typed error hierarchy, Faraday-based transport.
|
|
99
|
+
email:
|
|
100
|
+
executables: []
|
|
101
|
+
extensions: []
|
|
102
|
+
extra_rdoc_files: []
|
|
103
|
+
files:
|
|
104
|
+
- CHANGELOG.md
|
|
105
|
+
- LICENSE
|
|
106
|
+
- README.md
|
|
107
|
+
- examples/quickstart.rb
|
|
108
|
+
- lib/bybit.rb
|
|
109
|
+
- lib/bybit/authentication.rb
|
|
110
|
+
- lib/bybit/client.rb
|
|
111
|
+
- lib/bybit/configuration.rb
|
|
112
|
+
- lib/bybit/error.rb
|
|
113
|
+
- lib/bybit/rest_api/account_service.rb
|
|
114
|
+
- lib/bybit/rest_api/affiliate_service.rb
|
|
115
|
+
- lib/bybit/rest_api/asset_service.rb
|
|
116
|
+
- lib/bybit/rest_api/base_service.rb
|
|
117
|
+
- lib/bybit/rest_api/bot_service.rb
|
|
118
|
+
- lib/bybit/rest_api/broker_service.rb
|
|
119
|
+
- lib/bybit/rest_api/crypto_loan_service.rb
|
|
120
|
+
- lib/bybit/rest_api/earn_service.rb
|
|
121
|
+
- lib/bybit/rest_api/market_service.rb
|
|
122
|
+
- lib/bybit/rest_api/p2p_service.rb
|
|
123
|
+
- lib/bybit/rest_api/position_service.rb
|
|
124
|
+
- lib/bybit/rest_api/rfq_service.rb
|
|
125
|
+
- lib/bybit/rest_api/spot_margin_service.rb
|
|
126
|
+
- lib/bybit/rest_api/trade_service.rb
|
|
127
|
+
- lib/bybit/rest_api/user_service.rb
|
|
128
|
+
- lib/bybit/session.rb
|
|
129
|
+
- lib/bybit/utils/wire_keys.rb
|
|
130
|
+
- lib/bybit/version.rb
|
|
131
|
+
homepage: https://github.com/bybit-exchange/bybit.ruby.api
|
|
132
|
+
licenses:
|
|
133
|
+
- MIT
|
|
134
|
+
metadata:
|
|
135
|
+
source_code_uri: https://github.com/bybit-exchange/bybit.ruby.api
|
|
136
|
+
documentation_uri: https://bybit-exchange.github.io/docs/v5/intro
|
|
137
|
+
changelog_uri: https://github.com/bybit-exchange/bybit.ruby.api/blob/main/CHANGELOG.md
|
|
138
|
+
rubygems_mfa_required: 'true'
|
|
139
|
+
post_install_message:
|
|
140
|
+
rdoc_options: []
|
|
141
|
+
require_paths:
|
|
142
|
+
- lib
|
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
|
+
requirements:
|
|
145
|
+
- - ">="
|
|
146
|
+
- !ruby/object:Gem::Version
|
|
147
|
+
version: '3.0'
|
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
153
|
+
requirements: []
|
|
154
|
+
rubygems_version: 3.0.3.1
|
|
155
|
+
signing_key:
|
|
156
|
+
specification_version: 4
|
|
157
|
+
summary: Official Bybit V5 REST API connector for Ruby
|
|
158
|
+
test_files: []
|