Skip to content

feat: add support for Python 3.13 and 3.14 - #4

Closed
pcriadoperez wants to merge 59 commits into
masterfrom
claude/python-3.13-3.14-support-y5e369
Closed

feat: add support for Python 3.13 and 3.14#4
pcriadoperez wants to merge 59 commits into
masterfrom
claude/python-3.13-3.14-support-y5e369

Conversation

@pcriadoperez

Copy link
Copy Markdown
Owner

Summary

Brings this fork up to date with sammchardy/python-binance master (a merge commit, ~90 upstream commits through v1.0.37) and adds Python 3.13 / 3.14 support on top of it.

Support matrix

  • .github/workflows/python-app.yml — test matrix extended to ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
  • tox.inienvlist extended with py313, py314
  • setup.py — added the Programming Language :: Python :: 3.13 / 3.14 classifiers

Code changes for 3.13 / 3.14

binance/helpers.pyget_loop()

On Python 3.14 asyncio.get_event_loop() no longer creates an event loop implicitly; it raises RuntimeError. The old implementation only recovered when the exception message started with "There is no current event loop in thread", which is a fragile contract to depend on. It also emitted a DeprecationWarning on 3.10–3.13 whenever it created a loop. The new version:

  1. returns the running loop if there is one (asyncio.get_running_loop()),
  2. otherwise falls back to the thread's current loop, ignoring the implicit-creation DeprecationWarning,
  3. creates and sets a new loop when there is none, or when the current one is already closed.

binance/ws/threaded_stream.pyasyncio.iscoroutinefunction is deprecated in 3.14 and slated for removal in 3.16; switched to inspect.iscoroutinefunction.

binance/ws/websocket_api.py — removed an unused loop = asyncio.get_event_loop() in connection_lock; the value was never read, and the call itself raises on 3.14 when no loop is set.

examples/asyncio.get_event_loop()asyncio.run(), and datetime.utcfromtimestamp(0).replace(tzinfo=...)datetime.fromtimestamp(0, tz=...).

Verification

Ran against real 3.13.12 and 3.14.0rc2 interpreters, with 3.12.3 as the baseline.

  • pip install -r requirements.txt -r test-requirements.txt resolves cleanly on both 3.13 and 3.14.
  • Offline test subset (test_ids, test_init, test_cryptography, test_reconnecting_websocket, test_threaded_stream, test_error_propagation, test_verbose_mode, test_ws_urls_integration, test_websocket_verbose, test_depth_cache, test_socket_manager): 95 passed / 12 failed, identical on 3.12, 3.13 and 3.14 — no version-specific regressions.
  • get_loop() exercised on all three versions with DeprecationWarning escalated to an error, covering: fresh main thread, repeat call reuses the same loop, call from inside a running loop returns that loop, a closed current loop is replaced, and a worker thread gets its own loop. All pass with no warnings.
  • A DeprecationWarning sweep over the test run on 3.14 shows no remaining warnings originating from binance/ (the ones left come from aiohttp and websockets internals).
  • ruff check --target-version=py39 . passes; pyright reports 0 errors.

Pre-existing failures (not introduced here)

The 12 failures above reproduce identically on 3.12, so they are unrelated to this change:

  • 11 async tests in tests/test_ids.py fail with TypeError: ClientResponse.__init__() missing 1 required keyword-only argument: 'stream_writer'aioresponses is not compatible with the current unpinned aiohttp release. This affects every Python version in the matrix and likely makes CI red on master today; fixing it means pinning a test dependency, which felt out of scope for this PR.
  • tests/test_socket_manager.py::test_ticker_socket times out here because the sandbox has no outbound access to the Binance stream endpoints.

Generated by Claude Code

pcriadoperez and others added 30 commits February 19, 2025 10:47
* fix: json_dumps when using orjson

* add test

* add orjson to test requirements
* add timeout to jobs

* fix: sending ws api request over queue size

* fix tests

* fix tests

* skip test for python 3.7
* feat: auto generate missing endpoints

* update async docs and rerun generator

* reset generated code

* several fixes

* fix github error secret

* add array of deprecated endpoints

* check function for deprecated and public private
…ammchardy#1554)

* fix: return empty json instead of throwing error on empty response

* fix pyright
* add timeout to jobs

* feat: add ws support for options

* add missing functions

* lint

* lint tests

* update timeout gh action

---------

Co-authored-by: carlosmiei <43336371+carlosmiei@users.noreply.github.com>
* update gitignore

* chore: bump to v1.0.28
* add timeout to jobs

