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 and European Options (EAPI).
  • 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

European Options (EAPI) use a dedicated client that reuses the same plumbing (HTTP, rate limiting, clock sync):

from panzer import BinanceOptionsClient

opt = BinanceOptionsClient()                 # https://eapi.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

European Options (EAPI)

Available on BinanceOptionsClient (market is fixed to "eapi"). Returns raw parsed JSON from Binance — implied volatility and greeks are computed by Binance, no Black-Scholes needed on the client side.

Method Description Key parameters
exchange_info_options() Contract catalog (strikes, expiries, CALL/PUT, status)
mark(symbol=) Mark price, implied volatility and greeks (delta/gamma/theta/vega) Returns a list; omit symbol for all contracts
index(underlying) Underlying index price underlying: e.g. "BTCUSDT"
ticker(symbol=) 24h stats per contract Omit symbol for all contracts
depth(symbol) Order book (inherited) limit (default 100; affects weight)
klines(symbol, interval) Candlestick data (inherited) limit, start_time, end_time
open_interest(underlying_asset, expiration) Aggregated OI per expiry underlying_asset: e.g. "BTC"; expiration: YYMMDD (e.g. "260626")
exercise_history(underlying=) Past exercises/settlements limit (default 100), start_time, end_time
from panzer import BinanceOptionsClient

opt = BinanceOptionsClient()
info  = opt.exchange_info_options()
marks = opt.mark("BTC-260626-140000-C")      # markIV, delta, gamma, theta, vega
oi    = opt.open_interest("BTC", "260626")

Caveat for analysis: in mark(), bidIV/askIV use -1 as a sentinel meaning "no quote". Filter those out before aggregating; markIV is always >= 0.

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.6.0.tar.gz (61.0 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.6.0-py3-none-any.whl (50.4 kB view details)

Uploaded Python 3

File details

Details for the file panzer-2.6.0.tar.gz.

File metadata

  • Download URL: panzer-2.6.0.tar.gz
  • Upload date:
  • Size: 61.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for panzer-2.6.0.tar.gz
Algorithm Hash digest
SHA256 db28128081075e042cfb243b62f66944144db8397a8987abd640f9a315ea17c1
MD5 fa8130d5529f5d171a57a470b6ce04c3
BLAKE2b-256 7408a956bbe06bf48781e5ca3ee0f4b5b1b7fb86aa75c4920ff68570434e9e47

See more details on using hashes here.

File details

Details for the file panzer-2.6.0-py3-none-any.whl.

File metadata

  • Download URL: panzer-2.6.0-py3-none-any.whl
  • Upload date:
  • Size: 50.4 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.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 146bc007017ff8aea99d822783b096b2dd301a25a747052d24d55067bd60823f
MD5 a92362668da13070248696e483c84b36
BLAKE2b-256 66c67615b6062f1624485d8365a6b0b2cc3d7e7185fa0fd69cfbdce5ab781939

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