In this article, we’ll walk through a small Python utility that:
Connects to Binance (spot testnet or live)
Fetches and caches market metadata for all trading pairs
Extracts and prints the full set of trading rules (“filters”) for a single symbol
This kind of script is invaluable when you’re building an automated trading bot or simply need to validate orders client-side against Binance’s on-exchange constraints. ## 1. Imports & Configuration
import os
import math
import time
import logging
from binance.client import Client
from binance.exceptions import BinanceAPIException, BinanceOrderException
Standard libraries (os
,
math
, time
, logging
) handle
environment variables, rounding, timing and log output.
Client
is the official Binance
connector.
Exceptions let us catch API- or order-specific errors gracefully.
Next up, your API credentials and a flag for testnet vs. live:
= "<YOUR_API_KEY>"
API_KEY = "<YOUR_API_SECRET>"
API_SECRET = True # switch to False for real trading USE_TESTNET
Replace those placeholder strings (or load from environment variables) before running. ## 2. Logging Setup
logging.basicConfig(=logging.INFO,
levelformat="%(asctime)s - %(levelname)s - %(message)s"
)
A consistent log format helps you trace exactly what’s happening and when. You’ll see timestamps, log levels, and your own messages in the console. ## 3. Initializing the Binance Client
try:
= Client(API_KEY, API_SECRET, testnet=USE_TESTNET)
client
client.ping()f"Client initialized (testnet={USE_TESTNET})")
logging.info(= client.get_server_time()
server_time f"Server time: {server_time['serverTime']}")
logging.info(except BinanceAPIException as e:
f"Binance API error during init: {e}")
logging.error(= None
client except Exception as e:
f"Unexpected error during init: {e}")
logging.error(= None client
Instantiate the client with your keys and
testnet
flag.
Ping the server to verify connectivity.
Fetch server time as a sanity check—if your local clock is wildly skewed, signed requests may be rejected.
Error handling ensures you don’t proceed without
a working client
.
= {}
exchange_info_cache
if client:
try:
= client.get_exchange_info()
info for s in info.get("symbols", []):
"symbol"]] = s
exchange_info_cache[s["Fetched and cached exchange info for all symbols.")
logging.info(except BinanceAPIException as e:
f"Binance API error fetching exchange info: {e}")
logging.error(except Exception as e:
f"Error fetching exchange info: {e}") logging.error(
Why cache?
Calling get_exchange_info()
on each order is wasteful.
Markets rarely change filters mid-day, so we fetch once and
reuse.
Data structure:
exchange_info_cache
maps string symbols
(e.g. "BTCUSDT"
) to their full info dict.
# ORDER PARAMETERS (edit these)
= "BTCUSDT"
symbol = 0.001
quantity = None # None ⇒ MARKET order
price
= exchange_info_cache.get(symbol)
symbol_info if not symbol_info:
f"Symbol {symbol} not found in cache.")
logging.error(# Here you’d re-fetch or abort
symbol
chooses which market to
inspect.
quantity
and
price
are placeholders for trading logic;
here we only need symbol_info
.
import json
def print_symbol_info(info: dict):
"""
Nicely prints all the fields of a Binance symbol-info dict.
"""
# Basic summary
print(f"Symbol: {info.get('symbol')}")
print(f"Status: {info.get('status')}")
print(f"Base / Quote Asset: {info.get('baseAsset')} / {info.get('quoteAsset')}")
print(f"Precisions: base={info.get('baseAssetPrecision')}, "
f"quote={info.get('quoteAssetPrecision')}")
print(f"Spot / Margin: {info.get('isSpotTradingAllowed')} / {info.get('isMarginTradingAllowed')}")
print(f"Order Types: {', '.join(info.get('orderTypes', []))}")
print(f"Iceberg / OCO / OTO allowed? {info.get('icebergAllowed')}, "
f"{info.get('ocoAllowed')}, {info.get('otoAllowed')}")
print(f"Self-Trade Prevention (default): {info.get('defaultSelfTradePreventionMode')}")
print(f"Allowed STP modes: {', '.join(info.get('allowedSelfTradePreventionModes', []))}")
print()
# Detailed filters
print("Filters:")
for f in info.get("filters", []):
= f.pop("filterType", "<unknown>")
ft = ", ".join(f"{k}={v}" for k, v in f.items())
params print(f" • {ft}: {params}")
print()
Summary section pulls out the top-level fields you most frequently need: status, assets, precisions, allowed order types, and advanced features.
Filters section iterates through every rule Binance enforces client-side:
PRICE_FILTER (price bounds & tick size)
LOT_SIZE (quantity bounds & step size)
ICEBERG_PARTS, MARKET_LOT_SIZE, TRAILING_DELTA, etc.
By printing them in a bullet list, you instantly see every constraint you must honor before sending an order.
Finally, we invoke:
print_symbol_info(symbol_info)
and watch as our console floods with a clear, human-readable summary of BTCUSDT’s every trading rule. Armed with this information, you can:
Client-side validate price and quantity to avoid “filter violation” errors.
Round to the correct tick/step sizes.
Respect minimum notional, maximum open orders, and advanced order flags.
Choose the right self-trade prevention mode.
We will get the following output:
Symbol: BTCUSDT
Status: TRADING
Base / Quote Asset: BTC / USDT
Precisions: base=8, quote=8
Spot / Margin: True / False
Order Types: LIMIT, LIMIT_MAKER, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT
Iceberg / OCO / OTO allowed? True, True, True
Self-Trade Prevention (default): EXPIRE_MAKER
Allowed STP modes: NONE, EXPIRE_TAKER, EXPIRE_MAKER, EXPIRE_BOTH, DECREMENT
Filters:
• PRICE_FILTER: minPrice=0.01000000, maxPrice=1000000.00000000, tickSize=0.01000000
• LOT_SIZE: minQty=0.00001000, maxQty=9000.00000000, stepSize=0.00001000
• ICEBERG_PARTS: limit=10
• MARKET_LOT_SIZE: minQty=0.00000000, maxQty=102.70390393, stepSize=0.00000000
• TRAILING_DELTA: minTrailingAboveDelta=10, maxTrailingAboveDelta=2000, minTrailingBelowDelta=10, maxTrailingBelowDelta=2000
• PERCENT_PRICE_BY_SIDE: bidMultiplierUp=5, bidMultiplierDown=0.2, askMultiplierUp=5, askMultiplierDown=0.2, avgPriceMins=5
• NOTIONAL: minNotional=5.00000000, applyMinToMarket=True, maxNotional=9000000.00000000, applyMaxToMarket=False, avgPriceMins=5
• MAX_NUM_ORDERS: maxNumOrders=200
• MAX_NUM_ALGO_ORDERS: maxNumAlgoOrders=5
Here’s a field‐by‐field rundown of everything, why it matters, and how you’d use it in your trading logic: ## Market Basics
Symbol: BTCUSDT
The trading pair: you buy/sell BTC using
USDT. Every API call needs this exact string.
Status: TRADING
Means new orders are accepted. If you see other statuses
(BREAK
, HALT
, etc.), you must pause or reject
orders for that symbol.
Base / Quote Asset: BTC / USDT
Base (BTC
): the asset you’re buying
or selling.
Quote (USDT
): the currency you pay
or receive.
Precisions: base=8
,
quote=8
Base precision 8 → you can specify quantities down to 10⁻⁸ BTC.
Quote precision 8 → prices down to 10⁻⁸ USDT.
Enforce these by rounding (or flooring) your quantities/prices to these decimal places before sending orders.
Spot / Margin: True / False
Spot trading is allowed (you can place normal buy/sell orders).
Margin trades are not allowed on this pair (you cannot borrow funds here).
LIMIT, LIMIT_MAKER, MARKET,
STOP_LOSS, STOP_LOSS_LIMIT,
TAKE_PROFIT, TAKE_PROFIT_LIMIT
LIMIT: specify exact price.
LIMIT_MAKER: only post as maker (won’t eat liquidity).
MARKET: execute immediately at best price.
STOP_LOSS / TAKE_PROFIT: trigger a market order when price crosses your stop/target.
STOP_LOSS_LIMIT / TAKE_PROFIT_LIMIT: trigger a limit order at your stop/target price.
Use this list to validate your UI or reject unsupported order types in your bot.
Iceberg allowed? True
You can hide part of a large order behind smaller visible
“slices.”
OCO allowed? True
One‐Cancels‐the‐Other pairs a stop and a limit in a single
request.
OTO allowed? True
One‐Triggers‐the‐Other lets you chain orders (e.g. open a position, then
automatically place a stop and take‐profit).
If your strategy uses any of these, you’re good to go on BTCUSDT.
Default Mode: EXPIRE_MAKER
If your own orders would match, the maker side (the order resting in the
book) will expire.
Allowed Modes:
NONE
, EXPIRE_TAKER
, EXPIRE_MAKER
,
EXPIRE_BOTH
, DECREMENT
You can override the default in each order to control how (or if) Binance prevents you from filling your own orders.
Before you submit an order, you must ensure it obeys all of these constraints, or Binance will reject it:
PRICE_FILTER
minPrice=0.01, maxPrice=1,000,000
tickSize=0.01
Prices must sit in [0.01, 1 000 000] USDT and be multiples of
0.01.
LOT_SIZE
minQty=0.00001, maxQty=9 000
stepSize=0.00001
Quantities (for limit orders) must be between 0.00001 BTC and 9 000 BTC,
in increments of 0.00001 BTC.
ICEBERG_PARTS
MARKET_LOT_SIZE
TRAILING_DELTA
minTrailingAboveDelta=10, maxTrailingAboveDelta=2000
minTrailingBelowDelta=10,
maxTrailingBelowDelta=2000
Sets how far (in price-ticks) your trailing-stop must trail above/below
the market price.
PERCENT_PRICE_BY_SIDE
bidMultiplierUp=5, bidMultiplierDown=0.2
askMultiplierUp=5, askMultiplierDown=0.2
avgPriceMins=5
Used for conditional orders that trigger based on a percentage offset
from the 5-minute average price.
NOTIONAL
minNotional=5 USDT, applyMinToMarket=True
maxNotional=9 000 000 USDT,
applyMaxToMarket=False
Total order value (qty × price
) must be ≥ 5 USDT. The max
constraint applies only to limit orders.
MAX_NUM_ORDERS
MAX_NUM_ALGO_ORDERS
Your bot’s order-validation routine should:
Round/floor price to nearest
tickSize
and quantity to nearest
stepSize
.
Check minPrice ≤ price ≤ maxPrice
(limit orders) and minQty ≤ qty ≤ maxQty
.
Compute notional = qty × price
(or
fetch market price for market orders) and ensure it meets
NOTIONAL
bounds.
Verify you haven’t exceeded
MAX_NUM_ORDERS
or
MAX_NUM_ALGO_ORDERS
.
Respect trading permissions (spot vs. margin) and allowed order-type flags.
Specify a valid STP mode if self-trade prevention matters for your strategy.
By enforcing these rules client-side, you’ll avoid filter-violation errors and keep your algorithmic trading smooth and reliable. ## Conclusion
This article encapsulates a best practice to fetch and cache exchange metadata, and validate every order against those rules client-side.
Such upfront validation leads to smoother algorithmic trading, fewer
surprise API rejections, and a clearer understanding of your market’s
boundaries. Feel free to extend this script with your own
check_order_limits()
function (we covered a more complete
version earlier) to enforce every rule automatically before you ever hit
Binance’s order endpoints.