Package: hubble-exchange
Python SDK for Hubble ExchangeHubble Exchangeis a Layer 1 Blockchain for a Decentralised Perps OrderBookTwitterInstallationThe simplest way is to install the package from PyPI:pipinstallhubble-exchangeExample usage:All read/write functions are asyncRequires HUBBLE_RPC, HUBBLE_WS_RPC, HUBBLE_ENV, PRIVATE_KEY environment variable to be setexportHUBBLE_RPC=https://candy-hubblenet-rpc.hubble.exchange/ext/bc/iKMFgo49o4X3Pd3UWUkmPwjKom3xZz3Vo6Y1kkwL2Ce6DZaPm/rpcexportHUBBLE_WS_RPC=wss://candy-hubblenet-rpc.hubble.exchange/ext/bc/iKMFgo49o4X3Pd3UWUkmPwjKom3xZz3Vo6Y1kkwL2Ce6DZaPm/wsexportHUBBLE_ENV=hubblenextexportPRIVATE_KEY=<privatekey>exportHUBBLE_INDEXER_API_URL=""importosfromhubble_exchangeimportHubbleClient,OrderBookDepthResponse,LimitOrder,IOCOrderasyncdefmain():asyncdefcallback(response):print(f"Received response:{response}")returnresponseclient=HubbleClient(os.getenv("PRIVATE_KEY"))# get a dict of all market ids and names - for example {0: "ETH-Perp", 1: "AVAX-Perp"}markets=awaitclient.get_markets()# place limit orderslimit_orders=[]limit_orders.append(LimitOrder.new(3,1,1.2,False,False))# market = 3, qty = 1, price = 1.2, reduce_only = False, post_only = Falselimit_orders.append(LimitOrder.new(0,0.1,1800,False,True))# market = 0, qty = 0.1, price = 1800, reduce_only = False, post_only = True# placed_orders list will contain the order ids for the orders placedplaced_orders=awaitclient.place_limit_orders(limit_orders,True,callback)# place ioc ordersioc_orders=[]ioc_orders.append(IOCOrder.new(3,1,1.2,False,3))# market = 3, qty = 1, price = 1.2, reduce_only = False, expiry = 3 secondsioc_orders.append(IOCOrder.new(0,0.1,1800,False,3))# market = 0, qty = 0.1, price = 1800, reduce_only = False, expiry = 3 seconds# placed_orders list will contain the order ids for the orders placedplaced_orders=awaitclient.place_ioc_orders(ioc_orders,True,callback)# get limit order details - only works for open limit ordersorder_details=awaitclient.get_limit_order_details(order.id,callback)# get status for limit orders - works for all order statusesorder_status=awaitclient.get_limit_order_status(order.id,callback)# cancel an orderawaitclient.cancel_limit_orders([order],True,callback)# order can also be cancelled by order idawaitclient.cancel_order_by_id(order.id,True,callback)# get current order book for market = 1order_book=awaitclient.get_order_book(1,callback)# get current margin and positions(uses the address for which private key is set)positions=awaitclient.get_margin_and_positions(callback)# get order fillsorder_fills=awaitclient.get_order_fills()# get all open ordersopen_orders=awaitclient.get_open_orders(None,callback)# get open orders for market_id = 0open_orders=awaitclient.get_open_orders(0,callback)# get user trades in all markets in a time rangeawaitclient.get_trades(None,1691669740,1691583340,callback)# get user trades in market_id = 3 in a time rangeawaitclient.get_trades(3,1691669740,1691583340,callback)# subscribe to order book updates for market = 0; receives a new message every second(only for those prices where the quantity has changed)asyncdefon_message(ws,message):print(f"Received orderbook update:{message}")asyncio.run(client.subscribe_to_order_book_depth(0,callback=on_message))GetPositionsResponse descriptionmargin: trader's marginreservedMargin: trader's reserved marginpositions: List[Position]Position descriptionmarket: market idopenNotional: open notionalsize: size of the positionunrealisedFunding: unrealised funding payment for this positionliquidationThreshold: max amount of size that can be liquidated at oncenotionalPosition: notional position sizeunrealisedProfit: unrealised profit on this positionmarginFraction: margin/notional positionliquidationPrice: the asset price at which this position might get liquidatedmarkPrice: latest price of this marketOrderStatusResponse descriptionexecutedQty: executed quantityorderId: order idorigQty: total order quantityprice: order pricereduceOnly: whether the order is reduce-only or notpostOnly: whether the order is post-only or notpositionSide: (LONG|SHORT)status: order status (NEW|FILLED|CANCELED|REJECTED|PARTIALLY_FILLED)symbol: market idtime: time when the order was placed in unix formattype: LIMIT_ORDERupdateTime: time when the order was last updated in unix formatsalt: order saltOrderbook depth feedOrderbook depth feed can be subscribed to using thesubscribe_to_order_book_depthmethod. It emits the change in orderbook depth every second. It only emits the prices where the quantity has changed, but with absolute quantities(not the change).importosfromhubble_exchangeimportHubbleClientasyncdefmain():client=HubbleClient(os.getenv("PRIVATE_KEY"))# subscribe to market id 0awaitclient.subscribe_to_order_book_depth(0,callback=callback)OrderBookDepthUpdateResponse descriptionT: timestampsymbol: market idbids: list of [price, quantity] for bidsasks: list of [price, quantity] for asksTrader feedAll order updates related to a particular trader can be subscribed to using thesubscribe_to_trader_updatesmethod.
It can be subscribed in 2 confirmation modes - head block or accepted block. Events received in head block mode are not finalised and can be reverted. When an event is removed from the chain, the client will receive aremoved=Trueevent. Events received in accepted block mode are finalised and will alwats haveremoved=False.importosfromhubble_exchangeimportHubbleClient,ConfirmationModeasyncdefmain():client=HubbleClient(os.getenv("PRIVATE_KEY"))awaitclient.subscribe_to_trader_updates(ConfirmationMode.accepted,callback)TraderFeedUpdate descriptionTrader: address of the traderOrderId: order idOrderType: order type - "limit" order or "ioc" (market order)Removed: whether the event is being removed or notEventName: name of the contract event (OrderAccepted|OrderRejected|OrderMatched|OrderCancelAccepted|OrderCancelRejected)Args: args is a dynamic field and it contains information about the event. For example, OrderPlaced event contains order object, OrderMatched event contains fillAmount and priceBlockNumber: block number in which the transaction was includedBlockStatus: (head|accepted) whether the block is accepted or only a preferred block(head block)Timestamp: timestamp of the block in unix formatTransactionHash: transaction hashEventName descriptionOrderAccepted - Order was successfully placedOrderRejected - Order was rejectedOrderMatched - Order was matchedOrderCancelAccepted - Order was successfully cancelled(only for Limit orders)OrderCancelRejected - Order cancel was rejected(only for Limit orders)Removed events are emmitted during chain reorgs, and are most likely to be temporary. They are only emmitted when subscribing to head block events. If removed=True, the client might need to do a reverse operation for the given event. For example if an OrderMatched event is received with removed=True, the client should add the fillAmount back to unfilled quantity of the order.Market feedAll trades of a particular market can be subscribed to using thesubscribe_to_market_updatesmethod.
Similar to the trader feed, it has 2 confirmation modes - head block or accepted block.importosfromhubble_exchangeimportHubbleClient,ConfirmationModeasyncdefmain():client=HubbleClient(os.getenv("PRIVATE_KEY"))# subscribe to market id 0awaitclient.subscribe_to_market_updates(0,ConfirmationMode.accepted,callback)MarketFeedUpdate descriptionTrader: address of the trader who performed the tradeMarket: market idSize: size of the trade in decimalsPrice: price at which it was executedRemoved: whether the event is being removed or notEventName: name of the contract event (PositionModified)BlockNumber: block number in which the transaction was includedBlockStatus: (head|accepted) whether the block is accepted or only a preferred block(head block)Timestamp: timestamp of the block in unix formatTransactionHash: transaction hashRemoved events are emmitted during chain reorgs, and are most likely to be temporary. They are only emmitted when subscribing to head block events. If removed=True, the client might need to do a reverse operation for the given event.Get limit order statusGet the status of a limit order. It can be used to get the status of any order, even if it is not placed using the sdk.order_status=awaitclient.get_limit_order_status(order.id,callback)Status DescriptionInvalid = order does not exist
Placed = order is unfilled
PartiallyFilled = order is partially filled
Filled = order is completely filled
Cancelled = cancelledOpen ordersReturns all open orders of the trader. It can be filtered by market id.# get all open ordersopen_orders=awaitclient.get_open_orders(None,callback)# get open orders for market_id = 0open_orders=awaitclient.get_open_orders(0,callback)OpenOrder descriptionMarket: market idPrice: order priceSize: order sizeFilledSize: filled sizeTimestamp: timestamp of order place transaction in unix formatSalt: order saltOrderId: order idReduceOnly: whether the order is reduce-only or notPostOnly: whether the order is post-only or notOrderType: order type - "limit" order or "ioc" (market order)Get historical user tradesReturns all trades of the trader. It can be filtered by market id.# get user trades in all markets in a time rangeawaitclient.get_trades(None,1691669740,1691583340,callback)# get user trades in market_id = 3 in a time rangeawaitclient.get_trades(3,1691669740,1691583340,callback)Trade descriptionBlockNumber: block number in which the order match transaction was includedTransactionHash: transaction hashMarket: market idTimestamp: timestamp of the block in unix formatTradedAmount: amount of the trade in decimalsPrice: price at which it was executedRealizedPnl: realized pnl of the trade in USDOpenNotional: open notionalExecutionMode: execution mode - "maker" or "taker"Candlestick dataHistoricalcandlestick datacan be fetched using theget_candlestick_datamethod using market and time range.It can be obtained for the following timeframes:1m - 1 minute3m - 3 minutes5m - 5 minutes15m - 15 minutes30m - 30 minutes1h - 1 hour4h - 4 hours8h - 8 hours1d - 1 day1w - 1 week1M - 1 monthfromhubble_exchangeimportHubbleClient# get candlestick data for market id 0, timeframe = 5 minutes, start time = 1696932000, end time = 1696939200response=awaitclient.get_candlesticks(0,"5m",1696932000,1696939200)CandlestickData descriptionExample candlestick data:{start:1696500000,// start of the time period in unix formatend:1696500300,// end of the time period in unix formatvolume:192267.282,// volume in USDopen:0.8745,// open price for the durationclose:0.8851,// close price for the durationhigh:0.8947,// highest price for the durationlow:0.8675,// lowest price for the duration}Historical funding rateHistorical funding rate can be queried using theget_funding_ratemethod using market and timestamp.
Funding payment happens every hour at the end of the hour as per UTC time. For example, funding payment for 1pm to 2pm will happen at 2pm.If the provided timestamp is not the end of an hour, the funding rate for the previous hour will be returned. For example, if the timestamp is 1:25pm, the funding rate for 1pm will be returned.fromhubble_exchangeimportHubbleClient# get funding payments for market id 0, time = 1696932000funding_rate=awaitclient.get_funding_rate(0,1696932000)Predicted funding ratePredicted funding rate can be queried using theget_predicted_funding_ratemethod using market. It returns the predicted funding rate for the current hour.fromhubble_exchangeimportHubbleClient# get predicted funding rate for market id 2funding_rate=awaitclient.get_predicted_funding_rate(2)Historical open interestHistorical open interest can be queried using theget_open_interestmethod using market and timestamp. It returns the open interest in the units of the base asset. For example, if the market is ETH-Perp, the open interest will be in ETH.fromhubble_exchangeimportHubbleClient# get open interest for market id 0, time = 1696932000open_interest=awaitclient.get_open_interest(0,1696932000)Get nonceAn account nonce is a transaction counter in each account, which is used to prevent replay attacks. Nonce should be increased by 1 in every transaction. The functionget_noncefetches the number of transactions sent by the address from the blockchain.Nonce can be fetched once usingget_nonceand be incremented in every subsequent transaction. However, if the same address is sending multiple transactions in parallel, it is recommended to useget_noncebefore every transaction.nonce=awaitclient.get_nonce()# use this nonce in the next transaction like thisawaitclient.place_limit_orders(orders,True,callback,{"nonce":nonce})Custom transaction optionsThe following options can be passed to the client to override the default{"nonce":Nonce,"gas":int,"maxFeePerGas":Union[str,Wei],"maxPriorityFeePerGas":Union[str,Wei],},It can be used forplace_limit_orders,cancel_limit_orders,cancel_order_by_idmethods.
Example:fromweb3importWeb3client=HubbleClient(os.getenv("PRIVATE_KEY"))placed_orders=awaitclient.place_limit_orders(orders,callback,{"gas":500_000,"maxFeePerGas":Web3.to_wei(80,'gwei'),"maxPriorityFeePerGas":Web3.to_wei(20,'gwei'),})Transaction modesThere are different modes in which the client can wait for acknowledgement of the transaction. The default behaviour is to send the transaction and not wait for the acknowledgement.
This can be changed by explicitly asking the function to wait while sending the trasaction.TransactionMode.no_wait: The default behaviour is to send transactions to the blockchain and NOT wait for the acknowledgement.TransactionMode.wait_for_head: Wait for the transaction to be included in the canonical chain. At this time the block is preferred but not yet finalized. However, once the block in included in the canonical chain, the matching engine will start processing the order.TransactionMode.wait_for_accept: Wait for the transaction to be finalised.Example:fromhubble_exchangeimportTransactionModeclient=HubbleClient(os.getenv("PRIVATE_KEY"))placed_orders=awaitclient.place_limit_orders(orders,callback,mode=TransactionMode.wait_for_accept)# or set the default mode for all transactionsclient.set_transaction_mode(TransactionMode.wait_for_head)Waiting for response in place_limit_orders and cancel_limit_ordersTheplace_limit_orders,place_ioc_orders, andcancel_limit_ordersmethods can be called in 2 modes - wait for response or don't wait for response.
To get the acknowledgement of the transaction, usewait_for_response=True. The response will be a list of dicts with order ids and success boolean. Waiting for response will be slower because this can be confirmed only after the transaction is mined(accepted).
When usingwait_for_response=True, the sdk will automatically set the transaction mode toTransactionMode.wait_for_acceptbecause the response can be confirmed only after the transaction is mined.Alternatively, the client can also use trader feed to listen to all the updates. This is faster when done with ConfirmationMode.headConfigmininum_quantity: minimum quantity that can be placed in an order for a particular market. The quantity also has to be a multiple of this number.price_precision: number of decimal places for price for a particular market.fromhubble_exchange.constantsimportget_minimum_quantity,get_price_precision# get minimum quantity for market id 3min_qty=get_minimum_quantity(3)# get price precision for market id 3price_precision=get_price_precision(3)