bybit-connector-ruby 0.1.0 → 0.1.3

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,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bybit
4
+ module RestApi
5
+ class SpreadTradeService < BaseService
6
+ # Get Trade History
7
+ #
8
+ # GET /v5/spread/execution/list
9
+ #
10
+ # @option kwargs [String] :symbol Spread combination symbol name.
11
+ # @option kwargs [String] :order_id Spread combination order ID.
12
+ # @option kwargs [String] :order_link_id User-defined custom order ID.
13
+ # @option kwargs [Integer] :start_time Start timestamp in milliseconds. See time range logic in description.
14
+ # @option kwargs [Integer] :end_time End timestamp in milliseconds. See time range logic in description.
15
+ # @option kwargs [Integer] :limit Number of results per page. Range `[1, 50]`. Default `20`.
16
+ # @option kwargs [String] :cursor Pagination cursor token from `nextPageCursor` in a previous response.
17
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
18
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/trade-history
19
+ def get_history(**kwargs)
20
+ params = Bybit::Utils::WireKeys.camelize(kwargs)
21
+ @session.sign_request(method: :get, path: '/v5/spread/execution/list', params: params)
22
+ end
23
+
24
+ # Get Spread Instruments Info
25
+ #
26
+ # GET /v5/spread/instrument
27
+ #
28
+ # @option kwargs [String] :symbol Spread combination symbol name, e.g. `SOLUSDT_SOL/USDT`
29
+ # @option kwargs [String] :base_coin Base coin in uppercase, e.g. `SOL`
30
+ # @option kwargs [Integer] :limit Number of records per page, range `[1, 500]`. Default is `200`
31
+ # @option kwargs [String] :cursor Pagination cursor; use `nextPageCursor` from the previous response
32
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
33
+ # @see https://bybit-exchange.github.io/docs/v5/spread/market/instrument
34
+ def get_spread_instruments_info(**kwargs)
35
+ params = Bybit::Utils::WireKeys.camelize(kwargs)
36
+ @session.public_request(path: '/v5/spread/instrument', params: params)
37
+ end
38
+
39
+ # Get Spread Wallet Balance
40
+ #
41
+ # GET /v5/spread/max-qty
42
+ #
43
+ # @param symbol [String] Spread trading pair.
44
+ # @param side [String] Order side:
45
+ # - `1`: Buy
46
+ # - `2`: Sell
47
+ # @param order_price [String] Order price.
48
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
49
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/max-qty
50
+ def get_spread_max_qty(symbol:, side:, order_price:)
51
+ params = Bybit::Utils::WireKeys.camelize({ symbol: symbol, side: side, orderPrice: order_price })
52
+ @session.sign_request(method: :get, path: '/v5/spread/max-qty', params: params)
53
+ end
54
+
55
+ # Amend Order
56
+ #
57
+ # POST /v5/spread/order/amend
58
+ #
59
+ # @param symbol [String] Spread combination symbol name.
60
+ # @option kwargs [String] :order_id Spread combination order ID. Either `orderId` or `orderLinkId` is required.
61
+ # @option kwargs [String] :order_link_id User-defined custom order ID. Either `orderId` or `orderLinkId` is required.
62
+ # @option kwargs [String] :qty New order quantity after modification (string-formatted number).
63
+ # At least one of `qty` or `price` must be provided.
64
+ # @option kwargs [String] :price New order price after modification (string-formatted number).
65
+ # At least one of `qty` or `price` must be provided.
66
+ # - `""` (empty string): keeps the existing price unchanged.
67
+ # - `"0"`: updates the price to zero.
68
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
69
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/amend-order
70
+ def amend_spread_order(symbol:, **kwargs)
71
+ params = Bybit::Utils::WireKeys.camelize(kwargs.merge(symbol: symbol))
72
+ @session.sign_request(method: :post, path: '/v5/spread/order/amend', body: params)
73
+ end
74
+
75
+ # Cancel Order
76
+ #
77
+ # POST /v5/spread/order/cancel
78
+ #
79
+ # @option kwargs [String] :order_id Spread combination order ID. Either `orderId` or `orderLinkId` is required.
80
+ # @option kwargs [String] :order_link_id User-defined custom order ID. Either `orderId` or `orderLinkId` is required.
81
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
82
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/cancel-order
83
+ def cancel_spread_order(**kwargs)
84
+ params = Bybit::Utils::WireKeys.camelize(kwargs)
85
+ @session.sign_request(method: :post, path: '/v5/spread/order/cancel', body: params)
86
+ end
87
+
88
+ # Cancel All Orders
89
+ #
90
+ # POST /v5/spread/order/cancel-all
91
+ #
92
+ # @option kwargs [String] :symbol Spread combination symbol name. When specified, all orders for this symbol
93
+ # are cancelled and `cancelAll` is ignored.
94
+ # @option kwargs [Boolean] :cancel_all Set to `true` to cancel all open orders across all symbols.
95
+ # Ignored when `symbol` is provided.
96
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
97
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/cancel-all
98
+ def cancel_all_spread_orders(**kwargs)
99
+ params = Bybit::Utils::WireKeys.camelize(kwargs)
100
+ @session.sign_request(method: :post, path: '/v5/spread/order/cancel-all', body: params)
101
+ end
102
+
103
+ # Create Order
104
+ #
105
+ # POST /v5/spread/order/create
106
+ #
107
+ # @param symbol [String] Spread combination symbol name.
108
+ # @param side [String] Order direction.
109
+ # @param order_type [String] Order type.
110
+ # @param qty [String] Order quantity (string-formatted number).
111
+ # @option kwargs [String] :price Order price (string-formatted number). Required for Limit orders.
112
+ # Not required for Market orders.
113
+ # @option kwargs [String] :order_link_id User-defined custom order identifier. Maximum 45 characters.
114
+ # Supports alphanumeric characters, dashes, and underscores.
115
+ # @option kwargs [String] :time_in_force Time-in-force strategy for order execution.
116
+ # - `GTC`: Good Till Cancel (default for Limit).
117
+ # - `IOC`: Immediate or Cancel.
118
+ # - `FOK`: Fill or Kill.
119
+ # - `PostOnly`: Maker-only; rejected if it would take liquidity.
120
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
121
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/create-order
122
+ def create_spread_order(symbol:, side:, order_type:, qty:, **kwargs)
123
+ params = Bybit::Utils::WireKeys.camelize(kwargs.merge(symbol: symbol, side: side, orderType: order_type, qty: qty))
124
+ @session.sign_request(method: :post, path: '/v5/spread/order/create', body: params)
125
+ end
126
+
127
+ # Get Order History
128
+ #
129
+ # GET /v5/spread/order/history
130
+ #
131
+ # @option kwargs [String] :symbol Spread combination symbol name.
132
+ # @option kwargs [String] :base_coin Base coin to filter by.
133
+ # @option kwargs [String] :order_id Spread combination order ID. Has higher priority than time-based filters.
134
+ # @option kwargs [String] :order_link_id User-defined custom order ID. Has higher priority than time-based filters.
135
+ # @option kwargs [Integer] :start_time Start timestamp in milliseconds. See time range logic in description.
136
+ # @option kwargs [Integer] :end_time End timestamp in milliseconds. See time range logic in description.
137
+ # @option kwargs [Integer] :limit Number of results per page. Range `[1, 50]`. Default `20`.
138
+ # @option kwargs [String] :cursor Pagination cursor token from `nextPageCursor` in a previous response.
139
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
140
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/order-history
141
+ def get_spread_order_history(**kwargs)
142
+ params = Bybit::Utils::WireKeys.camelize(kwargs)
143
+ @session.sign_request(method: :get, path: '/v5/spread/order/history', params: params)
144
+ end
145
+
146
+ # Get Open Orders
147
+ #
148
+ # GET /v5/spread/order/realtime
149
+ #
150
+ # @option kwargs [String] :symbol Spread combination symbol name.
151
+ # @option kwargs [String] :base_coin Base coin to filter by.
152
+ # @option kwargs [String] :order_id Spread combination order ID.
153
+ # @option kwargs [String] :order_link_id User-defined custom order ID.
154
+ # @option kwargs [Integer] :limit Number of results per page. Range `[1, 50]`. Default `20`.
155
+ # @option kwargs [String] :cursor Pagination cursor token from `nextPageCursor` in a previous response.
156
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
157
+ # @see https://bybit-exchange.github.io/docs/v5/spread/trade/open-order
158
+ def get_spread_open_orders(**kwargs)
159
+ params = Bybit::Utils::WireKeys.camelize(kwargs)
160
+ @session.sign_request(method: :get, path: '/v5/spread/order/realtime', params: params)
161
+ end
162
+
163
+ # Get Spread Orderbook
164
+ #
165
+ # GET /v5/spread/orderbook
166
+ #
167
+ # @param symbol [String] Spread combination symbol name, e.g. `SOLUSDT_SOL/USDT`
168
+ # @option kwargs [Integer] :limit Number of bid/ask levels per side, range `[1, 25]`. Default is `1`
169
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
170
+ # @see https://bybit-exchange.github.io/docs/v5/spread/market/orderbook
171
+ def get_spread_orderbook(symbol:, **kwargs)
172
+ params = Bybit::Utils::WireKeys.camelize(kwargs.merge(symbol: symbol))
173
+ @session.public_request(path: '/v5/spread/orderbook', params: params)
174
+ end
175
+
176
+ # Get Spread Recent Public Trades
177
+ #
178
+ # GET /v5/spread/recent-trade
179
+ #
180
+ # @param symbol [String] Spread combination symbol name, e.g. `SOLUSDT_SOL/USDT`
181
+ # @option kwargs [Integer] :limit Number of records per page, range `[1, 1000]`. Default is `500`
182
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
183
+ # @see https://bybit-exchange.github.io/docs/v5/spread/market/recent-trade
184
+ def get_spread_recent_trades(symbol:, **kwargs)
185
+ params = Bybit::Utils::WireKeys.camelize(kwargs.merge(symbol: symbol))
186
+ @session.public_request(path: '/v5/spread/recent-trade', params: params)
187
+ end
188
+
189
+ # Get Spread Tickers
190
+ #
191
+ # GET /v5/spread/tickers
192
+ #
193
+ # @param symbol [String] Spread combination symbol name, e.g. `SOLUSDT_SOL/USDT`
194
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
195
+ # @see https://bybit-exchange.github.io/docs/v5/spread/market/tickers
196
+ def get_spread_tickers(symbol:)
197
+ params = Bybit::Utils::WireKeys.camelize({ symbol: symbol })
198
+ @session.public_request(path: '/v5/spread/tickers', params: params)
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bybit
4
+ module RestApi
5
+ class StrategyService < BaseService
6
+ # Create a strategy order
7
+ #
8
+ # POST /v5/strategy/create
9
+ #
10
+ # @param params [Hash] Request payload. strategy_type: chaseOrder | iceberg | pov | twap.
11
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
12
+ # @see https://bybit-exchange.github.io/docs/v5/strategy/create-strategy
13
+ def create_strategy(**params)
14
+ body = Bybit::Utils::WireKeys.camelize(params)
15
+ @session.sign_request(method: :post, path: '/v5/strategy/create', body: body)
16
+ end
17
+
18
+ # Query trading strategy list with filters
19
+ #
20
+ # GET /v5/strategy/list
21
+ #
22
+ # @option kwargs [String] :strategy_id Strategy ID for exact lookup (UUID format).
23
+ # If provided, returns only this specific strategy.
24
+ # Useful for checking status of a known strategy.
25
+ # @option kwargs [String] :status Filter by strategy status.
26
+ # - "2": Running (currently executing)
27
+ # - "3" or "4": Terminated (stopped)
28
+ # - "5": Paused (temporarily halted)
29
+ # - "6": Untriggered (waiting for trigger price)
30
+ # Can provide multiple values separated by comma.
31
+ # @option kwargs [String] :symbol Filter by trading pair symbol.
32
+ # Case-sensitive, must match exactly.
33
+ # Example: "BTCUSDT", "ETHUSDT"
34
+ # @option kwargs [String] :category Filter by product category.
35
+ # Useful to separate spot from derivatives strategies.
36
+ # @option kwargs [String] :strategy_type Filter by strategy type.
37
+ # Useful to view only specific algorithm strategies.
38
+ # @option kwargs [Integer] :begin_time_e0 Start time filter (Unix timestamp in seconds).
39
+ # Returns strategies created on or after this time.
40
+ # Example: 1673214964 (Jan 9, 2023)
41
+ # @option kwargs [Integer] :end_time_e0 End time filter (Unix timestamp in seconds).
42
+ # Returns strategies created before this time.
43
+ # Must be greater than beginTimeE0 if both provided.
44
+ # @option kwargs [Integer] :page_size Number of results per page.
45
+ # Default: 20
46
+ # Maximum: 50
47
+ # @option kwargs [String] :cursor Pagination cursor from previous response.
48
+ # Use nextCursor value from previous query to get next page.
49
+ # Empty for first page.
50
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
51
+ # @see https://bybit-exchange.github.io/docs/v5/strategy/strategy-list
52
+ def query_list(**kwargs)
53
+ params = Bybit::Utils::WireKeys.camelize(kwargs)
54
+ @session.sign_request(method: :get, path: '/v5/strategy/list', params: params)
55
+ end
56
+
57
+ # Query orders created by a specific strategy
58
+ #
59
+ # GET /v5/strategy/order-list
60
+ #
61
+ # @param strategy_id [String] Strategy ID to query orders for (UUID format).
62
+ # This is the parent strategy whose child orders you want to retrieve.
63
+ # Obtain from strategy creation response or strategy list query.
64
+ # @option kwargs [String] :status Filter by order status.
65
+ # - "1": Created (active, not filled yet)
66
+ # - "2": PartiallyFilled (some quantity executed)
67
+ # - "3": Filled (fully executed)
68
+ # - "4": Cancelled (order canceled)
69
+ # - "5": Rejected (order rejected by exchange)
70
+ # Can filter by multiple statuses.
71
+ # @option kwargs [String] :symbol Filter by trading pair symbol.
72
+ # Useful if strategy could trade multiple symbols.
73
+ # @option kwargs [Integer] :begin_time_e0 Start time filter (Unix timestamp in seconds).
74
+ # Returns orders created on or after this time.
75
+ # @option kwargs [Integer] :end_time_e0 End time filter (Unix timestamp in seconds).
76
+ # Returns orders created before this time.
77
+ # @option kwargs [Integer] :page_size Number of results per page.
78
+ # Default: 20, Maximum: 50
79
+ # @option kwargs [String] :cursor Pagination cursor from previous response.
80
+ # Use nextCursor from previous query for next page.
81
+ # @option kwargs [String] :strategy_type Filter by strategy type.
82
+ # Should match the parent strategy type.
83
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
84
+ # @see https://bybit-exchange.github.io/docs/v5/strategy/order-list
85
+ def query_order_list(strategy_id:, **kwargs)
86
+ params = Bybit::Utils::WireKeys.camelize(kwargs.merge(strategyId: strategy_id))
87
+ @session.sign_request(method: :get, path: '/v5/strategy/order-list', params: params)
88
+ end
89
+
90
+ # Stop a running strategy
91
+ #
92
+ # POST /v5/strategy/stop
93
+ #
94
+ # @param strategy_id [String] Strategy ID to stop (UUID format).
95
+ # Obtained from strategy creation response or strategy list query.
96
+ # Only Running or Untriggered strategies can be stopped.
97
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
98
+ # @see https://bybit-exchange.github.io/docs/v5/strategy/stop-strategy
99
+ def stop_strategy(strategy_id:)
100
+ params = Bybit::Utils::WireKeys.camelize({ strategyId: strategy_id })
101
+ @session.sign_request(method: :post, path: '/v5/strategy/stop', body: params)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bybit
4
+ module RestApi
5
+ class TaxService < BaseService
6
+ # Batch create tax report files
7
+ #
8
+ # POST /v5/fht/compliance/tax/private/batch_create
9
+ #
10
+ # @param start_time [Integer] Tax report start time (Unix timestamp, seconds)
11
+ # @param end_time [Integer] Tax report end time (Unix timestamp, seconds); interval must not exceed 12 months
12
+ # @param items [Array] Non-empty list of tax report export items
13
+ # @option kwargs [String] :source_institution Optional source institution identifier
14
+ # @option kwargs [String] :export_file_type Export file format:
15
+ # - `csv` — CSV text format
16
+ # - `orc` — ORC columnar format (default)
17
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
18
+ # @note Generated from the Bybit OpenAPI specification; no public documentation page is currently available.
19
+ def batch_create_tax_reports(start_time:, end_time:, items:, **kwargs)
20
+ params = Bybit::Utils::WireKeys.camelize(kwargs.merge(startTime: start_time, endTime: end_time, items: items))
21
+ @session.sign_request(method: :post, path: '/v5/fht/compliance/tax/private/batch_create', body: params)
22
+ end
23
+
24
+ # Query batch tax report status
25
+ #
26
+ # GET /v5/fht/compliance/tax/private/batch_query
27
+ #
28
+ # @param batch_id [String] Batch ID returned by `batch_create`
29
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
30
+ # @note Generated from the Bybit OpenAPI specification; no public documentation page is currently available.
31
+ def batch_query_tax_reports(batch_id:)
32
+ params = Bybit::Utils::WireKeys.camelize({ batchId: batch_id })
33
+ @session.sign_request(method: :get, path: '/v5/fht/compliance/tax/private/batch_query', params: params)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -249,6 +249,16 @@ module Bybit
249
249
  params[:permissions] = permissions unless permissions.nil?
