noteのタイトル画像

[アービトラージ] 売買する枚数を捌ける価格を計算する

前回は、QASH,ETH,BTCのask/bid値をtickerから取得して、差益を得られるタイミングを捉えるプログラムを作って見ました。

しかし、tickerで得たaskで買い、bidで売るには、かなり運が良くないとできません。売買したいだけの枚数がそのask/bid値で達成できるとは限らないのです。

そのために、今回は前回のプログラムを少し改造して、実際にx枚数の通貨を売り買いするのに十分(であると思われる)ask/bidを計算してみます。

板情報を取得して、指定した枚数を購入・売却できるask/bidを調べてみましょう。

板情報を取得するccxtのメソッドは
 fetchOrderBook
です。
取得する板は
 QASH/ETH asks
 ETH/BTC asks
 QASH/BTC bids
の3つです。
取得した結果は以下のようになりました。

--- QASH/ETH.asks ---
[ [ 0.0011, 2707 ],
  [ 0.00110999, 2000 ],
  [ 0.00111, 100 ],
  [ 0.00112, 2000 ],
  [ 0.001125, 1000 ],
  [ 0.00112999, 100 ],
  [ 0.00113, 100 ],
  [ 0.00113382, 117.1885 ],
  [ 0.00113499, 100 ],
  [ 0.001135, 100 ],
  [ 0.00113999, 100 ],
  [ 0.00114, 100 ],
  [ 0.00114117, 14.23149905 ],
  [ 0.00114181, 197.3488 ],
  [ 0.00114499, 100 ],
  [ 0.001145, 100 ],
  [ 0.00114893, 117.1886 ],
  [ 0.00114999, 100 ],
  [ 0.00115, 100 ],
  [ 0.00115499, 100 ],
  [ 0.001155, 100 ] ]
--- ETH/BTC.asks ---
[ [ 0.03501227, 3.1 ],
  [ 0.03505236, 6.55215994 ],
  [ 0.03505239, 15.283 ],
  [ 0.03505241, 1.88 ],
  [ 0.03519827, 3.5504 ],
  [ 0.03524816, 1.19428 ],
  [ 0.03524817, 152.83 ],
  [ 0.03524818, 4.9599 ],
  [ 0.03528675, 6.7154 ],
  [ 0.03528681, 5.1239 ],
  [ 0.03532152, 8.3093 ],
  [ 0.03554284, 76.415 ],
  [ 0.03555853, 0.4169 ],
  [ 0.03571814, 0.4154 ],
  [ 0.03591691, 0.03332515 ],
  [ 0.03593572, 0.4117 ],
  [ 0.03598, 10 ],
  [ 0.036, 1 ],
  [ 0.0360509, 0.41705 ],
  [ 0.03606, 0.011 ],
  [ 0.03608298, 36.00336 ] ]
--- QASH/BTC.bids ---
[ [ 0.00003751, 6238.33644361 ],
  [ 0.0000375, 150 ],
  [ 0.00003737, 223.356 ],
  [ 0.00003731, 568.202403 ],
  [ 0.00003727, 400 ],
  [ 0.00003725, 12366.99181208 ],
  [ 0.00003711, 1885.92704 ],
  [ 0.0000371, 190.81132075 ],
  [ 0.00003709, 9506.5251 ],
  [ 0.00003707, 2800 ],
  [ 0.00003703, 9405.02699738 ],
  [ 0.000037, 150 ],
  [ 0.00003694, 10742.83210307 ],
  [ 0.00003688, 1020 ],
  [ 0.00003677, 50000 ],
  [ 0.00003676, 13 ],
  [ 0.00003675, 75 ],
  [ 0.00003662, 2000 ],
  [ 0.00003658, 15 ],
  [ 0.00003651, 7620.62052554 ],
  [ 0.0000365, 200 ] ]

2つ値をもった配列の配列が戻されています。
値は
 [ 価格、数量 ]
の組みとなっており、買い板、売り板のそれぞれで先に捌けていく順番に並んでいます。

liquidの板情報はask/bidの板数を指定しても最大で21個までしか取得できませんでした。
よって、あまり大きな枚数を指定すると、板を外れてしまうことになるので、売買数量は試行錯誤で決めます。

では、前回から改良したプログラムを示します。

// QASHとBTC,ETHで差益を狙う
// 最適売買値を板情報から取得する

// いつものccxt 
const ccxt = require('ccxt');

// 売買したい枚数
const volume = 10000; // QASHの売買単位

/**
 * スリープ関数
 * @param {*} time ミリ秒 
 */
const sleep = async time => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
};

/**
 * 切り捨て関数(桁指定)
 * @param {*} value     : 値 
 * @param {*} precision : 小数点桁数
 */
const floor = (value, precision) => {
  return Math.floor(value * (10 ** precision)) / (10 ** precision);
}

