Bot

仮想通貨botの開発を本格的に始めてみる#31(2023/11/15)「MMBotの構造解析①」

2023年11月15日

前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。

今回は、前回の記事で省略していた関数の詳細な解説を具体例を挙げながらまとめていきます。

参考にしたのはこちらの記事です。

口座残高を参照する関数

# JPY残高を参照する関数
def get_asset():

    while True:
        try:
            value = bitflyer.fetch_balance()
            break
        except Exception as e:
            logger.info(e)
            time.sleep(1)
    return value

このコードは、Bitflyer APIを使用して、Bitflyer アカウントの残高情報を取得するための関数です。具体的には、JPY(日本円)の残高を取得しています。

関数の動作は以下の通りです。

  1. bitflyer.fetch_balance() を呼び出して Bitflyer アカウントの残高情報を取得しようとします。
  2. 通信エラーが発生した場合(Exceptionが発生した場合)、エラーログを出力して、1秒待機した後に再試行します。
  3. 成功するかエラーが発生するまで、while ループが継続します。
  4. 成功した場合、取得した残高情報を返します。

この関数は、API通信中にエラーが発生することを考慮して、リトライメカニズムを備えています。

証拠金を参照する関数

# JPY証拠金を参照する関数
def get_colla():

    while True:
        try:
            value = bitflyer.privateGetGetcollateral()
            break
        except Exception as e:
            logger.info(e)
            time.sleep(1)
    return value

このコードは、Bitflyerという取引所でのAPIを使用して、ユーザーのJPY証拠金(証拠金の額)を取得するための関数です。

具体的には、以下の処理が行われています。

  1. bitflyer.privateGetGetcollateral()を呼び出して、ユーザーのJPY証拠金に関する情報を取得します。
  2. エラーが発生した場合には、そのエラーメッセージをログに記録し、1秒待ってから再試行します。これはAPI呼び出しに失敗した場合に、連続して再試行する仕組みを提供しています。
  3. 正常に情報を取得できれば、その情報を返します。

この関数を呼び出すことで、ユーザーのJPY証拠金に関する最新の情報を取得できます。

板情報から指値を入れる基準値を計算する関数

以下のコードを部分ごとに解析していきます。

# 板情報から実効Ask/Bid(=指値を入れる基準値)を計算する関数
def get_effective_tick(size_thru, rate_ask, size_ask, rate_bid, size_bid):

    while True:
        try:
            value = bitflyer.fetchOrderBook(PAIR)
            break
        except Exception as e:
            logger.info(e)
            time.sleep(2)

    i = 0
    s = 0
    while s <= size_thru:
        if value['bids'][i][0] == rate_bid:
            s += value['bids'][i][1] - size_bid
        else:
            s += value['bids'][i][1]
        i += 1

    j = 0
    t = 0
    while t <= size_thru:
        if value['asks'][j][0] == rate_ask:
            t += value['asks'][j][1] - size_ask
        else:
            t += value['asks'][j][1]
        j += 1

    time.sleep(0.5)
    return {'bid': value['bids'][i-1][0], 'ask': value['asks'][j-1][0]}

このコードは、取引所の板情報を使用して、指定された条件に基づいて実効的なAsk(売り注文の価格)およびBid(買い注文の価格)を計算する関数 get_effective_tick を定義しています。

各要素の定義

def get_effective_tick(size_thru, rate_ask, size_ask, rate_bid, size_bid):

この部分では、獲得したデータをsize_thru, rate_ask, size_ask, rate_bid, size_bidの4要素にそれぞれ定義しています。

無限ループ

while True:

while文でループ処理を作ります。関数は無限ループ内にあり、条件が満たされる限り続きます。

板情報取得の試行

try:
    value = bitflyer.fetchOrderBook(PAIR)
    break

bitflyer オブジェクトの fetchOrderBook メソッドを使用して、取引所の板情報を取得しようとします。取得できた場合は、無限ループから脱出します。

エラーハンドリング

except Exception as e:
    logger.info(e)
    time.sleep(2)

何らかの例外が発生した場合、例外の内容をログに記録し (logger.info(e))、2秒待機 (time.sleep(2)) してから再び板情報の取得を試みるようになっています。これにより、一時的な通信エラーや取引所の問題に対処し、安定した板情報を取得できるようにしています。

実効Ask/Bidの計算

i = 0
s = 0
while s <= size_thru:
    if value['bids'][i][0] == rate_bid:
        s += value['bids'][i][1] - size_bid
    else:
        s += value['bids'][i][1]
    i += 1

j = 0
t = 0
while t <= size_thru:
    if value['asks'][j][0] == rate_ask:
        t += value['asks'][j][1] - size_ask
    else:
        t += value['asks'][j][1]
    j += 1

取得した板情報を元に、指定された条件に基づいて実効的なAsk/Bidを計算しています。rate_ask および rate_bid は、既存の注文の価格を指定します。size_ask および size_bid は、それぞれAsk/Bid注文の数量を指定します。

このコードでは、size_thruAMOUNT_THRU として指定されています。以下は該当部分のコードです。

AMOUNT_THRU は、実効Ask/Bidを計算する際の指定された取引サイズの下限を表しています。この値は、実際のトレード戦略や取引所の制約に基づいて設定されます。

