【zaif】自動取引botは微妙に仕様を変えて今も働いています。改めて紹介。
zaifの自動取引bot
以前にこんなのを作ってました。
- zaif apiとpythonの勉強。一定間隔でリミット注文を出し続ける自動取引botを作った|アルパカのサンドバッグ
- zaifのマイナス手数料が欲しくて作った自動取引botの現状(2018/06/19)|アルパカのサンドバッグ
この自動取引botを作ってからこっち、現在も1日に数件は取引されている状況です。
微妙にcloud9の手数料に負けている気がしてなりません。
という悲しい話はさておき、微妙に中身を変えているのでそれを紹介してみます。
更新版コード
from datetime import datetime
import time
import json
from zaifapi import *
from decimal import (Decimal)
import logging
from pprint import pprint
import zaif_custom_cancel as cancel
import sys
FILE_ID='nffn'
args = sys.argv
########################################
# ログ関連
########################################
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
handler.setLevel(logging.INFO)
fileHandler = logging.FileHandler('./log/'+FILE_ID+".log")
fileHandler.setFormatter(formatter)
logger.addHandler(handler)
logger.addHandler(fileHandler)
########################################
# ZAIFAPI
########################################
zaif_keys_json = open('config/zaifkeys.json', 'r')
zaif_keys = json.load(zaif_keys_json)
KEY = zaif_keys["key"]
SECRET = zaif_keys["secret"]
zaif = ZaifPublicApi()
trade = ZaifTradeApi(KEY, SECRET)
########################################
# ユーザ定義用変数
########################################
### 覚悟
KAKUGO = False
### 購入上限と下限(jpy)
### 購入上限と現在価格の低い方から購入加減と現在価格の1/2の高い方まで買い注文を出し揃える
### 切りの良い価格でない方がmakerになりやすい(?)かもしれない
buy_max = 900345
buy_min = 0 #使わなくなった
### 購入金額下限(可変)
buy_minus = 5000
### 1件の注文に利用する金額(jpy)
### 注文の最低単位は0.001btcなので、それ以下だと注文が不発する
### あと1000円未満の注文だとマイナス手数料が入らない
buy_jpy = 1000
### 利確値幅(jpy)
### 注文価格+これがリミット価格になる
margin = 10000
### 注文値幅(jpy)
### この価格刻みで注文を出す
separate = 10000
########################################
# 関数
########################################
def exists_active_order(orders, action, price, comment):
for order_id in orders:
if (action == orders[order_id]['action']
and price == orders[order_id]['price']
and comment == orders[order_id]['comment']):
return True
return False
def get_search_order_ids(orders, action='all', price=-1, comment='1'):
search_order_ids = []
for order_id in orders:
if (action in ['all', orders[order_id]['action']]
and price in [-1, orders[order_id]['price']]
and comment in ['*', orders[order_id]['comment']]):
search_order_ids.append(order_id)
return search_order_ids
########################################
# 処理開始
########################################
# 実行時、引数に1を指定すると同じIDの買い注文をすべてキャンセルする
if len(args) > 1:
if args[1] == '1':
logger.info('このプログラムで発注されている既存の注文をキャンセルします')
time.sleep(1)
cancel.custom_cancel_orders('bid',FILE_ID)
logger.info('キャンセル完了')
time.sleep(1)
# 最終的には処理全体をループさせる。1分周期くらいで
while True:
logger.info('確認開始')
# 各種情報取得。成功するまで続ける
while True:
try:
last_price = zaif.last_price('btc_jpy')['last_price']
funds_jpy = trade.get_info2()['funds']['jpy']
break
except Exception as ex:
logger.debug(ex)
logger.info('502か何かが出たっぽいので5秒後に再実行')
time.sleep(5)
# 初期化。
order_price = buy_max
# 覚悟がないならこの時点でLast_priceをセットすればよいのでは?
if not KAKUGO:
while order_price > last_price:
order_price -= separate
# 注文のチェック。
# 現在価格から下方無限に、separateずつ注文の有無を確認する。
# ループは以下の条件で終了する
# 1. funds_jpyがbuy_jpy以下になる(注文できないため)
# 2. order_priceがlast_price/2かbuy_min以下になる(注文範囲を超えているため)
while (funds_jpy > buy_jpy and order_price > last_price / 2 and order_price > buy_min and order_price > last_price - buy_minus):
logger.info("注文価格: %s ... 注文開始" % str(order_price))
# なんか注文がエラーになっても通ってたりすることがあるらしいので都度active_ordersを確認する
while True:
try:
# 注文を探す
active_orders = trade.active_orders(currency_pair='btc_jpy')
# 買いと売りの両方を探す。売りはコメントが付かないので誤認する可能性がある。
# どうしようもないので気にしない。
# 注文が有る場合、その価格はなにもしない。
if (exists_active_order(orders=active_orders, price=order_price, action='bid', comment=FILE_ID)
or exists_active_order(orders=active_orders, price=order_price + margin, action='ask', comment='')):
logger.info("まだ注文が残っているのでスルーします。")
else:
# 注文が無い場合は(1) リミット売りが成立している (2) エラーなどで注文が通らなかった
# のいずれかなので、注文し直す。
order_amount = Decimal(buy_jpy / order_price).quantize(Decimal('0.001'))
trade_result = trade.trade(
currency_pair = 'btc_jpy',
action = 'bid',
price = order_price,
amount = order_amount,
limit = order_price + margin,
comment = FILE_ID
)
# trade_result = trade.trade(
# currency_pair = 'btc_jpy',
# action = 'bid',
# price = order_price - 5,
# amount = 0.0001
# )
# 残高の更新
funds_jpy = trade_result['funds']['jpy']
logger.info("%sJPYに%sBTCの注文完了。残JPYは%sです。" % (str(order_price), str(order_amount), str(funds_jpy)))
break
except Exception as ex:
logger.debug(ex)
logger.info('注文が通らなかったっぽいので10秒後に注文し直します。')
time.sleep(10)
# 注文価格を引き下げて次のループへ
order_price -= separate
if funds_jpy < buy_jpy:
logger.info('日本円がないので終了します。')
if order_price < last_price / 2:
logger.info('注文可能価格を割ったので処理を終了します。')
if order_price < buy_min:
logger.info('注文価格帯を割ったので処理を終了します。')
logger.info('確認終了')
# 定期的に注文の状態を確認する
time.sleep(60)
変更点
下限を決める必要がないことに気付いた
一番大きいのがこれです。
前のやつは上限と下限を決めてその範囲に注文を並べ続ける、という感じでしたが冷静に考えて無駄だなと思いました。
というわけで常に現在の価格より少し低いところに注文を出すように変更しています。
こちらの方法の欠点は、価格が大きく動いたときにそれを拾いきれないというところですね。
無駄に含み損を増やさないということでもあるので一長一短かなとは思いますけど。
注文下限が変わったらしい
注文の下限が1桁繰り上がったらしいです。
0.0001BTCじゃなくて0.001BTCになったんだったかな。
0.001単位なのか、0.001以上(0.0011がセーフなのか?とか)なのかはよくわかっていません。
一応、それに合わせて単位の丸める桁を変えているつもりです。
なんだかんだで注文あたりの金額は千円以上を指定していたためこの制限はあまり意識していません。
設定だいぶ変わってます
1万円ごとに1000円というだいぶ気弱な設定になっています。
適宜見直してください。
それでも前述の通り1日に数件はヒットする感じなので、暗号通貨怖い……ってなります。
注意点。
数ヶ月が経過しましたので気になるところを挙げてみます。
最終的にBTCが上がると信じられる人向け。
損切りしないスタイルなので、価格には戻ってもらわないと困るというのが実情です。
最終的にBTCが10万円になる、とかいうシナリオだと悲しみに暮れることになります。
まあなんだかんだ、現在の価格は通過点でしょと思っています。
信じているわけではありません。
微妙なところ。
いずれにしても数年単位は放置できるお金以外を入れるのは避けるべきですね。
どこまで追いかけるかを考慮しておく
現物取引なんでロスカットとかそういう心配が皆無なのはいいんですけど。
それでも資金が足りなくなって低いところを追いかけきれないというのはやや悲しいですよね。
自分は50万円くらいまでは追いかけられるような設定にしています。
もちろんこの帯域を増やせば増やすほど、それぞれの価格帯での利益は薄くなるわけですが。
資金の増加を見つつ設定は定期的に見直す
課題といえば課題ですが、複利的な設定が存在しません。
預金額を増やした場合とか、気付いたら増えてた(あるといいな)場合などでも、できるのは追いかける価格帯を増やすことだけ。
現在の価格帯での利益を増やすには、1注文あたりの金額を増やすしかないです。
とはいえ利益の増え方はだいぶ気長なので、数ヶ月に1回くらいでよさそうです。
いや、そのへんは設定次第なのでアレですけど。