This release is a pre-release and may not be stable for production use.
Open Binancian Futures
A Python framework for creating, backtesting, and deploying automated trading bots on Binance USDⓈ-M Futures.
Features
- Live Trading – Monitor multiple symbols and execute trades automatically
- Backtesting – Run deterministic backtests on Binance Vision archives or injected historical data
- Webhooks – Real-time notifications via Slack/Discord
Prerequisites
- Python 3.12+
- Binance API keys with
Enable Futurespermission for live trading (Get keys)
Getting Started
1. Install the package
pip install open-binancian-futures
2. Create a .env file (see .env.example)
| Variable | Required | Default | Description |
|---|---|---|---|
API_KEY |
Yes* | - | Binance API key (mainnet) |
API_SECRET |
Yes* | - | Binance API secret (mainnet) |
SYMBOLS |
No | BTCUSDT |
comma-separated list of symbols to trade |
INTERVALS |
No | 1d |
Comma-separated candle intervals (1m, 5m, 1h, ...). First entry is the primary interval |
LEVERAGE |
No | 1 |
Leverage multiplier (1 ~ 125) |
SIZE |
No | 0.05 |
Trade size per order (e.g., 0.05 = 5% of balance) |
* For testnet, use API_KEY_TEST and API_SECRET_TEST instead
View All Configuration Options
| Variable | Type | Default | Description |
|---|---|---|---|
IS_TESTNET |
bool | false |
Use testnet (true/false) |
GTD_NLINES |
number | - | Candles to hold open orders (GTC if not set) |
TIMEZONE |
string | UTC |
Timezone (e.g., Asia/Seoul) |
WEBHOOK_URL |
string | - | Slack/Discord webhook for notifications |
| Backtesting | |||
IS_BACKTEST |
bool | false |
Enable backtest mode |
BALANCE |
number | 100 |
Initial backtest balance |
INDICATOR_INIT_SIZE |
number | 200 |
Candles for indicator warm-up |
BACKTEST_START_DATE |
string | - | Inclusive UTC start date for Vision backtests |
BACKTEST_END_DATE |
string | - | Inclusive UTC end date for Vision backtests |
BACKTEST_DATA_DIR |
path | cache | Binance Vision ZIP archive cache directory |
3. Create your strategy
Extend the Strategy class and implement load(), run(), and run_backtest() functions:
load(DataFrame): Loads technical indicators you want to userun(str, str): Executes your trading logicrun_backtest(str, str, int): Backtesting logic (optional)
Example Strategy
import asyncio
import pandas_ta as ta
from binance_sdk_derivatives_trading_usds_futures.rest_api.models import (
NewOrderSideEnum,
NewOrderTimeInForceEnum,
)
from open_binancian_futures.types import OrderType
from open_binancian_futures.strategy import Strategy
from open_binancian_futures.constants import settings
from open_binancian_futures.utils import fetch
from pandas import DataFrame
from typing import cast, override
class MyStrategy(Strategy):
@override
def load(self, df: DataFrame) -> DataFrame:
"""Add technical indicators to the dataframe"""
# You can use `pandas_ta` to add technical indicators
df["RSI_14"] = ta.rsi(df["Close"], length=14)
return df
@override
async def run(self, symbol: str, interval: str) -> None:
"""Execute your trading logic"""
latest = self.indicators[symbol][interval].iloc[-1] # Access to the latest candle
entry_price = latest["Close"]
if latest["RSI_14"] < 30:
async with cast(asyncio.Lock, self.lock):
if entry_quantity := self.exchange_info.to_entry_quantity(
symbol=symbol,
entry_price=entry_price,
balance=self.balance,
):
fetch(
self.client.rest_api.new_order,
symbol=symbol,
side=NewOrderSideEnum.BUY,
type=OrderType.LIMIT.value,
price=float(entry_price),
quantity=float(entry_quantity),
time_in_force=NewOrderTimeInForceEnum.GTC,
)
@override
async def run_backtest(self, symbol: str, interval: str, index: int) -> None:
"""Backtesting logic (optional)"""
...
4. Deterministic backtesting
The package backtester accepts a DataFrame, a CSV/Parquet path, or a custom
HistoricalDataSource. The input must contain Open_time, Open, High,
Low, and Close; add Symbol when more than one symbol is present.
Numeric Open_time values follow Binance's epoch-millisecond convention.
from open_binancian_futures import (
BacktestConfig,
Backtesting,
DataFrameDataSource,
MarketExecutionPolicy,
OrderType,
PositionSide,
)
runner = Backtesting(
strategy=my_strategy,
data_source=DataFrameDataSource(candles, interval="1h"),
config=BacktestConfig(
initial_balance=100.0,
leverage=1,
warmup_bars=0,
interval="1h",
market_execution=MarketExecutionPolicy.NEXT_OPEN,
),
)
result = runner.run()
print(result.summary.format())
print(result.summary.trades)
print(result.equity_curve)
Backtesting.run() returns the BacktestRunResult; existing callers that
only use the side effects can continue to ignore the return value.
CsvDataSource(path, symbol="ETHUSDT", interval="1h") and
ParquetDataSource(...) provide the file-backed equivalents. Injected data
does not create a Binance client or make a network request. The default
Backtesting() path requires BACKTEST_START_DATE and BACKTEST_END_DATE
and loads candles from Binance Vision public archives without creating an
authenticated Binance client. Backtest strategies should use the framework
order helpers; live REST operations require an explicitly supplied client. When
BacktestConfig.interval is omitted, a direct DataFrame/CSV/Parquet source
uses its declared interval; otherwise the configured interval takes
precedence. Every symbol must expose that selected interval; inconsistent
symbol-specific interval keys are rejected instead of being relabeled.
BinanceVisionDataSource resolves monthly USDⓈ-M kline ZIP files first and
falls back to daily files when a monthly archive is unavailable. Archives are
cached locally and are never silently replaced by REST candle data. The
date-only end_date/--end-date includes the entire UTC calendar day. The
INDICATOR_INIT_SIZE setting is loaded as warm-up context before the requested
start date, so the requested dates describe the evaluated period rather than
being consumed by indicator initialization. The
old BinanceHistoricalDataSource remains available only as an explicit
compatibility source for callers migrating from the previous engine.
When an execution interval is explicitly configured, the source must provide
that interval; the runner raises instead of evaluating another interval under
the wrong label.
The default engine evaluates completed candles in UTC chronological order.
Existing orders are eligible on the current candle, while newly created
LIMIT, STOP, TAKE_PROFIT, and TAKE_PROFIT_MARKET orders wait until the
next candle. A newly created MARKET order fills at the next available
evaluation candle open by default (MarketExecutionPolicy.NEXT_OPEN).
MarketExecutionPolicy.CLOSE explicitly enables same-close market fills. Limit and STOP_MARKET gaps fill at the candle open; a
STOP_LIMIT gaps remain pending until their limit can execute, while the
triggered limit remains active across later candles. Intrabar triggers fill at
the configured price, and Stop Loss wins over Take Profit when both are
reached. Multiple crossed partial exits are processed in that same
deterministic priority order. Costs, slippage, and funding are zero by
default. OHLC data cannot reveal the order of prices within a candle, so
these priority and gap rules are modeling assumptions, not tick-level fills.
Open positions are realized at each symbol's final evaluated close with
Trade.exit_reason == "end_of_backtest". Last-bar next-open market orders
remain unfilled and are canceled with their reserved margin released.
Partial exit orders close only their requested quantity. A custom CostModel
is applied to both entry and exit fills.
The returned BacktestRunResult exposes by_symbol, summary,
equity_curve, and final_balance. Metrics use each symbol's actual
evaluated-bar count, classify zero PNL as break-even, and keep the loss sign
negative in expectancy calculations.
For strategy code that should work without Binance SDK enums, use the domain adapter:
await self.submit_order(
self.order_intent(
"ETHUSDT", PositionSide.BUY, OrderType.LIMIT,
price=99.0, quantity=1.0,
)
)
OrderIntent supports managed live TRAILING_STOP_MARKET orders with
activation_price and callback_rate. With a managed gateway, replace
synchronous set_trailing_stop(...) calls with await submit_order(...); see
managed helper migration.
The backtest gateway does not implement these extended trailing-stop and
close-position intents. Managed market orders use an explicit reference price
or the runtime’s latest indicator close for sizing.
Existing run_backtest(symbol, interval, index) implementations and direct
OrderList.open_order(...) calls remain supported. The latter are discovered
by the runner after each callback; new non-market orders still follow the
same-candle deferral rule. Strategy.open_order(...) remains available and
routes through the managed gateway when one is configured.
Migration: completed data and next-open execution
The market default changed from CLOSE to NEXT_OPEN. A signal at a close of
100 followed by an open of 110 now enters at 110. To reproduce the former
market-price assumption, pass BacktestConfig(market_execution=MarketExecutionPolicy.CLOSE).
This setting does not restore the former callback-before-fill ordering.
Existing orders expire and fill for all symbols before close callbacks start. Close-time cancellation cannot undo a fill earlier in that candle. Callbacks run in sorted symbol order, giving reproducible shared-balance priority. Entry-fill hooks run after this accounting phase, see only data completed before that candle, and their new orders wait for a later candle.
Every strategy data view is a fresh copy containing only completed candles
across all symbols and intervals. The engine uses Close_time when supplied;
otherwise it infers completion from the interval, including calendar months
for 1M. Constructors receive only the warm-up prefix. Custom load() is
recomputed on each bounded view; it must handle empty frames and should be
free of order submissions and other side effects. This correctness-first
approach can cost more time than computing indicators once over all data.
Keep timestamps and rows intact when adding indicator columns.
Replace full-history preprocessing or indexing beyond the current bar with calculations over the supplied prefix:
def load(self, df):
return df.assign(mean_close=df["Close"].rolling(20).mean())
async def run_backtest(self, symbol, interval, index):
visible = self.indicators[symbol][interval]
current = visible.iloc[index] # index == len(visible) - 1
hourly = self.indicators[symbol]["1h"]
if hourly.empty: # no completed hourly candle yet
return
# Make decisions from current and hourly.iloc[-1].
Plain run_backtest(symbol, index) callbacks remain supported. Strategy or
indicator exceptions fail the run; the CLI exits with a nonzero status.
5. Running
open-binancian-futures my_strategy.py
You can override environment variables from the command line:
open-binancian-futures --backtest \
--symbols ETHUSDT --intervals 1h,4h \
--start-date 2024-01-01 --end-date 2024-03-31 \
--data-dir .cache/binance-vision \
my_strategy.py
--backtest and --live remain the mode switches. In a Vision backtest,
the first value in --intervals is the execution interval and the remaining
values are loaded as indicator context. Live trading uses the supervised
managed runtime described below.
License
MIT License - see LICENSE for details.
Disclaimer
USE AT YOUR OWN RISK.
The author and contributors are not responsible for any financial losses or damages arising from the use of this software. Cryptocurrency trading involves significant risk. Always test thoroughly and trade responsibly.
Managed live recovery
Live trading now starts through a supervised runtime with a durable SQLite order
journal. Keep .obf-runtime/orders.sqlite3 across restarts, or set
OBF_RUNTIME_PATH / LiveTrading(journal_path=...) to a stable local path. Startup
adopts existing target-symbol orders and positions; hedge mode is rejected before
changes. Normal shutdown preserves exchange orders and positions.
Unknown placement outcomes hold the affected symbol and are queried by the original client order ID instead of resent. Connection recovery restores account state and missing closed candles before strategy decisions resume. Strategy errors require a fresh runtime and cannot be cleared by reconnecting.
See live operations, scope and testnet checklist and adapter injection / managed helper migration.
Release files for open-binancian-futures 26.3.0rc1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| open_binancian_futures-26.3.0rc1.tar.gz | 98.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| open_binancian_futures-26.3.0rc1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 211.0 kB
Release files / open_binancian_futures-26.3.0rc1.tar.gz
| Download URL | open_binancian_futures-26.3.0rc1.tar.gz |
|---|---|
| Size | 98.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
55f5b8b934d3e3b1a31da5c15ffea37f53f00e20846eefb0672d4ffc684e9c48
|
|
BLAKE2b-256 checksum How to use checksums |
dc46bbedc3c32e3a2941adccdc8b4e90a1c5240be1925b5fc1abf610bea7644d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / open_binancian_futures-26.3.0rc1-py3-none-any.whl
| Download URL | open_binancian_futures-26.3.0rc1-py3-none-any.whl |
|---|---|
| Size | 112.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7c624a9e3fcdf85edaf3eb8fd92a230ac90f2f43f3077a81db231935712e6fa1
|
|
BLAKE2b-256 checksum How to use checksums |
559586a266ebbbd81d44a6e8bb2a1c9bb4293ba69ac187f7fefbc62c2f3c67a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency log