Pythonを使ったbitcoinアービトラージの(ほぼ)自動売買プログラムが完成しました!その②
[2020/5/5追記]ここで紹介しているプログラムは最終版ではありません。あくまで参考程度にご参照ください。最終版のプログラムは販売する形で提供させていただくことにいたしました。詳しくはこちら→https://tsurezure.info/arbitrage/index.php/2020/05/05/post-496/
こちらの記事でPythonを使ったbitcoinアービトラージの(ほぼ)自動売買プログラムを公開しましたが、いろいろと不備があり売った数量に対して買う数量が少なくなってしまう等のバグがありました。今回プログラムの見直しを行い確実に売買処理を行えるように修正しましたので再度ソースコードを公開します。今回もとりあえず貼り付けるだけで中身の説明はまた別途させてください。。
修正内容をざっくりご紹介すると、前回のプログラムではGMOでのBTC購入数量をbitbankでのBTC残高の増減を見て決めていたのですが、今回はちゃんとbitbankでの売り注文の約定履歴を確認し、約定した取引の数量から購入数量を決めるようにしました。また、Googleスプレッドシートを活用することで外出先からスマホでプログラムの稼働状況を確認したりアービトラージ条件(発注単位や発注を出す差額の条件等)を変更できるようにしました。それでは下記にソースコードをご紹介します。
こちらの記事で紹介している通りGMOで買ってbitbankで売るのが効率的ですが、前回の記事作成時点ではbitbankの方が安かったのでbitbankで買ってGMOで買うプログラムになっていました。今回は本来やりたかったGMOで買ってbitbankで売るプログラムにしています。bitbankで指値で売り注文を出し、約定した瞬間にGMOで成行買い注文をします。
それでは今回作成したプログラムのソースコードはこちらです。いつものことですが、 '*********’となっている部分はご自身のものに修正していただく必要があります。
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):
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)
i = 0
while i == 0:
try:
response = requests.get(endPoint + path, headers=headers)
json_data = response.json()
GMO_BTC = json_data['data'][pair]['available']
print('json_data=',GMO_BTC)
i = 1
except KeyError:
print('retry')
time.sleep(5)
continue
return float(GMO_BTC)
def 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)
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:
try:
size_sum = size_sum + float(json_data["data"][side][i]["size"])
except KeyError:
print('retry')
time.sleep(5)
response = requests.get(endPoint + path)
json_data = response.json()
size_sum = size_sum + float(json_data["data"][side][i]["size"])
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
#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)
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('retry get_asset')
CSVwrite(['retry get_asset'])
time.sleep(5)
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:
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:
return 0
def cancel_order(self, pair, active_order):
try:
value = self.prv.cancel_order(pair, active_order)
return value
except Exception as 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:
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):
with open(filepath+'GMO買いbitbank売りarbitrage-20200224.csv', 'a', newline="") as f:
writer = csv.writer(f)
writer.writerow(csvdata)
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()
while 1:
dt = datetime.now()
print(dt)
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:
count = 0
try:
worksheet = gfile.sheet1
except gspread.exceptions.APIError as e:
print(e)
CSVwrite([e])
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('canceled,', 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)
try:
worksheet.update_acell('B5',str(dt))
except gspread.exceptions.APIError as e:
print(e)
CSVwrite([e])
Gspreadsheet()
continue
#売却余力確認
current_BTC = prv_set.get_asset()['assets'][1]['onhand_amount']
G_current_BTC = GMO_asset(BTCasset)
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('売却BTC不足です')
CSVwrite(['売却BTC不足です'])
time.sleep(10)
continue
#購入余力確認
GMO_JPYasset=GMO_asset(JPYasset)
print('JPY資金=',GMO_JPYasset)
if 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('canceled,', active_order )
bitbank_Order(bitbank_limit_order + buffer, 'sell')
Sound()
else:
if active_order != 0:
prv_set.cancel_order(pair, active_order)
print('canceled,', 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")
Sound2()
i = i + 1
prev_trade_id = done_order[0]['trade_id']
print('prev_trade_id=',prev_trade_id)
ゼロ送金時間アービトラージ手法の紹介はこちら↓
https://tsurezure.info/arbitrage/index.php/2020/01/26/post-326/
ディスカッション
コメント一覧
まだ、コメントがありません