Skip to content

Releases: pybotters/pybotters

v1.1.0

28 Apr 03:45
f8955fe
Compare
Choose a tag to compare

What's Changed

Added

  • Ensure WebSocket connection with Ping-Pong by @MtkN1 in #281

Documentation

  • Fix Read the Docs build using Poetry by @MtkN1 in #276
  • Fix typo in docstring in DataStore.get method by @nukopy in #275

Development

  • Use Ruff instead of Flake8, isort, and Black by @MtkN1 in #277
  • Migrate project management from Poetry to Hatch by @MtkN1 in #279
  • Update project configuration by @MtkN1 in #280
  • Configuring automatically generated release notes by @MtkN1 in #284
  • Integrate version control into Git tag with hatch-vcs by @MtkN1 in #285

New Contributors

Full Changelog: v1.0.0...v1.1.0

🚀 Version 1.0.0

31 Jan 11:53
6ffbe9d
Compare
Choose a tag to compare

Release highlights

pybotters が遂に v1.0 リリースを迎えられることを嬉しく思います 🎉🎉

pybotters の最初のリリースは v0.1.0 で 2021 年 4 月です。
3 年近くの時を経ててようやく v1.0 へのバージョンアップができました。

v1.0 リリースには以下の変更内容が含まれています。

  • 幾つかの新機能
  • 多数の破壊的な変更
  • 多数の内部改善、テストカバレッジ 100% (取引所固有の DataStore を除く)
  • ドキュメントの大幅アップデート、ブランディングロゴの追加
  • リポジトリの移管 (MtkN1/pybotters -> pybotters/pybotters)

また pybotters はスポンサーシップを始めました。
開発者である @MtkN1 の GitHub スポンサーに登録して頂く事で、プロジェクトを支援できます。

💖 https://github.com/sponsors/MtkN1

プロジェクト継続の為にご支援のほどよろしくお願いします 🙏

New Features

  • Add fetch API by @MtkN1 in #243
    async def main():
        async with pybotters.Client() as client:
            result = await client.fetch(
                "GET",
                "https://api.bitflyer.com/v1/getticker",
                params={"product_code": "BTC_JPY"},
            )
            print(result.response.status, result.response.reason)
            print(result.data)
  • Redesign GMOCoin DataStore by @MtkN1 in #249
    • GMOCoinDataStore のデータ型が再設計されました。 シンボルのデータが ENUM 型で独自定義されているなど、他の DataStore とは異なる仕様になっていました。 それを他と同様に「取引所由来のデータ型」を尊重する仕様になるよう変更しました。
    # legacy
    {
        'ask': Decimal('6484170'),
        'bid': Decimal('6481482'),
        'high': Decimal('6535603'),
        'last': Decimal('6481889'),
        'low': Decimal('6256694'),
        'symbol': <Symbol.BTC_JPY: 24>,
        'timestamp': datetime.datetime(2024, 1, 8, 13, 49, 0, 31000),
        'volume': Decimal('1877.89')
    }
    # new
    {
        'channel': 'ticker',
        'ask': '6492347',
        'bid': '6489767',
        'high': '6535603',
        'last': '6490200',
        'low': '6256694',
        'symbol': 'BTC_JPY',
        'timestamp': '2024-01-08T13:47:46.410Z',
        'volume': '1875.45'
    }
    • なお、旧仕様の GMOCoinDataStorepybotters.models.legacy.gmocoin から引き続き利用できます。
  • Enhance WebSocket Features by @MtkN1 in #259
  • Improve DataStore.sorted in Order Book by @MtkN1 in #265
    store = pybotters.bitFlyerDataStore()
    ...
    print(store.board.sorted(limit=2))

Breaking changes

💥 このアップデートには多数の破壊的な変更が含まれています 💥

以下の Pull request にて v1.0 における全ての破壊的変をまとめているので、マイグレーションガイドとして活用できます。

  • Remove some APIs #252

以下の機能を利用してる場合コードが動作しなくなる場合があります。
必ずマイグレーションガイドを確認してトレード bot 等のコードの動作確認を行ってください
影響が多いと考えられるものには Emoji 💥 を付けます。

  • トップレベルの同期関数 pybotters.get など
  • Bybit の古い DataStore BybitUSDTDataStore など
  • Binance の古い DataStore BinanceDataStore
  • 💥 Bybit V5 のDataStore BybitV5DataStore
  • DataStore の引数 auto_case
  • 💥 WebSocket キュー WebSocketQueue
  • コールバック用関数 pybotters.print_handler
  • KuCoin の API 認証情報にキー名 kucoinspot kucoinfuture
  • メソッド DataStore.wait
  • WebSocketRunnner (Client.ws_connect の戻り値) の属性 .connected
  • WebSocketRunnner (Client.ws_connect の戻り値) の属性 .ws
  • 💥💥 全ての取引所固有の DataStore のメソッド sorted

また機能の変更以外に、サポート対象から Python 3.7 が外され Python 3.8 >= が対象になります。


  • Drop Python 3.7 and support Python 3.8 through 3.12 by @MtkN1 in #246
  • Remove some APIs by @MtkN1 in #252
  • Remove return value of DataStore.wait method by @MtkN1 in #257
  • Remove KuCoin API name compatibility by @MtkN1 in #254
  • Improve Binance Authentication by @MtkN1 in #256
  • Improve WebScoket auth by @MtkN1 in #262

Docs

ドキュメントを大幅にアップデートしました ✨

以前は実装されている機能の説明がなかったりしましたが、今回のアップデートで全てを網羅するようになっています!
まだドキュメントをみたことない方も、以前のドキュメントをみたことある方も、是非一度チェックしてください 👉

https://pybotters.readthedocs.io/ja/stable


Improves

Bug fixes

  • Wait for WebSocket login to complete in Bitget by @MtkN1 in #242
  • Fix signatures when no parameters are specified in MEXC by @MtkN1 in #250
  • Fix Binace order book buffer by @MtkN1 in #255

Developments

GitHub リポジトリを開発者の @MtkN1 から @pybotters に移管しました。
これは pybotters のプロジェクトとしての公共性をより表現する為です。

