richmanさんの執行戦略逆指値版スクリプトの公開
仮想通貨botter Advent Calendar 2022の記事です。
こんにちは、POTATO教授(@Pf_potato)です。
某所ではすでにコードを共有していたのですが、richmanbtcさん(@richmanbtc2)の執行戦略の逆指値版を考えたのでここで公開します。
richmanbtcさんのチュートリアルについて
仮想通貨トレードを機械学習で行うにあたってのチュートリアルをrichmanbtcさんが公開しています。機械学習で仮想通貨シストレをしたいなら一度は目を通してみるといいと思います。
そしてここでは指値戦略でのコードが公開されていますが、今回この記事ではこれを逆指値で執行するバージョンにします。
ソースコード
コードは以下のようになります。
# tutorialを単純に逆指値に置き換える
@numba.njit
def reverse_calc_force_entry_price(entry_price=None, hi=None, pips=None):
y = entry_price.copy()
y[:] = np.nan
force_entry_time = entry_price.copy()
force_entry_time[:] = np.nan
for i in range(entry_price.size):
for j in range(i + 1, entry_price.size):
if round(hi[j] / pips) > round(entry_price[j - 1] / pips):
y[i] = entry_price[j - 1]
force_entry_time[i] = j - i
break
return y, force_entry_time
def reverse_calc_y_and_cost(df, horizon, fee, pips):
# Force Entry Priceの計算
df['buy_fep'], df['buy_fet'] = reverse_calc_force_entry_price(
entry_price=df['buy_price'].values,
hi=df['hi'].values,
pips=pips,
)
# calc_force_entry_priceは入力と出力をマイナスにすれば売りに使えます
df['sell_fep'], df['sell_fet'] = reverse_calc_force_entry_price(
entry_price=-df['sell_price'].values,
hi=-df['lo'].values, # 売りのときは安値
pips=pips,
)
df['sell_fep'] *= -1
# 指値が約定したかどうか (0, 1)
df['buy_executed'] = ((df['buy_price'] / pips).round() < (df['hi'].shift(-1) / pips).round()).astype('float64')
df['sell_executed'] = ((df['sell_price'] / pips).round() > (df['lo'].shift(-1) / pips).round()).astype('float64')
# yを計算
df['y_buy'] = np.where(
df['buy_executed'],
df['sell_fep'].shift(-horizon) / df['buy_price'] - 1 - 2 * fee,
0
)
df['y_sell'] = np.where(
df['sell_executed'],
-(df['buy_fep'].shift(-horizon) / df['sell_price'] - 1) - 2 * fee,
0
)
# バックテストで利用する取引コストを計算
df['buy_cost'] = np.where(
df['buy_executed'],
df['buy_price'] / df['cl'] - 1 + fee,
0
)
df['sell_cost'] = np.where(
df['sell_executed'],
-(df['sell_price'] / df['cl'] - 1) + fee,
0
)
return df
fee = 0.0
pips = 0.01
df = pd.read_pickle('BTCUSDT_1H.pkl')
df['buy_price'] = df['cl'] + pips
df['sell_price'] = df['cl'] - pips
df = reverse_calc_y_and_cost(df, 1, fee, pips)
print('累積リターン')
df['y_buy'].cumsum().plot(label='buy')
df['y_sell'].cumsum().plot(label='sell')
plt.title('return')
plt.legend(bbox_to_anchor=(1.05, 1))
plt.show()
特に込み入った説明はしないので、雰囲気で読み取ってください。
要するに、設定した買いトリガー(buy_price)を高値がこえたら買い判定を出して、売りはその反対です。
注意点として、「逆指値」と先ほどまで説明してきましたが、実際にはトリガーが発動したら成行注文を行う想定になっています。なので、成行の執行戦略と考えてもらっていいと思います。
※逆指値は「価格が指定したトリガーをこえたら売りか買いのどちらかの指値を出す」という意味で使われることが多いです。
大雑把な説明になりましたが、もし疑問や「ここはもっとこうすればいいんじゃない?」的な指摘があればTwitterでお知らせください。
以上です。
おまけ
自分のこのコードが正しいか分からなかったので、とむいさんの某お祈りの会で相談させてもらってました。
なぜか終値から1pipだけ離してトリガーを置いただけのコードで右肩上がりのリターンを描いたので、どこか間違えているに違いないと思ったからです。
とのことです。
feeを考えなければあり得る話だそうですね。
一応私もそういうことがあるとは分かっていましたが、一時間足で1pip離して右肩上がりというのはちょっと信じがたかったので相談できてよかったです。
あまりMLライクな場所ではないにもかかわらず、場所をお貸しくださりましてありがとうございました。
Discussion