「売買システム入門」トゥーシャー・シャンデ〜ストラテジ検証④ CB-PB 仕切りルールについて

このサイトはRubyでバックテストツールを作成し様々なストラテジーを検証することを目的としています。前回まではSMA65CC3についての検証を行っていました。その中でシステムを、N日検定とMFEを用いた方法で評価しました。

今回も引き続きこちらの本を読み進めていきます。

今回はSMA65CC3システムの次に紹介されているCB-PBシステムについて検証を行っていきます。特にこの章では仕切りルールに対して焦点が当てられているので、それに習って2パターンの仕切りルールを検証していきたいと思います。 それでは参りましょう。

目次

CB-PBシステムとは

著書にはこのようにあります。

20日の新高値を付けた後の下落局面で買いポジションを建てる買いシグナルのみのシステムをチャネル・ブレイクアウト-プルバック(CB-PB)システムと呼ぶ。

トゥーシャー・シャンデ『売買システム入門』(PanRolling、2000年)

ブレイクアウトシステムに似ていますが、ブレイクアウトしてからの戻しで買おうってシステムのようですね。

戻しといっても色々な定義の仕方があります。ここでは、このようにすることにしています。

まず20日新高値を付け、その後7日以内に新たに5日安値を更新するところまで下落する。5日安値を更新した場合、翌日の寄り付きで買い仕掛ける。

トゥーシャー・シャンデ『売買システム入門』(PanRolling、2000年)

これで仕掛けのルールが決まりますので、この仕掛けに対し、ランダムな仕掛けより優位なのかを検証することができます。ストラテジーを書いて検証していきましょう。

ストラテジー構築

プログラムはこのようになります。

module Kabu
  class CbPbDays

    def initialize(n)
      @n = n
    end

    def decide(env)
      code = env[:code]
      date = env[:date]
      soks = env[:soks]
      position = env[:position]

      highs = soks[-27..-2].high(20)
      is_high = false
      highs.zip(soks[-8..-2]).each do |high,sok|
        next if high != sok.high
        is_high = true
        break
      end

      lows = soks[-6..-2].low(5)
      is_low = lows[-1] == soks[-2].low

      if not position.nil?
        if position.term >= @n
          return Action::Sell.new(code, date, soks[-1].open,1)
        else
          return Action::None.new(code, soks[-1].open)
        end
      end

      if is_high and is_low
        Action::Buy.new(code, date, soks[-1].open,1)
      else
        Action::None.new(code, soks[-1].open)
      end
    end
  end
end

仕切りルールを10日後の引けで仕切ることにして、理想的な取引がされているチャートを確認してみます。

image1

良さそうですね。これはうまくハマっているパターンですが、下記は天井を拾ったようなパターンです。

image2

N日検定

N日検定はその仕掛けがランダムな仕掛けより優位なのかを検証するためのものです。この検定では仕切りルールをN日後にした場合、適当なNであっても勝率が55%を超えるかを調べます。

ここではN=5,10,15,20,30,50について調べてみました。対象は33業種別株価指数の2007年〜2017年のデータです。

Code 5 10 15 20 30 50
I201 0.47 0.49 0.49 0.44 0.47 0.60
I202 0.50 0.56 0.49 0.53 0.56 0.49
I203 0.48 0.49 0.55 0.47 0.57 0.52
I204 0.58 0.53 0.53 0.52 0.52 0.53
I205 0.43 0.58 0.54 0.41 0.46 0.59
I206 0.49 0.46 0.49 0.44 0.47 0.56
I207 0.52 0.62 0.54 0.54 0.54 0.61
I208 0.48 0.47 0.47 0.47 0.38 0.43
I209 0.56 0.51 0.53 0.58 0.60 0.54
I210 0.49 0.56 0.56 0.48 0.56 0.62
I211 0.57 0.58 0.54 0.57 0.58 0.64
I212 0.44 0.37 0.42 0.38 0.41 0.61
I213 0.51 0.55 0.58 0.52 0.53 0.59
I214 0.52 0.53 0.56 0.48 0.54 0.60
I215 0.54 0.60 0.60 0.54 0.64 0.63
I216 0.55 0.47 0.51 0.50 0.56 0.64
I217 0.49 0.46 0.45 0.41 0.59 0.47
I218 0.58 0.63 0.56 0.52 0.50 0.63
I219 0.51 0.53 0.51 0.55 0.54 0.52
I220 0.45 0.54 0.48 0.43 0.47 0.42
I221 0.43 0.45 0.45 0.52 0.43 0.33
I222 0.47 0.47 0.47 0.38 0.50 0.55
I223 0.41 0.38 0.47 0.50 0.50 0.37
I224 0.49 0.49 0.46 0.41 0.45 0.40
I225 0.58 0.59 0.49 0.47 0.56 0.54
I226 0.50 0.52 0.55 0.52 0.58 0.60
I227 0.51 0.53 0.59 0.54 0.61 0.49
I228 0.48 0.44 0.44 0.31 0.37 0.52
I229 0.46 0.45 0.46 0.44 0.35 0.39
I230 0.52 0.46 0.54 0.53 0.42 0.48
I231 0.58 0.51 0.57 0.50 0.48 0.59
I232 0.58 0.53 0.51 0.47 0.44 0.52
I233 0.52 0.61 0.55 0.59 0.60 0.54
  0.51 0.51 0.51 0.48 0.51 0.53

