トレーリングストップ　エントリー版


この記事は[QuantXトレーリングストップ](https://qiita.com/izawalouis/items/111c97871c68fdf09c3c)の続きです。

#エントリーのトレーリングストップと前回のトレーリングストップとの違い

前回はトレーリングストップを使用してピークの株価で株を売るのを目的にしていました。今度は株価の底値から上がった時に株を買うエントリーのトレーリングストップを開発しました。
![Screen Shot 2019-07-18 at 11.36.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459110/136f5523-817e-a57f-8f6e-7e70ac2a799c.png)

前回のトレーリングストップと反対なので、maxを更新しながら線が動のではなくて、minを更新します。

持っている株を売る目的のトレーリングストップとは違い、エントリーのトレーリングストップはまだ株を持っていないのでアルゴリズムが少し違ったものになります。

前回と異なり、今回はポジションがない状態からの操作なので、一定期間のminを使用して問題ありません。

また、トレーリングストップはsell_sigに入れては問題が起こりましたが、エントリーのトレーリングストップはbuy_sigに入れても大丈夫です。なぜなら、エントリーのトレーリングストップによるbuy_sigがTrueになるのが珍しくsellに影響を与えないからです。エントリー用トレーリングストップはbuy_sigとして投入します。

#コード

一般的なWMAモデルをまた使用しました。これも前回と同じトレーリングストップも入っています。

```
# Sample Algorithm
# ライブラリーのimport
# 必要ライブラリー
import maron
import maron.signalfunc as sf
import maron.execfunc as ef
# 追加ライブラリー
# 使用可能なライブラリに関しましては右画面のノートをご覧ください①
import pandas as pd
import talib as ta
import numpy as np

# オーダ方法(目的の注文方法に合わせて以下の2つの中から一つだけコメントアウトを外してください)
# オーダー方法に関しましては右画面のノートをご覧ください②
#ot = maron.OrderType.MARKET_CLOSE # シグナルがでた翌日の終値のタイミングでオーダー
ot = maron.OrderType.MARKET_OPEN   # シグナルがでた翌日の始値のタイミングでオーダー
#ot = maron.OrderType.LIMIT        # 指値によるオーダー

# 銘柄、columnsの取得
# 銘柄の指定に関しては右画面のノートをご覧ください③
# columnsの取得に関しては右画面のノートをご覧ください④
def initialize(ctx):
    # 設定, 6行目から20行目は変えないようにお願いします。
    ctx.flag_profit = False #利益確定売りを用いるかTrueなら用いるFalseなら用いない
    ctx.flag_loss = True #損切りを用いるかTrueなら用いるFalseなら用いない
    ctx.flag_trailing_stop = True#トレーリングストップを用いるかTrueなら用いるFalseなら用いない
    ctx.trailing_min = False
    ctx.loss_cut = -0.03 #損切りのボーダーマイナス％
    ctx.profit_taking = 0.05 #利益確定売りのボーダープラス％
    ctx.trailing_stop = -0.03 #トレーリングストップのmax値比較ボーダーマイナス％
    ctx.codes = [4563]
    ctx.symbol_list  = ["jp.stock.{}".format(code) for code in ctx.codes]
    ctx.configure(
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols":ctx.symbol_list,
          "columns": [
            #"open_price_adj",    # 始値(株式分割調整後)
            #"high_price_adj",    # 高値(株式分割調整後)
            #"low_price_adj",     # 安値(株式分割調整後)
            "close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後)
            # "volume_adj",         # 出来高
            # "txn_volume",         # 売買代金
                     ]}})
  
    # シグナル定義
    def _my_signal(data):
      cp = data["close_price_adj"].fillna(method="ffill")

      new_min = cp.rolling(window=7, center=False).min()
      # この部分に作成するアルゴの指標を書き込んで下さい。
       #各銘柄の終値(株式分割調整後)を取得、欠損データの補完
      
      for (sym, val) in cp.items():
        #valがすべてNaNだったときはtalibで計算しない
        isna = val.isnull().all()
        if isna:
          continue
  
      #単純移動平均線(SMA)の設定
      #データの入れ物を用意
      s_wma = pd.DataFrame(data=0,columns=[], index=cp.index)
      m_wma = pd.DataFrame(data=0,columns=[], index=cp.index)
      l_wma = pd.DataFrame(data=0,columns=[], index=cp.index)
      #TA-Libによる計算
      for (sym,val) in cp.items():
        s_wma[sym] = ta.WMA(cp[sym].values.astype(np.double), timeperiod=5)
        m_wma[sym] = ta.WMA(cp[sym].values.astype(np.double), timeperiod=25)
        l_wma[sym] = ta.WMA(cp[sym].values.astype(np.double), timeperiod=75)
      #SMAの売買シグナルの定義
      s_wma_ratio = s_wma/m_wma
      l_wma_ratio = m_wma/l_wma
      
      buy_sig = ((cp.shift(7)*0.89 > cp) & (new_min*1.03 < cp)) | ((s_wma_ratio > 1.00) & (s_wma_ratio < 1.10) & (l_wma_ratio > 1.00) & (l_wma_ratio < 1.10))
      sell_sig = (((s_wma_ratio < 0.99) & (l_wma_ratio < 0.99))) 
  
      # market_sigという全て0が格納されているデータフレームを作成
      # market_sigに関しては右画面のノートをご覧ください⑥
      market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
      
      # buy_sigがTrueのとき1.0、sell_sigがTrueのとき-1.0とおく
      market_sig[buy_sig == True] = 1.0
      market_sig[sell_sig == True] = -1.0
      market_sig[(buy_sig == True) & (sell_sig == True)] = 0.0
  
      # loggerに関しましては右画面のノートをご覧ください⑦
      # ctx.logger.debug(market_sig)

      return {
        "wma5":s_wma,
        "wma25":m_wma,
        "wma75":l_wma,
        "market:sig": market_sig,
      }
      
    # シグナル登録
    ctx.regist_signal("my_signal", _my_signal)

def handle_signals(ctx, date, current):
    '''
    current: pd.DataFrame
    '''
    market_sig = current["market:sig"]
    done_syms = set([])
    
    
    for (sym,val) in ctx.portfolio.positions.items():
        returns = val["returns"]
        trailing = val["max_returns"] - returns
        if (ctx.flag_loss) & (returns < ctx.loss_cut):
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="損切り(%f)" % returns)
          done_syms.add(sym)
        elif (ctx.flag_profit) & (returns > ctx.profit_taking):
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
          done_syms.add(sym)
        elif (ctx.flag_trailing_stop) & (returns - val["max_returns"] < ctx.trailing_stop):
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="トレーリング利確(%f)" % returns)
          done_syms.add(sym)
        elif (ctx.flag_trailing_stop) & (returns > 0.05) & (ctx.trailing_stop > 0.02):
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="トレーリング１０％利確(%f)" % returns)
          done_syms.add(sym)
        elif (ctx.flag_trailing_stop) & (returns - val["max_returns"] < ctx.trailing_stop):
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="トレーリング利確(%f)" % returns)
          done_syms.add(sym)
          
          
          
      # 買いシグナル
    buy = market_sig[market_sig > 0.0] 
    for (sym, val) in buy.items():
      if sym in done_syms:
        continue
      
      sec = ctx.getSecurity(sym)
      sec.order_target_percent(0.8, orderType=ot, comment="SIGNAL BUY")
      #ctx.logger.debug("BUY: %s,  %f" % (sec.code(), val))
      pass
      
    # 売りシグナル

    sell = market_sig[market_sig < 0.0]
    for (sym, val) in sell.items():
      if sym in done_syms:
        continue
      sec = ctx.getSecurity(sym)
      sec.order_target_percent(0, orderType=ot, comment="SIGNAL SELL")
      #ctx.logger.debug("SELL: %s,  %f" % (sec.code(), val))
    
```

#コードの説明
###def _my_signal:

```
      cp = data["close_price_adj"].fillna(method="ffill")       
      new_min = cp.rolling(window=7, center=False).min()
      for (sym, val) in cp.items():
        #valがすべてNaNだったときはtalibで計算しない
        isna = val.isnull().all()
        if isna:
          continue
  
      #単純移動平均線(SMA)の設定
      #データの入れ物を用意
      s_wma = pd.DataFrame(data=0,columns=[], index=cp.index)
      m_wma = pd.DataFrame(data=0,columns=[], index=cp.index)
      l_wma = pd.DataFrame(data=0,columns=[], index=cp.index)
      #TA-Libによる計算
      for (sym,val) in cp.items():
        s_wma[sym] = ta.WMA(cp[sym].values.astype(np.double), timeperiod=5)
        m_wma[sym] = ta.WMA(cp[sym].values.astype(np.double), timeperiod=25)
        l_wma[sym] = ta.WMA(cp[sym].values.astype(np.double), timeperiod=75)
      #SMAの売買シグナルの定義
      s_wma_ratio = s_wma/m_wma
      l_wma_ratio = m_wma/l_wma
      
      buy_sig = ((cp.shift(7)*0.89 > cp) & (new_min*1.03 < cp)) | ((s_wma_ratio > 1.00) & (s_wma_ratio < 1.10) & (l_wma_ratio > 1.00) & (l_wma_ratio < 1.10))
      sell_sig = (((s_wma_ratio < 0.99) & (l_wma_ratio < 0.99))) 
```

まず、cpをその日の株価に設定し、new_minをその日から一週間前のminに設定します。
これで、buy_sigでエントリーのトレーリングストップのコードを書きます。

```
buy_sig = ((cp.shift(7)*0.89 > cp) & (new_min*1.03 < cp)) | ((s_wma_ratio > 1.00) & (s_wma_ratio < 1.10) & (l_wma_ratio > 1.00) & (l_wma_ratio < 1.10))
```

cp.shift(7)*0.89 > cp では7日前の株価の約90％が今日の株価を超えていたら一つの条件が満たされます。
new_min*1.03 < cp では今日の株価が先週のminの１０３％を超えていたら二つ目の条件が満たされます。
この二つの条件を満たしたら、エントリーのトレーリングストップを実行する設定になっています。

説明すると、一週間で急減（約-10％）して最低限より３％上がったら株を買うことになっています。このような急減している株価は一般的に一週間かけて下がってまた上がることが調べた結果わかりました。

#比べた結果
トレーリングストップだけのアルゴリズム：![Screen Shot 2019-07-16 at 15.30.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459110/f1c68b6f-1b8f-c922-2174-07a1aa70d8fa.png)![Screen Shot 2019-07-18 at 14.23.40.png]


エントリーのトレーリングストップを含めたアルゴリズム：
![Screen Shot 2019-07-18 at 12.23.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459110/acdf7dde-4eae-0ec9-8f03-c0dd64113ba7.png)


トレーリングストップだけのアルゴリズム：
　　　　　　　　　　　![Screen Shot 2019-07-18 at 14.23.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459110/a8075048-14dd-6037-6c87-ba7245969a08.png)

エントリーのトレーリングストップを含めたアルゴリズム：

　　　　　　　　　　　![Screen Shot 2019-07-18 at 14.24.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459110/04d44f88-2670-0ea9-3b3e-06a04b4a0879.png)

エントリー用トレーリングストップを利用して急減後エントリーできるようになりました。