エヴァチャート(周期推定オシレータ)

目次
・概要
・スクリプト
・理論1:ディジタルフィルタを作り周波数分離をする
・理論2:最大エネルギーの周波数を特定する
・過去データの検証
・その他、便利な使い方

概要

 こんにちは。今日は自分の作ったエヴァチャートオシレーターについて理論を説明しようと思います。今年からBTCにジョインしたばかりの赤字出まくってる新参者ですが、新しいオシレーターを作ってみました。
 エヴァチャートオシレーターの強みは、なんといっても「周期の推定」が出来ることです。周期の推定が出来ると、トレンドやウェッジの終わり時がわかり、いつまでロング・ショートをし続ければいいかが分かります。スイングトレードに有利になると考えて作りました。

スクリプト

 スクリプトは以下となります。TradingViewに貼り付けてお使い下さい。

※2つのインディケーターを使わなくても大丈夫な30本版に修正しました。

//@version=3
study(title="EvaMacD", shorttitle="EMD")
inputLen = input(30, title="長さ", minval=0)
inputFlag0 = input(true, title="ライン0")
inputFlag1 = input(true, title="ライン1")
inputFlag2 = input(true, title="ライン2")
inputFlag3 = input(true, title="ライン3")
inputFlag4 = input(true, title="ライン4")
inputFlag5 = input(true, title="ライン5")
inputFlag6 = input(true, title="ライン6")
inputFlag7 = input(true, title="ライン7")
inputFlag8 = input(true, title="ライン8")
inputFlag9 = input(true, title="ライン9")
inputFlag10 = input(true, title="ライン10")
inputFlag11 = input(true, title="ライン11")
inputFlag12 = input(true, title="ライン12")
inputFlag13 = input(true, title="ライン13")
inputFlag14 = input(true, title="ライン14")
inputFlag15 = input(true, title="ライン15")
inputFlag16 = input(true, title="ライン16")
inputFlag17 = input(true, title="ライン17")
inputFlag18 = input(true, title="ライン18")
inputFlag19 = input(true, title="ライン19")
inputFlag20 = input(true, title="ライン20")
inputFlag21 = input(true, title="ライン21")
inputFlag22 = input(true, title="ライン22")
inputFlag23 = input(true, title="ライン23")
inputFlag24 = input(true, title="ライン24")
inputFlag25 = input(true, title="ライン25")
inputFlag26 = input(true, title="ライン26")
inputFlag27 = input(true, title="ライン27")
inputFlag28 = input(true, title="ライン28")
inputFlag29 = input(true, title="ライン29")

pecd(inputFlag, length, colorNum) => 
    [_, _, histLine] = macd(hlc3, length, 2 * length, length)
    result = security(tickerid, period, histLine)
    color = if colorNum == 0 
        inputFlag ? change(histLine) > 0  ? #adff2f : #ee82ee : black
    else 
        if colorNum == 1 
            inputFlag ? change(histLine) > 0  ? #00fa9a : #9370db : black
        else 
            if colorNum == 2 
                inputFlag ? change(histLine) > 0  ? #40e0d0 : #ff00ff : black
    [result, color]

[r0, c0] = pecd(inputFlag0, inputLen + 0, 0)
[r1, c1] = pecd(inputFlag1, inputLen + 1, 0)
[r2, c2] = pecd(inputFlag2, inputLen + 2, 0)
[r3, c3] = pecd(inputFlag3, inputLen + 3, 0)
[r4, c4] = pecd(inputFlag4, inputLen + 4, 0)
[r5, c5] = pecd(inputFlag5, inputLen + 5, 1)
[r6, c6] = pecd(inputFlag6, inputLen + 6, 1)
[r7, c7] = pecd(inputFlag7, inputLen + 7, 1)
[r8, c8] = pecd(inputFlag8, inputLen + 8, 1)
[r9, c9] = pecd(inputFlag9, inputLen + 9, 1)
[r10, c10] = pecd(inputFlag10, inputLen + 10, 2)
[r11, c11] = pecd(inputFlag11, inputLen + 11, 2)
[r12, c12] = pecd(inputFlag12, inputLen + 12, 2)
[r13, c13] = pecd(inputFlag13, inputLen + 13, 2)
[r14, c14] = pecd(inputFlag14, inputLen + 14, 2)
[r15, c15] = pecd(inputFlag15, inputLen + 15, 0)
[r16, c16] = pecd(inputFlag16, inputLen + 16, 0)
[r17, c17] = pecd(inputFlag17, inputLen + 17, 0)
[r18, c18] = pecd(inputFlag18, inputLen + 18, 0)
[r19, c19] = pecd(inputFlag19, inputLen + 19, 0)
[r20, c20] = pecd(inputFlag20, inputLen + 20, 1)
[r21, c21] = pecd(inputFlag21, inputLen + 21, 1)
[r22, c22] = pecd(inputFlag22, inputLen + 22, 1)
[r23, c23] = pecd(inputFlag23, inputLen + 23, 1)
[r24, c24] = pecd(inputFlag24, inputLen + 24, 1)
[r25, c25] = pecd(inputFlag25, inputLen + 25, 2)
[r26, c26] = pecd(inputFlag26, inputLen + 26, 2)
[r27, c27] = pecd(inputFlag27, inputLen + 27, 2)
[r28, c28] = pecd(inputFlag28, inputLen + 28, 2)
[r29, c29] = pecd(inputFlag29, inputLen + 29, 2)