https://github.com/MtkN1/pybotters
👉
https://github.com/pybotters/pybotters


What's Changed

Full Changelog: v0.17.0...v1.0.0

🚀 Version 0.17.0

18 Sep 12:29
adfa9cf
Compare
Choose a tag to compare

🪧 What's Changed

🚀 New Features

✅ Phemex Hedged Contract API の DataStore をサポートしました

新しくリリースされた Phemex Hedged Contract の DataStore をサポートしました。 Hedged Contract API とは USDT 建ての契約用の API です。 従来の契約は Contract API という表記になっておりクリプト建てと米ドル建ての契約が存在しています。

Hedged Contract の WebSocket API は配信されるチャンネル名が通常の Contract API から新しくなっていた為、今回の変更で DataStore をこれに対応しました。 なお REST API 認証の互換性があることは確認している為、従来の pybotters バージョンから引き続き利用ができます。

Phemex Hedged Contract WebSocket API ドキュメント: https://phemex-docs.github.io/#hedged-contract-websocket-api

アップデート後の PhemexDataStore では以下ストアにて対応するチャンネルのデータがハンドリングされるようになります。

  • trade store: trades_p channel
  • orderbook store: orderbook_p channel
  • ticker store: tick_p channel
  • market24h store: market24h_p channel
  • kline store: kline_p channel
  • accounts store: aop_p channel
  • orders store: aop_p channel
  • positions store: aop_p channel

🔥 Breaking changes

