-
Notifications
You must be signed in to change notification settings - Fork 28
Home
少し行儀の悪い方法ですが、おそらく一番トラブルの少ない方法です。
- パスの通っている場所を調べる。
$ python3 -c 'import sys; print(sys.path)'
['', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload',
'/home/sk/.local/lib/python3.8/site-packages', '/usr/lib/python3.8/site-packages']
- どこでも良いので(できれば管理者権限の必要ないディレクトリ)にbotfwのシンボリックリンクを作成。
$ cd /home/sk/.local/lib/python3.8/site-packages # pip install --user で使われるディレクトリ
$ ln -s /path/to/btc_bot_framework/botfw botfw
使用しているシェルの設定ファイル(.bashrc, .zshrc等)にPYTHONPATHを追加します。
正しい作法ですが、IDEによってはlinterのエラーがでたり、自動補完が効かなかったりします
export PYTHONPATH="/path/to/btc_bot_framework:$PYTHONPATH"
プロジェクト内に直接コードを書いて、プロジェクトルートからモジュールとして実行する場合は
特にインストール作業は必要ありません。
例えば、samples/bitflyer/orderbook.pyなら以下のように実行できます。(__init__.pyが必要かも)
$ python3 -m samples.bitflyer.orderbook
定数値(の内部値)はccxtとの一貫性や親和性のため取引所固有の値ではなく、ccxtに準拠しています。
- 'FX_BTC_JPY'(bitflyer) -> 'FX_BTC_JPY'(ccxt)
- 'BTC_JPY'(bitflyer) -> 'BTC/JPY'(ccxt)
- 'BUY'(bitflyer), 'Buy'(bitmex), 'BUY'(binance) -> 'buy'(ccxt)
- 'LIMIT'(bitflyer), 'Limit'(bitmex), 'LIMIT'(binance) -> 'limit'(ccxt)
注文・ポジション管理周りの設計は現在のポジションの反対側注文を行った際に自動的に反対ポジションが決済されることを前提としています。
liquidのように注文毎に決済を行うかを指定できる場合、内部で自動的に必要なオプションを追加た上で発注が行われます。
取引所によっては予め反対注文の挙動を公式サイトから変更する必要がある場合があるのでご注意ください。
また、上記の理由によりgmocoinなど反対ポジションの自動決済をサポートしていない取引所はポジション管理・注文管理に対応していません。
具体的な実装例はsamples内のファイルを参照してください。
- samples/simple_bot.py 簡単なbot。あくまで使い方を確認する用。
- samples/bitlyfer/trade.py 約定データを取得して表示します。
- samples/bitflyer/orderbook.py 板情報を取得して表示します。
trade.pyとorderbook.pyで利用されているtest_trade()とtest_orderbook()は それぞれ'botfw/base/trade.py'と'botfw/base/orderbook.py'内にあります。
ログ情報はすべてlogging(標準ライブラリ)から出力されるので、はじめにロガーの初期化を行ってください。
ログフォーマットに拘りがなければ、setup_logger()で必要最低限の設定を行えます。
import logging
import botfw
botfw.setup_logger(logging.INFO)
log = logging.getLogger('MyLogger')
log.info('Hello!')
対象の取引所のクラスを生成して、create_trade()に取得したい通貨ペアのシンボルを渡すことでTradeクラスが生成されます。
Tradeは約定情報を受け取るとadd_callback()で設定されたコールバック関数を呼び出します。
引数は前から順にts(タイムスタンプ), price(価格), size(約定サイズ)です。
# Bitflyerの'FX_BTC_JPY'を取得する例
bitflyer = botfw.Bitflyer()
trade = bitflyer.create_trade('FX_BTC_JPY')
trade.add_callback(lambda ts, price, size: print(ts, price, size))
input() # 入力待機してプログラムが終了するのを防ぐ
- 注意点1
概要にもありますが、通貨ペアのシンボルはccxtに準拠します。例えば、Bitmexの'XBTUSD'は'BTC/USD'になります。
ccxtが対応していない取引所、または通貨ぺアについては取引所の表記に従います。 - 注意点2
コールバック関数は別スレッド(受信用のスレッド)から呼び出されるため、関数内部で長時間ブロッキングするような処理は避けてください。 - 注意点3
約定サイズは取引所の仕様に関わらず、Base通貨のサイズになります。
例えば、Bitmexの'BTC/USD'のサイズはQuote通貨(USD)で配信されていますが、Base通貨(BTC)に変換されます。
create_trade()はTradeクラスの無駄な複製を防ぐためのものです。
以下のように取引所クラスを生成せずに直接Tradeクラスを生成しても問題ありません。
trade = botfw.Bitflyer.Trade('FX_BTC_JPY')
コールバック関数は複数の追加・削除が可能です。参考
基本的には約定情報と同じですが、取得したデータを内部に保持するという点で異なります。
買い板、売り板の参照はそれぞれbids(), asks()で取得でき、現在価格に近い順に(best bid, best askが先頭)[price, size]のリスト(ビューオブジェクト)で保存されています。
コールバック関数は板情報が更新されたことを知らせるためのものであり、特に設定は必要ありません。
約定情報の注意点1,2,3が同様に当てはまるのでご注意ください。
# Bitflyerの'FX_BTC_JPY'を取得する例
bitflyer = botfw.Bitflyer()
orderbook = bitflyer.create_orderbook('FX_BTC_JPY')
# orderbook.add_callback(lambda: print('updated'))
while True:
for price, size in orderbook.bids()[:5]:
print('bid', price, size)
for price, size in orderbook.asks()[:5]:
print('ask', price, size)
time.sleep(1)
取引所クラスを生成して、init_account()メソッドにccxt同様のフォーマットでapi_keyとapi_secretを渡します。 この関数は内部でccxtの初期化、注文イベント用のwebsocketの認証などを行い以下の4つの変数を生成します。
-
api
ccxt apiにapiカウントの機能を付け加えたクラスです。 -
websocket
認証済みのwebsocket。注文イベントの受信用。 -
order_manager
注文イベントの処理を行うクラス。基本的に直接利用することはありません。 -
order_group_manager
注文グループを生成・管理するためのクラス。
次に、order_group_manager.create_order_group()に通貨シンボルとグループ名(任意の名称)を渡してorder_groupを生成します。
order_groupは注文をグルーピングするためのクラスで同一のシンボルに対しても複数作成することができます。
これは日本の取引所のように複数アカウントを持つことが難しい取引所において複数のロジックを実行する際に有用です。
注文、キャンセル、ポジション・(未実現)損益の確認はorder_groupを通して行います。
また、注文オブジェクト・ポジション情報の更新は注文イベント受信用のスレッドから非同期に行われます。
bitflyer = botfw.Bitflyer()
bitflyer.init_account({
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_API_SECRET',
})
api = bitflyer.api
ogm = bitflyer.order_group_manager
# trade = bitflyer.create_trade('FX_BTC_JPY')
# orderbook = bitflyer.create_orderbook('FX_BTC_JPY')
og = ogm.create_order_group('FX_BTC_JPY', 'group_name_1')
og.set_order_log(logging.getLogger('group_name_1')) # 自動で注文・キャンセルのログを表示
指値または成行の注文を行います。特殊注文には対応していません。
- type: str
注文タイプ。 'limit'または'market' - side: str
売買の向き。 'buy'または'sell' - amount: float
注文サイズ。単位はccxt(取引所)の仕様に従います。 - price: float = None
注文価格。(type='limit'の場合のみ) - params: dict = {}
追加オプション。ccxtのcreate_orderのparamにそのまま渡される引数です。
主にPostOnly注文を行いたい場合などに使用します。 - sync: bool = False
Trueの場合、レスポンスがあるまで関数内でブロッキングします。 Falseの場合、レスポンスを待たずにWAIT_OPEN状態の注文オブジェクトを返却します。
order = og.create_order('limit', 'buy', 0.01, 700000)
print(order)
返り値は注文オブジェクト(Order)で、注文を管理する上で重要なフィールドは以下の3つです。
- state
Order Stateのいずれかの値で、注文の状態を表します。 - state_ts
stateが最後に変更されたタイムスタンプ。 - id
正常に注文が完了(OPEN)した場合は注文IDがセットされます。
stateは発注直後はWAIT_OPENになりますが、受付が正常に完了した場合OPENとなり、約定後はCLOSEDになります。 注文受付に失敗した場合、または途中(部分約定を含む)でキャンセルされた場合は、CANCELEDとなります。
非同期注文(デフォルト)では注文エラーが発生した際に注文スレッドから例外を補足することはできません。
注文エラーが発生したか知りたい場合はstateがCANCELEDでidがNoneになっているかどうかを確認してください。
エラー内容自体はログに表示されるため、エラー原因を後から特定する上ではこれで十分かと思われます。
どうしてもエラーの内容(エラーコード)を注文スレッドから知りたい場合は、以下の様に同期注文(sync=True)にした上で例外を補足してください。
try:
order = og.create_order('limit', 'buy', 0.01, 700000, sync=True)
except Exception as e:
print(e)
liquidやbybitなどの一部の取引所では注文を行った後から、数量や指値価格を変更することができます。
注文の向き(買い/売り)の変更はできません。
- order: Order
変更したい注文のオブジェクト(create_orderが返すオブジェクト) - amount: float = None
注文サイズ。変更しない場合省略可 - price: float = None
注文価格。変更しない場合省略可 - params: dict = {}
追加オプション。ccxtのcreate_orderのparamにそのまま渡される引数です。 - sync: bool = False
Trueの場合、レスポンスがあるまで関数内でブロッキングします。 Falseの場合、レスポンスを待たずに関数を抜けます。この時、リクエストが返ってくるまで間 editingフラグがTrueになるので重複して注文変更のリクエストを送らないように注意してください。
if not order.editing:
og.edit_order(order, price=10000)
取引所が注文の変更に対応しているかどうかは以下のように確認できます。
import ccxt
ccxt.liquid().has['editOrder'] # Liquidの場合
未約定(部分約定含む)の注文のキャンセルをリクエストします。
- order: Order
キャンセルしたい注文のオブジェクト(create_orderが返すオブジェクト) - sync: bool = False
Trueの場合、レスポンスがあるまで関数内でブロッキングします。 レスポンスの確認は必ずしもキャンセルの成功を保証するものではなく、取引所の仕様によります。
stateはキャンセルが完了するまでの間WAIT_CANCELになり、キャンセル完了後はCANCELEDになりますが、 「キャンセルに失敗して再度OPENになる場合」や「キャンセル完了前に全約定によりCLOSEDになる場合」があることに注意してください。
og.cancel_order(order)
ポジションと損益の情報はorder_groupないのposition_groupを参照してください。
pg = og.position_group
print(pg.position, pg.average_price)
print(pg.pnl, pg.unrealized_pnl, pg.fee)
ポジション情報はdecimalクラスを使って計算された浮動小数点の有効桁数内において正確な値(float)ですが、
一方で損益(pnl)は平均約定価格を追跡して計算される値であり、サーバー側のポジション単位の計算とは異なるため正確な値ではありません。
また計算上の都合からposition=0である場合はaverage_price=1となることに注意してください。
ポジションサイズの通貨は取引所の仕様に従いますが、一方で(未実現)損益は取引所の仕様とは無関係に常にQuote通貨で計算されます。
例えば、
- bitflyerの'FX_BTC_JPY'ではポジションはBase通貨(BTC)、損益はQuote通貨(JPY)
- bitmexの'BTC/USD'ではポジションはQuote通貨(USD)、損益はQuote通貨(USD)
となります。
注文イベントによるポジションの計算はwebsocketの切断やサーバー側のエラーなどにより、ごく稀に実際のポジションと誤差を生じる場合があります。
そのため一定時間おきにREST API経由でポジションを取得して誤差が生じていないかを確認して、自動的に誤差を修正する機能を提供しています(デフォルトでは無効)。
修正はサーバー側のポジションがローカルで管理してるポジションと一致するように成行注文を出して行います。
この機能は証拠金取引のみで利用可能です。
現物取引の場合は手動で修正を行ってください。
設定を有効化するにはposition_group_manager.set_position_sync_conf()を利用してください。
ogm.set_position_sync_config('FX_BTC_JPY', 0.01, 1.00)
引数は前から順に以下の様になります。
- symbol
監視対象となる通貨のシンボル。 - min_size
常に取引所の最小注文サイズにしてください。 - max_size
注文の最大サイズ。誤差が大きい場合のこのサイズ以下に複数の成行注文に分割されます。
誤差を検知した場合警告ログを表示しますがすぐに修正は行わず、再度確認を行った際に誤差が一致した場合にのみ修正を行います。
これはポジション確認のタイミング次第で実際には誤差が生じていないにもかかわらず誤差が生じていると誤認して、
不要な注文を出してしまうことを防ぐための仕様です。
TODO
TODO
TODO
TODO
TODO
TODO
TODO
TODO
TODO
注文形式。create_order()のtypeに指定する値です。
- LIMIT (='limit')
指値注文。priceで価格を指定してください。 - MARKET (='market')
成行注文。priceは不要です。
注文の売り又は買い。create_order()のsideに指定する値です。
- BUY (='buy')
買い注文。 - SELL (='sell')
売り注文。
注文クラスのstate変数にセットされる値。
- OPEN (='open')
注文が有効(部分約定含む)である状態。 - CLOSED (='closed')
注文が全て約定した状態。 - CANCELED (='canceled')
注文が失効もしくはキャンセル(部分約定含む)された状態。 - WAIT_OPEN (='wait_open')
注文が送信されて、受付待ちの状態。 - WAIT_CANCEL (='wait_cancel')
注文のキャンセルを送信して、キャンセル待ちの状態。
注文に変化があった際に通知されるイベントです。
- EVENT_EXECUTION (='execution')
(部分)約定通知。 - EVENT_OPEN (='open')
注文の受付完了。 - EVENT_CANCEL (='cancel')
注文のキャンセル完了。 - EVENT_OPEN_FAILED (='open_failed')
注文の受付失敗。 - EVENT_CANCEL_FAILED (='cancel_failed')
注文のキャンセル失敗。 - EVENT_CLOSE (='close')
注文の全約定によるクローズ。取引所によっては通知されません。 - EVENT_ERROR (='error')
エラー通知。
各取引所のクラスは共通のベースクラスを継承して以下のように定義されています。
{Exchange}の部分はそれぞれの取引所の名称に置き換えて考えてください。
引数用フィールド
- symbol
- type
- side
- amount
- price
- params
注文管理用フィールド
- id
- filled
- state
- state_ts
- trade_ts
- open_ts
- close_ts
- editing
- external
注文グループ用フィールド
- group_name
- event_cb
TODO
- id
- ts
- type
- price
- size
- fee
- message
- info
このクラスは取引所毎に異なるクラスの名称を共通の名前にエイリアスし、初期化処理を共通化するためのものです。
例えば、bitflyerの場合、以下のように定義されています。
class Bitflyer(ExchangeBase):
Api = BitflyerApi
Websocket = BitflyerWebsocket
OrderManager = BitflyerOrderManager
OrderGroupManager = BitflyerOrderGroupManager
Trade = BitflyerTrade
Orderbook = BitflyerOrderbook
ccxtを継承したクラスで通常のccxtメソッドに加えて、追加のメソッドとAPIの呼び出し回数を勘定する機能を提供します。
このクラスは主に内部で使用されるものなので、apiの余力を確認する以外の目的で参照することは基本的にありません。
websocketのコネクションを管理するクラスです。
内部で利用するクラスなので直接利用することはありません。
内部的に注文を管理するためのクラスです。
OrderGroupの生成と管理を行うクラスです。
ポジション管理・損益計算を行うクラスで、OrderGroupの内部で利用されます。
OrderGroupManagerによって生成されるクラスで、このクラスから注文とキャンセルを行います。
約定情報を提供するクラスです。
受信したデータを予め設定したコールバック関数に約定毎に渡します。
- add_callback(cb)
約定データを受信した際に呼び出されるコールバック関数(引数: ts, price, size)を指定します。
コールバック関数は複数追加した場合、登録した順に呼び出されます。 - remove_callback(cb)
add_callbackで追加したコールバック関数を削除します。
メソッドはインスタンス経由で参照する度にidが変わるため、コールバック関数として追加したメソッドを後から削除する場合は、予めadd_callbackに渡す前に変数に控えておいてください。
cb = obj.method
trade.add_callback(cb)
# trade.remove_callback(obj.method) # エラー
trade.remove_callback(cb) # 正常にコールバック関数を削除できる
板情報を提供するクラスです。
受信したデータを買い板(bids)と売り板(asks)に適切にソートして保持します。
- bids()
買い板のリスト(正確にはビューオブジェクト)を返します。 価格が高い順(best bidが先頭)に[price, size]の形式で格納されています。 - asks()
売り板のリスト(正確にはビューオブジェクト)を返します。 価格が低い順(best askが先頭)に[price, size]の形式で格納されています。 - add_callback(cb)
板が更新された際に呼び出されるコールバック関数(引数なし)を追加します。 コールバック関数は複数追加可能ですが、websocketのスレッドから登録した順に呼び出されるため、ブロッキングする処理は避けてください。 - remove_callback(cb)
add_callbackで追加したコールバック関数を削除します。 メソッド変数はインスタンス経由で参照する度にidが変わるため、コールバック関数として追加したメソッドを後から削除する場合は、予めadd_callbackする際に変数に控えておいてください。
TODO
-
多少コードが冗長になる場合でも、基本的には設計上の正しさを優先します。
-
フレームワーク部分(botfw)については以下の規則を適用します。
- pep8に準拠。ただし__init__.pyは例外
- コメントを含めて英語(askiiコードのみ)で記述。それ以外のsampleやgitログ等は自由。
-
メソッド名や引数の変数名とその順序、また定数変数(全部大文字の変数)の内部値は可能な限りccxtと揃えます。
- 'FX_BTC_JPY'(bitflyer) -> 'FX_BTC_JPY'(ccxt)
- 'BTC_JPY(bitflyer) -> 'BTC/JPY'(ccxt)
- 'BUY'(bitflyer), 'Buy'(bitmex), 'BUY'(binance) -> 'buy'(ccxt)
- 'LIMIT'(bitflyer), 'Limit'(bitmex), 'LIMIT'(binance) -> 'limit'(ccxt)
-
メソッド名の英単語は省略しませんが、変数名、及び引数名は意味の分かる範囲内で自由に省略します。
-
日本語部分(サンプルコード、README)を含め、全角スペース全面禁止
commit 161 (9537ca0a8404e68d4021eac02c86d675492e0545) ----------
- 注文・キャンセルを非同期(デフォルト)に変更。同期はsync=Trueを引数に渡す。
- simulation mode 実装
- liquid 追加
- OrderGroupの注文一覧(orders)を削除。代わりにget_orders()を追加。
- 変数名commissionをfeeに変更。手数料周りの実装を共通化。
commit 217 (6667332febf181d03ad7f77f15600de9f82a6bb8) ----------
- bitflyer web注文API 削除
commit 251 (b453d933ee80907ff7bfec3b18894f63088d69ae) ----------
- メソッド名変更: create_basics -> init_account
commit 262 (a875b395cedb4e52dec09446233cc68ad3c3e6eb) --------