Python SDK
Runtime objects and service interfaces available to custom BeeQuant strategy code.
1. SDK overview
The BeeQuant Python SDK provides a consistent quantitative-trading API across backtesting, paper trading, and live trading. You can use the SDK when writing Python code in both the custom trading logic node and the custom data processing node. However, exchange clients (ctx.exchange) are available only to custom trading logic and visual trading nodes. Custom data processing nodes cannot access ctx.exchange or ctx.signal, including market-data and trading methods such as get_price, get_klines, and get_funding_rate. They can only transform their input DataFrame.
| Module | Purpose |
|---|---|
| beequant.object | Data object definitions for accounts, orders, positions, candlesticks, and related entities |
| beequant.api | Abstract interfaces for trading clients, runtime context, logging, and other services |
The backend runtime for custom data processing and custom trading logic nodes includes the following commonly used packages, which can be imported directly:
- Data processing:
pandas,numpy,pyarrow,scipy,python-dateutil, andpytz - Technical analysis:
taandTA-Lib - Machine learning:
scikit-learn,lightgbm, andoptuna - Deep learning:
torch(the GPU image additionally includes a CUDA-enabled build,matplotlib, andseaborn) - Other packages:
requests,sqlalchemy,redis,pydantic, andpyparsing
statsmodels, xgboost, catboost, and tensorflow. Use scikit-learn, lightgbm, or torch instead.2. Data objects beequant.object
2.1 Account — Account information
| Field | Type | Description |
|---|---|---|
| id | str | Account ID |
| index | int | Account index |
| exchange | str | Exchange name |
| market_type | str | Market type (spot/um/cm) |
| initial_capital | float | Initial capital |
| total_equity | float | Total account equity |
| available_balance | float | Available balance |
| frozen_balance | float | Frozen balance |
| unrealized_pnl | float | Unrealized PnL |
| realized_pnl | float | Realized PnL |
| used_margin | float | Margin in use |
| margin_ratio | float | Margin ratio |
| leverage | int | Leverage |
| balances | list[Balance] | Balance entries |
| positions | list[Position] | Open positions |
2.2 Balance — Balance information
| Field | Type | Description |
|---|---|---|
| account_id | str | Account ID |
| exchange | str | Exchange |
| asset | str | Asset |
| amount | float | Total balance |
| available | float | Available balance |
| frozen | float | Frozen balance |
| entry_price | float | Average entry price |
| updated_at | int | Last update time in milliseconds |
2.3 Position — Position information
| Field | Type | Description |
|---|---|---|
| account_id | str | Account ID |
| exchange | str | Exchange |
| symbol | str | Trading symbol |
| position_side | str | Position side (long/short) |
| amount | float | Position quantity |
| entry_price | float | Average entry price |
| mark_price | float | Mark price |
| leverage | int | Leverage |
| margin | float | Margin |
| unrealized_pnl | float | Unrealized PnL |
| realized_pnl | float | Realized PnL |
| notional_value | float | Notional value |
| updated_at | int | Last update time in milliseconds |
PositionSide values: long (long position) / short (short position) / both (hedge mode)2.4 Order — Order information
| Field | Type | Description |
|---|---|---|
| order_id | str | Order ID |
| symbol | str | Trading symbol |
| side | str | Order side (buy/sell) |
| position_side | str | Position side |
| order_type | str | Order type (market/limit) |
| price | float | Order price |
| amount | float | Order quantity |
| filled_amount | float | Filled quantity |
| filled_price | float | Average fill price |
| commission | float | Trading fee |
| status | str | Order status |
| exit_reason | str | Position exit reason |
| created_at | int | Creation time in milliseconds |
OrderStatus: created / filled / partially_filled / canceled / rejected / expired / failedOrderType: market / limitMarketType: spot / um (USDT-margined futures) / cm (coin-margined futures)2.5 Kline — Candlestick data
| Field | Type | Description |
|---|---|---|
| timestamp | int | Timestamp in milliseconds |
| open | float | Open price |
| high | float | High price |
| low | float | Low price |
| close | float | Close price |
| volume | float | Trading volume |
2.6 Ticker — Market ticker
Backtests do not simulate an order book: last_price is the only meaningful price; bid_price and ask_price equal the latest price; and bid_qty and ask_qty are 0.0.
| Field | Type | Description |
|---|---|---|
| exchange | str | Exchange |
| symbol | str | Trading symbol |
| market_type | str | Market type (spot/um/cm) |
| last_price | float | Latest price |
| bid_price | float | Best bid price |
| bid_qty | float | Quantity at the best bid |
| ask_price | float | Best ask price |
| ask_qty | float | Quantity at the best ask |
| update_time | int | Last update time in milliseconds |
2.7 SymbolInfo — Symbol information
| Field | Type | Description |
|---|---|---|
| exchange | str | Exchange |
| symbol | str | Trading symbol |
| base_asset | str | Base asset |
| quote_asset | str | Quote asset |
| min_qty | float | Minimum order quantity |
| step_size | float | Order quantity increment |
| tick_size | float | Minimum price increment |
| max_leverage | float | Maximum leverage |
3. API interfaces beequant.api
3.1 UserContext — Runtime context
The main entry point for strategy code. It provides unified access to trading, logging, signals, and file storage, and supports dynamic attributes for retaining user-defined state across bars.
| Attribute | Type | Description |
|---|---|---|
| ctx.exchange | Exchange | Exchange client manager. Use ctx.exchange[0] to get the first client. Available only in custom trading logic and visual trading nodes; unavailable in custom data processing nodes |
| ctx.log | Log | Logging service |
| ctx.signal | Signal | Signal access. Available only in custom trading logic and visual trading nodes; unavailable in custom data processing nodes |
| ctx.file | File | File persistence service (models / parameters) |
| ctx.<any_name> | Any | Dynamic attribute for retaining user-defined state across bars, for example ctx.entry_price = {} |
def init(ctx: UserContext):
ctx.max_position = 0.1 # Dynamic attribute retained across bars
ctx.entry_price = {}
def handle_data(ctx: UserContext, datas):
client = ctx.exchange[0]
ctx.log.info("Processing bar")3.2 Client — Trading client
Provides a consistent interface for queries, market data, positions, and order placement across backtesting, paper trading, and live trading. Access it through ctx.exchange[0].
Metadata and utility methods
| Method | Return value | Description |
|---|---|---|
| get_exchange() | str | Returns the exchange name, such as binance, okx, or bybit |
| get_market() | str | Returns the market type: spot, um, or cm |
| get_exchange_market() | tuple[str, str] | Returns (exchange, market) |
| set_symbol_list(symbols) | - | Sets the symbols monitored by the strategy |
| get_symbol_list() / symbol_list | list[str] | Returns the current symbol list |
| get_price(symbol) | float | Returns the current price from ticker.last_price |
| get_tickers(symbols) | list[Ticker] | Returns tickers for multiple symbols |
| get_total_equity(quote_asset) | float | Returns total account equity, denominated in USDT by default |
| get_leverage(symbol) | int | Returns the current leverage |
| set_default_leverage(leverage) | - | Sets the default leverage |
| get_cumulative_realized_pnl() | Optional[float] | Returns the strategy's cumulative realized PnL |
Account queries
| Method | Return value | Description |
|---|---|---|
| get_account() | Account | Returns account information |
| get_balance(symbol) | Balance | Returns the balance for a specific asset |
| get_balances() | list[Balance] | Returns all balances |
| get_position(symbol, position_side) | Position | Returns a specific position |
| get_positions() | list[Position] | Returns all positions |
Order queries
| Method | Return value | Description |
|---|---|---|
| get_pending_orders(symbol=None) | list[Order] | Returns open orders; omit symbol to query all symbols |
| get_history_orders(symbol, limit=100) | list[Order] | Returns historical orders |
Market data
| Method | Return value | Description |
|---|---|---|
| get_kline(symbol, interval=None) | Optional[Kline] | Returns the current candlestick. Backtests prevent look-ahead; see the backtest market-data limitations above for cross-mode details |
| get_klines(symbol, interval, limit=100) | list[Kline] | Returns completed historical candlesticks with timestamp, open, high, low, close, and volume fields |
| get_ticker(symbol) | Ticker | Returns the current ticker; backtests return a simulated price and do not provide a real order book |
Funding rates and fees
Real data is available only for UM/CM futures clients. Spot markets and unsupported environments return None or [].
| Method | Return value | Description |
|---|---|---|
| get_funding_rate(symbol) | Optional[dict] | Returns the current or most recently available funding-rate information |
| get_funding_fees(symbol, start_ms, end_ms) | list[dict] | Returns funding-fee records for the given interval; use symbol="" to query all symbols in the account |
get_funding_rate returns a dict containing symbol, fundingRate, nextFundingRate, nextFundingTimeMs, markPrice, indexPrice, and intervalHours.
client = ctx.exchange[0] # Must be a UM/CM futures account
info = client.get_funding_rate("BTC_USDT")
if info is not None:
rate = info.get("fundingRate", 0.0) # Current rate; 0.0001 = 0.01%
next_ms = info.get("nextFundingTimeMs", 0) # Next settlement time in milliseconds
hours = info.get("intervalHours", 8) # Settlement interval in hoursHigh-level trading methods ⭐
| Method | Return value | Description |
|---|---|---|
| place(symbol, side, qty, limit_price, cost, position_side) | Optional[str] | Places a general-purpose order and returns its order_id |
| target(symbol, qty, limit_price, cost, auto_partial) | Optional[str] | Adjusts a one-way position to the target quantity |
| target_percent(symbol, target_percent, limit_price, threshold, auto_partial) | Optional[str] | Adjusts to a target portfolio exposure; this is the most commonly used method |
| close_all(symbol) | list[str] | Closes all positions for the symbol and returns the order IDs |
| cancel_all(symbol=None) | - | Cancels all open orders |
| set_exit_reason(order_id, reason) | bool | Records the reason for closing a position, primarily for backtest analysis |
target_percent() explained
Automatically calculates the target position quantity from total account equity and the current price. This is the recommended way to manage position size.
client.target_percent(
symbol="BTC_USDT",
target_percent=0.1, # Target position value as a share of total equity
limit_price=None, # None places a market order
threshold=0.05, # Skip rebalancing when the deviation is below 5%
auto_partial=True, # Reduce the order automatically if funds are insufficient
)Target position value = total account equity × target_percent
Target position quantity = target position value ÷ current price
| target_percent | Spot behavior | Futures behavior |
|---|---|---|
| 0 | Close the position | Close all positions |
| 0.5 | Position value = 50% of total equity | Long; notional value = 50% of total equity |
| 1.0 | Fully invested | Long; notional value = 100% of total equity |
| -0.5 | Unsupported (must be ≥ 0) | Short; notional value = 50% of total equity |
| 2.0 | Not recommended | 2× leveraged long exposure |
# Assume total equity is 10,000 USDT and BTC is priced at 50,000 USDT.
# target_percent = 0.8 -> target quantity = 10,000 * 0.8 / 50,000 = 0.16 BTC
client.target_percent("BTC_USDT", 0.8) # No position -> open a 0.16 BTC long
client.target_percent("BTC_USDT", 0.8) # 0.1 BTC long -> add 0.06 BTC
client.target_percent("BTC_USDT", 0.8) # 0.2 BTC long -> reduce by 0.04 BTC
client.target_percent("BTC_USDT", -0.8) # 0.1 BTC long -> close the long, then open a short
client.target_percent("BTC_USDT", 0) # Close the positionBasic trading methods
# Spot
client.buy(symbol, qty=None, price=None, cost=None)
client.sell(symbol, qty=None, price=None, cost=None)
# One-way futures positions
client.open_long(symbol, qty=None, price=None, cost=None)
client.close_long(symbol, qty=None, price=None, cost=None)
client.open_short(symbol, qty=None, price=None, cost=None)
client.close_short(symbol, qty=None, price=None, cost=None)
# Other methods
client.set_leverage(symbol, leverage=None)
client.cancel_order(symbol, order_id)qty always means a quantity of the base currency, while cost always means an amount denominated in USDT. Provide one or the other, not both.3.3 Exchange — Client manager
Manages multiple client instances and supports lookup by name or index.
| Method | Description |
|---|---|
| ctx.exchange[0] | Returns a client by index; this is the most common form |
| ctx.exchange.get_by_name('binance') | Returns a client by exchange name |
| ctx.exchange.get_all_clients() | Returns all registered clients |
3.4 Signal — Signal access
Provides access to trading signals generated by the strategy. The system retrieves data from ctx.signal.get() in advance and passes it to the datas parameter of handle_data.
# datas is supplied automatically, so you normally do not call ctx.signal.get() yourself.
def handle_data(ctx: UserContext, datas):
signal_df = datas # DataFrame for one time step, including symbol/timestamp/prediction
# Historical features are calculated by upstream processing nodes.
# historyPeriods must cover the required lookback window.3.5 Log — Logging service
| Method | Description |
|---|---|
| ctx.log.info(msg, source='', extra='') | Business events such as opening, closing, or rebalancing positions |
| ctx.log.detail(msg, source='') | Per-bar details such as selection rankings and target positions; suitable for frequent output |
| ctx.log.warn(msg, source='') | Risk and validation warnings |
| ctx.log.error(msg, source='') | Error messages |
| ctx.log.debug(msg, source='') | Local debugging; not recommended in production |
| ctx.log.dataframe(df, title='Data preview', source='') | Writes a DataFrame preview that the web interface renders as a table |
ctx.log.*. Do not call print() directly.3.6 File — File persistence
Persists model files (machine learning) and parameter files (strategy state recovery) across runs. Every method is addressed by file name; saving under an existing name overwrites it (and does not consume an extra file slot). Internal file IDs are never exposed.
Local paths: the strategy process can only read and write the task work directory. /tmp, the home directory and arbitrary absolute paths are rejected. Always use ctx.file.work_path(name) when you need a path on disk.
The work directory is ephemeral: work_path(name) points at the task container's local disk, which is destroyed when the task ends.torch.save() and booster.save_model() only cover the "write locally" step —you must then call ctx.file.save_model(name) to upload before the file is actually persisted. Loading works the same way: call ctx.file.load_model(name) to fetch the remote file locally, then hand the path to the library.
| Method | Return value | Description |
|---|---|---|
| work_path(name) | str | Writable absolute path inside the task work directory |
| save_model(name, local_path='') | Optional[str] | Uploads a model file; returns the file name on success |
| load_model(name, local_path='') | Optional[str] | Downloads a model file; returns the local path on success |
| save_params(name, params) | Optional[str] | Stores a parameter/state dict as JSON |
| load_params(name, default=None) | Optional[dict] | Reads parameters; returns default when absent |
| save_bytes(name, data) | Optional[str] | Uploads in-memory bytes directly |
| load_bytes(name) | Optional[bytes] | Reads a remote file into memory |
| list_files() | list[FileInfo] | Lists every file owned by the current user |
| exists(name) | bool | Checks whether a file exists |
| delete(name) | bool | Deletes a file to free quota |
| quota() | FileQuota | Returns file count and storage usage |
FileInfo: .name / .kind ("model" | "params") / .size / .size_str / .hash / .created_at / .updated_at; FileQuota: .file_count / .max_files / .used_bytes / .max_total_bytes / .max_model_bytes / .max_params_bytes
| Limit | Maximum |
|---|---|
| Single model file | 100 MB |
| Single parameter file (.json) | 1 MB |
| Files per user | 50 |
| Total storage per user | 1 GB |
| File name length | 100 characters, no path separators |
Allowed extensions (the test is whether strategy code can legally read the file back): models .lightgbm (lgb.Booster(model_file=...)), .pt / .pth (torch.load(..., weights_only=True)), .lstm / .transformer (platform AiModel loader); parameters .json.
⚠ .pkl / .pickle / .joblib cannot be saved (existing files stay readable and deletable): the static security rules permanently block import pickle, pickle.load and joblib.load, so such a file could never be deserialized. Export scikit-learn parameter arrays to .json, or switch to lightgbm / torch. tensorflow, onnxruntime and safetensors are not preinstalled either, so .h5 / .keras / .onnx / .safetensors are unsupported. Note that open(), pathlib, os and tempfile are blocked as well: writing to local disk must be done by the library itself (torch.save / booster.save_model). That step only fills the ephemeral work directory — you still need ctx.file.save_model(name) to persist it. For parameter files ctx.file.save_params() does both in one call.
# Model: persist after training, reuse on the next run
path = ctx.file.load_model("alpha.pt")
if path:
model.load_state_dict(torch.load(path, weights_only=True))
else:
train(model)
torch.save(model.state_dict(), ctx.file.work_path("alpha.pt"))
ctx.file.save_model("alpha.pt")
# LightGBM
booster.save_model(ctx.file.work_path("alpha.lightgbm"))
ctx.file.save_model("alpha.lightgbm")
# Parameters: strategy state recovery
state = ctx.file.load_params("state.json", default={"grid_level": 0})
state["grid_level"] += 1
ctx.file.save_params("state.json", state)
# Quota: clean up before hitting the file-count limit
q = ctx.file.quota()
if q.file_count >= q.max_files:
oldest = sorted(ctx.file.list_files(), key=lambda f: f.updated_at)[0]
ctx.file.delete(oldest.name)4. Complete strategy example
The following signal-driven position management strategy demonstrates the standard use of all three SDK lifecycle functions. See the custom trading logic node documentation for more templates.
from beequant.api.context import UserContext
from beequant.api.client import Client
def init(ctx: UserContext):
"""Initialize the strategy. Called once."""
ctx.log.info("Strategy initialized", source="Demo")
ctx.open_threshold = 0.5
ctx.close_threshold = 0.1
ctx.max_position = 0.1
def handle_data(ctx: UserContext, datas):
"""Process one signal time step."""
client: Client = ctx.exchange[0]
if datas is None or datas.empty:
return
for _, row in datas.iterrows():
symbol = row["symbol"]
prediction = float(row.get("prediction", 0.0))
if prediction >= ctx.open_threshold:
order_id = client.target_percent(
symbol, ctx.max_position, threshold=0.05, auto_partial=True
)
ctx.log.info(
f"Open/rebalance: {symbol}, target={ctx.max_position}, order_id={order_id}"
)
elif abs(prediction) <= ctx.close_threshold:
order_id = client.target_percent(symbol, 0)
ctx.log.info(f"Close: {symbol}, order_id={order_id}")
else:
ctx.log.detail(f"Hold: {symbol}, prediction={prediction:.4f}")
def handle_tick(ctx: UserContext):
"""Observe prices on each tick. Optional."""
client: Client = ctx.exchange[0]
symbol = client.symbol_list[0] if client.symbol_list else "BTCUSDT"
price = client.get_price(symbol)
ctx.log.detail(f"Tick: {symbol} price={price}")5. Symbol naming conventions
| Market type | Format | Examples |
|---|---|---|
| Spot | BASEUSDT | BTCUSDT, ETHUSDT |
| UM (USDT-margined futures) | BASE_USDT | BTC_USDT, ETH_USDT |
| CM (coin-margined futures) | BASE_USD | BTC_USD, ETH_USD |
snake_case, such as account_id. JSON serialization converts them to camelCase, such as accountID. All data objects provide to_dict() and from_dict() methods.