仮想通貨のアービトラージ(裁定取引)コードを公開します
Krakenでドル→仮想通貨→ユーロの変換が為替レートと大きく違う(多い時で7%ほど)ことがあったのでこの差分を抜き取るコードを書きました。実験済みです。
結果なんですが、
上げ相場のときのみ儲かりました。1回commit_plan関数を実行すると300円くらい儲かりました。
下げ相場の時は仮想通貨ホールド中に暴落するため損失をだしてしまいました。あとやはり実行中に相場が動いてしまいますね。
ぼくのコンセプトに間違いがあった可能性等ふくめ、改良案を募るためにコードを公開します。
コードの概要は、まず取引値でシミュレートし、変換候補(USD→ETH→EURなど)に目星をつけた後、板の厚さも込でシミュレートし、実行します。
USD→ETH→EUR→BTC→USDのような循環をおこない増やしていくイメージです。
エディタで見てくださいね。ソッチのほうが見やすいですから。
個人的にはKrakenの出来高が少ないのがかなり不利だと感じたので、Krakenが盛り上がったらもう一度試してみようと考えています。
import krakenex
import fx
iimport time import urllib.request import urllib.parse import json from pprint import pprint ##Kraken パブリックapiのキー apikey = apisec = ##Kraken シークレットAPIのキーです zako = zako2 = CANDIDATE = {"EUR":["XBT","REP","ETH","LTC","XLM","XRP","DASH","ETC","XMR","ZEC"],"JPY":["XBT","ETH","XRP"],"GBP":["XBT","ETH"],"CAD":["XBT","ETH","XRP"],"USD":["XBT","REP","ETH","LTC","XLM","XRP","DASH","ETC","XMR","ZEC"]} #ALTS = ["DASH/XBT","ETC/XBT","ETH/XBT","GNO/XBT","ICN/XBT","LTC/XBT","MLN/XBT","REP/XBT","XDG/XBT","XLM/XBT","XMR/XBT","XRP/XBT","ZEC/XBT",] k = krakenex.API(apikey, apisec) k2 = krakenex.API(zako, zako2) #####通貨コードの雑用的操作です###### def initialX(d): if len(d)<4: return "X"+d else: return d def lastZ(d): if len(d)<4: return "Z" else: return "" ###APIでオーダーを出す関数です def order(pair,buy_or_sell,v,price): v = str(v) price = str(price) if price=="MARKET": res = k2.query_private("AddOrder",{"pair":pair,"ordertype":"market","type":buy_or_sell,"volume":v}) else: res = k2.query_private("AddOrder",{"pair":pair,"ordertype":"limit","type":buy_or_sell,"volume":v,"price":price}) return res.get("result"),pair ###オーダーがまだあることを確認しています def check_order_done(): for i in range(5): try: orders = k2.query_private("OpenOrders") return orders["result"]["open"] except: pass ###売り板・買い板のデータを取得します def check_depth(pair): for i in range(5): try: h = k2.query_public("Depth",{"pair":pair}) return h["result"] except: pass ###オーダーキャンセルします def cancel_order(txid): k2.query_private("CancelOrder",{"txid":txid}) def cancel_all(): for key in check_order_done(): cancel_order(key) ###今の資産状況を取得します def check_balance(): for i in range(5): try: orders = k2.query_private("Balance").get("result") except: orders = False if orders: return orders def check_history(): orders = k2.query_private("TradesHistory")["result"] return orders def check_asset(): orders = k2.query_private("Ledgers",{"ofs":"1"})["result"] return orders def check_position(): positions = k2.query_private("OpenPositions")["result"] return positions ### v(フィアットのボリューム)と買い板データを入れるといくらコインをもらえるか返します def sum_orders_market(v,lst): sum_m = 0 sum_p = 0 for i in lst: i0 = float(i[0])#price i1 = float(i[1])#amount p = i0*i1 if v<sum_p+p: res = sum_m + (v-sum_p)/i0 print(res) return res else: sum_m += i1 sum_p += p ### vの通貨が一体いくらのフィアットになるか計算します def sum_sell_market(v,lst): sum_m = 0 sum_p = 0 for i in lst: i0 = float(i[0])#price i1 = float(i[1])#amount p = i0*i1 if v<sum_m+i1: res = sum_p + (v-sum_m)*i0 print(res) return res else: sum_m += i1 sum_p += p ### どの変換ルートが利益になるか判別します ###(元の法廷通貨、変換先の法廷通貨、いくら動かすか、最低何%儲かって欲しいか)が引数です def chose(currency,currency0,DOLLAR,limitrate): first = currency0[0] common_coin = list(set(CANDIDATE[currency])&set(CANDIDATE[currency0])) print(common_coin) sell_list = [i+currency for i in common_coin] buy_list=[i+currency0 for i in common_coin] pairs = ",".join(sell_list+buy_list) print("通信中") ticks = k.query_public('Ticker',{'pair':pairs})['result'] print("終了") go_score = []#USD to EUR ###fxのレートを取得します fx_rate = float(return_rate(currency+currency0)) print(fx_rate) candidate = CANDIDATE[currency] for i in common_coin: pair1 = initialX(i)+lastZ(i)+currency0 pair2 = initialX(i)+lastZ(i)+currency step1 = float(ticks[pair1]["a"][0]) dic1 = ticks[pair1]["a"] step1c = float(ticks[pair1]["c"][0]) step1d = float(ticks[pair1]["b"][0]) step1d = (step1*4+step1d*6)/10 if step1d<step1c: step1c = step1d step2 = float(ticks[pair2]["b"][0]) dic2 = ticks[pair2]["b"] step2c = float(ticks[pair2]["c"][0]) step2d = float(ticks[pair2]["a"][0]) step2d = (step2*4+step2d*6)/10 if step2d>step2c: step2c = step2d ###ここの部分は重要です ###フィアットに売り抜けないといけないので売りは成り行きを想定します go_score.append((i+":なりゆき なりゆき",step2/step1,step1,step2,pair1,pair2,2)) go_score.append((i+":指値 なりゆき",step2/step1c,step1,step2,pair1,pair2,4)) #スコアをソートします go_score.sort(key=lambda x:-float(x[1])) print("ランキング") for i in go_score[:30]: print(currency0+"→ "+i[0]+"→ "+currency+": 利益"+str((i[1]*fx_rate-1)*100)+"%"+" "+str(i[2])+" "+str(i[3])) ###ここからが本番です ###板の厚さを無視したシュミレーション結果であるgo_scoreを板の厚さ込でシュミレーションします ###Krakenは板がすごく薄いので300ドルか300ユーロの入力を想定します all_dic = {} for i in go_score[:5]: if not all_dic.get(i[4]): dic1 = check_depth(i[4])[i[4]]["asks"] all_dic[i[4]] = dic1 else: dic1 = all_dic[i[4]] if not all_dic.get(i[5]): dic2 = check_depth(i[5])[i[5]]["bids"] all_dic[i[5]] = dic2 else: dic2 = all_dic[i[5]] if i[-1]==2: first = sum_orders_market(DOLLAR,dic1) second = sum_sell_market(first,dic2) if not (first and second): continue rate = 100*(second*fx_rate-DOLLAR)/DOLLAR print(currency0+"→ "+i[0]+"→ "+currency+": 利益"+str(rate)+"%"+" "+str(i[2])+" "+str(i[3])) if rate>limitrate: return {"pair":i[4],"buy_or_sell":"buy","v":DOLLAR/i[2],"price":"MARKET"},{"pair":i[5],"buy_or_sell":"sell","v":False,"price":"MARKET"} elif i[-1]==3: first = sum_orders_market(DOLLAR,dic1) if not first: continue rate = 100*(first*i[3]*fx_rate-DOLLAR)/DOLLAR print(currency0+"→ "+i[0]+"→ "+currency+": 利益"+str(rate)+"%"+" "+str(i[2])+" "+str(i[3])) if rate>limitrate: return {"pair":i[4],"buy_or_sell":"buy","v":DOLLAR/i[2],"price":"MARKET"},{"pair":i[5],"buy_or_sell":"sell","v":False,"price":i[3]} elif i[-1]==4: second = sum_sell_market(DOLLAR/i[2],dic2) if not second: continue rate = 100*(second*fx_rate-DOLLAR)/DOLLAR print(currency0+"→ "+i[0]+"→ "+currency+": 利益"+str(rate)+"%"+" "+str(i[2])+" "+str(i[3])) if rate>limitrate: return {"pair":i[4],"buy_or_sell":"buy","v":DOLLAR,"price":i[2]},{"pair":i[5],"buy_or_sell":"sell","v":False,"price":"MARKET"} print("まだダメです") return False,False ###シュミレーションを利用して、実際に取引を行うコードです ###法廷通貨→ 仮想通貨 → もうひとつの法廷通貨 の変換を行います def commit_plan(cur0,cur1,rate,amount="All"): timeout1=10;timeout2=100 if amount=="All": amount = float(check_balance()["Z"+cur0]) print([cur0,cur1,rate,amount]) print("====発射====") timeout = 0 quest1,quest2 = chose(cur1,cur0,amount,rate) if quest1==False: return False while True: o1 = order(**quest1) if o1[0]: o1id = o1[0].get("txid") print(o1) print("is ordered") break print(o1) time.sleep(0.5) flag = True while flag: if timeout>timeout1: cancel_order(o1id) print("キャンセルしました"+o1[1]) return "DONE" res= check_order_done() if res !={}: flag=True time.sleep(1) timeout +=1 else: flag=False b = check_balance() b = b[o1[1][:4]] print("途中経過") print(b) quest2["v"]=b timeout =0 while True: o2 = order(**quest2) if o2[0]: o2id = o2[0].get("txid") break time.sleep(0.5) print(o2) flag = True while flag: res= check_order_done() if res !={}: flag=True time.sleep(1) timeout += 1 else: flag=False b = check_balance() b = b[o2[1][-4:]] print("結果") print(b) return "DONE" ###最高位の関数です。資産状況をみながらcommit_planを実行します def operation(): balance = check_balance() eur = float(balance["ZEUR"]) usd = float(balance["ZUSD"]) result = False if eur>usd: if eur>270 : result = commit_plan("EUR","USD",3,270) elif usd>300: result = commit_plan("USD","EUR",3,300) elif usd>eur: if usd>300: result = commit_plan("USD","EUR",3,300) elif eur>270: result = commit_plan("EUR","USD",3,270) return result def return_rate(pair="EURUSD"): url = "https://query.yahooapis.com/v1/public/yql" params = { "q": 'select * from yahoo.finance.xchange where pair in ("{0}")'.format(pair), "format": "json", "env": "store://datatables.org/alltableswithkeys" } url += "?" + urllib.parse.urlencode(params) res = urllib.request.urlopen(url) # 結果はJSON形式で受け取ることができます result = json.loads(res.read().decode('utf-8')) #pprint(result) rate = result["query"]["results"]["rate"]["Rate"] return rate if __name__=="__main__": while True: res = False try: res = operation() except Exception as exp: print(exp) if res == "DONE": print("オペレーションが完了しました")