
仮想通貨bot 勉強記録⑯
~赤三兵・黒三兵botでバックテストをする~
◆前回までのあらすじ
試作botが動くようになりました。やったぜ。
◆今回やること
・バックテスト用のコードを作る
書いたコードがこちら
from datetime import datetime,timedelta
import time
from rich import print as pp
import pybybit
"""------------------------"""
apis = [
'プライベートキー',
'シークレットキー'
]
bybit = pybybit.API(*apis, testnet=True)
"""------------------------"""
t = int((datetime.now() - timedelta(hours=4)).timestamp())
min = 1
response = bybit.rest.inverse.public_kline_list(
symbol = "BTCUSD",
interval= min,
from_= t
).json()
#ローソク足情報を返す関数
def get_price(min,i):
data = response['result'][i]
#返り値取得
return { "close_time" : data['open_time'],
"open_price" : float(data['open']),
"high_price" : float(data['high']),
"low_price" : float(data['low']),
"close_price" : float(data['close'])}
#画面出力
def print_price(data):
pp( " 時間: " + datetime.fromtimestamp(data['close_time']).strftime('%Y/%m/%d %H:%M')
+ " 始値: " + str(data['open_price'])
+ " 終値: " + str(data['close_price']))
#ローソク足の条件判別
def check_candle(data,side):
try:
#実体割合の算出
realbody_rate = abs(data["close_price"] - data["open_price"]) / (data["high_price"]-data["low_price"])
except ZeroDivisionError:
increase_rate = 0
try:
#実体の大きさの算出
increase_rate = (data["close_price"] / data["open_price"]) - 1
except ZeroDivisionError:
increase_rate = 0
if side == "buy":
if data["close_price"] < data["open_price"] : return False #ローソク足が赤だったらFalse
elif increase_rate < 0.0001 : return False #実体の大きさが現在価格の0.01%未満ならFalse
elif realbody_rate < 0.1 : return False #実体の割合がローソク足の10%未満ならFalse
else : return True #上記すべて条件が当てはまらなければTrue
if side == "sell":
if data["close_price"] > data["open_price"] : return False #ローソク足が緑だったらFalse
elif increase_rate > -0.0001 : return False #実体の大きさが現在価格の0.01%未満ならFalse
elif realbody_rate < 0.1 : return False #実体の割合がローソク足の10%未満ならFalse
else : return True #上記すべて条件が当てはまらなければTrue
#ローソク足の連続上昇の判別
def check_ascend( data,last_data ):
#今回の始値が前回の始値を上回っている且つ今回の終値が前回の終値を上回っていればTure
if data["open_price"] > last_data["open_price"] and data["close_price"] > last_data["close_price"]:
return True
else: return False
#ローソク足の連続下降の判別
def check_descend( data,last_data ):
#今回の始値が前回の始値を下回っている且つ今回の終値が前回の終値を下回っていればTure
if data["open_price"] < last_data["open_price"] and data["close_price"] < last_data["close_price"]:
return True
else:
return False
#買いサイン・注文を出す関数
def buy_signal( data,last_data,flag ):
if flag["buy_signal"] == 0 and check_candle( data,"buy" ):#陽線1本目
flag["buy_signal"] = 1
elif flag["buy_signal"] == 1 and check_candle( data,"buy" ) and check_ascend( data,last_data ):#陽線2本目
flag["buy_signal"] = 2
elif flag["buy_signal"] == 2 and check_candle( data,"buy" ) and check_ascend( data,last_data ):#陽線3本目
pp("3本連続で陽線 なので" + str(data["close_price"]) + "で買い指値")
flag["buy_signal"] = 3
"""指値買い注文コード"""
flag["order"]["exist"] = True
flag["order"]["side"] = "BUY"
#陽線が途切れたらシグナルリセット
else:
flag["buy_signal"] = 0
return flag
#売りサイン・注文を出す関数
def sell_signal( data,last_data,flag ):
if flag["sell_signal"] == 0 and check_candle( data,"sell" ):#陰線1本目
flag["sell_signal"] = 1
elif flag["sell_signal"] == 1 and check_candle( data,"sell" ) and check_descend( data,last_data ):#陰線2本目
flag["sell_signal"] = 2
elif flag["sell_signal"] == 2 and check_candle( data,"sell" ) and check_descend( data,last_data ):#陰線3本目
pp("3本連続で陰線 なので" + str(data["close_price"]) + "で売り指値")
"""指値売り注文コード"""
flag["order"]["exist"] = True
flag["order"]["side"] = "SELL"
#陰線が途切れたらシグナルリセット
else:
flag["sell_signal"] = 0
return flag
#決済用の関数
def close_position( data,last_data,flag ):
if flag["position"]["side"] == "BUY":
if data["close_price"] < last_data["close_price"]:
pp("前回の終値を下回ったので" + str(data["close_price"]) + "あたりで成行で決済します")
"""成行売り注文コード"""
flag["position"]["exist"] = False
if flag["position"]["side"] == "SELL":
if data["close_price"] > last_data["close_price"]:
pp("前回の終値を上回ったので" + str(data["close_price"]) + "あたりで成行で決済します")
"""成行買い注文コード"""
flag["position"]["exist"] = False
return flag
#注文状況確認用の関数
def check_order( flag ):
"""注文状況確認コード"""
flag["order"]["exist"] = False
flag["order"]["count"] = 0
flag["position"]["exist"] = True
flag["position"]["side"] = flag["order"]["side"]
return flag
#メイン関数
def main():
last_data = get_price(1,0)
print_price( last_data )
flag = {
"buy_signal":0,
"sell_signal":0,
"order":{
"exist" : False,
"side" : "",
"count" : 0},
"position":{
"exist" : False,
"side" : ""}
}
i = 0
while i<200:
if flag["order"]["exist"]:
flag = check_order( flag )
data = get_price(1,i)
print_price( data )
if flag["position"]["exist"]:
flag = close_position( data,last_data,flag )
else:
flag = buy_signal( data,last_data,flag )
flag = sell_signal( data,last_data,flag )
last_data["close_time"] = data["close_time"]
last_data["open_price"] = data["open_price"]
last_data["close_price"] = data["close_price"]
i+=1
main()
勉強記録⑫で作ったテストコードを改良し、売りエントリーにも対応しました。実際に注文は行わないので、Bybitに注文したり決済したりするコードは書いていません。
◆解説
今回は解説無しです!!!!(すまん)
前回のコードをバックテスト用にいじっているだけなので、勉強記録⑪、勉強記録⑫を見ればコードの内容が分かるはず。
◆結果
動かすとこんな感じになります。
アスペクト比おかしくなってしもうた。。。
ちゃんと売り買い両方の指値注文と、決済をやってくれてます。
しかし、このコードだとBybitのローソク足が取得できる最大200本分しかテストが行えません。
動作確認のテストだけなら問題ないんですが、損益確認のテストとしては情報が足りないです。
なので次回は1回でローソク足を大量に取得するコードを作成します。