Pythonを使ったbitcoinアービトラージの(ほぼ)自動売買プログラムが完成しました!その③

5月 5, 2020

[2020/5/5追記]ここで紹介しているプログラムは最終版ではありません。あくまで参考程度にご参照ください。最終版のプログラムは販売する形で提供させていただくことにいたしました。詳しくはこちら→https://tsurezure.info/arbitrage/index.php/2020/05/05/post-496/


こちらの記事でPythonを使ったbitcoinアービトラージの(ほぼ)自動売買プログラムの修正版を公開しましたが、まだそれでもたまにエラーで止まってしまう場合があったのでさらにロジックを見直しました。今回はそちらのソースコードを公開いたします。「try」と「except」を随所にちりばめています。また、CSVへのログ書き出しもあまり必要なくなってきたのとCSVへの書き込みに失敗して止まるケースもあったのでコメントアウトしました。ソースコードのアップばかりして解説を全然できておらずすみません。

import time
from pprint import pprint
import requests
import json
import hmac
import hashlib
from datetime import datetime
import python_bitbankcc
import winsound
import sys
import csv
from oauth2client.service_account import ServiceAccountCredentials
from httplib2 import Http
import gspread
filepath = '*******************************************'
gfile = ''
def Gspreadsheet():
	global gfile
	scopes = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/spreadsheets']
	json_file = filepath+'*******************************************'
	credentials = ServiceAccountCredentials.from_json_keyfile_name(json_file, scopes=scopes)
	http_auth = credentials.authorize(Http())
	doc_id = '*******************************************'
	client = gspread.authorize(credentials)
	gfile   = client.open_by_key(doc_id)#読み書きするgoogle spreadsheet
order_amount = 0
active_order = 0 #注文ID
trade_id = 0 #取引ID
prev_trade_id = 0
current_BTC = 0
prev_BTC = 0
bitbank_limit_order = 0
prev_bitbank_limit_order = 0
pair = 'btc_jpy'
JPYasset=0
BTCasset=1	
#GMO
apiKey    = '*******************************************'
secretKey = '*******************************************'		
endPoint  = 'https://api.coin.z.com/private'
def GMOPrvCommon(method, path, text):
	timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
	text = timestamp + text
	sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()		
	headers = {
	    "API-KEY": apiKey,
	    "API-TIMESTAMP": timestamp,
	    "API-SIGN": sign
	}
	return headers
def GMO_order(order_amount,side):
	method    = 'POST'
	path      = '/v1/order'
	reqBody = {
	    "symbol": "BTC",
	    "side": side,
	    "executionType": "MARKET",
	    "size": order_amount
	}			
	text = method + path + json.dumps(reqBody)
	headers = GMOPrvCommon(method, path, text)
	res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody))
	print (json.dumps(res.json(), indent=2))
	#CSVwrite([json.dumps(res.json(), indent=2)])		
def GMO_asset(pair):
    i = 0
    while i == 0:
	    try:
	    	method    = 'GET'
	    	path      = '/v1/account/assets'
	    	text = method + path
	    	sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()
	    	headers = GMOPrvCommon(method, path, text)    
	    	response = requests.get(endPoint + path, headers=headers)
	    	json_data = response.json()
	    	GMO_asset = json_data['data'][pair]['available']
	    	i = 1
	    except Exception as e:
	    	print(e)
	    	#CSVwrite([e])
	    	print('retry json_data')
	    	time.sleep(1)
	    	continue
    return float(GMO_asset)
    
def GMO_CannotBuy():
	current_BTC_price = int(python_bitbankcc.public().get_ticker('btc_jpy')["sell"])
	GMO_JPYasset=GMO_asset(JPYasset)
	print('JPY資金=',GMO_JPYasset)
	if bitbank_amount * current_BTC_price * 2 > GMO_JPYasset:
		print('JPY資金不足です')
		#CSVwrite(['JPY資金不足です'])
		prv_set.cancel_order(pair, active_order)
		print('order canceled. order_id =', active_order)
		return True
	return False
def PriceOrder(order_amount, side):
	endPoint = 'https://api.coin.z.com/public'
	path     = '/v1/orderbooks?symbol=BTC'
	response = requests.get(endPoint + path)
	json_data = response.json()
	i = 0
	size_sum = 0
	price_sum = 0
	taker_price = 0
	while size_sum < order_amount:
		j = 0
		while j == 0:
			try:
				size_sum = size_sum + float(json_data["data"][side][i]["size"])
				j = 1
			except Exception as e:
				print(e)
				#CSVwrite([e])
				print('retry json_data')
				time.sleep(1)
				response = requests.get(endPoint + path)
				json_data = response.json()
		price_sum = price_sum + float(json_data["data"][side][i]["price"])
		taker_price = taker_price + float(json_data["data"][side][i]["price"]) * (float(json_data["data"][side][i]["size"]))
		i = i + 1
	delta = size_sum - order_amount
	return taker_price - float(json_data["data"][side][i-1]["price"]) * delta
