ストキャスティクスアルゴリズム #QuantX


## ストキャスティクス

今日は個人投資家の間でも非常に人気があるらしいストキャスティクス（stochastics）を[QuantX](https://quantx.io/)で書いてみたいと思います．

ストキャスティクスの計算式は非常に簡単で，基本となる**ファストストキャスティクス**は以下の計算式でラインを引き，シグナルを探します．

```math
％K = \frac{直近の終値-過去ｎ日間の最安値}{過去ｎ日間の最高値-過去ｎ日間の最安値} \times 100　\\
```
```math 
％D = ｍ日の％Kの単純移動平均
```
`％K`が`％D`を上抜いたら買い,逆に`％K`が`％D`を割り込んだら売りというシグナルになるのが一般的ですが，この `％K`と`％D`を使うシグナルは短期売買向きで，長期保有をしたい投資家は，`％D`をさらに単純移動平均した`SD`(Slow D)と`％D`自身の動きを見る**スローストキャスティクス**というシグナルを使うそうです．

今回も，[TA-Lib](https://mrjbq7.github.io/ta-lib/func_groups/momentum_indicators.html)にStochastic関数があり，
親切にも，ファストストキャスティクス`STOCHF` とスローストキャスティクス関数`STOCH`が用意されていますので，それを利用したいと思います．


## シグナル
#### ファストストキャスティクス 
`％K`が`％D`を上抜いたら買い,逆に`％K`が`％D`を割り込んだら売り

#### スローストキャスティクス
`％D`が`SD`を上抜いたら買い，逆に`％D`が`SD`を割り込んだら売り
ただし，`SD`が20％〜80％の間にあるときは，シグナルが出ても無視する．（ダマシ減らしの工夫だそうです）
参照：[オシレーター分析／ストキャスティクス | はじめてのテクニカル分析 / マネックス証券](https://info.monex.co.jp/technical-analysis/indicators/006.html)


## 手仕舞い

シグナルに書いた売りのタイミングで手仕舞う．

## コード

```python
import talib as ta 
import pandas as pd 
import numpy as np 


def initialize(ctx):
    # 設定
    ctx.fastK_period = 20   
    ctx.fastD_period = 3
    ctx.slowK_period = 3 
    ctx.slowD_period = 3 
    
    ctx.upper_thred = 80 
    ctx.lower_thred = 20 
    
    ctx.target = 0.10  
    ctx.codes = [1605, 1925, 2503, 4519, 4911, 6301, 6752, 7741, 8001 ]

    ctx.symbol_list  = ["jp.stock.{}".format(code) for code in ctx.codes]
    
    ctx.configure(
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols": ctx.symbol_list,
          "columns": [
            "high_price_adj",    # 高値(株式分割調整後)
            "low_price_adj",     # 安値(株式分割調整後)
            "close_price_adj",    # 終値(株式分割調整後) 
          ]}})
          

    def _STOCHASTIC_FAST(data):
      df_high = data["high_price_adj"].fillna(method='ffill')
      df_low = data["low_price_adj"].fillna(method='ffill')
      df_close = data["close_price_adj"].fillna(method='ffill')
      
      d_fastK = dict()
      d_fastD = dict()
      
      for symbol in data.minor_axis:
        fastK, fastD = ta.STOCHF(df_high[symbol].values.astype(np.double), 
                                 df_low[symbol].values.astype(np.double), 
                                 df_close[symbol].values.astype(np.double), 
                                 fastk_period=ctx.fastK_period, 
                                 fastd_period=ctx.fastD_period,)
        d_fastK[symbol] = fastK
        d_fastD[symbol] = fastD
        
      df_fastK = pd.DataFrame(d_fastK, index=data.major_axis)
      df_fastD = pd.DataFrame(d_fastD, index=data.major_axis)
      
      buy_sig = (df_fastK > df_fastD) & (df_fastK.shift(1) < df_fastD.shift(1)) 
      sell_sig = (df_fastK < df_fastD) & (df_fastK.shift(1) > df_fastD.shift(1)) 
      
      return {
        "fastK":df_fastK, 
        "fastD": df_fastD, 
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,
        }


    def _STOCHASTIC(data):
      
      df_high = data["high_price_adj"].fillna(method='ffill')
      df_low = data["low_price_adj"].fillna(method='ffill')
      df_close = data["close_price_adj"].fillna(method='ffill')
      
      d_slowK = dict()
      d_slowD = dict()
      
      for symbol in data.minor_axis:
        slowK, slowD = ta.STOCH(df_high[symbol].values.astype(np.double), 
                                df_low[symbol].values.astype(np.double), 
                                df_close[symbol].values.astype(np.double), 
                                fastk_period=ctx.fastK_period, 
                                slowk_period=ctx.slowK_period, 
                                slowd_period=ctx.slowD_period)

        d_slowK[symbol] = slowK
        d_slowD[symbol] = slowD

      df_slowK = pd.DataFrame(d_slowK, index=data.major_axis)
      df_slowD = pd.DataFrame(d_slowD, index=data.major_axis)      
      
      buy_sig = (df_slowK > df_slowD) & (df_slowK.shift(1) < df_slowD.shift(1)) & (df_slowD < ctx.lower_thred)
      sell_sig = (df_slowK < df_slowD) & (df_slowK.shift(1) > df_slowD.shift(1)) & (df_slowD > ctx.upper_thred)
      
      
      return {
        "slowK": df_slowK, 
        "slowD": df_slowD,
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,        
        }
        

    # シグナル登録 memo 参照
    ctx.regist_signal("STOCHASTIC_FAST", _STOCHASTIC_FAST)
    #ctx.regist_signal("STOCHASTIC", _STOCHASTIC)
    

def handle_signals(ctx, date, current):
    df = current.copy()

    # 買いシグナル
    df_long = df[df["buy:sig"]]
    if not df_long.empty:
      for (sym, val) in df_long.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "買いシグナル"
        sec.order_target_percent(ctx.target, comment= msg)
  
    # 売り（ポジションクローズ）シグナル
    df_sell = df[df["sell:sig"]]
    if not df_sell.empty:
      for (sym, val) in df_sell.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "売りシグナル"
        sec.order_target_percent(0, comment= msg)
```
## 結果

### ファストストキャスティクス

![2018-08-01-16:16:50.jpg](https://qiita-image-store.s3.amazonaws.com/0/14019/adb46b22-70af-ac70-8dfd-7098588d8a89.jpeg)

### スローストキャスティクス
![2018-08-01-16:17:28.jpg](https://qiita-image-store.s3.amazonaws.com/0/14019/4611e760-f617-aede-a467-fe73f2f6e300.jpeg)

## memo 

#### シグナル登録
今回はファスト用とスロー用のシグナルを作って，`regist_signal`関数に一つ渡す（一つはコメントアウト）という形を取りました．一つずつお試しください．


## 感想
+ 夏休み子ども科学電話相談は毎年聞いています．恐竜キッズには全くついていけません．
+ もし電話して質問できるなら，ゴジラが火を吹く仕組みを教えてもらいたいです．
+ 今回の銘柄選びは，セクターが重ならないように選びました． 
+ スローとファストを比べて，たしかに，スローの方が成績が良いことは確認出来ました．これは研究しがいがありそうですね．
+ 「`SD`が20％〜80％の間にあるときは，シグナルが出ても無視」は非常に重要なようです．これを外すとかなり成績が悪くなります．

## 免責注意事項
+ このコードに基づき投資した結果、損害が発生しても，一切責任を持ちません．
+ このコードが正しく機能する保証は一切致しません．
+ このアルゴリズムを勧めているわけではありません．あくまで QuantX / Python のサンプルコードとして掲載しているだけです．