An advanced api client for python botters.

Overview

[ALPHA] pybotters

An advanced api client for python botters.

📌 Description

pybotters仮想通貨botter向けのPythonライブラリです。複数取引所に対応した非同期APIクライアントであり、bot開発により素晴らしいDXを提供します。

🚀 Features

  • HTTP / WebSocket Client
    • 複数取引所のプライベートAPIを自動認証
    • aiohttpライブラリを基盤とした非同期通信
    • WebSocketの自動再接続、自動ハートビート
  • DataStore
    • WebSocket用の自動データ保管クラス
    • 参照渡しによる高速なデータ参照
    • 取引所別データモデルの実装
  • Developer Experience
    • asyncioライブラリを利用した非同期プログラミング
    • typingモジュールによる型ヒントのサポート

🏦 Exchanges

Name API auth DataStore
Bybit WIP
BTCMEX WIP
Binance WIP
bitFlyer WIP WIP
GMO Coin WIP WIP
Liquid WIP WIP
bitbank WIP WIP
FTX WIP WIP
BitMEX WIP WIP

※表の順番は著者個人の優先度順です

🐍 Requires

Python 3.7+

🛠 Installation

pip install git+https://github.com/MtkN1/pybotters

🔰 Usage

Single exchange

import asyncio
import pybotters

apis = {
    'bybit': ['BYBIT_API_KEY', 'BYBIT_API_SECRET'],
}

async def main():
    async with pybotters.Client(apis=apis, base_url='https://api.bybit.com') as client:
        # REST API
        resp = await client.get('/v2/private/position/list', params={'symbol': 'BTCUSD'})
        data = await resp.json()
        print(data)

        # WebSocket API (with print handler)
        ws = await client.ws_connect(
            url='wss://stream.bybit.com/realtime',
            send_json={'op': 'subscribe', 'args': ['trade.BTCUSD', 'order', 'position']},
            hdlr_json=lambda msg, ws: print(msg),
        )
        await ws # this await is wait forever (for usage)

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

Multiple exchanges

apis = {
    'bybit': ['BYBIT_API_KEY', 'BYBIT_API_SECRET'],
    'btcmex': ['BTCMEX_API_KEY', 'BTCMEX_API_SECRET'],
    'binance': ['BINANCE_API_KEY', 'BINANCE_API_SECRET'],
}

async def main():
    async with pybotters.Client(apis=apis) as client:
        await client.post('https://api.bybit.com/v2/private/order/create', data={'symbol': 'BTCUSD', ...: ...})
        ...
        await client.post('https://www.btcmex.com/api/v1/order', data={'symbol': 'XBTUSD', ...: ...})
        ...
        await client.post('https://dapi.binance.com/dapi/v1/order', data={'symbol': 'BTCUSD_PERP', ...: ...})
        ...

🗽 License

MIT

💖 Author

https://twitter.com/MtkN1XBt