250
250
  @session.sign_request(method: :post, path: '/v5/user/update-sub-api', body: params)
251
251
  end
252
+
253
+ # Query Referral Code
254
+ #
255
+ # GET /v5/user/invitation/code
256
+ #
257
+ # @return [Hash] Bybit V5 ApiResponse envelope (retCode / retMsg / result / retExtInfo / time).
258
+ # @see https://bybit-exchange.github.io/docs/v5/user/referral-code
259
+ def query_referral_code
260
+ @session.sign_request(method: :get, path: '/v5/user/invitation/code')
261
+ end
252
262
  end
253
263
  end
254
264
  end
data/lib/bybit/session.rb CHANGED
@@ -21,7 +21,8 @@ module Bybit
21
21
 
22
22
  def initialize(config)
23
23
  @config = config
24
- @conn = config.faraday_connection || build_connection
24
+ @conn = config.faraday_connection ? config.faraday_connection.dup : build_connection
25
+ @conn.options.params_encoder = Faraday::FlatParamsEncoder
25
26
  end
26
27
 
27
28
  # Public unsigned endpoint — no X-BAPI-* headers attached.
@@ -52,9 +53,9 @@ module Bybit
52
53
  headers = build_headers(signed: signed, method: method, query_str: query_str, body_str: body_str)
