海外取引所から日本へ出金時の現先構築・解消自動化bot
FTXに続いてバイナンスも破綻するという噂が広まっており、たぶんないとは思いますが、念には念を入れて日本に資金を戻したいと思いました
バイナンスから日本への送金の際、価格変動リスクをなくすため、以下の方法で資金を送ることにしました
①バイナンスで現先のポジションを作る
②現物をbitbankに送金
③binanceとbitbank間で、現先を同時に閉じる。特にbinanceの流動性が低いため、時間をかけて少しずつ閉じる
この、①③を自動化するbotを公開します。取引所が違っても、少し変更すれば動くと思います
①バイナンスで現先のポジションを作る
#!/usr/bin/env python
# coding: utf-8
import datetime
from datetime import timezone
import time
import requests
import json
import ccxt
import traceback
import pprint
import asyncio
import os
def print_log(message, mode=0):
# mode
# 0 標準出力のみ
# 1 ファイル出力のみ
# 2 標準出力+ファイル出力
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if mode == 0 or mode == 2:
print(now + " " + message)
if mode == 1 or mode == 2:
filename = f"syslog_{os.path.splitext(os.path.basename(__file__))[0]}.txt"
with open(filename, 'a') as f:
print(now + " " + message, file=f)
# 概要
# binanceの先物(Perpetual)現先ポジをオープンする
# basis rate(現先乖離)が指定以上のときは待機する
#==============================================================================
# settings
#==============================================================================
interval = 5 #間隔(秒)
size = 1 #1回の買数量(×100USD(binance側の単位))
sizeMax = 1 #最大買数量(×100USD単位(binance側の単位))
#binance
exchange = ccxt.binance({
'apiKey': '',
'secret': '',
'options': {
#'defaultType': 'future', # usd-m
'defaultType': 'delivery', # coin-m
},
})
symbolFuturePos = "BTCUSD_PERP" #ポジション参照時のシンボル指定
symbolFuture = "BTC/USD" #注文時のシンボル指定
symbolSpot = "BTC/BUSD"
sizeIncrement = 100000 #現物最小注文数量0.00001
#basis rate
basis_thres = 0.0 # basisがこれ以上大きくなるまで待つ
def basisRate():
#https://binance-docs.github.io/apidocs/delivery/en/#basis-market-data
url = f"https://dapi.binance.com/futures/data/basis?pair=BTCUSD&contractType=PERPETUAL&period=5m&limit=1"
res = requests.get(url)
res.raise_for_status()
res = res.json()
#[{"indexPrice":"56845.01009091","contractType":"CURRENT_QUARTER","basisRate":"0.0681","futuresPrice":"60715.8","basis":"3870.78990909","pair":"BTCUSD","timestamp":1617805800000}]
basis = float(res[0]["basisRate"])
print_log(f"basis: {basis}", 2)
return basis
#==============================================================================
# main
#==============================================================================
exchangeSpot = ccxt.binance({
'apiKey': exchange.apiKey,
'secret': exchange.secret
})
coin = symbolSpot.split("/")[0]
# binance先物ポジション数量(BTC換算の初期数量、売りはマイナス)
def position(symbol):
position = 0
res = exchange.fetch_positions(symbols=[symbol])
print_log(pprint.pformat(res), 1)
for pos in res:
# notionalValue + unrealizedProfit=BTC換算の初期数量
pos = pos["info"]
position += float(pos["notionalValue"]) + float(pos["unRealizedProfit"])
return position
totalSize = 0
totalSpot = 0
startPosition = position(symbolFuturePos)
print_log(f"startPosition: {startPosition}", 2)
# exit(0)
while True:
while True:
try:
# if basisRate() > basis_thres:
# time.sleep(5)
# continue
break
except Exception:
print_log(traceback.format_exc(), 2)
time.sleep(5)
try:
#売り
print_log(f"sell {symbolFuture} size:{size}", 2)
res = exchange.create_market_sell_order(symbolFuture, size)
print_log(pprint.pformat(res), 1)
#注文執行完了待ち
id = res["id"]
res = exchange.fetch_order(id, symbol=symbolFuture)
print_log(pprint.pformat(res), 1)
while res["status"] != "closed":
time.sleep(1)
res = exchange.fetch_order(id, symbol=symbolFuture)
print_log(pprint.pformat(res), 1)
#先物ポジション(注文後)
positionFuture = position(symbolFuturePos)
print_log(f"positionFuture: {positionFuture}", 2)
#現物注文数量 = 先物ポジションの増分
sizeSpot = abs(positionFuture - startPosition) - totalSpot
sizeSpot = sizeSpot * sizeIncrement // 1 / sizeIncrement
if sizeSpot > 0:
# 買い
print_log(f"buy {symbolSpot} size:{sizeSpot}", 2)
res = exchangeSpot.create_market_buy_order(symbolSpot, sizeSpot)
print_log(pprint.pformat(res), 1)
# 注文執行完了待ち
id = res["id"]
res = exchangeSpot.fetch_order(id, symbol=symbolSpot)
print_log(pprint.pformat(res), 1)
while res["status"] != "closed":
time.sleep(1)
res = exchangeSpot.fetch_order(id, symbol=symbolSpot)
print_log(pprint.pformat(res), 1)
# 資金移動 spot to coin-m
print_log(f"transfer {symbolSpot} size:{sizeSpot}", 2)
res = exchangeSpot.sapiPostAssetTransfer({
"type": "MAIN_CMFUTURE",
"asset": coin,
"amount": sizeSpot,
"timestamp": int(time.time() * 1000)
# https://binance-docs.github.io/apidocs/spot/en/#trade-fee-user_data
})
print_log(pprint.pformat(res), 1)
totalSpot += sizeSpot
totalSize += size
print_log(f"total size: {totalSize}", 2)
print_log(f"total notional value future: {positionFuture - startPosition}", 2)
print_log(f"total notional value spot: {totalSpot}", 2)
if totalSize >= sizeMax:
break
except Exception:
print_log(traceback.format_exc(), 2)
break
time.sleep(interval)
③binanceとbitbank間で、現先を同時に閉じる
#!/usr/bin/env python
# coding: utf-8
import datetime
from datetime import timezone
import time
import requests
import json
import ccxt
import traceback
import pprint
import asyncio
import os
def print_log(message, mode=0):
# mode
# 0 標準出力のみ
# 1 ファイル出力のみ
# 2 標準出力+ファイル出力
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if mode == 0 or mode == 2:
print(now + " " + message)
if mode == 1 or mode == 2:
filename = f"syslog_{os.path.splitext(os.path.basename(__file__))[0]}.txt"
with open(filename, 'a') as f:
print(now + " " + message, file=f)
# 概要
# binanceの先物(Perpetual)とbitbank現物の現先ポジをクローズする
# basis rate(現先乖離)が指定以上のときは待機する
#==============================================================================
# settings
#==============================================================================
interval = 5 #間隔(秒)
size = 1 #1回の買数量(×100USD(binance側の単位))
sizeMax = 1 #最大買数量(×100USD単位(binance側の単位))
#binance
exchange = ccxt.binance({
'apiKey': '',
'secret': '',
'options': {
#'defaultType': 'future', # usd-m
'defaultType': 'delivery', # coin-m
},
})
#bitbank
exchangeSpot = ccxt.bitbank({
'apiKey': '',
'secret': '',
})
symbolFuturePos = "BTCUSD_PERP" #ポジション参照時のシンボル指定
symbolFuture = "BTC/USD" #注文時のシンボル指定
symbolSpot = "BTC/JPY"
sizeIncrement = 10000 #現物最小注文数量0.0001
#basis rate
basis_thres = 0.0 # basisがこれ以上大きくなるまで待つ
def basisRate():
#https://binance-docs.github.io/apidocs/delivery/en/#basis-market-data
url = f"https://dapi.binance.com/futures/data/basis?pair=BTCUSD&contractType=PERPETUAL&period=5m&limit=1"
res = requests.get(url)
res.raise_for_status()
res = res.json()
#[{"indexPrice":"56845.01009091","contractType":"CURRENT_QUARTER","basisRate":"0.0681","futuresPrice":"60715.8","basis":"3870.78990909","pair":"BTCUSD","timestamp":1617805800000}]
basis = float(res[0]["basisRate"])
print_log(f"basis: {basis}", 2)
return basis
#==============================================================================
# main
#==============================================================================
# binance先物ポジション数量(BTC換算の初期数量、売りはマイナス)
def position(symbol):
position = 0
res = exchange.fetch_positions(symbols=[symbol])
print_log(pprint.pformat(res), 1)
for pos in res:
# notionalValue + unrealizedProfit=BTC換算の初期数量
pos = pos["info"]
position += float(pos["notionalValue"]) + float(pos["unRealizedProfit"])
return position
totalSize = 0
totalSpot = 0
startPosition = position(symbolFuturePos)
print_log(f"startPosition: {startPosition}", 2)
# exit(0)
while True:
while True:
try:
# if basisRate() > basis_thres:
# time.sleep(5)
# continue
break
except Exception:
print_log(traceback.format_exc(), 2)
time.sleep(5)
try:
#買い
print_log(f"buy {symbolFuture} size:{size}", 2)
res = exchange.create_market_buy_order(symbolFuture, size)
print_log(pprint.pformat(res), 1)
#注文執行完了待ち
id = res["id"]
res = exchange.fetch_order(id, symbol=symbolFuture)
print_log(pprint.pformat(res), 1)
while res["status"] != "closed":
time.sleep(1)
res = exchange.fetch_order(id, symbol=symbolFuture)
print_log(pprint.pformat(res), 1)
#先物ポジション(注文後)
positionFuture = position(symbolFuturePos)
print_log(f"positionFuture: {positionFuture}", 2)
#現物注文数量 = 先物ポジションの増分
sizeSpot = abs(positionFuture - startPosition) - totalSpot
sizeSpot = sizeSpot * sizeIncrement // 1 / sizeIncrement
if sizeSpot > 0:
# 売り
print_log(f"sell {symbolSpot} size:{sizeSpot}", 2)
res = exchangeSpot.create_market_sell_order(symbolSpot, sizeSpot)
print_log(pprint.pformat(res), 1)
# 注文執行完了待ち
id = res["id"]
res = exchangeSpot.fetch_order(id, symbol=symbolSpot)
print_log(pprint.pformat(res), 1)
while res["status"] != "closed":
time.sleep(1)
res = exchangeSpot.fetch_order(id, symbol=symbolSpot)
print_log(pprint.pformat(res), 1)
totalSpot += sizeSpot
totalSize += size
print_log(f"total size: {totalSize}", 2)
print_log(f"total notional value future: {positionFuture - startPosition}", 2)
print_log(f"total notional value spot: {totalSpot}", 2)
if totalSize >= sizeMax:
break
except Exception:
print_log(traceback.format_exc(), 2)
break
time.sleep(interval)