bitflyer用のBOT雛形作ったよ!pythonのソースコード(各種発注関数付)
かぴぱら(@kapipara180)です。
今回はbitflyer用のBOT雛形を作成しました。色々なストラテジーを組むうちに、ストラテジーを入れ込む型が固まってきたので公開します!
BOT雛形の構成要素は以下の通りです。ひとつでも今実装していないもの、コーディングに詰まっているものがあれば有用だと思います。(一部は無料でソースを公開!)
2020/1/15:全部無料にしました!
・保有ポジションの取得関数
def POSITION(): # 保有ポジションの方向・数量を取得
# LONGを持っている場合は建玉をプラスで返し、SHORTを持っている時はマイナスで返す
try:
ret = api.getpositions(product_code="FX_BTC_JPY")
except Exception as e:
logger.error("Error!")
logger.error(e)
return {'side': pos['side'], 'size': pos['size']}
if len(ret) == 0:
side = 'NO POSITION'
size = 0
elif ret[0]['side'] == 'BUY':
side = 'LONG'
sum = 0.0
for o in ret:
sum = sum + o['size']
size = sum
else:
side = 'SHORT'
sum = 0.0
for o in ret:
sum = sum + o['size']
size = sum * -1.0 # SHORTはポジションをマイナスとみなす
return {'side': side, 'size': size}
・残高取得関数
def BALANCE(): # 残高の取得 pnlは未実現損益 jpyは証拠金残高
try:
ret = api.getcollateral()
pnl = ret['open_position_pnl']
jpy = ret['collateral']
return {'pnl': pnl, 'jpy': jpy}
except Exception as e:
logger.error("Error!")
logger.error(e)
return {'pnl': pnl, 'jpy': jpy}
・最大ロット取得関数
def GET_LOT(price_now): # 建てられる最大ロットを取得
Last_btc_value = price_now
Free_jpy = BALANCE()['jpy']
Free_levaraged = Free_jpy * leverage
Capacity = Free_levaraged / Last_btc_value
lot = float(Decimal(Capacity).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
logger.info('ロットサイズ:%f '% lot)
return lot
・成行注文関数
・ストップ注文関数
def STOP(side, price, size): # 逆指値注文を発注
size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
price = float(Decimal(price).quantize(Decimal('0'), rounding=ROUND_DOWN)) #priceは整数
o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "STOP", "side": side, "trigger_price": price,"size": size}])
logger.info('新規STOP注文:%s %s %d %f' % (o,side,price,size))
return price
・ストップリミット注文関数
・トレーリングストップ注文関数
・ATR計算関数(簡易版)
・クロスオーバー、クロスアンダー関数
def CROSSOVER(x,y): # xがyを上抜いたことを確認する(返値はbool)
modori = x[-1] > y[-1] and x[-2] < y[-2]
return modori
def CROSSUNDER(x,y): # xがyを下抜いたことを確認する(返値はbool)
modori = x[-1] < y[-1] and x[-2] > y[-2]
return modori
・ログの出力
・cryptowatchからの1分足の取得
・1つ前が陽線なら買いエントリー、1つ前が陰線なら売りエントリーをするセットアップ(雛形なのでこの上なく簡単なロジックにしました)
・決済ロジック(LONGで持っているときに売シグナルが出た際にドテン。とその逆)
・発注ロジック(ノーポジションの場合にシグナルが出たら発注)
以上の要素を整形し、コメントもふんだんに付けました。この雛形があれば、python初心者の方でもBOT作成にチャレンジできます!BOT作成初心者の方はぜひこの雛形から学び、最強のBOTを作成してください!
プロンプトの出力結果は ↓ のようになります。
以下ソース。解説はコメントを参照!それでもわからない場合はdiscordかノートにコメントをください!返せる範囲で迅速に返します。
#!/usr/bin/env python3
# coding: utf-8
# written by kapipara180 please follow me * - > - *
import pybitflyer
import time
import datetime
import logging
from decimal import (Decimal, ROUND_DOWN)
from pytz import timezone, utc
import requests
api_key = "xxxxxx" # ご自身のAPIキーを入力
api_secret = "xxxxxx" #ご自身の秘密鍵を入力
api = pybitflyer.API(api_key=api_key, api_secret=api_secret) # pybitflyer
leverage = 15 # レバレッジ
SLEEP = 30 # 処理間隔(s)
def CUSTOMTIME(*args): # 日本時間への変換
utc_dt = utc.localize(datetime.datetime.utcnow())
my_tz = timezone("Japan")
converted = utc_dt.astimezone(my_tz)
return converted.timetuple()
def POSITION(): # 保有ポジションの方向・数量を取得
# LONGを持っている場合は建玉をプラスで返し、SHORTを持っている時はマイナスで返す
try:
ret = api.getpositions(product_code="FX_BTC_JPY")
except Exception as e:
logger.error("Error!")
logger.error(e)
return {'side': pos['side'], 'size': pos['size']}
if len(ret) == 0:
side = 'NO POSITION'
size = 0
elif ret[0]['side'] == 'BUY':
side = 'LONG'
sum = 0.0
for o in ret:
sum = sum + o['size']
size = sum
else:
side = 'SHORT'
sum = 0.0
for o in ret:
sum = sum + o['size']
size = sum * -1.0 # SHORTはポジションをマイナスとみなす
return {'side': side, 'size': size}
def BALANCE(): # 残高の取得 pnlは未実現損益 jpyは証拠金残高
try:
ret = api.getcollateral()
pnl = ret['open_position_pnl']
jpy = ret['collateral']
return {'pnl': pnl, 'jpy': jpy}
except Exception as e:
logger.error("Error!")
logger.error(e)
return {'pnl': pnl, 'jpy': jpy}
def GET_LOT(price_now): # 建てられる最大ロットを取得
Last_btc_value = price_now
Free_jpy = BALANCE()['jpy']
Free_levaraged = Free_jpy * leverage
Capacity = Free_levaraged / Last_btc_value
lot = float(Decimal(Capacity).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
logger.info('ロットサイズ:%f '% lot)
return lot
def MARKET(side, size): # 成行注文を発注
size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN)) #sizeは小数点二桁
o = api.sendchildorder(product_code="FX_BTC_JPY", child_order_type="MARKET", side=side, size=size)
logger.info('新規MARKET注文:%s %s %f' % (o, side, size))
return size # 返りは特に使わないので適当
def STOP(side, price, size): # 逆指値注文を発注
size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
price = float(Decimal(price).quantize(Decimal('0'), rounding=ROUND_DOWN)) #priceは整数
o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "STOP", "side": side, "trigger_price": price,"size": size}])
logger.info('新規STOP注文:%s %s %d %f' % (o,side,price,size))
return price
def STOP_LIMIT(side, price, size): # ストップリミット注文を発注
size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
price = float(Decimal(price).quantize(Decimal('0'), rounding=ROUND_DOWN))
o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "STOP_LIMIT", "side": side, "price": price,"trigger_price":price,"size": size}])
logger.info('新規STOP-LIMIT注文:%s %s %d %f' % (o,side,price,size))
return price
def TRAIL(side, offset, size): #トレーリングストップ注文を発注
size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
offset = float(Decimal(offset).quantize(Decimal('0'), rounding=ROUND_DOWN))
o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "TRAIL", "side": side, "size": size, "offset":offset}])
logger.info('新規TRAIL注文:%s %s %d %f' % (o,side,offset,size))
return offset
def ATR(high, low, length): # length分のatrを算出する ギャップは発生しないものとして簡略化
# high, lowは[0]が最も古く[-1]が最も新しい値が入っている配列を渡す
atr_tmp = []
for i in range(-length-1,-1): #直近の足の値は使わない
atr_tmp.append(high[i]-low[i])
atr = max(atr_tmp)
return atr
def CROSSOVER(x,y): # xがyを上抜いたことを確認する(返値はbool)
modori = x[-1] > y[-1] and x[-2] < y[-2]
return modori
def CROSSUNDER(x,y): # xがyを下抜いたことを確認する(返値はbool)
modori = x[-1] < y[-1] and x[-2] > y[-2]
return modori
# ログ出力用
logger = logging.getLogger('LoggingTest')
logger.setLevel(10)
fh = logging.FileHandler('bitflyer_xxxx.log') # ログファイル名
logger.addHandler(fh)
sh = logging.StreamHandler()
logger.addHandler(sh)
formatter = logging.Formatter('%(asctime)s %(lineno)03d: %(message)s', datefmt="%Y-%m-%d %H:%M:%S")
fh.setFormatter(formatter)
sh.setFormatter(formatter)
logging.Formatter.converter = CUSTOMTIME # 日本時間を適用
pos = {'side': 'NO POSITION', 'size': 0}
bal = {'pnl': 0, 'jpy': 0}
logger.info('========== Basic strategy Start! ==========') # 繰り返しスタート
while True:
try:
# 直近の口座の状態を取得
pos = POSITION()
bal = BALANCE()
logger.info('ポジション:%s %f' % (pos['side'], pos['size']))
logger.info('未実現損益:%d , 証拠金残高:%d' % (bal['pnl'], bal['jpy']))
#セットアップ・発注シグナルの記述***********************
now = str(int(datetime.datetime.now().timestamp())) # 現在時刻の取得
buy_entry = False # 買い注文を入れるシグナルの初期化
sell_entry = False # 売り注文を入れるシグナルの初期化
# CryptowatchのAPIで1分足を取得 periodsは秒 from,toを指定しなければ直近500本が取得できる
r = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?periods=60")
ohlcv = r.json()['result']['60'] #1:o 2:h 3:l 4:c 5:v
high = []
low = []
close = []
open = []
for i in range(len(ohlcv)): #使いやすいように分離
open.append(ohlcv[i][1])
high.append(ohlcv[i][2])
low.append(ohlcv[i][3])
close.append(ohlcv[i][4])
# 非常に簡単な例として、一つ前の足が陽線ならば買いシグナル、陰線ならば売りシグナルとする
# ここに売買ストラテジーを記述
buy_entry = close[-2] > open[-2]
sell_entry = close[-2] < open[-2]
logger.info("買シグナル:%s 売りシグナル:%s" % (buy_entry, sell_entry))
price_now = close[-1] # 一番後ろが最新
#********************************************************
#注文ロジックの記述**************************************
# ロットサイズの決定
lot = GET_LOT(price_now)
# 決済
if pos["side"] == 'SHORT' and buy_entry: # SHORTの建玉を持っている際に買いシグナルが出たら決済
entry = MARKET("BUY", abs(pos["size"])) # 成行買決済
time.sleep(10) # 注文が通るまでタイムラグがあるので待つ
if pos["side"] == 'LONG' and sell_entry: # LONGの建玉を持っている際に売りシグナルが出たら決済
entry = MARKET("SELL", abs(pos["size"])) # 成行買決済
time.sleep(10)
# 発注
if pos["side"] == 'NO POSITION' and buy_entry: # ポジションがないときに買いシグナルが出たら発注
entry = MARKET("BUY", lot)
if pos["side"] == 'NO POSITION' and sell_entry: # ポジションがないときに売りシグナルが出たら発注
entry = MARKET("SELL", lot)
# ********************************************************
logger.info('終値:%d' % price_now) # 最終価格
logger.info('') # 1サイクル終了で空行を入れる
time.sleep(SLEEP) # SLEEPで指定した時間(s)だけ待って繰り返し
except Exception as e: # 繰り返し処理の中でエラーが出た場合、エラーログを出力
logger.error("Error!")
logger.error(e)
time.sleep(5)
ご意見・ご質問はnoteのコメントへどうぞ!
この記事が気に入ったらサポートをしてみませんか?