日足データ拡張

2021年5月、richmanbtc先生がnoteに[平均足を使った戦略](https://note.com/btcml/n/n6198a3714fe5)を投稿したことで、すこしのあいだ日足戦略がbotter界隈でトレンドになりました。
当時駆け出しbotterでまだ一円も稼げなかった私も血眼になって戦略を探しました。
その結果、どの期間とっても一ヶ月で2~10倍になるというかなり強い戦略を見つけましたが、うきうきでbotを実装しているときに**めっちゃリークしている**と知り、がっかりしたほろ苦い駆け出しの思い出があります。

# 長い取引時間軸
仮想通貨botterの中でポジションを1日以上持ち続ける戦略を採用している人は少ないと思います。ポジションを保持する時間を伸ばすメリットは、値幅とりやすい、ロット積みやすいなどあり、デメリットはデータが少ないこと、良さそうな戦略が見つかったとしても偽陽性の可能性が高い、期待値に収束するのに時間がかかることがあげられます。

日足の場合、一年で365個しかデータが取れないわけです。うまくデータ拡張したとしても取引回数は増やせないため、期待値収束のデメリットは避けようがありません。


# 取引回数を増やすアイデア
仮想通貨界では日足とはUTC0時始まりの24時間のローソク足のことを言います。日本時間では午前9時はじまりの足ですね。
ほとんどの取引所でそうなってるのでが、GMOコインさんはなんと日本時間午前6時始まりの足を返してくるという謎の独自仕様になってます。
でもたしかに、GMOコインさんの通り、UTC0時以外で日足作ってみてもいいんじゃない？

というわけでUTC1時、2時、…、23時の各時刻の日足を作成し、24倍にデータ拡張、エントリータイミングも24倍にする手法を考えます。

# 検証
データはbinanceのBTCUSDT、手数料未考慮。
各足の終値でロングorショートをエントリーして次の足の終値で決済する戦略を考えます。
この戦略を簡単なMLモデル（バイナリ分類）で予測します。
01分類で各タイミングで必ずエントリーするパターンと、predict_probaを使って、モデルの確信度が高い時だけエントリーするパターンを実験します。

長くなっちゃうのでget_ohlcv1d, calc_features, cvなどのコードは省略しますが、それぞれいい感じに定義。get_ohlcv1d(i)でi時間ずらした日足をgetできる。cvはクロスバリデーションの結果を返す（パージングでリークしないようにしている）。

### UTC0時の日足のみ使う
```python
df = get_ohlcv1d(0)
df["y"] = df["close"].shift(-1) / df["close"] - 1
df["sign"]=np.where(df["y"]>0,1,0)
df = calc_features(df)
df["y_pred"] = cv(df,features,"sign")
df["pnl"]=np.where(df["y_pred"]>0,df["y"],-df["y"])
df["pnl"].cumsum().plot()

df["y_pred_proba"] = cv_proba(df,features,"sign")
df["pnl_proba"]=np.where(df["y_pred_proba"]>0.7,df["y"],0)
df["pnl_proba"]=np.where(df["y_pred_proba"]<0.3,-df["y"],df["pnl_proba"])
df["pnl_proba"].cumsum().plot()
plt.legend()
plt.show()
```
![スクリーンショット 2023-12-17 13.31.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2414890/49cb72bb-64c1-c85d-ea13-ab34e4a7797f.png)

### 各時刻はじまりの日足を使う
```python
df24 = pd.DataFrame()
for i in range(24):
    df = get_ohlcv1d(i)
    df["y"] = df["close"].shift(-1) / df["close"] - 1
    df["sign"]=np.where(df["y"]>0,1,0)
    df = calc_features(df)
    df24 = pd.concat([df24, df])
df24=df24.sort_values(by="timestamp")
df24["y_pred"] = cv(df24,features,"sign")
df24["pnl"]=np.where(df24["y_pred"]>0,df24["y"],-df24["y"])
df24["pnl"].cumsum().plot()

df24["pnl_proba"]=np.where(df24["y_pred_proba"]>0.7,df24["sign"],0)
df24["pnl_proba"]=np.where(df24["y_pred_proba"]<0.3,-df24["y"],df24["pnl_proba"])
plt.legend()
plt.show()
```
![スクリーンショット 2023-12-17 13.31.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2414890/b4bc3a3a-adc7-d75a-3fee-83ec413a1793.png)
（これって、1時間ごとエントリー判断して、ポジションを24時間保持する戦略ってだけだな）

正直良くわかりません。
ただ、試行回数増やす工夫をする→その上で厳選してエントリーする、みたいな工夫はそれなりにワークする可能性あります。

# 気づき
こういった「ずらし足」を使うには注意が必要です。

はじめに紹介したrichmanbtc先生のnoteでも言及されてる平均足順張り、ローソク足逆張りの戦略は2021年前半まではかなりワークします。要は平均足でトレンド判断して、押し目買い＆戻り売りをする戦略です。
```python
def calc_heikin(ohlcv):
    cl = .25 * (ohlcv["open"] + ohlcv["high"] + ohlcv["low"] + ohlcv["close"])
    op = cl.ewm(1, adjust=False).mean().shift(1)
    return op,cl
df=get_ohlcv1d(0)
df["y"]=df["close"].shift(-1)/df["close"]-1
op,cl=calc_heikin(df)
df["signal"] = np.where((op<cl)&(df["open"]>df["close"]),1,0)
df["signal"] = np.where((op>cl)&(df["open"]<df["close"]),-1,df["signal"])
df["pnl"] = df["signal"] * df["y"]
df["pnl"].cumsum().plot()
plt.legend()
plt.show()
```
![スクリーンショット 2023-12-17 13.49.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2414890/420beac7-5d7e-1b64-bea4-16eeaf211d80.png)

しかし12時間ずらした日足で同じことをするとまったく効かなくなります。
![スクリーンショット 2023-12-17 13.51.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2414890/e57773ea-63b1-b1d3-459f-6d1f07ee6712.png)

日足の戦略を探す上で、他にもUTC0時の日足で効きそうだなと思った戦略も時間をずらした日足だと効かないといったことが多々ありました。
「チャート分析が効くのは、みんながチャートを見ているから」と言われることがあります。これを当てはめて考えれば、時間をずらした日足は誰も見てないので、効きようがありません。通常の日足と比べるとパフォーマンスは落ちると思われます。