未来を見てしまった滑らかな線
フィルタは、入力列 xₜ を、最近の入力と最近の出力の重み付き和として出力 yₜ に変えます。指数移動平均が最も単純で、yₜ=αxₜ+(1-α)yₜ₋₁ です。scipy.signal ではフィルタは係数リストの組で、b が入力用、a が出力用です。ここでは b = [alpha]、a = [1, -(1 - alpha)] のとき yₜ=b₀xₜ-a₁yₜ₋₁ になります。signal.lfilter(b, a, x) は実際のシステムのように時間の前向きに実行するので、yₜ は s≤t の xₛ だけに依存します。既定では静止状態(状態ゼロ)から始まり、100付近の価格では最初の値がずれます。zi = signal.lfilter_zi(b, a) * x[0] を渡せば最初の値から始められます。結果は pd.Series(x).ewm(alpha=alpha, adjust=False).mean() と機械精度で一致します。
signal.filtfilt(b, a, x) は同じフィルタを前向きと後ろ向きに実行します。遅れが打ち消されるので、滑らかな線はデータにぴったり重なり、チャート上は完璧に見えますが、各出力はそれより後の入力に依存します。売買シグナルの作成に使えばリークです。トレンドシグナル、つまり対数価格からその平均を引いたものを翌日のリターンと相関させます。SPYの2,501日、α=0.1 では、因果フィルタのシグナルの相関は -0.055(弱い平均回帰)、ゼロ位相では -0.33 です。α=0.3 では -0.076 と -0.54 です。ゼロ位相の数字は、フィルタ後の値がすでに明日の価格を含んでいるので、どのトレーダーにも稼げません。シグナルの検査は簡単で、ある日付でデータを切り詰め、それより前の値が変わらないことを確かめます。
lfilterはリアルタイム、filtfiltは後知恵:トレーディングデスクで動かせるのは前者だけ。- 初期状態を設定する:
lfilter_zi(b, a) * x[0]が立ち上がりの過渡を消す。 - 切り詰めテスト:ある日付でデータを切る。有効なシグナルはそれより前で変わらない。
ラボ:採点される内容
- フィルタ係数10点
- 因果的な平均20点
- pandas版10点
- ゼロ位相の平均15点
- シグナルと翌日のリターン25点
- SPYの研究20点