REST API manager for Binance API. Manages weights and credentials simply and securely.
Project description
Panzer
Python library for managing Binance REST API connections with automatic rate limiting and secure credential management.
Features
- Multi-market support: Spot, USDT-M Futures, COIN-M Futures.
- Automatic rate limiting: Fixed-window limiter synchronized with Binance's
X-MBX-USED-WEIGHT-1Mheader. Sleeps before hitting limits instead of getting banned. - Signed requests: HMAC-SHA256 signing for authenticated endpoints (orders, account, trades). Supports GET, POST and DELETE.
- Secure credential storage: AES-128-CBC encryption tied to the machine
identity. Credentials are stored encrypted on disk (
~/.panzer_creds) and decrypted only in memory when needed. - Dynamic weight calculation: Endpoint weights loaded from
weights.pyand adjusted by parameters (e.g.,depthlimit,klineslimit). - Clock synchronization: Estimates server time offset via
/timeendpoint samples. - Centralized error handling:
BinanceAPIExceptionwith parsed error codes and messages. - Rotating file logs: One log file per module in
logs/, with configurable rotation.
Installation
pip install panzer
Or from source:
git clone https://github.com/nand0san/panzer.git
cd panzer
pip install -e .
Requires Python >= 3.11. Runtime dependencies: requests, pycryptodome.
Quick Start — Public Endpoints
from panzer import BinancePublicClient
# Create a client (loads rate limits and syncs clock automatically)
client = BinancePublicClient(market="spot", safety_ratio=0.9)
# Public endpoints — weights are calculated automatically
klines = client.klines("BTCUSDT", "1m", limit=500)
trades = client.trades("BTCUSDT", limit=100)
book = client.depth("BTCUSDT", limit=100)
info = client.exchange_info()
Supported Markets
spot = BinancePublicClient(market="spot") # https://api.binance.com
um = BinancePublicClient(market="um") # https://fapi.binance.com
cm = BinancePublicClient(market="cm") # https://dapi.binance.com
Quick Start — Authenticated Endpoints
BinanceClient extends BinancePublicClient with signed request support.
It manages API keys securely through CredentialManager.
from panzer import BinanceClient
# First run: prompts for api_key and api_secret, encrypts and stores them
# in ~/.panzer_creds. Subsequent runs load them automatically.
client = BinanceClient(market="spot")
# Account info
account = client.account()
print(account["balances"][:3])
# Place an order
order = client.new_order(
symbol="BTCUSDT",
side="BUY",
order_type="LIMIT",
quantity=0.001,
price=50000,
time_in_force="GTC",
)
# Query trades, open orders, cancel
my_trades = client.my_trades("BTCUSDT", limit=10)
open_orders = client.open_orders("BTCUSDT")
cancelled = client.cancel_order("BTCUSDT", order_id=12345)
all_orders = client.all_orders("BTCUSDT", limit=100)
Credential Management
Credentials follow a three-layer lookup: memory -> disk -> prompt.
from panzer import CredentialManager
cm = CredentialManager() # uses ~/.panzer_creds by default
# Add credentials programmatically (auto-detects sensitive names)
cm.add("api_key", "your_key_here") # encrypted on disk
cm.add("api_secret", "your_secret_here") # encrypted on disk
cm.add("market", "spot") # stored in plain text
# Retrieve
key = cm.get("api_key", decrypt=True) # decrypted value
Sensitive names (api_key, api_secret, password, *_id) are automatically
detected and encrypted with AES-128-CBC. The encryption key is derived from the
machine identity (home directory + CPU info), so credentials can only be
decrypted on the machine where they were created. No master password is needed.
The credential file ~/.panzer_creds looks like:
# Archivo de credenciales de Panzer
api_key = "U2FsdGVk..."
api_secret = "Y3J5cHRv..."
market = "spot"
Public Endpoints
All wrapper methods share timeout (seconds, default 10) and return parsed JSON.
| Method | Description | Key parameters |
|---|---|---|
ping() |
Test connectivity | |
server_time() |
Server time (also updates clock sync) | |
exchange_info(symbol=) |
Exchange metadata and rate limits | symbol: optional filter |
klines(symbol, interval) |
Candlestick data | limit (default 500), start_time, end_time (ms) |
trades(symbol) |
Recent trades | limit (default 500) |
agg_trades(symbol) |
Compressed/aggregate trades | limit, from_id, start_time, end_time |
depth(symbol) |
Order book | limit (default 100; affects weight) |
force_orders(symbol=) |
Liquidation orders (futures only) | limit (default 100, max 1000), start_time, end_time |
Derivatives (futures only)
These methods are available only on "um" and "cm" clients. Calling them
on "spot" raises KeyError.
| Method | Description | Key parameters |
|---|---|---|
open_interest(symbol) |
Current open interest | |
open_interest_hist(symbol, period) |
Historical aggregated OI | period ("5m".."1d"), limit (default 30, max 500), start_time, end_time |
premium_index(symbol=) |
Mark price, index price, funding rate | Returns dict (UM + symbol) or list (CM, or all symbols) |
funding_rate_history(symbol=) |
Historical funding rates | limit (default 100, max 1000), start_time, end_time |
funding_info() |
Funding config for all contracts | Interval, cap, floor per symbol |
Range Pagination
Fetch complete time ranges with automatic pagination and deduplication.
from panzer import BinancePublicClient
client = BinancePublicClient(market="um")
# All 1m candles for the last 24h — automatically paginated
import time
end = int(time.time() * 1000)
start = end - 86_400_000
klines = client.klines_range("BTCUSDT", "1m", start, end)
aggs = client.agg_trades_range("BTCUSDT", start, end)
| Method | Description | Pagination strategy |
|---|---|---|
klines_range(symbol, interval, start_time, end_time) |
All klines in range | 1000-candle blocks, parallel |
agg_trades_range(symbol, start_time, end_time) |
All aggTrades in range | 1-hour chunks, parallel + sub-pagination |
Bulk / Parallel Requests
Fetch data from multiple symbols simultaneously. Panzer pre-calculates weights, splits into batches that fit the rate limit window, and fires requests in parallel using a thread pool.
symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "XRPUSDT", "BNBUSDT"]
# All return dict[str, data] keyed by symbol
all_trades = client.bulk_trades(symbols, limit=500, max_workers=10)
all_klines = client.bulk_klines(symbols, "1h", limit=100, max_workers=10)
all_books = client.bulk_depth(symbols, limit=100, max_workers=10)
all_agg = client.bulk_agg_trades(symbols, limit=500, max_workers=10)
# Generic: mix different endpoints in one parallel call
results = client.parallel_get([
("/api/v3/trades", {"symbol": "BTCUSDT", "limit": 100}),
("/api/v3/klines", {"symbol": "ETHUSDT", "interval": "1h", "limit": 24}),
("/api/v3/depth", {"symbol": "SOLUSDT", "limit": 20}),
], max_workers=5)
| Method | Description | Weight per call |
|---|---|---|
bulk_trades(symbols) |
Recent trades, N symbols | 25 |
bulk_klines(symbols, interval) |
Candlesticks, N symbols | 2 |
bulk_depth(symbols) |
Order books, N symbols | 5-250 |
bulk_agg_trades(symbols) |
Aggregate trades, N symbols | 4 |
parallel_get(jobs) |
Arbitrary mixed endpoints | varies |
Authenticated Endpoints
Available via BinanceClient. All require valid API credentials.
| Method | HTTP | Description | Key parameters |
|---|---|---|---|
account() |
GET | Account info (balances, permissions) | recv_window |
my_trades(symbol) |
GET | Own trade history | limit, from_id |
new_order(symbol, side, order_type) |
POST | Place an order | quantity, price, time_in_force, **extra |
cancel_order(symbol) |
DELETE | Cancel an order | order_id or orig_client_order_id |
open_orders(symbol=) |
GET | Current open orders | symbol (optional) |
all_orders(symbol) |
GET | All orders (open + closed) | limit, order_id |
Using signed_request() for any authenticated endpoint
# Generic signed request for endpoints without a wrapper
data = client.signed_request(
"GET",
"/api/v3/myTrades",
params=[("symbol", "BTCUSDT"), ("limit", 10)],
)
Using get() for any public endpoint
# Spot 24h ticker — no wrapper needed
ticker = client.get("/api/v3/ticker/24hr", params={"symbol": "BTCUSDT"})
Weights are calculated automatically from weights.py tables. If an endpoint
is not in the table, it defaults to weight 1. You can also override manually:
data = client.get("/api/v3/depth", params={"symbol": "BTCUSDT", "limit": 5000}, weight=250)
Rate Limiting
The limiter works transparently. When accumulated weight approaches the limit
(controlled by safety_ratio), the client sleeps until the next minute window.
This means your code may block for up to ~60 seconds — it won't raise an error,
it just waits.
# safety_ratio=0.9 means sleep when reaching 90% of the limit (e.g., 5400/6000 for spot)
client = BinancePublicClient(market="spot", safety_ratio=0.9)
# Inspect limiter state at any time
print(client.limiter.used_local) # Weight used in current window
print(client.limiter.last_server_used) # Last value from X-MBX-USED-WEIGHT-1M
print(client.limiter.max_per_minute) # Limit from /exchangeInfo (e.g., 6000)
The limiter synchronizes with Binance's X-MBX-USED-WEIGHT-1M response header
after every request, so it stays accurate even if other processes share the same IP.
Clock Synchronization
ensure_time_offset_ready() calls /time multiple times to estimate the offset
between your local clock and Binance's server. This is recommended before loops
that run many requests, because the limiter uses server time to align with
Binance's rate limit windows.
client.ensure_time_offset_ready(min_samples=3)
# After sync, you can get the estimated server time
server_ms = client.now_server_ms()
If you skip this step, the limiter still works — it just uses your local clock, which may be slightly off from Binance's window boundaries.
Error Handling
from panzer.errors import BinanceAPIException
try:
client.klines("INVALID", "1m")
except BinanceAPIException as e:
print(e.status_code) # 400
print(e.error_payload.code) # -1121
print(e.error_payload.msg) # "Invalid symbol."
All API errors (HTTP 4xx/5xx and Binance-specific error codes) raise
BinanceAPIException with the parsed error payload when available.
The library does not retry failed requests automatically. If a request fails, the exception is raised immediately. Retry logic is left to the caller.
Logging
Each module writes to its own rotating log file in logs/:
logs/
binance_public_spot.log
binance_client_spot.log
binance_fixed_limiter.log
binance_signer.log
credentials.log
errors.log
http.log
Logs also print to stdout. The logs/ directory is created automatically
and is gitignored.
Architecture
panzer/
__init__.py # Exports BinanceClient, BinancePublicClient, CredentialManager
crypto.py # AesCipher (AES-128-CBC, machine-derived key)
credentials.py # CredentialManager (memory -> disk -> prompt)
errors.py # BinanceAPIException, handle_response
log_manager.py # LogManager (rotating file + stdout)
time_sync.py # TimeOffsetEstimator
exchanges/binance/
client.py # BinanceClient (authenticated, extends BinancePublicClient)
config.py # Parses /exchangeInfo rate limits
public.py # BinancePublicClient (public endpoints only)
signer.py # BinanceRequestSigner (HMAC-SHA256)
weights.py # Endpoint weight tables per market
http/
client.py # Low-level HTTP + header sync (public & signed)
rate_limit/
binance_fixed.py # Fixed-window rate limiter
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file panzer-2.5.1.tar.gz.
File metadata
- Download URL: panzer-2.5.1.tar.gz
- Upload date:
- Size: 53.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69e81e99fdf766c30e9d5fd1a45ea92565e206f3056210b8358e75d80d2a1cf7
|
|
| MD5 |
4c5cd9f604c359a9888f349803ee5bf5
|
|
| BLAKE2b-256 |
391c17a0011d25d7a5cca48289f9b96ac2ea84d292f46786a14516caa6149f00
|
File details
Details for the file panzer-2.5.1-py3-none-any.whl.
File metadata
- Download URL: panzer-2.5.1-py3-none-any.whl
- Upload date:
- Size: 44.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f67b1a4fa05a00be5eed3e4c6db994ee09f6749de9c95c03ebbd5675dcdae4fb
|
|
| MD5 |
524466b9739e7bea959c26f7830dcec4
|
|
| BLAKE2b-256 |
eba9761aa33bd357043459854d5786261e4ca89e4290881f8b3e656874d58a63
|