Understaning & Placing Orders on Bybit with Python

by John | April 30, 2023

 

Are you looking to take your cryptocurrency trading to the next level? Look no further than Bybit, the leading crypto derivatives exchange. With advanced trading tools, low fees, and a user-friendly platform, Bybit makes it easy to trade Bitcoin, Ethereum, and other popular cryptocurrencies. And if you sign up using our affiliate link  and use the referral code CODEARMO, you'll receive exclusive benefits and bonuses up to $30,000 to help you get started. Don't miss out on this opportunity to join one of the fastest-growing communities in crypto trading. Sign up for Bybit today and start trading like a pro!

 

Important Information

For readers, that intend to try this code on their own account, it is advisable you follow these steps:

- Navigate to your assets page. 

- Click on 'derivative' 

- Ensure you have only a very small amount of capital in your derivative account. for the purposes of this article we will use 1 USDT , if you have in excess of this you can use the 'transfer' button , and everything except 1 USDT to follow along with this article. 

 

Contents

  • Description of order types 
  • How to place orders with python 
  • Configuring price tick and quantity to avoid errors from Bybit API

 

 

Market Orders

 

A market order is a type of order to buy or sell a security or asset at the current market price. When you place a market order, you are essentially telling the exchange to execute the trade immediately at the best available price.

For example, if you want to buy shares of a stock using a market order, you would specify the number of shares you want to purchase, and the exchange would execute the trade at the current market price. This means that the price you pay for the shares may be slightly higher or lower than the price you saw when you placed the order, since the market price is constantly fluctuating.

Market orders are typically used when speed of execution is more important than getting the best possible price. They are also commonly used for highly liquid assets where the bid-ask spread is relatively narrow, meaning that the difference between the highest price a buyer is willing to pay and the lowest price a seller is willing to accept is relatively small.

 

Below we show how to send an order to Bybit for 1 DOGEUSDT contract , which at the time of writing is worth about 7 cents. If you haven't already installed Pybit please see the article on setting up an API key and installing Pybit. Once we have imported Pybit as shown below:

 

from pybit.unified_trading import HTTP
import json 

with open('authcreds.json') as j:
    creds = json.load(j)
        
key = creds['KEY_NAME']['key']
secret = creds['KEY_NAME']['secret']


session = HTTP(api_key=key,
               api_secret=secret) 


#Place a market order to buy 1 DOGE CONTRACT
# worth approx 7 cents at the time of writing
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Market',
    side = 'Buy',
    qty= '1'
    )

 

If you have authenticated successfully, you should now see something that looks similar to the following below the chart on your screen

 

Contracts Qty Value Entry Price Mark Price Liq. Price Position Margin Unrealized P&L (%) Realized P&L
DOGEUSDT 1 0.07 USDT 0.07985 0.07986 -- 0.0080 USDT (0.00%) 0.0000 USDT

 

 

The table below shows the pros and cons of using market orders. It should be noted that for algorithmic trading, market orders are by far simpler to use, however, the drawback is higher fees and uncertain execution. 

 

Pros Cons
Guaranteed execution No price control
Fast execution High price volatility risk
Simple to use May result in slippage (i.e. execution at less favorable price) in fast-moving markets
Useful for traders who want to enter or exit a position quickly May not be suitable for large orders or illiquid markets
Can be used in situations where the exact price of execution is not important Can result in larger trading costs due to the spread between the bid and ask prices

 

 

Adding a Target Price and a Stop loss Price

 

The script below adds a target price of $0.1 and a stop price of $0.05 , this means that if the price exceeds either level, the trade will be closed in profit/loss respectively. 

 

#Place a market order to buy 1 DOGE CONTRACT
# worth approx 7 cents at the time of writing
# we can also add a target price and a stop price
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Market',
    side = 'Buy',
    qty= '1',
    takeProfit='0.1',
    stopLoss = '0.05'
    )

 