plot(inputFlag0 ? r0 : 0, "Histogram", c0, 1, line)
plot(inputFlag1 ? r1 : 0, "Histogram", c1, 1, line)
plot(inputFlag2 ? r2 : 0, "Histogram", c2, 1, line)
plot(inputFlag3 ? r3 : 0, "Histogram", c3, 1, line)
plot(inputFlag4 ? r4 : 0, "Histogram", c4, 1, line)
plot(inputFlag5 ? r5 : 0, "Histogram", c5, 1, line)
plot(inputFlag6 ? r6 : 0, "Histogram", c6, 1, line)
plot(inputFlag7 ? r7 : 0, "Histogram", c7, 1, line)
plot(inputFlag8 ? r8 : 0, "Histogram", c8, 1, line)
plot(inputFlag9 ? r9 : 0, "Histogram", c9, 1, line)
plot(inputFlag10 ? r10 : 0, "Histogram", c10, 1, line)
plot(inputFlag11 ? r11 : 0, "Histogram", c11, 1, line)
plot(inputFlag12 ? r12 : 0, "Histogram", c12, 1, line)
plot(inputFlag13 ? r13 : 0, "Histogram", c13, 1, line)
plot(inputFlag14 ? r14 : 0, "Histogram", c14, 1, line)
plot(inputFlag15 ? r15 : 0, "Histogram", c15, 1, line)
plot(inputFlag16 ? r16 : 0, "Histogram", c16, 1, line)
plot(inputFlag17 ? r17 : 0, "Histogram", c17, 1, line)
plot(inputFlag18 ? r18 : 0, "Histogram", c18, 1, line)
plot(inputFlag19 ? r19 : 0, "Histogram", c19, 1, line)
plot(inputFlag20 ? r20 : 0, "Histogram", c20, 1, line)
plot(inputFlag21 ? r21 : 0, "Histogram", c21, 1, line)
plot(inputFlag22 ? r22 : 0, "Histogram", c22, 1, line)
plot(inputFlag23 ? r23 : 0, "Histogram", c23, 1, line)
plot(inputFlag24 ? r24 : 0, "Histogram", c24, 1, line)
plot(inputFlag25 ? r25 : 0, "Histogram", c25, 1, line)
plot(inputFlag26 ? r26 : 0, "Histogram", c26, 1, line)
plot(inputFlag27 ? r27 : 0, "Histogram", c27, 1, line)
plot(inputFlag28 ? r28 : 0, "Histogram", c28, 1, line)
plot(inputFlag29 ? r29 : 0, "Histogram", c29, 1, line)

理論1:ディジタルフィルタを作り周波数分離をする