* fix: fix response for async client when receiving empty text response
…ve async callbacks called in a task and update docs (sammchardy#1570)

* add timeout to jobs

* feat: expose variable max_queue_size and have async callbacks called in a task

* fix tests

* fix proxy in test

* pass params to socket manager

* skip test in 3.7

* add pytest timeout

* improve logging and error throw on failed connection and add test

* update tests

* skip for 3.7

* pyright tests

* fix test

* comment coveralls

* add debug logging to test

* add more logigng test only file

* fix test

* run all tests

* reduce logging

* update symbol for test

* uncomment coveralls to test

* Revert "uncomment coveralls to test"

This reverts commit e759466.
datetime.datetime.utcfromtimestamp() will be deprecated in future versions
* add timeout to jobs

* feat: error handling depth cache

* imrpove example

* lint
* add timeout to jobs

* chore: update testnet urls

* url fix

* cleanup client

* edit event loop in test

* increase timeout

* fix tests

* fix test
* update gitignore

* bump version
* add timeout to jobs

* feat: assure v3 is used

* lint

* lint

* remove change

* fix test

* Revert "lint"

This reverts commit a3c46d1.

* revert lint

* Revert "revert lint"

This reverts commit 81bd534.
* add timeout to jobs

* docs: update websocket errors docs

* revert change
…is already closed (sammchardy#1593)

* add timeout to jobs

* lint

* reverse timeout change

* fix failing tests
* spot links in the docstrings updated

* Margin api docstrings updated

* Docstrings for fapi endpoints updated

* Missing dapi endpoints added and dapi docstrings updated

* Missing eapi endpoints added and docstrings updated

* papi endpoints updated

* futures_coin_v1_put_order returned

* Wallet links updated
* Update test_client_futures.py

* Update test_async_client_futures.py

typo

* Update async_client.py

typo
…ange by default) (sammchardy#1607)

* futures_historical_klines*: default limit=None; cleaned accidental secret-like string

* trigger CI rerun
* update gitignore

* bump version

* feat: add demo trading support

* update url

* add futures helper methods

* update conf test

* fix missing url

* 8911

* rm proxy none

* update ws_futures_demo_url

* skip failing test and update options symbol

* remove 3.7

* temporarly only test 3.12

* add several fixes including parallel testing

* fix failing tests

* run all tox versions

* update proxy on github action

* add timeout

* fix ws proxy

* add proxy fix for async ws client

* add fix for for options tests

* fix failing tests

* fix lint

* run all tox versions

* add max parallel to 1

* skip ws proxy for py37

* comment options test for py37

* remove 3.7

* increase max parallel to 2

---------

Co-authored-by: Pablo <pablo@criadoperez.com>
… for spot market (sammchardy#1613)

* feat: websocket userdatastream.singature support, deprecate listenkey for spot market

* skip failing test

* fix hanging test, and resuse ws client

* cleaner code
* update gitignore

* bump version

* chore: bump to 1.0.30
* update gitignore

* bump version

* fix(signature): encode non-ascii symbol

* bump version
…ammchardy#1620)

* update gitignore

* bump version

* fix(client): handle non ascii symbols in public and private methods

* fix
* update gitignore

* bump version

* chore: bump to 32
* add timeout to jobs

* feat: Add support for Binance USDS-M Futures conditional/algo orders

This update adds support for Binance's new algo order endpoints for conditional orders, which will be mandatory after 2025-12-09 for order types: STOP, STOP_MARKET, TAKE_PROFIT, TAKE_PROFIT_MARKET, and TRAILING_STOP_MARKET.

Changes:
- Added new order status enums: ACCEPTED, TRIGGERING, TRIGGERED, FINISHED
- Added new dedicated algo order methods:
  * futures_create_algo_order() / async
  * futures_get_algo_order() / async
  * futures_get_all_algo_orders() / async
  * futures_get_open_algo_orders() / async
  * futures_cancel_algo_order() / async
  * futures_cancel_all_algo_open_orders() / async
- Updated existing futures order methods to auto-detect and route conditional orders:
  * futures_create_order() now automatically routes conditional order types to algo endpoint
  * futures_get_order() supports 'conditional' parameter and algoId/clientAlgoId
  * futures_get_all_orders() supports 'conditional' parameter
  * futures_get_open_orders() supports 'conditional' parameter
  * futures_cancel_order() supports 'conditional' parameter and algoId/clientAlgoId
  * futures_cancel_all_open_orders() supports 'conditional' parameter
- Applied same changes to both sync (client.py) and async (async_client.py) clients

References:
- https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Algo-Order
- https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Algo-Order
- https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Algo-Order

* test: Add comprehensive tests for algo/conditional orders

Added tests for both sync and async clients covering:
- Creating algo orders via dedicated method
- Auto-routing conditional orders in futures_create_order
- Getting specific algo orders
- Getting all algo orders history
- Getting open algo orders
- Canceling algo orders
- Canceling all algo open orders
- Using conditional parameter with existing methods

Tests validate both the new dedicated algo order methods and the
backward-compatible conditional parameter on existing methods.

* fix: Use lowercase 'triggerprice' parameter for algo orders

The Binance API expects the trigger price parameter to be lowercase
'triggerprice' not camelCase 'triggerPrice'. Updated both client
implementations and tests to use the correct parameter name.

Changes:
- Updated futures_create_order to convert triggerPrice -> triggerprice
- Added handling for both camelCase and lowercase input
- Updated all tests to use lowercase 'triggerprice' parameter
- Fixes APIError: Mandatory parameter 'triggerprice' was not sent

This ensures compatibility with Binance's algo order endpoints which
expect lowercase parameter names for trigger prices.

* fix: Revert to camelCase 'triggerPrice' per official API docs

After reviewing the official Binance API documentation at:
https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Algo-Order

The parameter is clearly documented as 'triggerPrice' (camelCase), not 'triggerprice' (lowercase).

Example from API docs:
- Parameter: triggerPrice (DECIMAL, NO)
- Response field: "triggerPrice": "750.000"

Changes:
- Reverted futures_create_order to use triggerPrice (camelCase)
- Updated tests to use triggerPrice (camelCase)
- Removed incorrect lowercase conversion

The API error message may display parameter names in lowercase, but the
actual parameter expected by the API is camelCase as documented.

* fix tests

* update websockets

* fix tests

* add tests

* default algoType

* update readme

* fix test id

* skip test

* fix linting

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: carlosmiei <43336371+carlosmiei@users.noreply.github.com>
* fix: reconnect keep alive

* lint
pcriadoperez and others added 23 commits December 15, 2025 10:14
…ammchardy#1641)

assert statements get disabled when running python with -O flag which
means the region check silently dissapears in production. Replaced with
BinanceRegionException that gives callers a proper catchable error type.

Affects get_staking_asset_us, stake_asset_us, unstake_asset_us,
get_staking_balance_us, get_staking_history_us, get_staking_rewards_history_us
* feat: add verbose mode

* lint

* fix test

* fix tests

---------

Co-authored-by: carlosmiei <43336371+carlosmiei@users.noreply.github.com>
* update gitignore

* bump version

* chore: bump to 1.0.34
* update gitignore

* bump version

* chore: normalize package name
…ardy#1672)

* feat: Update futures algo order API with new TP/SL parameters

- Enhanced futures_create_algo_order() to support new API parameters:
  * priceMatch: for LIMIT/STOP/TAKE_PROFIT orders (OPPONENT/QUEUE options)
  * priceProtect: price protection for STOP_MARKET/TAKE_PROFIT_MARKET
  * activatePrice & callbackRate: for TRAILING_STOP_MARKET orders
  * selfTradePreventionMode: STP modes (EXPIRE_TAKER/MAKER/BOTH)
  * goodTillDate: for GTD time in force
  * newOrderRespType: ACK or RESULT response type
  * workingType: trigger based on MARK_PRICE or CONTRACT_PRICE
  * closePosition: close all positions
  * reduceOnly: reduce only mode
  * timeInForce: IOC/GTC/FOK/GTX support
  * positionSide: LONG/SHORT for hedge mode

- Updated comprehensive documentation with all parameter descriptions
- Added practical code example in docstring
- Created 12 new test cases for both sync and async clients:
  * Test priceProtect parameter
  * Test trailing stop with activatePrice/callbackRate (skipped for testnet)
  * Test selfTradePreventionMode
  * Test priceMatch parameter
  * Test newOrderRespType parameter
  * Test workingType parameter

- Added examples/futures_algo_order_examples.py with practical usage examples

This update aligns with the latest Binance Futures API documentation
for New Algo Order endpoint supporting advanced TP/SL functionality.

Note: Some tests may fail in testnet due to timestamp sync issues,
which is a known testnet limitation, not a code issue.

* fix linting

---------

Co-authored-by: carlosmiei <43336371+carlosmiei@users.noreply.github.com>
It's not necessary to sign the spot delist schedule request.
* chore: bump to 35

* update changelog
…y#1657)

* update streams and tests

* lint

* lint

* fix tests

* chore: suppress pre-existing ruff lint errors and skip unavailable API tests

- Add ruff rule ignores in pyproject.toml to pass CI lint
- Skip options websocket stream tests (service unavailable)
- Skip test_stream_get_listen_key_and_close (testnet returns 410)
- Skip test_options_historical_trades (endpoint removed from Binance)

* fix: rename unused variable to satisfy RUF059 lint rule

* fix: round trigger_price to 1 decimal to fix precision error on LTCUSDT
* feat: Add USD-M futures WebSocket URL category support (public/market/private)

Binance is retiring legacy USD-M futures WebSocket URLs on 2026-04-23.
This migrates all fstream methods to use the new categorized URL structure.

* fix: Limit listenKey query-param format to USD-M futures only, not COIN-M

* fix: correct testnet URL in integration tests (fstream not stream)
* feat: add LLM-friendly documentation and agent skill

Add llms.txt (concise overview) and llms-full.txt (full method reference)
following the llmstxt.org standard, plus a cross-client Agent Skill
(.agents/skills/) compatible with 30+ coding agents and a Claude Code
plugin for marketplace distribution.

- generate_llms_txt.py: auto-generates both files by introspecting Client
- .agents/skills/python-binance/: agentskills.io-compatible skill
- python-binance-plugin/: Claude Code plugin wrapping the same skill
- README.rst: add LLM & AI Agent Support section with npx install command

* refactor: remove plugin, compact llms-full.txt

- Remove python-binance-plugin/ directory
- Switch llms-full.txt to compact format: one heading + description +
  params per method, no response examples (360KB -> 198KB)
- Drop RST-to-Markdown conversion code (no longer needed)
- Truncate long param descriptions to 80 chars

* refactor: slim llms-full.txt to ~18K tokens

Switch to compact one-line-per-method format with param names only.
Truncate descriptions to 120 chars, drop auto-generated placeholder
descriptions. Down from 48K to 18K tokens.

* fix: resolve all ruff and pyright errors

- client.py: fix extra space after keyword, spacing around colon
- helpers.py: add missing space after comma
- code-generator.py: fix operator spacing, indentation issues
- examples/depth_cache_threaded_example.py: fix indentation, add type ignores

* fix: ignore E203 in ruff config (conflicts with ruff-format slice style)
* chore: update ids

* ignore file

* add id tests to python

* fix tests

Copy link
Copy Markdown
Owner Author

CI status: pushed one fix, two pre-existing blockers remain

The first CI run ended with test (3.8) failed and every other leg cancelled — including the 3.13 and 3.14 legs this PR exists to add, which never ran at all.

Fixed here: fail-fast: false (c59f952)

The test matrix had no fail-fast: false, so the first failing interpreter cancelled all the others. That made every matrix entry unevaluable the moment any single one broke. Added fail-fast: false so each version reports on its own merits.

Not fixed here: two pre-existing aioresponses problems

Both are independent of this diff — this PR touches only the CI matrix, tox envlist, classifiers, changelog, three binance/ modules and two examples. Neither can affect whether a test dependency imports.

1. aioresponses 0.7.9 does not support Python 3.8 — this is what broke the 3.8 leg:

aioresponses/core.py:131: in RequestMatch
    def _build_raw_headers(self, headers: Mapping[str, str]) -> Tuple:
E   TypeError: 'ABCMeta' object is not subscriptable

collections.abc.Mapping[str, str] in an annotation evaluated at class-body time needs PEP 585, i.e. Python 3.9+. Reproduced in a bare 3.8 venv containing only aioresponses — no repo code involved. 0.7.8 imports fine on 3.8.

Proposed patch for test-requirements.txt:

aioresponses<0.7.9; python_version < '3.9'

2. aioresponses 0.7.9 is incompatible with aiohttp >= 3.13 — this affects every Python version, and is almost certainly making master red today:

TypeError: ClientResponse.__init__() missing 1 required keyword-only argument: 'stream_writer'

requirements.txt leaves aiohttp unpinned, so CI resolves to 3.14.x and all 11 async tests in tests/test_ids.py fail. Proposed patch, also test-requirements.txt:

aiohttp<3.13

I've deliberately not pushed either one. Pinning aiohttp in the test environment means the suite stops exercising the aiohttp version your users actually get, which is a real trade-off and your call rather than something to slip into a Python-version PR.

Evidence that 3.13 and 3.14 themselves are fine

With aiohttp<3.13 installed locally (nothing else changed), against real 3.13.12 and 3.14.0rc2 interpreters, with 3.12.3 as the control:

3.12.3 3.13.12 3.14.0rc2
tests/test_ids.py 22 passed 22 passed 22 passed
10-file offline subset 106 passed 106 passed 106 passed

Zero failures, identical on all three. The subset covers test_ids, test_init, test_cryptography, test_reconnecting_websocket, test_threaded_stream, test_error_propagation, test_verbose_mode, test_ws_urls_integration, test_websocket_verbose and test_depth_cache. Excluded only test_socket_manager, which needs outbound access to the Binance stream endpoints.

So the library changes in this PR stand on their own; what's left is a test-dependency decision that predates it and applies to the whole matrix.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

CI now reports per-version, and 3.13 / 3.14 match the control exactly

With fail-fast: false in place, every leg ran instead of being cancelled. Results on c59f952, Id Tests step:

leg interpreter result
lint 3.9 passed
test (3.8) 3.8.18 failed — aioresponses needs 3.9+ (collection error)
test (3.10) 3.10.21 11 failed, 11 passed
test (3.11) 3.11.x 11 failed, 11 passed
test (3.12) 3.12.x 11 failed, 11 passed
test (3.13) 3.13.15 11 failed, 11 passed
test (3.14) 3.14.7 11 failed, 11 passed

3.10 through 3.14 are identical — same eleven test names, same stream_writer TypeError, nothing version-specific. 3.12 is the control: it was in the matrix before this PR and fails exactly the same way.

Two things this confirms for the versions this PR adds:

  • actions/setup-python provisions 3.13.15 and 3.14.7 on ubuntu-22.04 without trouble — no runner-image or toolcache gap.
  • pip install -r requirements.txt -r test-requirements.txt resolves and installs on both, binance imports, and the 11 sync tests in test_ids.py pass.

CI exercised 3.14.7 stable, which is better evidence than my local run (3.14.0rc2).

The remaining red is entirely the aioresponses situation described above, which stops every leg at Id Tests before tox -e py ever runs. The two proposed test-requirements.txt pins take the offline suite to 106/106 on 3.12, 3.13 and 3.14 locally; I'm leaving that call to you since pinning aiohttp for tests trades away coverage of the version users actually install.


Generated by Claude Code

claude added 2 commits August 24, 2026 03:05
Test on 3.13 and 3.14 in CI and tox, advertise them via PyPI classifiers,
and fix the asyncio usages that break or warn on those versions.

- get_loop(): Python 3.14 no longer creates an event loop implicitly in
  asyncio.get_event_loop() and raises RuntimeError instead, so stop
  matching on the exception message. Prefer the running loop, fall back to
  the thread's current loop, and create a new one when there is none (or
  when the current one is already closed). Suppress the DeprecationWarning
  that 3.10-3.13 emit on the implicit-creation path.
- Use inspect.iscoroutinefunction, as asyncio.iscoroutinefunction is
  deprecated in 3.14 and slated for removal in 3.16.
- Drop the unused asyncio.get_event_loop() call in WebsocketAPI.connection_lock.
- Update the examples to asyncio.run() and datetime.fromtimestamp(0, tz=...).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoUswMgTWWfPd4UhBzryTT
Without fail-fast disabled, the first failing interpreter cancels every
other leg. The 3.8 leg currently fails at collection time, which cancelled
3.9 through 3.14 before any of them reported a result, so a matrix entry
can never be evaluated on its own merits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoUswMgTWWfPd4UhBzryTT
@pcriadoperez
pcriadoperez force-pushed the claude/python-3.13-3.14-support-y5e369 branch from c59f952 to f881b9d Compare August 24, 2026 03:05

Copy link
Copy Markdown
Owner Author

Closing — this should target upstream instead

This PR was opened against pcriadoperez/python-binance:master. The change belongs upstream in sammchardy/python-binance, and GitHub does not allow changing a PR's base repository (only its base branch), so this one is being closed in favour of a fresh PR from this same branch to upstream.

The branch has been rebased onto sammchardy/python-binance@7d7b7fb (v1.0.37) so the upstream PR is clean:

  • the merge commit is gone
  • 6b9447f "add timeout to jobs" was dropped — upstream added the same timeout-minutes values independently, so it contributed zero diff against upstream
  • what remains is two commits, 04e51f6 and f881b9d, touching 9 files

The resulting tree is byte-identical (d73b749) to c59f952, the commit CI validated on this PR — so the 3.13.15 / 3.14.7 results reported above still describe exactly this code.

Open the upstream PR here:
https://github.com/sammchardy/python-binance/compare/master...pcriadoperez:python-binance:claude/python-3.13-3.14-support-y5e369?expand=1

The analysis in the comments above (the two aioresponses blockers and their proposed patches) still applies and is worth carrying into the upstream discussion, since it affects the whole matrix on upstream master too.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.