こんにちは、mabuiです。
こちらの記事を参考に、直近20秒の出来高を見て自動売買をするbotを作成しました。
ロジックはほぼ参考にさせていただいて、勉強用にコメントを追加したりしています。
bitflyer_scalping_volume_bot.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
from pubnub.callbacks import SubscribeCallback from pubnub.enums import PNStatusCategory from pubnub.pnconfiguration import PNConfiguration from pubnub.pnconfiguration import PNReconnectionPolicy from pubnub.pubnub import PubNub, SubscribeListener import pandas as pd from datetime import datetime, timezone, timedelta import pybitflyer import json # bitFlyer API setting public_api = pybitflyer.API() bitFlyer_keys = json.load(open('bitFlyer_keys.json', 'r')) api = pybitflyer.API(api_key=bitFlyer_keys['key'], api_secret=bitFlyer_keys['secret']) # dataframe for executions df_all = pd.DataFrame(index=['datetime'], columns=['id', 'side', 'price', 'size', 'exec_date', 'buy_child_order_acceptance_id', 'sell_child_order_acceptance_id']) # プログラム起動時のbitFlyerのポジション情報取得 bf_positions = pd.DataFrame(api.getpositions(product_code='FX_BTC_JPY')) local_position = 'NONE' local_position_price = 0 if not(bf_positions.empty): local_position = bf_positions.ix[[0], ['side']].values.flatten() local_position_price = int(bf_positions.ix[[0], ['price']].values.flatten()) sum_profit = 0 # calc buy and sell volume from lightning_executions_FX_BTC_JPY message def store_executions(channel, message, store_time_sec): # メッセージデータ取得 df_new = pd.DataFrame(message) # 約定時間を日本時間に修正 df_new['exec_date'] = pd.to_datetime(df_new['exec_date']) + timedelta(hours=9) global df_all # 取得したメッセージを追加 df_all = df_all.append(df_new) # 取得したメッセージ分含め、全てのインデックス値を約定時間の値に更新 df_all.index = df_all['exec_date'] # 最後の約定時間を現在時刻として取得 date_now = df_all.index[len(df_all) - 1] # 現在時間から指定秒数分前までのmessageデータ取得 df_all = df_all.ix[df_all.index >= (date_now - timedelta(seconds=store_time_sec))] # ロングポジションの取引のみに絞って、取引高を合計 buy_vol = df_all[df_all.apply(lambda x: x['side'], axis=1) == 'BUY']['size'].sum(axis=0) # print("buy_vol: %s" % buy_vol) # ショート(以下同) sell_vol = df_all[df_all.apply(lambda x: x['side'], axis=1) == 'SELL']['size'].sum(axis=0) # print("sell_vol: %s" % sell_vol) # 直前の約定金額 ex_price = int(df_all.ix[[len(df_all) - 1], ['price']].values.flatten()) # print("ex_price: %s" % ex_price) return df_all, buy_vol, sell_vol, ex_price # close buy or sell position def close(side, order_size, ex_price): oposit_side = 'NONE' if side == 'BUY': oposit_side = 'SELL' elif side == 'SELL': oposit_side = 'BUY' bf_positions = pd.DataFrame(api.getpositions(product_code='FX_BTC_JPY')) if not(bf_positions.empty): bf_position = bf_positions.ix[[0], ['side']].values.flatten() bf_position_price = int(bf_positions.ix[[0], ['price']].values.flatten()) if bf_position == side: print('[' + side + ' Close]') callback = api.sendchildorder(product_code='FX_BTC_JPY', child_order_type='MARKET', side=oposit_side, size=order_size) print(callback) if not(callback.get('status')): ordered_profit = 0 if side == 'BUY': ordered_profit = (ex_price - bf_position_price) * order_size elif side == 'SELL': ordered_profit = -(ex_price - bf_position_price) * order_size print('Order Complete!', 'ex_price:', ex_price, 'pos_price:', bf_position_price, 'profit:', format(ordered_profit, '.2f')) return 'NONE', ordered_profit else: return side, 0 # entry buy or sell position def entry(side, order_size): print('[' + side + ' Entry]') callback = api.sendchildorder(product_code='FX_BTC_JPY', child_order_type='MARKET', side=side, size=order_size) print(callback) if not(callback.get('status')): print('Order Complete!') return side else: return 'NONE' def received_message_task(channel, message): global local_position global local_position_price global sum_profit # 判断に使うデータを保持する秒数 store_time_sec = 20 # 注文量 order_size = 0.003 # 新規エントリーを決定する買いと売りの取引高差分 entry_triger = 0 df, buy_vol, sell_vol, ex_price = store_executions(channel, message, store_time_sec) # 利益と利益率の計算 order_profit = 0 if local_position == 'BUY': order_profit = (ex_price - local_position_price) * order_size elif local_position == 'SELL': order_profit = -(ex_price - local_position_price) * order_size order_profit_rate = order_profit / (ex_price * order_size) # 取引高の反転を見て、ポジションを閉じる if (local_position == 'BUY') and (buy_vol < sell_vol): local_position, ordered_profit = close('BUY', order_size, ex_price) sum_profit = sum_profit + ordered_profit elif (local_position == 'SELL') and (buy_vol > sell_vol): local_position, ordered_profit = close('SELL', order_size, ex_price) sum_profit = sum_profit + ordered_profit # 新規エントリー if (local_position == 'NONE'): if ((buy_vol - sell_vol) > entry_triger): local_position = entry('BUY', order_size) if local_position == 'BUY': local_position_price = ex_price elif (-(buy_vol - sell_vol) > entry_triger): local_position = entry('SELL', order_size) if local_position == 'SELL': local_position_price = ex_price # summary print(df.index[len(df) - 1].strftime('%H:%M:%S'), 'BUY/SELL', format(buy_vol, '.2f'), format(sell_vol, '.2f'), 'PRICE', ex_price, local_position, format(order_profit, '.2f'), format(order_profit_rate, '.4f'), 'SUM_PROFIT', format(sum_profit, '.2f')) # pubnub設定インスタンス config = PNConfiguration() # bitFlyer realtime api subscribe key. config.subscribe_key = 'sub-c-52a9ab50-291b-11e5-baaa-0619f8945a4f' # 3秒おきに自動で再接続する方針 https://www.pubnub.com/docs/python/reconnection-policies config.reconnect_policy = PNReconnectionPolicy.LINEAR # pubnubインスタンス生成 pubnub = PubNub(config) def main(channels): class BitflyerSubscriberCallback(SubscribeCallback): def presence(self, pubnub, presence): pass def status(self, pubnub, status): if status.category == PNStatusCategory.PNUnexpectedDisconnectCategory: pass elif status.category == PNStatusCategory.PNConnectedCategory: pass elif status.category == PNStatusCategory.PNReconnectedCategory: pass elif status.category == PNStatusCategory.PNDecryptionErrorCategory: pass def message(self, pubnub, message): """ bitFlyer → PubNub から送信されるメッセージに対して処理を実行 """ try: received_message_task(message.channel, message.message) # ストリーミング中のメッセージの出力 # print("MESSAGE: %s" % message.message) except: print('Could not do received_message_task.') listener = BitflyerSubscriberCallback() pubnub.add_listener(listener) pubnub.subscribe().channels(channels).execute() if __name__ == '__main__': # 引数に約定情報のチャンネル名を指定 main(['lightning_executions_FX_BTC_JPY']) |
先月作成した時に実際に3~4時間程実行してテストしてみましたが、どうも同じタイミングで注文が入ってくるようで、徐々に資金が減っていきましたw
一回の注文量0.003btc(2月のレートで3000円程)で回して-500円程です。
とはいえとても自動売買ツール作りの勉強になりましたので、今後に生かしたいと思います。
githubでも公開していますのでよろしければどうぞこちらへ。