Bybit SDK
Complete reference for ctx.bybit — programmatic trading on Bybit from worker code.
Bybit V5 API
This SDK wraps the Bybit V5 unified API. Live, demo, and testnet environments all supported.
Setup
- Add a Bybit trading block to your workspace canvas.
- Open the inspector → API Keys → pick a key (or add a new one in Settings → API Keys).
- Connect the block to your Worker block via an edge.
- In the worker's inspector, the
ctx.bybitadapter is now available.
def tick(ctx):
ticker = ctx.bybit.get_tickers("BTCUSDT")
ctx.log.info(f"BTC: {ticker['list'][0]['lastPrice']}")If no Bybit block is connected, calling any method raises:
RuntimeError: No trading block connected. Connect a Bybit block to this worker.Categories & Modes
Bybit V5 splits markets into four categories. The default is taken from your block's inspector but can be overridden per call:
| Category | Description |
|---|---|
linear | USDT/USDC perpetuals & futures (default) |
inverse | Coin-margined perpetuals (BTCUSD, ETHUSD) |
spot | Spot trading |
option | Options (BTC, ETH, SOL) |
Account modes (api_mode):
| Mode | Endpoint | Notes |
|---|---|---|
live | api.bybit.com | Real funds |
demo | api-demo.bybit.com | Paper-trading with real market data |
testnet | api-testnet.bybit.com | Synthetic test environment |
Mode is stored on the API key — switch in Settings → API Keys → Bybit.
Market Data
get_tickers
Latest ticker (last price, bid, ask, 24h volume).
ctx.bybit.get_tickers(symbol: str = "BTCUSDT", base_coin: str = None) -> dict| Param | Type | Description |
|---|---|---|
symbol | str | Trading pair, e.g. "BTCUSDT" |
base_coin | str | For options only: filters by base ("BTC", "ETH", "SOL") |
Returns: {"list": [{"symbol", "lastPrice", "bid1Price", "ask1Price", "volume24h", ...}]}
t = ctx.bybit.get_tickers("BTCUSDT")
last = float(t["list"][0]["lastPrice"])WebSocket cache
On PRO/MAX plans this returns cached ticker data at 0ms latency from the live WS stream — no REST call.
get_klines
OHLCV candlestick data.
ctx.bybit.get_klines(
symbol: str,
interval: str = "60",
limit: int = 200,
start: int = None,
end: int = None,
category: str = None,
) -> dict| Param | Type | Description |
|---|---|---|
interval | str | "1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "D", "W", "M" (minutes / Day / Week / Month) |
limit | int | Max 1000 |
start, end | int | Unix ms timestamps |
Returns: {"list": [[start, open, high, low, close, volume, turnover], ...]}
candles = ctx.bybit.get_klines("BTCUSDT", interval="60", limit=24)
closes = [float(c[4]) for c in candles["list"]]
sma_24h = sum(closes) / len(closes)get_mark_price_klines
Mark-price candles (for derivatives — used for liquidation calculations).
ctx.bybit.get_mark_price_klines(
symbol: str,
interval: str = "60",
limit: int = 200,
category: str = None,
) -> dictSame shape as get_klines. Only available for linear / inverse categories.
get_index_price_klines
Index-price candles (basket of spot prices the perpetual tracks).
ctx.bybit.get_index_price_klines(symbol, interval, limit, category) -> dictget_orderbook
Current order book depth.
ctx.bybit.get_orderbook(symbol: str, limit: int = 50, category: str = None) -> dict| Param | Limit |
|---|---|
linear / inverse | 1, 25, 50, 100, 200, 500 |
spot | 1, 50, 200 |
option | 25 |
Returns: {"s": "BTCUSDT", "b": [[price, size], ...], "a": [[price, size], ...], "ts": 1234567890, "u": 12345}
ob = ctx.bybit.get_orderbook("BTCUSDT", limit=50)
best_bid = float(ob["b"][0][0])
best_ask = float(ob["a"][0][0])
spread_bps = (best_ask - best_bid) / best_bid * 10000get_recent_trades
Most recent public trades.
ctx.bybit.get_recent_trades(symbol: str, limit: int = 60, category: str = None) -> dictget_funding_history
Historical funding rates for perpetuals.
ctx.bybit.get_funding_history(
symbol: str,
limit: int = 200,
start_time: int = None,
end_time: int = None,
category: str = None,
) -> dictget_open_interest
Open-interest history (aggregate position size on the perp).
ctx.bybit.get_open_interest(
symbol: str,
interval_time: str = "1h", # "5min" | "15min" | "30min" | "1h" | "4h" | "1d"
limit: int = 200,
start_time: int = None,
end_time: int = None,
category: str = None,
) -> dictget_long_short_ratio
Long/short ratio across the platform (sentiment indicator).
ctx.bybit.get_long_short_ratio(
symbol: str,
period: str = "1h", # "5min" | "15min" | "30min" | "1h" | "4h" | "1d"
limit: int = 50,
category: str = None,
) -> dictget_instruments
All tradable instruments for a category (and optionally a base coin for options).
ctx.bybit.get_instruments(base_coin: str = "BTC", category: str = "option") -> dictget_delivery_price
Delivery / exercise prices for futures and options.
ctx.bybit.get_delivery_price(base_coin: str = "BTC", category: str = "option") -> dictAccount & Wallet
get_balance
Wallet balance across coins.
ctx.bybit.get_balance(coin: str = None) -> dict| Param | Description |
|---|---|
coin | Filter to a single coin (e.g. "USDT"). Omit to return all. |
Returns: {"list": [{"totalEquity", "totalAvailableBalance", "coin": [{"coin", "walletBalance", "availableToWithdraw", ...}]}]}
bal = ctx.bybit.get_balance("USDT")
usdt = bal["list"][0]["coin"][0]
ctx.log.info(f"Free USDT: {usdt['availableToWithdraw']}")WebSocket cache
PRO/MAX: cached from the private WS stream at 0ms — no REST.
get_account_info
Account type (UTA / Classic), unified margin mode, master/sub.
ctx.bybit.get_account_info() -> dictget_fee_rate
Maker / taker fees for your tier.
ctx.bybit.get_fee_rate(symbol: str = None, category: str = None) -> dictget_transaction_log
Account transaction log (deposits, withdrawals, fees, funding, realized PnL).
ctx.bybit.get_transaction_log(
account_type: str = "UNIFIED",
category: str = None,
currency: str = None,
type: str = None, # TRADE | SETTLEMENT | DELIVERY | LIQUIDATION | TRANSFER_IN | …
start_time: int = None,
end_time: int = None,
limit: int = 50,
) -> dictPositions
get_positions
All open positions, optionally filtered by symbol.
ctx.bybit.get_positions(symbol: str = None) -> dictReturns: {"list": [{"symbol", "side", "size", "entryPrice", "leverage", "unrealisedPnl", "markPrice", "liqPrice", ...}]}
positions = ctx.bybit.get_positions()
for p in positions["list"]:
if float(p["size"]) > 0:
ctx.log.info(f"{p['symbol']} {p['side']}: PnL {p['unrealisedPnl']}")get_closed_pnl
Closed-position PnL history.
ctx.bybit.get_closed_pnl(symbol: str = None, limit: int = 50, category: str = None) -> dictset_leverage
Set leverage on a symbol (separate buy / sell legs for hedge mode).
ctx.bybit.set_leverage(symbol: str, buy_leverage, sell_leverage=None) -> dictctx.bybit.set_leverage("BTCUSDT", 10) # one-way: 10x both sides
ctx.bybit.set_leverage("BTCUSDT", 5, 3) # hedge: 5x long, 3x shortswitch_margin_mode
Switch isolated ↔ cross margin for a symbol.
ctx.bybit.switch_margin_mode(
symbol: str,
trade_mode: int, # 0 = cross, 1 = isolated
buy_leverage: str = "10",
sell_leverage: str = None,
) -> dictset_position_mode
Switch one-way ↔ hedge mode at the account or symbol level.
ctx.bybit.set_position_mode(
mode: int, # 0 = one-way, 3 = hedge (both buy & sell)
symbol: str = None, # None = applies to whole category
coin: str = None, # for inverse: by margin coin
category: str = None,
) -> dictset_risk_limit
Set the per-position risk limit (controls max position size and forced-liquidation tier).
ctx.bybit.set_risk_limit(
symbol: str,
risk_id: int, # Bybit's pre-defined tier ID; see `get_risk_limit`
position_idx: int = 0, # 0 = one-way / hedge buy, 1 = hedge buy, 2 = hedge sell
category: str = None,
) -> dictset_trading_stop
Apply Take Profit / Stop Loss / trailing stop to an open position.
ctx.bybit.set_trading_stop(
symbol: str,
take_profit: str = None,
stop_loss: str = None,
trailing_stop: str = None,
tp_trigger_by: str = None, # "LastPrice" | "MarkPrice" | "IndexPrice"
sl_trigger_by: str = None,
active_price: str = None, # activation price for trailing stop
tp_size: str = None, # partial TP size (default = full)
sl_size: str = None,
position_idx: int = 0,
category: str = None,
) -> dict# Set TP at 3% above entry, SL at 1% below
pos = ctx.bybit.get_positions("BTCUSDT")["list"][0]
entry = float(pos["entryPrice"])
ctx.bybit.set_trading_stop(
symbol="BTCUSDT",
take_profit=str(entry * 1.03),
stop_loss=str(entry * 0.99),
)Orders
place_order
Place a single order.
ctx.bybit.place_order(
symbol: str,
side: str, # "Buy" | "Sell"
qty: str, # quantity as string
order_type: str = "Market", # "Market" | "Limit"
price: str = None, # required for Limit
category: str = None,
) -> dictReturns: {"orderId": "...", "orderLinkId": "..."}
# Market buy 0.001 BTC
r = ctx.bybit.place_order("BTCUSDT", "Buy", "0.001", "Market")
# Limit sell 0.5 ETH at $3000
r = ctx.bybit.place_order(
symbol="ETHUSDT", side="Sell", qty="0.5",
order_type="Limit", price="3000",
)Trade logging
Every successful order is recorded in your TradeLog table — visible in Journal block and ctx.bybit.get_executions().
amend_order
Modify an unfilled or partially-filled order (qty, price, TP, SL).
ctx.bybit.amend_order(
symbol: str,
order_id: str,
qty: str = None,
price: str = None,
take_profit: str = None,
stop_loss: str = None,
category: str = None,
) -> dictcancel_order
Cancel a single order by ID.
ctx.bybit.cancel_order(symbol: str, order_id: str, category: str = None) -> dictcancel_all_orders
Cancel all open orders, optionally filtered.
ctx.bybit.cancel_all_orders(
symbol: str = None,
category: str = None,
base_coin: str = None,
settle_coin: str = None,
) -> dictget_orders
Active (unfilled) orders.
ctx.bybit.get_orders(symbol: str = None) -> dictWebSocket cache
PRO/MAX: cached from private WS at 0ms.
get_order_history
Filled / cancelled / rejected orders.
ctx.bybit.get_order_history(
symbol: str = None,
order_id: str = None,
limit: int = 50,
category: str = None,
) -> dictget_executions
Trade fills with fees (the actual transactions, not just orders).
ctx.bybit.get_executions(
symbol: str = None,
order_id: str = None,
limit: int = 50,
category: str = None,
) -> dictbatch_place_orders
Place up to 20 orders in one call (atomic acceptance, individual fills).
ctx.bybit.batch_place_orders(orders: list, category: str = None) -> dictctx.bybit.batch_place_orders([
{"symbol": "BTCUSDT", "side": "Buy", "orderType": "Limit", "qty": "0.001", "price": "60000"},
{"symbol": "BTCUSDT", "side": "Buy", "orderType": "Limit", "qty": "0.001", "price": "59000"},
{"symbol": "BTCUSDT", "side": "Buy", "orderType": "Limit", "qty": "0.001", "price": "58000"},
])batch_cancel_orders
Cancel up to 20 orders in one call.
ctx.bybit.batch_cancel_orders(orders: list, category: str = None) -> dictopen = ctx.bybit.get_orders("BTCUSDT")["list"]
ctx.bybit.batch_cancel_orders([{"symbol": o["symbol"], "orderId": o["orderId"]} for o in open])Account Operations
internal_transfer
Transfer between sub-accounts and account types.
ctx.bybit.internal_transfer(
coin: str,
amount: str,
from_account_type: str = "UNIFIED", # UNIFIED | FUND | SPOT | CONTRACT | OPTION
to_account_type: str = "FUND",
transfer_id: str = None, # auto UUID if omitted
) -> dictCommon Patterns
Demo / testnet during development
Set the API-key mode in Settings → API Keys → Bybit to demo or testnet. All worker calls automatically route to the right environment. Switch back to live when you're ready.
Error handling
All methods that hit a remote API can raise BybitAPIError (extends ExchangeAPIError). For network failures, httpx.HTTPStatusError. Wrap calls if needed:
def tick(ctx):
try:
positions = ctx.bybit.get_positions()
except Exception as e:
ctx.log.error(f"Bybit unreachable: {e}")
returnCloud-Run proxy (PRO+ plans)
Workers on PRO and above route Bybit calls through a Google Cloud Run proxy with rotating exit IPs. This avoids rate-limit concentration when many users share the same server IP. Transparent — no code changes.
WebSocket caching
PRO+ plans subscribe to Bybit's public + private WS streams in the background. Methods get_tickers, get_orderbook, get_balance, get_positions, get_orders automatically prefer the cache when fresh — 0 ms latency, 0 REST calls.
Recipes
Simple DCA (dollar-cost averaging)
def setup(ctx):
ctx.state.set("last_buy_ts", 0)
ctx.state.set("buy_amount_usdt", 50)
def tick(ctx):
import time
now = int(time.time())
last = ctx.state.get("last_buy_ts", 0)
if now - last < 3600: # once an hour
return
px = float(ctx.bybit.get_tickers("BTCUSDT")["list"][0]["lastPrice"])
qty = round(ctx.state.get("buy_amount_usdt", 50) / px, 4)
r = ctx.bybit.place_order("BTCUSDT", "Buy", str(qty), "Market")
ctx.state.set("last_buy_ts", now)
ctx.log.info(f"DCA buy: {qty} BTC at {px} → {r.get('orderId')}")Fixed-grid market making
def setup(ctx):
grid = []
px = float(ctx.bybit.get_tickers("BTCUSDT")["list"][0]["lastPrice"])
step = px * 0.005 # 0.5% grid spacing
for i in range(1, 6):
grid.append({"symbol": "BTCUSDT", "side": "Buy", "orderType": "Limit",
"qty": "0.001", "price": str(round(px - i * step))})
grid.append({"symbol": "BTCUSDT", "side": "Sell", "orderType": "Limit",
"qty": "0.001", "price": str(round(px + i * step))})
ctx.bybit.batch_place_orders(grid)
ctx.state.set("grid_active", True)
def tick(ctx):
# Re-fill on any executed legs every minute
open_orders = ctx.bybit.get_orders("BTCUSDT")["list"]
if len(open_orders) >= 10:
return # all 10 orders still alive
# … reissue logic …Trailing stop on existing position
def tick(ctx):
positions = ctx.bybit.get_positions("BTCUSDT")["list"]
pos = next((p for p in positions if float(p["size"]) > 0), None)
if not pos:
return
# Activate a 1% trailing stop the moment we're 2% in profit
entry = float(pos["entryPrice"])
mark = float(pos["markPrice"])
if mark > entry * 1.02:
ctx.bybit.set_trading_stop(
symbol="BTCUSDT",
trailing_stop=str(round(entry * 0.01)),
active_price=str(round(entry * 1.02)),
)
ctx.log.info("Trailing stop activated")Reference
| Bybit V5 endpoint | SDK method |
|---|---|
GET /v5/market/tickers | get_tickers |
GET /v5/market/kline | get_klines |
GET /v5/market/mark-price-kline | get_mark_price_klines |
GET /v5/market/index-price-kline | get_index_price_klines |
GET /v5/market/orderbook | get_orderbook |
GET /v5/market/recent-trade | get_recent_trades |
GET /v5/market/funding/history | get_funding_history |
GET /v5/market/open-interest | get_open_interest |
GET /v5/market/account-ratio | get_long_short_ratio |
GET /v5/market/instruments-info | get_instruments |
GET /v5/market/delivery-price | get_delivery_price |
GET /v5/account/wallet-balance | get_balance |
GET /v5/account/info | get_account_info |
GET /v5/account/fee-rate | get_fee_rate |
GET /v5/account/transaction-log | get_transaction_log |
GET /v5/position/list | get_positions |
GET /v5/position/closed-pnl | get_closed_pnl |
POST /v5/position/set-leverage | set_leverage |
POST /v5/position/switch-isolated | switch_margin_mode |
POST /v5/position/switch-mode | set_position_mode |
POST /v5/position/set-risk-limit | set_risk_limit |
POST /v5/position/trading-stop | set_trading_stop |
POST /v5/order/create | place_order |
POST /v5/order/amend | amend_order |
POST /v5/order/cancel | cancel_order |
POST /v5/order/cancel-all | cancel_all_orders |
GET /v5/order/realtime | get_orders |
GET /v5/order/history | get_order_history |
GET /v5/execution/list | get_executions |
POST /v5/order/create-batch | batch_place_orders |
POST /v5/order/cancel-batch | batch_cancel_orders |
POST /v5/asset/transfer/inter-transfer | internal_transfer |