53
54
 
54
55
  # 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.
56
+ # what we signed. Faraday still parses URLs passed to `req.url`, so use
57
+ # its flat encoder to preserve repeated keys instead of collapsing them
58
+ # or adding array brackets.
58
59
  full_url = if !query_str.empty? && PAYLOAD_QUERY_METHODS.include?(method)
59
60
  "#{path}?#{query_str}"
60
61
  else
@@ -126,11 +127,21 @@ module Bybit
126
127
  body = normalize_legacy_body(body) if body.is_a?(Hash)
127
128
 
128
129
  # 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.
130
+ # 401/403/429 get their promised AuthError / RateLimitError classes even
131
+ # when a CDN/WAF returns HTML (no retCode); the README documents this
132
+ # contract. 5xx / other non-auth 4xx keep their generic Server/Client
133
+ # buckets so retry and pager logic can tell them apart.
131
134
  if !body.is_a?(Hash) || !body['retCode'].is_a?(Integer)
132
135
  preview = truncate_for_error(raw)
133
- if status >= 500
136
+ if [401, 403].include?(status)
137
+ raise Bybit::AuthError.new(
138
+ "Bybit auth error (status=#{status}): #{preview}", http_status: status
139
+ )
140
+ elsif status == 429
141
+ raise Bybit::RateLimitError.new(
142
+ "Bybit rate limit (status=#{status}): #{preview}", http_status: status
143
+ )
144
+ elsif status >= 500
134
145
  raise Bybit::ServerError, "Bybit server error (status=#{status}): #{preview}"
135
146
  elsif status >= 400
136
147
  raise Bybit::ClientError, "Bybit client error (status=#{status}): #{preview}"
data/lib/bybit/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bybit
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.3'
5
5
  end