◆時系列データ・データ分析編　～サンプリングのテクニック～◆

## モチベーション

株価予測をしようとしたときに、予測ラベルが重なるようなラベリングをすることも多々あります。
例えば、$n$期間の株価データ($x_t, x_{t+1}, ..., x_{t+n}$)があったとして、ラベルを$y_{t, T} = x_{t+T}/x_{t} - 1$と定義しますと、$y_t$と$y_{t+1}$では、ほぼ滑らかになりますが、$x_{t+1}, ..., x_{t+T-1}$の元は重複します。

ここで、ファイナンス機械学習の書物を参考(*p.48-p.49*)にしますと

>連続で均一で構造化されたデータセットをそのまま機械学習アルゴリズムに入力することも出来るが、一般的に2つの理由からこれはお勧めできない。
>第一に、**いくつかの機械学習アルゴリズム(たとえば、SVM)は、大きなサンプルサイズに対してうまく機能しない。**
>第二に、**機械学習アルゴリズムは、適切なサンプルサイズのみで学習を行うと最高の精度を達成する。**

例えば、上で定義したラベル($y_t$)を次のようにエンコードします。

```math

\hat{y_t} = \begin{cases}

1 & (y_t > 5\%) \\
0 & (|y_t| \leq 5\%) \\
-1 & (y_t < -5\%)
\end{cases}
```

このようなラベルでは、もしも、上のような連続的なラベルを使いますと、任意の時間から$T$期間先までに5%変動するかしないかを予測することになります。しかし、マーケットでは、例えば、オープンから30分の間であったり、クローズ前の30分間とかが出来高も含め大きくなる傾向があります。なので、ファイナンス機械学習では、特定の相場材料があった時に予測した方が精度が出ると言っているため、サンプリングは時系列データ分析においては効果的であると考えられます。

この記事では、サンプリングのテクニック及び効果について調査したいと思います。

## サンプリングの種類

サンプリングとしては次が挙げられます。

|種類  |メリット  |デメリット  |
|---|---|---|
|等間隔サンプリング  |単純さ  |初期のバーの位置によって結果が異なる  |
|一様サンプリング  | 均一に抽出できる  | 乱数によって結果が異なる |
|イベントベースサンプリング  | 特にイベントがない期間は無視できる  | イベントの基準を定義する必要があるため、専門性が高い |

ここでは、主にイベントベースサンプリングに焦点を当ててみたいと思います。

## イベント検知手法

イベント検知の方法はいくつかありまして、例えば、株式市場であれば、ニューヨークやロンドン、東京の株式市場の開場の時間、特定の経済イベント、ボラティリティの急上昇などです。

ここでは、ファイナンス機械学習のCUSUMフィルターという手法に焦点を当てたいと思います。

### CUSUMフィルター

まずは、iid観測値 $\\{y_t\\}_{t=1,...,T}$を考えます。iidとは独立同分布で、元が同じ分布に属していて、且つ互いに独立であるという意味です。最初に定義したラベルは互いに独立ではないのでご注意ください。

まずは、累積値を次のように定義します。

```math
S_t = \max\{0, S_{t-1} + y_t - E_{t-1}[y_t] \}
```

ただし、$S_0 = 0$です。
このように見ると一見難しいですが、$S_{t-1} > E_{t-1}[y_t] - y_t$の場合は$S_{t-1}$に足して、そうでない場合は何もしないだけです。

ここで、こちらの仮定に少し柔軟性を持たせて、理論を拡張しましょう。ここからはオリジナルなので、間違っていたらすみません。

まずは、$E_{t-1}[y_t] = y_{t-1}$としますと、$y_t - E_{t-1}[y_t] = y_t - y_{t-1} = \Delta{y_t}$を考えられます。このままですと、定常性が無いので、こちらを価格変化率に直します。つまり、$S_t = \max\\{0, S_{t-1} + \Delta{y_t} \\} = (S_{t-1} + \Delta{y_t})_{+}$と書き直せます。

次に、$\Delta{y_t} = y_{t} / y_{t-1} - 1$とします。価格差分から価格変化率に直すことで、イベント検知に定常性を持たせます。

次に、ローリングのwindowを$T > 0$, スレッショルドを$h \geq 0$とおき、$\displaystyle  t_+ = \\{ t| \sum_{n=t-T+1}^{t}(\Delta{y_n})^{+} \geq h \\}$を探します。



## 実証検証

実装はオリジナルのものですので、使用する際には自己責任でお願いします。

まず、イベント検知するためのデータはBinanceのBTCUSDT-spotを利用します。この時系列データの検知を行います。

![BTCUSDT_close_price.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/729216/b2b2231e-69ce-6a3b-1978-29bc78fa22c0.png)


```python
def cusum_filter(series, T, threshold):
    """
        Args:
            series (pd.Series) : series of stock price
            T      (int)       : window size
            threshold (float)  : threshold of CUSUM filter
        Returns:
            pd.DatetimeIndex
    """
    p_change = series.pct_change()
    p_change.loc[p_change < 0] = 0
    return series[p_change.rolling(T).sum() > threshold].index
```

次に使用方法です。こちらでは、10期間(10分間)で正の価格変化率の累積和が10%を超えた部分を表示します。

```python
T = 10
threshold = 0.1
events = cusum_filter(df["close"], T, threshold)
"""
events
>>>
DatetimeIndex(['2017-09-04 08:41:00', '2017-09-04 08:42:00',
               '2017-09-04 08:43:00', '2017-09-04 08:44:00',
               '2017-09-04 08:45:00', '2017-09-04 08:46:00',
               '2017-09-04 08:47:00', '2017-11-12 07:08:00',
               '2017-11-12 07:09:00', '2017-11-12 07:10:00',
               ...
               '2020-03-13 02:40:00', '2020-03-13 02:41:00',
               '2020-03-13 02:42:00', '2020-03-13 02:43:00',
               '2020-03-13 02:44:00', '2020-03-13 02:45:00',
               '2020-03-13 02:46:00', '2020-03-13 02:47:00',
               '2020-03-13 02:48:00', '2020-03-13 19:51:00'],
              dtype='datetime64[ns]', name='datetime', length=114, freq=None)
"""
```

合計で114期間見つかりました。

次に表示してみます。

![BTCUSDT_close_price_events.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/729216/a5a8b862-0eb8-be3e-b00a-1db7c6d4e95e.png)

赤い所がイベント発生時刻です。

以上になります。