画像・音を解析する世界では、周期の分析を「ディジタル信号処理」にて行います。詳細は以下などで勉強すると良いです。

 この世界では時間tを周波数fの世界に変換して考えます。基本的な計算式は、f=1/t となります。例えばオーディオ信号の時間データを周波数の世界に変換すると以下のようになります。

 信号からある一定の周波数のデータのみを抽出したい時、高い周波数、低い周波数のみを抽出したいときは、「ディジタルフィルタ」という技術を使い、フィルタリングをしてあげます。イメージとしては、高音のシンバルの音のみを取り出す、低音のベースの音のみを取り出すようなイメージです。具体的には、チャートを時間軸から周波数軸に変換した時に以下のような周波数の極大点を特定するという事を目指します。強い周波数は強い周期を意味します。つまり、現在のチャート波形の周期が推定しやすくなります。


 TradingViewでは、フーリエ変換を行うことができないため、チャートの周波数を見ることができませんが、フィルタを作ることはできました。主にディジタルフィルタには「FIRフィルタ」「IIRフィルタ」の二種類がありますが、ここでは簡易的なローパスフィルタを「IIRフィルタ」で作ることを目指します。IIRフィルタの式は以下となります。(この時の伝達関数 H(z) は省略します。)

 詳しくは、以下のページを読むとテクニカル分析が好きな方はよく理解できるのではないかと思います。

 この式の変数をいじり、指数的にy(n)に影響するようにしてあげます。

 さて、この式に見覚えはありませんでしょうか。これはテクニカル分析で有名な指数移動平均(EMA)そのものの式なのです。そしてこれがローパスフィルタになります。指数移動平均フィルタ(ローパスフィルタ)は、データ量が多いほど低い周波数を抽出し、高い周波数を遮断することができます。ローパスフィルタについては以下を参考にして下さい。


 つまり、時間軸の単位が1日など大きいときはより低い周波数、言い換えると大きな周期を抽出することができます。時間軸の単位が1分など小さいときは、より高い周波数、言い換えると小さな周期を抽出することができます。

 ここで、2つの移動平均を引き算します。

 ここで、左側のローパスフィルタの遮断周波数が高く、右側のローパスフィルタの遮断周波数が低い時、これはローパスフィルタ1をかけた時間軸データからローパスフィルタ2をかけた時間軸データの引き算となり、結果、「バンドパスフィルタ」がかけられた結果を生成できます。このバンドパスフィルタを使って、特定の周波数を抽出できます。

 ところで、指数移動平均EMAの引き算ってどこかで見覚えがありませんでしょうか?これは「MACD」です。さらに「MACDヒストグラム」ではさらに移動平均をかけたシグナルを引き算しています。この行為は、平滑化された、つまりローパスフィルタをかけられたデータを引き算する行為となり、結果、「ハイパスフィルタ」になります。つまりMACDヒストグラムを使うとこれによって、更に強いハイパスフィルタをかけられたデータが自動的に出来上がります。

 また、時間軸が大きいデータ(例えば1日単位など)のデータは1日分の動きがギュッと圧縮されています。これは細かい動きを潰し高い周波数を遮断しているため、「ローパスフィルタ」をかけられたと考えることができます。自分は、MACDの計算をする時、極力ノイズを減らしたいという気持ちから高値、低値、終値を平均化したHLC3 のデータを用いています。

 まとめると、以下のようになります。これら全てがあわさって、バンドパスフィルタを通した有効な結果が時間軸上で抽出できます。

・EMA          →ローパスフィルタ
・MACDの引き算     →バンドパスフィルタ
・MACDのシグナルと引き算 →ハイパスフィルタ
・MACDにHLC3データを使う→ローパスフィルタ

MACDヒストグラムというのは、(1), HLC3データを使いローパスフィルタがかかったデータに対して、(2), 2つのEMAをかけてローパスフィルタをかけたそれぞれのデータを引き算してバイパスフィルタを作り、(3)シグナルと引き算してハイパスフィルタを更にかけたものと捉えることができます。

 遮断周波数の決定には、データの長さを用います。データが長いほどより遮断周波数が低くなります。そこで、MACDの引き算に用いる短期EMA、長期EMA、シグナルの比率を1:2:1 の比率にすることにしました。この比率はまだ研究の余地がありますが、過去の経験と試行錯誤した結果、最低でも長期は2倍以上の長さにした方がより良いフィルタが生成できました。また、ちょうど2倍4倍8倍など2^x上にしたほうがフィルタとして機能しやすかったため、通常のMACDの比率にはしていません。このMACDヒストグラムにより周波数領域で以下のようなフィルタがかかったデータが、時間領域で生成され表示されます。


この作業を30回やると、30本の線が画面上に表示されます。

理論2:最大エネルギーの周波数を特定する

(ここからスクリプト化できなかったために段々と職人芸が入ってきます。)
 さて、では抽出した結果をどうやって解析すればいいでしょうか。30本のMACDヒストグラムの結果に対し、頑張って目で追いかけて「最急降下法(山登り法)」を実行することにしました。

 つまり、エヴァチャートが上げから下げになった極大値、下げから上げになった極小値を、目で追いかけ、その時のMACDの「短期の長さ」を抽出するのです。また、当然、極大値・極小値はたくさんのデータが出てきますが、前回のトレンド・ウェッジの極大・極小の時間から、最も大きな反対の極大・極小となる「短期の長さ」を結果として用います。これがエヴァチャートで得られる結果となります。
 仮に、1時間軸で長さ30を取得した場合、最後にトレンド・ウェッジがターンしてから、1*30 = 30 時間が現在最も大きな周期と推定できます。仮に、15分軸で長さ50を取得した場合、最後にトレンド・ウェッジがターンしてから、1/4*50 = 12.5 時間が現在最も大きな周期と推定できます。

 なぜ30本なのかというのを説明します。通常は、1時間軸、2時間軸など2倍2倍で結果を見ることが多いと思います。1時間軸で60の値の結果は、1時間*60=60時間を示します。これはフィルタがきちんと機能していれば、2時間*30=60時間と同様の結果を示すはずです。つまり、全時間帯域を保証しようとした時に最低30本必要という結果が得られました。また細かいチューニングやノイズに振り回されないために、30 ~ 60 の領域が最も適切でした。このあたりはまだ研究の余地が残されています。

 なお、より大きな周期のほうがエネルギーが強く、大きなトレンドになりやすくなります。ですので、より大きな周期を見つけたときはそちらに従うと、小さな周期よりもトレードで騙しを回避しやすくなります。

