Python client for the Alfax Trade API (Binance-compatible).
Project description
alfax Python SDK
First-party Python 3.12+ client for the Alfax Trade API.
The endpoint paths, request/response schemas, authentication, and WebSocket stream
formats are byte-compatible with Binance Futures USDT-M. Existing ccxt,
python-binance, Freqtrade, and Hummingbot strategies work by changing only the
base_url.
Install
pip install alfax
# with dev/test tools:
pip install "alfax[dev]"
Quick start
import os
from alfax import Client
c = Client(
api_key=os.environ["ALFAX_KEY"],
api_secret=os.environ["ALFAX_SECRET"],
base_url="https://api.alfax.trade", # default
)
# Public endpoints (no auth required)
print(c.ping()) # {}
print(c.server_time()) # 1700000000000
info = c.exchange_info()
print(info.symbols[0].symbol) # "BTCUSDT"
# Private endpoints
acc = c.account()
print(acc.totalWalletBalance) # Decimal("10000.00")
print(acc.challenge.stage) # "STAGE_1"
# Place an order (money values are strings — no floats)
order = c.new_order(
symbol="BTCUSDT",
side="BUY",
type="LIMIT",
quantity="0.001",
price="60000.00",
time_in_force="GTC",
)
print(order.orderId, order.status)
# Cancel it
cancelled = c.cancel_order(symbol="BTCUSDT", order_id=order.orderId)
WebSocket streams (async)
import asyncio
from alfax import Client, MarketStream, UserStream
async def main():
# Public market stream
async with MarketStream(["btcusdt@bookTicker"], base_url="wss://api.alfax.trade") as stream:
async for msg in stream:
print(msg)
break
# Private user-data stream
c = Client(api_key="K", api_secret="S")
listen_key = c.new_listen_key()
async with UserStream(listen_key=listen_key, base_url="wss://api.alfax.trade") as stream:
async for event in stream:
print(event)
break
asyncio.run(main())
Environment variables
| Variable | Description | Default |
|---|---|---|
ALFAX_KEY |
API key | — |
ALFAX_SECRET |
API secret | — |
ALFAX_BASE_URL |
REST base URL | https://api.alfax.trade |
Endpoints covered
Public (no auth)
| Method | Path | SDK method |
|---|---|---|
| GET | /fapi/v1/ping |
client.ping() |
| GET | /fapi/v1/time |
client.server_time() |
| GET | /fapi/v1/exchangeInfo |
client.exchange_info() |
| GET | /fapi/v1/ticker/bookTicker |
client.book_ticker() |
| GET | /fapi/v1/klines |
client.klines() |
Private (HMAC-SHA256 signed)
| Method | Path | SDK method |
|---|---|---|
| GET | /fapi/v2/account |
client.account() |
| GET | /fapi/v2/positionRisk |
client.position_risk() |
| GET | /fapi/v1/userTrades |
client.user_trades() |
| POST | /fapi/v1/order |
client.new_order() |
| DELETE | /fapi/v1/order |
client.cancel_order() |
| GET | /fapi/v1/order |
client.get_order() |
| GET | /fapi/v1/openOrders |
client.open_orders() |
| GET | /fapi/v1/allOrders |
client.all_orders() |
| POST | /fapi/v1/leverage |
client.change_leverage() |
| POST | /fapi/v1/listenKey |
client.new_listen_key() |
| PUT | /fapi/v1/listenKey |
client.keepalive_listen_key() |
| DELETE | /fapi/v1/listenKey |
client.close_listen_key() |
Alfax extensions
| Method | Path | SDK method |
|---|---|---|
| GET | /fapi/v1/challenge |
client.challenge_status() |
| GET | /fapi/v1/challenge/rules |
client.challenge_rules() |
| POST | /fapi/v1/challenge/purchase |
client.create_challenge_purchase(product_id=…) |
| GET | /fapi/v1/challenge/lifecycle |
client.challenge_lifecycle(challenge_id) |
| POST | /fapi/v1/payout |
client.request_payout(challenge_id=…, amount=…) |
| GET | /fapi/v1/payout |
client.get_payout(payout_id) |
| POST | /fapi/v1/payout/approve |
client.approve_payout(payout_id) (operator) |
| POST | /fapi/v1/payout/reject |
client.reject_payout(payout_id, reason) (operator) |
Challenge purchase saga
# Trader buys a challenge — a Temporal workflow on the server side
# creates the payment intent, waits for the provider webhook, verifies,
# and mints the challenge.
purchase = c.create_challenge_purchase(product_id="challenge-10k-v1")
print(purchase.workflowId, purchase.status) # purchase-…, "initiated"
# Later (once the workflow has completed) — inspect the minted
# challenge's lifecycle workflow:
state = c.challenge_lifecycle(challenge_id)
print(state.Stage, state.Status) # STAGE_1, ACTIVE
Payouts
# Trader requests a withdrawal from a FUNDED challenge
p = c.request_payout(challenge_id="44444444-…", amount="500")
print(p.payoutId, p.status) # ?, "requested"
# Operator (key must hold "operator" permission)
c.approve_payout(p.payoutId)
# Poll until terminal
state = c.get_payout(p.payoutId)
print(state.Status, state.TxRef) # paid, stub-tx-…
# Over-cap requests are accepted but the workflow rejects them
over = c.request_payout(challenge_id="44444444-…", amount="999999")
c.approve_payout(over.payoutId) # signals approve; cap check still runs first
state = c.get_payout(over.payoutId)
assert state.Status == "rejected"
assert "exceeds available" in state.FailReason
Error handling
from alfax.errors import AlfaxError, AlfaxAPIError, AuthError, RateLimitError, OrderError
try:
order = c.new_order(...)
except OrderError as e:
print(f"Order rejected (code {e.code}): {e.msg}")
except RateLimitError:
print("Rate limited — back off")
except AlfaxAPIError as e:
print(f"API error {e.status}: {e.msg}")
Examples
See examples/ for runnable scripts:
01_ping.py— connectivity and exchange info02_account_balance.py— account balances and positions03_place_limit_order.py— place and cancel a limit order04_stream_book_ticker.py— async WebSocket book ticker stream05_challenge_status.py— challenge status and rules
Development
python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
pytest -q
ruff check .
Money handling
All money fields use decimal.Decimal — never float. Pass prices and
quantities as strings:
# Correct
c.new_order(symbol="BTCUSDT", side="BUY", type="LIMIT",
quantity="0.001", price="60000.00")
# Wrong — will break precision
c.new_order(symbol="BTCUSDT", side="BUY", type="LIMIT",
quantity=0.001, price=60000.00)
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 alfax-0.1.1.tar.gz.
File metadata
- Download URL: alfax-0.1.1.tar.gz
- Upload date:
- Size: 47.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87e66c232a9963e208fe49c7ac11aa73751cf708827833f83de56c13f8518323
|
|
| MD5 |
4363aae329727c8a17712d75a54490e2
|
|
| BLAKE2b-256 |
60ca945c33a644c6b6d05dc7189ac70247fe88597eff3bf9059e60d9fed6aa4f
|
File details
Details for the file alfax-0.1.1-py3-none-any.whl.
File metadata
- Download URL: alfax-0.1.1-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a8ab63e416b7c012b3e87dd3403b505ad3d8f09d3289fe89334cba49b7fdd86
|
|
| MD5 |
d7e2530e27aa8f6fe765ce147942abfb
|
|
| BLAKE2b-256 |
528a70b372f26d869f5955dcb99cdbdff6d591594d3ff77c1f9d3240b1da2c7f
|