アービトラージプログラムに手数料の計算を加えました

2月 3, 2020

こちらの記事やこちらの記事でアービトラージの差額計算をするPythonプログラムを組んできました。いろんな取引所の中から一番差額の開く取引所を見つける処理が入っていたのですが、こちらの記事で紹介した通り、最近はbitbankとGMOに絞っても良いのかなと考えています。そこで今回はbitbankとGMOに対象を絞った上で、新たに手数料を加味して計算するようにプログラムを修正しました。対象の取引所が変わったときに修正しやすいように各種パラメータは変数を定義して値を格納しました。それがこちら。
(ソースコード全文は記事の末尾にあります)

そして実際の計算を行っている部分がこちら。

そして実行結果がこちら。

購入・売却ともにメイカーとして約定できれば良いのですが、片方しか約定しないリスクもあるので今回はbitbankで指値の売り注文を出しておいてそれがメイカーとして約定した瞬間にGMOで成行の買い注文を出す前提で組みました。ただ、成行注文すると板の状況次第で想定よりも高く買ってしまうケースも出てきてしまうと思うので、今後は板の状況を取得した上で成行の場合の約定価格を予想して計算結果に組めるようにしたいですね。

最後にソースコード全文を紹介します。

import ccxt
import time
import datetime		
import csv			
from pprint import pprint
import requests
import json
import winsound
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			#指定秒数待って繰り返し
ask_exchange = 'GMO'	#購入元取引所
bid_exchange = 'bitbank'	#売却先取引所
ask_taker_fee = 0.0005	#GMOのTaker手数料
bid_maker_fee = -0.0002		#bitbankのMaker手数料
transfer_fee = 0	#送金手数料 GMOの場合無料
ask_amount = 1		#購入数量
bid_amount = ask_amount - transfer_fee	#売却数量(送金手数料差し引き後)
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)					
	#bitbank売り価格取得
	exchange = eval('ccxt.' + 'bitbank' + '()')
	orderbook = exchange.fetch_order_book ('BTC/JPY')
	bid_price = orderbook['bids'][0][0]
	#GMOコイン買い価格取得
	response = requests.get('https://api.coin.z.com/public/v1/ticker?symbol=BTC_JPY')
	json_data = response.json()
	ask_price = float(json_data["data"][0]["ask"])
	#アービトラージ計算結果を出力
	print ('買い数量=', ask_amount, 'BTC')
	print ('送金手数料=',transfer_fee, 'BTC')
	print ('売り数量=', bid_amount, 'BTC')
	arbi = bid_price*bid_amount - ask_price*ask_amount
	print (ask_exchange,'で', ask_price, 'x',ask_amount,'円で買って(Taker)')
	print (bid_exchange,'で', bid_price, 'x',bid_amount,'円で売れば(Maker)')
	print (arbi, '円の利益!')
	final_ask_taker_fee = ask_price * ask_amount * ask_taker_fee
	final_bid_maker_fee = bid_price * bid_amount * bid_maker_fee
	print ('taker手数料は',final_ask_taker_fee, '円')
	print ('maker手数料は',final_bid_maker_fee, '円')
	print ('手数料込みだと',arbi - final_ask_taker_fee - final_bid_maker_fee, '円の利益!')
	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')
		winsound.Beep(800, 300)			
		time.sleep(0.3)					
		winsound.Beep(800, 300)			
		time.sleep(0.3)					
		winsound.Beep(800, 300)			
	else:
		print('発注条件成立せず\n')	
	time.sleep(delay)

[2020/2/2追記]
こちらの記事に投稿した通り、GMOコインのAPIの銘柄にはBTCとBTC_JPYの2種類あり、BTCが現物、BTC_JPYがレバレッジ(信用)でした。なので前述のソースコード上も正しくは
response = requests.get('https://api.coin.z.com/public/v1/ticker?symbol=BTC’)
でした。
[2020/2/3追記]
もう1か所ミスが見つかりました。。メイカーとして売り注文を出すなら「ask」の価格を使う必要があります。詳しくはこちら↓
https://tsurezure.info/arbitrage/index.php/2020/02/03/post-379/?preview=true