Botter になってみようとしたら意外な発見をしてしまった件
なんとなく Twitter をみていたら以下の記事が流れてきました。
「なにこれ面白そう!ぼくも botter になる!」と仕事終わりに Python の環境構築始めたのが月曜日。 Poetry 使うのか?とか色々やった挙句結局 Colab に戻りなんとなく動くものができたら意外な発見をしてしまったので、コードを示して説明します。
まあ単に良い値で取引をするのって大事だよね、というだけのことなんでプロの皆さんには当たり前のことなのかもしれませんが.....
Python よくわからないマンなのでおかしなところや、こういう時はこういう書き方をするんだ素人め!とかあったら是非教えてください。
環境とライブラリ
実行環境には Google Colab を使い、言語は(当然ですが) Python を使っています。 Python を選んだのは数値計算なら Python かなということと、 Haneto 氏も Python を使っているようだったからです。
ライブラリとしては以下のような数値計算の定番と、
import time
import numpy as np
import pandas as pd
%matplotlib inline
テクニカル分析のライブラリ pandas-ta
!pip install pandas_ta
import pandas_ta as ta
取引所からのデータ取得用に ccxt を利用しました。取引所としては記事中で評価の良さそうだった、 ftx を利用しました。
!pip install ccxt
import ccxt
import ccxt.async_support as ccxta
exchange = ccxt.ftx()
ccxt を使うと、 ftx からの BTC/USD の分足データの取得がこんな感じでできます。簡単ですね!
symbol = 'BTC/USD'
btcusd_ohlcv1m = exchange.fetch_ohlcv(symbol, '1m', limit=5000)
試しにいろんな指標を適用してみた
さて、このデータを整形して色々なテクニカル指標を適用してみます。正直なにがいいのかとか全然わからないのですが、とりあえずやってみました。
btcusd_ohlcv1m_df = pd.DataFrame(btcusd_ohlcv1m, columns=['time','open','high','low','close','volume'])
btcusd_ohlcv1m_df = btcusd_ohlcv1m_df.rename(index=pd.to_datetime(btcusd_ohlcv1m_df['time'], unit='ms')).drop(['time'], axis=1)
SampleStrategy = ta.Strategy(
name="sample",
ta=[
{"kind": "ema"},
{"kind": "atr"},
{"kind": "macd"},
]
)
btcusd_ohlcv1m_df.ta.strategy(SampleStrategy)
btcusd_ohlcv1m_df = btcusd_ohlcv1m_df.dropna()
pandas-ta にはまとめてテクニカル指標を適用できる Strategy という仕組みがあります。便利ですね。
MACD をテストしてみた
テクニカル指標が算出できたので、次にこの指標を元に売買のシミュレーションをしてみました。
df = btcusd_ohlcv1m_df
signal = 'MACDh_12_26_9'
quant = 0
value = 0
position = []
for i, v in df.iterrows():
time = v.index
signal1 = v['MACDh_12_26_9'] > 0
signal2 = v['MACD_12_26_9'] > 0
targetQuant = int(signal1) + int(signal2)
diffQuant = targetQuant - quant
# 最悪価格で約定と仮定
price = v['low'] if(diffQuant < 0) else v['high']
diffValue = - (diffQuant * price)
midPrice = (v['high'] + v['low']) / 2
quant += diffQuant
value += diffValue
position.append({ 'time': v.name, 'quant': quant, 'value': value, 'pl': value + quant * midPrice })
outDf = pd.DataFrame(position)
outDf = outDf.rename(outDf['time']).drop(['time'], axis=1)
# (df['close'] - df['close'][0]).plot()
outDf['pl'].plot()
この損益の結果はこんな感じ。
うーんまあそんなうまくいかないよね。こっから色々試して上手いやり方を見つけていくのかな、と最初は思っていました......
逆指標なんてない
で、じゃあこいつをどう評価したらいいんかな、とか考えていたんですよ。でも、あんまり気持ちのいい下がりっぷりじゃないですか。これと逆の売買をすれば利益出るんじゃないの?と思っちゃったんですね。以下のようにさっきのコードの targetQuant を逆転させるだけで量は逆になるので。
targetQuant = -(int(signal1) + int(signal2))
で、その結果がこちら。
おっと、さらに悪化しましたね。なんででしょうね?
高頻度 Bot やっぱ強いんじゃね?
ロジックをみていて、ひょっとして最悪の値で売買するという縛りがきつすぎるんじゃね?と気付きました。つまり、買う場合は分足の高値で、売る場合は分足の安値で売っているという風に仮定していたんですね。コードのこの部分です。
# 最悪価格で約定と仮定
price = v['low'] if(diffQuant < 0) else v['high']
diffValue = - (diffQuant * price)
そこで、これを分足の高値と安値の平均値に置き換えてみました。こんな風に。
# 中央価格で約定と仮定
midPrice = (v['high'] + v['low']) / 2
# price = v['low'] if(diffQuant < 0) else v['high']
diffValue = - (diffQuant * midPrice)
その結果がカバー画像にも載せたこちら。
ヤバいですね。2単位で大体 100,000 ドルの売買を三日間しただけで、 17,500 ドルの利益が出ています。短期間での売買ではいかに良い値で売買できるかで全てが変わることがわかりました。やっぱり高頻度 bot がいいみたいだなあ。
おわりに
今回の実験結果は自分にとっては結構面白い発見でした。同時刻の指標を使っていると先読みをしている事にならないかというツッコミどころがあるのですが。今日はもう遅いので寝ようと思います。また明日頑張ろう。