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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2e337e2d3927cdb53b707d0bfd441323cb293a43e2c3ded843d273c47f439af5
4
+ data.tar.gz: c4dfa85d62da8da24fc1cccdc5101631404182a3910de24f65f899ed972771dd
5
+ SHA512:
6
+ metadata.gz: 9bad03bebb0cc8d633b4f014d42da79b592ade8d8f90b77a4a9ed239a0d46ae094aa8ba236a01616abb5b0b7596a85627cccad53ebce950334c544eef3ff0570
7
+ data.tar.gz: 310c666e1c0b387b84d91ab4fff0f78f401224eb09f74724bd64e021c4bf5fc1ff4f3458e383ed7a040bba69528602bedbb48f2da8b3d54cf1af30a142fa3f42
data/CHANGELOG.md ADDED
@@ -0,0 +1,259 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 0.1.0 — 2026-07-20
9
+
10
+ ### Added
11
+ - `AccountService#batch_set_collateral(...)`
12
+ - `AccountService#get_info(...)`
13
+ - `AccountService#get_wallet_balance(...)`
14
+ - `AccountService#get_instruments(...)`
15
+ - `AccountService#get_borrow_history(...)`
16
+ - `AccountService#get_collateral_info(...)`
17
+ - `AccountService#get_dcp_info(...)`
18
+ - `AccountService#get_fee_rate(...)`
19
+ - `AccountService#get_mmp_state(...)`
20
+ - `AccountService#get_smp_group(...)`
21
+ - `AccountService#get_transaction_log(...)`
22
+ - `AccountService#get_transferable_amount(...)`
23
+ - `AccountService#get_user_settings(...)`
24
+ - `AccountService#manual_borrow(...)`
25
+ - `AccountService#manual_repay(...)`
26
+ - `AccountService#no_convert_repay(...)`
27
+ - `AccountService#one_click_repay(...)`
28
+ - `AccountService#reset_mmp(...)`
29
+ - `AccountService#set_collateral_coin(...)`
30
+ - `AccountService#set_margin_mode(...)`
31
+ - `AccountService#set_mmp(...)`
32
+ - `AccountService#set_price_limit(...)`
33
+ - `AccountService#set_spot_hedging(...)`
34
+ - `AccountService#upgrade_to_uta_pro(...)`
35
+ - `AffiliateService#get_sub_list(...)`
36
+ - `AffiliateService#get_user_list(...)`
37
+ - `AssetService#get_coin_balance(...)`
38
+ - `AssetService#get_coin_greeks(...)`
39
+ - `AssetService#list_funding_history(...)`
40
+ - `BrokerService#distribute_award(...)`
41
+ - `BrokerService#get_award_info(...)`
42
+ - `BrokerService#get_distribution_record(...)`
43
+ - `BrokerService#get_broker_account_info(...)`
44
+ - `BrokerService#list_broker_sub_uids(...)`
45
+ - `BrokerService#get_broker_rate_limit_cap(...)`
46
+ - `BrokerService#list_broker_earnings(...)`
47
+ - `BrokerService#set_api_limit(...)`
48
+ - `CryptoLoanService#adjust_ltv(...)`
49
+ - `CryptoLoanService#get_adjustment_history(...)`
50
+ - `CryptoLoanService#get_collateral_data(...)`
51
+ - `CryptoLoanService#get_loanable_data(...)`
52
+ - `CryptoLoanService#get_max_collateral_amount(...)`
53
+ - `CryptoLoanService#get_max_loan(...)`
54
+ - `CryptoLoanService#get_position(...)`
55
+ - `CryptoLoanService#get_fixed_borrow_contract_info(...)`
56
+ - `CryptoLoanService#get_fixed_borrow_order_info(...)`
57
+ - `CryptoLoanService#get_fixed_borrow_order_quote(...)`
58
+ - `CryptoLoanService#get_fixed_renew_info(...)`
59
+ - `CryptoLoanService#get_fixed_supply_contract_info(...)`
60
+ - `CryptoLoanService#get_fixed_supply_order_info(...)`
61
+ - `CryptoLoanService#get_fixed_supply_order_quote(...)`
62
+ - `CryptoLoanService#borrow_fixed(...)`
63
+ - `CryptoLoanService#cancel_fixed_borrow_order(...)`
64
+ - `CryptoLoanService#repay_fixed_fully(...)`
65
+ - `CryptoLoanService#renew_fixed(...)`
66
+ - `CryptoLoanService#repay_fixed_with_collateral(...)`
67
+ - `CryptoLoanService#cancel_fixed_supply_order(...)`
68
+ - `CryptoLoanService#get_flexible_borrow_history(...)`
69
+ - `CryptoLoanService#get_flexible_ongoing_coin(...)`
70
+ - `CryptoLoanService#get_flexible_repayment_history(...)`
71
+ - `CryptoLoanService#borrow_flexible(...)`
72
+ - `CryptoLoanService#repay_flexible(...)`
73
+ - `CryptoLoanService#repay_flexible_with_collateral(...)`
74
+ - `BotService#close_dca_bot(...)`
75
+ - `BotService#create_dca_bot(...)`
76
+ - `EarnService#add_liquidity(...)`
77
+ - `EarnService#add_margin(...)`
78
+ - `EarnService#claim_liquidity_interest(...)`
79
+ - `EarnService#get_advance_earn_order(...)`
80
+ - `EarnService#get_advance_earn_position(...)`
81
+ - `EarnService#get_advance_earn_product(...)`
82
+ - `EarnService#get_advance_earn_product_extra_info(...)`
83
+ - `EarnService#get_double_win_leverage(...)`
84
+ - `EarnService#get_apr_history(...)`
85
+ - `EarnService#get_hourly_yield_history(...)`
86
+ - `EarnService#get_order_history(...)`
87
+ - `EarnService#get_position(...)`
88
+ - `EarnService#get_product(...)`
89
+ - `EarnService#get_yield_history(...)`
90
+ - `EarnService#get_fixed_term_order(...)`
91
+ - `EarnService#get_fixed_term_position(...)`
92
+ - `EarnService#get_fixed_term_product(...)`
93
+ - `EarnService#get_hold_to_earn_product(...)`
94
+ - `EarnService#get_hold_to_earn_yield_history(...)`
95
+ - `EarnService#get_liquidity_mining_liquidation_records(...)`
96
+ - `EarnService#get_liquidity_mining_orders(...)`
97
+ - `EarnService#get_liquidity_mining_positions(...)`
98
+ - `EarnService#get_liquidity_mining_products(...)`
99
+ - `EarnService#get_liquidity_mining_yield_records(...)`
100
+ - `EarnService#get_rwa_nav_chart(...)`
101
+ - `EarnService#get_rwa_order_list(...)`
102
+ - `EarnService#get_rwa_position_list(...)`
103
+ - `EarnService#get_rwa_product_list(...)`
104
+ - `EarnService#get_smart_leverage_redeem_est_amount_list(...)`
105
+ - `EarnService#get_token_daily_yield(...)`
106
+ - `EarnService#get_token_historical_apr(...)`
107
+ - `EarnService#get_token_hourly_yield(...)`
108
+ - `EarnService#get_token_order_list(...)`
109
+ - `EarnService#get_token_position(...)`
110
+ - `EarnService#get_token_product(...)`
111
+ - `EarnService#list_coupons(...)`
112
+ - `EarnService#modify_earn_position(...)`
113
+ - `EarnService#place_advance_earn_order(...)`
114
+ - `EarnService#place_order(...)`
115
+ - `EarnService#place_fixed_term_order(...)`
116
+ - `EarnService#place_rwa_order(...)`
117
+ - `EarnService#place_token_order(...)`
118
+ - `EarnService#pwm_asset_trend(...)`
119
+ - `EarnService#pwm_claim(...)`
120
+ - `EarnService#pwm_create_custom_plan(...)`
121
+ - `EarnService#pwm_fund_nav(...)`
122
+ - `EarnService#pwm_fund_transfer(...)`
123
+ - `EarnService#pwm_get_new_plan_detail(...)`
124
+ - `EarnService#pwm_get_plan_detail(...)`
125
+ - `EarnService#pwm_inst_create_fund(...)`
126
+ - `EarnService#pwm_inst_create_investment_plan(...)`
127
+ - `EarnService#pwm_inst_create_sub_account(...)`
128
+ - `EarnService#pwm_inst_get_investment_plans(...)`
129
+ - `EarnService#pwm_inst_list_funds(...)`
130
+ - `EarnService#pwm_inst_list_orders(...)`
131
+ - `EarnService#pwm_inst_manage_investment_plan(...)`
132
+ - `EarnService#pwm_inst_manage_order(...)`
133
+ - `EarnService#pwm_inst_settle_profit(...)`
134
+ - `EarnService#pwm_invest_more(...)`
135
+ - `EarnService#pwm_list_investment_plans(...)`
136
+ - `EarnService#pwm_list_order(...)`
137
+ - `EarnService#pwm_list_product_cards(...)`
138
+ - `EarnService#pwm_query_fund_transfer_result(...)`
139
+ - `EarnService#pwm_redeem(...)`
140
+ - `EarnService#pwm_subscribe(...)`
141
+ - `EarnService#redeem_fixed_term(...)`
142
+ - `EarnService#reinvest_liquidity(...)`
143
+ - `EarnService#remove_liquidity(...)`
144
+ - `EarnService#set_fixed_term_auto_invest(...)`
145
+ - `TradeService#get_execution_history(...)`
146
+ - `BotService#close_combo_bot(...)`
147
+ - `BotService#create_combo_bot(...)`
148
+ - `BotService#get_combo_detail(...)`
149
+ - `BotService#get_combo_limit(...)`
150
+ - `BotService#close_futures_grid_bot(...)`
151
+ - `BotService#create_futures_grid_bot(...)`
152
+ - `BotService#get_futures_grid_detail(...)`
153
+ - `BotService#validate_futures_grid_input(...)`
154
+ - `BotService#close_futures_martingale_bot(...)`
155
+ - `BotService#create_futures_martingale_bot(...)`
156
+ - `BotService#get_futures_martingale_detail(...)`
157
+ - `BotService#get_futures_martingale_limit(...)`
158
+ - `BotService#close_grid_bot(...)`
159
+ - `BotService#create_grid_bot(...)`
160
+ - `BotService#get_grid_detail(...)`
161
+ - `BotService#validate_grid_input(...)`
162
+ - `MarketService#get_adl_alert(...)`
163
+ - `MarketService#get_delivery_price(...)`
164
+ - `MarketService#get_fee_group_info(...)`
165
+ - `MarketService#get_funding_rate_history(...)`
166
+ - `MarketService#get_historical_volatility(...)`
167
+ - `MarketService#get_index_price_components(...)`
168
+ - `MarketService#get_index_price_kline(...)`
169
+ - `MarketService#get_instruments_info(...)`
170
+ - `MarketService#get_insurance_pool(...)`
171
+ - `MarketService#get_long_short_ratio(...)`
172
+ - `MarketService#get_mark_price_kline(...)`
173
+ - `MarketService#get_kline(...)`
174
+ - `MarketService#get_new_delivery_price(...)`
175
+ - `MarketService#get_open_interest(...)`
176
+ - `MarketService#get_order_price_limit(...)`
177
+ - `MarketService#get_orderbook(...)`
178
+ - `MarketService#get_premium_index_price_kline(...)`
179
+ - `MarketService#get_recent_public_trades(...)`
180
+ - `MarketService#get_risk_limit(...)`
181
+ - `MarketService#get_rpi_orderbook(...)`
182
+ - `MarketService#get_server_time(...)`
183
+ - `MarketService#get_tickers(...)`
184
+ - `TradeService#amend_order(...)`
185
+ - `TradeService#batch_amend_orders(...)`
186
+ - `TradeService#batch_cancel_orders(...)`
187
+ - `TradeService#batch_create_orders(...)`
188
+ - `TradeService#cancel_all_orders(...)`
189
+ - `TradeService#cancel_order(...)`
190
+ - `TradeService#create_order(...)`
191
+ - `TradeService#set_dcp_time_window(...)`
192
+ - `TradeService#get_open_orders(...)`
193
+ - `TradeService#get_order_history(...)`
194
+ - `TradeService#get_spot_borrow_quota(...)`
195
+ - `TradeService#pre_check_order(...)`
196
+ - `P2pService#get_account_info(...)`
197
+ - `P2pService#get_ads(...)`
198
+ - `P2pService#get_all_orders(...)`
199
+ - `P2pService#get_chat_messages(...)`
200
+ - `P2pService#get_counterparty_user_info(...)`
201
+ - `P2pService#get_my_ad_details(...)`
202
+ - `P2pService#get_my_ads(...)`
203
+ - `P2pService#get_order_detail(...)`
204
+ - `P2pService#get_pending_orders(...)`
205
+ - `P2pService#get_user_payment(...)`
206
+ - `P2pService#mark_order_as_paid(...)`
207
+ - `P2pService#create_ad(...)`
208
+ - `P2pService#release_assets(...)`
209
+ - `P2pService#remove_ad(...)`
210
+ - `P2pService#send_chat_message(...)`
211
+ - `P2pService#update_ad(...)`
212
+ - `P2pService#upload_chat_file(...)`
213
+ - `PositionService#add_reduce_margin(...)`
214
+ - `PositionService#confirm_new_risk_limit(...)`
215
+ - `PositionService#get_closed_positions(...)`
216
+ - `PositionService#get_closed_pnl(...)`
217
+ - `PositionService#get_move_position_history(...)`
218
+ - `PositionService#get_positions(...)`
219
+ - `PositionService#move_position(...)`
220
+ - `PositionService#set_auto_add_margin(...)`
221
+ - `PositionService#set_leverage(...)`
222
+ - `PositionService#set_trading_stop(...)`
223
+ - `PositionService#switch_position_mode(...)`
224
+ - `RfqService#accept_non_lp_quote(...)`
225
+ - `RfqService#cancel_all_quotes(...)`
226
+ - `RfqService#cancel_all_rfqs(...)`
227
+ - `RfqService#cancel_quote(...)`
228
+ - `RfqService#cancel_rfq(...)`
229
+ - `RfqService#create_quote(...)`
230
+ - `RfqService#create_rfq(...)`
231
+ - `RfqService#execute_quote(...)`
232
+ - `RfqService#get_public_trades(...)`
233
+ - `RfqService#get_quotes(...)`
234
+ - `RfqService#get_quotes_realtime(...)`
235
+ - `RfqService#get_config(...)`
236
+ - `RfqService#get_rfqs(...)`
237
+ - `RfqService#get_rfqs_realtime(...)`
238
+ - `RfqService#get_trade_history(...)`
239
+ - `SpotMarginService#get_historical_interest_rate(...)`
240
+ - `SpotMarginService#get_position_tiers(...)`
241
+ - `SpotMarginService#get_tiered_collateral_ratio(...)`
242
+ - `SpotMarginService#get_vip_margin_data(...)`
243
+ - `UserService#create_sub_api_key(...)`
244
+ - `UserService#create_sub_member(...)`
245
+ - `UserService#delete_api_key(...)`
246
+ - `UserService#delete_sub_api_key(...)`
247
+ - `UserService#delete_sub_member(...)`
248
+ - `UserService#frozen_sub_member(...)`
249
+ - `UserService#get_affiliate_custom_open_info(...)`
250
+ - `UserService#get_member_account_type(...)`
251
+ - `UserService#list_sub_api_keys(...)`
252
+ - `UserService#get_api_key_info(...)`
253
+ - `UserService#list_escrow_sub_members(...)`
254
+ - `UserService#list_referrals(...)`
255
+ - `UserService#list_sub_member_uids(...)`
256
+ - `UserService#list_sub_members(...)`
257
+ - `UserService#sign_agreement(...)`
258
+ - `UserService#update_api_key(...)`
259
+ - `UserService#update_sub_api_key(...)`
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bybit-exchange
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # bybit-connector-ruby
2
+
3
+ Official lightweight Ruby connector for the [Bybit V5 REST API](https://bybit-exchange.github.io/docs/v5/intro).
4
+
5
+ `bybit-connector-ruby` wraps the Bybit V5 HTTP endpoints as a set of typed Ruby methods with keyword-arg signatures. Its goal is the same as [`pybit`](https://github.com/bybit-exchange/pybit) on the Python side: an easy-to-use, high-performance connector with a small dependency footprint.
6
+
7
+ ## Installation
8
+
9
+ Ruby ≥ 3.0 is required (Ruby 3.3.x recommended).
10
+
11
+ ```
12
+ gem install bybit-connector-ruby
13
+ ```
14
+
15
+ Or in your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'bybit-connector-ruby'
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```ruby
24
+ require 'bybit'
25
+
26
+ Bybit.configure do |c|
27
+ c.api_key = ENV['BYBIT_KEY']
28
+ c.api_secret = ENV['BYBIT_SECRET']
29
+ c.testnet = true # omit / false for mainnet
30
+ end
31
+
32
+ client = Bybit::Client.new
33
+
34
+ # Public endpoint — no auth needed
35
+ puts client.market.get_server_time
36
+
37
+ # Signed endpoint — apiKey + apiSecret required
38
+ wallet = client.account.get_wallet_balance(account_type: 'UNIFIED')
39
+ puts wallet['result']['list']
40
+
41
+ # Place an order
42
+ order = client.trade.create_order(
43
+ category: 'linear', symbol: 'BTCUSDT',
44
+ side: 'Buy', order_type: 'Limit', qty: '0.01',
45
+ price: '30000', time_in_force: 'GTC',
46
+ )
47
+ puts "orderId: #{order['result']['orderId']}"
48
+ ```
49
+
50
+ See `examples/quickstart.rb` for a runnable script.
51
+
52
+ ## Configuration
53
+
54
+ All options live on `Bybit::Configuration`. Either configure globally with the block:
55
+
56
+ ```ruby
57
+ Bybit.configure do |c|
58
+ c.api_key = ENV['BYBIT_KEY']
59
+ c.api_secret = ENV['BYBIT_SECRET']
60
+ c.testnet = false # default false
61
+ c.recv_window = '5000' # ms, X-BAPI-RECV-WINDOW header
62
+ c.timeout = 10 # Faraday timeout, seconds
63
+ end
64
+ ```
65
+
66
+ Or pass overrides to `Client.new`:
67
+
68
+ ```ruby
69
+ client = Bybit::Client.new(api_key: '...', api_secret: '...', testnet: true)
70
+ ```
71
+
72
+ Bring your own Faraday connection to inject retries / logging / adapters:
73
+
74
+ ```ruby
75
+ conn = Faraday.new('https://api-testnet.bybit.com') do |f|
76
+ f.request :retry, max: 3
77
+ f.response :logger
78
+ end
79
+ Bybit.configure { |c| c.faraday_connection = conn }
80
+ ```
81
+
82
+ Base URLs (exported constants):
83
+
84
+ - `Bybit::BASE_URL_MAINNET` — `https://api.bybit.com`
85
+ - `Bybit::BASE_URL_TESTNET` — `https://api-testnet.bybit.com`
86
+
87
+ ## Services
88
+
89
+ Each API group is a property on `Bybit::Client`:
90
+
91
+ - `client.market` — public market data (kline, tickers, orderbook, instruments-info, ...)
92
+ - `client.trade` — orders (create / amend / cancel / batch / history)
93
+ - `client.position` — positions, leverage, TP/SL, move-position
94
+ - `client.account` — wallet, margin, collateral, fee-rate, transaction log
95
+ - `client.asset` — coin balance, coin greeks, funding history
96
+ - `client.user` — sub-accounts, API-key management
97
+ - `client.affiliate` — sub-affiliate lists
98
+ - `client.broker` — broker earnings, distributions
99
+ - `client.crypto_loan` — flexible / fixed crypto loans
100
+ - `client.rfq` — request-for-quote (block trades)
101
+ - `client.spot_margin` — UTA spot margin
102
+ - `client.earn` — earn, liquidity mining, RWA, PWM, hold-to-earn
103
+ - `client.p2p` — P2P advertise / order / chat
104
+ - `client.bot` — DCA / grid / futures-combo / futures-grid / martingale
105
+
106
+ ## Error Handling
107
+
108
+ Every failure is a subclass of `Bybit::Error`:
109
+
110
+ ```ruby
111
+ begin
112
+ client.trade.create_order(category: 'linear', symbol: 'BTCUSDT', side: 'Buy', ...)
113
+ rescue Bybit::AuthError => e # retCode 10003/10004/10005/... or HTTP 401/403
114
+ # bad key / bad sign / permission
115
+ rescue Bybit::RateLimitError => e # retCode 10006 / 10018 or HTTP 429
116
+ sleep 1 && retry
117
+ rescue Bybit::TimeoutError => e # Faraday::TimeoutError
118
+ rescue Bybit::NetworkError => e # Faraday::ConnectionFailed / SSLError
119
+ rescue Bybit::ServerError => e # HTTP 5xx w/ non-JSON body
120
+ rescue Bybit::ClientError => e # HTTP 4xx w/ non-JSON body (WAF, CDN, etc.)
121
+ rescue Bybit::ParseError => e # unrecognized body shape; e.body holds raw payload
122
+ rescue Bybit::ApiError => e # any other retCode != 0 — catch-all API error
123
+ end
124
+ ```
125
+
126
+ Full hierarchy:
127
+
128
+ - `Bybit::Error` (StandardError)
129
+ - `Bybit::ConfigurationError` — missing api_key / conflicting options
130
+ - `Bybit::TransportError`
131
+ - `Bybit::TimeoutError`
132
+ - `Bybit::NetworkError`
133
+ - `Bybit::ServerError` (5xx w/o body)
134
+ - `Bybit::ClientError` (non-auth 4xx w/o body)
135
+ - `Bybit::ParseError` — body did not parse or shape mismatch (has `#body`, `#http_status`)
136
+ - `Bybit::ApiError` — Bybit V5 body with retCode != 0
137
+ - `Bybit::AuthError`
138
+ - `Bybit::RateLimitError`
139
+
140
+ Every `ApiError` exposes `#ret_code`, `#ret_msg`, `#result`, `#time`, `#http_status`. See the [Bybit V5 error-code list](https://bybit-exchange.github.io/docs/v5/error) for meanings.
141
+
142
+ ## Return Value
143
+
144
+ Every service method returns the raw parsed JSON as a `Hash`:
145
+
146
+ ```ruby
147
+ response = client.market.get_kline(category: 'spot', symbol: 'BTCUSDT', interval: '1')
148
+ response['retCode'] # => 0
149
+ response['retMsg'] # => 'OK'
150
+ response['result'] # => { 'category' => 'spot', 'symbol' => 'BTCUSDT', 'list' => [...] }
151
+ response['time'] # => 1234567890000
152
+ ```
153
+
154
+ ## Testnet
155
+
156
+ Toggle `testnet: true` for [https://testnet.bybit.com](https://testnet.bybit.com):
157
+
158
+ ```ruby
159
+ client = Bybit::Client.new(api_key: ENV['BYBIT_TESTNET_KEY'], api_secret: ENV['BYBIT_TESTNET_SECRET'], testnet: true)
160
+ ```
161
+
162
+ ## Development
163
+
164
+ ```
165
+ bundle install
166
+ bundle exec rspec # tests
167
+ bundle exec rubocop # lint
168
+ bundle exec rake # both
169
+ ```
170
+
171
+ ## License
172
+
173
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # End-to-end quickstart — public + signed calls + rescue matrix.
4
+ # Run with: BYBIT_KEY=... BYBIT_SECRET=... ruby examples/quickstart.rb
5
+
6
+ require 'bybit'
7
+
8
+ Bybit.configure do |c|
9
+ c.api_key = ENV['BYBIT_KEY']
10
+ c.api_secret = ENV['BYBIT_SECRET']
11
+ c.testnet = true # flip to false for mainnet
12
+ end
13
+
14
+ client = Bybit::Client.new
15
+
16
+ # 1. Public endpoint — server time (no auth needed)
17
+ puts '--- server time ---'
18
+ puts client.market.get_server_time.inspect
19
+
20
+ # 2. Signed endpoint — wallet balance
21
+ puts '--- wallet balance ---'
22
+ begin
23
+ wallet = client.account.get_wallet_balance(account_type: 'UNIFIED')
24
+ puts wallet['result']['list'].inspect
25
+ rescue Bybit::AuthError => e
26
+ warn "auth failed: [#{e.ret_code}] #{e.ret_msg}"
27
+ rescue Bybit::RateLimitError => e
28
+ warn "rate-limited: #{e.ret_msg}"
29
+ rescue Bybit::TimeoutError => e
30
+ warn "timeout: #{e.message}"
31
+ rescue Bybit::NetworkError => e
32
+ warn "network: #{e.message}"
33
+ rescue Bybit::ApiError => e
34
+ warn "api error [#{e.ret_code}]: #{e.ret_msg}"
35
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module Bybit
6
+ # HMAC-SHA256 signer for the Bybit V5 REST API.
7
+ # Payload format: timestamp + api_key + recv_window + (query_string OR request_body).
8
+ module Authentication
9
+ module_function
10
+
11
+ def sign_v5(api_secret, timestamp, api_key, recv_window, payload)
12
+ msg = "#{timestamp}#{api_key}#{recv_window}#{payload}"
13
+ OpenSSL::HMAC.hexdigest('SHA256', api_secret, msg)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # gen-sdk-ruby:service-requires:start
4
+ require 'bybit/rest_api/account_service'
5
+ require 'bybit/rest_api/affiliate_service'
6
+ require 'bybit/rest_api/asset_service'
7
+ require 'bybit/rest_api/bot_service'
8
+ require 'bybit/rest_api/broker_service'
9
+ require 'bybit/rest_api/crypto_loan_service'
10
+ require 'bybit/rest_api/earn_service'
11
+ require 'bybit/rest_api/market_service'
12
+ require 'bybit/rest_api/p2p_service'
13
+ require 'bybit/rest_api/position_service'
14
+ require 'bybit/rest_api/rfq_service'
15
+ require 'bybit/rest_api/spot_margin_service'
16
+ require 'bybit/rest_api/trade_service'
17
+ require 'bybit/rest_api/user_service'
18
+ # gen-sdk-ruby:service-requires:end
19
+
20
+ module Bybit
21
+ # Global configuration block — Bybit.configure { |c| c.api_key = ... }
22
+ class << self
23
+ attr_writer :configuration
24
+
25
+ def configuration
26
+ @configuration ||= Configuration.new
27
+ end
28
+
29
+ def configure
30
+ yield configuration
31
+ end
32
+ end
33
+
34
+ class Client
35
+ # gen-sdk-ruby:client-readers:start
36
+ attr_reader :account, :affiliate, :asset, :bot, :broker, :crypto_loan, :earn,
37
+ :market, :p2p, :position, :rfq, :spot_margin, :trade, :user
38
+
39
+ # gen-sdk-ruby:client-readers:end
40
+
41
+ def initialize(config = nil, **overrides)
42
+ cfg = (config || Bybit.configuration).dup
43
+ overrides.each { |k, v| cfg.public_send("#{k}=", v) }
44
+ session = Session.new(cfg)
45
+ @config = cfg
46
+
47
+ # gen-sdk-ruby:client-inits:start
48
+ @account = RestApi::AccountService.new(session)
49
+ @affiliate = RestApi::AffiliateService.new(session)
50
+ @asset = RestApi::AssetService.new(session)
51
+ @bot = RestApi::BotService.new(session)
52
+ @broker = RestApi::BrokerService.new(session)
53
+ @crypto_loan = RestApi::CryptoLoanService.new(session)
54
+ @earn = RestApi::EarnService.new(session)
55
+ @market = RestApi::MarketService.new(session)
56
+ @p2p = RestApi::P2pService.new(session)
57
+ @position = RestApi::PositionService.new(session)
58
+ @rfq = RestApi::RfqService.new(session)
59
+ @spot_margin = RestApi::SpotMarginService.new(session)
60
+ @trade = RestApi::TradeService.new(session)
61
+ @user = RestApi::UserService.new(session)
62
+ # gen-sdk-ruby:client-inits:end
63
+ end
64
+
65
+ # Redacted #inspect so console printing doesn't leak the secret.
66
+ def inspect
67
+ "#<Bybit::Client testnet=#{@config.testnet} base_url=#{@config.resolved_base_url}>"
68
+ end
69
+ alias to_s inspect
70
+ end
71
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bybit
4
+ BASE_URL_MAINNET = 'https://api.bybit.com'
5
+ BASE_URL_TESTNET = 'https://api-testnet.bybit.com'
6
+ DEFAULT_RECV_WINDOW = '5000'
7
+ DEFAULT_TIMEOUT = 10
8
+
9
+ class Configuration
10
+ # Attributes safe to expose in serialized form. Anything not in this
11
+ # allowlist is dropped from to_json / as_json / marshal_dump — no accidental
12
+ # secret leakage through pino/winston-style default serializers.
13
+ SAFE_ATTRS = %i[testnet base_url recv_window timeout].freeze
14
+
15
+ attr_accessor :api_key, :api_secret, :testnet, :base_url,
16
+ :recv_window, :timeout, :faraday_connection
17
+
18
+ def initialize
19
+ @testnet = false
20
+ @recv_window = DEFAULT_RECV_WINDOW
21
+ @timeout = DEFAULT_TIMEOUT
22
+ end
23
+
24
+ def resolved_base_url
25
+ base_url || (testnet ? BASE_URL_TESTNET : BASE_URL_MAINNET)
26
+ end
27
+
28
+ # Serialization-safe hash used by as_json / to_json / marshal_dump.
29
+ def to_h_safe
30
+ {
31
+ api_key: redact(@api_key),
32
+ api_secret: redact(@api_secret),
33
+ testnet: @testnet,
34
+ base_url: resolved_base_url,
35
+ recv_window: @recv_window,
36
+ timeout: @timeout
37
+ }
38
+ end
39
+
40
+ def to_json(*args)
41
+ require 'json'
42
+ to_h_safe.to_json(*args)
43
+ end
44
+
45
+ def as_json(*_)
46
+ to_h_safe.transform_keys(&:to_s)
47
+ end
48
+
49
+ def marshal_dump
50
+ to_h_safe
51
+ end
52
+
53
+ # Un-marshalling a redacted Configuration is intentionally lossy — callers
54
+ # should never round-trip credentials through Marshal.
55
+ def marshal_load(hash)
56
+ hash.each { |k, v| instance_variable_set("@#{k}", v) }
57
+ end
58
+
59
+ # Redact credentials from #inspect and #to_s so a stray puts/logger call
60
+ # doesn't leak the secret into log aggregation.
61
+ def inspect
62
+ "#<Bybit::Configuration api_key=#{redact(@api_key)} api_secret=#{redact(@api_secret)} testnet=#{@testnet} base_url=#{resolved_base_url}>"
63
+ end
64
+ alias to_s inspect
65
+
66
+ private
67
+
68
+ def redact(value)
69
+ # Use .to_s.empty? so an Integer / other non-String credential (rare but
70
+ # possible via misconfig) doesn't crash with NoMethodError.
71
+ value.nil? || value.to_s.empty? ? '(unset)' : '[REDACTED]'
72
+ end
73
+ end
74
+ end