Comments
  • KuCoinのサポート

    KuCoinのサポート

    KuCoin(公式ドキュメント)の実装をしています!

    実装状況

    • [x] Auth
    • [x] Socket
    • [x] DataStore

    KuCoinのソケット接続の仕様に対応するために、新たにDynamicEndpointDynamicEndpointHostsというクラスを導入しました。こちらはWebSocketRunner._run_foreverの最初に呼びだされ、URLの動的解決を行います。この仕様上静的なエンドポイントが存在しないため、ws_connectに渡すURLはkucoinとしました(以下のサンプルが参照ください)。

    現状以下ができており、順にストアを作っていこうかと思っています。

    • ソケットの接続確認(public/private)
    • REST APIの認証確認
    class WebSocketRunner:
    
        ... 
    
      async def _run_forever(
    
           ...
    
        ) -> None:
            if url in DynamicEndpointHosts.items:
                url = await DynamicEndpointHosts.items[url](url, session)
    
            ...
    
    class DynamicEndpoint:
        @staticmethod
        async def kucoin(url: str, session: aiohttp.ClientSession):
            api_keys = session.__dict__["_apis"]
            if "kucoin" in api_keys:
                # public and private
                from pybotters.auth import Auth
                resp = await session.post(
                    "https://api-futures.kucoin.com/api/v1/bullet-private", auth=Auth
                )
            else:
                # public only
                resp = await session.post(
                    "https://api-futures.kucoin.com/api/v1/bullet-public"
                )
            data = (await resp.json())["data"]
            token = data["token"]
            server = data["instanceServers"][0]
            endpoint = server['endpoint']
            id = str(uuid.uuid4())
            host = aiohttp.typedefs.URL(endpoint).host
            HeartbeatHosts.items[host] = Heartbeat.kucoin
            return f"{endpoint}?token={token}&acceptUserMessage=true&connectId={id}"
    
    
    class DynamicEndpointHosts:
        items = {
            "kucoin": DynamicEndpoint.kucoin
        }
    
    

    サンプルコード

    import pybotters
    import uuid
    
    with pybotters.Client(base_url="https://api-futures.kucoin.com") as client:
        # socket 接続
        # kucoinのソケットエンドポイントは動的に決まるので、"kucoin"と渡すことでエンドポイントの解決は内部で行なれる
        await client.ws_connect(
            "kucoin",
            send_json=[
                {
                    "id": str(uuid.uuid4()),
                    "type": "subscribe",
                    "topic": "/contractMarket/tickerV2:XBTUSDM",
                    "response": True
                },
        
                {
                    "id": str(uuid.uuid4()),
                    "type": "subscribe",
                    "topic": "/contractMarket/tradeOrders:XBTUSDM",
                    "privateChannel": True,
                    "response": True
                }
            ],
        )
    
        # get tick
        await client.get("/api/v1/ticker", params=dict(symbol="XBTUSDTM"))
        
        # order
        await client.post(
            "/api/v1/orders",
            data=dict(
                clientOid=str(uuid.uuid4()),
                side="buy",
                symbol="XBTUSDTM",
                type="limit",
                leverage="1",
                price="19200",
                size=1
            ),
        )
    
    opened by ko0hi 15
  • GMO コイン用の DataStore を一部実装する

    GMO コイン用の DataStore を一部実装する

    related: #60



    • できるだけエディタの支援を効かせるため、積極的に型を書いています。
      • 値の一覧が既知のプロパティについては列挙型を定義しています。(レファレンス > パラメータ など)
      • 各データは TypedDict として実装してあります。独自クラスではなく TypedDict 型を採用した理由は、現状データストアが取り扱うデータの型が Dict[str, Any] である Item であるため、既存データストアの実装を変更せずに互換性を持たせられるためです。(データの出し入れ時に Item 型との cast をしています)
    • GMO コインは REST API と WebSocket API で同じ種類のデータについても返却データの型が一致していないため、片方にしか存在しないプロパティは Optional 型になっています。
    enhancement 
    opened by supermomonga 10
  • Binanceのデータストアの拡張

    Binanceのデータストアの拡張

    (参照:#172, #187)

    目標

    • Coin-M・Spot用のデータストアを実装する

    実装方針(https://github.com/MtkN1/pybotters/issues/187#issuecomment-1287162492) 

    • BinanceDataStoreBaseという抽象クラスを用意し、その実装として BinanceSpotDataStoreBinanceUSDSMDataStoreBinanceCOINMDataStoreを提供する
    • Spot・USD-M・Coin-Mの共通化が難しい場合、抽象クラスは用意せずに冗長的にクラスを分ける

    タスク

    • [x] Spotのデータ構造を確認
    • [x] Coin-Mのデータ構造を確認
    • [x] 実装方針(抽象 or 冗長)の決定
    • [x] BinanceUSDSMDataStoreの実装(現行のBinanceDataStoreの移行)
    • [x] BinanceSpotDataStoreの実装
    • [x] BinanceCOINMDataStoreの実装
    opened by ko0hi 7
  • FTXDataStore の orders チャネルにおける close されたオーダーの取り扱いについて

    FTXDataStore の orders チャネルにおける close されたオーダーの取り扱いについて

    FTXDataStore を使用して WebSocket API を利用した BOT を作成しています。 そのなかで IOC オーダーを post した後、その執行状況(どの程度fillされたかなど)を確認してから次のループを実行したいと思っています。

    このとき store.orders を利用して執行状況を確認できればよいのですが、現状の実装では close されたオーダーは store.orders から削除される実装になっています。 一方で IOC オーダーは直ちに close されるため、 store.orders からは取得できず、やむなく都度 REST API を発行して対応しています。

    一応実装はできているのですがレートリミットもこわいので、 store.orders から close されたオーダーも取得できるようにはならないでしょうか。よろしくお願いします。

    opened by kaznak 7
  • bitbank datastore 追加

    bitbank datastore 追加

    内容

    • bitbank datastore の追加
    • public websocketのtransactions, depthに対応
    • Heartbeatで15秒ごとに"2"を送信
    • FTXのデータストアを流用して作成
    • 板情報では通貨ペアのキーをpairとしています(FTXではsymbol)
    • (参考)bitbankのwebsocketドキュメント https://github.com/bitbankinc/bitbank-api-docs/blob/master/public-stream_JP.md
    enhancement 
    opened by Osaifu-Crypto 7
  • Store unRealizedProfit information from Binance positionRisk API to Binance position datastore

    Store unRealizedProfit information from Binance positionRisk API to Binance position datastore

    Even though "up" (Unrealized Profit) information in WebSocket ACCOUNT_UPDATE event is properly processed and stored by Binance position datastore, Binance position datastore ignores "unRealizedProfit" information when it receives position data from /fapi/v2/positionRisk REST API,

    This pull request fixes the inconsistency.

    invalid 
    opened by WannabeBotter 4
  • GMOCoinDataStoreがTypeErrorで落ちる。

    GMOCoinDataStoreがTypeErrorで落ちる。

    課題

    下記のエラーでGMOCoinDataStoreが落ちてしまいます。

    cast(list[Item], data)cast(list, data)の間違いではないでしょうか。

    Traceback (most recent call last):
        ...
        self._insert(cast(list[Item], data))
    TypeError: 'type' object is not subscriptable
    

    再現コードと当該箇所(cast(list[Item], data)となっている箇所は他にもいくつかありました)です。

    async def main():
        async with pybotters.Client(base_url="https://api.coin.z.com") as client:
            store = pybotters.GMOCoinDataStore()
            await store.initialize(
                client.get("/private/v1/positionSummary"),
            )
    

    https://github.com/MtkN1/pybotters/blob/5f3558a50e93dbb33597579b240e967a40630bc3/pybotters/models/gmocoin.py#L378

    チェックリスト

    • [x] Fix
    bug 
    opened by kohilin 4
  • Sphinxのドキュメントを追加

    Sphinxのドキュメントを追加

    以下、Read The Docs側の手順です

    Read The Docsにアクセスします

    これから登録する場合はGitHubアカウントがおすすめです

    https://readthedocs.org

    image

    「プロジェクトを取り込む」をクリックします

    image

    「リポジトリをimport」画面で、対象のリポジトリ( MtkN1/pybotters )の右に表示されている「+」をクリックします

    image

    mainブランチにマージされている場合は「次へ」をクリックします、ブランチを変更する場合には「デフォルトブランチ」にブランチ名を入力します

    image

    自動的にビルドが開始します、「ビルド」をクリックするとビルド状況が確認できます

    image

    ビルドが成功していたら、「開く」をクリックすると、デプロイされたドキュメントが表示されます

    image

    image

    documentation 
    opened by drillan 4
  • GMOコインのWebSocket API制限により複数購読ができない

    GMOコインのWebSocket API制限により複数購読ができない

    課題

    • pybottersは ws_connect メソッド send_json 引数にリストで渡すことでsubscribeメッセージを非同期で投げる仕様としている
    • しかしGMOコインのWebSocket API(Public/Private)においては以下の様な制限があるため {"error":"ERR-5003 Request too many."} が返却されて2つ以上のチャンネルの購読ができない
    • https://api.coin.z.com/docs/#restrictions
      • 同一IPからのリクエスト(subscribe, unsubscribe)は、1秒間1回を上限とします。

    • 詳細はsupermomonga氏のGist(GMOコインへの要望書)を参照
      • https://gist.github.com/supermomonga/2283f57bef66a69ab158072e8c3a8c28

    検討

    1. ws_connect メソッドに onopen のような引数を持たせて購読についてもユーザーに制御可能にする
    2. GMOコインのみ分岐させてpybotters側で別制御にする

    チェックリスト

    • [x] 実装
    • [x] Wiki
    bug enhancement 
    opened by MtkN1 4
  • Binance Spotでprivate weboscoketのlisten keyを取得する際のAuthエラー

    Binance Spotでprivate weboscoketのlisten keyを取得する際のAuthエラー

    Binance SpotにてUserDataStream websocketのlisten keyを取得しようとすると下記のエラーが出ます。

     {'code': -1101, 'msg': "Too many parameters; expected '0' and received '2'."}
    

    問題としては #44 と同じなのですが、private websocketのlisten keyの取得にはauthが必要なため 「auth=Noneを与えることで回避する」という方法を取ることができません。

    回避策としては

    ①. Binanceのauth内で条件分岐する ②. ①に加え、spot用のauthは別に作ることで見通しを良くする

    くらいしか思いついてないのですが、他にどういった対処法が考えうるでしょうか。

    なお、COIN-Mの方では現状のauthで問題なく取得することができました。

    サンプルコード

    import asyncio
    import pybotters
    
    async def main():
        async with pybotters.Client(apis=..., base_url="https://api.binance.com") as client:
            d = await (await client.post("/api/v3/userDataStream")).json()
            print(d)  #  {'code': -1101, 'msg': "Too many parameters; expected '0' and received '2'."}
    
    if __name__ == "__main__":
        asyncio.run(main())
    
    

    関連issue・PR: #187 #188 #189

    opened by ko0hi 3
  • KuCoinで予期せぬウェブソケットエンドポイントが発行される

    KuCoinで予期せぬウェブソケットエンドポイントが発行される

    問題

    動的にソケットのエンドポイントを定めるKuCoinの実装において、ws-api.kucoin.comをデフォルトエンドポイントとしてハードコードしているが、異なるエンドポイントが割り当てられるのを確認しました。 Heartbeatが送られず接続が切断されてしまいます。

    状況は以下の通りです。

    • 同じネットワーク・同じソースコードの別マシンにおいて起きた
    • ws-api-spot-kucoin.comが発行された
    • ずっと起きていたわけではない(接続が確立していたので最初のうちはws-api-kucoin.comが発行されていたはず)

    対策

    https://github.com/MtkN1/pybotters/pull/185#issuecomment-1285856234

    では heartbeat=10.0がデフォルトで設定されているとのことでしたがこちらそうなっていますでしょうか? pybottersのソースコードにそのような設定を見つけられず、aiohttpを辿っても見つけられませんでした(ただの見逃しでしたらすみません)。

    ws-api-kucoin.comに対してHertbeatを送るよう修正したところ切断されなくなったので、やはりHeartbeatが届いていないのが原因のように思えます。

    opened by ko0hi 1
  • DataStore でのオーダー約定時など変更ストリームの統一性を確認する

    DataStore でのオーダー約定時など変更ストリームの統一性を確認する

    概要

    例えば現状の bitFlyerDataStore では指値オーダーが約定した際、変更ストリームには operation: delete で「未約定数が 0 にはなっていないレコード」が put される。 これは bitFlyerDataStore 実装で約定イベント時に「該当のレコードを削除する操作」のみを行っているためであり、つまりは「該当のレコードを更新してから削除」を行っていない為である。 多くのストアは実装時に watch 機能はなかったので、削除されるレコードを一旦更新するといった冗長性を持たせてはいなかった。

    この仕様では、ユーザーは watch 機能からは削除された約定の最新の状態が取れない。 オーダー情報という 1 件のレコードのデータ遷移としても、未約定数が 0 になってから削除されるのが望ましい。

    • 削除操作が実装されている各ストアについて統一性を確認する
      • 配信回数の多い板情報については、この冗長性を持たせるか検討する
    opened by MtkN1 2
  • Binance の認証 (Security Type) 仕様に準拠していない

    Binance の認証 (Security Type) 仕様に準拠していない

    課題

    Binance のドキュメントによると、API 認証の Security Type は 3 つに分かれている。

    https://binance-docs.github.io/apidocs/spot/en/#endpoint-security-type

    1. Headers に API キーなし、 Signature なし (Public 系)
    2. Headers に API キーあり、 Signature あり (Private 系)
    3. Headers に API キーなし、 Signature なし (Token 系)

    Security Type | Description -- | -- NONE | Endpoint can be accessed freely. TRADE | Endpoint requires sending a valid API-Key and signature. MARGIN | Endpoint requires sending a valid API-Key and signature. USER_DATA | Endpoint requires sending a valid API-Key and signature. USER_STREAM | Endpoint requires sending a valid API-Key. MARKET_DATA | Endpoint requires sending a valid API-Key.

    pybotters は認証を無効にすると 1. 、 認証を有効にすると 2. のリクエストが行えるが、 3. の認証については対応できていない。 結果、 Spot / Margin において User Data Streams の Token リクエスト /fapi/v1/listenKey がエラーとなっている。

    API 本番環境では Spot / Margin の API サーバーのみエラーを返され、 USDⓈ-M / COIN-M の API サーバーではこの仕様に準拠していなくても (3. の認証でも) リクエストは成功となっている。

    #190 で Spot エンドポイントの簡易的な修正を行うが、 pybotters はライブラリとしてこの仕様に準拠することが必要か、見通しよく実現可能か検討する。

    調査

    • [x] Security Type がエンドポイントパスから判別可能か調査する (全てを網羅するのは現実的ではない)
      • -> 判別不可能

    対応方針

    ユーザー自身において、リクエストするエンドポイントの Security Type ごとに認証を分ける必要がある。 なお v0.14.0 においては以下の通り、一部認証不可となっている。

    v0.14.0 with Spot エンドポイントにおいての現状

    Security Type | Description -- | -- NONE | 自動認証が入るとエラーになる、リクエストの引数 auth=None で認証を無効にする TRADE | 自動認証可能 MARGIN | 自動認証可能 USER_DATA | 自動認証可能 USER_STREAM | /api/v3/userDataStream のみハードコードされ認証可能、それ以外はエラーとなり認証不可能 MARKET_DATA | エラーとなり認証不可能

    experimental ブランチ #147 での本対応

    次期 pybotters.Client では以下のように認証オブジェクトをモジュール化する予定である。

    auth = pybotters.SomeExchangeAuth(key="...", secret="...")
    r = await client.get("/path/to/endpoint", auth=auth)
    

    これが可能になれば、Binance の Security Type ごとの認証を実装することでユーザー自身で正常な認証を選択可能になる。 この Issue は上記が実現されたらクローズする。

    • [ ] 認証オブジェクトがモジュール化される
    • [ ] Security Type ごとの認証クラスを実装する

    Originally posted by @MtkN1 in https://github.com/MtkN1/pybotters/issues/190#issuecomment-1297153865

    opened by MtkN1 1
  • experimental の _raw_session について

    experimental の _raw_session について

    Discordより: https://discord.com/channels/832651305155297331/832653694839160892/997056199218438174

    _raw_session について、この仕組みが experimental においても必要が検討して必要なら experimental にも互換性も持たせる

    bug 
    opened by MtkN1 0
Releases(v0.15.0)
  • v0.15.0(Dec 30, 2022)

    🪧 What's Changed

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

    詳細は https://github.com/MtkN1/pybotters/pull/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 を利用することもできます。

    詳細は https://github.com/MtkN1/pybotters/pull/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 プロパティで影響元のストアが渡されます。 プログラムはこのプロパティを利用してストアのデータを取得することが可能になります。

    詳細は https://github.com/MtkN1/pybotters/pull/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 と全く同じ開発環境を自動で構築されるのでコントリビュートされる方は是非ご利用ください。

    詳細は https://github.com/MtkN1/pybotters/pull/202 をご覧ください。

    💖 New Contributors

    • @meetaco made their first contribution in https://github.com/MtkN1/pybotters/pull/204

    Full Changelog: https://github.com/MtkN1/pybotters/compare/v0.14.1...v0.15.0

    Source code(tar.gz)
    Source code(zip)
  • v0.14.1(Nov 21, 2022)

    🪧 What's Changed

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

    Full Changelog: https://github.com/MtkN1/pybotters/compare/v0.14.0...v0.14.1

    Source code(tar.gz)
    Source code(zip)
  • v0.14.0(Nov 13, 2022)

    🪧 What's Changed

    • Binance DataStore が Spot / COIN-M をサポートしました by @ko0hi in https://github.com/MtkN1/pybotters/pull/189
    • KuCoin をサポートしました by @ko0hi in https://github.com/MtkN1/pybotters/pull/185
    • Bitget Trade DataStoreで side の項目に間違った値が入っている問題を修正しました by @ko0hi in https://github.com/MtkN1/pybotters/pull/183
    • bitFlyer Positions DataStore で watch 機能を利用した際、ポジションが解消されたイベントが受け取れない問題を修正しました by @ko0hi in https://github.com/MtkN1/pybotters/pull/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.BinanceDataStore()
            sstore = pybotters.BinanceSpotDataStore()
            fstore = pybotters.BinanceUSDSMDataStore()
            dstore = pybotters.BinanceCOINMDataStore()
    
            await asyncio.gather(
                sstore.initialize(
                    client.get("https://api.binance.com/api/v3/openOrders"),
                    client.get("https://api.binance.com/api/v3/openOrderList"),
                    client.get("https://api.binance.com/api/v3/account"),
                    client.post("https://api.binance.com/api/v3/userDataStream"),
                ),
                fstore.initialize(
                    client.get("https://fapi.binance.com/fapi/v1/openOrders"),
                    client.get("https://fapi.binance.com/fapi/v2/balance"),
                    client.get("https://fapi.binance.com/fapi/v2/positionRisk"),
                    client.post("https://fapi.binance.com/fapi/v1/listenKey"),
                ),
                dstore.initialize(
                    client.get("https://dapi.binance.com/dapi/v1/openOrders"),
                    client.get("https://dapi.binance.com/dapi/v1/balance"),
                    client.get("https://dapi.binance.com/dapi/v1/positionRisk"),
                    client.post("https://dapi.binance.com/dapi/v1/listenKey"),
                ),
            )
    
            await asyncio.gather(
                client.ws_connect(
                    f"wss://stream.binance.com:9443/stream?streams={sstore.listenkey}",
                    hdlr_json=sstore.onmessage,
                ),
                client.ws_connect(
                    f"wss://fstream.binance.com/stream?streams={fstore.listenkey}",
                    hdlr_json=fstore.onmessage,
                ),
                client.ws_connect(
                    f"wss://dstream.binance.com/stream?streams={dstore.listenkey}",
                    hdlr_json=dstore.onmessage,
                ),
            )
    
            await asyncio.gather(
                # Spot
                watcher(sstore.account.watch()),
                watcher(sstore.ocoorder.watch()),
                watcher(sstore.order.watch()),
                # USDⓈ-M
                watcher(fstore.balance.watch()),
                watcher(fstore.position.watch()),
                watcher(fstore.order.watch()),
                # COIN-M
                watcher(dstore.balance.watch()),
                watcher(dstore.position.watch()),
                watcher(dstore.order.watch()),
            )
    
    
    if __name__ == "__main__":
        try:
            asyncio.run(main())
        except KeyboardInterrupt:
            pass
    

    ✨ KuCoin の API 認証 / DataStore をサポートしました

    700 種類以上のコインが上場し、CoinMarketCap の取引高 5 位の KuCoin をサポートしました! Spot / Futures 両方対応しており、API 認証及び DataStore を利用できます。

    1. リファレンス

    • class pybotters.KuCoinDataStore
      • coroutine initialize(*aws)
        • kline
        • positions
        • endpoint
          • /api/v1/bullet-public, /api/v1/bullet-private で取得された動的 WebSocket エンドポイントを格納する
      • ticker
      • kline
      • symbolsnapshot
      • orderbook5
      • orderbook50
      • execution
      • indexprice
      • markprice
      • orderevents
      • orders
      • balance
      • marginfundingbook
      • marginpositions
      • marginpositionevents
      • marginorderevents
      • marginorders
      • instrument
      • announcements
      • transactionstats
      • balanceevents
      • positions
      • endpoint
        • WebSocket の動的エンドポイント

    ※詳細な情報 (対応するエンドポイント及び WebSocket イベント) は docstrings に記載されています。

    2. サンプルコード

    Kline の取得、約定監視、板監視、オーダー のサンプルコードです。

    import asyncio
    import pybotters
    import uuid
    import rich
    
    
    async def sample1():
        # kline取得
        async with pybotters.Client(base_url="https://api.kucoin.com") as client:
            store = pybotters.KucoinDataStore()
    
            await store.initialize(
                client.get("/api/v1/market/candles?type=1min&symbol=BTC-USDT"),
                client.post("/api/v1/bullet-public"),
            )
            await client.ws_connect(
                store.endpoint,
                send_json=[
                    {
                        "id": str(uuid.uuid4()),
                        "type": "subscribe",
                        "topic": "/market/candles:BTC-USDT_1min",
                        "response": True,
                    },
                    {
                        "id": str(uuid.uuid4()),
                        "type": "subscribe",
                        "topic": "/market/candles:BTC-USDT_3min",
                        "response": True,
                    },
                ],
                hdlr_json=store.onmessage,
            )
    
            while True:
                rich.print(
                    [
                        # 未確定足
                        store.kline.latest("BTC-USDT", "1min"),
                        store.kline.latest("BTC-USDT", "3min"),
                        # 確定足
                        store.kline.find({"symbol": "BTC-USDT", "interval": "1min"})[-2:],
                        store.kline.find({"symbol": "BTC-USDT", "interval": "3min"})[-2:],
                    ]
                )
                await asyncio.sleep(1)
    
    
    async def sample2():
        # 約定監視
        async with pybotters.Client(base_url="https://api-futures.kucoin.com") as client:
            store = pybotters.KucoinDataStore()
            await store.initialize(client.post("/api/v1/bullet-public"))
            await client.ws_connect(
                store.endpoint,
                send_json=[
                    {
                        "id": str(uuid.uuid4()),
                        "type": "subscribe",
                        "topic": "/contractMarket/execution:XBTUSDTM",
                        "response": True,
                    }
                ],
                hdlr_json=store.onmessage,
            )
    
            with store.execution.watch() as stream:
                async for msg in stream:
                    rich.print(msg)
    
    
    async def sample3():
        # 板監視
        async with pybotters.Client(base_url="https://api-futures.kucoin.com") as client:
            store = pybotters.KucoinDataStore()
            await store.initialize(client.post("/api/v1/bullet-public"))
            await client.ws_connect(
                store.endpoint,
                send_json=[
                    {
                        "id": str(uuid.uuid4()),
                        "type": "subscribe",
                        "topic": "/contractMarket/level2Depth5:XBTUSDTM",
                        "response": True,
                    }
                ],
                hdlr_json=store.onmessage,
            )
    
            while True:
                await store.orderbook5.wait()
                rich.print(store.orderbook5.sorted())
    
    
    async def sample4():
        # 注文
        async with pybotters.Client(
            base_url="https://api-futures.kucoin.com",
            apis={
                "kucoinfuture": [
                    "YOUR_API_KEY",
                    "YOUR_SECRET_KEY",
                    "YOUR_PASSPHRASE",
                ]
            },
        ) as client:
            store = pybotters.KucoinDataStore()
            await store.initialize(client.post("/api/v1/bullet-private"))
            await client.ws_connect(
                store.endpoint,
                send_json=[
                    {
                        "id": str(uuid.uuid4()),
                        "type": "subscribe",
                        "topic": "/contractMarket/tradeOrders",
                        "privateChannel": True,
                        "response": True,
                    },
                ],
                hdlr_json=store.onmessage,
            )
    
            await client.post(
                "/api/v1/orders",
                data=dict(
                    clientOid=str(uuid.uuid4()),
                    side="buy",
                    symbol="XBTUSDTM",
                    type="limit",
                    price="18000",
                    leverage=1,
                    size=1,
                ),
            )
    
            await asyncio.sleep(1)
    
            rich.print(store.orders.find())
    
            await client.delete("/api/v1/orders")
    
            await asyncio.sleep(1)
    
            rich.print(store.orders.find())
    

    💊 Bugfix

    • BitgetDataStore.trade の "side" フィールドに "price" の値が入っていたバグを修正しました
    • DataStore において _remove メソッドが watch にデータをキューイングしていないバグを修正しました

    New Contributors

    • @ko0hi made their first contribution in https://github.com/MtkN1/pybotters/pull/182

    Full Changelog: https://github.com/MtkN1/pybotters/compare/v0.13.1...v0.14.0

    Source code(tar.gz)
    Source code(zip)
  • v0.13.1(Jul 30, 2022)

    ⏫ 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: https://github.com/MtkN1/pybotters/compare/v0.13.0...v0.13.1

    Source code(tar.gz)
    Source code(zip)
  • v0.13.0(Jun 6, 2022)

    🚀 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, ...] )で受け取る
      • 変更の性質を受け取れない
      • 内部実装が試験的であり( https://github.com/MtkN1/pybotters/pull/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

    Source code(tar.gz)
    Source code(zip)
  • v0.12.0(Mar 31, 2022)

    🚀 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: https://github.com/MtkN1/pybotters/compare/v0.11.1...v0.12.0

    Source code(tar.gz)
    Source code(zip)
  • v0.11.1(Mar 19, 2022)

    👾 Hotfix

    • OKX Demo trading で WebSocket 認証ができない問題を修正しました
    • Bitget の API 認証ができない問題を修正しました
    Source code(tar.gz)
    Source code(zip)
  • v0.11.0(Mar 19, 2022)

    🚀 New Features

    ✨ 取引所 OKX をサポートしました

    海外主要取引所の OKX をサポートしました。 Private API 認証及び WebSocket API による DataStore クラスを利用できます 🎉

    OKX API ドキュメント:
    https://www.okx.com/docs-v5/en/

    API 認証

    pybotters に渡す API 辞書に okx のキーで API 情報をリストで格納してください。 OKX は他の取引所と異なり API のパスフレーズが存在します。 リストの3要素目にパスフレーズを記載してください。

    {
        "...": ["...", "..."],
        "okx": ["OKX_API_KEY", "OKX_API_SECRET", "YOUR_PASSPHRASE"]
    }
    

    Demo Trading

    pybotters は OKX の Demo Trading に対応しています。 デモ版はリアルマーケットとは別の API キーの作成が必要です。 デモ版 API は okx_demo のキーで API 辞書に格納してください。 デモ版 API を選択してリクエストを行うには、ヘッダーに {"x-simulated-trading": "1"} を指定してください。

    async def main():
        apis = {"okx_demo": ["OKX_API_KEY", "OKX_API_SECRET", "YOUR_PASSPHRASE"]}
        headers = {"x-simulated-trading": "1"}
        async with pybotters.Client(apis=apis, headers=headers) as client:
            ...
    

    DataStore のプロパティ

    OKX の DataStore は WebSocket のチャンネルごとに紐づいています。

    Public (https://www.okx.com/docs-v5/en/#websocket-api-public-channel)

    • instruments
    • tickers
    • openinterest
    • candle
    • trades
    • estimatedprice
    • markprice
    • markpricecandle
    • pricelimit
    • books
    • optsummary
    • fundingrate
    • indexcandle
    • indextickers

    Private (https://www.okx.com/docs-v5/en/#websocket-api-private-channel)

    • account
    • positions
    • balance_and_position (※親クラス)
      • balance
      • position
    • orders
    • ordersalgo
    • algoadvance
    • liquidationwarning
    • accountgreeks

    (※) balance_and_position WebSocket から取得するデータ構造が階層構造になっています。 そのため store.balance_and_position.balance store.balance_and_position.position といったように子クラスを定義しています。 親クラスには差分処理していないイベントがメッセージが格納されています。

    DataStore の initialize

    OKX の DataStore は以下のエンドポイントの initialize に対応しています。

    • GET /api/v5/trade/orders-pending (DataStore: orders)
    • GET /api/v5/trade/orders-algo-pending (DataStore: ordersalgo, algoadvance)

    サンプルコード

    initialize と ws_connect で全チャンネルを接続するサンプルです。 while True のループでは試しに ticker 情報を表示しています。 コメントアウトの部分はデモ版 API です。

    import asyncio
    import pybotters
    
    async def main():
        # headers = {"x-simulated-trading": "1"}
        async with pybotters.Client(base_url="https://www.okx.com") as client:
            store = pybotters.OKXDataStore()
    
            # initialize
            await store.initialize(
                client.get("/api/v5/trade/orders-pending"),
                client.get("/api/v5/trade/orders-algo-pending?ordType=conditional"),
            )
    
            # ws_connect
            ws_public, ws_private = await asyncio.gather(
                client.ws_connect(
                    "wss://ws.okx.com:8443/ws/v5/public",
                    # "wss://wspap.okx.com:8443/ws/v5/public?brokerId=9999",
                    send_json={
                        "op": "subscribe",
                        "args": [
                            {"channel": "instruments", "instType": "SWAP"},
                            {"channel": "tickers", "instId": "BTC-USD-SWAP"},
                            {"channel": "open-interest", "instId": "BTC-USD-SWAP"},
                            {"channel": "candle1m", "instId": "BTC-USD-SWAP"},
                            {"channel": "trades", "instId": "BTC-USD-SWAP"},
                            {"channel": "estimated-price", "instType": "SWAP", "uly": "BTC-USD"},
                            {"channel": "mark-price", "instId": "BTC-USD-SWAP"},
                            {"channel": "mark-price-candle1m", "instId": "BTC-USD-SWAP"},
                            {"channel": "price-limit", "instId": "BTC-USD-SWAP"},
                            {"channel": "books", "instId": "BTC-USD-SWAP"},
                            {"channel": "opt-summary", "uly": "BTC-USD"},
                            {"channel": "funding-rate", "instId": "BTC-USD-SWAP"},
                            {"channel": "index-candle1m", "instId": "BTC-USD"},
                            {"channel": "status"},
                        ],
                    },
                    hdlr_json=store.onmessage,
                ),
                client.ws_connect(
                    "wss://ws.okx.com:8443/ws/v5/private",
                    # "wss://wspap.okx.com:8443/ws/v5/private?brokerId=9999",
                    send_json={
                        "op": "subscribe",
                        "args": [
                            {"channel": "account"},
                            {"channel": "positions", "instType": "ANY"},
                            {"channel": "balance_and_position"},
                            {"channel": "orders", "instType": "ANY"},
                            {"channel": "orders-algo", "instType": "ANY"},
                            {"channel": "algo-advance", "instType": "ANY"},
                            {"channel": "liquidation-warning", "instType": "ANY"},
                            {"channel": "account-greeks", "instType": "ANY"},
                        ],
                    },
                    hdlr_json=store.onmessage,
                ),
            )
    
            # loop
            while True:
                print(store.tickers.find())
                await store.tickers.wait()
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    ✨ 取引所 Bitget をサポートしました (Contributed by @kunmosky1 💖)

    コピートレードが可能な海外取引所の Bitget をサポートしました。 Private API 認証及び WebSocket API による DataStore クラスを利用できます 🎉

    Bitget API ドキュメント:
    https://bitgetlimited.github.io/apidoc/en/spot/ https://bitgetlimited.github.io/apidoc/en/mix/

    API 認証

    pybotters に渡す API 辞書に bitget のキーで API 情報をリストで格納してください。 Bitget も上記 OKX と同様に API のパスフレーズが存在します。 リストの3要素目にパスフレーズを記載してください。

    {
        "...": ["...", "..."],
        "bitget": ["BITGET_API_KEY", "BITGET_API_SECRET", "YOUR_PASSPHRASE"]
    }
    

    DataStore のプロパティ

    Bitget の DataStore は WebSocket のチャンネルごとに紐づいています。

    Public (https://bitgetlimited.github.io/apidoc/en/mix/#public-channels)

    • trade
    • orderbook
    • ticker
    • candlesticks

    Private (https://bitgetlimited.github.io/apidoc/en/mix/#private-channels)

    • account
    • orders
    • positions

    DataStore の initialize

    Bitget の DataStore は以下のエンドポイントの initialize に対応しています。

    • GET /api/mix/v1/order/current (DataStore: orders)

    サンプルコード

    initialize と ws_connect で全チャンネルを接続するサンプルです。 while True のループでは試しに ticker 情報を表示しています。

    import asyncio
    import pybotters
    
        async with pybotters.Client(base_url="https://api.bitget.com") as client:
            store = pybotters.BitgetDataStore()
    
            # initialize
            await store.initialize(
                client.get("/api/mix/v1/order/current", params={"symbol": "BTCUSDT_UMCBL"}), # リニア契約
                client.get("/api/mix/v1/order/current", params={"symbol": "BTCUSD_DMCBL"}) # インバース契約
            )
    
            # ws_connect
            ws = await client.ws_connect(
                "wss://ws.bitget.com/mix/v1/stream",
                send_json={
                    "op": "subscribe",
                    "args": [
                        # インバース契約
                        {"instType":"DMCBL", "channel":"account", "instId":"default"},
                        {"instType":"DMCBL", "channel":"positions", "instId":"default"},
                        {"instType":"DMCBL", "channel":"orders", "instId":"default"},
                        {"instType":"mc", "channel":"trade", "instId":"BTCUSD"},
                        {"instType":"mc", "channel":"ticker", "instId":"BTCUSD"},
                        {"instType":"mc", "channel":"candle1m", "instId":"BTCUSD"},
                        {"instType":"mc", "channel":"books", "instId":"BTCUSD"},
                        # リニア契約
                        {"instType":"UMCBL", "channel":"account", "instId":"default"},
                        {"instType":"UMCBL", "channel":"positions", "instId":"default"},
                        {"instType":"UMCBL", "channel":"orders", "instId":"default"},
                        {"instType":"mc", "channel":"trade", "instId":"BTCUSDT"},
                        {"instType":"mc", "channel":"ticker", "instId":"BTCUSDT"},
                        {"instType":"mc", "channel":"candle1m", "instId":"BTCUSDT"},
                        {"instType":"mc", "channel":"books", "instId":"BTCUSDT"},
                    ],
                },
                hdlr_json=store.onmessage,
            )
    
            # loop
            while True:
                print(store.ticker.find())
                await store.ticker.wait()
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    ✨ WebSocket の接続状況が確認できるようになりました

    wc_connect の返り値から WebSocket の接続状況が確認できるようになりました。 DataStore で発注管理している bot などの場合は、格納されているデータが信頼できる状態か確認を行うことで誤発注などを避ける手段となります。

    以下のように connected で処理を分岐することが可能です。

    async def main():
        async with pybotters.Client() as client:
            ws = await client.ws_connect("...")
            while True:
                if ws.conneted: # ws.conneted: bool
                    print("ws is connected")
                else:
                    print("ws is disconnected")
                await asyncio.sleep(5.0)
                # Ctrl+C to break
    

    ※ 従来の機能として ws_connect で接続したコネクションは常に自動で再接続されます。 再接続の隙間や、取引所のメンテナンス中などのタイミングで connected が False になります。

    また waitconnected が True になるまで待機することができます。 接続されている場合は await は即時終了します。

    async def main():
        async with pybotters.Client() as client:
            ws = await client.ws_connect("...")
            while True:
                await ws.wait()
                print("ws is connected. continue logic...")
                ...
                # Ctrl+C to break
    

    返り値の変更: ws_connect の返り値は asyncio.Task から独自の WebSocketRunner クラスに変更されます。

    ⏫ Improvements

    • ✨ bitbankDataStore の Depth に timestamp を追加しました (Contributed by @yota-p 💖)

    階層構造ではないので DataStore に格納していなかった timestamp をクラスプロパティとして格納するようになりました。

    store = pybotters.bitbankDataStore()
    store.depth.timestamp
    
    • ✨ WebSocket でのエラーメッセージの logging を向上しました

    殆どの取引所において認証エラーや、DataStoreを利用した場合に subscribe エラー等を logging するようになりました。

    • ✨ いくつかの取引所の API 認証タイムスタンプを弾かれないようにしました

    pybotters が裏で利用している認証タイムスタンプは 秒 で使っていましたが、ccxt などから ミリ秒 で API を一度でも利用すると弾かれる事象がありました(Coincheck など)。 取引所が 秒 で指定していない限りは ミリ秒 を利用するようになりました。

    🐛 Bugfix

    • CoincheckDataStore の orderbok.sorded の型アノテーションを修正しました
    • BybitUSDTDataStore で kline が initialize できない問題を修正しました

    Issues

    ✅WebSocketの接続状況を確認できるようにする #100
    ✅OKXのAPI認証をサポートする #129
    ✅OKXのDataStoreをサポートする #130
    ✅CoincheckDataStoreの型アノテーションが正しくない #131
    ✅BybitUSDTDataStoreのinitializeにklineがない #134
    ✅認証Nonce(Timestamp)を13桁に見直す #135
    ✅README.mdにDiscordのリンクを追加する #136
    ✅WebSocketのエラーメッセージをloggingする #138

    Pull requests

    ✅bitbankDataStoreのDepthにtimestampを追加 #137
    ✅Bitgetの認証とDataStoreをサポートした #139

    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(Feb 4, 2022)

    🎉 New Features

    ✨ Coincheck の DataStore をサポートしました

    国内現物取引所の Coincheck の DataStore をサポートしました。 Coincheck の WebSocket API によるリアルタイムデータを活用できます 🚀

    DataStore のプロパティ

    • trades: 取引履歴
    • orderbook: 板情報

    ※ Coincheck の WebSocket API はパブリックデータのみ提供しています。プライベートデータはありません。

    以下、接続のサンプルコードです。

    import asyncio
    import pybotters
    
    async def main():
        async with pybotters.Client(base_url="https://coincheck.com") as client:
            store = pybotters.CoincheckDataStore()
            wstask = await client.ws_connect(
                "wss://ws-api.coincheck.com/",
                send_json=[
                    {"type": "subscribe", "channel": "btc_jpy-trades"},
                    {"type": "subscribe", "channel": "btc_jpy-orderbook"},
                ],
                hdlr_json=store.onmessage,
            )
            await store.initialize(client.get("/api/order_books"))
            while True:
                pybotters.print("Trades")
                pybotters.print(store.trades.find()[-1:])
                pybotters.print("Orderbook")
                pybotters.print({k: v[:5] for k, v in store.orderbook.sorted().items()})
                await store.wait()
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    詳しくは Coincheck のドキュメントもご覧ください。 https://coincheck.com/ja/documents/exchange/api#websocket

    ✨ DataStore のリバースイテレーションに対応しました

    DataStore を reversed 関数でリバースイテレーションできるようになりました。 時系列データなどを逆順(時間の新しい順)で効率的に取得できます。

    以下は、Bybit のトレードを WebSocket で10件取得するまで待機し逆順で表示するサンプルコードです。

    ※ この機能は辞書の reversed を行っている為、 Python 3.8 以降で利用できます

    async def main():
        async with pybotters.Client() as client:
            store = pybotters.BybitUSDTDataStore()
            wstask = await client.ws_connect(
                "wss://stream.bybit.com/realtime_public",
                send_json=[
                    {"op": "subscribe", "args": ["trade.BTCUSDT"]},
                ],
                hdlr_json=store.onmessage,
            )
            print("wait for trades")
            while len(store.trade) < 10:
                await store.wait()
            print("printing trades in reverse order")
            for item in reversed(store.trade):
                print(item)
    

    ✨ GMOコインのアクセストークンが自動延長されるようになりました

    GMOCoinDataStore の initialize メソッドに「アクセストークンを取得」のリクエストを渡すと DataStore にトークンが保持され、アクセストークンが30分ごとに自動延長されます。 プログラムの起動中にアクセストークンが切れることがないので安全にプライベート WebSocket の再接続時されます。

    サンプルコード

    async def gmocoin():
        async with pybotters.Client(base_url="https://api.coin.z.com") as client:
            store = pybotters.GMOCoinDataStore()
            await store.initialize(
                client.post("/private/v1/ws-auth"),
            )
            wstask = await client.ws_connect(
                f"wss://api.coin.z.com/ws/private/v1/{store.token}",
                send_json=[
                    {"command": "subscribe", "channel": "executionEvents"},
                    {"command": "subscribe", "channel": "orderEvents"},
                    {"command": "subscribe", "channel": "positionEvents"},
                    {"command": "subscribe", "channel": "positionSummaryEvents", "option": "PERIODIC"},
                ],
                hdlr_json=store.onmessage,
            )
            while True:
                ...
                # something code
    

    🎈 Improves

    • ✨ BybitInverseDataStore に wallet を追加しました

    Bybit の更新に伴い BybitInverseDataStorewallet を追加しました。 ストアからインバースの残高情報を参照できます。 また initialize も残高エンドポイントの初期化に対応しました。

    ※ Bybit インバースの残高については、従来の position にもデータが存在しています。

    • ✨ PhemexDataStore で認証エラーの Warning が表示されるようになりました

    🐛 Bugfix

    ✨ いくつかのバグを修正しました

    • GMOCoinDataStore の ticker が上書きされない問題を修正しました
    • Python 3.8 以下の場合 GMOCoinDataStore が TypeError で落ちる問題を修正しました
    • bitbankDataStore の depth.sorted() の型アノテーションの間違いを修正しました

    Issues

    ✅ 各取引所のDataStoreを実装する #20 ✅ DataStoreのリバースイテレーション可能にする #113 ✅ PhemexDataStoreで認証エラーのWarningを表示する #114 ✅ BybitInverseDataStoreにwalletを追加する #118 ✅ GMOCoinDataStoreのtickerが上書きされない #120 ✅ GMOCoinDataStoreがTypeErrorで落ちる。 #122 ✅ GMOコインのPrivate WebSocketアクセストークンを自動延長する #124 ✅ bitbankのDatastoreのDepthで、sorted()の返り値の型の定義が正しくない #126

    Source code(tar.gz)
    Source code(zip)
  • v0.9.0(Jan 3, 2022)

    🎉 New Features

    ✨ PhemexのDataStoreをサポートしました (Contributed by @kunmosky1 💖)

    PhemexのDataStoreをサポートしました。PhemexのWebSocketによるリアルタイムデータを活用できます!

    DataStoreのプロパティ

    • trade: トレード
    • orderbook: 板情報
    • ticker: ティッカー
    • market24h: マーケット
    • kline: ローソク足
    • accounts: アカウントデータ
    • positions: ポジション

    以下接続のサンプルコードです。接続後はローソク足の更新を表示しています。

    import asyncio
    import pybotters
    import time
    
    apis = {'phemex': ['API_KEY','API_SECRET'],}
    
    async def main():
        async with pybotters.Client(apis=apis, base_url='https://api.phemex.com') as client:
            store = pybotters.PhemexDataStore()
            endtime = int(time.time()) - 60 * 999 #ws接続時に1000本送られてくるので、それより前の2000本を取得(合計で起動時に2999本)
            await store.initialize(
                client.get('/exchange/public/md/kline', params={'symbol': 'BTCUSD', 'resolution': 60, 'from': endtime - 60 * 2000, 'to': endtime}),
            )
            wstask = await client.ws_connect(
                'wss://phemex.com/ws',
                send_json=[
                    {'id': 100, 'method': 'trade.subscribe', 'params': ['BTCUSD']},
                    {'id': 101, 'method': 'tick.subscribe', 'params': ['.BTC']},
                    {'id': 103, 'method': 'orderbook.subscribe', 'params': ['BTCUSD']},
                    {'id': 104, 'method': 'kline.subscribe', 'params': ['BTCUSD',60]},
                    {'id': 105, 'method': 'market24h.subscribe', 'params': []},
                    {'id': 200, 'method': 'aop.subscribe', 'params': []},
                ],
                hdlr_json=store.onmessage,
            )
            # Ctrl+C to break
            while True:
                await store.kline.wait()
                data = store.kline.find()
                print(data)
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    ✨ Bybit USDT契約のポジション ワンウェイモードに対応しました

    Bybitのアップデートに伴い、pybotters.BybitUSDTDataStore もワンウェイモードに対応しました!

    store = pybotters.BybitUSDTDataStore()
    await client.ws_connect(...)
    ...
    position = store.position.one("BTCUSDT") # one-way mode
    position = store.position.both("BTCUSDT") # hedge mode
    

    ✨ Bybitのタイムスタンプ認証が通りやすくなりました

    BybitはクライアントPCの時刻同期が少しでもズレいると、REST APIでは invalid request, please check your timestamp and recv_window param といったエラーが派生したり、WebSocketでは WSServerHandshakeError が発生することがありました。 今回のアップデートでは内部的にタイムスタンプのズレの許容幅を広げ、おおよそ5秒のズレまでは上記エラーを発生しづらくなります。

    🐛 Bugfix

    ✨ 多数のバグを修正しました

    コミュニティの方々のバグ報告により、7つのバグを修正しました。 いつもバグ報告ありがとうございます!

    • Bybit Spotで注文削除(DELETEリクエスト)などができない問題が修正しました
    • Binance Spot WebScoketの1秒につき5リクエスト制限に対応しました
    • bitFlyerの親注文(入れ子のリクエスト)などができない問題を修正しました
    • BybitUSDTDataStoreのinitializeにウォレットを再実装しました
    • PhemexやBybitのDELETEリクエストに params を指定しても警告を表示しないようにしました
    • GMOCoinDataStoreでタイムスタンプの例外が発生する問題を修正しました
    • bitbankの認証タイムスタンプをUNIXタイムスタンプ(13桁)に修正しました
    • bitbankDataStoreの板情報のsortedメソッドを数値でソートするように修正しました

    Issues

    ✅ Bybit SpotのDELETE認証方式を修正する #95 ✅ BinanceのWebSocket制限に対応する #96 ✅ bitFlyerの親注文APIがエラーになる #98 ✅ BybitUSDTDataStoreのウォレット実装が漏れている #99 ✅ HTTPリクエストのメソッドにおけるパラメーター指定時の警告を削除する #101 ✅ Bybitの認証タイムスタンプについて変更を検討する #102 ✅ Bybit USDT無期限のワンウェイモードに対応する #105 ✅ GMOコインのタイムスタンプにミリ秒がないレコードがパースできない #106 ✅ Binance USDⓈ-M Futuresの新しいWebSocketエンドポイントに対応する #108 ✅ bitbankの認証タイムスタンプを修正する #109 ✅ 旧BybitDataStoreクラスがauto_castを受け付けない #111 ✅ bitbankDataStoreの板情報のソートが間違っている #110

    Pull requests

    ✅ Phemexのデータストアをサポートする #97 ✅ GMOコインのタイムスタンプのミリ秒をパースする #107

    Source code(tar.gz)
    Source code(zip)
  • v0.8.0(Nov 11, 2021)

    🎉 New Features

    ✨ bitFlyerのDataStoreをサポートしました

    DataStoreのプロパティ

    • board: 板情報
    • ticker: Ticker
    • executions: 約定
    • childorderevents: 注文イベント
    • childorders: アクティブな注文
    • parentorderevents: 親注文(特殊注文)イベント
    • parentorders: アクティブな親注文(特殊注文)
    • positions: 建玉の一覧

    REST APIのinitializeとWebSocket購読のサンプルコード

    import asyncio
    import pybotters
    
    async def main():
        async with pybotters.Client(apis=apis, base_url="https://api.bitflyer.com") as client:
            store = pybotters.bitFlyerDataStore()
            await store.initialize(
                client.get("/v1/me/getchildorders?product_code=FX_BTC_JPY"),
                client.get("/v1/me/getparentorders?product_code=FX_BTC_JPY"),
                client.get("/v1/me/getpositions?product_code=FX_BTC_JPY"),
            )
            wstask = await client.ws_connect(
                "wss://ws.lightstream.bitflyer.com/json-rpc",
                send_json=[
                    {"method": "subscribe", "params": {"channel": "lightning_board_snapshot_FX_BTC_JPY"}, "id": 1},
                    {"method": "subscribe", "params": {"channel": "lightning_board_FX_BTC_JPY"}, "id": 2},
                    {"method": "subscribe", "params": {"channel": "lightning_ticker_FX_BTC_JPY"}, "id": 3},
                    {"method": "subscribe", "params": {"channel": "lightning_executions_FX_BTC_JPY"}, "id": 4},
                    {"method": "subscribe", "params": {"channel": "child_order_events"}, "id": 5},
                    {"method": "subscribe", "params": {"channel": "parent_order_events"}, "id": 6},
                ],
                hdlr_json=store.onmessage,
            )
            while True:
                await store.wait()
                for name, ds in store._stores.items():
                    print(f"{name}: {len(ds)=}")
                print()
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    ✨ [重要] BybitのDataStoreを Inverse/USDT 取引用にクラス分けしました

    BybitのDataStoreは以下の2クラスに変更されました。それぞれの取引に最適化されます。

    pybotters.BybitInverseDataStore, pybotters.BybitUSDTDataStore

    既存のpybotters.BybitDataStore は引き続き利用できますが、非推奨になります。(今後廃止されます)

    ✨ DataStoreのstr型数値フィールド自動キャスト機能を追加しました

    auto_cast=Trueにすることにより、「str型の数字」のデータを自動的にint/floatに変換します。

    store = pybotters.BybitInverseDataStore(auto_cast=True)
    

    この機能を有効にすることで演算が便利になりますが、WebScoketイベントごとのstr型フィールドを判定するので処理負荷が増加します。

    ✨ WebSocketにおけるバイナリ型データのリクエスト/ハンドリングをサポートしました

    Client.ws_connectメソッドに、send_bytes, hdlr_bytesを追加しました。 Huobi Global(非対応非期初)などのデータハンドリングも可能になります。

    Huobiのサンプル

    import asyncio
    import aiohttp
    import gzip
    import json
    import pybotters
    
    async def huobi_handler(msg_bytes: bytes, ws: aiohttp.ClientWebSocketResponse):
        msg_json = json.loads(gzip.decompress(msg_bytes).decode())
        if "ping" in msg_json:
            ping = msg_json["ping"]
            await ws.send_str(f'{{"pong":{ping}}}')
        else:
            print(msg_json)
    
    async def main():
        async with pybotters.Client() as client:
            ws = await client.ws_connect(
                "wss://api.huobi.pro/ws",
                send_str='{"sub": "market.btcusdt.ticker"}',
                hdlr_bytes=huobi_handler,
            )
            await ws
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    ✨ GMOCoinDataStoreの板情報にタイムスタンプを追加しました

    pybotters.GMOCoinDataStore.orderbooks.timestamp からタイムスタンプを取得できます

    Issues

    ✅ Bybitの型問題の抜本的な解決方法について #82 ✅ バイナリのWebSocketデータのハンドリングに対応する #87 ✅ GMOコインのデータストアにタイムスタンプを追加する #88

    Pull requests

    ✅ Remove default empty list #92 ✅ add API Reference #93

    Source code(tar.gz)
    Source code(zip)
  • v0.7.1(Sep 28, 2021)

    🐛 Bugfix

    ✅ 一部ストアにおいてデータが更新されない不具合を修正しました

    DataStoreの内部メソッド _clear が正しく動作しない不具合を修正しました。 これを使用している以下のストアが v0.6.0 リリース 以降、動作に問題がありましたがこの修正により改善されます。

    • pybotters.BinanceDataStore
      • Orderストア
    • pybotters.FTXDataStore
      • Marketsストア
    • pybotters.experimental.BybitUSDTDataStore
      • Walletストア
    Source code(tar.gz)
    Source code(zip)
  • v0.7.0(Sep 24, 2021)

    🎉 New Features

    ✨ BybitのWebSocketのLiquidation(清算)をサポートしました

    2021-9-14にサポートされた WebSocketのLiquidationトピックをDataStoreに追加しました! なお、REST APIの /v2/public/liq-records2021-9-24に廃止される ので注意してください。

    WebSocketの清算イベントを表示するサンプルです。 清算は直ぐには発生しないので気長に待ちましょう🛀

    import asyncio
    import pybotters
    
    async def main():
        async with pybotters.Client() as client:
            store = pybotters.BybitDataStore()
            await client.ws_connect(
                "wss://stream.bybit.com/realtime",
                send_json={"op": "subscribe", "args": ["liquidation.BTCUSD"]},
                hdlr_json=store.onmessage,
            )
            while True:
                print(await store.liquidation.wait())
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    🌈 試験的にBybitの型変換済みDataStoreを実装しました

    Experimentalな機能ですが、 文字列になってる数値をfloatやintに変換 して保管する新設計のDataStoreを実装しました! このように価格や数量などのフィールドはfloatやintに変換を掛けて保管するようになります。

    #before
    [
        {"symbol": "BTCUSD", "price": "1234.50", "id": 1234500, "side": "Sell", size: "100"}
    ]
    #after
    [
        {"symbol": "BTCUSD", "price": 1234.5, "id": 1234500, "side": "Sell", size: 100}
    ]
    

    また、従来の pybotters.BybitDataStore はInverse契約とUSDT(Linear)契約の共通DataStoreでしたが、新しいDataStoreでは双方の契約の細かな仕様や型の違いに対応するために、クラスを2つに分割しました。 pybotters.experimental.BybitInverseDataStorepybotters.experimental.BybitUSDTDataStore から利用できます。

    最も大きな違いとしては、クラスが共通でなくなった為にポジションストアのメンバ名が変更されています。

    # before
    store = pybotters.BybitDataStore()
    store.position_inverse.getone("BTCUSD")
    store.position_inverse.getboth("BTCUSDM21")
    store.position_usdt.getboth("BTCUSDT")
    # after
    store = pybotters.experimental.BybitInverseDataStore()
    store.position.one("BTCUSD")
    store.position.both("BTCUSDM21")
    store = pybotters.experimental.BybitUSDTDataStore()
    store.position.both("BTCUSDT")
    

    その他の細かい違いとしては、walletストアはUSDTのみ、insuranceストアはインバースのみ、USDTのstoporderストアはキー名を変換せずstop_order_idのまま利用するようにした、などです。

    Bybitは 文字列の数値 のおかげで演算がやっかいなので、この試験的な機能は役に立つかもしれません。 型変換済みのDataStoreで板情報を表示するコードです!

    import asyncio
    import pybotters
    
    async def main():
        async with pybotters.Client() as client:
            store = pybotters.experimental.BybitInverseDataStore()
            await client.ws_connect(
                "wss://stream.bybit.com/realtime",
                send_json={"op": "subscribe", "args": ["orderBookL2_25.BTCUSD"]},
                hdlr_json=store.onmessage,
            )
            while not len(store.orderbook):
                await store.wait()
            pybotters.print(store.orderbook.sorted())
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    ⚠ これは暫定的・試験的な実装なので、将来的に大きく変更が入るかもしれません

    ✨ BybitのKlineの初期化をサポートしました (Contributed by @LopeRope 💖)

    initializeメソッドでKlineのデータを格納できる機能を追加しました!

    使い方はこちらです。

    import asyncio
    import time
    import pybotters
    
    async def main():
        async with pybotters.Client(base_url="https://api.bybit.com") as client:
            store = pybotters.BybitDataStore()
            ts = int(time.time()) - 300
            await store.initialize(
                client.get(f"/v2/public/kline/list?symbol=BTCUSD&interval=1&from={ts}")
            )
            pybotters.print(store.kline.find())
            await client.ws_connect(
                "wss://stream.bybit.com/realtime",
                send_json={"op": "subscribe", "args": ["klineV2.1.BTCUSD"]},
                hdlr_json=store.onmessage,
            )
            while True:
                pybotters.print(store.kline.find())
                await store.kline.wait()
    
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    

    Issues

    ✅ Bybitの型問題の抜本的な解決方法について #82 ✅ BybitのLiquidationトピックを追加する #83

    Pull requests

    ✅ BybitのKlineのinitializeを追加する #81

    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Aug 15, 2021)

    🎉 New Features

    ✨ GMOコインのDataStoreをサポートしました (Contributed by @supermomonga 💖)

    pybottersでは初の国内取引所DataStoreをサポートしました! pybotters.GMOCoinDataStore から、GMOコインのWebSokcetによるリアルタイムデータを利用することができます。 さらに本DataStoreは初の試みとして データの型付け をサポートしています。 symbolはstr型、priceなどはDecimal型、timestampはdatetime型など、データの意味通りの型でDataStoreに保管されます。 従来のDataStoreは数値や時刻データが文字列型でもそのまま(元の型のまま)保管していましたが、これにより演算やさらなる処理が容易になります。 GMOCoinDataStoreのインターフェースはリファレンスをご覧ください。

    ✨ CoincheckのAPI認証をサポートしました

    国内取引所 Coincheck のAPI認証をサポートしました! オーダー発注や残高の取得などのプライベートAPIが利用できます。 ※DataStoreは未対応です

    ✨ waitメソッドから受信データを取得できるようになりました (Contributed by @kaznak 💖)

    DataStore.wait メソッドの返り値は従来はNoneでしたが、** 待機した結果取得したデータ ** を返すようになりました。 これにより ** PostOnlyオプションにより即時キャンセルされてしまったオーダー ** などのDataStoreに残らないオーダー(※)を取得できます。 ※各種DataStoreのオーダーはアクティブオーダーのみ管理する仕様なので約定orキャンセル済みは削除される 注意: 親クラスの DataStoreManager.wait メソッドの方にはこの機能はありません。

    async def main():
        async with pybotters.Client(base_url='https://ftx.com/api, apis=apis) as client:
            store = pybotters.FTXDataStore()
            ws = await client.ws_connect(
                'wss://ftx.com/ws',
                send_json={'op': 'subscribe', 'channel': 'orders'},
                hdlr_json=store.onmessage,
            )
    
            asyncio.create_task(client.post('/orders', data=...)) # Send an order that will be canceled immediately
            result = await store.orders.wait()
            print(result)
            # {'id': ..., 'status': 'closed', ...: ...}
            print(store.orders.find())
            # []
    

    📖 Documentation

    ✨ Sphinxによるドキュメント自動生成を導入しました (Contributed by @drillan 💖)

    Read The Docsにドキュメントを生成できるSphinxを導入しました! GitHub WikiからRead The Docsにドキュメントを移行する予定です。 https://pybotters.readthedocs.io/ja/latest/

    Other

    • pybottersのリリース状況についての表記をBETAから Preview の名称に変更しました
    • DataStoreを管理する抽象クラスの名称をDataStoreInterfaceからDataStoreManagerに変更しました
    • DataStoreに隠しメソッド _pop, _find_and_delete を追加しました

    詳細については以下をご覧ください。

    Issues

    ✅ 対応取引所の追加(Phemex、Coincheck) #52 ✅ GMO コインの DataStore を実装する #60 ✅ リリース表記をPreview版にする #73 ✅ FTXDataStore の orders チャネルにおける close されたオーダーの取り扱いについて #75 ✅ DataStoreInterfaceのクラス名を変更する #79

    Pull requests

    ✅ GMO コイン用の DataStore を一部実装する #74 ✅ DataStore のレコード削除メソッドのサンプル実装 #76 ✅ DataStore の wait メソッドが変更されたデータを返す実装 #77 ✅ Sphinxのドキュメントを追加 #78

    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Jul 11, 2021)

    ✅ #20 bitbankのDataStoreをサポート (contributed by @Osaifu-Crypto ) ✅ #52 PhemexのAPI認証をサポート ✅ #67 GMO コインの認証処理を修正 (contributed by @supermomonga) ✅ #70 WebSocketのClientOSErrorをキャッチする ✅ #71 BTCMEXのサポートを廃止する(BitMEXをサポートする)

    🎉 New Features

    ✨ bitbankのDataStoreをサポートしました

    bitbankのWebSocketデータをハンドリングするDataStoreを実装しました! pybotters.bitbankDataStore から利用できます。 bitbankはSockei.io形式なので接続方法については従来とは少し異なります。下記サンプルコードを参照してください。 本機能はおさいふ君さん(@Osaifu_Crypto)にコードを提供して頂きました💖

    各チャンネルのデータを監視するサンプルコードです。(コメントアウト/解除して利用してください)

    async def main():
        async with pybotters.Client() as client:
            store = pybotters.bitbankDataStore()
            wstask = await client.ws_connect(
                'wss://stream.bitbank.cc/socket.io/?EIO=3&transport=websocket',
                send_str=[
                    '42["join-room","ticker_xrp_jpy"]',
                    '42["join-room","transactions_xrp_jpy"]',
                    '42["join-room","depth_whole_xrp_jpy"]',
                ],
                hdlr_str=store.onmessage,
            )
            while True:
                # Transactions
                # await store.transactions.wait()
                # pybotters.print(store.transactions.find()[-1])
    
                # Depth
                await store.depth.wait()
                pybotters.print({k:v[:6] for k, v in store.depth.sorted().items()})
    
                # Ticker
                # await store.ticker.wait()
                # pybotters.print(store.ticker.find())
    

    ✨ PhemexのAPI認証をサポートしました

    シンガポールを拠点とする新興取引所 Phemex のAPI認証をサポートしました。 REST/WebSocketのプライベートAPIを利用できます。(※WebSocketデータを保管するDataStore機能は未実装です)

    新規オーダーを送信、プライベートWebsocketに接続するサンプルコード

    async def main():
        async with pybotters.Client(apis=apis, base_url='https://api.phemex.com') as client:
            data = {
                'symbol': 'BTCUSD',
                'side': 'Buy',
                'priceEp': 334100000,
                'orderQty': 1,
                'ordType': 'Limit',
                'timeInForce': 'PostOnly',
            }
            r = await client.post('/orders', data=data)
            data = await r.json()
            pybotters.print(data)
    
            wstask = await client.ws_connect(
                'wss://phemex.com/ws',
                send_str='{"method":"aop.subscribe","params":[],"id":123}',
                hdlr_json=pybotters.print_hander
            )
            await wstask # Ctrl+C to break
    

    ✨ BTCMEXのサポートを廃止、BitMEXのDataStoreをサポートしました

    BTCMEXが営業終了しましたのでコードやWikiの記述を削除しました。 代わりにBTCMEXはBitMEXのほぼ互換APIだったので、やっつけながらDataStoreを逆輸入しBitMEXのDataStoreをサポートしました。 pybotters.BitMEXDataStore から利用できます。

    🐛 Bugfix

    • GMOコインのWebSocket認証トーンが正常にリクエストできるよう不具合を修正しました (本修正はめちゃコミッタさん(@supermomonga)に提供して頂きました💖)
    • WebSocketの接続がClientOSErrorといったエラーで落ちないように処理を修正しました
    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Jun 21, 2021)

    ✅Binanceのイベント仕様に従ってDataStoreを更新する #58 ✅Bybit DataStoreのKline(ローソク足)のキーが足りていない #62 ✅DataStoreにおいてWebSocketエラーメッセージのWarningを表示する #63

    ✅Python 3.7以外のテストが通っていない #51 ✅Linter, Formatter を導入したい #59

    🎉 New Features

    ✨ BinanceのDataStoreでREST API経由の板情報の更新に対応しました

    BinanceのAPIドキュメント(How to manage a local order book correctly)のロジックに準拠して板情報を更新できるようになりました。 これによりBinanceの板情報のDataStoreのREST API経由の初期化が可能になり、より正確かつ1000行以上のなデータが補完できるようになります。

    🎈 板情報をREST APIで初期化し、WebSocketのイベントごとに最良気配値5行を表示するサンプル ※REST APIでの初期化(initialize)はAPIドキュメント通り、WebSocket接続後に行います

    async def main():
        async with pybotters.Client(base_url='https://fapi.binance.com') as client:
            store = pybotters.BinanceDataStore()
    
            streams = '/'.join([
                '[email protected]@100ms',
            ])
            wstask = await client.ws_connect(
                f'wss://fstream.binance.com/stream?streams={streams}',
                hdlr_json=store.onmessage,
            )
            await store.initialize(
                client.get('/fapi/v1/depth?symbol=BTCUSDT&limit=1000'),
            )
    
            while not store.orderbook.initialized:
                await store.wait()
    
            while True:
                book = store.orderbook.sorted()
                pybotters.print({'a': book['a'][:5], 'b': book['b'][:5]})
                await store.orderbook.wait()
    

    ✔ Improvements

    ✨ WebSocketのsubscribeのメッセージを間違えた場合にもDataStoreがWarnningを表示するようになりました

    🐛 Bugfix

    BybitのDataStoreでKlineにおいて複数時間足が正しく反映されないバグを修正しました。

    ♻ CI/CD

    テストコードの改善や、Linter・Formatterを導入しました。

    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Jun 13, 2021)

    ✅ GMOコインのWebSocket API制限により複数購読ができない #50 ✅ BybitのDataStoreクラスにタイムスタンプを追加する #47 ✅ ws_connectのハンドラ未指定の場合はprintハンドラを選択する #46 ✅ Binanceで認証を利用するとPublic APIがパラメーターエラーとなる #44 ✅ HTTPリクエストのparams/data引数の指定間違いに警告を表示する #43 ✅ Binance Futures USDⓈ-MのDataStoreを実装する #20

    ✅ DataStore サブクラスインスタンス取得時の型エラーを解決 #55 ✅ GitHub Actions の matrix 実行で fail-fast を無効化 #54 ✅ GitHub Actions による自動テスト実行 #49

    🚀 New Experience

    ✨ Binance Futures USDⓈ-MのDataStoreを正式に実装しました

    BinanceのWebSocketをハンドリングする pybotters.BinanceDataStore の実装が完了しました。 リアルタイムで板情報・ティッカー・アカウントのオーダーやポジションなどがDataStoreにキャッシュされ高速に参照することが可能です! 今回はBinance Futures USDⓈ-Mのみ対応しており、Binance Futures COIN-M、Spotは未対応です。 「listenKey」の自動延長はDataStoreのinitializeメソッドの機能で組み込んであるのでこれを利用することで持続的にユーザーストリームを購読できます。

    全てのマーケットストリーム・ユーザーストリームを購読しREST APIで初期化する全部入りサンプル🔥

    async def main():
        async with pybotters.Client(apis=apis, base_url='https://fapi.binance.com') as client:
            store = pybotters.BinanceDataStore()
            await store.initialize(
                client.get('/fapi/v2/balance'),
                client.get('/fapi/v2/positionRisk?symbol=BTCUSDT'),
                client.get('/fapi/v1/openOrders?symbol=BTCUSDT'),
                client.post('/fapi/v1/listenKey'),
            )
    
            streams = '/'.join([
                '[email protected]',
                '[email protected]',
                '[email protected]_1m',
                '[email protected]_1m',
                '[email protected]', # or [email protected]
                '[email protected]',
                '[email protected]',
                '[email protected]@500ms',
                '[email protected]',
                store.listenkey,
            ])
            wstask = await client.ws_connect(
                f'wss://fstream.binance.com/stream?streams={streams}',
                hdlr_json=store.onmessage,
            )
    
            while not all([
                len(store.markprice),
                len(store.kline),
                len(store.continuouskline),
                len(store.ticker),
                len(store.bookticker),
                len(store.orderbook) >= 40,
            ]):
                await store.wait()
    
            # サンプル: オーダーブックを監視
            while True:
                pybotters.print(store.orderbook.sorted())
                await store.orderbook.wait()
    

    ✨ BybitのDataStoreにタイムスタンプ機能を追加しました

    BybitのWebSocketで配信される一部データにあるタイムスタンプ(timestamp_e6)を pybotters.BybitDataStoreに整数型で記録します。 疑似的にBybitのサーバータイムスタンプとして遅延を計ることなとが可能です。 この機能はorderBookL2_25, orderBook_200, instrument_info, klineV2を購読することで利用が可能です。

    async def main():
        async with pybotters.Client() as client:
            store = pybotters.BybitDataStore()
            wstasks = await asyncio.gather(
                client.ws_connect(
                    'wss://stream.bybit.com/realtime',
                    send_json={'op': 'subscribe', 'args': [
                        'orderBookL2_25.BTCUSD|BTCUSDM21', # or orderBook_200.100ms.BTCUSD|BTCUSDM21
                        'instrument_info.100ms.BTCUSD|BTCUSDM21',
                        'klineV2.1.BTCUSD|BTCUSDM21',
                    ]},
                    hdlr_json=store.onmessage,
                ),
                client.ws_connect(
                    'wss://stream.bybit.com/realtime_public',
                    send_json={'op': 'subscribe', 'args': [
                        'orderBookL2_25.BTCUSDT', # or orderBook_200.100ms.BTCUSDT
                        'instrument_info.100ms.BTCUSDT',
                        'candle.1.BTCUSDT',
                    ]},
                    hdlr_json=store.onmessage,
                ),
            )
    
            while True:
                print(store.timestamp_e6)
                # 1623609894038501
                await store.wait()
    

    ✨ API自動認証をオフにする引数を追加しました

    pybottersはREST/WebSocket APIを自動認証していますが、全て自動的に認証を行っていたので一部の取引所のPublic APIは認証により逆にエラーとなっていました。 リクエストメソッドの auth 引数を None にすることで手動で認証をオフに出来ます。

    async def main():
        async with pybotters.Client(apis=apis) as client:
            # ERROR
            r = await client.get('https://api.binance.com/api/v3/depth', params={'symbol': 'BTCUSDT', 'limit': '10'})
            print(await r.json())
            # {'code': -1101, 'msg': "Too many parameters; expected '2' and received '4'."}
    
            # OK
            r = await client.get('https://api.binance.com/api/v3/depth', params={'symbol': 'BTCUSDT', 'limit': '10'}, auth=None)
            print(await r.json())
    

    細かい新機能

    GMOコインのWebSocketにおいて複数チャンネルを購読できない問題を解消しました。 その他は各Issueをご覧ください。

    ♻ CI/CD

    ✨ GitHub Actions による自動テスト実行などを追加しました

    自動テスト実行などによってpybottersの開発品質が向上します! こちらは @supermomonga 氏にコントリビュートして頂きました💖

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Jun 6, 2021)

    ✅ 試験的にBinanceのDataStoreを実装する(各取引所のDataStoreを実装する) #20 ✅ BybitのInstrumentが重複する #45

    🌈 Experimental

    ✨ 試験的にBinanceのDataStoreを実装しました #20

    一部のマーケットストリームとユーザーデータストリームのDataStoreを実装しました。 ** 重要 ** 基本的にはFutures(USDⓈ-M、COIN-M)でテストをしており、Spotは対応できてない可能性があります。 板情報は未実装です。ベスト値のみ取れる板ティッカー(bookticker)のみ使えます。 また、initializeのようなメソッドも実装していないのでポジションやオーダーの初期値は取得できません。 完全なDataStoreは今後実装予定です。

    実装済みストリーム

    • trade (Market Streams trade, aggTrade)
    • bookticker (Market Streams bookTicker)
    • balance (User Data Streams)
    • position (User Data Streams)
    • order (User Data Streams)

    ユーザーデータストリーム(USDⓈ-M)の例

    WebSocket受信ごとにオーダー・残高・ポジションを表示する。 ※listenKeyの自動keepaliveは現状pybotters側で実装していないので手動でタスク化する必要があります。

    
    async def main():
        async with pybotters.Client(base_url='https://fapi.binance.com') as client:
            store = pybotters.BinanceDataStore()
            r = await client.post('/fapi/v1/listenKey')
            data = await r.json()
            listenKey = data['listenKey']
            pybotters.print(data)
            wstask = await client.ws_connect(
                f'wss://fstream.binance.com/ws/{listenKey}',
                hdlr_json=store.onmessage,
            )
            listentask = asyncio.create_task(keepalive(client))
            while True:
                pybotters.print(store.order.find())
                pybotters.print(store.balance.find())
                pybotters.print(store.position.find())
                print('-' * 80)
                await store.wait()
    
    
    async def keepalive(client: pybotters.Client):
        while not client._session.closed:
            await client.put('/fapi/v1/listenKey')
            await asyncio.sleep(1800.0) # 30 minutes
    

    🐛 Bugfix

    🐛 BybitのDataStoreのレコードが重複するバグを修正しました #45

    再接続時にレコード重複するバグを修正しました。(Issues参照)

    Source code(tar.gz)
    Source code(zip)
  • v0.2.2(May 23, 2021)

    🚀 New Experience

    データストアでソート済みの板情報の取得可能にする #40

    ✨ ソート済みの板情報が取得できるようになりました

    FTXの例

    async def main():
        store = pybotters.FTXDataStore()
        ws = await client.ws_connect(
            'wss://ftx.com/ws',
            send_json={'op': 'subscribe', 'channel': 'orderbook', 'market': 'BTC-PERP'},
            hdlr_json=store.onmessage,
        )
        pybotters.print(store.orderbook.sorted())
    # {
    #     'asks': [
    #         [35389.0, 0.38],
    #         [35392.0, 0.05],
    #         [35393.0, 0.39],
    #         ...
    #     ],
    #     'bids': [
    #         [35388.0, 0.6244],
    #         [35386.0, 0.1177],
    #         [35382.0, 0.2],
    #         ...
    #     ]
    # }
    

    Bybitの例

    async def main():
        store = pybotters.BybitDataStore()
        ws = await client.ws_connect(
            'wss://stream.bybit.com/realtime',
            send_json={'op': 'subscribe', 'args': ['orderBookL2_25.BTCUSD']},
            hdlr_json=store.onmessage,
        )
        pybotters.print(store.orderbook.sorted())
    # {
    #     'Sell': [
    #         {'price': '35508.50', 'symbol': 'BTCUSD', 'id': 355085000, 'side': 'Sell', 'size': 432225},
    #         {'price': '35509.00', 'symbol': 'BTCUSD', 'id': 355090000, 'side': 'Sell', 'size': 1},
    #         {'price': '35514.50', 'symbol': 'BTCUSD', 'id': 355145000, 'side': 'Sell', 'size': 213690},
    #         ...
    #     ],
    #     'Buy': [
    #         {'price': '35508.00', 'symbol': 'BTCUSD', 'id': 355080000, 'side': 'Buy', 'size': 1004576},
    #         {'price': '35507.50', 'symbol': 'BTCUSD', 'id': 355075000, 'side': 'Buy', 'size': 88192},
    #         {'price': '35507.00', 'symbol': 'BTCUSD', 'id': 355070000, 'side': 'Buy', 'size': 86766},
    #         ...
    #     ]
    # }
    

    🐛 Bugfix

    Bybitデータストアのinitializeメソッド引数仕様の変更漏れ #41

    🐛 Bybitデータストアのinitializeメソッド引数仕様

    v0.2.0リリースの実装が変更漏れとなっていました。 変更点はv0.2.0のリリースノートを参照してください。

    https://github.com/MtkN1/pybotters/releases/tag/v0.2.0

    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(May 2, 2021)

  • v0.2.0(Apr 30, 2021)

    🚀 New Experience

    FTXのデータストアを実装した #20 同期リクエストをサポートした #27 apisの暗黙的な読み込みに対応した #36 FTXのサブアカウントのWebSocket認証に対応した #37

    ✨ FTXのデータストアを実装しました、サブアカウントのWebSocket認証に対応しました

    FTXのWebSocketのデータを簡単に扱えるようになりました!

    Reference、およびExchangesをご覧ください。

    ✨ 同期リクエストをサポートしました

    requestsライブラリのように、asyncioを表面的に使用せずいとも簡単にリクエストが可能になりました!

    import pybotters
    
    r = pybotters.request('GET', 'https://...', apis=apis)
    r = pybotters.get('https://...', params={'foo': 'bar'}, apis=apis)
    r = pybotters.post('https://...', data={'foo': 'bar'}, apis=apis)
    r = pybotters.put('https://...', data={'foo': 'bar'}, apis=apis)
    r = pybotters.delete('https://...', data={'foo': 'bar'}, apis=apis)
    
    print(r.text())
    print(r.json())
    

    NOTE:
    この呼び出し方は自動的にセッションを開き必ず閉じるのでkeep-alive接続はありません。 対話モードや検証に利用する際に便利ですが、botとして実装する場合は非同期のClientクラスの利用をおすすめします。

    詳細はAdvanced Usageをご覧ください。

    ✨ apisの暗黙的な読み込みに対応しました

    カレントディレクトリにJSONファイルを配置したり、環境変数を設定することでapisの設定を省力可能になりました!

    詳細はAdvanced Usageをご覧ください。

    🚧 Specification Change

    ♻️ BybitDataStore.initialize の引数仕様を変更しました

    initializeメソッドはリストを受け入れていましたが、可変長引数に変更しました。

    store = pybotters.BybitDataStore()
    # before
    await store.initialize([
        client.get('https://...'),
        client.get('https://...'),
    ])
    # after
    await store.initialize(
        client.get('https://...'),
        client.get('https://...'),
    )
    
    Source code(tar.gz)
    Source code(zip)
  • v0.1.2(Apr 19, 2021)

    🔧 Bugfix

    BybitデータストアのUSDTの契約ポジションが正常に反映されない #34

    📖 Documentation

    READMEとWikiに取引所APIドキュメントのリンクを追加する #33

    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(Apr 18, 2021)

    🚀 New Experience

    #23 WebSocket接続時に複数回リクエストに対応した(bitFlyerなどで複数チャンネルが購読に対応した) #28 BybitデータストアのREST API経由の初期化に対応した #29 リクエストメソッドのkwargsに対応した #30 apisをJSON形式のファイルオープンに対応した

    ✨ WebSocketの複数回リクエストに対応しました

    bitFlyer等、WebSocketの購読に複数回リクエスト送信がいる取引所で、必要なトピックを購読できるようになりました。

    send_json(またはsend_str)をリストで指定します。

    async def main():
        async with pybotters.Client(apis=apis) as client:
            wstask = await client.ws_connect(
                'wss://ws.lightstream.bitflyer.com/json-rpc',
                send_json=[
                    {
                        'method': 'subscribe',
                        'params': {'channel': 'child_order_events'},
                        'id': 'subscribe:child_order_events',
                    },
                    {
                        'method': 'subscribe',
                        'params': {'channel': 'parent_order_events'},
                        'id': 'subscribe:lightning_ticker_FX_BTC_JPY',
                    },
                ],
                hdlr_json=lambda msg, ws: print(msg),
            )
            await wstask
    

    ✨ BybitデータストアのREST API経由の初期化に対応しました

    BybitのWebSocketは初期データ(オーダー・ポジション・ウォレット等)が配信されませんでしたが、REST API経由のデータを取得できるようになりました。

    async def main():
        async with pybotters.Client(apis=apis.getall(), base_url='https://api.bybit.com') as client:
            store = pybotters.BybitDataStore()
            await store.initialize([
                client.get('/v2/private/order', params={'symbol': 'BTCUSD'}),
                client.get('/v2/private/position/list', params={'symbol': 'BTCUSD'}),
                client.get('/v2/private/wallet/balance'),
            ])
            print(store.order.find())
            print(store.position.find())
            print(store.wallet.find())
    

    ✨ リクエストメソッドのkwargsに対応しました

    主にFTXで、リクエスト毎にサブアカウントを指定できるようになります。

    async def main():
        async with pybotters.Client(apis=apis, base_url='https://ftx.com/api', headers={'FTX-SUBACCOUNT': 'my_subaccount_nickname'}) as client:
            r = await client.get('...')
            r = await client.get('...', headers={'FTX-SUBACCOUNT': 'my_alt_subaccount_nickname'})
    

    ✨ apisをJSON形式のファイルオープンに対応しました

    API情報をJSONで保存している場合、コード内に変数で書かなくても読み込みるようになりました。

    (ファイル名:apis.json)

    {
        "bybit": ["BYBIT_API_KEY", "BYBIT_API_SECRET"],
        "btcmex": ["BTCMEX_API_KEY", "BTCMEX_API_SECRET"],
        "binance": ["BINANCE_API_KEY", "BINANCE_API_SECRET"],
        "....": ["...", "..."]
    }
    

    文字列(ディレクトリパス)を指定して読み込む。

    async def main():
        async with pybotters.Client(apis='apis.json') as client:
            ...
    
    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(Apr 13, 2021)

    ✨ Features

    • API認証
      • Bybit, Binance, FTX, BTCMEX, BitMEX, bitFlyer, GMO Coin, Liquid, bitbankに対応
    • DataStore
      • Bybit, BTCMEXに対応
    • パッケージング
      • PyPIに登録済み

    🧪 Tests

    • API認証ロジックは単体テスト、本番環境でテスト済み
    Source code(tar.gz)
    Source code(zip)
Bifrost C2. Open-source post-exploitation using Discord API

Bifrost Command and Control What's Bifrost? Bifrost is an open-source Discord BOT that works as Command and Control (C2). This C2 uses Discord API for

38 Dec 05, 2022
Модуль для создания скриптов для ВКонтакте | vk.com API wrapper

vk_api vk_api – Python модуль для создания скриптов для ВКонтакте (vk.com API wrapper) Документация Примеры Чат в Telegram Документация по методам API

Kirill 1.2k Jan 04, 2023
Google Search Results via SERP API pip Python Package

Google Search Results in Python This Python package is meant to scrape and parse search results from Google, Bing, Baidu, Yandex, Yahoo, Home depot, E

SerpApi 254 Jan 05, 2023
Jira-cache - Jira cache with python

Direct queries to Jira have two issues: they are sloooooow many queries are impo

John Scott 6 Oct 08, 2022
Sukoshi is a proof-of-concept Python implant that leverages the MQTT protocol for C2 and uses AWS IoT Core as infrastructure.

Sukoshi | 少し Overview Sukoshi is a proof-of-concept Python implant that leverages the MQTT protocol for C2 and uses AWS IoT Core as infrastructure. It

Steven Patterson 37 Oct 29, 2022
A small script to migrate or synchronize users & groups from Okta to AWS SSO

aws-sso-sync-okta A small script to migrate or synchronize users & groups from Okta to AWS SSO Changelog Version Remove hardcoded values on variables

Paul 4 Feb 11, 2022
This is a tutorial on how to make a Discord Bot using the discord.py library

HowToMakeADiscordBot This Github repository is here to help you code a Discord Bot using the discord.py library! 1 - Setup: Download the code inside t

Baz 1 Oct 31, 2021
Python-random-quote - A file-based quote bot written in Python

Let's Write a Python Quote Bot! This repository will get you started with building a quote bot in Python. It's meant to be used along with the Learnin

amir mohammad fateh 1 Jan 02, 2022
Client to allow skytrack to be used with GSPro Golf simulator application

Skytrack Interface for GSPro A Basic Interface connection from Skytrack Launch Monitors to be able to play simulator golf via GSPro About The Project

James Peruggia 2 Oct 24, 2021
Send SMS text messages via email with as many accounts as you want :)

