GCP ConsoleでBitMEXマーケット・メーキングテストボットを動かす手順

最近ビットコインの高速取引に興味があっていろいろと試しています。テスト環境はどうしようと悩んでいたところ、BitMEXだけ[テスト環境 testnet](https://testnet.bitmex.com/app/trade/XBTUSD)を提供しています。これに対して、マーケット・メーキングの実験を[Google Compute Platform GCP](https://cloud.google.com/)の[Cloud Shell](https://console.cloud.google.com/)で簡易に行ってみようと思いました。

手順は基本的にSample BitMEX Market Making Botの[README](https://github.com/trading-bot/bitmex-market-maker#getting-started)の通りですが、いくつか注意点があります。

なお、デフォルトの取引戦略は[基礎的なマーケットメーキング](https://github.com/trading-bot/bitmex-market-maker#operation-overview)です。READMEの[Operation Overview](https://github.com/trading-bot/bitmex-market-maker#operation-overview)を読んでみましょう。

# Testnet BitMEXのアカウントとAPI Keyを作成

BitMEXのtestnetでアカウント作成後、[API Keyを作成](https://testnet.bitmex.com/app/apiKeys
)するときには、Key Permissionsに*Order*を指定する必要があります。これでtestnetテスト環境に対して、注文とキャンセルができるようになります。もちろん、それをしていしないで、注文・キャンセルが失敗することを確認するテストも可能です。

![スクリーンショット 2018-06-18 7.46.31.png](https://qiita-image-store.s3.amazonaws.com/0/36973/f7f7a52e-e737-e661-25a7-f673a1ed6f42.png)

# GCPのCloud ShellにPython 3.6をインストール
[Cloud Shell](https://console.cloud.google.com/)にはデフォルトでPython 3.5が入っているのですが、今回はPython 3.6を使いたいので、Condaでホームにインストールして、Cloud Shellの起動毎に再インストールしなくても住むようにしました。

```
mkdir -p ~/tmp; cd ~/tmp
curl -s \
  https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh
sh ~/tmp/Miniconda3-latest-Linux-x86_64.sh -b -p ~/miniconda3/
echo 'export PATH="~/miniconda3/bin:$PATH"' >> ~/.bashrc
export PATH="~/miniconda3/bin:$PATH"
conda create -n bitmex python=3
```

インストールしたPython3.6を有効にします。このコマンドは、Cloud Shellを新たに起動するたびに必要です。

```
source activate bitmex
```

# Sample BitMEX Market Making Botをインストール

## インストール

```
source activate bitmex
pip install bitmex-market-maker
mkdir ~/testnet-bitmex-market-maker/
cd ~/testnet-bitmex-market-maker/
marketmaker setup
```

## 設定

`testnet-bitmex-market-maker/settings.py` を編集し、API_KEYとAPI_SECRETを正しく記入します。テスト環境なので、気軽に DRY_RUN=False にしてしまいましょう。

## 実行

```
cd ~/testnet-bitmex-market-maker/
marketmaker
```

こんな感じの出力になります。

```
$ marketmaker
2018-06-18 08:17:51,544 - INFO - market_maker - BitMEX Market Maker Version: v1.4

2018-06-18 08:17:51,545 - INFO - ws_thread - Connecting to wss://testnet.bitmex.com/realtime?subscribe=quote:XBTUSD,trade:XBTUSD,instrument,order:XBTUSD,execution:XBTUSD,margin,position
2018-06-18 08:17:51,546 - INFO - ws_thread - Authenticating with API Key.
2018-06-18 08:17:51,547 - INFO - ws_thread - Started thread
2018-06-18 08:17:53,549 - INFO - ws_thread - Connected to WS. Waiting for data images, this may take a moment...
2018-06-18 08:17:53,550 - INFO - ws_thread - Got all market data. Starting.
2018-06-18 08:17:53,550 - INFO - market_maker - Using symbol XBTUSD.
2018-06-18 08:17:53,550 - INFO - market_maker - Order Manager initializing, connecting to BitMEX. Live run: executing real trades.
2018-06-18 08:17:53,550 - INFO - market_maker - Resetting current position. Canceling all existing orders.
2018-06-18 08:17:53,550 - INFO - bitmex - sending req to https://testnet.bitmex.com/api/v1/order: {"filter": "{\"ordStatus.isTerminated\": false, \"symbol\": \"XBTUSD\"}", "count": 500}
2018-06-18 08:17:54,927 - INFO - market_maker - XBTUSD Ticker: Buy: 6474.5, Sell: 6475.0
2018-06-18 08:17:54,927 - INFO - market_maker - Start Positions: Buy: 6471.8, Sell: 6477.7, Mid: 6475.0
2018-06-18 08:17:54,927 - INFO - market_maker - Long delta limit exceeded
2018-06-18 08:17:54,927 - INFO - market_maker - Current Position: 1000, Maximum Position: 10
2018-06-18 08:17:54,927 - INFO - market_maker - Current XBT Balance: 1.228862
2018-06-18 08:17:54,927 - INFO - market_maker - Current Contract Position: 1000
2018-06-18 08:17:54,927 - INFO - market_maker - Position limits: -10/10
2018-06-18 08:17:54,927 - INFO - market_maker - Avg Cost Price: 6472.5
2018-06-18 08:17:54,927 - INFO - market_maker - Avg Entry Price: 6472.5
2018-06-18 08:17:54,927 - INFO - market_maker - Contracts Traded This Run: 0
2018-06-18 08:17:54,927 - INFO - market_maker - Total Contract Delta: 0.1548 XBT
2018-06-18 08:17:54,927 - INFO - market_maker - Creating 3 orders:
2018-06-18 08:17:54,927 - INFO - market_maker - Sell 1000 @ 6477.5
2018-06-18 08:17:54,927 - INFO - market_maker - Sell 1100 @ 6510.0
2018-06-18 08:17:54,927 - INFO - market_maker - Sell 1200 @ 6542.5
2018-06-18 08:17:54,928 - INFO - bitmex - sending req to https://testnet.bitmex.com/api/v1/order/bulk: {"orders": [{"price": 6542.5, "orderQty": 1200, "side": "Sell", "clOrdID": "mm_bitmex_rd5tG3aURzmxMfW9166f7A", "symbol": "XBTUSD"}, {"price": 6510.0, "orderQty": 1100, "side": "Sell", "clOrdID": "mm_bitmex_s8eaXKHES5mxuKpO6xfeWg", "symbol": "XBTUSD"}, {"price": 6477.5, "orderQty": 1000, "side": "Sell", "clOrdID": "mm_bitmex_xoXXUYI3Sd2NUIf6D4Exjg", "symbol": "XBTUSD"}]}
-----
2018-06-18 08:18:00,906 - INFO - market_maker - XBTUSD Ticker: Buy: 6474.5, Sell: 6475.0
2018-06-18 08:18:00,907 - INFO - market_maker - Start Positions: Buy: 6471.8, Sell: 6477.7, Mid: 6475.0
2018-06-18 08:18:00,907 - INFO - market_maker - Long delta limit exceeded
2018-06-18 08:18:00,907 - INFO - market_maker - Current Position: 1000, Maximum Position: 10
2018-06-18 08:18:00,907 - INFO - market_maker - Current XBT Balance: 1.228912
2018-06-18 08:18:00,907 - INFO - market_maker - Current Contract Position: 1000
2018-06-18 08:18:00,907 - INFO - market_maker - Position limits: -10/10
2018-06-18 08:18:00,907 - INFO - market_maker - Avg Cost Price: 6472.5
2018-06-18 08:18:00,907 - INFO - market_maker - Avg Entry Price: 6472.5
2018-06-18 08:18:00,907 - INFO - market_maker - Contracts Traded This Run: 0
2018-06-18 08:18:00,907 - INFO - market_maker - Total Contract Delta: 0.1548 XBT
-----
2018-06-18 08:18:05,913 - INFO - market_maker - XBTUSD Ticker: Buy: 6474.5, Sell: 6475.0
2018-06-18 08:18:05,913 - INFO - market_maker - Start Positions: Buy: 6471.8, Sell: 6477.7, Mid: 6475.0
2018-06-18 08:18:05,913 - INFO - market_maker - Long delta limit exceeded
2018-06-18 08:18:05,914 - INFO - market_maker - Current Position: 1000, Maximum Position: 10
2018-06-18 08:18:05,914 - INFO - market_maker - Current XBT Balance: 1.228932
2018-06-18 08:18:05,914 - INFO - market_maker - Current Contract Position: 1000
2018-06-18 08:18:05,914 - INFO - market_maker - Position limits: -10/10
2018-06-18 08:18:05,915 - INFO - market_maker - Avg Cost Price: 6472.5
2018-06-18 08:18:05,915 - INFO - market_maker - Avg Entry Price: 6472.5
2018-06-18 08:18:05,915 - INFO - market_maker - Contracts Traded This Run: 0
2018-06-18 08:18:05,915 - INFO - market_maker - Total Contract Delta: 0.1547 XBT
```

たまに socket.timeout で見事に死にます。まぁ、サンプルなのでご愛嬌ということで。

```
2018-06-18 08:17:28,547 - INFO - bitmex - sending req to https://testnet.bitmex.com/api/v1/order/bulk: {"orders": [{"price": 6541.5, "orderQty": 1200, "side": "Sell", "clOrdID": "mm_bitmex_e+LjxgfeSkuTv5Qaj4hK0w", "symbol": "XBTUSD"}, {"price": 6509.0, "orderQty": 1100, "side": "Sell", "clOrdID": "mm_bitmex_J+wPHxWoTeSby/BRmHfdsg", "symbol": "XBTUSD"}, {"price": 6476.5, "orderQty": 1000, "side": "Sell", "clOrdID": "mm_bitmex_ZTSNv+l0TDWUeWmaDdXNKg", "symbol": "XBTUSD"}]}
2018-06-18 08:17:35,555 - WARNING - bitmex - Timed out on request: order/bulk ({"orders": [{"price": 6541.5, "orderQty": 1200, "side": "Sell", "clOrdID": "mm_bitmex_e+LjxgfeSkuTv5Qaj4hK0w", "symbol": "XBTUSD"}, {"price": 6509.0, "orderQty": 1100, "side": "Sell", "clOrdID": "mm_bitmex_J+wPHxWoTeSby/BRmHfdsg", "symbol": "XBTUSD"}, {"price": 6476.5, "orderQty": 1000, "side": "Sell", "clOrdID": "mm_bitmex_ZTSNv+l0TDWUeWmaDdXNKg", "symbol": "XBTUSD"}]}), retrying...
Traceback (most recent call last):
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/site-packages/requests/packages/urllib3/connectionpool.py", line 386, in _make_request
    six.raise_from(e, None)
  File "<string>", line 2, in raise_from
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/site-packages/requests/packages/urllib3/connectionpool.py", line 382, in _make_request
    httplib_response = conn.getresponse()
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/http/client.py", line 1331, in getresponse
    response.begin()
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/http/client.py", line 297, in begin
    version, status, reason = self._read_status()
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/http/client.py", line 258, in _read_status
    line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/socket.py", line 586, in readinto
    return self._sock.recv_into(b)
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/ssl.py", line 1009, in recv_into
    return self.read(nbytes, buffer)
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/ssl.py", line 871, in read
    return self._sslobj.read(len, buffer)
  File "/home/chicong_v/miniconda3/envs/bitmex/lib/python3.6/ssl.py", line 631, in read
    v = self._sslobj.read(len, buffer)
socket.timeout: The read operation timed out

...

Exception: Max retries on order/bulk ({"orders": [{"price": 6541.5, "orderQty": 1200, "side": "Sell", "clOrdID": "mm_bitmex_e+LjxgfeSkuTv5Qaj4hK0w", "symbol": "XBTUSD"}, {"price": 6509.0, "orderQty": 1100, "side": "Sell", "clOrdID": "mm_bitmex_J+wPHxWoTeSby/BRmHfdsg", "symbol": "XBTUSD"}, {"price": 6476.5, "orderQty": 1000, "side": "Sell", "clOrdID": "mm_bitmex_ZTSNv+l0TDWUeWmaDdXNKg", "symbol": "XBTUSD"}]}) hit, raising.
2018-06-18 08:17:35,750 - INFO - market_maker - Shutting down. All open orders will be cancelled.
2018-06-18 08:17:35,750 - INFO - market_maker - Resetting current position. Canceling all existing orders.
2018-06-18 08:17:35,751 - INFO - bitmex - sending req to https://testnet.bitmex.com/api/v1/order: {"filter": "{\"ordStatus.isTerminated\": false, \"symbol\": \"XBTUSD\"}", "count": 500}
2018-06-18 08:17:37,430 - INFO - ws_thread - Websocket Closed
```

完全に死ぬ前には、すべてのオーダーをキャンセルしようとしてくれています。これは、正常動作中の "Ctr-C" 押下の場合と同じです。

```
-----
^C2018-06-18 08:23:23,680 - INFO - market_maker - Shutting down. All open orders will be cancelled.
2018-06-18 08:23:23,680 - INFO - market_maker - Resetting current position. Canceling all existing orders.
2018-06-18 08:23:23,680 - INFO - bitmex - sending req to https://testnet.bitmex.com/api/v1/order: {"filter": "{\"ordStatus.isTerminated\": false, \"symbol\": \"XBTUSD\"}", "count": 500}
2018-06-18 08:23:24,063 - INFO - market_maker - Canceling: Sell 1200 @ 6542.5
2018-06-18 08:23:24,064 - INFO - market_maker - Canceling: Sell 1100 @ 6510.0
2018-06-18 08:23:24,064 - INFO - market_maker - Canceling: Sell 1100 @ 6480.5
2018-06-18 08:23:24,064 - INFO - bitmex - sending req to https://testnet.bitmex.com/api/v1/order: {"orderID": ["f18f0705-d6e4-3a7d-682e-a0ff5a2f431f", "f78a672c-8c2a-d0a3-ebb9-5c491f672433", "4a35ba81-fce8-0e53-62c5-ec399dbaa6b0"]}
2018-06-18 08:23:25,664 - INFO - ws_thread - Websocket Closed
```

# 展望

気軽にマーケット・メーキング気分を味わうことができたかと思います。今後としては

- 長期的に動かすために、VMやContainerに入れて動かし、ログを見れるようにしましょう。
- API Keyを環境変数などで安全に渡したいですね。
- [取引戦略](https://github.com/trading-bot/bitmex-market-maker#advanced-usage)を設定してみましょう。