【シストレ入門】フィボナッチリトレースメント♬

今回は、関数やクラスを学習した応用としてFibonacci数列を計算するクラスを作って遊んでみた。
また、以前のPandas入門で作成したFxと株価のアプリを応用します。
作成したアプリは、おまけに載せます。
###コードは以下に置きました
[MuAuan/FibonacciRetracement](https://github.com/MuAuan/FibonacciRetracement)

まず、以下の数字列を見たことありますか？
###Fibonacci数列
$F_n$

```
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155　．．．
```
この数字列は以下の性質がある。
####隣り合う数字の比
 $F_n/F_{n+1}$

```
1.00000 1.00000 0.50000 0.66667 0.60000 0.62500 0.61538 0.61905 0.61765 0.61818 0.61798 0.61806 0.61803 0.61804 0.61803 0.61803 ．．．
```
 $F_{n+1}/F_n$

```
1.00000 1.00000 2.00000 1.50000 1.66667 1.60000 1.62500 1.61538 1.61905 1.61765 1.61818 1.61798 1.61806 1.61803 1.61804 1.61803 ．．．
```
####黄金比
つまり、上の収束値は以下のような関係にある。

```math
\begin{align}
\frac{F_{n+1}}{F_n} &=& 1 + \frac{F_n}{F_{n+1}} \\
(\frac{F_{n+1}}{F_n})^2 &=& \frac{F_{n+1}}{F_n} + 1
\end{align}
```
すなわち、この比は二次方程式$x^2-x-1=0$の解であり、

```math
x＝\frac{1\pm\sqrt5}{2}
```
この二つの解の内、正の解が上記の比であり、黄金比$\phi=\frac{1+\sqrt5}{2}=1.618...$と呼ばれ、自然界の多くの場面で遭遇する。また、逆数は$x^2+x-1=0$の解であり、$x＝\frac{-1+\sqrt5}{2}=\phi -1$であり$=\phi '$とおくと、上の関係を満たすことが分かる。
参考にひまわりなどの例がある。

【参考】
[フィボナッチ数](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%9C%E3%83%8A%E3%83%83%E3%83%81%E6%95%B0)
###フィボナッチリトレースメント
シストレの世界では参考の解説のようにフィボナッチリトレースメントという「上昇相場における押し目や下降相場における一時的な戻りの目標価格を判断する指標として「フィボナッチリトレースメント」が使われます」
参考②より「リトレースメントとは、いわゆる「綾押し」「綾戻し」のことです。つまり形成されたトレンドの中で一時的にみられる価格の逆行のことです。」
【参考】
①[フィボナッチリトレースメント（Fibonacci Retracement）](https://www.fxbroadnet.com/tech/technicalchart/tech20.jsp)
②[リトレースメント　押えの知識四要@](https://orange2.net/a_lexis/technical2/retrace.php)
今回は、参考②の「3/4,2/3,1/2,1/3,1/4」などを意識して検証したいと思います。
###検証
対象は、FXでは一般に難易度が高いという噂の南アのZARJPY=Xとした。
①まず、Fibonacci数列で、適当な期間について通常のように上値、下値の間のリトレースメントを見る。
結果は、下値を付けた後の戻り2020.5以降の戻りが予測線にちゃんと乗っているように見える
![fx_ZARJPY=X_ema_df_decompose_2018-01-01_2020-08-14ex0.875.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/d1c4ec79-6e68-2dec-1cc0-78c1b4735798.png)
ここで、注意したいのは、いい加減なところから始めたにもかかわらずほぼ戻りを再現し、しかも下値より左側のピークの多くも引いた線の近くで戻っているように見えること。
※これらの戻りは未来値である下値を利用しているのに合っているという事実が何を物語るのかが大きな疑問である

なお、上値と下値の間のリトレースメントは以下の関数で計算している。
ラベルm618dのdはn=2, tは3...を意味している。
また、m50は50%線である。

```py
def M618_(x, n):
    # 下限値と上限値の間のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= f_ratio
    m618 = pd.Series(x).min() + f*(pd.Series(x).max()-pd.Series(x).min())
    return m618
```

②期間を2020.1.1-2020.8.14とし、明確な上値と下値を半分の期間で入るように描画して見る。また、上値近傍のフィボナッチリトレースメントを追記してみる。
結果は、2020.4中旬以降は予測線で動いていることが分かる。
しかも、ここでも下値より左側の期間のリトレースメントを再現しているように見える。
![fx_ZARJPY=X_ema_df_decompose_2020-01-01_2020-08-14ex0.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/91c7476c-baff-ace1-40f0-a3539fe908d7.png)

ここでは、上記に追加して、上値近傍の線を以下の関数で追加している。
ラベルm618_3は、n=3を意味している。

```py
def M618_1(x, n):
    # 上限値近傍のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= f_ratio
    m618 = pd.Series(x).max() - f*(pd.Series(x).max()-pd.Series(x).min())
    return m618
```
③今度は、期間を2019.4.1-2020.8.14として描いて見る。
この図では2019.4の上値と2019.9の下値を使って、その後の推移を予測している。
この場合も、次の期間のこの値幅の範囲の挙動を予測できている。さらに2020.3に下値を抜けたが、その後の推移も大きな戻りや反発を再現できている。
特に、m618_20(次の底値)を予測できていることが良くて、下値が変わればそこから再度次の予測をすれば、上のグラフのようにその後の推移をより精度よく予測できる。もっともm618_23など、その後の戻しもほぼ予測できており、その必要がないほどである。
![fx_ZARJPY=X_ema_df_decompose_2019-04-01_2020-08-14ex0.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/f6c9e19e-b99b-11ac-7308-ee6861ff7282.png)
下値以下の予測は以下の関数で実施している。
ラベルはm618_23の23は、n=3を意味している。

```py
def M618_2(x, n):
    # 下限値以下のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= f_ratio
    m618 = pd.Series(x).min() - f*(pd.Series(x).max()-pd.Series(x).min())
    return m618
```
###株価
以下のように株価もリトレースメントはよく一致している。
![fx_7201.T_ema_df_decompose_2020-01-01_2020-08-14ex0.8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/074bf9f3-3356-c314-cb5e-7b1febd55e72.png)
###有理数を試してみる
Fibonacci数列と有理数（分数比）の数列を比較すると以下のようにほぼ対応する位置に見いだせる。
ということで、参考②の話もあるので、同じように分数比で同じようにリトレースメントを計算して比較しようと思う。

| ー |Fibonacci|Fibonacci|Rational|Rational|
|:-:|:-:|:-:|:-:|:-:|
| 1  |91.0|1-$\phi'^5$|93.8|15/16|
| 2  |85.4|1-$\phi'^4$|87.5|7/8|
| 3  |76.4|1-$\phi'^3$|75|3/4|
| 4  |61.8|$\phi'=1-\phi'^2$|66.6|2/3|
|5  |50|1/2|50|1/2|
|  6 |38.2|$\phi '^2=1-\phi'$|33.3|1/3|
| 7  |23.6|$\phi '^3$|25|1/4|
| 8  |14.6|$\phi '^4$|16.7|1/6|
| 9  |9.0|$\phi '^5$|12.5|1/8|
| 10  |5.57|$\phi '^6$|6.25|1/16|
|   |ー|ー|ー|ー|
rational numberは、その他の有理数もありますが、(チャート上)直感的に見えやすい数字を並べています。

結果は以下の通り
①検証の①に対応する期間と条件
結果は、この場合もリトレースメントの線はよく一致しているように見える。
![fx_ZARJPY=X_ema_df_decompose_2018-01-01_2020-08-14ratio0.875.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/6e3b1bfa-d0f2-9a5f-24ea-31d235496f25.png)
③検証の③に対応する期間と条件
以下のように、かなり戻りの位置を再現している。
ほぼ、優劣はないと思われる。
下値を抜けた時の-50%と-100%は、そもそも同一の位置になるのも原因かもしれないが、上述したように両者で似たような位置に数値があるのが元々の原因なのだと思う
![fx_ZARJPY=X_ema_df_decompose_2019-04-01_2020-08-14ratio0.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/3ff4bb5c-a3be-8f22-9fed-ca37f76a09f7.png)
下値以下の位置を数値で比較すると以下のとおりである。
いくつかの線が有理数では該当するものが無いが、①で見たようにこれとて作ろうと思えば作れてしまう。

|Fibonacci|Rational|備考|
|:-:|:-:|:-:|
|6.8236|6.8236|下値|
|－|6.749006|－|
|6.64947|6.674412|ほぼ同じ|
|6.541853|6.525225|ほぼ同じ|
|6.367723|－|2020.3下降線|
|6.22685|6.22685|-50%|
|6.085976|－|下値反発後の戻り|
|5.630099|5.630099|-100%|
###議論
この話の面白そうなところは、つまり拡大したり、縮小してもこのアップダウンは存在し、しかもその無限に存在する上値と下値には、黄金比で代表される一つの指数が存在しそうだというところにある。
すなわち、いわゆるフラクタル現象の一つの可能性があるということである。
この話は、FXにしろ、株価にしろ、上がるかどうかを予測する話ではない。しかし、トレンドの中で、リトレースメント（戻り）が発生するとすれば、この係数で計算される値辺りで戻ったり、反発したりするということである。
昔は、目間や暗算で投資を実施していたろうから、有理数的な考え方でやっていただろうが、今やシストレでやっているだろうから、当然このFibonacci数列を導入していると思った方がいいと判断できる。
こういう知識を取り入れて、少しでも科学的な投資をやるのは成功への近道なんだと思う。
###まとめ
・Fibonacci数列を高速に計算するクラスを作成した
・応用として、Fibonacciリトレースメントを検証した
・fx、株価共に、フィボナッチリトレースメントが成り立っている
・有理数で同様なリトレースメントを検証した結果、ほぼ同様な予測線を計算できることが分かった

・シストレに導入するには、時々刻々変化する時系列の数字から適切なフィボナッチリトレースメントを計算する必要がある

###おまけ
####fibonacci
```class_fibonacci.py
import numpy as np

class MyFibonacciClass:
    f_const = 0.6180339887498949 #514229/832040 #0.6180
    def __init__(self,x=1,y=0,n=10):
        self.x = x
        self.y = y
        self.n = n
                
    def calc_1(self,n):
        const = 1
        for i in range(1,n):
            const *= MyFibonacciClass.f_const
        return const, self.y + const*(self.x - self.y), self.x - const*(self.x - self.y)
    def calc_1_(self,n):
        const = 1
        for i in range(1,n):
            const *= MyFibonacciClass.f_const
        return self.y + (1-const)*(self.x - self.y)
    
    def calc_fib(self,m, sb1,sb2):
            if m ==1 or m== 2:
                return 1
            else:
                return sb1 + sb2
    def calc_fib2(self,m):
            if m ==1 or m== 2:
                return 1
            else:
                return MyFibonacciClass.calc_fib2(self,m-1) + MyFibonacciClass.calc_fib2(self,m-2)
            
    def calc_fib3(self, m): 
        # fastest m=100000;108 ms ± 4.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
        a, b = 1, 1
        for _ in range(m-2):
            a, b = b, a + b
        return b
    
    def calc_fib4(self,m): # m =< 70
        return round((((1 + 5 ** 0.5) / 2) ** m - ((1 - 5 ** 0.5) / 2) ** m) / 5 ** 0.5)
  
    def calc_print(self):
        sb1 = 1
        sb2 = 1
        for n0 in range(1,self.n+1):
            s = MyFibonacciClass.calc_fib(self,n0, sb1, sb2)
            print(n0, s,end=' ')
            #print(s,end=' ')
            rs = np.float64(sb1/s)
            rs1 = np.float64(s/sb1)
            print(rs, rs1)
            #print('{0:5,.5f}'.format(rs1), end=' ')
            sb2 = sb1
            sb1 = s
            
if __name__ == '__main__':
    fibonacci1 = MyFibonacciClass(1604.5,834.6,10)
    for i in range(1,11,1):
        c,a,b = fibonacci1.calc_1(i)
        print('{0:5,.3f}_{1:5,.3f}_{2:5,.3f}'.format(c,a,b))

    fibonacci2 = MyFibonacciClass(1604.5,834.6,10000)
    fibonacci2.calc_print()
    print(fibonacci2.calc_fib3(1000000))
```
####fx_plot.py
```fx_plot.py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import datetime as dt
import statsmodels.api as sm
from statsmodels.tsa.seasonal import STL
import pandas_datareader.data as DataReader

stock0='ZARJPY=X'  #'USDJPY=X' #'EURJPY=X' #AUDJPY=X JPYAUD=X ZARJPY=X GBPJPY=X JPYUSD=X
bunseki = "trend"
start = dt.date(2018,1,1)
end = dt.date(2020,8,14)

df=DataReader.get_data_yahoo("{}".format(stock0),start,end) 
print(df)

series=df['Close']
cycle, trend = sm.tsa.filters.hpfilter(series, 144)
df['trend']=  trend
f_ratio = 0.6180339887498949

def MAX_(x):
    return pd.Series(x).max()
def MIN_(x):
    return pd.Series(x).min()
def M618_(x, n):
    # 下限値と上限値の間のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= f_ratio
    m618 = pd.Series(x).min() + f*(pd.Series(x).max()-pd.Series(x).min())
    return m618
def M618_2(x, n):
    # 下限値以下のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= f_ratio
    m618 = pd.Series(x).min() - f*(pd.Series(x).max()-pd.Series(x).min())
    return m618
def M618_1(x, n):
    # 上限値近傍のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= f_ratio
    m618 = pd.Series(x).max() - f*(pd.Series(x).max()-pd.Series(x).min())
    return m618
def M50_(x, n):
    # 50％のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= 0.5
    m618 = pd.Series(x).max() - f*(pd.Series(x).max()-pd.Series(x).min())
    return m618
def M50_1(x, n):
    # 下限値以下のさらに50％のリトレースメントを求める
    f = 1
    for i in range(0,n,1):
        f *= 0.5
    m618 = pd.Series(x).min() - f*(pd.Series(x).max()-pd.Series(x).min())
    return m618


series2 = df['trend'].values.tolist()
print(series2[len(series2)-10:len(series2)])
m = 1/5 # 上限値、下限値を求めるデータ範囲；全データ辺りの割合
df['Close']=series  #series" #cycle" #trend
df['Close2']=series2
df['max'] = MAX_(df['Close'][:int(len(series)*m)])
df['min'] = MIN_(df['Close'][:int(len(series)*m)])
df['m50'] = M50_(df['Close'][:int(len(series)*m)],1)
df['m50_1'] = M50_1(df['Close'][:int(len(series)*m)],1)
# 上限値近傍のリトレースメントを求める
df['m618_3'] = M618_1(df['Close'][:int(len(series)*m)],3)
df['m618_4'] = M618_1(df['Close'][:int(len(series)*m)],4)
df['m618_5'] = M618_1(df['Close'][:int(len(series)*m)],5)
# 下限値以下のリトレースメントを求める
df['m618_20'] = M618_2(df['Close'][:int(len(series)*m)],0)
df['m618_21'] = M618_2(df['Close'][:int(len(series)*m)],1)
df['m618_22'] = M618_2(df['Close'][:int(len(series)*m)],2)
df['m618_23'] = M618_2(df['Close'][:int(len(series)*m)],3)
df['m618_24'] = M618_2(df['Close'][:int(len(series)*m)],4)
# 下限値と上限値の間のリトレースメントを求める
df['m618'] = M618_(df['Close'][:int(len(series)*m)],1)
df['m618d'] = M618_(df['Close'][:int(len(series)*m)],2)
df['m618t'] = M618_(df['Close'][:int(len(series)*m)],3)
df['m618q'] = M618_(df['Close'][:int(len(series)*m)],4)
df['m618q5'] = M618_(df['Close'][:int(len(series)*m)],5)
df['m618q6'] = M618_(df['Close'][:int(len(series)*m)],6)

date_df=df['Close'].index.tolist()
print(df[len(series)-10:len(series)])

fig, ax1 = plt.subplots(1,1,figsize=(1.6180 * 8, 8*1),dpi=200)
ax1.plot(df['Close'],label="series")
ax1.plot(df['Close2'],label="series2")
ax1.plot(df['max'],label="max")
ax1.plot(df['min'],'black',label="min")
ax1.plot(df['m50'],'blue',label="m50")
ax1.plot(df['m50_1'],'blue',label="m50_1")
ax1.plot(df['m618_3'],label="m618_3")
ax1.plot(df['m618_4'],label="m618_4")
ax1.plot(df['m618_5'],label="m618_5")
ax1.plot(df['m618_20'],label="m618_20")
ax1.plot(df['m618_21'],label="m618_21")
ax1.plot(df['m618_22'],label="m618_22")
ax1.plot(df['m618_23'],label="m618_23")
ax1.plot(df['m618_24'],label="m618_24")
ax1.plot(df['m618'],label="m618")
ax1.plot(df['m618d'],label="m618d")
ax1.plot(df['m618t'],label="m618t")
ax1.plot(df['m618q'],label="m618q")
ax1.plot(df['m618q5'],label="m618q5")
ax1.plot(df['m618q6'],label="m618q6")

ax1.set_title("{}".format(stock0))
ax1.legend()
#ax1.grid()
plt.savefig("./fx/fx_{}_ema_df_decompose_{}_{}ex{}.png".format(stock0,start,end,m))
plt.pause(1)
plt.close()
```