If you revisit the tab below the screen you will now see you have 2 units of DOGE (currently worth about $0.14). In addition, you should see two green dashed lines representing the target and the stop loss price. 

 

 

Limit Orders

 

A limit order is a type of order used in trading where you set a specific price to buy or sell an asset. If the market price reaches the set price, the limit order is executed automatically. The order will only be executed at the limit price or better, meaning that if the market price does not reach the set price, the order will not be filled. 

An important concept regarding limit orders is Time in Force , below the 4 types of time in force limit order. It is important to note, that generally for the hobbyist (who this article is aimed at) , the only ones you really need to know are GTC and PostOnly orders

 

Good Till Cancel (GTC)

If we specify GTC as the time in force parameter, the order will rest in the book, until either it is filled or canceled by the user. 

 

Immediate or Cancel (IOC)

An IOC order allows traders to execute an order at the best available price in the market, and any part of the order that cannot be filled immediately is automatically canceled.

 

Fill or Kill (FOK)

A FOK order is a type of order which the user can specify that if the order can't be filled in its entirety , it should be canceled e.g. we put a limit order for 100 DOGE at $0.071 , however, there are only 80 DOGE resting in the book at the time the order is submitted. This would result in the entire order being canceled and the trader's position being unchanged. 

 

Post Only

A post only is where a trader places an order on the order book, and if the order would immediately execute against an existing order, the order will be canceled instead of being filled. In other words, a Post-Only order ensures that your order is added to the order book as a maker order (an order that provides liquidity to the market), rather than as a taker order (an order that takes liquidity from the market).

 

Pros Cons
Allows traders to control the price at which an order is executed No guaranteed execution
Can prevent slippage by setting a specific price at which to execute the order Execution is not guaranteed if the market moves away from the limit price
Can be used to enter or exit a position at a specific price May result in the order not being filled if the market does not reach the limit price
Can be used to minimize trading costs by setting a limit price that is better than the current market price May miss out on trading opportunities if the market moves in the desired direction without reaching the limit price
Useful for traders who want to control the exact price at which they enter or exit a position May require constant monitoring of the market to ensure the limit order is executed

 

 

To demostrate using these orders types we will copy and paste some of the code to get orderbook information from the previous article

 

response = session.get_orderbook(
    category="linear",
    symbol="DOGEUSDT",
    limit=50).get('result')


def format_order_book(response):
    '''
    

    Parameters
    ----------
    response : dict
    
    Returns
    -------
    two list of lists containing bid/ask price with associated volume

    '''
    bids = response.get('b')
    asks = response.get('a')
    
    return bids, asks
    

bids, asks = format_order_book(response)          

for bid in bids[:10]:
    print(f'Bid price - {bid[0]} , quantity = {bid[1]}')


'''
Bid price - 0.07858 , quantity = 703283
Bid price - 0.07857 , quantity = 462443
Bid price - 0.07856 , quantity = 888012
Bid price - 0.07855 , quantity = 1144183
Bid price - 0.07854 , quantity = 1413185
Bid price - 0.07853 , quantity = 760301
Bid price - 0.07852 , quantity = 852670
Bid price - 0.07851 , quantity = 642636
Bid price - 0.0785 , quantity = 846594
Bid price - 0.07849 , quantity = 545895
'''

 

Below we submit one of each of the 4 orders described above. It is left to the reader, to investigate which orders are executed, and when reading the order descriptions above, it should become clear why. 

 

#GTC order
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Limit',
    timeInForce='GTC',
    side = 'Buy',
    qty= '1',
    price = '0.07849'
    )


#IOC order
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Limit',
    timeInForce='IOC',
    side = 'Buy',
    qty= '1',
    price = '0.07849'
    )


#FOK order
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Limit',
    timeInForce='FOK',
    side = 'Buy',
    qty= '1',
    price = '0.08'
    )


#PostOnly

response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Limit',
    timeInForce='PostOnly',
    side = 'Buy',
    qty= '1',
    price = '0.08'
    )

 

 