/**
 * 必要な枚数を購入・売却できる価格を求める。
 * @param {*} ex     : 取引所オブジェクト
 * @param {*} pair   : 通貨ペア 
 * @param {*} side   : asks or bids
 * @param {*} volume : 売買したい枚数
 */
const tickfrombook = async (ex, pair, side, volume) => {
  const book = await ex.fetchOrderBook(pair);
  //console.log(`--- ${pair}.${side} ---`);
  //console.log(book[side]);
  let _volume = volume;
  for (let i = 0; i < book[side].length; i++) {
    _volume -= book[side][i][1];  // volume
    if (_volume <= 0) {
      return book[side][i][0];    // priceを戻す
    }
  }
  throw 'not match asks/bids on book';  // 板情報に無い時は例外発生
}

// メインループ
(async () => {
  const ex = new ccxt.liquid();

  while (true) {

    try {
      // 買う
      const qash_eth = await tickfrombook(ex, 'QASH/ETH', 'asks', volume);
      // 買う
      const eth_btc = await tickfrombook(ex, 'ETH/BTC', 'asks', qash_eth * volume);

      // 売る
      const qash_btc = await tickfrombook(ex, 'QASH/BTC', 'bids', volume);

      console.log('-- calc ---');
      // 1QASHを買うには x(=QASH/ETH)のETHが必要とすると、xのETHを買うには y(=ETH/BTC)のBTCが必要
      // 必要なBTCは = QASH/ETH * ETH/BTC
      console.log(`${volume} QASH/BTC buy  = QASH/ETH * ETH/BTC: ${floor(qash_eth * eth_btc, 8)}`);
      // 1QASHを売って得られるBTCは、QASH/BTC
      console.log(`${volume} QASH/BTC sell = QASH/BTC          : ${floor(qash_btc, 8)}`);
      // 差益を得るには、
      // 1QASH/BTC sell > 1QASH/BTC buy でなければならない。
      if(qash_btc > qash_eth * eth_btc) {
        // 差益がある
        console.log(`差益; ${floor(qash_btc - qash_eth * eth_btc, 8)}`);
      }
    } catch(e) {
      console.log(e);
    }
    // 5秒待つ
    await sleep(5000);
  }
})();

どんどんコード行数が伸びていますが、ご了承ください。
説明のためにあちこちにコメントを挿入して、多少冗長でも説明しやすさを心がけました。

今回のコードの肝は
 tickfrombook
メソッドです。

板情報から売買に必要な枚数を確保できる価格まで板情報を遡っています。
先ほども書きました通り、板の枚数が少ないのであまり大量の枚数を捌こうとすると板の枚数を超えてしまいます。
試しにQASHを100,000枚買うためのETHを指定すると

not match asks/bids on book

という例外を発生させて計算ロジックから抜けています。

では、10,000枚にして再実行してみましょう。
実行結果は次のようになりました。

-- calc ---
10000 QASH/BTC buy  = QASH/ETH * ETH/BTC: 0.00004044
10000 QASH/BTC sell = QASH/BTC          : 0.00003724
-- calc ---
10000 QASH/BTC buy  = QASH/ETH * ETH/BTC: 0.00004044
10000 QASH/BTC sell = QASH/BTC          : 0.00003724
-- calc ---
10000 QASH/BTC buy  = QASH/ETH * ETH/BTC: 0.00004041
10000 QASH/BTC sell = QASH/BTC          : 0.00003724

・・・(以下略)・・・

多少値が動いています。buy/sellの値にかなり幅があるのがわかります。
では、枚数を1枚とかにしてみましょう。すると結果は以下のようになりました。

-- calc ---
1 QASH/BTC buy  = QASH/ETH * ETH/BTC: 0.00003849
1 QASH/BTC sell = QASH/BTC          : 0.00003751
-- calc ---
1 QASH/BTC buy  = QASH/ETH * ETH/BTC: 0.00003849
1 QASH/BTC sell = QASH/BTC          : 0.00003751
-- calc ---
1 QASH/BTC buy  = QASH/ETH * ETH/BTC: 0.00003849
1 QASH/BTC sell = QASH/BTC          : 0.00003751

先の10,000枚の時よりも差が縮まっていますね。それだけ売買しやすいということです。

指定枚数を大きくすると売買が成立する価格が離れていきます。かと言って指定枚数を小さくすると只でさえ少ない差益がさらに少なくなる可能性が大です。

この辺りは売買する取引所で、対象の通貨ペアの変動がどれくらいあるのかを見て、試行錯誤で決めないとならないでしょう。



ソフトウェア・エンジニアを40年以上やってます。 「Botを作りたいけど敷居が高い」と思われている方にも「わかる」「できる」を感じてもらえるように頑張ります。 よろしくお願い致します。