def GMOStatus():
	i = 0
	while i == 0:
		try:
			endPoint = 'https://api.coin.z.com/public'
			path     = '/v1/status'
			response = requests.get(endPoint + path)
			json_data = response.json()["data"]["status"]
			if json_data == 'OPEN':
				i = 1
			else:	
				print('GMO closed')
				prv_set.cancel_order(pair, active_order)
				print('order canceled. order_id =', active_order)		
				time.sleep(10)	
		except Exception as e:
			print(e)
			#CSVwrite([e])
			print('retry json_data')
			time.sleep(1)
		
	return
	
#bitbank
class BitBankPubAPI:
    def get_ticker(pair):
        pub = python_bitbankcc.public()
        try:
            value = pub.get_ticker(pair)
            return value
        except Exception as e:
            print(e)
            #CSVwrite([e])
            return None
class BitBankPrvAPI:
    def __init__(self):
        API_KEY = '*******************************************'
        API_SECRET = '*******************************************'
        self.prv = python_bitbankcc.private(API_KEY, API_SECRET)
    def get_asset(self):
        try:
            value = self.prv.get_asset()
            return value
        except Exception as e:
            print(e)
            #CSVwrite([e])
            print('retry get_asset')
            #CSVwrite(['retry get_asset'])
            time.sleep(1)
            value = self.prv.get_asset()
            return value
    def order(self, pair, price, amount, side, order_type):
        try:
            value = self.prv.order(pair, price, amount, side, order_type)
            return value
        except Exception as e:
            print(e)
            #CSVwrite([e])
            return None
    def get_active_order(self, pair):
        try:
            value = self.prv.get_active_orders(pair)
            value = value['orders'][0]["order_id"]
            return value
        except Exception as e:
            print(e)
            #CSVwrite([e])
            return 0
    def cancel_order(self, pair, active_order):
        try:
            value = self.prv.cancel_order(pair, active_order)
            return value
        except Exception as e:
            print(e)
            #CSVwrite([e])
            return None
    def get_trade_history(self, pair, order_count):
        try:
            value = self.prv.get_trade_history(pair, order_count)
            return value
        except Exception as e:
            print(e)
            #CSVwrite([e])
            return None
prv_set = BitBankPrvAPI()
pub_set = BitBankPubAPI()
def bitbank_Order(price, side):
	global prev_BTC
	global current_BTC
	global prev_bitbank_limit_order
	current_BTC = prv_set.get_asset()['assets'][1]['onhand_amount']
	prev_BTC = current_BTC
	print('BTC=',current_BTC)
	prev_bitbank_limit_order = bitbank_limit_order
	amount = bitbank_amount
	order_type = 'limit'
	prv_set.order(pair, price, amount, side, order_type)
	print(pair, price, amount, side, order_type)
	#CSVwrite(['price-amount-side-order_type', price, amount, side, order_type])
def Sound():
	winsound.Beep(800, 300)
	time.sleep(0.3)
	winsound.Beep(800, 300)
	time.sleep(0.3)
	winsound.Beep(800, 300)
	time.sleep(0.3)
def Sound2():	
	winsound.Beep(1000, 1500)
def Sound3():
	winsound.Beep(900, 500)
	winsound.Beep(1000, 500)
def CSVwrite(csvdata):
    i = 0
    while i == 0:
    	try:
    		with open(filepath+'GMO買いbitbank売りarbitrage-20200224.csv', 'a', newline="") as f:		
    			writer = csv.writer(f)									
    			writer.writerow(csvdata)
    			i = 1		
    	except Exception as e:
        	print(e)
        	#CSVwrite([e])
        	print('retry CSV write')
        	time.sleep(1)
	        continue			
def GsheetUpdate(cell, data):
    i = 0
    while i == 0:
	    try:
	    	worksheet.update_acell(cell,data)
	    	i = 1
	    except Exception as e:
	    	print(e)
	    	#CSVwrite([e])
	    	print('retry update_acel')
	    	Gspreadsheet()
	    	time.sleep(1)
	    	continue
