Skip to main content

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-1M header. 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.py and adjusted by parameters (e.g., depth limit, klines limit).
  • Clock synchronization: Estimates server time offset via /time endpoint samples.
  • Centralized error handling: BinanceAPIException with 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

panzer-2.5.1.tar.gz (53.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

panzer-2.5.1-py3-none-any.whl (44.9 kB view details)

Uploaded Python 3

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

Hashes for panzer-2.5.1.tar.gz
Algorithm Hash digest
SHA256 69e81e99fdf766c30e9d5fd1a45ea92565e206f3056210b8358e75d80d2a1cf7
MD5 4c5cd9f604c359a9888f349803ee5bf5
BLAKE2b-256 391c17a0011d25d7a5cca48289f9b96ac2ea84d292f46786a14516caa6149f00

See more details on using hashes here.

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

Hashes for panzer-2.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f67b1a4fa05a00be5eed3e4c6db994ee09f6749de9c95c03ebbd5675dcdae4fb
MD5 524466b9739e7bea959c26f7830dcec4
BLAKE2b-256 eba9761aa33bd357043459854d5786261e4ca89e4290881f8b3e656874d58a63

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page