【BitMEX】脳死BOT、2度目の進化(※バグ修正)を経て注文サイズがまともになりました
まだバグあったよ!
参考にしてる人がもしいたらすまんやで(震え声)
いやはや、実際に動かしてみるとわかることは多いですね。
良い話風に言っても現実は変わりませんけど。
ちなみにこれまでの記事はこちら。
- 【脳死BOT】一年越しくらいに雑な感じでBitMEXの自動取引BOT作ってみた|アルパカのサンドバッグ
- なんか脳死BOTがうまいことBTCの値上がりをとらえてましt……あれ、バグってる?|アルパカのサンドバッグ
内容:サイズが小さすぎ。
えっと、まず経緯としては、値動きのとらえ方的にはいい感じっぽかったんで追加入金して取引サイズを増やそうとかそんなことを思ったんですね。
それまではずっと1ドル単位での取引でしたから。
しかし今日、0.06BTC(3万円相当)くらい入金しても取引量が変わらず。
そこでようやくバグに気付く運びとなりました。
原因:頭が悪い。単位間違えてた。
原因はすぐに判明しました。
というのも、fetch_balance()
で取得できる値ってBTCの数量なんですよね。
そしてBitMEXの注文はUSD単位という……。
まともに動くはずがありません。
要するに、サイズが当初の想定より1/5000くらい小さい値になってたってことですね。
脳死してるのは僕だったといっても過言ではないでしょう。
修正:USD換算する。
というわけで、fetch_ticker()
して、USDに換算した値で計算するようにしました。
というかもうぜんぶ関数の中でよろしくやってくれれば良い気がしてきています。
というわけでちょっと見直したのがこちら。
全体的に直したからまたバグが出来ている気はしますが。
import ccxt
import datetime
import json
import logging
import time
from random import random
### 準備
# ログ
logger = logging.getLogger(__name__) # %(name)sになる
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s-%(message)s')
# 標準出力
streamHandler = logging.StreamHandler()
streamHandler.setFormatter(formatter)
# ロガーにハンドラーを追加
logger.addHandler(streamHandler)
# ログイン周りの処理
bm_keys_json = open('config/mexkeys.json', 'r')
bm_key = json.load(bm_keys_json)
bitmex = ccxt.bitmex(bm_key)
### パラメータ
# ccxt関連
symbol = 'BTC/USD'
type = 'market'
side = 'buy'
amount = 1.0
price = None
params = {}
# ログ出力条件用変数
last_stopPx = 0
loop_count = 0
# 取引条件設定
diff_triger = 5
offset_percent = 1
size_percent = 5
min_size = 1.0
loop_second = 60
### 関数
def make_position():
# 現在の所持数を取得
balance = bitmex.fetch_balance()['BTC']['total']
last_price = bitmex.fetch_ticker(symbol)['last']
# 注文数を算出
order_amount = round(balance * last_price * size_percent / 100.0)
order_amount = order_amount if order_amount >= min_size else min_size
# 向きを決定
side = 'buy' if random() > 0.5 else 'sell'
logger.info("Create ORDER..." + side + ":" + str(order_amount))
type = 'market'
price = None
params = {}
order = bitmex.create_order(symbol, type, side, order_amount, price, params)
logger.info("merket order is done. id:" + order['id'])
def make_trailstop():
type = 'stop'
# ポジションが存在するはずなのでそれを元に注文の向きを決める
cur_position = bitmex.private_get_position()[0]['currentQty']
# ポジション未成立の場合はスルー
if cur_position != 0:
reversed_side = 'sell' if cur_position > 0 else 'buy'
amount = None
price = None
# ストップの位置を決める
last_price = bitmex.fetch_ticker(symbol)['last']
# 0.5刻みじゃないとエラーになるので、1単位まで丸める
offset = (-1.0 if side == 'buy' else 1.0 ) * round(offset_percent / 100.0 * last_price)
stopPx = last_price + offset
params = {
'stopPx': stopPx,
'pegPriceType': 'TrailingStopPeg',
'pegOffsetValue': offset,
'execInst': 'Close',
}
order = bitmex.create_order(symbol, type, reversed_side, amount, price, params)
logger.info("trailing stop order is done. id:" + order['id'])
def cancel_orders(orders):
for order in orders:
bitmex.cancel_order(order['id'])
logger.info("order id:" + order['id'] + "is cancelled.")
### 処理開始
logger.info("=====GO AHEAD, BRAIN-DEAD BOT=====")
while True:
try:
# アクティブな注文の取得
orders = bitmex.fetch_open_orders()
# 現在の所持数を取得
position = bitmex.private_get_position()[0]['currentQty']
## 注文がない場合
if len(orders) == 0:
## ポジションがない場合は成行注文でポジションを作る
if position == 0:
make_position()
## いずれにしてもストップを入れる
make_trailstop()
## 注文がある場合
else:
## ポジションがない(成立しなかった)場合、キャンセルする
if position == 0:
cancel_orders(orders)
## ポジションがある場合
else:
if orders[0]['type'] == 'stop':
# 変化があればログを出力する
stopPx = str(orders[0]['info']['stopPx'])
# 流石にログが出力されなさすぎて不安なのでちょっとは出す
loop_count = loop_count + 1
if last_stopPx != stopPx or loop_count > 60:
loop_count = 0
last_stopPx = stopPx
logger.info('Current stopPx is ' + stopPx)
else:
cancel_orders(orders)
except Exception as x:
logger.info("Error!")
logger.info(x)
finally:
time.sleep(loop_second)
課題。
というわけでまた微修正されました。
動きは変わりませんが。
とりあえずストップで強制的に所持ポジションを全部手仕舞いするところがいまいちなんですよね。
他の注文できなくなるんで。
そこはちょっと見直したい……。
あとひとつ、通算成績的なものを記録する機能がほしいなとも思ってます。
脳死BOTの機能というよりは、もうひとつ常駐プログラムを動かして流れる前に注文の履歴を取得するようなかんじですかね。
fetch_orders()
だと直近100件まで取れるみたいなので、その結果をえんやこらして取得する感じで。
そのうちやりたい。
そのうち。