【初心者向け】Zaif API×Pythonでプチフィンテック20 仮想通貨自動取引botの作成(ポジションリストの実装)

前回から引き続き、botの実装を進めて行きます。

今回は、ポジションリストの生成と初期化の実装を行います。

<スポンサーリンク>

実装

ログ出力とエラー処理の枠組み実装

プログラムの中で実処理が始まるのはこの②ポジションリストの生成と初期化からですので、このタイミングで一旦ログを出力するようにします。また、②の処理が終わるということは、実処理としての準備作業が終わることを意味しますので、この終わりのタイミングでもログを出力します。

さらに、ポジションリスト生成の過程でZaif APIを利用しますが、その際にエラーが発生する可能性がありますので、エラーに対する処理を行うための実装もしておきます。

logger.info('準備開始')

try:

   #この中にポジションリストの生成と初期化処理を記述

except Exception as e:
    logger.exception('準備失敗: %r' % e)
    sys.exit()

logger.info('準備完了')

中央のコメント部分に、②の処理を記述しますが、それをtry~exceptで括ることにより、②の処理で例外が発生した際にその対応を行えるようにしています。

なお、except節の内容について2点ほど補足します。

まず、「excpet Exception as e」という記述で、発生した例外そのものを「e」という変数に格納します。さらに、ログの出力内容に「‘準備失敗: %r’ % e」という記述をすることにより、例外発生時には「準備失敗:[例外の詳細な内容]」という形式でログが出力されるようにしています。

また、続く「sys.exit()」は、sysモジュールに含まれるexitメソッドにより、プログラムを終了するための記述です。

この記述が無ければ、準備が失敗しているにも関わらずメイン処理に制御が流れてしまいますが、その場合に意図しない発注が行われ、思わぬ損失を出してしまうリスクがあります。そこで、失敗時にはプログラムを中止するようにしています。

ポジションリストの生成

続いて、取引する通貨ペアやレンジなど、前処理で与えた情報を元にポジションリストを生成する処理を記述します。

なお、ポジションリストについては、第17回目の記事に具体的なイメージを掲載していますので、そちらも必要に応じてご参照ください。

今回からいよいよ、botの作成に入ります。 今回はbotの作成に先立ち、どのような処理を行うbotを作成しようとしているのかを説明しま...
    #ポジションリストの生成と、全ポジションをFalse(未発注)で初期化
    position_list = {}
    for i in range(LOWER_PRICE, UPPER_PRICE+1, ORDER_INTERVAL):
        position_list[i] = False

まず、この処理はtry~exceptの中に入れる記述ですので、各行の先頭に空白(インデント)を入れることを忘れないで下さい。(以降、同様です。)

処理の概要は以下の通りです。

まず、2行目でポジションリストを格納する変数「position_list」を定義するとともに、空のディクショナリ「{}」をそれに代入することにより、position_listがディクショナリ型であることを明示します。

続く3、4行目で、このディクショナリの中身を埋めていきますが、その形式は、キーとして発注価格、値としてTrue(発注済み)またはFalse(未発注)を持つディクショナリ型とします

具体的には、LOWER_PRICE(発注レンジの下限)からUPPER_PRICE(発注レンジの上限)まで、ORDER_INTERVAL(発注間隔)毎にポジションのステータスを入れる変数のリストを生成し、さらにポジション全てに対して「未発注」というステータスを設定します。

なお、

for 変数 in range(下限、上限、間隔):
    変数を使った処理

という記述で、「下限」 <= X < 「上限」となるXを、「下限」から「間隔」単位で取得し、それを1つ1つ「変数」に格納し、その「変数」を用いた処理を連続的に行わせることができます

ここで、「X < 「上限」」から分かる通り、「上限」については値を取得する範囲には含まれません。そのため、プログラム中の記述では「上限」を「UPPER_PRICE+1」とすることにより、前処理で指定した上限の価格も発注対象となるようにしています。

注文データの確認とポジションリストの修正

