Execution·Per Exchange·Intermediate

Binance Order Execution

End-to-end order placement on Binance — authentication, spot vs futures routing, order types, and WebSocket order status updates.

binanceexecutionwebsocket

Execute Trades on Binance

1. Overview

This notebook demonstrates how to execute trades programmatically on Binance using the python-binance library. It covers the full execution workflow — from authentication and market data retrieval to placing market and limit orders.

What You Will Learn

StepDescription
AuthenticationConnect to Binance via API keys
Market DataFetch ticker prices and order book depth
Fee LookupRetrieve your account's trading fee tier
Market OrderPlace an immediate buy/sell order
Limit OrderPlace a price-controlled buy/sell order
Order StatusQuery and display order fill details
Cancel OrderCancel an open limit order

Note: This notebook uses the Binance Testnet by default. Switch testnet=True to testnet=False and supply live API keys to trade on the real exchange. Dummy Mode: Live API calls in this notebook are commented out and replaced with realistic dummy data so it can run end-to-end without a live connection or trading funds. Uncomment the real calls and remove the dummy assignments to run against a real account. The Google Colab userdata dependency has also been replaced with plain placeholder variables so this runs outside Colab too.

2. Dependency Imports

Install and import the required libraries. python-binance is the official community client for the Binance REST and WebSocket APIs.

[9]
# Install the Binance client library if not already present.
!pip install python-binance --quiet
[10]
from binance.client import Client
from binance.enums import (
    SIDE_BUY, SIDE_SELL,
    ORDER_TYPE_MARKET, ORDER_TYPE_LIMIT,
    TIME_IN_FORCE_GTC,
)
import warnings

# Suppress all warnings for cleaner output in a demonstration context.
warnings.filterwarnings("ignore")

3. Authentication

3.1. create_client Function

This function initialises a Binance Client using your API key and secret. Set testnet=True to connect to the Binance Spot Testnet — no real funds are involved.

[11]
def create_client(api_key: str, api_secret: str, testnet: bool = True) -> Client:
    """Create and return an authenticated Binance client.

    Parameters
    ----------
    api_key    : Your Binance API key.
    api_secret : Your Binance API secret.
    testnet    : If True, connects to Binance Testnet (safe for learning).

    Returns
    -------
    Client: Authenticated Binance client instance.
    """
    # Initialise client; testnet flag routes requests to the test environment.
    client = Client(api_key, api_secret, testnet=testnet)
    return client

Explanation of create_client function

This function is crucial for establishing a connection to the Binance API. It takes your api_key and api_secret to authenticate your requests. The testnet parameter is very important: if set to True, it directs your requests to the Binance Spot Testnet, which is a simulated environment. This allows you to test your trading strategies and code without risking real funds. If testnet is set to False, your requests will go to the live Binance exchange, where actual trades will be executed with real money. The function returns an authenticated Client object, which you will use for all subsequent API calls.

[12]
# ── Replace with your own keys ──────────────────────────────────────────────
API_KEY    = "YOUR_BINANCE_API_KEY"
API_SECRET = "YOUR_BINANCE_API_SECRET"
# ────────────────────────────────────────────────────────────────────────────

# Create the client (testnet = True by default).
# Commented out to run locally without network access / real keys
# client = create_client(API_KEY, API_SECRET, testnet=True)

client = None  # Dummy placeholder client (no live connection in this offline run)
print("Binance client created successfully.")
Binance client created successfully.

4. Market Data Functions

4.1. get_ticker_price Function

Fetches the latest best bid/ask prices for a trading symbol from the Binance order book ticker endpoint.

[13]
def get_ticker_price(client: Client, symbol: str) -> dict:
    """Return the best bid and ask price for a symbol.

    Parameters
    ----------
    client : Authenticated Binance client.
    symbol : Trading pair symbol, e.g. 'BTCUSDT'.

    Returns
    -------
    dict: Contains 'bid' and 'ask' as floats.
    """
    # get_orderbook_ticker returns the best bid and ask for the symbol.
    ticker = client.get_orderbook_ticker(symbol=symbol)
    return {
        "bid": float(ticker["bidPrice"]),
        "ask": float(ticker["askPrice"]),
    }

Explanation of get_ticker_price function

This function retrieves real-time market data, specifically the best bid and ask prices for a given trading symbol (e.g., 'BTCUSDT').

  • client.get_orderbook_ticker(symbol=symbol): This is the core API call. It fetches the current best bid (highest price a buyer is willing to pay) and best ask (lowest price a seller is willing to accept) from the order book.
  • ticker['bidPrice'] and ticker['askPrice']: These keys in the API response contain the bid and ask prices, respectively. They are typically strings, so they are converted to float for numerical operations.
  • Return Value: The function returns a dictionary with 'bid' and 'ask' prices, making it easy to access the current market spread.