過去データの検証

 日本語で言ってもわかりにくいので、実際に過去のデータより周期を推測します。

1月の下落時です。ここではピークは全てが緑に輝いている12/17を起点とします。(ここで一瞬だけ緑に縦上がってる1/6を起点にしたくなりますが、これは騙しです。)以降、紫の下降のデータで染まっているのが分かると思います。ここで反対に緑に侵食される最も大きな時(1/28)に30~39が緑になりました。つまり山登り法で39のデータを採用できます。誤差が0%であれば、12/17+39日=1/27 が次の転換点と推測できます。つまり当時(1/28)がトレンド転換点としてエヴァチャートでは認識してしまっています。
 実際は、2/6でした。ここで誤差が発生しています。この理由ですが、当然チャートは一つの周期で動くほど単純ではありません。なので、この誤差をいかに許容し見極めるかが最もエヴァチャートの難しいところとなります。このようにその時の時刻をトレンド判定として騙しが出る時、トレンドはものすごく勢いがあることが多いです。下がりすぎです。(この時はより小さい周期も見るべきです。)誤差は推測39日に対し、実際は49日でした。

2月の上昇時です。緑に上昇する中で最も紫化したのは、2/19の43 でした。起点は2/6 のため、2/6 + 6時間*43 = 2/17 7時という結果になりました。この下がった地点で2/19なので、この時の上昇は「上がりすぎ」といえます。実際の結果は2/21 にトレンド反転しました。誤差は4日間となりました。もうお気づきかと思いますが、トレンドの勢いがある方が誤差は大きく伸びやすくなります。これはエヴァチャートの今後の課題です。

次に2/21 を起点とします。2/24に3時間軸で48の結果を得ました。2/21+3*48時間=2/27 という結果になりました。実際の最下点は2/26 になりました。誤差は1日間となりました。

次に2/26を起点とします。2/28に12時間軸で37 という結果を得ました。2/26 + 37*12/24 = 3/16 という結果になりました。実際は3/6なので大外れです。ここで、6時間軸を見てみます。

強い紫の縦線が入ってるのがおわかりでしょうか。こういうケースでは、この周期は過信禁物です。このような侵食がある時は、より大きな周期で動いている可能性があります。そこで、一つ大きな周期を見てみます。

2日軸まで全体を見ると、3/4 の上げ自体が巨大下げトレンドの調整として見れることができます。つまりこの時、より大きな周期の力が動いていたのです。起点を1/6として、1/6 + 2日*53 = 4/22 となりました。実際は、4/5 にトレンド転換したので、誤差は17日間となりました。106日コースだと誤差もそれなりに大きくなる事があるので注意が必要です。

次に、4/6 を起点とします。4/24 に8時間軸で45の結果が得られました。 4/6+8*45時間 = 4/21 となりました。つまり4/24地点で3日前です。上がりすぎです。ここから先は言うまでもなく急降下しました。

次ですが、小さな周期ではなく4日軸という巨大な周期を見る結果となりました。1/6を起点として、1/6 + 4*43日 = 6/27 となりました。実際は6/25だったので、なんと2日の誤差になりました。172日の期間を開けて誤差2日は相当精度が高いです。 これは非常に良い推定ができたと思います。

なお、大きなトレンドだけで見てきましたが、当然数日単位の小さなウェッジでもエヴァチャートは有効です。ですが、大きな周期のエネルギーの方が強いため騙しも発生しやすくなります。そこだけ注意が必要です。

その他、便利な使い方

ちなみにこのエヴァチャートですが、上下への膨らみ方でその時の勢いを見ることができます。例えば、上の4日軸の場合、緑のキュッとしてるところはエネルギーが非常にあります。エネルギーが無くなると、上下にブワッと発散します。これは5分軸だと頻繁に見ることができ、ブレークアウトの終了判定や、騙し上昇の見極めにも使えます。例えばこんな感じになります。

最初のブレークアウトは本物、二番目のブレークアウトは騙しということがエヴァチャートより見極められました。

こんな感じで色々使えるので、ぜひ活用してみて下さい。ただし、誤差や騙しも当然あるので他のオシレーターと併用して使うのを推奨します。
エヴァチャートと相性の良いオシレーターは、VIX指数、RCI3LINEなどを自分はよく使っています。いろいろまだまだ研究不足のため、無料公開の代わりに新しいことや、便利な使い方がわかったら共有していただけることを望んでいます。まだまだ発展途上のオシレーターです。

いいなと思ったら応援しよう!