以前、一緒の職場だった先輩が共同創業した会社が今月上場するそうだ。
今の時価総額が
1120億円
株主状況を見ると、先輩の持ち分は
8.68%
ようするに、先輩の持ち株の価値は
100億円 超え!
マジか……。
どこで こんなに差がついたんだろう……。
まぁ先輩の出身大学は
いつも通り東京大学
差は学生時代かららついていたけどさ……。
私は このままじゃ駄目だと思う。
今いる職場で汗水鼻水を垂らしながら身を粉にして働いて、僅かばかりの希望にかけて「昇格」を目指すか……
私の技術力は標準レベルも無いけど、「今より給料の良い会社」を見つけ出し転職を目指すか……
商才もビジョンも何をするのも決まってないけど、起業してみるか……
老い先短い人生を、本気で考えるしか無い…30代にも言ってたけどさ。
このブログで唯一 人気の記事は
システムトレードのバックテスト結果
すでにネタ切れだけどネットを調査してストラテジーのバックテストを行ってみた。
今回は「押し目買いシステム」と書かれたブログにソースコードとバックテストを見つけたよ。
このブログではRubyを使って検証している。
ストラテジの原作は次の書籍「高勝率システムの考え方と作り方と検証」に掲載されているらしい。
では詳細を見てみよう。
ヒストリカルボラティリティを使った押し目買いの期待値検証
ブログには次のように書かれていた。
- 1.トレード対象の銘柄は1株当たり5ドル以上で、200 日単純移動平均線を上回っていること。これによって、長期的な上昇トレンドであることが示される。
- 2.過去21 日(1カ月)での1日の平均出来高が少なくとも25 万株あること。これで、流動性がある銘柄であることが保証される。
- 3.100 日HV(ヒストリカルボラティリティ)の値が30 を超える(ヒストリカルボラティリティの定義については付録を参照のこと)。
- 4.10 日ADX(アベレージ・ディレクショナル・インデックス)の値が30 を超える(ADX の定義については付録を参照のこと)。
- 5.その銘柄が2日以上、続けて下げて引けていること。
- 6.今日、その銘柄はY期間移動平均線(Y=4、5、6)よりも少なくともX%(X=4%、5%、6%)下で引けること。この点については、具体例を見れば明らかになるだろう。
- 7.上のルールが満たされていれば、翌日に今日の終値よりもさらにZ%(Z=4~10%)下に指値を入れて買う。
- 8.3期間単純移動平均線を上回って引ける日に、終値で手仕舞う
ローレンス・A・コナーズ他『高勝率システムの考え方と作り方と検証』(PanRolling、2014年)
意味不明……。
要するにストラテジーは次のようになる。
【買いルール】
- 終値 > 200日移動平均
- 100日HV(ヒストリカルボラティリティ) > 30
- 10日ADX > 30
- 2日連続陰線
- 4日移動平均からの乖離率 < -4%
上記を満たす場合に当日に前日比 -4%の位置で売買可能ならば買い。
つまり当日に取引があり、安値が前日比 -4%以下を満たしている場合に買い。
【売りルール】
次を満たす場合に、終値で手仕舞う。
- 3期間単純移動平均線を上回って引ける日
最初からこう書けば良いのに……。
難しいストラテジではないが、「ヒストリカルボラティリティ」の実装が必要だ。
ヒストリカルボラティリティ(Historical Volatility:HV)
ヒストリカルボラティリティは一度実装した。
でも上手く行かなかったなぁ……。
再度見てみよう。
ヒストリカル・ボラティリティ(HV)は、過去の価格の変動率をもとにして計算されるテクニカル指標。
ヒストリカルボラティリティを求めるには、
まず「価格変動率」を求める。
価格変動率=log|V(t)÷V(t-1)|
ここで、
- log:自然対数
- V(t):今日の価格(株価)
- V(t-1):昨日の価格(株価)
そして、ある一定期間(25日や30日がよく使われる)の価格変動率から標準偏差を求める。
1日あたりのヒストリカル・ボラティリティ=標準偏差=分散の平方根
この標準偏差が「1日あたりのヒストリカルボラティリティ」だ。
なお、一般的にヒストリカルボラティリティは「年率」で考えることが多いので、年率換算する。
ヒストリカルボラティリティ(年率:%)=年間の営業日数の平方根 × 1日あたりのヒストリカルボラティリティ × 100
- 年間の営業日数:250日を使うのが一般的
- 平方根:√(ルート)のこと
まぁ計算式で書くと分かりにくいが、Excelで計算すると分かりやすい。
ここで、LN 関数は、e を底とする対数(自然対数)を計算する関数。
STDEVPで算出された標準偏差は、1日当たりのHVを表している。
なお分散は計算コストを減らした書き方ができる。
最近のパソコンの計算速度は早いが、数学に詳しければアルゴリズムを工夫できる。
バックテスト結果
計算時間は全銘柄で10時間20分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
株価データ: 日足 銘柄リスト: 全銘柄 2000/01/06~2021/09/15における成績です。 ---------------------------------------- 全トレード数 3869 勝ちトレード数(勝率) 2342(60.53%) 負けトレード数(負率) 1527(39.47%) 全トレード平均利率 1.67% 勝ちトレード平均利率 6.94% 負けトレード平均損率 -6.40% 勝ちトレード最大利率 83.67% 負けトレード最大損率 -58.91% 全トレード平均期間 5.61 勝ちトレード平均期間 4.31 負けトレード平均期間 7.62 ---------------------------------------- 必要資金 ¥2,307,600 最大ポジション(簿価) ¥2,999,300 最大ポジション(時価) ¥3,322,100 純利益 ¥27,739,900 勝ちトレード総利益 ¥69,615,330 負けトレード総損失 -¥41,875,430 全トレード平均利益 ¥7,170 勝ちトレード平均利益 ¥29,725 負けトレード平均損失 -¥27,423 勝ちトレード最大利益 ¥393,600 負けトレード最大損失 -¥202,300 プロフィットファクター 1.66 最大ドローダウン(簿価) -¥1,215,000 最大ドローダウン(時価) -¥1,310,700 ---------------------------------------- 現在進行中のトレード数 5 ---------------------------------------- 平均年利 54.64% 平均年利(直近5年) 13.49% 最大連勝 15回 最大連敗 8回 ---------------------------------------- [年度別レポート] 年度 取引回数 運用損益 年利 勝率 PF 最大DD 2021年 158回 -¥432,600円 -18.75% 51.90% 0.78倍 -31.63% 2020年 260回 ¥289,840円 12.56% 57.31% 1.08倍 -42.31% 2019年 202回 ¥645,024円 27.95% 58.91% 1.30倍 -58.91% 2018年 231回 ¥172,463円 7.47% 55.41% 1.05倍 -39.29% 2017年 227回 ¥881,720円 38.21% 55.51% 1.36倍 -30.10% 2016年 222回 ¥206,450円 8.95% 55.41% 1.06倍 -32.60% 2015年 240回 -¥52,285円 -2.27% 50.83% 0.98倍 -28.42% 2014年 265回 ¥290,800円 12.60% 55.47% 1.08倍 -27.71% 2013年 260回 ¥2,616,722円 113.40% 58.08% 1.85倍 -31.37% 2012年 174回 ¥1,539,300円 66.71% 64.37% 1.73倍 -41.70% 2011年 175回 ¥1,546,016円 67.00% 59.43% 1.62倍 -40.39% 2010年 143回 ¥2,219,246円 96.17% 69.93% 2.71倍 -27.14% 2009年 172回 ¥2,802,700円 121.46% 69.19% 2.85倍 -26.23% 2008年 127回 ¥859,100円 37.23% 62.99% 1.60倍 -31.82% 2007年 111回 ¥1,445,200円 62.63% 63.96% 2.93倍 -27.39% 2006年 107回 ¥580,600円 25.16% 60.75% 1.58倍 -21.60% 2005年 188回 ¥3,858,000円 167.19% 81.91% 7.69倍 -17.99% 2004年 182回 ¥1,914,600円 82.97% 69.78% 2.35倍 -23.64% 2003年 154回 ¥3,050,500円 132.19% 74.68% 4.10倍 -23.64% 2002年 71回 ¥790,000円 34.23% 70.42% 2.44倍 -18.63% 2001年 93回 ¥976,500円 42.32% 70.97% 2.49倍 -21.30% 2000年 107回 ¥1,540,000円 66.74% 76.64% 4.81倍 -11.11% |
利益曲線は次のとおり。
おぉ!
意外や意外、単調増加をするんだね。
ただ今年に入って負けてる……。
昨年バックテストしてたら実運用して損失を出していたかもしれない。
また、負ける場合には一週間程度の株の保有になるようなので、実用するにはタフな精神力が必要だ。
更に「手仕舞い」では「終値で手仕舞う」必要があるので
購入している株価を板に張り付いて見ておく
必要がある。
マザーズ市場のみ見てみる
この手法はマザーズ市場だとより安定する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
株価データ: 日足 銘柄リスト: マザーズ一覧 2002/06/03~2021/09/08における成績です。 ---------------------------------------- 全トレード数 496 勝ちトレード数(勝率) 301(60.69%) 負けトレード数(負率) 195(39.31%) 全トレード平均利率 2.16% 勝ちトレード平均利率 7.92% 負けトレード平均損率 -6.73% 勝ちトレード最大利率 48.39% 負けトレード最大損率 -42.31% 全トレード平均期間 5.57 勝ちトレード平均期間 4.28 負けトレード平均期間 7.56 ---------------------------------------- 必要資金 ¥671,000 最大ポジション(簿価) ¥1,987,700 最大ポジション(時価) ¥2,057,300 純利益 ¥4,453,000 勝ちトレード総利益 ¥10,071,100 負けトレード総損失 -¥5,618,100 全トレード平均利益 ¥8,978 勝ちトレード平均利益 ¥33,459 負けトレード平均損失 -¥28,811 勝ちトレード最大利益 ¥229,500 負けトレード最大損失 -¥202,300 プロフィットファクター 1.79 最大ドローダウン(簿価) -¥556,600 最大ドローダウン(時価) -¥617,900 ---------------------------------------- 現在進行中のトレード数 2 ---------------------------------------- 平均年利 34.93% 平均年利(直近5年) 18.34% 最大連勝 10回 最大連敗 5回 ---------------------------------------- [年度別レポート] 年度 取引回数 運用損益 年利 勝率 PF 最大DD 2021年 23回 ¥61,900円 9.23% 60.87% 1.29倍 -20.13% 2020年 58回 ¥17,100円 2.55% 58.62% 1.02倍 -42.31% 2019年 57回 ¥142,300円 21.21% 63.16% 1.29倍 -21.12% 2018年 51回 ¥113,100円 16.86% 50.98% 1.16倍 -21.53% 2017年 29回 ¥280,900円 41.86% 68.97% 2.11倍 -16.82% 2016年 36回 ¥31,100円 4.63% 55.56% 1.05倍 -32.60% 2015年 25回 ¥332,800円 49.60% 60.00% 2.85倍 -11.21% 2014年 52回 ¥230,400円 34.34% 65.38% 1.39倍 -26.37% 2013年 46回 ¥1,047,600円 156.13% 60.87% 3.30倍 -12.79% 2012年 21回 ¥452,800円 67.48% 71.43% 3.02倍 -22.58% 2011年 24回 ¥89,700円 13.37% 54.17% 1.15倍 -40.39% 2010年 22回 ¥548,200円 81.70% 77.27% 3.61倍 -20.68% 2009年 19回 ¥563,500円 83.98% 63.16% 4.68倍 -9.55% 2008年 8回 ¥108,400円 16.15% 87.50% 9.67倍 -4.69% 2007年 10回 ¥136,900円 20.40% 80.00% 10.13倍 -2.58% 2005年 4回 ¥271,100円 40.40% 100.00% ∞倍 0.00% 2004年 7回 -¥3,800円 -0.57% 42.86% 0.96倍 -16.35% 2003年 3回 ¥17,300円 2.58% 66.67% 1.36倍 -11.48% 2002年 1回 ¥11,700円 1.74% 100.00% ∞倍 0.00% |
利益曲線は次のとおり。
2004年は下がっているけど、当時のマザースの値動きは当てにならないので無視して良いだろう。
低位株・新興株にはより有効な手法のようだ。
まとめ
今回、ヒストリカルボラティリティを実現した。
以前は実装に失敗したので、過去の手法にも適用して再度バックテストにチャレンジしたい。
またProtraのライブラリ内に実装すれば、ヒストリカルボラティリティのグラフを可視化することもできそうだ。
色々と進める事はできるがモチベーションが沸かないなぁ……。
ソースコード
バックテストには無料OSSの「Protra」を利用した。
TIlib、Utility、TrendCheck、TOPIXライブラリはGitHubに置いている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# loop-type: date-only //============================== require "TIlib" require "Utility" require "TrendCheck" //========================================== // // [高勝率システムの考え方と作り方と検証] // 終値 > 200日移動平均 // 100日HV(ヒストリカルボラティリティ) > 30 // 10日ADX > 30 // 2日連続陰線 // 4日移動平均からの乖離率 < -4% // //========================================== codes = CodeList if ($code_num && $code_num != Length(codes)) Print("前回と異なる銘柄リストでは実行できません。") Dummy end $code_num = Length(codes) //グローバル変数を初期化 if (!$__INIT__) $budgetIni = 3000000 $buyUnit = 500000 // 1回の購入資金 (50万円) $MaxHoldDay = 0 // 最大保有日数 $shortSelling = 0 // 空売り戦略 Yes(1)/No(0) $Interest = 1 // 無制限(0) / 単利(1) / 複利(2) $reverse = 0 // 購入順序 昇順(0) / 降順(1) $udcount = 0 // 騰落レシオ利用数 Init() //------------------------------------------------ $DMI = [$code_num] $MA = [$code_num] $MA3 = [$code_num] $DiffMA = [$code_num] //------------------------------------------------ InitDone() // 騰落レシオ初期化 $__INIT__ = 1 end // 価格変動率 def DayBefore(i) // 前日比 = (終値) ÷ 1日前の終値 return Log((float){i}Close / (float){i - 1}Close ) end // ヒストリカル・ボラティリティ // hv = Math.sqrt(250) * closes[-101..-1].log.dev(100)[-1] * 100 // HV = √250 * 1日あたりのヒストリカルボラティリティ * 100 // 過去20日分(log|今日の価格(株価)÷昨日の価格(株価) |) def CalcHV(num) // 1日あたりのヒストリカルボラティリティ day_hv = 0 a1 = 0 a2 = 0 c = 0 while (c < num) if ! ({(int)(c * -1)}Close && {(int)(c * -1 - 1)}Close) return 0 end a0 = DayBefore(c * -1) a1 = a0 * a0 + a1 a2 = a0 + a2 c = c + 1 end // 標準偏差 = √Σ(a_i - a)^2/N = Σa_i^2/N - (Σa_i/N)^2 day_hv = Sqrt(a1 / (float)num - (a2 / (float)num) * (a2 / (float)num)) // 年間の営業日数の平方根 × 1日あたりのヒストリカルボラティリティ × 100 return Sqrt(250) * day_hv * 100 end def Main(i) //================================================== // 条件(買条件, 売条件共通部分) //================================================== //まだ上場していない銘柄は株価データがないためnullが返る if (Index == null) return end if ($order[(int)Code] == -1) $order[(int)Code] = i end if ! ($DiffMA[i]) // 銘柄ごとのグローバル変数を初期化する $DMI[i] = DMI_new(14, 10, 0) // ADX(10日) $MA[i] = MA_new(200) // 200日移動平均線 $MA3[i] = MA_new(3) // 3日移動平均線 $DiffMA[i] = DiffMA_new(4) // 4日移動平均線乖離率 $hold[i] = 0 return end //指標の計算を1日進める DMI_next($DMI[i]) MA_next($MA[i]) MA_next($MA3[i]) DiffMA_next($DiffMA[i]) //================================================== // 保有してない→購入 //================================================== if (! $hold[i]) adx = DMI_adx($DMI[i]) ma = MA_value($MA[i]) diff = DiffMA_value($DiffMA[i]) if ! (ma && adx && diff && Close && {-1}Close && Open && {-1}Open) return end //================================================== // 売買(買い) //================================================== // 終値 > 200日移動平均 flag1 = Close > ma // 100日ヒストリカルボラティリティ > 30 flag2 = CalcHV(100) > 30 // ADX > 30 flag3 = adx > 30 // 2日連続陰線 flag4 = ({-1}Open > {-1}Close) && (Open > Close) // 4日移動平均からの乖離率 < -4% flag5 = diff < -4 if (flag1 && flag2 && flag3 && flag4 && flag5) PrintLog("買い候補") $buyflag[i][0] = 1 $buyflag[i][1] = diff $buyflag[i][2] = 1 $buyCnt = $buyCnt + 1 end //================================================== // 保有している→売却 //================================================== elsif ($hold[i]) if ($set[i] < 1) $set[i] = 1 return end $set[i] = $set[i] + 1 //================================================== // 売買(売り) //================================================== ma = MA_value($MA3[i]) if ! (ma) return end // 終値 > 200日移動平均 flag1 = Close > ma //3期間単純移動平均線を上回って引ける日 if (flag1) PrintLog("利益確定") $sellflag[i] = 1 $set[i] = 0 end end end def CheckHighLow2(t) t = Yobine(t, 0) if ! ({1}High > t && t > {1}Low) return 0 end if (t > {1}Open) return 0 else return t end end //================================================== // 買い(指値) //================================================== def Buying2(i) if (0 == PricedataExistCheck({1}Open)) t = 0 if ($buyflag[i][0] == 1) // 10) [翌日指値(寄付)][終値(-4.00%)]以下で[買い]を仕掛ける t = CheckHighLow2(Close * 0.96) end if (t) BuyingLimitedPrice(i, 1, t) end end end //==================== // 買い処理 //==================== def SortBuy() if ! (HasPricedata({1}Open)) return end $long = 0 $long = Num($buyUnit, Close) codeset = $order[(int)Code] Buying2(codeset) end //==================== // 売り処理(その日の引けで売る) //==================== def Sell_(i) if ($sellflag[i]) // 当日のCloseで売る Selling(i) $sellflag[i] = 0 $buyflag[i][2] = 0 end // 使用した$buyflag 配列を初期化 if ($buyflag[i][0]) $buyflag[i][0] = 0 $buyflag[i][1] = 0 end end //==================== // 銘柄コードを変えながらMain関数,BuySell関数を実行 //==================== Print("-------------------------------------------------") Print("日付 = "+ Year + "/" + Month + "/" + Day) $buyCnt = 0 // 購入数初期化 i = -1 while (i + 1 < $code_num) i = i + 1 {codes[i]}Main(i) end i = 0 if ($buyCnt) sortList = SelectionSort(10, 0) cnt = $buyCnt if ($buyCnt > 10) cnt = 10 end while i < cnt {sortList[i]}SortBuy() i = i + 1 end end //---------------------------------------------- i = -1 while (i + 1 < $code_num) i = i + 1 {codes[i]}Sell_(i) end |