active_order = prv_set.get_active_order(pair)
prv_set.cancel_order(pair, active_order)
prev_trade_id = prv_set.get_trade_history(pair,'1')['trades'][0]['trade_id']
print('prev_trade_id=',prev_trade_id)
max_count = 5
count = max_count-1
Gspreadsheet()
SUM_GMObitbank = 0
while 1:
	dt = datetime.now()
	print(dt)
	GMOStatus()
	active_order = prv_set.get_active_order(pair)
	print('active_order = ',active_order)
	#CSVwrite([dt])
	
	#Googleスプレッドシートでパラメータ取得
	print('count=',count)
	count = count + 1
	if count == max_count:
		Gspreadsheet()
		count = 0
		i = 0
		while i == 0:
			try:
				worksheet  = gfile.sheet1
				i = 1
			except Exception as e:
				print(e)
				#CSVwrite([e])
				print('retry gfile.sheet1')
				Gspreadsheet()
				continue
		print('Loading OK')
		cell = worksheet.acell('B1').value
		print(cell)	
		if cell == '0':
			print('cell=0')
			prv_set.cancel_order(pair, active_order)
			print('order canceled. order_id =', active_order)
			time.sleep(10)
			count=max_count-1
			continue
		bitbank_amount = float(worksheet.acell('B2').value)       #発注数量
		print('order_unit=', bitbank_amount)
		margin = int(worksheet.acell('B3').value)	#差額計算時の調整用
		print('margin=',margin)
		buffer = int(worksheet.acell('B4').value)	#指値注文時の価格調整用
		print('buffer=',buffer)
		GsheetUpdate('B5',str(dt))
		GsheetUpdate('B6',str(SUM_GMObitbank))
		GsheetUpdate('B7',str(current_BTC))
		
	#売却余力確認
	current_BTC = prv_set.get_asset()['assets'][1]['onhand_amount']	
	G_current_BTC = GMO_asset(BTCasset)
	time.sleep(0.1)
	SUM_GMObitbank = float(current_BTC) + float(G_current_BTC)	
	print('SUM GMO and bitbank ',SUM_GMObitbank)
	if bitbank_amount > float(current_BTC):
		prv_set.cancel_order(pair, active_order)
		print('order canceled. order_id =', active_order)
		print('売却BTC不足です')
		#CSVwrite(['売却BTC不足です'])
		time.sleep(10)
		continue
	
	#購入余力確認
	if GMO_CannotBuy():
		count = max_count-1
		time.sleep(10)
		continue
	
	#差額計算
	price_order = PriceOrder(1, 'asks')
	bitbank_limit_order = int(python_bitbankcc.public().get_ticker('btc_jpy')["sell"])    #※指値でMaker取引をするためbuyではなくsell(売り注文)の値を取得
	print('bitbank_limit_order=',bitbank_limit_order)
	print('prev_bitbank_limit_order=',prev_bitbank_limit_order)
	price_fee = price_order * 0.0005 #GMO taker fee
	limit_fee = bitbank_limit_order * -0.0002 #bitbank maker fee
	print('テイカー手数料は',price_fee,'円')
	print('メイカー手数料は',limit_fee,'円')
	print(price_order,'円で買って(テイカー)')
	print(bitbank_limit_order,'円で売れば(メイカー)')
	profit = bitbank_limit_order - price_order - price_fee - limit_fee
	print(profit,'円の利益!')
	transfer_fee = 0			#GMOの場合送金手数料無料
	#bitbankで売却
	if profit > transfer_fee + margin:
		if active_order == 0:
			bitbank_Order(bitbank_limit_order + buffer, 'sell')
		elif prev_bitbank_limit_order - bitbank_limit_order > 0 :
			prv_set.cancel_order(pair, active_order)
			print('order canceled. order_id =', active_order)
			bitbank_Order(bitbank_limit_order + buffer, 'sell')
		Sound()			
	else:
		if active_order != 0:
			prv_set.cancel_order(pair, active_order)
			print('order canceled. order_id =', active_order)
	#GMOでの購入処理
	done_order = prv_set.get_trade_history(pair,'20')['trades']	
	i = 0
	print('done_trade_id=',done_order[i]['trade_id'])
	while prev_trade_id != done_order[i]['trade_id']:
		order_amount = done_order[i]['amount']
		print('done_order_id[',i,']=',done_order[i])
		#CSVwrite(['done_order_id[',i,']=',done_order[i]])
		print('order_amount=',order_amount)
		print('GMOで購入', order_amount)
		#CSVwrite(['GMOで購入', order_amount])
		GMO_order(order_amount,"BUY")
		GsheetUpdate('B9',str(dt))
		Sound2()
		i = i + 1
	prev_trade_id = done_order[0]['trade_id']
	print('prev_trade_id=',prev_trade_id)	
	time.sleep(1)

ゼロ送金時間アービトラージ手法の紹介はこちら↓
https://tsurezure.info/arbitrage/index.php/2020/01/26/post-326/