Trigger Orders

 

A trigger order is an instruction to execute a trade automatically when the market reaches a certain price level. It consists of two parts: a trigger price and an execution price. The trigger price is the level at which the order is activated, while the execution price is the price at which the order is filled. Once the market reaches the trigger price, the order is automatically placed and will be executed at the best available price. Trigger orders are useful for traders who want to take advantage of market movements without having to monitor the market constantly. They are commonly used in volatile markets where prices can change quickly, and can be used to enter or exit a position.

 

 

Pros Cons
Can be used to automate trades based on price movements May result in execution at less favorable price due to market volatility
Can help manage risk and protect profits Requires accurate setting of trigger and limit prices
Can be used to enter or exit a position automatically without constantly monitoring the market May not execute if the trigger price is not met
Can be useful for traders who are not always available to monitor the market May not be suitable for fast-moving markets with high volatility
Can help traders take advantage of opportunities when they arise May incur additional fees or charges for using trigger orders

 

Example 1

Lets say a trader believes that if DOGE will break through $0.10 (when price currently $0.07) it will reach $0.15 , well , he could place a trigger order to market buy 1 DOGE at $0.10 and another one to sell 1 DOGE if the price crosses above $0.15. The function calls below achieve this logic. These orders are known as conditional i.e. if the condition (trigger price) isn't met they won't be executed. 

 

# trigger to buy DOGE if the price crosses above $0.1
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Market',
    triggerDirection = 1,
    side= 'Buy',
    qty= '1',
    triggerPrice='0.1'
    )


# trigger to sell DOGE if the price crosses above $0.15
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Market',
    triggerDirection = 1,
    side= 'Sell',
    qty= '1',
    triggerPrice='0.15'
    )

 

Example 2

Lets say a trader believes that in the event of DOGE falling below $0.049 , this will mean that $0.05 will become resistance. Well, he could place a conditional limit order that if the price should fall below 0.049 this will send a limit order to 0.05. 

 


#example 2 

# trigger order to place a sell limit order if the price should dip below $0.05

response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Limit',
    triggerDirection = 2,
    triggerPrice = '0.049',
    side= 'Sell',
    qty= '1',
    price='0.05'
    )

 

Cancelling Orders

 

Often we will want to programatically cancel orders to either replace them or because we no longer want to have limit or trigger orders. First lets see how to cancel all orders, note we must pass the symbol we want to cancel all orders for. 

 

print(session.cancel_all_orders(category='linear',
                                symbol='DOGEUSDT'))

'''
{'retCode': 0,
 'retMsg': 'OK',
 'result': {'list': [{'orderId': '4427d835-7af5-4c32-ba76-6b1914e03ca4',
    'orderLinkId': ''}]},
 'retExtInfo': {},
 'time': 1682962579247}
'''

 

At this stage it is important to introduce the concept of an order Id. This is an important parameter for keeping track of open orders. Bybit will generate a unique order Id when we send an order.  

 

Often we will only want to cancel a single order, rather than all of the open/untriggered orders. Below we create a new order, use a helper function to parse the order-id , then instruct the program to sleep for 20 seconds and cancel the order. 

 

def parse_order_id(response):
    '''
    
   {'retCode': 0,
    'retMsg': 'OK',
    'result': 
        {'orderId': 'bcd70f0c-0310-4c6f-ae82-ed23648d9b50',
         'orderLinkId': ''},
        'retExtInfo': {},
        'time': 1682963779739}
   
   
    Parameters
    ----------
    response : dict
        DESCRIPTION. API response from bybit as shown above in the example 
        response, here we want to extract the orderId 

    Returns
    -------
    orderid as string


    '''
    result = response.get('result')
    #get order id
    oid = result.get('orderId')
    
    return oid




#GTC order
response = session.place_order(
    category='linear',
    symbol = 'DOGEUSDT',
    orderType = 'Limit',
    timeInForce='GTC',
    side = 'Buy',
    qty= '1',
    price = '0.07'
    )