PhemexDataStore が Hedged Contract をサポートするにあたって ** PhemexDataStore の以下ストアにおいて破壊的な変更があります **。

  • trade ストアと orderbook ストアのアイテムにおいて、数量を表すキー sizeqty に変更します。
    • キーが Phemex のドキュメントと不一致していた為。
  • trade ストアと kline ストアのアイテムにおいて、価格を表す price, open, high, low, close, turnover の値に対して意図的に ÷ 10000 を行って価格を扱い易くしていた仕様を削除し元の値を尊重します。
    • これにより Contract API の場合は元の priceEp と同等の値が格納されます。 つまりユーザー側で明示的にスケールを演算する必要があります。 スケールの仕様ついては詳しくは Phemex のドキュメント (https://phemex-docs.github.io/#price-ratio-value-scales) をご覧ください。

🎨 Sample Code

以下は PhemexDataStore の利用例です。 Phemex Hedged Contract WebSocket API (testnet) の AOP(Account / Order / Position) チャンネルを購読して DataStore で変更を監視します。

import asyncio

import pybotters


async def main():
    async with pybotters.Client() as client:
        store = pybotters.PhemexDataStore()

        asyncio.create_task(
            client.ws_connect(
                "wss://testnet-api.phemex.com/ws",
                send_json={
                    "id": 1234,
                    "method": "aop_p.subscribe",
                    "params": [],
                },
                hdlr_json=store.onmessage,
            )
        )

        async def watch_accounts():
            with store.accounts.watch() as stream:
                async for change in stream:
                    print("accounts", change)

        async def watch_orders():
            with store.orders.watch() as stream:
                async for change in stream:
                    print("orders", change)

        async def watch_positions():
            with store.positions.watch() as stream:
                async for change in stream:
                    print("positions", change)

        await asyncio.gather(
            watch_accounts(),
            watch_orders(),
            watch_positions(),
        )


try:
    asyncio.run(main())
except KeyboardInterrupt:
    pass

変更の詳細は #233 をご覧ください。

✅ Binance の現物用 "WebSocket API" をサポートしました。

Binance の現物用 "WebSocket API" とは、Public API (マーケットデータの取得など) と Private API (注文の送信や残高の取得など) を ** WebSocket 接続経由 **でリクエストできる API です。 従来の "Websocket Market Streams" [^2] や "User Data Streams" [^3] ではリアルタイムデータの受信に限られていましたが、この "WebSocket API" では REST API 同様の操作を WebSocket 経由で行えます。

これに REST API を利用するより早いレイテンシーで注文を作成できる可能性があります。 ただし Rate Limit としては REST API と同様なので注意が必要です。

以下は Binance "WebSocket API" で Public API (ティッカー) と Private API (アカウントステータス) をリクエストするサンプルコードです。

import asyncio
import uuid

import pybotters
from rich import print


async def main():
    apis = {"binance_testnet": [api_key, api_sercret]}

    async with pybotters.Client(apis=apis) as client:
        wsq = pybotters.WebSocketQueue()

        wsrunnner = await client.ws_connect(
            "wss://testnet.binance.vision/ws-api/v3", hdlr_json=wsq.onmessage
        )

        if wsrunnner.ws is not None:
            id1 = str(uuid.uuid4())
            id2 = str(uuid.uuid4())
            ids = {id1, id2}

            data = {
                "id": id1,
                "method": "ticker.price",
                "params": {"symbol": "BNBBTC"},
            }
            await wsrunnner.ws.send_json(data, auth=None)

            data = {
                "id": id2,
                "method": "account.status",
                "params": {},
            }
            await wsrunnner.ws.send_json(data)

            async for msg in wsq.iter_msg():
                print(msg)

                ids.remove(msg["id"])
                if not len(ids):
                    break


asyncio.run(main())

変更の詳細は #235 をご覧ください。

✅ KuCoin の high-frequency 用 API の動作確認を行いました

KuCoin に新しく追加された high-frequency API の対応状況を確認しました。 high-frequency API とはその名の通り取引のレイテンシーが低く、Rate Limit も緩い API とのことです。 現在現物のみで利用できる API のようです。

pybotters では新しく対応が必要な事象がないか確認したところ、従来のバージョンより認証の互換性があり API が利用可能ということが分かりました。 その為コードの変更等はありません。

high-frequency API を利用する為のサンプルコードについては Issue #230 のコメントをご覧ください。

⚡️ KuCoin の API 名は "kucoin" に変更されます

以前 KuCoin の API キーは「現物用」と「先物用」で独立しており別々の発行になっていました。 つまり現物と先物を取引するには API キーが 2 つ必要だったということです。 しかし現在の KuCoin は「現物用」と「先物用」で独立する仕様はなくなっていることを確認しました。

pybotters では以前の仕様に対応する為に "kucoinspot" "kucoinfuture" の 2 つの API 名を定義していました。 今回の変更ではこの KuCoin の API 名は "kucoin" に統合されます。

# Old
{
    "kucoinspot": ["KUCOIN_SPOT_API_KEY", "KUCOIN_SPOT_API_SECRET", "KUCOIN_SPOT_API_PASSPHRASE"],
    "kucoinfuture": ["KUCOIN_FUTURE_API_KEY", "KUCOIN_FUTURE_API_SECRET", "KUCOIN_FUTURE_API_PASSPHRASE"]
}
# New
{
    "kucoin": ["KUCOIN_COMMON_API_KEY", "KUCOIN_COMMON_API_SECRET", "KUCOIN_COMMON_API_PASSPHRASE"],
}

** しかしながら pybotters では API 名が "kucoinspot" "kucoinfuture" のままでも認証が動作するよう互換性を保つ措置を行っています ** 。 引き続き Old の状態でもプログラムがエラーになることはありません。 この措置は pybotters がプレビュー版の間は保たれる予定です。 ただしドキュメント等は "kucoin" の定義にマイグレーションされますので、既存のユーザーは変更の対応をお願いします。

⬆️ Improves / Bugfix

  • ✅ WebSocket の例外処理を向上しました
  • ✅ Phemex の新しいエンドポイント URL を認証ホストに追加しました
  • ✅ KuCoin の DELETE メソッドで発生する認証のバグを修正しました

📝 Documentation

Pull requests

  • Update Phemex auth hosts by @MtkN1 in #232
  • Support Phemex Hedged Contract Websocket Data by @MtkN1 in #233
  • Catch ServerDisconnectedError exception by @MtkN1 in #234
  • Support Binance Spot "WebSocket API" by @MtkN1 in #235
  • Fix KuCoin signatures affected by HTTP methods by @MtkN1 in #236
  • Integrate KuCoin API name by @MtkN1 in #237
  • Renew documentation by @MtkN1 in #238

Full Changelog: v0.16.0...v0.17.0

🚀 Version 0.16.0

08 Mar 17:59
4c8fd5f
Compare
Choose a tag to compare

🪧 What's Changed

  • Workaround: Bybit の Derivatives v3 Contract API に対応する by @cococig in #219
  • typing-extensionsのバージョンアップ by @yota-p in #222
  • Bybit の認証方式を新しい認証方式に変更する by @MtkN1 in #223
  • PyPI へのアップロードを自動化する by @MtkN1 in #224
  • README.md のバッジや分類を追加する by @MtkN1 in #225
  • Bybit V5 API DataStore by @MtkN1 in #226

🚀 New Features

✅ Bybit V5 API をサポートしました (API 認証 / DataStore)

Bybit にて新しくリリースされた V5 API 1 を pybotters で利用できるようになりました!
V5 API では Bybit の以下主要プロダクトの API インターフェースが統合されました。

  • USDT 無期限
  • USDC 無期限
  • USDC オプション
  • インバース契約
  • 現物取引
  • マージン取引
  • レバレッジトークン

⚠️ 注意:

  1. V5 API でこれら全てのプロダクトを利用するには Bybit 側で「統合取引アカウント」 2 へのアップグレートが必要です。
    アップグレートを行っていない「デリバティブアカウント」の場合は、V5 API からは USDT 無期限とインバース契約のみ利用可能 3 です。
    基本的には「統合取引アカウント」へアップグレートをおすすめしますが、既存の口座をアップグレートしたくない場合は、サブアカウントを作成してそちらを「統合取引アカウント」へアップグレートして利用することも可能です。
  2. (互換性のある変更) pybotters 内部の自動認証の仕組みにおいて Bybit への REST API リクエストを全て新しい認証方式 4 に変更しています。 URL クエリまたはリクエストボディに与えられていた認証情報がリクエストヘッダーに付与されるようになります。 これは従来の V2 API 5 の認証にも影響を与えますが、新しい認証方式でも動作することは確認済みです。
  3. (互換性のある変更) 同様に WebSocket API の認証方式も変更しています。 URL クエリに付与されていた認証情報は、WebSocket メッセージとして送信されるようになります。 こちらも V2 API でも動作することは確認済みです。

統合された API インターフェースを利用することで各プロダクト間での API 呼び出しがシームレスになります!
pybotters で V5 API を利用して他のトレーダーに差を付けましょう 🚀

💡 サンプルコード

REST API で成行注文を送信する

🚧 このコードを実行するとテストネットに BTCUSDT 0.001 枚の買い成行注文が送信されます。

import asyncio

import pybotters
from rich.pretty import pprint

apis = {
    "bybit_testnet": ["BYBIT_TESTNET_API_KEY", "BYBIT_TESTNET_API_SECRET"],
}


async def main():
    async with pybotters.Client(
        base_url="https://api-testnet.bybit.com", apis=apis
    ) as client:
        async with client.post(
            "/v5/order/create",
            data={
                "category": "linear",
                "symbol": "BTCUSDT",
                "side": "Buy",
                "orderType": "Market",
                "qty": "0.001",
            },
        ) as resp:
            content = await resp.json()
        pprint(content)


asyncio.run(main())

Public WebSocket を購読して DataStore でウォッチする

BTCUSDT に関する全ての Public WebSocket トピックを指定している為大量データの print 出力が表示されます。
適宜 topics の内容をコメントアウトして絞ってください。

import asyncio

import pybotters
import pybotters.store
from rich.pretty import pprint


async def watcher(ctx: pybotters.store.StoreStream):
    try:
        with ctx as stream:
            async for change in stream:
                pprint(change)
    except KeyboardInterrupt:
        pass


async def main():
    topics = [
        "orderbook.50.BTCUSDT",
        "publicTrade.BTCUSDT",
        "tickers.BTCUSDT",
        "kline.5.BTCUSDT",
        "liquidation.BTCUSDT",
    ]

    async with pybotters.Client() as client:
        store = pybotters.BybitV5DataStore()

        ws = await client.ws_connect(
            "wss://stream.bybit.com/v5/public/linear",
            send_json={
                "op": "subscribe",
                "args": topics,
            },
            hdlr_json=store.onmessage,
        )

        for topic in topics:
            dsname, *_ = topic.split(".")
            asyncio.create_task(watcher(store[dsname].watch()))

        await ws


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass

Private WebSocket を購読して DataStore でウォッチする

こちらのコードを実行後、テストネットにて注文などのアカウント操作をするとリアルタイムデータが DataStore のウォッチ機能で出力されます。
watcher 関数のコメントアウトを切り替えると、リアルタイムデータがハンドリングされた後の実際のストアの中身を確認することができます。

import asyncio

import pybotters
import pybotters.store
from rich.pretty import pprint

apis = {
    "bybit_testnet": ["BYBIT_TESTNET_API_KEY", "BYBIT_TESTNET_API_SECRET"],
}


async def watcher(ctx: pybotters.store.StoreStream):
    with ctx as stream:
        async for change in stream:
            pprint(change)
            # pprint(change.store.find())


async def main():
    topics = [
        "position",
        "execution",
        "order",
        "wallet",
        "greeks",
    ]

    async with pybotters.Client(apis=apis) as client:
        store = pybotters.BybitV5DataStore()

        await store.initialize(
            client.get(
                "https://api-testnet.bybit.com/v5/position/list?category=linear&symbol=BTCUSDT"  # noqa
            ),
            client.get(
                "https://api-testnet.bybit.com/v5/order/realtime?category=linear&symbol=BTCUSDT"  # noqa
            ),
            client.get(
                "https://api-testnet.bybit.com/v5/account/wallet-balance?accountType=UNIFIED"  # noqa
            ),
        )

        ws = await client.ws_connect(
            "wss://stream-testnet.bybit.com/v5/private",
            send_json={
                "op": "subscribe",
                "args": topics,
            },
            hdlr_json=store.onmessage,
        )

        for topic in topics:
            asyncio.create_task(watcher(store[topic].watch()))

        await ws


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass

その他のサンプルや詳細仕様は、#223 #226 をご覧ください。

⬆️ Improves

✅ 依存関係を見直し、他のライブラリと併用しやすくなりました

pybotters の依存関係を整理しました。

具体的には typing-extensions のバージョン制約で、InfluxDB のクライアントライブラリ influxdb-client を pybottes に併せてインストールができない問題が発生していましたが、それを解消しました。

👷 CI / Misc

  • ✅ pybotters の PyPI アップロードを GitHub Actions で自動化しました
  • ✅ pybotters の GitHub README にバッジを追加したり PyPI の概要を更新しました

New Contributors

Full Changelog: v0.15.0...v0.16.0

  1. https://bybit-exchange.github.io/docs/v5/intro

  2. https://www.bybithelp.com/ja-JP/s/article/Introduction-to-Bybit-Unified-Trading-Account

  3. https://bybit-exchange.github.io/docs/v5/upgrade-guide#current-api-coverage

  4. https://bybit-exchange.github.io/docs/v5/intro#parameters-for-authenticated-endpoints

  5. https://bybit-exchange.github.io/docs-legacy/futuresV2/linear/#t-introduction

✨ v0.15.0 アップデート

30 Dec 12:37
86e4bc0
Compare
Choose a tag to compare

🪧 What's Changed

  • GMOコインの現物取引所にここ一年半ほどで追加された銘柄に対応 by @meetaco in #204
  • Dev Containers 仕様のオープン化に対応及びローカルの API 設定がマウントされるようにした by @MtkN1 in #202
  • Coincheck の WebSocket 仕様変更に対応した by @MtkN1 in #207
  • GitHub Actions のテストに Python 3.10+ を追加した by @MtkN1 in #208
  • WebSocketのメッセージキューを実装する by @MtkN1 in #209
  • PhemexDataStore で条件付き注文のデータを格納する by @MtkN1 in #210
  • bitFlyer のストアに現物向けの Balance を追加する by @MtkN1 in #211
  • watch メソッドの変更クラスに影響されたストアとデータを追加する by @MtkN1 in #212
  • Hotfix: bitFlyer の Positions ストアのメッセージ判断条件を修正する by @MtkN1 in #213
  • ストアにおけるアイテムの参照性を変更する by @MtkN1 in #214
  • Liquid に関する機能を廃止する by @MtkN1 in #215
  • FTX に関する機能を廃止する by @MtkN1 in #216
  • bitFlyer Positions ストアでのアップデートの watch ストリームを有効にする by @MtkN1 in #217

🚀 New Features

✅ bitFlyer の資産残高のストアを実装しました

bitFlyer の現物資産残高のストアが利用できるようになりました!

WebSocket の child_order_events チャンネルのデータを元に資産残高を計算し、リアルタイムで情報を利用できます。 また REST API による初期化にも対応しています。 ストアの値は GET /v1/me/getbalance の形式で格納されます。

[
  {
    "currency_code": "JPY",
    "amount": 1024078,
    "available": 508000
  },
  {
    "currency_code": "BTC",
    "amount": 10.24,
    "available": 4.12
  },
  {
    "currency_code": "ETH",
    "amount": 20.48,
    "available": 16.38
  }
]

⚠️ !注意! 残高 (amount) の値の計算のみ対応しており、利用可能残高 (available) は現状対応していません。

サンプルコード

Balance ストアの利用方法です。 REST API でデータを初期化し、watch メソッドで WebSocket からのデータを監視します。

import asyncio

import pybotters


async def main():
    async with pybotters.Client(base_url="https://api.bitflyer.com", apis=apis) as client:
        store = pybotters.bitFlyerDataStore()

        await client.ws_connect(
            "wss://ws.lightstream.bitflyer.com/json-rpc",
            send_json={
                "method": "subscribe",
                "params": {"channel": "child_order_events"},
            },
            hdlr_json=store.onmessage,
        )

        with store.balance.watch() as stream:
            await store.initialize(client.get("/v1/me/getbalance"))
            async for change in stream:
                print(change)


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass

詳細は #211 をご覧ください。

✅ WebSocket のメッセージキューを実装しました

pybotters では WebSocket のハンドラとして各取引所の DataStore を提供していましたが、新たに WebSocketQueue を実装しました。 WebSocketQueue はシンプルなメッセージキューです。 asyncio.Queue を継承して実装されています。

💡 使いどころ

  • DataStore を利用せずに素の WebSocket データを利用したい場合
  • DataStore が実装されていない取引所の WebSocket データを利用したい場合

💡 DataStore の watch メソッドとの比較

DataStore の watch メソッドでもデータのキューイングが可能ですが、WebSocketQueue とは仕様が異なります。 少し似た機能なので、違いを説明します。

  • 取得するデータ単位
    • watch
      • DataStore アイテムごと
        • 例えば trade というストアがあり WebSocket から {"channel": "trade, "data": [{"id": 1}, {"id": 2}]} のようなデータが配信されたとしたら、ストア内の処理で {"id": 1}{"id": 2} の 2 つのアイテムとして登録され、その 2 つが watch のストリームから取得されます。
    • WebSocketQueue
      • WebSocket メッセージごと
        • 上記の例で言うと、WebSocket メッセージ {"channel": "trade, "data": [{"id": 1}, {"id": 2}]} がそのまま取得されます。
  • 発火の単位
    • watch
      • DataStore アイテムの更新ごと
        • 例えば order というストアが WebSocket からあるオーダー ID がキャンセルされた旨のメッセージが配信された時に、order ストアに該当オーダー ID が存在していればそのアイテムは削除され watch に配信されます。 しかし該当オーダー ID が存在しない場合はストアに変更は存在しない為、watch に変更は配信されません。 つまり、WebSocket メッセージの配信数 >= watch の発火数 です。
    • WebSocketQueue
      • WebSocket メッセージの配信ごと
        • WebSocket メッセージの配信数 == WebSocketQueue の発火数 です。
  • データの形式
    • watch
      • 例えば trade というストアに WebSocket メッセージ {"channel": "trade, "data": [{"id": 1}]} が配信されたとしたら、ストア内の処理で {"id": 1} にパースされストアに格納されます。 このアイテムが watch で取得されます。
    • WebSocketQueue
      • 上記の例で言うと、{"channel": "trade, "data": [{"id": 1}]} がそのまま取得されます。

サンプルコード

Coincheck で約定をキューイングするサンプルコードです。

  1. pybotters.WebSocketQueue() でインスタンスを生成する
  2. onmessage メソッドをハンドラに渡す
  3. iter_msg メソッドをコールして async for ループに渡す
  4. WebSocket メッセージ受信時、async for で定義したループ変数に値が格納される
import asyncio

import pybotters


async def main():
    async with pybotters.Client() as client:
        wsq = pybotters.WebSocketQueue()

        await client.ws_connect(
            "wss://ws-api.coincheck.com/",
            send_json={"type": "subscribe", "channel": "btc_jpy-trades"},
            hdlr_json=wsq.onmessage,
        )

        async for msg in wsq.iter_msg():
            print(msg)


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass

iter_msg はタイムアウトを指定するなども可能です。 また asyncio.Queue の継承クラスなので get を利用することもできます。

詳細は #209 をご覧ください。

⏫ Improvements

✅ watch 機能で影響元のデータとストアが分かるようになりました

例えば bitFlyer でオーダーがキャンセルされた時に watch で受け取るデータは以下の通りでした。

# 変更前
StoreChange(
│   operation='delete',
│   data={
│   │   'product_code': 'BTC_JPY',
│   │   'child_order_id': 'JOR20221228-121846-331601',
│   │   'child_order_acceptance_id': 'JRF20221228-121846-183970',
│   │   'event_date': '2022-12-28T12:18:46.9290446Z',
│   │   'event_type': 'ORDER',
│   │   'child_order_type': 'LIMIT',
│   │   'side': 'BUY',
│   │   'price': 2228600,
│   │   'size': 0.001,
│   │   'expire_date': '2023-01-27T12:18:46'
│   }
)

キャンセルされたイベントの通知ですが data"event_type": "ORDER" となっており、プログラム側としても何の変更なのか分かりにくいです。

変更後は以下のようになります。

# 変更後
StoreChange(
│   store=<pybotters.models.bitflyer.ChildOrders object at 0x7fe8b38efcd0>,
│   operation='delete',
│   source={
│   │   'product_code': 'BTC_JPY',
│   │   'child_order_id': 'JOR20221228-121846-331601',
│   │   'child_order_acceptance_id': 'JRF20221228-121846-183970',
│   │   'event_date': '2022-12-28T12:18:48.8355776Z',
│   │   'event_type': 'CANCEL',
│   │   'price': 2228600,
│   │   'size': 0.001
│   },
│   data={
│   │   'product_code': 'BTC_JPY',
│   │   'child_order_id': 'JOR20221228-121846-331601',
│   │   'child_order_acceptance_id': 'JRF20221228-121846-183970',
│   │   'event_date': '2022-12-28T12:18:46.9290446Z',
│   │   'event_type': 'ORDER',
│   │   'child_order_type': 'LIMIT',
│   │   'side': 'BUY',
│   │   'price': 2228600,
│   │   'size': 0.001,
│   │   'expire_date': '2023-01-27T12:18:46'
│   }
)

source プロパティに影響元のアイテムが格納されるようになります。 "event_type": "CANCEL" という値が分かるようになるのでより柔軟な処理が行えるようになります。

また store プロパティで影響元のストアが渡されます。 プログラムはこのプロパティを利用してストアのデータを取得することが可能になります。

詳細は #212 をご覧ください。

✅ Coincheck の WebSocket 仕様変更に対応しました

以下のアナウンスの通り Coincheck の WebSocket が仕様変更され DataStore がデータをハンドリングできない状態でしたが正常にハンドリングされるよう修正しました。

https://corporate.coincheck.com/news/_XbwDwWN

Other Changes

  • ✅ PhemexDataStore で条件付き注文のデータを格納できるようになりました
  • ✅ GMOCoinDataStore の銘柄をアップデートしました
  • ✅ Liquid と FTX に関する機能を廃止しました
  • ✅ bitFlyerDataStore のポジションが減少した際 watch で取得されない問題を修正しました

👨‍💻👩‍💻 pybotters Development

✅ pybotters の Dev Containers を更新しました

VS Code などで利用できる開発コンテナの定義を刷新しました。

イメージの更新や、拡張機能のインストール、ローカルの PYBOTTERS_APIS で指定している環境変数とファイルが自動でマウントされるようになります。 Author @MtkN1 と全く同じ開発環境を自動で構築されるのでコントリビュートされる方は是非ご利用ください。

詳細は #202 をご覧ください。

💖 New Contributors

Full Changelog: v0.14.1...v0.15.0

v0.14.1 Bugfix 💊

21 Nov 12:12
8aec2db
Compare
Choose a tag to compare

🪧 What's Changed

  • Binance の DataStore で Spot の listenkey が更新されない問題を修正した by @MtkN1 in #196
  • KuCoinDataStore のポジションが正しく反映されないバグを修正した by @ko0hi in #198
  • ws_connect に heartbeat のデフォルト値を追加した by @MtkN1 in #201

Full Changelog: v0.14.0...v0.14.1

v0.14.0 アップデート ✨

13 Nov 16:26
d02dd00
Compare
Choose a tag to compare

🪧 What's Changed

  • Binance DataStore が Spot / COIN-M をサポートしました by @ko0hi in #189
  • KuCoin をサポートしました by @ko0hi in #185
  • Bitget Trade DataStoreで side の項目に間違った値が入っている問題を修正しました by @ko0hi in #183
  • bitFlyer Positions DataStore で watch 機能を利用した際、ポジションが解消されたイベントが受け取れない問題を修正しました by @ko0hi in #182

🚀 New Features

✨ Binance DataStore が Spot / COIN-M をサポートしました

BinanceDataStore は USDⓈ-M Futures トレードの WebSocket 配信情報のみをサポートしていましたが、このアップデートにより Spot と COIN-M のサポートを追加しました!
追加された Spot と COIN-M のストアも、従来の USDⓈ-M ストア同様に WebSocket から配信される板情報やオーダー情報などを利用できます。

1. 非推奨

既存のクラス pybotters.BinanceDataStore は USDⓈ-M のストアとして実装されていますが、このクラスは非推奨となり将来的に削除されます。
Spot / USDⓈ-M / COIN-M のストアはそれぞれ新しいクラス名で提供されます。 (以下参照)

2. リファレンス

実装されているストアのプロパティ変数名と、それに対応する WebSocket Streams を表します。

Spot

USDⓈ-M

COIN-M

3. サンプルコード

Spot, USDⓈ-M, COIN-M の User Data (残高、オーダー、ポジション) を watch 機能で監視するサンプルコードです。
このコードを実行中に Web 画面でオーダーを送信するなどすると、その情報が画面に表示されます。

import pybotters
import asyncio
from rich.pretty import pprint

apis = {"binance": ["KEY", "SECRET"]}


async def watcher(_stream):
    with _stream as stream:
        async for change in stream:
            pprint(change)


async def main():
    async with pybotters.Client(apis=apis) as client:
        pybotters.Binance...
Read more

Preview v0.13.1 アップデート✨

30 Jul 07:04
68d3cca
Compare
Choose a tag to compare

⏫ Improvements

  • FTXDataStore.orderbook に time プロパティを追加しました (Contributed by @sunbluesome 💖)

FTXDataStore では WebSocket から受信する Orderbooks データの "time" を利用していませんでしたが、プロパティ変数として格納するようになりました。

FTX - WebSocket- Orderbooks のデータ形式

{
    "channel": "orderbook",
    "market": "BTC-PERP",
    "type": "update",
    "data": {
        "time": 1657555432.5312576,
        "checksum": 804021281,
        "bids": [...],
        "asks": [...],
        "action": "update"
    }
}

参照方法

>>> store = pybotters.FTXDataStore()
...
>>> store.orderbook.time
1657555432.5312576
  • BinanceDataStore.kline を initialize できるようになりました (Contributed by @5QgKxT2j 💖)
async def main():
    async with pybotters.Client(base_url="https://fapi.binance.com") as client:
        store = pybotters.BinanceDataStore()
        await store.initialize(
            client.get(
                "/fapi/v1/klines",
                params={
                    "symbol": "BTCUSDT",
                    "interval": "1m",
                    "limit": "10",
                },
            )
        )
        print(store.kline.find())
  • BitMEXDataStore に各種 tradeBin と quoteBin プロパティを追加しました

添え字アクセスのみ可能だった tradeBin* と quoteBin* トピックをストアのプロパティに追加しました。

>>> store = pybotters.BitMEXDataStore()
>>> store.quotebin
store.quotebin1m	store.quotebin5m	store.quotebin1h	store.quotebin1d
>>> store.tradebin
store.tradebin1m	store.tradebin5m	store.tradebin1h	store.tradebin1d
  • pybotters の User Agent を設定しました

各取引所に API リクエストをすると pybotters クライアントあることが分かるようになります。

>>> async with client.get("https://httpbin.org/get") as resp:
...     data = await resp.text()
>>> print(data)
{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Host": "httpbin.org",
    "User-Agent": "pybotters/0.13.1"
  },
  "url": "https://httpbin.org/get"
}

🐛 Bigfix

  • BinanceDataStore.order.initialize において symbol の初期化が正しく行われない問題を修正しました

  • MEXC の GET の REST API において認証が正しく行われるよう修正しました

  • WebSocket クラスのコネクション確認変数の Typo を修正しました

ws = await client.ws_connect(...)
- ws.conneted  # Incorrect
+ ws.connected  # Corrent

👨‍💻👩‍💻 pybotters Development

(必読) pybotter コントリビューターの方へ

Contributing Wiki ページを刷新しました
https://github.com/MtkN1/pybotters/wiki/Contributing

  • VS Code 用の .devcontainer を定義しました (Contributed by @tk42 💖)

リポジトリに .devcontainer ディレクトリを追加しました。
これは VS Code の Remote - Container 拡張機能による開発コンテナの作成機能です。
マシンに VS Code と Docker がインストールされている場合は、"Reopen in Container" をクリックすることで必要な Python バージョンやその他ソフトウェアやライブラリ、VS Code の細かい設定が自動で行われます。
pybotters コントリビューターの方は是非利用してみてください!

  • リポジトリ及びブランチの運用方針を更新しました

develop ブランチの運用を廃止して main ブランチを開発ブランチとして運用するようにしました。
GitHub Flow に則る形になります。
(著者 MtkN1 によるメンテ方法についても、main ブランチを直接コミットせず必ず Pull Request を通して行うようになります)

詳しくは Wiki ページをご覧ください。

Issues

✅ mainブランチの運用方法についての検討する #104
✅ VS Code の devcontainer で pybotters 開発環境を用意する #164
✅ FTXのOrderBookにtimeを追加する #168
✅ BinanceDataStore.order.initialize による初期化時にレコードが削除されない #170
✅ User Agent を設定する #171
✅ MEXC の REST API が一部認証がエラーになる #173
✅ BitMEXDataStore のプロパティに tradeBin や quoteBin を追加する #174
✅ WebSocket 接続状態変数の Typo #175

Pull requests

✅ Added devcontainer.json and Dockerfile #165
✅ add time for Orderbook #169
✅ BinanceのKlineをREST APIのレスポンスでDataStoreを初期化可能にする #178

Full Changelog: v0.13.0...v0.13.1

Preview v0.13.0 アップデート✨

06 Jun 18:01
688f918
Compare
Choose a tag to compare

🚀 New Features

✨ DataStore の変更を監視する watch 機能を追加しました

store.xxx.wait メソッドの置き換えとなる store.xxx.watch メソッドを追加しました!
このメソッドを利用することで、より手軽に WebSocket からのデータ監視を行う事ができます。
2つのユースケースを用いて、 store.xxx.watch の機能と store.xxx.wait との違いを説明します。

ユースケース: bitFlyer でパブリックの約定履歴を監視する

async def main():
    async with pybotters.Client() as client:
        store = pybotters.bitFlyerDataStore()
        await client.ws_connect(
            "wss://ws.lightstream.bitflyer.com/json-rpc",
            send_json={"method": "subscribe", "params": {"channel": "lightning_executions_FX_BTC_JPY"}},
            hdlr_json=store.onmessage,
        )

        with store.executions.watch() as stream:
            async for change in stream:
                print(f"operation: {change.operation}")
                print(f"data: {change.data}")
                # operation: insert
                # data: {'product_code': 'FX_BTC_JPY', 'id': 2343846871, ...}

store.executions.watch メソッドを with 構文でコンテキストマネージャーを開くことで変更ストリームクラス StoreStreamstream 変数を得ます。
stream を async for 構文で非同期イテレーションをすることで、約定履歴ストアに変更があるまで待機し変更データクラス StoreChangechange 変数を得ます。
change には 2 つのプロパティがあります。
change.operation はその変更がどの性質( {insert,update,delete} )であるかを表す文字列が格納されます。
change.data は変更されたレコード(辞書)が格納されます。

async for のイテレートは break などで抜けない限り永久繰り返されます。
ループが必要なく任意回数の待機を行いたい場合は、 async for 構文で実行される同等のメソッド stream.get を await 文で呼び出してください。

store.xxx.waitstore.xxx.watch の大きな違いは以下の通りです。

  • store.xxx.wait
    • 結果を辞書のリスト( [item1, item2, ...] )で受け取る
    • 変更の性質を受け取れない
    • 内部実装が試験的であり( #77 )、メソッド名が体を表していない
  • store.xxx.watch
    • 結果を辞書で受け取る
    • 変更の性質を受け取れる
    • 非同期キューで実装されており、メソッド名が体を表している

store.xxx.wait は将来的に値を返さない変更を検討しています。
store.xxx.wait の戻り値を利用しているユーザーは store.xxx.watch にマイグレーションをお願いします。

ユースケース: bitFlyer オーダーキャンセルの反映を待つ

async def main():
    async with pybotters.Client(base_url="https://api.bitflyer.com") as client:
        store = pybotters.bitFlyerDataStore()
        await store.initialize(
            client.get(
                "/v1/me/getchildorders?product_code=FX_BTC_JPY&child_order_state=ACTIVE"
            ),
        )
        await client.ws_connect(
            "wss://ws.lightstream.bitflyer.com/json-rpc",
            send_json={
                "method": "subscribe",
                "params": {"channel": "child_order_events"},
                "id": 1,
            },
            hdlr_json=store.onmessage,
        )
        await store.wait()

        child_order_id = next(iter(store.childorders), {}).get("child_order_id", None)
        if not child_order_id:
            return
        with store.childorders.watch() as stream:
            data = {"product_code": "FX_BTC_JPY", "child_order_id": child_order_id}
            print(f"Request cancelchildorder for {child_order_id}")
            async with client.post("/v1/me/cancelchildorder", data=data):
                pass  # cancelchildorder dose not return value
            print("Waiting for cancel ...")
            async for change in stream:
                if change.data["child_order_id"] == child_order_id:
                    print("Cancel event received")
                    break

bitFlyer FX_BTC_JPY に1つ以上の childorder がある時に上記コードを実行すると、REST API で1つの childorder をキャンセルし、 WebSocket からキャンセル完了イベントによって DataStore からレコードが削除されるまで待機します。

このようなイベントを待つユースケースで発生する問題の1つとして「REST API の応答より WebSocket の応答の方が速い」という事象があります。
これは取引所側サーバーの仕様に起因するもので、store.xxx.wait を利用するとこの対処が難しくなっていました。
つまり REST API を叩き終わった後には DataStore に変更が適用されており、その後 wait すると所謂無限待機になってしまいます。

今回実装した store.xxx.watch は with 構文でコンテキストマネージャーを開いた際に内部でキューを生成しています。
コンテキストマネージャーの中では REST API を叩いても(または他の非同期処理を行っても)、 WebSocket による DataStore の変更はキューイングされるので確実にデータを取得できます。
結果としてそのようなユースケースを、上記サンプルコードのよう簡単かつ安全に実装できます。

🐛 Bugfix

  • bitFlyerDataStore のポジションがずれる問題を修正しました (Contributed by @shinji19 💖)
  • bitFlyerDataStore のポジションが初期化するたびに増える問題を修正しました (Contributed by @shinji19 💖)
  • BinanceDataStore の listen key の keep alive を testnet でも動作するように修正しました (Contributed by @LopeRope 💖)
  • BinanceDataStore のポジションの初期化時に "up" (Unrealized Profit) のキーを追加しました (Contributed by @WannabeBotter 💖)

🌈 Experimental

イベントリスナー機能の追加や設計刷新に関する大幅改修のブランチをリポジトリに Push しました。
様々な課題の解決の為の刷新で、将来的な正式バージョンのベースになります。

これを試したい先進的なユーザーはリポジトリをクローンしブランチ experimental を pip または poetry でインストールしてください。
簡単な説明とマイグレーションの為に experimental ブランチの pybotters/experimental ディレクトリに README.md があります。

https://github.com/MtkN1/pybotters/tree/experimental/pybotters/experimental

Issues

✅ DataStoreにデータのwatch機能を追加する #155

Pull requests

✅ Fix Binance listenKey keepalive #158
✅ Store unRealizedProfit info from riskPosition REST API to datastore #160
✅ bitFlyerのポジションがずれる問題の対応 #161
✅ bitFlyerのDataStoreでポジションを初期化するたびに増える問題の対応 #162

Preview v0.12.0 アップデート✨

31 Mar 17:30
ecb1ea6
Compare
Choose a tag to compare

🚀 New Features

✨ 取引所 MEXC の API 認証をサポートしました

新たに海外取引所の MEXC をサポートしました 🚀
MEXC は取り扱いが銘柄が豊富で、複数のチェーンの入出金に対応しているのが特徴の取引所です。

Private REST / WebSocket API による DataStore クラスを利用できます。
(DataStore は今後サポート予定です)

MEXC API ドキュメント:
https://mxcdevelop.github.io/APIDoc/ (v1, v2)
https://mxcdevelop.github.io/apidocs/spot_v3_en/ (v3)

このリリース現在 MEXCには v1, v2, v3 の3種類の API エンドポイントが存在しています。
v2 と v3 が Spot 取引用、v1 が Contract 取引専用となっている模様です。
WebSocket ついては Contract 用の v1 のみ存在しており、Spot 用の v2, v3 にはありません。
どれも API 仕様が若干異なっていたりするので、他の取引所より多少難しいかもしれません。

pybotters ではこれら v1 ~ v3 全ての API 認証に対応しています ✨
(ccxt は v3 には未対応です!)

サンプルコード

オープンオーダーの取得、オーダーの送信、WebSocket のサンプルコードです。

async def mexc_v3():
    async with pybotters.Client(base_url="https://api.mexc.com") as client:
        # Get open orders
        r = await client.get(
            "/api/v3/openOrders",
            params={"symbol": "BTCUSDT"}
        )
        data = await r.json()
        print(data)

        # Place order
        r = await client.post(
            "/api/v3/order",
            data={
                "symbol": "BTCUSDT",
                "side": "BUY",
                "type": "MARKET",
                "quoteOrderQty": "5",
        )
        data = await r.json()
        print(data)

async def mexc_v1():
    async with pybotters.Client(base_url="https://contract.mexc.com") as client:
        # Get open orders
        r = await client.get(
            "/api/v1/private/order/list/open_orders/BTC_USDT",
        )
        data = await r.json()
        print(data)

        # Place order
        r = await client.post(
            "/api/v1/private/order/submit",
            data={
                "symbol": "BTC_USDT",
                "price": "40000.0",
                "vol": "10", # contract vol
                "side": 1,
                "type": 1,
                "openType": 1,
            }
        )
        data = await r.json()
        print(data)

        # WebSocket login authentication
        ws = await client.ws_connect(
            "wss://contract.mexc.com/ws",
            send_json={"method": "push.personal.order", "param": {}},
        )
        await ws

⏫ Improvements & Bugfix

  • ✨ 1つの API 情報を複数の pybotters.Client に渡たせるようになりました

こうしたとき apis が参考渡しだったので、バイト化の関係上2回目でエラーになっていましたが値渡しをするように変更することで解消しました。

apis = {"mexc": ["API_KEY", "API_SECRET"]}

try:
    client1 = async with pybotters.Client(apis=apis)
    client2 = async with pybotters.Client(apis=apis) # before: error, after: ok
    ...
finally:
    await client1.close()
    await client2.close()
  • ✨ 空の API 情報を指定できるようになりました

apis に空辞書 {} を指定しても、環境変数などが設定されているとそちらの apis を優先されていましたが、 {} が優先されようになり明示的に認証なしで利用できるようになりました。

  • ✨ Phemex と Bitget の initialize でエラーが表示されるようになりました (Contributed by @kunmosky1 💖)

Phemex と Bitget の DataStore で REST API の initialize が失敗したとき、エラーが表示されるようになります。

Issues

✅MEXCのAPI認証に対応する #142
✅ソースコードの文字列クォーテーションを正規化する #145
⏩試験的に既存の機能を分離し抽象化を強化する #147
✅API情報が参照渡しなので元のAPI情報を書き換えてしまう #149
✅空の辞書をapisに渡すと意図しない暗黙的な読み込みをする #151

Pull requests

✅initializeの際のエラー時(主に認証エラー)にエラーメッセージを表示するように #144

Full Changelog: v0.11.1...v0.12.0