ジオマーリン

geomerlin.com に関することを中心に。難しいことをもっと難しく書くブログ。

仮想通貨のアービトラージ(裁定取引)コードを公開します

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("オペレーションが完了しました")