.. pybotters documentation master file, created by sphinx-quickstart on Thu Aug 5 19:33:41 2021. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. pybotters ========= .. toctree:: :maxdepth: 2 :hidden: user-guide advanced exchanges examples reference contributing **pybotters** は仮想通貨取引所向けの **非同期 HTTP / WebSocket API クライアント** です。 様々な取引所の Private API 認証に対応しており、素早くトレード bot を構築することができます。 また **WebSocket** と **DataStore** の機能を使うことで、リアルタイムデータを簡単に利用できます。 Installation ------------ pybotters は PyPI または GitHub からインストールできます。 From `PyPI `_ (リリースバージョン): .. code-block:: console $ pip install pybotters From `GitHub `_ (開発バージョン): .. code-block:: console $ pip install git+https://github.com/pybotters/pybotters.git ⚠️ Compatibility warning ------------------------ pybotters は現在次期バージョン (**v2**) を計画しています。 v2 ではコードベースのゼロから作り直され、全く新しい仕様に変更される予定です。 そのため、v1 で作成されたプログラムは v2 に対応していません。 ``requirements.txt`` や ``pyproject.toml`` などで pybotters を依存関係として指定している場合、 **バージョン指定** を行うことをお勧めします。 例えば、 ``pybotters<2.0`` と指定することで、v2 がリリースされても自動的にアップデートされないようにすることができます。 プロジェクト管理ツール (Poetry, PDM, Rye, UV など) を使っている場合は例として以下のようにバージョン指定をします: .. code-block:: console $ poetry|pdm|rye|uv add 'pybotters<2.0' .. important:: pybotters v2 のロードマップはこちらにあります! `pybotters/pybotters#248 `_ Quickstart ---------- `bitFlyer `_ の Private HTTP API と WebSocket API を利用する例です。 * HTTP API (Get Balance): .. code-block:: python import asyncio import pybotters async def main(): apis = {"bitflyer": ["BITFLYER_API_KEY", "BITFLYER_API_SECRET"]} async with pybotters.Client( apis=apis, base_url="https://api.bitflyer.com" ) as client: r = await client.fetch("GET", "/v1/me/getbalance") print(r.data) if __name__ == "__main__": asyncio.run(main()) .. note:: :class:`pybotters.Client` に API 認証情報 ``apis`` を入力することで、HTTP リクエストの **自動認証機能** が有効になります。 * WebSocket API (Ticker channel): .. code-block:: python import asyncio import pybotters async def main(): async with pybotters.Client() as client: wsqueue = pybotters.WebSocketQueue() await client.ws_connect( "wss://ws.lightstream.bitflyer.com/json-rpc", send_json={ "method": "subscribe", "params": {"channel": "lightning_ticker_BTC_JPY"}, "id": 1, }, hdlr_json=wsqueue.onmessage, ) async for msg in wsqueue: # Ctrl+C to break print(msg) if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: pass .. note:: :meth:`pybotters.Client.ws_connect` により、自動再接続機構を備えた WebSocket コネクションが作成されます。 .. warning:: WebSocket メッセージの受信は永続的に実行されます。 プログラムを終了するには ``Ctrl+C`` を入力します。 What's next ----------- まずは :doc:`user-guide` ページで pybotters の利用方法を学習しましょう。 または AI でこのドキュメントを活用したい場合は `llms.txt `_ または `llms-full.txt `_ をご利用ください。 これらのファイルは AI が処理しやすい形式でドキュメント内容を提供しています。 💖 Sponsor ----------- Please sponsor me! このプロジェクトはオープンソースで運営されています。 pybotters のソフトウェアとコミュニティの継続していく為に、是非 GitHub スポンサーによるサポートをお願いします 🙏 GitHub スポンサーになっていただくと、開発者がより多くの時間とリソースをプロジェクトに費やすことができ、新しい機能の開発やバグの修正、コミュニティのサポートなど、より良いプロダクトを提供できるようになります。 GitHub Sponsors: .. image:: https://github.githubassets.com/images/modules/profile/achievements/public-sponsor-default.png :target: https://github.com/sponsors/MtkN1 :height: 150px Indices and tables ------------------ * :ref:`genindex` * :ref:`modindex` * :ref:`search` User Guide ========== Client class ------------ :class:`pybotters.Client` は HTTP リクエストを行う為のメインクラスです。 :class:`.Client` の利用を開始するにはいくつかのステップが必要です。 1. :mod:`asyncio` と :mod:`pybotters` を ``import`` する 2. 非同期関数を *async def* で定義する 3. 定義した非同期関数の中から *async with* ブロックで :class:`.Client` インスタンスを初期化する .. code:: python import asyncio import pybotters async def main(): async with pybotters.Client() as client: ... asyncio.run(main()) 準備は整いましたか? :class:`.Client` インスタンスのメソッドから、以降に説明する HTTP リクエストと WebSocket 接続の機能を利用することができます。 .. note:: pybotters の中核機能は :mod:`asyncio` と :mod:`aiohttp` の上に構築されています。 それらの知識が全くないと、このユーザーガイドを進めるのは難しいかもしれません。 asyncio と aiohttp を掻い摘んで理解するには、著者によるこちらの記事がおすすめです。 botterのためのasyncio https://zenn.dev/mtkn1/articles/c61e77c1d221aa .. note:: このユーザーガイドの以降で説明する HTTP / WebSocket API には、仮想通貨取引所 bitFlyer の API を例として利用します。 ただし bitFlyer API の詳しい内容は説明を行いません。 公式ドキュメントをご確認ください。 https://lightning.bitflyer.com/docs HTTP API ------------- .. _fetch-api: Fetch API ~~~~~~~~~ :meth:`.Client.fetch` メソッドで HTTP リクエストを作成します。 :ref:`Fetch API ` は従来の :ref:`HTTP メソッド API ` と比較して、シンプルなリクエスト/レスポンスのフローを提供します。 一度の ``await`` 式で HTTP レスポンスデータの JSON デコードまで行います。 .. code-block:: python async def main(): async with pybotters.Client() as client: result = await client.fetch( "GET", "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"}, ) print(result.response.status, result.response.reason) print(result.data) 第 1 引数 (``method``) は HTTP メソッドです。 文字列で ``"GET"`` ``"POST"`` 等の HTTP メソッドを指定します。 第 2 引数 (``url``) はリクエストの URL です。 文字列で指定します。 返り値は :class:`.FetchResult` です。 :attr:`.FetchResult.response` 属性には :class:`aiohttp.ClientResponse` が格納されており、 :attr:`.FetchResult.data` 属性にはデコードされた JSON データが格納されています。 .. versionadded:: 1.0 .. _http-method-api: HTTP method API ~~~~~~~~~~~~~~~ 従来の :ref:`HTTP メソッド API ` で HTTP リクエストを作成します。 :ref:`HTTP メソッド API ` でリクエストを開始するには *async with* ブロックを利用します。 こちらは従来の :class:`aiohttp.ClientSession` と同様のリクエスト/レスポンスのフローになります。 * :meth:`.Client.request` * :meth:`.Client.get` * :meth:`.Client.post` * :meth:`.Client.put` * :meth:`.Client.delete` .. code-block:: python async def main(): async with pybotters.Client() as client: async with client.request( "GET", "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"}, ) as resp: data = await resp.json() print(data) async with client.get( "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"}, ) as resp: data = await resp.json() print(data) まず *async with* ブロックの返り値によってレスポンス :class:`aiohttp.ClientResponse` を受信します。 このレスポンスは HTTP ヘッダーまでとなります。 そして *async* :meth:`json` メソッドを ``await`` するによって残りの HTTP 本文が受信され、データが JSON としてデコードされた値が返ります。 Request parameters ~~~~~~~~~~~~~~~~~~ HTTP リクエストのパラメーターは ``params`` 引数または ``data`` 引数に指定します。 ``params`` 引数は「**URL クエリ文字列**」です。 主に ``GET`` リクエストに利用します。 ただし一部の仮想通貨取引所 API においては ``POST PUT DELETE`` リクエストでも利用することがあります。 .. code:: python async def main(): async with pybotters.Client() as client: result = await client.fetch( "GET", "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"}, ) print(r.response.status, r.response.reason) print(result.data) ``data`` 引数は「**HTTP 本文**」です。 主に ``POST`` リクエストで送信する JSON データとして利用します。 .. code:: python async def main(): async with pybotters.Client() as client: result = await client.fetch( "POST", "https://api.bitflyer.com/v1/me/sendchildorder", data={"product_code": "BTC_JPY", "child_order_type": "MARKET", "size": 0.01}, ) # NOTE: Authentication is required print(r.response.status, r.response.reason) print(result.data) これらの仕様は :ref:`Fetch API ` と :ref:`HTTP メソッド API ` の間でも同様です。 .. note:: この例は bitFlyer の「新規注文を出す」 API です。 実際にこれをリクエストするには自動認証 :ref:`authentication` が必要です。 .. warning:: aiohttp の知識がある方は JSON データの POST リクエストに ``json`` 引数を使おうとするかもしれません。 **しかし pybotters では** ``json`` **引数は利用できません** 。 これは pybotters の自動認証処理による影響です。 対応する取引所では ``data`` 引数を指定すると適切な JSON またはフォームなどの Content-Type が設定されます。 Response headers and data ~~~~~~~~~~~~~~~~~~~~~~~~~ :ref:`Fetch API ` の戻り値におけるオブジェクト属性 :attr:`.FetchResult.response` と、 :ref:`HTTP メソッド API ` の戻り値は共に :class:`aiohttp.ClientResponse` です。 HTTP レスポンスヘッダーについては、 ``headers`` 属性から取得できます。 .. code:: python async def main(): async with pybotters.Client() as client: # Fetch API r = await client.fetch( "GET", "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"}, ) print(r.response.headers) # HTTP method API async with client.get( "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"} ) as resp: print(resp.headers) HTTP レスポンスの JSON データについては、:ref:`Fetch API ` と :ref:`HTTP メソッド API ` にある説明の通りです。 :ref:`Fetch API ` では :attr:`.FetchResult.data` に格納されており、 :ref:`HTTP メソッド API ` では *async* :meth:`json` メソッドを ``await`` することで取得できます。 .. code:: python async def main(): async with pybotters.Client() as client: # Fetch API r = await client.fetch( "GET", "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"}, ) print(r.data) # HTTP method API async with client.get( "https://api.bitflyer.com/v1/getticker", params={"product_code": "BTC_JPY"} ) as resp: data = await resp.json() print(data) Base URL -------- :class:`.Client` の引数 ``base_url`` を設定することで、取引所 API エンドポイントのベース URL を省略して HTTP リクエストができます。 ``base_url`` を設定した場合、HTTP リクエストでは続きの相対 URL パスを設定します。 .. code:: python async def main(): async with pybotters.Client(base_url="https://api.bitflyer.com") as client: r = await client.fetch("GET", "/v1/getticker") r = await client.fetch("GET", "/v1/getboard") await client.ws_connect("wss://ws.lightstream.bitflyer.com/json-rpc") # Base URL is not applicable ただし pybotters では WebSocket API の URL には ``base_url`` は適用しません。 これは基本的に取引所の HTTP API と WebSocket API のベース URL が異なっている為であり、殆どの場合で期待される動作です。 .. _websocket-api: WebSocket API ------------- :meth:`.Client.ws_connect` メソッドで WebSocket 接続を作成します。 このメソッドは :mod:`asyncio` の機能により非同期で WebSocket コネクションを作成します。 .. code-block:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect( "wss://ws.lightstream.bitflyer.com/json-rpc", send_json={ "method": "subscribe", "params": {"channel": "lightning_ticker_BTC_JPY"}, }, hdlr_json=lambda msg, ws: print(msg), ) await ws.wait() # Ctrl+C to break * WebSocket メッセージの送信 ``send_str``, ``send_bytes``, ``send_json`` 引数で送信する WebSocket メッセージを指定します。 これらの引数は送信するメッセージをリストで括ることで複数のメッセージを送信できます (:ref:`multiple-websocket-senders-handlers`) 。 * WebSocket メッセージの受信 ``hdlr_str``, ``hdlr_bytes``, ``hdlr_json`` 引数で受信した WebSocket メッセージのハンドラ (コールバック) を指定します。 指定するハンドラの第 1 引数はそれぞれ :class:`str`, :class:`bytes`, :data:`typing.Any` を取る必要があります。 第 2 引数は :class:`.ClientWebSocketResponse` または ``None`` を取る必要があります。 上記のコードでは無名関数をハンドラに指定して WebSocket メッセージを標準出力しています。 pybotters には組み込みのハンドラとして、汎用性の高い :ref:`websocketqueue` や、 :ref:`取引所固有の DataStore ` があります。 これらの引数はハンドラをリストで括ることで複数のハンドラを指定できます (:ref:`multiple-websocket-senders-handlers`) 。 * 再接続 さらに :meth:`.Client.ws_connect` メソッドで作成した WebSocket 接続は **自動再接続** の機能を備えています。 これにより切断を意識することなく継続的にデータの取得が可能です。 戻り値は :class:`.WebSocketApp` です。 このクラスを利用して WebSocket のコネクションを操作できます。 上記の例では :meth:`.WebSocketApp.wait` メソッドで WebSocket の終了を待つことでプログラムの終了を防いでいます。 .. note:: :class:`.WebSocketApp` はに自動再接続の機構があります。 その為 :meth:`.WebSocketApp.wait` の待機は **実質的に無限待機です** 。 トレード bot ではなく、データ収集スクリプトなどのユースケースではハンドラに全ての処理を任せる場合があります。 そうした時に :meth:`.WebSocketApp.wait` はプログラムの終了を防ぐのに役に立ちます。 .. _authentication: Authentication -------------- 仮想通貨取引所の Private API を利用するには、API キー・シークレットによるユーザー認証が必要です。 pybotters では :class:`.Client` クラスの引数 ``apis`` に API 認証情報を渡すことで、認証処理が自動的に行われます。 以下のコードでは自動認証を利用して bitFlyer の Private API で資産残高の取得 (``/v1/me/getbalance``) のリクエストを作成します。 .. code:: python async def main(): apis = { "bitflyer": ["BITFLYER_API_KEY", "BITFLYER_API_SECRET"], } async with pybotters.Client(apis=apis) as client: result = await client.fetch("GET", "https://api.bitflyer.com/v1/me/getbalance") print(result.data) まるで Public API かのように Private API をリクエストを作成できます! もちろん、WebSocket API でも自動的に認証処理が行われます。 以下のコードでは bitFlyer の Private WebSocket API で注文イベント (``child_order_events``) を購読します。 .. code:: python async def main(): apis = { "bitflyer": ["BITFLYER_API_KEY", "BITFLYER_API_SECRET"], } async with pybotters.Client(apis=apis) as client: ws = await client.ws_connect( "wss://ws.lightstream.bitflyer.com/json-rpc", send_json={ "method": "subscribe", "params": {"channel": "child_order_events"}, "id": 123, }, hdlr_json=lambda msg, ws: print(msg), ) await ws.wait() # Ctrl+C to break .. warning:: コード上に API 認証情報をハードコードすることはセキュリティリスクがあります。 ドキュメント上は説明の為にハードコードしていますが、実際は環境変数を利用して ``os.getenv`` などから取得することを推奨します。 引数 ``apis`` の形式は以下のような辞書形式です。 .. code-block:: python { "API_NAME": [ "YOUR_API_KEY", "YOUR_API_SECRET", # "API_PASSPHRASE", # Optional ], "...": ["...", "..."], } pybotters の自動認証が対応している取引所の API 名はこちらの表から設定します。 ========================= ========================= Exchange API name ========================= ========================= Binance ``binance`` Binance Testnet (Future) ``binancefuture_testnet`` Binance Testnet (Spot) ``binancespot_testnet`` bitbank ``bitbank`` bitFlyer ``bitflyer`` Bitget ``bitget`` BitMEX ``bitmex`` BitMEX Testnet ``bitmex_testnet`` Bybit ``bybit`` Bybit Demo trading ``bybit_demo`` Bybit Testnet ``bybit_testnet`` Coincheck ``coincheck`` GMO Coin ``gmocoin`` Hyperliquid ``hyperliquid`` Hyperliquid Testnet ``hyperliquid_testnet`` KuCoin ``kucoin`` MECX ``mexc`` OKX ``okx`` OKX Demo trading ``okx_demo`` Phemex ``phemex`` Phemex Testnet ``phemex_testnet`` OKJ ``okj`` BitTrade ``bittrade`` ========================= ========================= また ``apis`` 引数に辞書オブジェクトではなく代わりに **JSON ファイルパス** を文字列として渡すことで、pybotters はその JSON ファイルを読み込みます。 .. code:: python async def main(): async with pybotters.Client(apis="/path/to/apis.json") as client: ... さらに :ref:`implicit-loading-of-apis` では、独自の環境変数などを利用して ``apis`` 引数の指定を省略して API 認証情報のハードコードを避けることができます。 .. _datastore: DataStore --------- :ref:`datastore` を利用することで WebSocket からのデータを簡単に処理、参照ができます。 :ref:`datastore` は「ドキュメント指向データベース」のような機能とデータ構造を持っています。 以下はデータを参照する為のメソッド :meth:`.DataStore.get` と :meth:`.DataStore.find` の利用例です。 >>> ds = pybotters.DataStore( ... keys=["id"], ... data=[ ... {"id": 1, "data": "foo"}, ... {"id": 2, "data": "bar"}, ... {"id": 3, "data": "baz"}, ... {"id": 4, "data": "foo"}, ... ], ... ) >>> print(ds.get({"id": 1})) {'id': 1, 'data': 'foo'} >>> print(ds.get({"id": 999})) None >>> print(ds.find()) [{'id': 1, 'data': 'foo'}, {'id': 2, 'data': 'bar'}, {'id': 3, 'data': 'baz'}, {'id': 4, 'data': 'foo'}] >>> print(ds.find({"data": "foo"})) [{'id': 1, 'data': 'foo'}, {'id': 4, 'data': 'foo'}] >>> print(ds.find({"id": "SPAM"})) [] * :meth:`.DataStore.get` * DataStore のキーを指定して一意のアイテム (1 件の辞書) を取得します * 一致するアイテムがない場合 ``None`` が返されます * :meth:`.DataStore.find` * アイテムをリストで取得します * クエリを指定しない場合全てのデータを取得されます * クエリを指定すると条件のデータのみを取得します。 一致するアイテムがない場合は空のリストが返されます ただし基本的に **DataStore クラスをそのまま利用するケースはありません**。 上記の例では :meth:`.DataStore.get` と :meth:`.DataStore.find` の説明の為に DataStore をそのまま利用しました。 基本的なユースケースでは次の :ref:`取引所固有の DataStore ` を利用します。 そこで格納されたデータを参照する方法として上記のメソッドを覚えておく必要があります。 .. note:: DataStore は、仮想通貨取引所の WebSocket API から高頻度で配信されるリアルタイムデータを処理してトレード bot から利用できるようにする為に開発されました。 DataStore の設計は MongoDB などの「ドキュメント指向データベース」を参考にしており、それを単純なリストと辞書のデータ構造で実現しています。 :mod:`sqlite3` のインメモリ機能などと比べても高速なデータ参照を実現しています。 またキー情報をハッシュ化してインデックスを作成することで一意のデータを特定できるようにしています。 それにより非常に高い頻度で更新される板情報などの更新処理に対応しています。 例えば Pandas DataFrame などのリッチなデータライブラリでリアルタイムの板情報を扱おうとすると、処理時間の注意が必要です。 DataFrame の更新には多くの処理が含まれる為、配信されるデータの更新頻度に対して DataFrame の更新処理が追い付かない場合があります。 それに比べて pybotters の DataStore はシンプルなデータを構造により高速な更新処理を実現しています。 ただし DataStore の内部構造は説明のように単純なリストと辞書なので **破壊可能である** ことに注意が必要です。 取得したアイテムをユーザー側で更新するべきではありません。 .. _exchange-specific-datastore: Exchange-specific DataStore --------------------------- :ref:`取引所固有の DataStore ` は対応取引所における WebSocket チャンネルの DataStore 実装です。 つまり、購読した WebSocket チャンネルのデータがこの取引所固有の DataStore に解釈されることでデータを利用できるようになります。 それぞれの :ref:`取引所固有の DataStore ` は :class:`.DataStoreCollection` を継承しており、これは :class:`.DataStore` の集まりです。 :class:`.DataStoreCollection` と :class:`.DataStore` の関係を一般的な RDB システムに例えると 「データベース」と「テーブル」のようなものです。 「データベース」には複数の「テーブル」が存在しており、「テーブル」にはデータの実体があります。 例: * :class:`.bitFlyerDataStore` (bitFlyer の WebSocket データをハンドリングする :class:`.DataStoreCollection`) * :attr:`.bitFlyerDataStore.ticker` (bitFlyer の Ticker チャンネルをハンドリングする :class:`.DataStore`) * :attr:`.bitFlyerDataStore.executions` (bitFlyer の約定履歴チャンネルをハンドリングする :class:`.DataStore`) * :attr:`.bitFlyerDataStore.board` (bitFlyer の板情報チャンネルをハンドリングする :class:`.DataStore`) * ... pybotters で提供されている取引所固有の DataStore は :doc:`exchanges` のページから探せます。 全てのリファレンスについては :ref:`exchange-specific-datastore-reference` のページにあります。 Attributes ~~~~~~~~~~ WebSocket チャンネルに対応する DataStore は、それぞれの取引所固有の DataStore の属性として割り当てられています。 >>> store = pybotters.bitFlyerDataStore() >>> store.ticker >>> store.executions >>> store.board WebSocket チャンネルに対応する全ての属性については、個別のリファレンスをご覧ください。 .. _onmessage: onmessage ~~~~~~~~~ 取引所固有の DataStore を利用するには、コールバック :attr:`.DataStoreCollection.onmessage` を :meth:`.Client.ws_connect` のハンドラ引数に渡します。 次のコードは bitFlyer の Ticker チャンネルを購読して DataStore としてデータを参照する例です。 .. code:: python 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_ticker_BTC_JPY"}, "id": 1, }, hdlr_json=store.onmessage, ) while True: # Ctrl+C to break ticker = store.ticker.get({"product_code": "BTC_JPY"}) print(ticker) await store.ticker.wait() .. _initialize: initialize ~~~~~~~~~~ WebSocket API は HTTP API と違って購読を開始しても「それ以降に更新されたデータ」しか配信されない場合があります。 そうするとプログラム開始時に「初期データ」が存在せず DataStore は空になってしまうので、トレード bot で利用するには不便です。 *async* :meth:`.DataStoreCollection.initialize` メソッドを利用すると HTTP API のデータを初期データとして格納できます。 次のコードは bitFlyer のポジションを HTTP API で初期化して、約定イベントチャンネルを購読することで完全なポジションを構築する例です。 .. code:: python async def main(): apis = { "bitflyer": ["BITFLYER_API_KEY", "BITFLYER_API_SECRET"], } async with pybotters.Client(apis=apis, base_url="https://api.bitflyer.com") as client: store = pybotters.bitFlyerDataStore() await store.initialize( client.get("/v1/me/getpositions", params={"product_code": "FX_BTC_JPY"}) ) 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, ) while True: # Ctrl+C to break positions = store.positions.find() print(positions) await store.positions.wait() :meth:`.DataStoreCollection.initialize` はそれぞれの取引所固有の DataStore において個別に実装されています。 その為、初期化に対応している HTTP API エンドポイントも異なります。 詳しくは個別のリファレンスをご覧ください。 .. _sorted: sorted ~~~~~~ 取引所固有の DataStore において Order Book 系の DataStore には :meth:`.DataStore.sorted` メソッドが実装されています。 これを利用するとリストでデータを参照する :meth:`.DataStore.find` とは違って、 ``{"asks": [...], "bids": [...]}`` のような辞書形式で板情報が参照できます。 また板情報はソート済みで返されるのでトレード bot で利用するのに便利です。 次のコードは bitFlyer の板情報を :meth:`.DataStore.sorted` で取得する例です。 .. code:: python 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_board_snapshot_BTC_JPY"}, "id": 1, }, { "method": "subscribe", "params": {"channel": "lightning_board_BTC_JPY"}, "id": 2, }, ], hdlr_json=store.onmessage, ) while True: # Ctrl+C to break board = store.board.sorted(limit=2) print(board) await store.board.wait() .. _wait: wait ~~~~ *async* :meth:`.DataStore.wait` メソッドは、その DataStore に更新が発生するまで待機できます。 上で説明した :ref:`onmessage` と :ref:`sorted` の例では、データの受信が始まる前に ``while True`` のループが始まるので最初に ``None`` や空のデータが標準出力されるはずです。 DataStore の参照をする前に :meth:`.DataStore.wait` することでデータの受信を待機できます。 次のコードは bitFlyer の Ticker を 2 銘柄を購読して受信するまで待機する例です。 .. code:: python 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_ticker_BTC_JPY"}, "id": 1, }, { "method": "subscribe", "params": {"channel": "lightning_ticker_ETH_JPY"}, "id": 2, }, ], hdlr_json=store.onmessage, ) while not len(store.ticker): await store.ticker.wait() print(store.ticker.find()) .. _watch: watch ~~~~~ *async* :meth:`.DataStore.watch` メソッドは、変更ストリームを開いて ``async for`` ループで更新データを待機及び取得できます。 *async* :meth:`.DataStore.wait` メソッドと同様に待機できますが、:meth:`.DataStore.watch` では変更データとその詳細を取得できます。 次のコードは bitFlyer の約定履歴を :meth:`.DataStore.watch` で監視する例です。 .. code:: python 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_BTC_JPY"}, "id": 1, }, hdlr_json=store.onmessage, ) with store.executions.watch() as stream: async for change in stream: # Ctrl+C to break print(change.data) .. _websocketqueue: WebSocketQueue -------------- DataStore が実装されていない取引所であったり、自らの実装でデータを処理したい場合は :class:`.WebSocketQueue` を利用できます。 .. code-block:: python async def main(): async with pybotters.Client() as client: wsqueue = pybotters.WebSocketQueue() await client.ws_connect( "wss://ws.lightstream.bitflyer.com/json-rpc", send_json={ "method": "subscribe", "params": {"channel": "lightning_ticker_BTC_JPY"}, }, hdlr_json=wsqueue.onmessage, ) async for msg in wsqueue: # Ctrl+C to break print(msg) Differences with aiohttp ------------------------ aiohttp との違いについて。 pybotters は `aiohttp `__ を基盤として利用しているライブラリです。 その為、:class:`pybotters.Client` におけるインターフェースの多くは :class:`aiohttp.ClientSession` と同様です。 また pybotters の HTTP リクエストのレスポンスクラスは :class:`aiohttp.ClientResponse` を返します。 その為 pybotters を高度に利用するには :class:`aiohttp` ライブラリについても理解しておくことが重要です。 ただし **重要な幾つかの違いも存在します** 。 * pybotters は HTTP リクエストの自動認証機能により、自動的に HTTP ヘッダーなどを編集します。 * pybotters では POST リクエストなどのデータは引数 ``data`` に渡します。 aiohttp では ``json`` 引数を許可しますが pybotters では許可されません。 これは認証機能による都合です。 * :meth:`pybotters.Client.fetch` は pybotters 独自の API です。 aiohttp には存在しません。 * :meth:`pybotters.Client.ws_connect` は aiohttp にも存在しますが、 pybotters では全く異なる独自の API になっています。 これは再接続機能や認証機能を搭載する為です。 Advanced Usage ============== .. _implicit-loading-of-apis: Implicit loading of ``apis`` ---------------------------- :class:`.Client` の引数 ``apis`` を指定せず以下のように暗黙的な読み込みが可能です。 1. カレントディレクトリに ``apis.json`` を配置する カレントディレクトリに ``apis.json`` という名前の JSON ファイルを配置することで自動的にそのファイルを読み込ます。 .. NOTE:: カレントディレクトリとは :meth:`os.getcwd` で得られる ``python`` コマンドを実行したディレクトリです。 .. warning:: Git などのバージョン管理を利用している場合、セキュリティ上の観点からカレントディレクトリの ``apis.json`` ファイルはバージョン管理外にするべきです。 ``.gitignore`` に ``apis.json`` を追加してください。 2. 環境変数 ``PYBOTTERS_APIS`` にファイルパスを設定する 環境変数 ``PYBOTTERS_APIS`` に API 認証情報の JSON ファイルパスを設定することでそのファイルを読み込みます。 UNIX 系の環境を利用している場合は、``~/.bashrc`` ファイルなどを編集することで環境変数を設定できます。 .. code:: bash # .~/.bashrc export PYBOTTERS_APIS=/path/to/apis.json **優先順位** 以下のような優先順位で pybotters に API 認証情報が読み込まれます。 複数の設定があった場合、下位の設定は無視されます。 1. :class:`.Client` の引数 ``apis`` を明示的に指定する 2. カレントディレクトリに ``apis.json`` JSON ファイルを配置する 3. 環境変数 ``PYBOTTERS_APIS`` に JSON ファイルパスを設定する Disable Authentication ---------------------- pybotters の自動認証処理を無効にする場合は、リクエストメソッドの引数 ``auth=None`` を設定します。 .. code:: python async def main(): apis = {"some_exchange": ["KEY", "SECRET"]} async with pybotters.Client(apis=apis) as client: r = await client.fetch("GET", "/public/endpoint", auth=None) .. note:: pybotters では :class:`~.Client` の引数 ``apis`` に API 認証情報を渡すことでホスト名に紐づく **全てのリクエスト** への自動認証が有効になります。 その為 Public API エンドポイントなどに対しても認証処理が働きます (これは pybotters が取引所のホスト名のみ把握しており、URL パス以降を把握していない為です) 。 Public API エンドポイントにおいて認証処理を無効にしたい場合は例のように引数 ``auth=None`` を設定します。 殆どの取引所では Public API に対して認証処理をしてもレスポンスには影響ありません。 ただし Binance Spot など一部では Public API がエラーになります。 その場合はこの ``auth=None`` を設定してください。 Fetch data validation --------------------- :meth:`Client.fetch() <.Client.fetch>` メソッドの返り値にあるプロパティ :attr:`FetchResult.data <.FetchResult.data>` は通常 JSON をパースしたオブジェクトが格納されますが、 少なくとも単純に ``if`` 文で評価しておくことでコードの安全性が高くなります。 .. code:: python async def main(): async with pybotters.Client() as client: r = await client.fetch("GET", "https://google.com") # Not JSON content if r.data: # NotJSONContent print(r["data"]) # KeyError will be raised else: print(f"Not JSON content: {r.text[:50]} ... {r.text[-50:]}") レスポンスが JSON ではないケースでは :attr:`FetchResult.data <.FetchResult.data>` には :class:`.NotJSONContent` が格納されます。 :class:`.NotJSONContent` は評価結果は必ず ``False`` となります。 その為 ``if r.data:`` で評価しておくことにより意図しないエラーを防げます。 .. note:: JSON の検証をより堅牢にするには Python 3.10 + の機能である ``match`` 文の Mapping Pattern を使うことをおすすめします。 https://peps.python.org/pep-0635/#mapping-patterns .. code:: python async def main(): async with pybotters.Client(base_url="https://api.bitflyer.com") as client: r = await client.fetch( "GET", "/v1/getticker", params={"product_code": "BTC_JPY"} ) match r.data: case {"product_code": str()}: print("Correct response", r.data) case {"status": int()}: print("Incorrect response", r.data) case pybotters.NotJSONContent(): print("NotJSONContent", r.data) aiohttp Keyword Arguments ------------------------- クライアント :class:`.Client` とリクエストメソッド :meth:`.Client.fetch` や :meth:`.Client.get` のキーワード引数 ``**kwargs`` に対応する引数を渡すことで、 pybotters がラップしている :class:`aiohttp.ClientSession` や :meth:`aiohttp.ClientSession.get` の引数にバイパスすることができます。 以下の例では aiohttp の実装である ``timeout`` 引数を設定してリクエストを作成します。 ``timeout`` 引数は pybotters には存在しません。 .. code:: python async def main(): async with pybotters.Client() as client: # TimeoutError will be raised await client.fetch("GET", "https://httpbin.org/delay/10", timeout=3.0) .. _multiple-websocket-senders-handlers: Multiple WebSocket senders/handlers ----------------------------------- :meth:`.Client.ws_connect` の ``send_*`` 引数と ``hdlr_*`` 引数には対応するオブジェクトのリスト形式で渡すことで 複数のメッセージが送信、または受信メッセージを複数のコールバックでハンドリングすることができます。 .. code:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect( "ws://...", send_json=[ {"op": "subscribe", "channel": "ch1"}, {"op": "subscribe", "channel": "ch2"}, {"op": "subscribe", "channel": "ch3"}, ], hdlr_json=[ func1, func2, func3, ], ) await ws.wait() .. warning:: これの副作用として「最上位がリスト形式の JSON」を ``send_json`` 引数に指定して送信することができません。 回避策として ``send_str`` 引数に ``json.dumps`` で文字列にダンプした値を与えてください。 しかしながら、仮想通貨取引所の WebSocket API において「最上位がリスト形式の JSON」を要求するものは今のところ確認していません。 Current WebSocket connection ---------------------------- :attr:`.WebSocketApp.current_ws` プロパティから現在接続中の WebSocket コネクションにアクセスできます。 この変数は :class:`pybotters.ClientWebSocketResponse` 型であり、 :class:`aiohttp.ClientWebSocketResponse` のサブクラスです。 このクラスから 1 回限りの WebSocket メッセージ送信などができます。 これは取引所 WebSocket API で注文の作成に対応しているケースなどで有用です。 .. code:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect("ws://...") if ws.current_ws: await ws.current_ws.send_json({"channel": "order"}) await ws.wait() ただし pybotters が管理している WebSocket が切断中にある場合、:attr:`.WebSocketApp.current_ws` プロパティは ``None`` が格納されます。 つまりプロパティのオブジェクトが動的に変化する可能性があると言いう意味です。 コードの安全性を高めるには、上記のコードのようにまず ``if ws.current_ws:`` と評価してから :attr:`.WebSocketApp.current_ws` を参照するべきです。 .. note:: :meth:`.WebSocketApp.current_ws.send_json` などで行うリクエストはその場限りのメッセージ送信になります。 これをチャンネルの購読に利用するべきではありません。 反対に :meth:`.Client.ws_connect` などの ``send_json`` 引数に与えるメッセージは、再接続も含めて接続直後に毎回送信するメッセージとなります。 WebSocket Handshake ------------------- :class:`.WebSocketApp` は ``await`` することで WebSocket ハンドシェイクが行われます。 正確にはバックグラウンドタスクによってハンドシェイクが終わるまで待機します。 .. code:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect("ws://...") # Wait WebSocket handshake 上記のコードをみると勘違いしがちですが :meth:`.Client.ws_connect` は **非同期関数ではなく同期関数です** 。 その正体としては :class:`.WebSocketApp` を生成しているだけです。 また :class:`.WebSocketApp` は ``await`` すると自身を返します。 .. code:: python async def main(): async with pybotters.Client() as client: ws = client.ws_connect("ws://...") # type: WebSocketApp ws = await ws # Wait WebSocket handshake, No need to assign ws variable 各状態のおける ``await WebSocketApp`` の仕様としては以下の通りです。 1. WebSocket 接続がない (初回または切断中) 場合、 WebSocket ハンドシェイクが行われるまで ``await`` によって待機します。 2. WebSocket 接続がある場合、 ``await`` による待機は即時完了します。 Automatic WebSocket heartbeat ----------------------------- :class:`.WebSocketApp` はデフォルトで自動 WebSocket ハートビートが有効になっています。 この動作は :class:`.Client.ws_connect` の引数 ``heartbeat`` によって変更できます。 ``heartbeat`` には ``float`` の秒数を指定します。 引数を指定しない場合デフォルトでは 10 秒です。 .. code:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect("ws://...", heartbeat=10.0) # default value ``heartbeat`` を設定するとバックグラウンドタスクが起動し、一定間隔で対象の WebSocket サーバーに Ping フレームを送信します。 その後 ``heartbeat`` 秒数間のタイムアウトを待ち、 Pong フレームを受信した場合はタイムアウトをリセットして次の Ping フレームを送信するまで待機します。 タイムアウトが発生した場合は現在の WebSocket 接続を切断して再接続を試みます。 ``heartbeat`` に ``None`` を指定すると Ping-Pong メッセージの送信を無効にします。 .. note:: WebSocket ハートビートは、 WebSocket の接続を保証する為の重要な機能です。 「お行儀のよい」 WebSocket サーバーは、切断時にクライアントに対して明示的な切断メッセージを送信します。 しかし一部の WebSocket サーバーは切断時に何もメッセージを送信しないため、クライアントは接続が切断されたかどうかを検知できません。 クライアントは接続が確立していると認識しているので pybotters に組み込まれている自動再接続も試行されません。 そういった状態に陥ると結果的に bot コードでは WebSocket データを受信せずにループ動作し続けることになります。 つまり、WebSocket 経由でポジションや注文や板情報などのデータを受信している場合はデータの状態が古いままになり、取引に支障をきたす可能性があります。 そこで WebSocket ハートビートを利用することでこの状態に陥ることを防ぎます。 Ping 及び Pong フレームは `WebSocket の仕様 `_ で定義されており、アプリケーションのメッセージ受信には影響されません。 これを送受信することで相手方のサービスが機能しているかを確認します。 前述の通りハートビートはデフォルトで有効になっており、トレード bot のユースケースでこれを無効にすることは推奨されません。 なお、このハートビート機能は aiohttp の実装 (:meth:`aiohttp.ClientSession.ws_connect` の ``heartbeat`` 引数) によるものです。 .. _manual-websocket-heartbeat: Manual WebSocket heartbeat -------------------------- :class:`.WebSocketApp` は自動で WebSocket ハートビートを実行しますが、:meth:`.WebSocketApp.heartbeat` メソッドを呼び出すことで手動でハートビートを実行できます。 .. code:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect("ws://...") while True: await ws.heartbeat() ... # Trading strategy 自動ハートビートによってある程度接続は保証されますが、手動ハートビートを実行することで任意のタイミングで接続を保証を確認できます。 :meth:`.WebSocketApp.heartbeat` を ``await`` することで、Ping フレームを送信して対応する Pong フレームの受信を待機します。 Pong フレームが引数 ``timeout`` の秒数受信できない場合は、現在の WebSocket 接続を切断して再接続を試みます。 その後、再度 Ping フレームを送信して Pong フレームの受信を試みます。 これらのハートビートシーケンスが終了するまで ``await`` によって待機します。 ハートビートのタイムアウトは引数 ``timeout`` で設定できます。 デフォルトは 10 秒です。 このメソッドは接続を保証したいステップで呼び出すべきでしょう。 例えば、WebSocket 経由でデータを受信して利用している場合はそれを利用する前が最適です。 WebSocket reconnection backoff ------------------------------ :meth:`.Client.ws_connect` の引数 ``backoff`` に ``float`` のタプルを設定することで、再接続の指数バックオフを変更できます。 タプルの意味は ``(最小待機秒, 最大待機秒, 係数, 初期待機秒)`` です。 .. code:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect("ws://...", backoff=(1.92, 60.0, 1.618, 5.0)) # default value 既定のバックオフ動作は以下の通りです。 * 正常切断であれば待機なしで再接続します * ハンドシェイク失敗であれば指数バックオフの秒数待機します * 初回の接続失敗であれば 0 ~ 5 秒 (BACKOFF_INITIAL) の間のランダムな時間待機します * 二回目の接続失敗であれば 1.92 秒 (BACKOFF_MIN) に 1.618 (BACKOFF_FACTOR) を掛けた時間待機します * その後の接続失敗であれば前回の待機時間にさらに 1.618 (BACKOFF_FACTOR) を掛けた時間待機します * ただし待機時間の上限は 60.0 秒 (BACKOFF_MAX) です * 接続に成功した場合はバックオフの計算は初回のステップにリセットされます URL when reconnecting to WebSocket ---------------------------------- :attr:`.WebSocketApp.url` に URL を代入することで、接続する WebSocket URL を変更できます。 .. code:: python async def main(): async with pybotters.Client() as client: ws = await client.ws_connect("ws://example.com/ws?token=xxxxx") ... ws.url = "ws://example.com/ws?token=yyyyy" 接続中の場合は直ちに影響はなく、その接続が終了した次回の接続で設定した WebSocket URL が利用されます。 .. note:: これはトークン認証方式を採用している取引所の WebSocket 接続に便利です。 多くの場合はそのトークンを延長する API がありますが、何かの原因でトークンが失効してしまった場合に別のトークンを発行してそれを URL に設定できます。 DataStore Iteration ------------------- :ref:`datastore` では :meth:`.DataStore.get` と :meth:`.DataStore.find` でデータを取得する方法を説明しましたが、他にもイテレーションによって取得することもできます。 >>> ds = pybotters.DataStore( ... keys=["id"], ... data=[ ... {"id": 1, "data": "foo"}, ... {"id": 2, "data": "bar"}, ... {"id": 3, "data": "baz"}, ... {"id": 4, "data": "foo"}, ... ], ... ) >>> for item in ds: ... print(item) ... {'id': 1, 'data': 'foo'} {'id': 2, 'data': 'bar'} {'id': 3, 'data': 'baz'} {'id': 4, 'data': 'foo'} または :func:`reversed` を利用して逆順で取得もできます。 >>> for item in reversed(ds): ... print(item) ... {'id': 4, 'data': 'foo'} {'id': 3, 'data': 'baz'} {'id': 2, 'data': 'bar'} {'id': 1, 'data': 'foo'} Maximum number of data in DataStore ----------------------------------- DataStore は :attr:`.DataStore._MAXLEN` 変数にて最大件数の制限を設けています。 これはトレード履歴のような大量に配信されるデータの格納することによって、マシンの RAM が枯渇しないようにするためです。 この制限を超えると、古いデータから順に自動で削除されます。 :attr:`.DataStore._MAXLEN` は、取引所固有の DataStore にてチャンネルごとに異なる値が設定されています。 通常は最大 9,999 件、トレード履歴などは最大 99,999 件として設定しています。 以下は例として :class:`.bitFlyerDataStore` で Ticker と約定履歴ストアの最大件数を確認するコードです。 >>> store = pybotters.bitFlyerDataStore() >>> store.ticker._MAXLEN 9999 >>> store.executions._MAXLEN 99999 How to implement original DataStore ----------------------------------- :class:`.DataStoreCollection` と :class:`.DataStore` を継承したクラスを作成することで、 ユーザーは pybotters が対応していない取引所や、pybotters ビルドインの実装に満足しない場合に独自の DataStore を実装することができます。 以下の手順に従うことで、pybotters 既定仕様の DataStore が実装できます。 * :class:`.DataStoreCollection` のサブクラス 1. :meth:`_init` メソッド * 引数: なし * 処理: :meth:`.DataStoreCollection.create` を使って取引所の WebSocket チャンネルに相当する DataStore を生成する処理を実装します 2. :meth:`_onmessage` メソッド * 引数: ``msg: Any, ws: ClientWebSocketResponse`` * 処理: 受信した WebSocket メッセージのチャンネルを解釈して各 DataStore に振り分ける処理を実装します 3. *async* :meth:`initialize` メソッド * 引数: ``*aws: Awaitable[aiohttp.ClientResponse]`` * 処理: 初期化用の HTTP API のレスポンスを解釈して各 DataStore に振り分ける処理を実装します 4. class Properties * :meth:`_init` メソッド内で生成した DataStore に便宜的にアクセスできるように、クラスに同名のプロパティを定義します * :class:`.DataStore` のサブクラス 1. :const:`_KEYS` 変数 * 解釈した WebSocket メッセージにキーが存在する場合、それをリストで設定します * 差分データが配信される WebSocket チャンネルにおいてこれを設定します * 例えば板情報について考えると、 ``"銘柄"`` と ``"方向"`` と ``"価格"`` がキーとなります。 このキーを元に ``"数量"`` を更新したりあるいはデータを削除します * キーが存在しないデータは :const:`_KEYS` を設定する必要がありません * 例えば約定履歴は時系列データです。新しいデータが配信されますが、過去のデータが更新されることはありません 2. :const:`_MAXLEN` 変数 * 変数を上書きしない場合値は 9999 となっています。 pybotters の既定では時系列データの場合は値を 99999 に上書きしています 3. :meth:`_onmessage` メソッド * 引数: ``msg: Any`` * ※ :meth:`.DataStoreCollection._onmessage` から渡す引数仕様に変更可能です * 処理: :meth:`.DataStore._insert` :meth:`.DataStore._update` :meth:`.DataStore._delete` などの CURD メソッドを用いて、WebSocket メッセージを解釈して内部のデータを更新します 4. :meth:`_onresponse` メソッド * 引数: ``msg: Any`` * ※ :meth:`.DataStoreCollection.initialize` から渡す引数仕様に変更可能です * 処理: :meth:`.DataStore._insert` :meth:`.DataStore._update` :meth:`.DataStore._delete` などの CURD メソッドを用いて、レスポンスを解釈して内部のデータを更新します 5. :meth:`sorted` メソッド (※板情報系のみ) * 引数: ``query: dict[str, Any]`` * 処理: 板情報を ``"売り", "買い"`` で分類した辞書を返します (:ref:`bitFlyerDataStore での例 `) 。 次のコードはシンプルな独自の DataStore の例です。 .. code:: python class SomeDataStore(DataStoreCollection): """ Some Exchange データストア""" def _init(self): self.create("trade") self.create("orderbook") self.create("position") def _onmessage(self, msg, ws): # ex: msg = {"channel": "xxx", "data": ...} channel = msg.get("channel") data = msg.get("data") if channel == "trade": self.trade._onmessage(data) elif channel == "orderbook" self.orderbook._onmessage(data) elif channel == "position" self.position._onmessage(data) async def initialize(self, *aws): for f in asyncio.as_completed(aws): resp = await f data = await resp.json() if resp.url.path == "/api/position": self.position._onresponse(data) @property def trade(self) -> "Trade": return self.get("trade") @property def orderbook(self) -> "OrderBook": return self.get("orderbook") @property def position(self) -> "Position": return self.get("position") class Trade(DataStore): """約定履歴ストア""" _MAXLEN = 99999 def _onmessage(self, data): # ex: data = [{"symbol": "xxx", "price": 1234, "...": ...}] self._insert(data) class OrderBook(DataStore): """板情報ストア""" _KEYS = ["symbol", "side", "price"] def _onmessage(self, data): # ex: data = {"symbol": xxx", "asks": {"price": 1234, "size": 0.1}, ...}, "bids": ...} symbol = data["symbol"] data_to_update = [] data_to_delete = [] for side in ("asks", "bids"): for row in data[side]: row = {"symbol": symbol, "side": side, **row} if row["price"] == 0.0: data_to_delete.append(row) else: data_to_update.append(row) self._update(data_to_update) self._update(data_to_delete) def sorted(self, query=None, limit=None): return self._sorted( item_key="side", item_asc_key="asks", item_desc_key="bids", sort_key="price", query=query, limit=limit, ) class Position(DataStore): """ポジションストア""" _KEYS = ["symbol"] def _onmessage(self, data): # ex: data = [{"symbol": "xxx", "side": "Buy", "size": 0.1] self._update(data) def _onresponse(self, data): # ex: data = [{"symbol": "xxx", "side": "Buy", "size": 0.1] self._clear() self._update(data) 既存の DataStore 実装を参考にするには、リポジトリの ``models/`` 内ソースコードを参照してください。 もし pybotters が未対応の取引所の DataStore を実装された場合は、pybotters へのコントリビュート (ソースコードの寄付) を検討して頂けるとありがたいです 🙏 pybotters は無料のオープンソースソフトウェア・プロジェクトであり人々のボランティア精神によって成り立っています。 コントリビュートするには GitHub リポジトリに Pull request を作成します。 詳しくは :doc:`contributing` ページをご覧ください。 Exchanges ========= 対応取引所における pybotters の個別の仕様について説明します。 コード例などについては :doc:`examples` をご覧ください。 bitFlyer -------- https://lightning.bitflyer.com/docs Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"bitflyer": ["API_KEY", "API_SERCRET"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://lightning.bitflyer.com/docs#%E8%AA%8D%E8%A8%BC * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます (*JSON-RPC 2.0 over WebSocket* のみ) 。 https://bf-lightning-api.readme.io/docs/realtime-api-auth WebSocket ~~~~~~~~~ bitFlyer の WebSocket には *Socket.IO* と *JSON-RPC 2.0 over WebSocket* がありますが、 pybotters の認証と DataStore は *JSON-RPC 2.0 over WebSocket* のみ対応しています。 DataStore ~~~~~~~~~ * :class:`.bitFlyerDataStore` (*JSON-RPC 2.0 over WebSocket* のみ) 以下の DataStore に格納される値は pybotters による独自実装です。 また特定のキーのみが更新されます。 * :attr:`.bitFlyerDataStore.positions` * ``size`` キーのみが更新されます。 * :attr:`.bitFlyerDataStore.collateral` * ``collateral`` キーのみが更新されます。 * :attr:`.bitFlyerDataStore.balance` * ``amount`` キーのみが更新されます。 .. warning:: bitFlyer の WebSocket チャンネル ``child_order_events`` は各種データを提供しておらず、計算の元となる約定情報のみを提供しています。 その為 ``bitFlyerDataStore`` は約定情報から独自に各種データを計算しています。 値が正確になるよう努めていますが、端数処理などの影響で実データとズレが生じる可能性があることに注意してください。 正確な値を必要とする場合は、HTTP API による :meth:`.bitFlyerDataStore.initialize` を利用してください。 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 GMO Coin -------- https://api.coin.z.com/docs/ Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"gmocoin": ["API_KEY", "API_SERCRET"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://api.coin.z.com/docs/#authentication-private * WebSocket 認証 GMO Coin はトークン認証方式です。 https://api.coin.z.com/docs/#authentication-private-ws :class:`.helpers.GMOCoinHelper` には「アクセストークン」を管理する機能があります。 :class:`.helpers.GMOCoinHelper` を利用すると「アクセストークンを延長」と「アクセストークンを取得」を自動で実行します。 さらに取得したアクセストークンから Private WebSocket URL を構築して :attr:`.WebSocketApp.url` を自動で更新します。 通常、 `GMO コインの定期メンテナンス `_ 後はアクセストークンは失効して Private WebSocket の再接続は失敗してしまいます。 このヘルパーを使うと、失効したアクセストークンを自動で再取得するので、メンテナンス後の再接続を確立するのに便利です。 利用可能なコードは :ref:`Examples GMOCoinHelper ` をご覧ください。 WebSocket ~~~~~~~~~ * レート制限 pybotters は GMO コインの WebSocket API の購読レート制限に対応しています。 https://api.coin.z.com/docs/#restrictions :meth:`.Client.ws_connect` でメッセージを送信する際、レート制限が自動適用されます。 DataStore ~~~~~~~~~ * :class:`.GMOCoinDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 bitbank ------- https://github.com/bitbankinc/bitbank-api-docs Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"bitbank": ["API_KEY", "API_SERCRET"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 認証方式は ``ACCESS-TIME-WINDOW`` を採用します。 https://github.com/bitbankinc/bitbank-api-docs/blob/master/rest-api_JP.md#%E8%AA%8D%E8%A8%BC * PubNub 認証 :mod:`pybotters.helpers.bitbank` のヘルパー関数を利用して、自動的に PubNub の認証を行います。 WebSocket ~~~~~~~~~ * Socket.IO bitbank の Public WebSocket は Socket.IO で実装されています。 pybotters は Socket.IO にネイティブでは対応していない為、低レベルで URL の指定と購読リクエストを送信をする必要があります。 低レベルで Socket.IO の購読リクエストには :meth:`.Client.ws_connect` の引数 ``send_str`` を ``'42["join-room","depth_whole_btc_jpy"]'`` のように指定します。 また pybotters は Socket.IO v4 に対応していません。 接続するには URL で v3 ``EIO=3`` を指定する必要があります。 利用可能なコードは :doc:`examples` をご覧ください。 * Ping-Pong * Socket.IO の Ping-Pong が自動で送信されます。 PubNub ~~~~~~ * PubNub クライアント bitbank の Private Stream API は PubNub によって配信されています。 これは WebSocket のようなプロトコルではありません。 pybotters はヘルパー関数として組み込みの PubNub クライアント :mod:`pybotters.helpers.bitbank` を提供しています。 このヘルパー関数群では Private Stream API のサブスクライブをできます。 さらにトークンの自動取得・トークンの自動更新を行います。 また :class:`.bitbankPrivateDataStore` を簡単に利用することができます。 (:ref:`Examples `) 別途、ファースト・パーティの `PubNub SDK `_ を利用することもできます。 これより高機能ですが、ただし pybotters の HTTP セッションとは互換性がありません。 組み込みのヘルパー関数を利用することで、イベントループをより適切に管理することができます。 DataStore ~~~~~~~~~ * :class:`.bitbankDataStore` * :class:`.bitbankPrivateDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 Coincheck --------- https://coincheck.com/ja/documents/exchange/api Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"coincheck": ["API_KEY", "API_SERCRET"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://coincheck.com/ja/documents/exchange/api#auth * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます。 https://coincheck.com/ja/documents/exchange/api#private-channels DataStore ~~~~~~~~~ * :class:`.CoincheckDataStore` * :class:`.CoincheckPrivateDataStore` .. note:: :class:`.CoincheckDataStore` の orderbook は、ドキュメントで説明されていない ``[pair]-limit-range-orderbook`` / ``GET /api/order_books?version=1.0`` にも対応しています。 こちらの payload には ``sequence_number`` が含まれるため、REST snapshot と WebSocket 更新を 安全に同期したい場合はこちらの組み合わせを利用してください。 .. warning:: 新規注文イベントは WebSocket からは送信されません。 ``POST /api/exchange/orders`` のレスポンスを別途再利用する必要があります。 またオープンオーダーに関して Coincheck のデータ形式には未約定数量が含まれていません。 ``CoincheckPrivateDataStore`` ではオープンオーダーの未約定数量が追跡できるように ``pending_amount`` および ``pending_market_buy_amount`` をイベントごとに計算してキーとして追加しています。 詳しくは :attr:`.CoincheckPrivateDataStore.order` をご覧ください。 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 OKJ --- https://dev.okcoin.jp/en/ Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"okj": ["API_KEY", "API_SERCRET", "API_PASSPHRASE"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://dev.okcoin.jp/en/#summary-yan-zheng * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます。 https://dev.okcoin.jp/en/#spot_ws-login WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://dev.okcoin.jp/en/#spot_ws-limit DataStore ~~~~~~~~~ 未サポート。 BitTrade -------- https://api-doc.bittrade.co.jp/ Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"bittrade": ["API_KEY", "API_SERCRET"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://api-doc.bittrade.co.jp/#4adc7a21f5 * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます。 https://api-doc.bittrade.co.jp/#7a52d716ff WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 * https://api-doc.bittrade.co.jp/#401564b16d * https://api-doc.bittrade.co.jp/#111d6cb2aa DataStore ~~~~~~~~~ 未サポート。 Bybit ----- https://bybit-exchange.github.io/docs/v5/intro V5 API のみ対応しています。 V3 API には対応していません。 Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"bybit": ["API_KEY", "API_SERCRET"]}`` * ``{"bybit_demo": ["API_KEY", "API_SERCRET"]}`` * ``{"bybit_testnet": ["API_KEY", "API_SERCRET"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://bybit-exchange.github.io/docs/v5/guide#authentication * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます。 https://bybit-exchange.github.io/docs/v5/ws/connect#authentication また Websocket Trade API におけるメッセージ送信では ``header`` オブジェクトにタイムスタンプ ``X-BAPI-TIMESTAMP`` が自動付与されます。 https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://bybit-exchange.github.io/docs/v5/ws/connect#how-to-send-the-heartbeat-packet DataStore ~~~~~~~~~ * :class:`.BybitDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 Binance ------- https://developers.binance.com/docs/binance-spot-api-docs/CHANGELOG pybotters は Binance API において Spot /USDⓈ-M / COIN-M / WebSocket API (Spot) で動作確認をしています。 Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"binance": ["API_KEY", "API_SERCRET"]}`` (Mainnet: Spot/USDⓈ-M/COIN-M) * ``{"binancespot_testnet": ["API_KEY", "API_SERCRET"]}`` (Testnet: Spot) * ``{"binancefuture_testnet": ["API_KEY", "API_SERCRET"]}`` (Testnet: USDⓈ-M/COIN-M) * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 * https://developers.binance.com/docs/binance-spot-api-docs/rest-api#signed-endpoint-examples-for-post-apiv3order * https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#signed-trade-and-user_data-endpoint-security * https://developers.binance.com/docs/derivatives/coin-margined-futures/general-info#signed-trade-and-user_data-endpoint-security * WebSocket 認証 Binance はトークン認証方式の為、ユーザーコードで URL に ``listenKey`` 含める必要があります。 * https://developers.binance.com/docs/binance-spot-api-docs/user-data-stream * https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Connect * https://developers.binance.com/docs/derivatives/coin-margined-futures/user-data-streams/Connect ただし Binance 系 DataStore に ``listenKey`` を管理する機能があります。 Binance 系 DataStore の ``initialize()`` は「*Create a ListenKey*」系の POST リクエストに対応しています。 これにより ``listenKey`` が DataStore の属性 ``listenkey`` に格納されます。 この属性を利用すると ``listenKey`` 付き URL を構築するのに便利です。 また DataStore 側で「*Ping/Keep-alive a ListenKey*」系の定期リクエストが有効になる為、ユーザーコードでの延長処理は不要です。 * WebSocket 認証 (*WebSocket API*) pybotters では Binance で *WebSocket API* と表されるタイプの API 認証に対応しています。 これは WebSocket メッセージで注文の作成などを可能にするもので、現時点では Spot のみ対応しています。 https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api 送信する WebSocket メッセージに対して、取引所が定める認証情報が自動設定されます。 https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#signed-trade-and-user_data-request-security これを利用するには、 :attr:`.WebSocketApp.current_ws` から ``send_json()`` メソッドを利用して引数 ``auth=pybotters.Auth`` を設定します。 WebSocket ~~~~~~~~~ * レート制限 pybotters は Binance Spot のみにある WebSocket API の購読レート制限に対応しています。 https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#websocket-limits :meth:`.Client.ws_connect` でメッセージを送信する際、レート制限が自動適用されます。 DataStore ~~~~~~~~~ * :class:`.BinanceSpotDataStore` (Spot) * :class:`.BinanceUSDSMDataStore` (USDⓈ-M) * :class:`.BinanceCOINMDataStore` (COIN-M) 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 OKX --- https://www.okx.com/docs-v5/en/ Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"okx": ["API_KEY", "API_SERCRET", "API_PASSPHRASE"]}`` (Live trading) * ``{"okx_demo": ["API_KEY", "API_SERCRET", "API_PASSPHRASE"]}`` (Demo trading) * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://www.okx.com/docs-v5/en/#overview-rest-authentication * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます。 https://www.okx.com/docs-v5/en/#overview-websocket-login WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://www.okx.com/docs-v5/en/#overview-websocket-overview DataStore ~~~~~~~~~ * :class:`.OKXDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 Phemex ------ https://phemex-docs.github.io/ Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"phemex": ["API_KEY", "API_SERCRET"]}`` (Mainnet) * ``{"phemex_testnet": ["API_KEY", "API_SERCRET"]}`` (Testnet) * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://phemex-docs.github.io/#rest-request-header * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます。 https://phemex-docs.github.io/#user-authentication WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://phemex-docs.github.io/#heartbeat DataStore ~~~~~~~~~ * :class:`.PhemexDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 Bitget ------ https://www.bitget.com/api-doc/common/intro Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"bitget": ["API_KEY", "API_SERCRET", "API_PASSPHRASE"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://www.bitget.com/api-doc/common/signature * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報の WebSocket メッセージが自動送信されます。 https://www.bitget.com/api-doc/common/websocket-intro WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://www.bitget.com/api-doc/common/websocket-intro#connect DataStore ~~~~~~~~~ * :class:`.BitgetV2DataStore` * :class:`.BitgetDataStore` MEXC ---- https://mexcdevelop.github.io/apidocs/spot_v3_en/ .. warning:: MEXC Future は注文系 API が *maintenance* となっているので、**実質的に API トレードできません**。 https://mexcdevelop.github.io/apidocs/contract_v1_en/#update-log また Spot についても一部銘柄 (**なんと BTC/USDT を含む**) は同じく注文系 API が利用停止になっています。 `https://support.mexc.com/hc/ja/articles/15149585234969-MEXC-BTC-USDT-FTM-USDT-OP-USDT-DOGE-USDT各取引ペアのAPIアップグレード-及びメンテナンスに関するお知らせ `_ Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"mexc": ["API_KEY", "API_SERCRET"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://mexcdevelop.github.io/apidocs/spot_v3_en/#signed * WebSocket 認証 MEXC はトークン認証方式の為、ユーザーコードで URL に ``listenKey`` 含める必要があります。 https://mexcdevelop.github.io/apidocs/spot_v3_en/#websocket-user-data-streams WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://mexcdevelop.github.io/apidocs/spot_v3_en/#websocket-market-streams DataStore ~~~~~~~~~ 注文系 API が利用できないことを鑑みて、サポート対象外としています。 KuCoin ------ https://www.kucoin.com/docs/beginners/introduction Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"kucoin": ["API_KEY", "API_SERCRET", "API_PASSPHRASE"]}`` * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://www.kucoin.com/docs/basic-info/connection-method/authentication/creating-a-request * WebSocket 認証 KuCoin はトークン認証方式の為、ユーザーコードで URL と ``token`` の発行をする必要があります。 https://www.kucoin.com/docs/websocket/basic-info/apply-connect-token/private-channels-authentication-request-required- ただし KuCoin 系 DataStore には発行された URL と ``token`` を管理する機能があります。 KuCoin 系 DataStore の ``initialize()`` は上記 ``/api/v1/bullet-private`` の POST リクエストに対応しています。 これにより発行された URL と ``token`` が DataStore の属性 ``endpoint`` に格納されます。 この属性を利用すると KuCoin の WebSocket URL を構築するのに便利です。 また同様に ``initialize()`` は ``/api/v1/bullet-public`` の POST リクエストにも対応しています。 https://www.kucoin.com/docs/websocket/basic-info/apply-connect-token/public-token-no-authentication-required- WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://www.kucoin.com/docs/websocket/basic-info/ping DataStore ~~~~~~~~~ * :class:`.KuCoinDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 BitMEX ------ https://www.bitmex.com/app/apiOverview .. warning:: BitMEX Mainnet は日本国内からは利用できません。 Testnet のみ利用可能です。 https://blog.bitmex.com/ja-jp-notice-to-japan-residents/ Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"bitmex": ["API_KEY", "API_SERCRET"]}`` (Mainnet) * ``{"bitmex_testnet": ["API_KEY", "API_SERCRET"]}`` (Testnet) * HTTP 認証 HTTP リクエスト時に取引所が定める認証情報が自動設定されます。 https://www.bitmex.com/app/apiKeysUsage#Authenticating-with-an-API-Key * WebSocket 認証 WebSocket 接続時に取引所が定める認証情報が自動設定されます。 https://www.bitmex.com/app/wsAPI#API-Keys DataStore ~~~~~~~~~ * :class:`.BitMEXDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 Hyperliquid ----------- https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api Authentication ~~~~~~~~~~~~~~ * API 認証情報 * ``{"hyperliquid": ["PRIVATE_KEY"]}`` (Mainnet) * ``{"hyperliquid_testnet": ["PRIVATE_KEY"]}`` (Testnet) * HTTP 認証 `Exchange endpoint `_ (``/exchange``) へのリクエストに対して以下の Request Body を省略することができます。 省略した場合、以下の値が自動設定されます。 * ``nonce``: 現在時刻のミリ秒 * ``signature``: ``action`` をハッシュ化し秘密鍵で署名した値 実際の利用方法は :ref:`Examples ` を参照してください。 * WebSocket 認証 `Post requests `_ の ``action`` タイプのメッセージ送信に対して以下の認証情報が自動設定されます。 * ``nonce``: 現在時刻のミリ秒 * ``signature``: ``action`` をハッシュ化し秘密鍵で署名した値 手動で署名をする必要がある場合は、より低レベルな署名ヘルパー :mod:`pybotters.helpers.hyperliquid` を利用してください。 WebSocket ~~~~~~~~~ * Ping-Pong 取引所が定める Ping-Pong メッセージが自動送信されます。 https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket DataStore ~~~~~~~~~ * :class:`.HyperliquidDataStore` 対応している WebSocket チャンネルはリファレンスの *ATTRIBUTES* をご覧ください。 Examples ======== このページでは :mod:`pybotters` のサンプルコードを紹介します。 .. note:: ここのサンプルコードでは `rich `_ ライブラリの print を利用しています。 :mod:`rich` はターミナル上に美しい形式のテキストを表示してくれるライブラリです。 API で取得した JSON を ``rich.print`` すると、人間に分かりやすく綺麗に表示してくれるのでとても役に立ちます。 別途インストールすることをおすすめします。 .. code:: bash pip install rich Trading bot ----------- :mod:`pybotters` で :ref:`websocket-api` と :ref:`datastore` を利用した bitFlyer トレード bot の基礎的な構成例です。 説明はコードの下にあります。 .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/trading-bot/bitflyer.py Workflow ~~~~~~~~ このサンプルコードは以下のような処理フローとなっています。 1. 環境変数から API 認証情報などを取得する 2. DataStore を生成して HTTP API で初期化する 3. WebSocket に接続する 4. WebSocket からの初期データを待機する 5. メインループを開始する メインループは、以下のような処理フローとなっています。 1. DataStore から各種データを取得する 2. アルゴリズム関数にデータを入力して、注文条件を計算する .. note:: アルゴリズムはトレード bot の損益を左右する最も重要な部分です。 あなたのトレード戦略を実装しましょう! サンプルコードでは「既存の注文がない場合、ベスト Bid - 1000 に買い指値を置く」というアルゴリズムになっています。 3. 注文の執行条件が真の場合、注文を執行する 1. 注文イベントの変更ストリームを開く 2. HTTP API で注文を送信する 3. HTTP API レスポンスが正常なら、DataStore のデータを同期するため待機します 1. 注文イベントの変更ストリームを開始して、データを取得するまで待機する 2. 取得したデータの注文受付 ID が HTTP API と同じ ID ならストリームを抜ける .. note:: DataStore の注文やポジションを利用してアルゴリズムで注文条件を管理している場合、**この待機処理は重要です** 。 HTTP API で注文した結果は WebSocket の注文イベントとして流れてきます。 その注文イベントによって DataStore のデータが更新されます。 しかしその WebSocket の注文イベントはいつ流れてくるか分かりません。 このサンプルコードでは 1 秒後に次のループに移ります。 bitFlyer の取引マッチングエンジンが混雑している場合、 1 秒より後に流れてくるかもしれません。 そうすると、DataStore が同期されていないので次のループでは **既存の注文がない** ことになります。 サンプルコードでのアルゴリズムは、既存の注文がない場合指値注文をするので **重複注文が発生することになります** 。 WebSocket を利用しつつ注文やポジションを厳密に管理するアプローチでは、このようにして待機処理を行います。 もちろん、このように厳密に管理せず小口で注文するようなアプローチも考えられるので、あなたのトレード戦略にあったアプローチを検討してください。 4. 1 秒待機して次のループに移る Place Order ----------- Hyperliquid ~~~~~~~~~~~ .. _examples-place-order-hyperliquid: .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/order/hyperliquid.py Order Book watching ------------------- 対応取引所の :ref:`取引所固有の DataStore ` を利用した板情報を監視するサンプルコードです。 このサンプルコードでは以下について理解できるでしょう。 1. それぞれの取引所に WebSocket 接続する 2. WebSocket でチャンネルを購読する 3. Order Book 系ストアの扱い方 4. Order Book 系ストアのデータ構造 bitFlyer ~~~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/bitflyer.py GMO Coin ~~~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/gmocoin.py bitbank ~~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/bitbank.py Coincheck ~~~~~~~~~ Coincheck の orderbook example は、ドキュメントで説明されていない ``GET /api/order_books?version=1.0`` と ``{"type": "subscribe", "channel": "[pair]-limit-range-orderbook"}`` を利用しています。これらの payload には ``sequence_number`` が含まれるため、 REST snapshot と WebSocket 更新を整合させて扱えます。 .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/coincheck.py Bybit ~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/bybit.py Binance ~~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/binance.py OKX ~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/okx.py Phemex ~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/phemex.py Bitget ~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/bitget.py KuCoin ~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/kucoin.py BitMEX ~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/bitmex.py Hyperliquid ~~~~~~~~~~~ .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/datastore/hyperliquid.py Open orders ----------- Coincheck ~~~~~~~~~ Coincheck の WebSocket は新規発注のイベントが配信されない仕様となっています。 次のように専用のメソッド :meth:`feed_response` を使って REST API のレスポンスを DataStore に取り込みます。 .. warning:: このサンプルは実際に少額の注文を発注します。 .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/open_orders/coincheck.py Helpers ------- .. _GMOCoinHelper: GMO Coin ~~~~~~~~ :class:`.helpers.GMOCoinHelper` を利用したサンプルコードです。 :meth:`~.helpers.GMOCoinHelper.manage_ws_token` を利用することで、`Private WebSocket のアクセストークン `_ を管理します。 デフォルトでは 5 分ごとにアクセストークンを延長します。 延長が失敗した場合は、アクセストークンを新しく作成します。 このメソッドは無限ループとなっているので、 :meth:`asyncio.create_task` でタスクとしてスケジュールしてください。 以下は適当なチャンネルを購読して、アクセストークン管理ヘルパーのタスクをスケジュールするサンプルコードです。 .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/helpers/gmocoin.py .. _bitbankhelper: bitbank ~~~~~~~ :class:`.bitbankPrivateDataStore` と組み込みの PubNub クライアント :mod:`pybotters.helpers.bitbank` を利用したサンプルコードです。 このサンプルコードでは、まず REST API を利用してアクティブオーダーを初期化します。 その後組み込みの PubNub クライアントを利用して Private Stream API を非同期でサブスクライブします。 そして、DataStore の :ref:`watch` 機能を利用してアクティブオーダーの変更を監視します。 .. literalinclude:: https://pybotters.readthedocs.io/ja/stable/../examples/helpers/bitbank.py API Reference ============= Client class ------------ .. autosummary:: :toctree: generated pybotters.Client Fetch API Returns ----------------- .. autosummary:: :toctree: generated pybotters.FetchResult pybotters.NotJSONContent WebSocket API Returns --------------------- .. autosummary:: :toctree: generated pybotters.WebSocketApp pybotters.ClientWebSocketResponse Common WebSocket handlers ------------------------- .. autosummary:: :toctree: generated pybotters.WebSocketQueue .. _exchange-specific-datastore-reference: Exchange-specific Data Stores ----------------------------- .. autosummary:: :toctree: generated pybotters.BinanceCOINMDataStore pybotters.BinanceSpotDataStore pybotters.BinanceUSDSMDataStore pybotters.BitMEXDataStore pybotters.BitgetV2DataStore pybotters.BitgetDataStore pybotters.BybitDataStore pybotters.CoincheckDataStore pybotters.CoincheckPrivateDataStore pybotters.GMOCoinDataStore pybotters.HyperliquidDataStore pybotters.KuCoinDataStore pybotters.OKXDataStore pybotters.PhemexDataStore pybotters.bitFlyerDataStore pybotters.bitbankDataStore pybotters.bitbankPrivateDataStore Abstract DataStore ------------------ .. autosummary:: :toctree: generated pybotters.DataStoreCollection pybotters.DataStore Store changes ------------- .. autosummary:: :toctree: generated pybotters.StoreChange pybotters.StoreStream Helpers ------- .. autosummary:: :toctree: generated pybotters.helpers.GMOCoinHelper pybotters.helpers.hyperliquid pybotters.helpers.bitbank pybotters.Client ================ .. currentmodule:: pybotters .. autoclass:: Client :members: .. rubric:: Methods .. autosummary:: ~Client.__init__ ~Client.close ~Client.delete ~Client.fetch ~Client.get ~Client.post ~Client.put ~Client.request ~Client.ws_connect pybotters.FetchResult ===================== .. currentmodule:: pybotters .. autoclass:: FetchResult :members: .. rubric:: Methods .. autosummary:: ~FetchResult.__init__ .. rubric:: Attributes .. autosummary:: ~FetchResult.response ~FetchResult.text ~FetchResult.data pybotters.NotJSONContent ======================== .. currentmodule:: pybotters .. autoclass:: NotJSONContent :members: .. rubric:: Methods .. autosummary:: ~NotJSONContent.__init__ .. rubric:: Attributes .. autosummary:: ~NotJSONContent.error pybotters.WebSocketApp ====================== .. currentmodule:: pybotters .. autoclass:: WebSocketApp :members: .. rubric:: Methods .. autosummary:: ~WebSocketApp.__init__ ~WebSocketApp.heartbeat ~WebSocketApp.wait .. rubric:: Attributes .. autosummary:: ~WebSocketApp.current_ws ~WebSocketApp.url pybotters.ClientWebSocketResponse ================================= .. currentmodule:: pybotters .. autoclass:: ClientWebSocketResponse :members: .. rubric:: Methods .. autosummary:: ~ClientWebSocketResponse.__init__ ~ClientWebSocketResponse.close ~ClientWebSocketResponse.exception ~ClientWebSocketResponse.get_extra_info ~ClientWebSocketResponse.ping ~ClientWebSocketResponse.pong ~ClientWebSocketResponse.receive ~ClientWebSocketResponse.receive_bytes ~ClientWebSocketResponse.receive_json ~ClientWebSocketResponse.receive_str ~ClientWebSocketResponse.send_bytes ~ClientWebSocketResponse.send_frame ~ClientWebSocketResponse.send_json ~ClientWebSocketResponse.send_str .. rubric:: Attributes .. autosummary:: ~ClientWebSocketResponse.client_notakeover ~ClientWebSocketResponse.close_code ~ClientWebSocketResponse.closed ~ClientWebSocketResponse.compress ~ClientWebSocketResponse.protocol pybotters.WebSocketQueue ======================== .. currentmodule:: pybotters .. autoclass:: WebSocketQueue :members: .. rubric:: Methods .. autosummary:: ~WebSocketQueue.__init__ ~WebSocketQueue.empty ~WebSocketQueue.full ~WebSocketQueue.get ~WebSocketQueue.get_nowait ~WebSocketQueue.join ~WebSocketQueue.onmessage ~WebSocketQueue.put ~WebSocketQueue.put_nowait ~WebSocketQueue.qsize ~WebSocketQueue.shutdown ~WebSocketQueue.task_done .. rubric:: Attributes .. autosummary:: ~WebSocketQueue.maxsize pybotters.BinanceCOINMDataStore =============================== .. currentmodule:: pybotters .. autoclass:: BinanceCOINMDataStore :members: .. rubric:: Methods .. autosummary:: ~BinanceCOINMDataStore.__init__ ~BinanceCOINMDataStore.initialize ~BinanceCOINMDataStore.onmessage ~BinanceCOINMDataStore.wait .. rubric:: Attributes .. autosummary:: ~BinanceCOINMDataStore.balance ~BinanceCOINMDataStore.bookticker ~BinanceCOINMDataStore.continuouskline ~BinanceCOINMDataStore.indexprice ~BinanceCOINMDataStore.indexpricekline ~BinanceCOINMDataStore.kline ~BinanceCOINMDataStore.liquidation ~BinanceCOINMDataStore.markprice ~BinanceCOINMDataStore.markpricekline ~BinanceCOINMDataStore.order ~BinanceCOINMDataStore.orderbook ~BinanceCOINMDataStore.position ~BinanceCOINMDataStore.ticker ~BinanceCOINMDataStore.trade pybotters.BinanceSpotDataStore ============================== .. currentmodule:: pybotters .. autoclass:: BinanceSpotDataStore :members: .. rubric:: Methods .. autosummary:: ~BinanceSpotDataStore.__init__ ~BinanceSpotDataStore.initialize ~BinanceSpotDataStore.onmessage ~BinanceSpotDataStore.wait .. rubric:: Attributes .. autosummary:: ~BinanceSpotDataStore.account ~BinanceSpotDataStore.bookticker ~BinanceSpotDataStore.kline ~BinanceSpotDataStore.ocoorder ~BinanceSpotDataStore.order ~BinanceSpotDataStore.orderbook ~BinanceSpotDataStore.ticker ~BinanceSpotDataStore.trade pybotters.BinanceUSDSMDataStore =============================== .. currentmodule:: pybotters .. autoclass:: BinanceUSDSMDataStore :members: .. rubric:: Methods .. autosummary:: ~BinanceUSDSMDataStore.__init__ ~BinanceUSDSMDataStore.initialize ~BinanceUSDSMDataStore.onmessage ~BinanceUSDSMDataStore.wait .. rubric:: Attributes .. autosummary:: ~BinanceUSDSMDataStore.balance ~BinanceUSDSMDataStore.bookticker ~BinanceUSDSMDataStore.compositeindex ~BinanceUSDSMDataStore.continuouskline ~BinanceUSDSMDataStore.kline ~BinanceUSDSMDataStore.liquidation ~BinanceUSDSMDataStore.markprice ~BinanceUSDSMDataStore.order ~BinanceUSDSMDataStore.orderbook ~BinanceUSDSMDataStore.position ~BinanceUSDSMDataStore.ticker ~BinanceUSDSMDataStore.trade pybotters.BitMEXDataStore ========================= .. currentmodule:: pybotters .. autoclass:: BitMEXDataStore :members: .. rubric:: Methods .. autosummary:: ~BitMEXDataStore.__init__ ~BitMEXDataStore.onmessage ~BitMEXDataStore.wait .. rubric:: Attributes .. autosummary:: ~BitMEXDataStore.execution ~BitMEXDataStore.funding ~BitMEXDataStore.instrument ~BitMEXDataStore.insurance ~BitMEXDataStore.liquidation ~BitMEXDataStore.margin ~BitMEXDataStore.order ~BitMEXDataStore.orderbook ~BitMEXDataStore.position ~BitMEXDataStore.quote ~BitMEXDataStore.quotebin1d ~BitMEXDataStore.quotebin1h ~BitMEXDataStore.quotebin1m ~BitMEXDataStore.quotebin5m ~BitMEXDataStore.trade ~BitMEXDataStore.tradebin1d ~BitMEXDataStore.tradebin1h ~BitMEXDataStore.tradebin1m ~BitMEXDataStore.tradebin5m ~BitMEXDataStore.wallet pybotters.BitgetV2DataStore =========================== .. currentmodule:: pybotters .. autoclass:: BitgetV2DataStore :members: .. rubric:: Methods .. autosummary:: ~BitgetV2DataStore.__init__ ~BitgetV2DataStore.onmessage ~BitgetV2DataStore.wait .. rubric:: Attributes .. autosummary:: ~BitgetV2DataStore.account ~BitgetV2DataStore.book ~BitgetV2DataStore.candle ~BitgetV2DataStore.fill ~BitgetV2DataStore.orders ~BitgetV2DataStore.orders_algo ~BitgetV2DataStore.positions ~BitgetV2DataStore.positions_history ~BitgetV2DataStore.ticker ~BitgetV2DataStore.trade pybotters.BitgetDataStore ========================= .. currentmodule:: pybotters .. autoclass:: BitgetDataStore :members: .. rubric:: Methods .. autosummary:: ~BitgetDataStore.__init__ ~BitgetDataStore.initialize ~BitgetDataStore.onmessage ~BitgetDataStore.wait .. rubric:: Attributes .. autosummary:: ~BitgetDataStore.account ~BitgetDataStore.candlesticks ~BitgetDataStore.orderbook ~BitgetDataStore.orders ~BitgetDataStore.positions ~BitgetDataStore.ticker ~BitgetDataStore.trade pybotters.BybitDataStore ======================== .. currentmodule:: pybotters .. autoclass:: BybitDataStore :members: .. rubric:: Methods .. autosummary:: ~BybitDataStore.__init__ ~BybitDataStore.initialize ~BybitDataStore.onmessage ~BybitDataStore.wait .. rubric:: Attributes .. autosummary:: ~BybitDataStore.all_liquidation ~BybitDataStore.execution ~BybitDataStore.greek ~BybitDataStore.kline ~BybitDataStore.liquidation ~BybitDataStore.lt_kline ~BybitDataStore.lt_nav ~BybitDataStore.lt_ticker ~BybitDataStore.order ~BybitDataStore.orderbook ~BybitDataStore.position ~BybitDataStore.ticker ~BybitDataStore.trade ~BybitDataStore.wallet pybotters.CoincheckDataStore ============================ .. currentmodule:: pybotters .. autoclass:: CoincheckDataStore :members: .. rubric:: Methods .. autosummary:: ~CoincheckDataStore.__init__ ~CoincheckDataStore.initialize ~CoincheckDataStore.onmessage ~CoincheckDataStore.wait .. rubric:: Attributes .. autosummary:: ~CoincheckDataStore.orderbook ~CoincheckDataStore.trades pybotters.CoincheckPrivateDataStore =================================== .. currentmodule:: pybotters .. autoclass:: CoincheckPrivateDataStore :members: .. rubric:: Methods .. autosummary:: ~CoincheckPrivateDataStore.__init__ ~CoincheckPrivateDataStore.initialize ~CoincheckPrivateDataStore.onmessage ~CoincheckPrivateDataStore.wait .. rubric:: Attributes .. autosummary:: ~CoincheckPrivateDataStore.execution ~CoincheckPrivateDataStore.order pybotters.GMOCoinDataStore ========================== .. currentmodule:: pybotters .. autoclass:: GMOCoinDataStore :members: .. rubric:: Methods .. autosummary:: ~GMOCoinDataStore.__init__ ~GMOCoinDataStore.initialize ~GMOCoinDataStore.onmessage ~GMOCoinDataStore.wait .. rubric:: Attributes .. autosummary:: ~GMOCoinDataStore.executions ~GMOCoinDataStore.orderbooks ~GMOCoinDataStore.orders ~GMOCoinDataStore.position_summary ~GMOCoinDataStore.positions ~GMOCoinDataStore.ticker ~GMOCoinDataStore.trades pybotters.HyperliquidDataStore ============================== .. currentmodule:: pybotters .. autoclass:: HyperliquidDataStore :members: .. rubric:: Methods .. autosummary:: ~HyperliquidDataStore.__init__ ~HyperliquidDataStore.onmessage ~HyperliquidDataStore.wait .. rubric:: Attributes .. autosummary:: ~HyperliquidDataStore.active_asset_ctx ~HyperliquidDataStore.active_asset_data ~HyperliquidDataStore.all_mids ~HyperliquidDataStore.bbo ~HyperliquidDataStore.candle ~HyperliquidDataStore.l2_book ~HyperliquidDataStore.notification ~HyperliquidDataStore.order_updates ~HyperliquidDataStore.trades ~HyperliquidDataStore.user_events ~HyperliquidDataStore.user_fills ~HyperliquidDataStore.user_fundings ~HyperliquidDataStore.user_non_funding_ledger_updates ~HyperliquidDataStore.user_twap_history ~HyperliquidDataStore.user_twap_slice_fills ~HyperliquidDataStore.web_data2 pybotters.KuCoinDataStore ========================= .. currentmodule:: pybotters .. autoclass:: KuCoinDataStore :members: .. rubric:: Methods .. autosummary:: ~KuCoinDataStore.__init__ ~KuCoinDataStore.initialize ~KuCoinDataStore.onmessage ~KuCoinDataStore.wait .. rubric:: Attributes .. autosummary:: ~KuCoinDataStore.announcements ~KuCoinDataStore.balance ~KuCoinDataStore.balanceevents ~KuCoinDataStore.endpoint ~KuCoinDataStore.execution ~KuCoinDataStore.indexprice ~KuCoinDataStore.instrument ~KuCoinDataStore.kline ~KuCoinDataStore.marginfundingbook ~KuCoinDataStore.marginorderevents ~KuCoinDataStore.marginorders ~KuCoinDataStore.marginpositionevents ~KuCoinDataStore.marginpositions ~KuCoinDataStore.markprice ~KuCoinDataStore.orderbook5 ~KuCoinDataStore.orderbook50 ~KuCoinDataStore.orderevents ~KuCoinDataStore.orders ~KuCoinDataStore.positions ~KuCoinDataStore.symbolsnapshot ~KuCoinDataStore.ticker ~KuCoinDataStore.transactionstats pybotters.OKXDataStore ====================== .. currentmodule:: pybotters .. autoclass:: OKXDataStore :members: .. rubric:: Methods .. autosummary:: ~OKXDataStore.__init__ ~OKXDataStore.initialize ~OKXDataStore.onmessage ~OKXDataStore.wait .. rubric:: Attributes .. autosummary:: ~OKXDataStore.account ~OKXDataStore.accountgreeks ~OKXDataStore.algoadvance ~OKXDataStore.balance_and_position ~OKXDataStore.books ~OKXDataStore.candle ~OKXDataStore.estimatedprice ~OKXDataStore.fundingrate ~OKXDataStore.indexcandle ~OKXDataStore.indextickers ~OKXDataStore.instruments ~OKXDataStore.liquidationwarning ~OKXDataStore.markprice ~OKXDataStore.markpricecandle ~OKXDataStore.openinterest ~OKXDataStore.optsummary ~OKXDataStore.orders ~OKXDataStore.ordersalgo ~OKXDataStore.positions ~OKXDataStore.pricelimit ~OKXDataStore.tickers ~OKXDataStore.trades pybotters.PhemexDataStore ========================= .. currentmodule:: pybotters .. autoclass:: PhemexDataStore :members: .. rubric:: Methods .. autosummary:: ~PhemexDataStore.__init__ ~PhemexDataStore.initialize ~PhemexDataStore.onmessage ~PhemexDataStore.wait .. rubric:: Attributes .. autosummary:: ~PhemexDataStore.accounts ~PhemexDataStore.kline ~PhemexDataStore.market24h ~PhemexDataStore.orderbook ~PhemexDataStore.orders ~PhemexDataStore.positions ~PhemexDataStore.ticker ~PhemexDataStore.trade pybotters.bitFlyerDataStore =========================== .. currentmodule:: pybotters .. autoclass:: bitFlyerDataStore :members: .. rubric:: Methods .. autosummary:: ~bitFlyerDataStore.__init__ ~bitFlyerDataStore.initialize ~bitFlyerDataStore.onmessage ~bitFlyerDataStore.wait .. rubric:: Attributes .. autosummary:: ~bitFlyerDataStore.balance ~bitFlyerDataStore.board ~bitFlyerDataStore.childorderevents ~bitFlyerDataStore.childorders ~bitFlyerDataStore.collateral ~bitFlyerDataStore.executions ~bitFlyerDataStore.parentorderevents ~bitFlyerDataStore.parentorders ~bitFlyerDataStore.positions ~bitFlyerDataStore.ticker pybotters.bitbankDataStore ========================== .. currentmodule:: pybotters .. autoclass:: bitbankDataStore :members: .. rubric:: Methods .. autosummary:: ~bitbankDataStore.__init__ ~bitbankDataStore.onmessage ~bitbankDataStore.wait .. rubric:: Attributes .. autosummary:: ~bitbankDataStore.depth ~bitbankDataStore.ticker ~bitbankDataStore.transactions pybotters.bitbankPrivateDataStore ================================= .. currentmodule:: pybotters .. autoclass:: bitbankPrivateDataStore :members: .. rubric:: Methods .. autosummary:: ~bitbankPrivateDataStore.__init__ ~bitbankPrivateDataStore.initialize ~bitbankPrivateDataStore.onmessage ~bitbankPrivateDataStore.wait .. rubric:: Attributes .. autosummary:: ~bitbankPrivateDataStore.asset ~bitbankPrivateDataStore.dealer_order ~bitbankPrivateDataStore.deposit ~bitbankPrivateDataStore.margin_notice ~bitbankPrivateDataStore.margin_payable ~bitbankPrivateDataStore.margin_position ~bitbankPrivateDataStore.spot_order ~bitbankPrivateDataStore.spot_trade ~bitbankPrivateDataStore.withdrawal pybotters.DataStoreCollection ============================= .. currentmodule:: pybotters .. autoclass:: DataStoreCollection :members: .. rubric:: Methods .. autosummary:: ~DataStoreCollection.__init__ ~DataStoreCollection.onmessage ~DataStoreCollection.wait pybotters.DataStore =================== .. currentmodule:: pybotters .. autoclass:: DataStore :members: .. rubric:: Methods .. autosummary:: ~DataStore.__init__ ~DataStore.find ~DataStore.get ~DataStore.wait ~DataStore.watch pybotters.StoreChange ===================== .. currentmodule:: pybotters .. autoclass:: StoreChange :members: .. rubric:: Methods .. autosummary:: ~StoreChange.__init__ .. rubric:: Attributes .. autosummary:: ~StoreChange.store ~StoreChange.operation ~StoreChange.source ~StoreChange.data pybotters.StoreStream ===================== .. currentmodule:: pybotters .. autoclass:: StoreStream :members: .. rubric:: Methods .. autosummary:: ~StoreStream.__init__ ~StoreStream.close ~StoreStream.get pybotters.helpers.GMOCoinHelper =============================== .. currentmodule:: pybotters.helpers .. autoclass:: GMOCoinHelper :members: .. rubric:: Methods .. autosummary:: ~GMOCoinHelper.__init__ ~GMOCoinHelper.create_access_token ~GMOCoinHelper.extend_access_token ~GMOCoinHelper.manage_ws_token pybotters.helpers.hyperliquid ============================= .. automodule:: pybotters.helpers.hyperliquid .. rubric:: Functions .. autosummary:: construct_l1_action construct_user_signed_action generate_message_types get_timestamp_ms sign_typed_data .. rubric:: Classes .. autosummary:: :toctree: EIP712Domain MessageType PhantomAgentMessage Signature pybotters.helpers.hyperliquid.EIP712Domain ========================================== .. currentmodule:: pybotters.helpers.hyperliquid .. autoclass:: EIP712Domain :members: .. rubric:: Methods .. autosummary:: ~EIP712Domain.__init__ ~EIP712Domain.clear ~EIP712Domain.copy ~EIP712Domain.fromkeys ~EIP712Domain.get ~EIP712Domain.items ~EIP712Domain.keys ~EIP712Domain.pop ~EIP712Domain.popitem ~EIP712Domain.setdefault ~EIP712Domain.update ~EIP712Domain.values .. rubric:: Attributes .. autosummary:: ~EIP712Domain.name ~EIP712Domain.version ~EIP712Domain.chainId ~EIP712Domain.verifyingContract ~EIP712Domain.salt pybotters.helpers.hyperliquid.MessageType ========================================= .. currentmodule:: pybotters.helpers.hyperliquid .. autoclass:: MessageType :members: .. rubric:: Methods .. autosummary:: ~MessageType.__init__ ~MessageType.clear ~MessageType.copy ~MessageType.fromkeys ~MessageType.get ~MessageType.items ~MessageType.keys ~MessageType.pop ~MessageType.popitem ~MessageType.setdefault ~MessageType.update ~MessageType.values .. rubric:: Attributes .. autosummary:: ~MessageType.name ~MessageType.type pybotters.helpers.hyperliquid.PhantomAgentMessage ================================================= .. currentmodule:: pybotters.helpers.hyperliquid .. autoclass:: PhantomAgentMessage :members: .. rubric:: Methods .. autosummary:: ~PhantomAgentMessage.__init__ ~PhantomAgentMessage.clear ~PhantomAgentMessage.copy ~PhantomAgentMessage.fromkeys ~PhantomAgentMessage.get ~PhantomAgentMessage.items ~PhantomAgentMessage.keys ~PhantomAgentMessage.pop ~PhantomAgentMessage.popitem ~PhantomAgentMessage.setdefault ~PhantomAgentMessage.update ~PhantomAgentMessage.values .. rubric:: Attributes .. autosummary:: ~PhantomAgentMessage.source ~PhantomAgentMessage.connectionId pybotters.helpers.hyperliquid.Signature ======================================= .. currentmodule:: pybotters.helpers.hyperliquid .. autoclass:: Signature :members: .. rubric:: Methods .. autosummary:: ~Signature.__init__ ~Signature.clear ~Signature.copy ~Signature.fromkeys ~Signature.get ~Signature.items ~Signature.keys ~Signature.pop ~Signature.popitem ~Signature.setdefault ~Signature.update ~Signature.values .. rubric:: Attributes .. autosummary:: ~Signature.r ~Signature.s ~Signature.v pybotters.helpers.bitbank ========================= .. automodule:: pybotters.helpers.bitbank .. rubric:: Functions .. autosummary:: fetch_channel_and_token fetch_subscribe subscribe subscribe_with_callback .. rubric:: Classes .. autosummary:: :toctree: BaseResponse ChannelAndToken SubscribeMessage SubscribeResponse TimeToken pybotters.helpers.bitbank.BaseResponse ====================================== .. currentmodule:: pybotters.helpers.bitbank .. autoclass:: BaseResponse :members: .. rubric:: Methods .. autosummary:: ~BaseResponse.__init__ ~BaseResponse.clear ~BaseResponse.copy ~BaseResponse.fromkeys ~BaseResponse.get ~BaseResponse.items ~BaseResponse.keys ~BaseResponse.pop ~BaseResponse.popitem ~BaseResponse.setdefault ~BaseResponse.update ~BaseResponse.values .. rubric:: Attributes .. autosummary:: ~BaseResponse.success ~BaseResponse.data pybotters.helpers.bitbank.ChannelAndToken ========================================= .. currentmodule:: pybotters.helpers.bitbank .. autoclass:: ChannelAndToken :members: .. rubric:: Methods .. autosummary:: ~ChannelAndToken.__init__ ~ChannelAndToken.clear ~ChannelAndToken.copy ~ChannelAndToken.fromkeys ~ChannelAndToken.get ~ChannelAndToken.items ~ChannelAndToken.keys ~ChannelAndToken.pop ~ChannelAndToken.popitem ~ChannelAndToken.setdefault ~ChannelAndToken.update ~ChannelAndToken.values .. rubric:: Attributes .. autosummary:: ~ChannelAndToken.pubnub_channel ~ChannelAndToken.pubnub_token pybotters.helpers.bitbank.SubscribeMessage ========================================== .. currentmodule:: pybotters.helpers.bitbank .. autoclass:: SubscribeMessage :members: .. rubric:: Methods .. autosummary:: ~SubscribeMessage.__init__ ~SubscribeMessage.clear ~SubscribeMessage.copy ~SubscribeMessage.fromkeys ~SubscribeMessage.get ~SubscribeMessage.items ~SubscribeMessage.keys ~SubscribeMessage.pop ~SubscribeMessage.popitem ~SubscribeMessage.setdefault ~SubscribeMessage.update ~SubscribeMessage.values .. rubric:: Attributes .. autosummary:: ~SubscribeMessage.a ~SubscribeMessage.f ~SubscribeMessage.p ~SubscribeMessage.k ~SubscribeMessage.c ~SubscribeMessage.d pybotters.helpers.bitbank.SubscribeResponse =========================================== .. currentmodule:: pybotters.helpers.bitbank .. autoclass:: SubscribeResponse :members: .. rubric:: Methods .. autosummary:: ~SubscribeResponse.__init__ ~SubscribeResponse.clear ~SubscribeResponse.copy ~SubscribeResponse.fromkeys ~SubscribeResponse.get ~SubscribeResponse.items ~SubscribeResponse.keys ~SubscribeResponse.pop ~SubscribeResponse.popitem ~SubscribeResponse.setdefault ~SubscribeResponse.update ~SubscribeResponse.values .. rubric:: Attributes .. autosummary:: ~SubscribeResponse.t ~SubscribeResponse.m pybotters.helpers.bitbank.TimeToken =================================== .. currentmodule:: pybotters.helpers.bitbank .. autoclass:: TimeToken :members: .. rubric:: Methods .. autosummary:: ~TimeToken.__init__ ~TimeToken.clear ~TimeToken.copy ~TimeToken.fromkeys ~TimeToken.get ~TimeToken.items ~TimeToken.keys ~TimeToken.pop ~TimeToken.popitem ~TimeToken.setdefault ~TimeToken.update ~TimeToken.values .. rubric:: Attributes .. autosummary:: ~TimeToken.t ~TimeToken.r Contributing ============ pybotters はオープンソースソフトウェアですので、どなたでも開発に参加できます。 **あなたの Pull request やアイディアを積極的に受け入れています!** 当プロジェクトに参加する方法や選定しているツールなどの環境構築方法、リポジトリの運用方針を記載します。 TL;DR ----- **ワークフロー** 0. (Optional) メンテナと議論が必要な場合 `Discussion `_ または `Issue `_ を作成する 1. マシンをセットアップする (Python 3.10, uv ...) 2. リポジトリを Fork する 3. main ブランチを元に、変更する内容を表す名称のブランチ (トピックブランチ) を作成する 4. あなたのアイディアをソースコードに反映する 💎💎 5. pytest, ruff を実行して正しいコードかチェックする 6. コードをプッシュして CI をパスする 7. Pull request を作成して、変更内容を記載する 8. レビューを受け、必要なら修正を行う 9. あなたのソースコードがマージされます 🚀🚀 Discussion and Issue -------------------- **Discussion 👉 Issue 👉 Pull request 👉 Merge!!** バグの疑いがある動作を発見したり、追加の機能リクエスト、質問などがある場合は `GitHub Discussion `_ から始めましょう。 その内容についてメンテナと議論して明確な回答を得ることができます。 Discussion によってバグの修正や追加機能などの課題が明確化した場合は、その内容を `GitHub Issue `_ にエスカレーションします。 Issue はやるべきことのトラッカーとして利用します。 メンテナまたはコントリビューターはこの Issue リストを元に Pull request をします。 しかし、このプロセスに完全に則る必要はありません。 内容が明確に感じている場合は Issue から作成しても問題ないし、初めから Pull request を作成しても構いません。 例えば明らかな小規模バグやドキュメントの誤字修正は Pull request から始めるのが早いです。 ただし、内容が間違っていたりプロジェクトの方針と異なる場合は我々はご提案を受け付けることができない場合があります。 内容に不安を感じるのでれば、あなたの時間を無駄にしない為にも Discussion から始めるのがベストだと思います 💬 もちろん Discord サーバーでカジュアルにチャットするのでも構いません! Computing --------- プロジェクトに貢献するためには、Python を実行する為のコンピューティング環境を用意します。 **ローカル** または **Codespaces** を利用する方法があります。 Local ~~~~~ いつも通りのあなたが使用しているコンピューターの環境です。 Codespaces ~~~~~~~~~~ より簡単にセットアップするには **GitHub Codespaces** がおすすめです。 https://docs.github.com/ja/codespaces GitHub Codespaces は GitHub が提供するクラウドベースの開発環境です。 誰でも無料枠があるので OSS にコントリビュートするのに最適です。 ``Code`` ボタンからクリックするだけで Web 上から簡単に Codespaces 環境を立ち上げることができます。 使い終わった環境は削除することができるので、ローカル環境を汚すことなくプロジェクトに貢献できます。 Python ------ pybotters は最小要求を **Python 3.10** としています。 Python 3.10 の最新マイナーバージョンをインストールしてコーディングすることを推奨します。 Python のインストール方法は多岐にわたります。 `こちらの記事 (python.jp) `__ を参考にするか、または次に紹介する **uv** を利用してください。 uv -- **uv** は Astral によって開発されている非常に高速な Python プロジェクトマネージャーです。 uv は機能の一つとして環境作成時に **目的の Python バージョンを自動でダウンロード** するので基本的なセットアップを省略できます。 当プロジェクトでは作業ごとの uv コマンドを Bash スクリプトとして定義済みなので (``scripts`` 配下) これを利用することで簡単にテストなどを実行できます。 https://docs.astral.sh/uv/ uv は上記公式ドキュメントからインストールしてください。 Git --- まずは `pybotters の GitHub リポジトリ `_ を Fork してください。 ローカル環境を利用している場合は Git でコードをチェックアウトします。 Git がローカルにない場合はインストールしてください。 .. code:: sh git clone https://github.com//pybotters .. NOTE:: Codespaces 環境なら既に Git がインストール済みでコードがチェックアウトされているはずです ✨ Dependencies ------------ 仮想環境の作成と、プロジェクト及び依存関係をインストールします。 .. code:: sh ./scripts/sync CI -- コードをリモートブランチにプッシュすると **GitHub Actions** によって定義されている CI が実行されます。 CI は **Static analysis** と **Type check** 及び **Test** についてのチェックが実施されます。 これらのチェックがエラーになる場合は、コードを修正してから再度プッシュしてください。 またはローカルでチェックを実施する場合は、以下の手順を参考にしてください。 Static analysis --------------- 当プロジェクトではコード静的解析として **Ruff** を採用しています。 上記プロジェクトセットアップ時に依存関係としてインストールされます。 https://docs.astral.sh/ruff/ Format ~~~~~~ フォーマット機能を利用してコードを自動修正できます。 .. code:: bash ./scripts/format Lint ~~~~ 静的解析機能を利用してコードの品質をチェックできます。 .. code:: bash ./scripts/lint Type check ---------- 当プロジェクトではタイプチェッカーとして **mypy** を採用しています。 上記プロジェクトセットアップ時に依存関係としてインストールされます。 https://mypy.readthedocs.io/ 型チェックのコマンドは以下の通りです。 .. code:: bash ./scripts/typing Testing ------- 当プロジェクトではテストに **pytest** を採用しています。 上記プロジェクトセットアップ時に依存関係としてインストールされます。 https://docs.pytest.org 実装したコードに対するテストコードを作成してください。 テストコードは ``tests/`` 配下にあります。 .. code:: sh ./scripts/test 全ての Python バージョンに対してテストカバレッジを実行するには、以下のコマンドを実行してください。 .. code:: sh ./scripts/test-all テストを実行すると標準出力と HTML のカバレッジレポートが生成されます。 HTML のレポートを確認するには、以下のコマンドを実行してください。 .. code:: sh python -m http.server -d htmlcov **テストの基準** * すべてのコードに対して **全て** テストを書いてください。 カバレッジ率は 100% です。 * 例外として :ref:`DataStore ` に関する単体テストコードは、テスト方法を確立するまで省略しています。 * ただし DataStore の動作確認ができる実環境用の機能テストコードを Pull request のコメントに張り付けてください。 * 外部との通信部分はモック化してください。 Documentation ------------- Sphinx ドキュメントを自動ビルドしてローカル環境で閲覧することができます。 .. code:: sh ./scripts/serve ローカル環境にホストせずにドキュメントをビルドすることもできます。 .. code:: sh ./scripts/docs Branch Strategy --------------- GitHub Flow (`日本語訳 `_) に従います。 main ブランチが最新の開発ブランチです。 Fork 及び Clone したリポジトリの main からトピックブランチ (例: ``fix-some-auth``)を作成します。 .. code:: sh git switch -c fix-some-auth main 変更したコードをリモートにプッシュしたら upstream/main を対象に Pull request を送信してください。 Pull request ------------ Branch Strategy に記したように、main ブランチを対象に Pull request を送信してください。 Pull request タイトルは、英語でかつコミットメッセージとなる文で記述することを推奨します。 (例: *Fix xxx in SomeExchangeDataStore* *Support SomeExchange HTTP auth* など) 内容については日本語でも構いません。 Pull request はメンテナによって *Squash-and-Merge* 戦略でマージされます。 *Squash-and-Merge* 戦略とは Pull request の変更が複数のコミットあったとしてもマージ時に 1 つに押し潰されます。 * あなたが Git に不慣れで作業経過のコミットが沢山あったとしても、それらは 1 つに押し潰されます * あなたが Git を心得ていて沢山の素敵なコミットメッセージを残したとしても、それらは 1 つに押し潰されます 設計思想や細かい変数名のデザインなどは、レビューし修正コードを提案します。 お気軽にプルリクください! OSS 開発にご興味がある方、是非プロジェクトにご参加ください✨🍰✨