Pythonでアービトラージ自動売買に挑戦⑥~GMOコイン追加~

1月 19, 2020

前回はこちら→bitFlyerでアービトラージ自動売買に挑戦⑤
Pythonを使ってビットコインのアービトラージに挑戦する記録の第6弾です。
前回は、取引所間の差額以外に直近の値動きと上げ・下げ局面を判断した上で発注サインを出す処理を追加しました。今回はGMOコインを差額計算する取引所の一つに加えたいと思います。

前回のプログラムではccxtに対応しているbitFlyer, coincheck, bitbank, Liquid, BTCBOXを対象に入れていたのですが、残念ながらGMOコインはccxtに対応しておらず対象外でした。今回はGMOコイン専用のAPIを使用して処理に加えたいと思います。早速ですが追加分のソースコードがこちらになります。

ソースコード全体は最後にご紹介します。GMOコインのAPIを使うとはいっても価格情報を引っ張ってきているだけになります。JSONデータからask,bidそれぞれのデータを指定するときに
json_data[“data"][0][“ask"]
としていますが、正直私はこの部分をよく理解できずに使っています。。はじめは
json_data[“data"][“ask"]
と指定したのですが、その場合だと
TypeError: list indices must be integers or slices, not str
というエラーが出てしまいました。リストのインデックスは文字列ではなく整数にしてくださいとのことです。そこでググってみたところ下記ページを見つけましたので[0]を追加することにしました。
https://ja.stackoverflow.com/questions/51774/json%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%B8%AD%E8%BA%AB%E3%81%AE%E6%8C%87%E5%AE%9A
自分の勝手な解釈だと、"data"の"0″番目の要素の中からask/bidに対応する値を取り出す、という意味だとしました。実際に読み込んだJSONデータのサンプルが下記になりますが、’data’:[{}]のように、[]で囲まれた要素は{}1個しかない、つまり0番目の要素が対象になっていることがわかります。
{'status’: 0, 'data’: [{'ask’: '983660’, 'bid’: '983333’, 'high’: '986588’, 'last’: '983333’, 'low’: '979613’, 'symbol’: 'BTC_JPY’, 'timestamp’: '2020-01-18T23:46:40.716Z’, 'volume’: '7553.15’}], 'responsetime’: '2020-01-18T23:46:40.822Z’}

下記が今回作成したソースコードの全文になります。

import ccxt
import time
import datetime		
import csv			
from pprint import pprint
import requests
import json
max = 500			#cryptowatchで取得できる足の数
before = 5			#さかのぼる足の数
periods = 1800		#3600秒=1時間足
range_th = 15000	#値動きの最大幅
range_flag = 0		#値動きの最大幅が許容内の時のフラグ
arbi_th = 2000		#裁定取引開始差額
arbi_flag = 0		#裁定取引開始差額の時のフラグ
sage_flag = 0		#下げ局面でないことを確認する時のフラグ
delay = 10			#指定秒数待って繰り返し
def get_price(min,i):
	data = response.json()
	last_data = data["result"][str(min)][i]
	return { "close_time" : last_data[0],
		"open_price" : last_data[1],
		"high_price" : last_data[2],
		"low_price" : last_data[3],
		"close_price":last_data[4] }
def check_candle( data ):
	if data["close_price"]-data["open_price"] > 0 : return True	
	else : return False
	
def check_ascend( data,last_data ):
	if data["open_price"] > last_data["open_price"] and data["close_price"] > last_data["close_price"]:
		return True
	else:
		return False
while 1:
	dt = datetime.datetime.now()
	print(dt)					
	exchange_list = ['bitflyer', 'coincheck', 'bitbank', 'liquid', 'btcbox']
	ask_exchange = ''
	ask_price = 99999999
	bid_exchange = ''
	bid_price = 0
	for exchange_id in exchange_list:
	    exchange = eval('ccxt.' + exchange_id + '()')
	    orderbook = exchange.fetch_order_book ('BTC/JPY')
	    bid = orderbook['bids'][0][0] if len (orderbook['bids']) > 0 else None
	    ask = orderbook['asks'][0][0] if len (orderbook['asks']) > 0 else None
	    if ask < ask_price:
	        ask_exchange = exchange_id
	        ask_price = ask
	    if bid > bid_price:
	        bid_exchange = exchange_id
	        bid_price = bid
	#GMOコインと比較
	response = requests.get('https://api.coin.z.com/public/v1/ticker?symbol=BTC_JPY')
	json_data = response.json()
	ask_GMO = float(json_data["data"][0]["ask"])
	if ask_GMO < ask_price:
		ask_exchange = 'GMOコイン'
		ask_price = ask_GMO
	bid_GMO = float(json_data["data"][0]["bid"])
	if bid_GMO > bid_price:
		bid_exchange = 'GMOコイン'
		bid_price = bid_GMO
	#アービトラージ計算結果を出力
	arbi = bid_price - ask_price
	print (ask_exchange, 'で', ask_price, '円で買って')
	print (bid_exchange, 'で', bid_price, '円で売れば')
	print (arbi, '円の利益!')
	if arbi > arbi_th:
		arbi_flag = 1
		print('①',arbi_th,'円以上の差額なのでフラグ立てます')
	else:
		arbi_flag = 0
		print('①',arbi_th,'円以下の差額なのでフラグ立てません')
			
	#過去のボラティリティを確認
	response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc",params = { "periods" : periods })
	json_data = response.json()
	i = max - before
	max_range = 0
	while i in range(max):
		data = get_price(periods, i)
		price_range = data["high_price"] - data["low_price"]
		print((max-i)*periods/3600,'時間前の値幅',price_range)
		if max_range <  price_range:
			max_range = price_range
		i = i+1
	print('直近',before*periods/3600,'時間の値幅は',max_range,'円です')
	if max_range < range_th:
		range_flag = 1
		print('②',range_th,'円以内の値幅なのでフラグ立てます')
	else:
		range_flag = 0
		print('②',range_th,'円以上の値幅なのでフラグ立てません')
	
	#売り局面でないことを確認
	data1 = check_candle(get_price(periods, max-3))
	data2 = check_candle(get_price(periods, max-2))
	data3 = check_candle(get_price(periods, max-1))
	data4 = check_ascend(get_price(periods, max-2),get_price(periods, max-3))
	data5 = check_ascend(get_price(periods, max-1),get_price(periods, max-2))
	if data1 or data2 or data3 or data4 or data5:
		sage_flag = 1
		print('③','下げ局面ではないのでフラグ立てます')
	else:
		sage_flag = 0
		print('③','下げ局面なのでフラグ立てません')
	if arbi_flag * range_flag * sage_flag == 1:
		print('発注条件成立しました\n')
	else:
		print('発注条件成立せず\n')	
	time.sleep(delay)