続いて注文データを参照して、ポジションリスト中のポジションのうち、既に注文済みのものがあれば、それを注文済みのステータス(True)に修正する処理を実装します。

    #注文データを参照し、発注済みのポジションをTrue(発注済み)に初期化
    params = {
        'method': 'active_orders',
        'nonce': time(),
        'currency_pair': CURRENCY_PAIR
    }

    response_dict = tradeRequester(params, KEY, SECRET)

    #全注文を参照し、ポジションリストを更新する必要があるか確認
    order_numbers = response_dict['return'].keys()
    for order_number in order_numbers:
        price = int(response_dict['return'][order_number]['price'])

        #買い注文がある場合の処理
        if response_dict['return'][order_number]['action'] == 'bid':
            if price in position_list.keys():
                position_list[price] = True

        #売り注文がある場合の処理
        if response_dict['return'][order_number]['action'] == 'ask':
            if price - LIMIT in position_list.keys():
                position_list[price - LIMIT] = True

注文データの確認は、現物取引APIの中の「active_orders」というものを使います。これを使うためには、パラメータとしてnonceと対象となる通貨ペア(currency_pair)を指定する必要がありますので、まずはそのパラメータ設定を行っています。

そして、取得した注文データを参照するわけですが、Zaif API仕様によると、注文データの形式例は以下のようになっています。

{
    "success": 1,
    "return": {
        "184": {
            "currency_pair": "btc_jpy",
            "action": "ask",
            "amount": 0.03,
            "price": 56000,
            "timestamp": 1402021125,
            "comment" : "demo"
        }
    }
}

「return」の中に注文データが注文番号単位で格納される形式となっています。order_numberは上記の例で言うと「184」という数字が該当します。

なお、Zaif APIから返ってくるデータは、例えば「”currency_pair”: “btc_jpy”」のように、「キー名:値」の繰り返しで構成されています。注文番号については、「”番号”: {「キー名:値」の繰り返し}というように、キーが固定の名称ではなく、番号になっていることに注意下さい

さて、このデータを使って、どのようにポジションと重なる注文の有無を判別するかについて説明します。

結論から言うと、以下2つのいずれかに該当する注文が、ポジションと重なっている注文であるとみなすことができます。

(1)「買い(Ask)」注文で、発注価格がポジション価格と同一
(2)「売り(Bid)」注文で、発注価格からリミットを引いた値がポジション価格と同一

例えば、発注価格\1,000,000のポジションがあって、リミットの設定を\200,000にしていると仮定します。

もし注文データ中に「\1,000,000買い」の注文があれば、(1)に基づいてポジションと重なる注文があると、素直に考えることができます。

そして、この買い注文が執行されて、リミットに応じた売りが発注されている場合、「ポジションと重なる注文がある」と判断したいところですが、注文データ中には「\1,200,000売り」の注文があるだけですので、この注文とポジションを紐づける必要があります

これが(2)のルールで、この条件に合致する注文が注文データ中にあれば、ポジションに該当する注文が「ある」とみなし、重複発注を防ぐことができます。

この処理を実装しているのが、上記コードの10~23行目です。

処理の内容について、1点だけ補足します。

この処理では、注文データ中の全ての注文を参照する必要がありますが、前述の通り、各注文にアクセスしようと思った時に固定的なキーではなく、逐一変化する注文番号をキーを使う必要があります。したがって、まずは注文データ中に含まれる注文番号を全て取得する必要があります。

これを行っているのが、11行目の

order_numbers = response_dict['return'].keys()

という記述です。

「response_dict[‘return’]」は実体として、「キー(=注文番号): 値(=各注文の詳細情報)」の繰り返しで構成されていますが、「response_dict[‘return’].keys()」という記述で、「response_dict[‘return’]」に含まれるキー、すなわち注文番号を全て取得しています

さらに、取得した注文番号のリストを「order_numbers」という変数に格納し、続く12行目でそこから1つずつ注文番号を取り出して、各々の発注価格を取り出しています。

残りのコードについて内容は多少複雑ですが、基本的にはZaif APIからの返り値の形式例を見つつ、コードに記述されているディクショナリの各要素に何が入っているかを丁寧に読み解いていけば、動作は理解できるものと思います。

終わりに

今回で、売買を行うメイン処理を進めるための事前準備が全て終わりました。

そこで、次回からいよいよメイン処理の実装に進めていきたいと思います。

カテゴリ:フィンテック
<スポンサーリンク>

シェアする

フォローする