55%を切っていますので、果たしてと言ったところです。銘柄によるところが大きそうです。

仕切りパターン1

仕切りパターンの1つ目として紹介されているのがN日後に仕切るというものです。個人的には投げやりではないかと思いますが。

N日検定で一番勝率のよかった50日を用いて検証してみましょう。

Code net income trades win(%) pf average dd
I201 81.34 35 60.0 1.96 2.32 -22.34
I202 16.25 35 48.57 1.08 0.46 -48.34
I203 6.47 33 51.52 1.06 0.2 -52.42
I204 49.31 36 52.78 1.67 1.37 -19.11
I205 35.69 34 58.82 1.28 1.05 -32.89
I206 7.74 32 56.25 1.07 0.24 -22.19
I207 61.0 33 60.61 1.77 1.85 -20.45
I208 -32.36 35 42.86 0.73 -0.92 -24.21
I209 -28.42 35 54.29 0.84 -0.81 -50.97
I210 78.42 34 61.76 1.7 2.31 -32.21
I211 80.6 33 63.64 1.71 2.44 -31.55
I212 0.85 33 60.61 1.0 0.03 -63.78
I213 51.14 34 58.82 1.41 1.5 -47.91
I214 48.12 35 60.0 1.41 1.37 -39.09
I215 79.06 35 62.86 1.69 2.26 -27.39
I216 54.63 33 63.64 1.49 1.66 -22.28
I217 64.74 34 47.06 1.6 1.9 -58.04
I218 65.65 35 62.86 1.57 1.88 -39.01
I219 38.39 33 51.52 1.36 1.16 -20.28
I220 -21.96 31 41.94 0.85 -0.71 -41.3
I221 8.78 33 33.33 1.08 0.27 -29.49
I222 77.17 33 54.55 1.45 2.34 -31.19
I223 -82.87 35 37.14 0.62 -2.37 -61.69
I224 -57.32 35 40.0 0.64 -1.64 -76.7
I225 28.34 35 54.29 1.27 0.81 -44.11
I226 43.91 35 60.0 1.34 1.25 -27.58
I227 27.71 35 48.57 1.22 0.79 -48.25
I228 20.35 31 51.61 1.17 0.66 -29.75
I229 31.49 33 39.39 1.14 0.95 -47.21
I230 -20.37 33 48.48 0.87 -0.62 -53.69
I231 16.26 34 58.82 1.09 0.48 -51.08
I232 -20.3 33 51.52 0.89 -0.62 -53.69
I233 39.38 35 54.29 1.52 1.13 -31.2
平均 25.7 33.0 53.1 1.3 0.8 -39.4
偏差 40.98 1.41 8.17 0.34 1.2 14.57

無事プラスにはなりました。PFは1.3、平均収益は0.8%となっています。取引コストは勘定していませんので、この数字ではしんどいですね。

仕切りパターン2

2つめのパターンとして紹介されているのが、新高値で仕切るというものです。同じ20日新高値を用いましょう。コードこのとおりです。

module Kabu
  class CbPbHigh

    def decide(env)
      code = env[:code]
      date = env[:date]
      soks = env[:soks]
      position = env[:position]

      highs = soks[-27..-2].high(20)
      is_high = false
      highs.zip(soks[-8..-2]).each do |high,sok|
        next if high != sok.high
        is_high = true
        break
      end

      lows = soks[-6..-2].low(5)
      is_low = lows[-1] == soks[-2].low

      if not position.nil?
        if highs[-1] <= soks[-1].high
          return Action::Sell.new(code, date, highs[-1],1)
        else
          return Action::None.new(code, soks[-1].open)
        end
      end

      if is_high and is_low
        Action::Buy.new(code, date, soks[-1].open,1)
      else
        Action::None.new(code, soks[-1].open)
      end
    end
  end
end

チャートも確認しておきます。

image3

問題なさそうですね。動かしてみます。

