peさんのNoteを解読する①


peさんのNote「pybottersとasyncioでmagito MM!」を解読する。
 元の記事

構成

Ⅰ. 状態管理クラス(Status)
Ⅱ. イベント監視クラス(EventWatcher - CancelWatcher)
Ⅲ. 注文に関する関数(limit_order - cancel_order)
Ⅳ. ロジックに関する関数(market_making_single_loop)
Ⅴ. メイン関数(main)

Ⅰ. 状態管理クラス(Status)


☆必要な知識

  1.  pybottersについて

  • asyncioの基本知識

以下の記事を参照

  • DataStoreについての知識

以下の記事を参照

  • watch構文

storeをpybotters.bitFlyerDatastore()とすると、

with self.store.xxx.watch() as stream:
    async for msg in stream:
        print(msg.data())

で、websocketのxxxのDatastoreが更新されたタイミングでその内容をprintできる。より理解するために以下の記事を参照

 2. アサーション

assert 条件式

で、条件式を満たしていない場合AssertionErrorが発生する。条件式の後ろに文字列を追加すればエラー時に文字列が表示される。

☆各関数について


auto_update_board関数では、_asks, _bids(ソートされた板情報)を更新している。


初期設定として、_store, _asks, _bids, _board_max_depth, _max_positionを定義し、_asks, _bidsをauto_update_board関数で取得している。


 get_limit_price関数は指定したside, t, dに従って、板情報から指値価格を計算している。
 positions関数は指定したsideの保有ポジション情報をリストで取得している。この時、ポジションが_max_positionを超えている場合、AssertionErrorとなる。
 remaining_size関数は指定したsideの保有ポジションサイズを取得している。
 その他に、それぞれ
Status().best_ask → ベストアスク価格
Status().best_bid → ベストビッド価格
Status().spread → スプレッド
を取得できるようにしている。

Ⅱ. イベント監視クラス


☆必要な知識

  1. クラス継承について

 クラスには親クラス、子クラスがあり、子クラスは親クラスを継承する。プログラマーは継承した子クラス内に、親クラスの中で追加したい部分や、変更したい部分を記述することで、親クラスに類似した別の機能を持つクラスを作ることが出来る。
 今回の内容で言えば、EventWatcherクラスは、すべてのイベント監視クラスのベースとなるクラスであり、EventWatcherクラス内の_is_trigger関数が子クラスによってオーバーライド(上書き)されることでChildOrderEventWatcherクラスが作られている。また、ChildOrderEventWatcherクラスはExcecutionWatcherクラス、CancelWatcherクラスでうまく変更を加えればこれらのクラスが機能するようにうまく設計されている。
 コーディングの行い方として、

class child(parent)

で子クラスが親クラスを継承する。また、コンストラクタにおいて、

class Parent:
    def __init__(self):
        self.A = a
        self.B = b

class Child(Parent):
    def __init__(self):
        super(Child, self).__init__(A=x, B=y)

とすると、親クラスにおけるA, Bを子クラスから変更できる。そして、子クラス内で親クラスと同じ関数名の関数を作ることで親クラスの関数をオーバーライド(上書き)できる。

 2. 型アノテーション

def 関数名(引数1: 型名, 引数2: 型名) -> 型名:

で、各引数と戻り値のオブジェクトの形式を明記できる。引数として、関数を指定したい場合、

def func_1(func_2: Callable[[型名], 型名], 引数2: 型名) -> 型名:

で、各引数と戻り値のオブジェクトの形式を明記できる。func_2は引数として指定された関数であり、[[引数の型名], 戻り値の型名]のように指定している。

 3. 可変長引数**kwargs

def func(arg1, **kwargs):
    print('arg1: ', arg1)
    print('kwargs: ', kwargs)

func(0, key=1)
# arg1: 0
# kwargs: {key: 1}

のように、**をつけた引数を定義すると、引数名がキー、値が要素となるような辞書として受け渡される。

☆各クラスについて


 実際に呼び出されるクラスはExecutionWatcherクラス、CancelWathcerクラスだけであり、EventWatcherクラス、ChildOrderEventWatcherクラスはこの呼び出される2つのクラスのベースとなっている。つまり、

ExecutionWatcherクラス、CancelWatcherクラス
↓(変更、追記)
ChildOrderEventWatcherクラス
↓(変更、追記)
EventWatcherクラス

のような関係になっている。
 EventWatcherクラスで注目すべき部分は上の赤枠内の部分であろう。まず、初期設定として、_store, _trigger_fn, _taskが定義されている。そして、_taskを、後述する_watch関数によって取得している。
 各変数の意味について考えると、_storeはpybotters.bitFlyerDataStore().xxxのようなものである。また残り2つの変数は共に、websocketからデータを取得することで何らかのイベントを観察するための変数であり、_trigger_fnはイベントが起こった時に真偽値を、_taskはイベントが起こった時のwebsocketから与えられるデータ等を格納している。なお、今回の実装では_taskのみが用いられており、_trigger_fnは形式上定義されていることに注意する。
 _watch関数では、DataStoreのデータが更新されたタイミングで_is_trigger関数で定義されるイベントが起こったかどうかを判定し、真の場合は更新されたデータの内容を返している。_is_trigger関数は子クラスによってオーバーライドされるのでここで定義されている_is_trigger関数は形式的なものである。
  done関数、result関数はそれぞれ、イベントが完了したかの真偽値、その時の_taskを返す関数である。


 ChildOrderEventWatcherクラスは、EventWatcherの子クラスであり、ExecutionWatcherクラス、CancelWatcherクラスの親クラスである。ExecutionWatcherクラス、CancelWatcherクラスはそれぞれ、
ExecutionWatcherクラス → 子注文の約定の有無(wsのデータ)
CancelWatcherクラス → 子注文のキャンセルが成功したかどうか(CANCEL or CANCEL_FAILED)
をイベントとし、()のデータを処理している。

Ⅲ. 注文に関する関数

☆各関数について


 limit_order関数は、指定された情報から指値注文を出し、child_order_acceptance_idを返す関数である。


 cancel_order関数は、指定されたorder_idに紐づいた注文をキャンセルする関数である。

Ⅳ. ロジックに関する関数


☆必要な知識

  1. functools.partial()について

以下の記事を参照

☆関数について


 _oneside_loop関数は、pricer関数によって計算された価格で指値注文を出し、板のスプレッドがs_updateを超える場合は注文のキャンセル&再計算した指値価格で再注文をしつつ、注文が約定した場合は約定情報を返す関数である。


 market_making_single_loop関数は、板のスプレッドがs_entryを超える場合に、最終的に両方の指値が刺さった場合にポジションが0となるように指値を出し続ける関数である。

Ⅴ. メイン関数


☆関数について

 main関数では、前半でwebsocketに接続し、板情報、子注文の購読をしている。そして、購読が開始されるまで待機し、後半ではmarket_making_single_loop関数によって指値を出し、両側の指値が約定した後にintervalだけsleepするループを回している。

以上

この記事が気に入ったらサポートをしてみませんか?