具体的には、メインループの変数get_effective_tick 内で以下のように利用されています。

tick = get_effective_tick(size_thru=AMOUNT_THRU, rate_ask=0, size_ask=0, rate_bid=0, size_bid=0)

この変数では、size_thruAMOUNT_THRU の値として実際の取引サイズの基準として用いられています。この値は、実効Ask/Bidを計算する際に、指定された取引サイズに達するまで板情報を順次確認していきます。従って、AMOUNT_THRU は取引サイズの下限を指定するパラメータとして機能しています。

# 数量X(この数量よりも下に指値をおく)
AMOUNT_THRU = 0.01

この部分は、板情報をもとにして実効的なAsk(売り注文の価格)とBid(買い注文の価格)を計算しています。この値は、トレード戦略や市場の流動性によって変動させることができます。適切な値を設定することで、市場状況やトレード戦略に合わせた効果的なトレードが行えるようになります。

以下に具体例を挙げて説明します。

具体例

假定している板情報が以下のような場合。

value = {
    'bids': [
        [100.0, 1.0],   # 価格: 100.0, 数量: 1.0
        [99.5, 2.0],    # 価格: 99.5, 数量: 2.0
        [98.0, 3.0],    # 価格: 98.0, 数量: 3.0
    ],
    'asks': [
        [101.0, 1.5],   # 価格: 101.0, 数量: 1.5
        [102.0, 2.0],   # 価格: 102.0, 数量: 2.0
        [103.0, 1.0],   # 価格: 103.0, 数量: 1.0
    ]
}

そして、rate_bid99.0rate_ask102.0size_bid0.5size_ask1.0 とすると、以下のように計算されます:

i = 0
s = 0
while s <= size_thru:
    if value['bids'][i][0] == rate_bid:
        s += value['bids'][i][1] - size_bid
    else:
        s += value['bids'][i][1]
    i += 1

このループは、size_thru(指定された条件)が満たされるまで、買い注文(Bid)の価格と数量を順番に見ていきます。rate_bid(指定された価格)と一致する場合、size_bid(指定された数量)を差し引いて累計数量 s に加えます。一致しない場合はそのまま数量を加えます。この例では、rate_bid99.0 なので、value['bids'][1] の価格 99.5 が一致し、2.0 - 0.5 = 1.5s に加えられます。

同様に、rate_asksize_ask を用いて実効的なAsk(売り注文の価格)を計算する部分も同様に動作します。この例では、rate_ask102.0 で、value['asks'][1] の価格 102.0 が一致するので、2.0 - 1.0 = 1.0t に加えられます。

結果的に、この計算によって実効的なAskの価格は 102.0 に、実効的なBidの価格は 99.5 になります。

さらに詳細な解析

以下に、具体的な数字を用いて上記のコードの動作を説明します。以下の例では、size_thru が 10 、rate_bid が 100 、size_bid が 2 とします。なお、これはあくまで例であり、実際の取引所データやパラメータによって異なります。

# 仮想の取引所データ
value = {
    'bids': [
        [99, 1],   # 99ドルで1BTCのBidがあると仮定
        [100, 2],  # 100ドルで2BTCのBidがある
        [101, 3],  # 101ドルで3BTCのBidがある
        # 以下略
    ],
    # ...
}

# 関数呼び出し
result = get_effective_tick(size_thru=10, rate_ask=0, size_ask=0, rate_bid=100, size_bid=2)

上記の例では、rate_bid が 100 ドルで基準価格とし、size_bid が 2 BTC としています。size_thru が 10 であるため、累積されるBidのサイズが 10 を超える直前の価格が実効Bidとなります。

  1. 最初のループ(i=0, s=0):
    • Bidの価格が 99 ドルで、そのサイズが 1 BTC です。rate_bid と一致しないため、s には 1 が加算されます。i は 1 になります。
  2. 2回目のループ(i=1, s=1):
    • Bidの価格が 100 ドルで、そのサイズが 2 BTC です。rate_bid と一致するため、s には (2 - 2) つまり 0 が加算されます。i は 2 になります。
  3. 3回目のループ(i=2, s=1):
    • Bidの価格が 101 ドルで、そのサイズが 3 BTC です。rate_bid と一致しないため、s には 3 が加算されます。i は 3 になります。
  4. 4回目のループ(i=3, s=4):
    • 以降同様にループが続き、s が 10 を超える直前の価格が実効Bidとして取得されます。この例では、最終的に value['bids'][3-1][0] が実効Bidの価格となります。


結果の返却

return {'bid': value['bids'][i-1][0], 'ask': value['asks'][j-1][0]}

計算された実効的なAsk/Bidの価格を返します。Askは value['asks'][j-1][0]、Bidは value['bids'][i-1][0] です。

最終的に、この関数は指定した条件に基づいて取引所の板情報から実効的なAsk/Bidを計算し、その結果を返す役割を果たします。

まとめ

MMBotの根幹となる価格取得とその後の処理についての解析をしました。

Botの基本戦略となる部分で何が行われているのかを理解することができました。

次回は、他の関数についても掘り下げて解析していきます。

「MMBotの構造解析②」へ続きます。

-Bot