SMS-Spammer Send SMS text messages via email with as many accounts as you want :) Example Set Up Guide! To start log into the gmail account you would

Riceblades11 10 Oct 25, 2022
Tracker to check the covid shot slot availability in India and send mobile alerts via Twilio Messaging Service.

Cowin-Slot-Tracker Tracker to check the covid vaccine slot availability in India and send mobile notifications through Twilio Messaging Service. Requi

invalid username 27 Nov 12, 2022
Darkflame Universe Account Manager

Darkflame Universe Account Manager This is a quick and simple web application intended for account creation and management for a DLU instance created

31 Nov 29, 2022
Discord bot that manages expiration of roles with subscriptions!

Discord bot that manages expiration of roles with subscriptions!

Chakeaw__ 3 Apr 28, 2022
Music bot because Octave is down and I can : )

Chords On a mission to build the best Discord Music Bot View Demo · Report Bug · Request Feature Table of Contents About The Project Built With Gettin

Aman Prakash Jha 53 Jan 07, 2023
Scrapping malaysianpaygap & Extracting data from the Instagram posts

Scrapping malaysianpaygap & Extracting data from the posts Recently @malaysianpaygap has gotten quite famous as a platform that enables workers throug

Yudhiesh Ravindranath 65 Nov 09, 2022
Auto-updater for the Northstar Titanfall 2 client

northstar-updater Auto-updater for the Northstar Titanfall 2 client Usage Put the exe into your Titanfall 2 directory next to Titanfall2.exe Then, whe

7 Nov 25, 2022
Install and manage Proton-GE and Luxtorpeda for Steam and Wine-GE for Lutris with this graphical user interface. Based on AUNaseef's ProtonUp, made with Python 3 and Qt 6.

ProtonUp-Qt Qt-based graphical user interface to install and manage Proton-GE installations for Steam and Wine-GE installations for Lutris. Based on A

638 Jan 02, 2023
Step by Step Guide To Install Discord Py Master Branch on Replit

Guide to Install Discord Py Master Branch on Replit Step 1 Create an empty repl on replit Step 2 Add this Basic Code to the file main.py so as to chec

Pranav Saxena 7 Nov 18, 2022
Collect links to profiles by username through search engines

Marple Summary Collect links to profiles by username through search engines (currently Google and DuckDuckGo). Quick Start ./marple.py soxoj Results:

125 Dec 19, 2022
Parse discord tokens from any file, even if there is other shit in the file with them.

Discord-Token-Parser Parse discord tokens from any file, even if there is other shit in the file with them. Any. File. I glued together all html from

4 May 07, 2022