print(response)
print('########################### ')
oid = parse_order_id(response=response)

print(f'Order id from api is {oid}')

import time
time.sleep(20)
session.cancel_order(category='linear',
                     symbol='DOGEUSDT',
                     orderId=oid)





'''
#sending initial order response
{'retCode': 0, 'retMsg': 'OK', 
 'result': {'orderId': '6501cc87-b408-4f33-8542-ad234962c833',
            'orderLinkId': ''}, 'retExtInfo': {}, 'time': 1682963996331}
########################### 

# parsing order id from helper function
Order id from api is 6501cc87-b408-4f33-8542-ad234962c833

# confirmation of closing order

Out[58]: 
{'retCode': 0,
 'retMsg': 'OK',
 'result': {'orderId': '6501cc87-b408-4f33-8542-ad234962c833',
  'orderLinkId': ''},
 'retExtInfo': {},
 'time': 1682964016545}
'''

 

 

 

 

 

 

 

Common Errors Placing Orders Through Pybit 

 

  • Often when making algorithms, prices to be sent to the matching engine are derived through some formula. For example, let's say we are putting an order through for ETHUSDT and the current price is $1800 , we want to put a buy order in 1% below the current price, to achieve this we have some logic similar to 
    
    current_price = 1800.11
    desired = current_price * ( 1  - 0.01)
    
    print(f'desired = {desired}')
    
    '''
    out:
        desired = 1782.1089
    
    '''

     

If we try to place an order on ETH with 4 decimals we will get the following error

 

InvalidRequestError: params error: Price invalid (ErrCode: 10001)

 

This is due to the fact that orders for ETHUSDT must be rounded to the nearest 2 decimal places. If we had of tried to place the order at 1782.11 the order would be submitted successfully.

 

  • The quantity algorithmic traders send to the exchange will often be generated by some formula , so lets take an example where a trader has some function that says an order should be submitted for 1% of the equity of the account. If the account size is for example $100 , this would translate in to a $1 order to be sent to the exchange. For ETH is minimum order size 0.01 which is equal to approx $18 dollars given a current price of 1800. Therefore if we try and send an order for 1/1800 = 0.0005556 we will get the following exception
    InvalidRequestError: The number of contracts exceeds minimum limit allowed (ErrCode: 10001)

     

 

Therefore it is advisable to check carefully the information associated with the instrument you intend on trading and perhaps make some sort of function to round price/qty to prevent unwanted exceptions. The function below will print out information regarding ETHUSDT that you can use for this. 

 

info = session.get_instruments_info(category='linear')

symbols = info.get('result').get('list')


def get_symbol_info(symbol, symbols):
    
    info = [x for x in symbols if x['symbol'] == symbol]
    if info:
        return info[0]

    raise Exception(f'Information for symbol = {symbol} not found')
    
    


print(get_symbol_info('ETHUSDT', symbols))
    
'''
{'symbol': 'ETHUSDT', 
 'contractType':
     'LinearPerpetual',
     'status': 'Trading',
     'baseCoin': 'ETH', 
     'quoteCoin': 'USDT', 
     'launchTime': '1615766400000',
     'deliveryTime': '0', 
     'deliveryFeeRate': '',
     'priceScale': '2', 
     'leverageFilter': {'minLeverage': '1',
                        'maxLeverage': '100.00',
                        'leverageStep': '0.01'},
     'priceFilter': {'minPrice': '0.01', 
                     'maxPrice': '19999.98', 
                     'tickSize': '0.01'},
     'lotSizeFilter': {'maxOrderQty': '1500.00',
                       'minOrderQty': '0.01',
                       'qtyStep': '0.01', 
                       'postOnlyMaxOrderQty': '15000.00'},
     'unifiedMarginTrade': True,
     'fundingInterval': 480, 'settleCoin': 'USDT'}
'''

 


Join the discussion

Share this post with your friends!