Code net income trades win(%) pf average dd
I201 9.55 89 71.91 1.07 0.11 -27.4
I202 32.34 85 72.94 1.13 0.38 -59.03
I203 -5.73 85 69.41 0.96 -0.07 -35.32
I204 40.4 91 79.12 1.4 0.44 -35.8
I205 -35.7 79 69.62 0.79 -0.45 -42.28
I206 -41.41 79 63.29 0.79 -0.52 -38.04
I207 -18.27 87 78.16 0.88 -0.21 -46.71
I208 -15.44 80 73.75 0.88 -0.19 -32.11
I209 -21.86 85 75.29 0.9 -0.26 -43.5
I210 47.93 87 73.56 1.27 0.55 -41.06
I211 -41.52 86 70.93 0.81 -0.48 -55.82
I212 -139.87 66 60.61 0.5 -2.12 -64.16
I213 -27.74 92 73.91 0.88 -0.3 -50.29
I214 -43.48 81 71.6 0.75 -0.54 -39.5
I215 -49.47 88 77.27 0.77 -0.56 -57.28
I216 -50.11 85 70.59 0.75 -0.59 -58.45
I217 -60.49 77 64.94 0.71 -0.79 -41.29
I218 -20.1 93 75.27 0.89 -0.22 -52.24
I219 -54.09 77 75.32 0.74 -0.7 -53.82
I220 -73.54 74 70.27 0.67 -0.99 -43.73
I221 -24.57 78 65.38 0.79 -0.31 -18.02
I222 -88.99 70 70.0 0.72 -1.27 -60.76
I223 -66.44 72 63.89 0.68 -0.92 -58.85
I224 -25.07 84 70.24 0.84 -0.3 -34.68
I225 70.9 96 79.17 1.7 0.74 -16.74
I226 -44.52 86 75.58 0.76 -0.52 -56.33
I227 -26.72 85 74.12 0.83 -0.31 -20.15
I228 -117.74 66 59.09 0.52 -1.78 -61.27
I229 -178.04 66 57.58 0.49 -2.7 -82.51
I230 -68.99 79 68.35 0.74 -0.87 -57.14
I231 2.9 82 69.51 1.01 0.04 -69.77
I232 -56.22 78 66.67 0.78 -0.72 -62.69
I233 -13.03 82 69.51 0.89 -0.16 -21.35
  -36.5 81.0 70.5 0.9 -0.5 -46.6
  49.66 7.55 5.43 0.24 0.7 15.56

勝率が平均80%を超えるなかなかのシステムですが、収益がマイナスです。コツコツドッカン系といったところですね。ロスカットのルールを検討することで改善出来るのかもしれません。MFEを調べてみましょう。

勝ちトレードが経験した最大の含み損のヒストグラムを累積したものです。

image4

-3.2%当たりをロスカットラインに設定した場合8割程度の勝ちトレードは救えそうです。

ストラテジーはこんな感じです。

class CbPbHighStopLoss

    attr_accessor :loss_line

    def initialize
      @loss_cutted = false
    end

    def setup
      @last_position = nil
    end

    def decide(env)
      code = env[:code]
      date = env[:date]
      soks = env[:soks]
      position = env[:position]

      highs = soks[-27..-2].high(20)
      is_high = false
      highs.zip(soks[-8..-2]).each do |high,sok|
        next if high != sok.high
        is_high = true
        break
      end

      lows = soks[-6..-2].low(5)
      is_low = lows[-1] == soks[-2].low

      if position.nil?
        if is_high and is_low
          return Action::Buy.new(code, date, soks[-1].open,1)
        else
          return Action::None.new(code, soks[-1].open)
        end
      end

      is_loss_cut = position.gain(soks[-2].close,1) < @loss_line

      if is_loss_cut
        @last_position = position
        return Action::Sell.new(code,date,soks[-1].open,1)
      end

      if highs[-1] <= soks[-1].high
        return Action::Sell.new(code, date, highs[-1],1)
      else
        return Action::None.new(code, soks[-1].open)
      end
    end
  end

仮に-3.4%をロスカットラインだとして、チャートを確認します。

image5

image6

ロスカット、通常仕切り共に問題なさそうです。

ロスカットを-10%〜-1%に置きながら、損益などを確認します。

image7

image8

image8

-1%のロスカットを設定すると、収益は改善し、偏差も下がっています。ただ思うほどの効果はありませんでした。ちなみに-11%のロスカットラインはロスカットがない場合の値になっています。-10%と滑らかに接続しないのは気になります。MFEの累積グラフには-16%あたりまで勝ちトレードが経験した含み損があるので、このまま-16%くらいまでロスカットラインを切り下げていくと徐々に収益が改善するのかもしれません。

まとめ

CB-PBシステムについて2パターンの仕切り方法で検証を行ってみました。どちらもいまいちでした。シャンデ氏の著書には、十分に成熟したマーケットを対象にするとあるため、長い期間に渡ってヨコヨコであったり、緩やかに上昇しているような銘柄に絞れば話は変わってくるのかもしれません。