[14]
# Fetch the current BTCUSDT ticker.
# Commented out to run locally with dummy data
# ticker = get_ticker_price(client, "BTCUSDT")

ticker = {"bid": 60000.00, "ask": 60005.00}  # Dummy data

print(f"BTCUSDT — Bid: {ticker['bid']:,.2f} | Ask: {ticker['ask']:,.2f}")
BTCUSDT — Bid: 60,000.00 | Ask: 60,005.00

4.2. get_order_book Function

Retrieves the top N levels of the order book for a symbol. Useful for understanding available liquidity before placing large orders.

[15]
def get_order_book(client: Client, symbol: str, depth: int = 5) -> dict:
    """Return the top N bid and ask levels from the order book.

    Parameters
    ----------
    client : Authenticated Binance client.
    symbol : Trading pair symbol, e.g. 'BTCUSDT'.
    depth  : Number of price levels to retrieve (default: 5).

    Returns
    -------
    dict: Contains 'bids' and 'asks' as lists of [price, quantity].
    """
    # get_order_book returns a dict with 'bids' and 'asks' lists.
    book = client.get_order_book(symbol=symbol, limit=depth)
    return {
        "bids": [[float(p), float(q)] for p, q in book["bids"]],
        "asks": [[float(p), float(q)] for p, q in book["asks"]],
    }

Explanation of get_order_book function

This function provides a deeper look into the market's liquidity by fetching the top depth levels of the order book for a specified symbol.

  • client.get_order_book(symbol=symbol, limit=depth): This API call retrieves a snapshot of the order book. The limit parameter controls how many bid and ask levels are returned (e.g., depth=5 gets the top 5 bids and asks).
  • book['bids'] and book['asks']: The API response contains two lists: 'bids' (representing buy orders) and 'asks' (representing sell orders). Each element in these lists is a [price, quantity] pair.
  • List Comprehension [[float(p), float(q)] for p, q in book['bids']]: This concise syntax iterates through the raw bid/ask data, converting both price p and quantity q from strings to floats, ensuring they can be used for calculations.
  • Return Value: The function returns a dictionary containing processed lists of 'bids' and 'asks', where each is a list of [price, quantity] pairs, ordered from best to worst price.
[16]
# Display the top 5 levels of the BTCUSDT order book.
# Commented out to run locally with dummy data
# book = get_order_book(client, "BTCUSDT", depth=5)

book = {  # Dummy data
    "bids": [[59999.5, 0.512], [59999.0, 1.204], [59998.5, 0.880], [59998.0, 2.001], [59997.5, 0.330]],
    "asks": [[60000.5, 0.470], [60001.0, 1.150], [60001.5, 0.905], [60002.0, 1.760], [60002.5, 0.290]],
}

print("Top 5 Asks (sell side):")
for price, qty in reversed(book["asks"]):
    print(f"  {price:>12,.2f}  |  {qty:.6f} BTC")

print("-" * 35)
print("Top 5 Bids (buy side):")
for price, qty in book["bids"]:
    print(f"  {price:>12,.2f}  |  {qty:.6f} BTC")
Top 5 Asks (sell side):
     60,002.50  |  0.290000 BTC
     60,002.00  |  1.760000 BTC
     60,001.50  |  0.905000 BTC
     60,001.00  |  1.150000 BTC
     60,000.50  |  0.470000 BTC
-----------------------------------
Top 5 Bids (buy side):
     59,999.50  |  0.512000 BTC
     59,999.00  |  1.204000 BTC
     59,998.50  |  0.880000 BTC
     59,998.00  |  2.001000 BTC
     59,997.50  |  0.330000 BTC

5. Fee Information

5.1. get_trade_fee Function

Queries your account's current maker and taker fee rates for a given symbol. Binance fees depend on your 30-day trading volume and BNB holdings.

[17]
def get_trade_fee(client: Client, symbol: str) -> dict:
    """Return maker and taker fee rates for a symbol.

    Parameters
    ----------
    client : Authenticated Binance client.
    symbol : Trading pair symbol, e.g. 'BTCUSDT'.

    Returns
    -------
    dict: Contains 'maker_fee' and 'taker_fee' as floats (e.g., 0.001 = 0.1%).
    """
    # get_trade_fee returns a list of fee objects; we extract the first match.
    fees = client.get_trade_fee(symbol=symbol)
    fee_info = fees[0] if fees else {"makerCommission": 0.001, "takerCommission": 0.001}
    return {
        "maker_fee": float(fee_info.get("makerCommission", 0.001)),
        "taker_fee": float(fee_info.get("takerCommission", 0.001)),
    }

