Studio3104::BLOG.new

uninitialized constant Studio3104 (NameError)

ローソク足チャートに引くレジスタンスライン/サポートライン算出ロジック比較

仮想通貨自動取引実験シリーズ第三弾。前回までで Ticker を蓄積しつつそれを元にして OHLC データフレームを作ってローソク足チャートを描画するところまで出来たので、今回はそこにレジスタンスラインとサポートラインを引いてみた。例によってソースコードの解説はしない。

前回まで

TL; DR

Window Shifting でやっていくことになりそう。(ツイートに貼ってある Window Shifting のチャートはちょっとバグったロジックでプロットしてしまっている。修正済みのロジックでプロットしたチャートは下に。)

Motivation

ローソク足チャートが出来たらレジスタンスラインとサポートラインを引きたくなるのは自然な流れでしょう。レジスタンスラインとサポートラインによって得られる情報はたくさんある。ダブルトップ、ダブルボトム、三尊、逆三尊などからトレンド転換を予測出来るし、ラインをブレイクしたときにどれくらいまでを目指すのかを見極めて指値や逆指値を置く、レンジ相場の幅、などなど。チャート分析において基本のキかつ最も重要な要素のひとつであろう。

Condition

何を基準にラインを引くかは大きく3つのアプローチがある。と思う。

  • ヒゲ(高値と安値)を参考にする
  • 実体(始値終値)を参考にする
  • ヒゲも実体も両方見る

f:id:studio3104:20211109123936p:plain
https://info.monex.co.jp/technical-analysis/indicators/011.html

今回の実装では ヒゲ(高値と安値)を参考にする を前提として準備した。実体だけを見る場合だと足の中での値動きを無視することになるし、両方を見る場合は実際に引かれる線が増えたりなどしてノイズになるだろうと考えた。

Implementation

今回は3つのアルゴリズムを使ってそれぞれの実装を行い、同じ OHLC データでラインを生成してチャートにプロットして比較した。

  • Fractal Candlestick Pattern
  • k-means
  • Window Shifting

それぞれの結果は以下の15分足のチャートにプロットして例示する。

f:id:studio3104:20211109132931p:plain

Fractal

Fractal Candlestick Pattern では、時系列順に並んでいる OHLC データから5要素ずつピックアップして比較します。OHLC データ ohlc の中で要素 n を起点とする場合、ohlc[n - 2] < ohlc[n - 1] < ohlc[n] > ohlc[n + 1] > ohlc[n + 2] もしくは ohlc[n - 2] > ohlc[n - 1] > ohlc[n] < ohlc[n + 1] < ohlc[n + 2] が真であれば要素 n は Fractal であることになります。山を描く構造の場合は Bearish Fractal でレジスタンスラインの値の候補となり、逆に谷を描く構造であれば Bullish Fractal でサポートラインの値の候補となります。

f:id:studio3104:20211109121831p:plain
https://medium.datadriveninvestor.com/how-to-detect-support-resistance-levels-and-breakout-using-python-f8b5dac42f21

実装ではレジスタンスラインの値の候補はデータフレームの中から高値を参照し、サポートラインの値の候補は安値を参照して決定するようにしている。
プロット結果は以下の通り。

f:id:studio3104:20211109133215p:plain

なんか惜しい。7.37の辺りに明らかにもう一本ラインが引かれていてほしい。人力で引くなら確実にここにも引く。よく見てみると 3:30, 4:40, 14:30 あたりのデータが Fractal になっていないのである。一応 Fractal Candlestick Pattern の名誉のために言うと、足を変えればいい感じに出ることもある。以下は同じ期間の30分足。

f:id:studio3104:20211109134612p:plain

プロットしたデータを生成した実装はこちら: https://github.com/studio3104/RainMaker/blob/9e97216ea5db990b5b7273c6a205328f70f19caa/libraries/signals/support_resistance/fractal.py

k-means

k-means (k 平均法) は非階層型のクラスタリングアルゴリズムで、データをクラスタとして扱い、平均を取得して k 個に分類する。k は変数なので任意のパラメタとして与えることが出来る。
以下リンク先情報を参考にして実装に落とし込んだ:

プロット結果は以下。

f:id:studio3104:20211110213746p:plain

見てわかる通りで結果としてはイマイチ。実装はともかくアルゴリズムとしては単純で、ロジックとしてオプティマイズ出来そうな余地としては k をいくつに設定するか判定する部分になるが、いくつか値を変えてみたが結果的にはどれも微妙だった。違う期間のチャートを食わせてみたりもしたけどちょっと違う感じ。
レジスタンスラインとサポートラインを引く目的で使うのは自分としてはちょっとナシ。ただし他の用途ではもしかしたら何かしら使えるかもしれない?わからん。

プロットしたデータを生成した実装はこちら: https://github.com/studio3104/RainMaker/blob/9e97216ea5db990b5b7273c6a205328f70f19caa/libraries/signals/support_resistance/kmeans.py

Window Shifting

価格の数列上 n の先頭に期間 w のウィンドウを置き、その中で最大値(or 最小値) m を見つける。数列上でウィンドウをひとつ後ろ側にシフトし、以下繰り返す。繰り返しの中で値 mceil(w / 2) 回連続して同一だった場合はその値を l とし、ラインの候補とする。

f:id:studio3104:20211111001419p:plain
https://algo.monster/problems/sliding_window_maximum

参照する値として、レジスタンスラインの候補としては高値の最大値、サポートラインの候補として安値の最小値としている。
実際にプロットしたチャートは以下。

f:id:studio3104:20211111041115p:plain

これはほぼ完璧に見える。他の期間他の足で試してみても同様にいてほしいところにちゃんとラインがいた。今回の検証ではウィンドウサイズは固定で9を設定しているが、ここにはオプティマイズの余地がある可能性があるかもしれない。

プロットしたデータを生成した実装はこちら: https://github.com/studio3104/RainMaker/blob/9e97216ea5db990b5b7273c6a205328f70f19caa/libraries/signals/support_resistance/window_shifting.py

Conclusion

今回の検証では Window Shifting が最も良さそうであるという結果になった。サポートライン、レジスタンスラインを OHLC から求めるにおけるロジックは他にも様々アプローチがあるようだが、Window Shifting で大きな問題が生じない限りはこれをオプティマイズしながら利用していこうと考えている。

Extras

数列上でウィンドウをひとつずつ動かしながら最大値を求めていくという要件はひとめ簡単でシンプルに見えるが、実は効率よく稼働するように実装するのはまあまあ難しい。たとえば数列をイテレーションしながらそのループの中で数列からウィンドウ分をスライスするとなるとめちゃくちゃに効率が悪く、数列の長さに対してエクスポネンシャルに実行時間が伸びてしまう。バシッといい感じに投機を見極めるロジックを実装出来たとしても、実装が遅くて機を逃すなんてことがあれば本末転倒だ。

ところで「まあまあ難しい」と書いたが、実際どれくらい難しいかというと、LeetCode でいえば Hard のレベル。というかまさにまんまの問題があった。

leetcode.com

What's next?

次こそは移動平均線に着手する。まあこれはたぶんハイパーイージーだし特に苦労がなければエントリーにはしないかも。苦労したり発見があればまた書きます。移動平均線も SMA だけではなくて EMA とか、他にもバリエーションがあるので何かしら気付きはありそうな気はしている。(プロットしたチャートには SMA が載ってるが、これはチャート描画ライブラリの機能なので数値を自分で計算しているわけではないのです)