しらべっぱなし帽子

調べっぱなしにしないためのメモ。

Pythonで仮想通貨の自動売買システムを作ってみた! 〜Zaif編〜

はじめに

最近はだいぶ下火ですが、今年1月くらいまではすごい勢いだった仮想通貨。

私も昨年の今頃は仕事そっちのけでハマってました。

FX取引(レバレッジきかせて先物空売りとかができるやつ)で8万ほどとかして目が覚めましたがw

 

そゆこともあって、

・感情に流されず売買(トレード)したい。

・短期売買をもっと頻繁かつ簡単にしたい。

と思うようになる。

じゃあプログラムで自動化してしまおう

 

やりたいこと

各取引所で公開しているAPIを利用して、プログラムから注文を自動的に実行させる。今回は大手取引所の一つである、Zaifの取引所(API)で実験。bitflyerでの売買プログラムも順次作成予定。

今回利用するZaifAPIはこちら。

corp.zaif.jp

 

自動売買プログラムに必要なスクリプトは2つ。

・売り/買い注文、キャンセル等のAPIをメソッド化した自作モジュール

・自作モジュールから適宜メソッド(API)を呼び出して、自動取引を行うプログラム(スクリプト

 

事前準備

Zaifの口座開設

・Python3の実行環境準備

 

コード

まずは売り/買い注文、キャンセル等のAPIをメソッド化した自作モジュールを作成。

### zaif_module.py ###

import hashlib
import hmac
import requests
import datetime
import json
import urllib.parse

class ZaifApi:
CURRENCY_PAIR= ""
API_KEY = ""
API_SECRET = ""
API_URL = "http://api.zaif.jp/tapi"
nonce = int((datetime.datetime.today() -datetime.datetime(2017,1,1)).total_seconds())*10

# コンストラクタ
def __init__(self, key, secret, currency_pair='btc_jpy'):
self.API_KEY = key
self.API_SECRET = secret
self.CURRENCY_PAIR = currency_pair
return

# ZaifのプライベートAPIにリクエストを送信する関数
def _private_api(self, i_params):
params = urllib.parse.urlencode(i_params);
s = hmac.new(bytearray(self.API_SECRET.encode('utf-8')), digestmod=hashlib.sha512)
s.update(params.encode('utf-8'))
headers = {'key':self.API_KEY, 'sign':s.hexdigest()}
z = requests.post(self.API_URL, data=params, headers=headers)

# 戻り値のチェック
if z.status_code != 200:
raise Exception("HTTP ERROR status={0}".format(z.status_code))
j = z.json()
if j["success"]!=1:
raise Exception("Api ERROR json={0}".format(j))
return j

# 売買を行うAPIの共通部分
def _trade_api(self, price, amount, action):
self.nonce = self.nonce + 1
j = self._private_api(
{
"method":"trade",
"currency_pair":self.CURRENCY_PAIR,
"price":price,
"action":action,
"amount":amount,
"nonce":self.nonce
})
return j

# 残高を得る関数
def balance(self):
self.nonce = self.nonce + 1
z = self._private_api({"method":"get_info2", "nonce":self.nonce})
f = z["return"]["funds"]
return {"btc":f["btc"], "jpy":f["jpy"], "xem":f["xem"], "mona":f["mona"]}

# 板情報を得る関数
def orderbook(self):
z = requests.get('https://api.zaif.jp/api/1/depth/{0}'.format(self.CURRENCY_PAIR))
if z.status_code != 200:
raise Exception("HTTP ERROR status={0}".format(z.status_code))
j = z.json()
return {"asks":[tuple(i) for i in j["asks"]], "bids":[tuple(i) for i in j["bids"]]}

# 売り注文を実行する関数
def sell(self, price, amount):
j = self._trade_api(price,amount,"ask")
return j["return"]["order_id"]

# 買い注文を実行する関数
def buy(self, price, amount):
j = self._trade_api(price, amount, "bid")
return j["return"]["order_id"]

# 注文をキャンセルする関数
def cancel(self, oid):
self.nonce = self.nonce + 1
return self._private_api({"method":"cancel_order", "order_id":oid, "nonce":self.nonce})

# 注文の状態を調べる関数
def is_active_order(self, oid):
self.nonce = self.nonce+1
j = self._private_api({"method":"active_orders", "currrency_pair":self.CURRENCY_PAIR, "nonce":self.nonce})
return str(oid) in j["return"]

 

次に自作モジュールから適宜オブジェクト(API)を呼び出して自動取引を行うプログラムを作成。

### zaif_trade.py ###
from zaif_module import ZaifApi
imoort time

# ここにキーを入力する。取扱注意!
API_KEY = ""
API_SECRET = ""

# Zaif取引所のパラメータ
order_min_size = 0.001 # BTCの数量最小値
order_digit = 4 # BTC数量の桁数
fee_rate = 0.0 # 取引手数料のレート(%)

# 1回の取引量を指定
buy_unit = 0.0014 # 購入価格
profit = 100 # 価格差

api = ZaifApi(API_KEY, API_SECRET)

while True:
print(" ### Zaif ### ")
print("BTC/JPYの自動売買取引を開始します")
time.sleep(5)

# 板情報の取得
print("板情報を取得しています")
ob = api.orderbook()

# 購入予定価格の決定(bidの先頭)
buy_price = ob["bids"][0][0]
print("購入予定価格は{0}円です".format(int(buy_price)))

# 購入数量を計算(購入数量 = 数量*(1+fee*2) - BTC残高)
balance = api.balance()

# BTCの購入数量の決定
buy_amount = round(buy_unit,order_digit)
print("BTCの購入数量は {0} です".format(buy_amount))

if buy_amount > 0:
# BTC残高が不十分なら注文の最小値を考慮して追加購入
buy_amount = max(order_min_size, buy_amount)

# JPY残高の確認
if balance["jpy"] < buy_amount*buy_price:
print("十分な日本円残高({0}円)がありません".format(balance["jpy"]))
break

# 買い注文の開始
print("買い注文を開始します")
print("購入価格 x 購入数量={0} x {1}".format(int(buy_price),buy_amount))
oid = api.buy(int(buy_price), buy_amount)
print("注文ID={0}".format(oid))

# 買い注文がサーバで処理されるまで少し待つ
print("サーバ処理中(5秒間処理待ち)")
time.sleep(5)

# さらに最大30秒間買い注文が約定するのを待つ
for i in range(0,10):
if api.is_active_order(oid) == False:
oid = None
break
print("まだ約定に時間がかかりそうです({0}回目/10回中)".format(i))
print("少々お待ちを")
time.sleep(5)

# 注文が残っていたらキャンセルする
if oid != None:
api.cancel(oid)
print("約定できなかったため注文をキャンセルしています")
print("キャンセルする注文IDは {0} です".format(oid))
time.sleep(5)
continue
else:
print("買い注文が約定されました")
else:
# 売却するBTCが既にあるなら何もしない
print("売却可能なBTCがあるので売り注文に移ります")

# 売り注文開始
print("売り注文開始します")

# BTC残高を調べる
print("売却可能なBTC残高を確認します")
balance = api.balance()
print("現在の所有しているBTC残高は {0} です".format(balance["btc"]))

# 売却数量は購入数量と一緒にする
sell_amount = buy_unit
print("BTCの売却数量は {0} です".format(sell_amount))

if sell_amount < order_min_size:
# 部分的な約定などで最小売却単位に届かないのならもう一度購入に戻る
price("十分なBTCが存在しません")
continue
else:
# 利益をのせて注文。BTCの場合はpriceを整数にする
print("{0}円の利益を上乗せして売り注文をします".format(profit))
print("売却価格+利鞘 x 売却数量={0} x {1}".format(int(buy_price+profit),sell_amount))

oid = api.sell(int(buy_price+profit), sell_amount)
print("注文ID={0}".format(oid))
time.sleep(5)

# 最大30秒間売り注文が約定するのを待つ
for i in range(0,10):
if api.is_active_order(oid) == False:
oid = None
break
print("まだ約定に時間がかかりそうです({0}回目/10回中)".format(i))
print("少々お待ちを")
time.sleep(3)

# 注文が残っていたらキャンセルする
if oid != None:
api.cancel(oid)
print("約定できなかったため注文をキャンセルしています")
print("注文ID={0}".format(oid))
time.sleep(5)

# 購入予定価格の再決定(bidの先頭)
ob02 = api.orderbook()
buy_price02 = ob02["bids"][0][0]
profit02 = 200

# 再売り注文
print("再売り注文をしています")
oid02 = api.sell(int(buy_price02+profit02), sell_amount)
print("売却数量+利益 x 売却数量={0} x {1}".format(int(buy_price02),sell_amount))
print("注文ID={0}".format(oid02))
time.sleep(5)

# 注文が成立するまで永遠に待つ
while api.is_active_order(oid02):
print("少々お待ちを")
time.sleep(3)
print("売り注文が約定されました。引き続き買い注文へ移ります")
print("注文ID={0}".format(oid02))
else:
print("売り注文が約定されました。引き続き買い注文へ移ります")
print("注文ID={0}".format(oid))

プログラムの内容は読んだもらえればわかると思うので、詳しい説明はしません。

簡単な流れとしては、

残高確認 → 買い注文 → 約定待ち → 約定完了 → 売り注文 → 約定待ち→ 約定完了 → 残高確認 ・・・

の繰り返しです。

 

最後に

bitflyer用のプログラムもつくっていきたい。

あと、今回は「買い → 売り」の順番にしか対応していない。

状況に応じて、空売りにも対応できるように実装していきたい。

 

参考資料

日経ソフトウエア 2017年 9月号 [雑誌]

日経ソフトウエア 2017年 9月号 [雑誌]

 
日経ソフトウエア 2017年11月号 [雑誌]

日経ソフトウエア 2017年11月号 [雑誌]