Explanation of get_trade_fee function

This function is used to retrieve the current trading fee rates for your account on a specific symbol. Trading fees on Binance can vary based on your VIP level, which is determined by your 30-day trading volume and BNB holdings.

  • client.get_trade_fee(symbol=symbol): This API call fetches an array of fee information for the specified trading pair. Even if you only query for one symbol, the API returns a list.
  • fees[0] if fees else {'makerCommission': 0.001, 'takerCommission': 0.001}: This line safely extracts the first (and likely only) fee object from the fees list. It also provides default values (0.1% for both maker and taker) in case the API call returns an empty list or fails to provide specific commission rates, preventing errors.
  • **`fee_info.get(
[18]
# Display the fee rates for BTCUSDT.
# Commented out to run locally with dummy data
# fees = get_trade_fee(client, "BTCUSDT")

fees = {"maker_fee": 0.0002, "taker_fee": 0.0004}  # Dummy data (default Binance VIP0 rates)

print(f"BTCUSDT Fees — Maker: {fees['maker_fee']*100:.4f}% | Taker: {fees['taker_fee']*100:.4f}%")
BTCUSDT Fees — Maker: 0.0200% | Taker: 0.0400%

6. Order Execution Functions

6.1. place_market_order Function

Places a market order on Binance. A market order executes immediately at the best available price. Use this when execution certainty is more important than price precision.

[19]
def place_market_order(client: Client, symbol: str, side: str, quantity: float) -> dict:
    """Place a market order on Binance.

    Parameters
    ----------
    client   : Authenticated Binance client.
    symbol   : Trading pair symbol, e.g. 'BTCUSDT'.
    side     : 'BUY' or 'SELL'.
    quantity : Amount of base asset to buy or sell.

    Returns
    -------
    dict: Full order response from Binance including orderId and fills.
    """
    # Map string side to the Binance enum constant.
    binance_side = SIDE_BUY if side.upper() == "BUY" else SIDE_SELL

    # Submit the market order via the REST API.
    order = client.order_market(
        symbol=symbol,
        side=binance_side,
        quantity=quantity,
    )
    return order
[20]
# Place a market BUY order for 0.001 BTC on the testnet.
# Commented out to run locally with dummy data
# market_order = place_market_order(client, "BTCUSDT", side="BUY", quantity=0.001)

market_order = {  # Dummy data
    "orderId": 100001,
    "symbol": "BTCUSDT",
    "side": "BUY",
    "status": "FILLED",
    "executedQty": "0.00100000",
}

print("Market Order Placed:")
print(f"  Order ID  : {market_order['orderId']}")
print(f"  Symbol    : {market_order['symbol']}")
print(f"  Side      : {market_order['side']}")
print(f"  Status    : {market_order['status']}")
print(f"  Qty Filled: {market_order['executedQty']} BTC")
Market Order Placed:
  Order ID  : 100001
  Symbol    : BTCUSDT
  Side      : BUY
  Status    : FILLED
  Qty Filled: 0.00100000 BTC

6.2. place_limit_order Function

Places a limit order on Binance. A limit order rests in the order book until the market reaches your specified price. Use this when price control matters more than immediate execution.

[21]
def place_limit_order(
    client: Client, symbol: str, side: str, quantity: float, price: float
) -> dict:
    """Place a limit order on Binance (Good-Till-Cancelled).

    Parameters
    ----------
    client   : Authenticated Binance client.
    symbol   : Trading pair symbol, e.g. 'BTCUSDT'.
    side     : 'BUY' or 'SELL'.
    quantity : Amount of base asset to buy or sell.
    price    : Limit price at which to execute.

    Returns
    -------
    dict: Full order response from Binance including orderId.
    """
    # Map string side to the Binance enum constant.
    binance_side = SIDE_BUY if side.upper() == "BUY" else SIDE_SELL

    # Submit the limit GTC order; it will rest in the book until filled or cancelled.
    order = client.order_limit(
        symbol=symbol,
        side=binance_side,
        quantity=quantity,
        price=str(price),
        timeInForce=TIME_IN_FORCE_GTC,
    )
    return order
[22]
# Fetch current ask price and place a passive limit BUY 1% below ask.
# Commented out to run locally with dummy data
# current_ask = get_ticker_price(client, "BTCUSDT")["ask"]
current_ask = ticker["ask"]  # reuse dummy ticker from earlier
limit_price  = round(current_ask * 0.99, 2)   # 1% below current ask.

# limit_order = place_limit_order(
#     client, "BTCUSDT", side="BUY", quantity=0.001, price=limit_price
# )

limit_order = {  # Dummy data
    "orderId": 100002,
    "symbol": "BTCUSDT",
    "side": "BUY",
    "status": "NEW",
}

print("Limit Order Placed:")
print(f"  Order ID   : {limit_order['orderId']}")
print(f"  Symbol     : {limit_order['symbol']}")
print(f"  Side       : {limit_order['side']}")
print(f"  Limit Price: {limit_price:,.2f} USDT")
print(f"  Status     : {limit_order['status']}")
Limit Order Placed:
  Order ID   : 100002
  Symbol     : BTCUSDT
  Side       : BUY
  Limit Price: 59,404.95 USDT
  Status     : NEW

7. Order Management Functions

7.1. get_order_status Function

Retrieves the current status of an order by its ID. Returns key fields such as fill quantity and average price.

[23]
def get_order_status(client: Client, symbol: str, order_id: int) -> dict:
    """Return the status of an existing order.

    Parameters
    ----------
    client   : Authenticated Binance client.
    symbol   : Trading pair symbol, e.g. 'BTCUSDT'.
    order_id : The integer order ID returned when the order was placed.

    Returns
    -------
    dict: Contains 'status', 'executed_qty', and 'avg_price'.
    """
    # Query the order by symbol and ID.
    order = client.get_order(symbol=symbol, orderId=order_id)
    executed_qty = float(order["executedQty"])
    avg_price    = (
        float(order["cummulativeQuoteQty"]) / executed_qty
        if executed_qty > 0 else 0.0
    )
    return {
        "status"      : order["status"],
        "executed_qty": executed_qty,
        "avg_price"   : round(avg_price, 2),
    }
[24]
# Check the status of the limit order we just placed.
# Commented out to run locally with dummy data
# status = get_order_status(client, "BTCUSDT", limit_order["orderId"])

status = {"status": "NEW", "executed_qty": 0.0, "avg_price": 0.0}  # Dummy data

print("Limit Order Status:")
print(f"  Status       : {status['status']}")
print(f"  Executed Qty : {status['executed_qty']} BTC")
print(f"  Avg Fill Price: {status['avg_price']:,.2f} USDT")
Limit Order Status:
  Status       : NEW
  Executed Qty : 0.0 BTC
  Avg Fill Price: 0.00 USDT

7.2. cancel_order Function

Cancels an open order on Binance. Only orders with status NEW or PARTIALLY_FILLED can be cancelled.

[25]
def cancel_order(client: Client, symbol: str, order_id: int) -> dict:
    """Cancel an open order on Binance.

    Parameters
    ----------
    client   : Authenticated Binance client.
    symbol   : Trading pair symbol, e.g. 'BTCUSDT'.
    order_id : The integer order ID to cancel.

    Returns
    -------
    dict: Cancellation confirmation from Binance.
    """
    # Send the cancel request; raises an exception if the order is already filled.
    result = client.cancel_order(symbol=symbol, orderId=order_id)
    return result
[26]
# Cancel the limit order if it is still open.
# Commented out to run locally with dummy data
# cancel_result = cancel_order(client, "BTCUSDT", limit_order["orderId"])

cancel_result = {"orderId": limit_order["orderId"], "status": "CANCELED"}  # Dummy data

print("Order Cancelled:")
print(f"  Order ID : {cancel_result['orderId']}")
print(f"  Status   : {cancel_result['status']}")
Order Cancelled:
  Order ID : 100002
  Status   : CANCELED

8. Execution Decision Framework

Choosing the right order type on Binance depends on your execution goal:

When to Use a Market Order

  • You need to enter or exit immediately (e.g., stop-loss, signal with rapid decay).
  • Order size is small relative to book depth (< 0.5% of visible liquidity).
  • Speed matters more than the exact fill price.

When to Use a Limit Order

  • You want price certainty and can wait for the market to come to you.
  • Order size is large (> 1% of book depth) — avoids eating through multiple levels.
  • Market is volatile (spread > 5 bps) — anchor your execution price.
  • You want to qualify for maker rebates (limit orders that rest in the book).

Binance-Specific Notes

  • Default fee tier: Taker 0.04% / Maker 0.02% (USDT-margined perpetuals).
  • Holding BNB in your account reduces fees by 25%.
  • The TIME_IN_FORCE_GTC flag keeps the order alive until it fills or you cancel it.

Resources

Here are some additional resources to deepen your understanding and further explore the capabilities of the python-binance library and Binance API: