8月6日です。広島原爆の日です。今日は一日、平和の事を考えて過ごす日です。
昨日は、相方がパソコンが落として壊し、修理手続きとなりました。代車じゃあるまいし代パソコン貸してくれるはず無いです・・・。
本日、相方が車の左バンパーとボディーを派手に壁で擦り、板金・塗装業者を調査中です。ディーラーに依頼したらマージン取られるだけです・・・。。
そして、8月6日の深夜、家で飼ってるノコギリクワガタが虫かごから逃げた事を発見し部屋中を探すハメに・・・。キチンと閉めた?いや事実逃げてるし・・・・。
・・・・勘弁してください。
朝6:45に家を出て~夜0:30に家に戻る生活なのです・・・・、仕事できない私が悪い?そうですね・・・(イラ。
パトラッシュ・・・僕はもう疲れたよ。。
では、本題。
斉藤 正章(さいとう まさあき)氏は、システム開発会社で勤務しながら2001年に元手30万円で株式投資を開始し2年9ヶ月で1億1千万円にした個人投資家として注目を集めたトレーダーです。
「斉藤正章氏の手法の有効性検証(protraシストレ)」でも紹介しています。
この方の手法は以前サンプル実装を利用してバックテストをしましたが結果は良くなかったです。
ですが、Web上に「システムトレードで11年間で10勝1敗の実績をあげている私の投資手法」という記事を載せており、それによると未だに勝ち続けているようです。
であれば、再度検証をしてみる必要があります。
利用しているシステムトレードの代表的な3つの戦略が丁寧に書かれています。
- 「順張り買い」戦略
- 「空売りデイトレ」戦略
- 「逆張り」戦略
今回は「順張り買い」戦略をProtraで実現します。
「順張り買い」戦略
【仕掛け条件】
次をすべて満たすとき、仕掛けとなります。
- 1) 過去500日間のボラティリティがX%以上(10%~20%)
- 2) 過去100日間のボラティリティがY%より小さい(1%~9%)
- 3) 終値が過去50日間の最高値を更新(レンジを上抜けした銘柄を選ぶ)
- 4) 終値が過去50日間の終値の最高値を更新した銘柄が、市場全体で50銘柄以上ある場合
X%の部分は10%~20%、Y%の部分は1%~9%の数値との事です。
この2つの条件で、「もともと値動きがあった銘柄が、直近2~3ヶ月間は値動きが乏しく、もみ合いの状態が続いている」銘柄を探しているとのことです。
【手仕舞い条件】
以下のいずれかを満たすとき、手仕舞いとなります。
- 利食い:終値と3日移動平均乖離率が+15%以上になった場合
- 損切り:終値が過去20日間の終値の最安値を更新した場合
「ボラティリティ」と乖離率と勘違いしていたので、ググって調べました。
「ボラティリティ」とは何か?
ボラティリティは、2つあります。
「ヒストリカルボラティリティ」
「インプライドボラティリティ」
「ヒストリカルボラティリティ」は、過去n日間の前日比率に基づき、将来のm日間の価格変動率を求める指標です。
ヒストリカルボラティリティ(Historical Volatility:HV)
計算式は次のとおりです。
HV=ヒストリカル・ボラティリティ=標準偏差=分散の平方根
分散=((データ-平均値)の2乗)の総和÷個数
データ-平均値=前日比-前日比平均
前日比 = Log(終値) ÷ 1日前の終値
「標準偏差」を愚直にprotraで実現すると面倒です。
ですが理系の強み。
それは数学の美しさに気づいている事です。
「平均・分散」の計算を、計算コストを減らして短いソースコードで書くで、説明済ですが、分散を求めるには平均を求める必要性があるため式の変換が肝です。
これで、平均を先に求める必要はありません。
ここで、
a=前日比
n=n日
です。
困りごと
上記サイトには「ボラティリティ」としか書かれていません。ヒストリカル・ボラティリティで正しいのかな・・
また、サイトにソースコードが載っているものの、データベースからデータ取り出し部分だけで、肝心の計算式が分かりません。
再現させないための斎藤氏の策略にさえ感じます・・・。
一般には、
- ローリスク(標準偏差)0%~12.0%
- ミドルリスク(標準偏差)12.0%~22.0%
- ハイリスク(標準偏差)22.0%以上
です。
ですが100日HV、500日HVを計算させると、この範囲内に収まりません・・・
仕方なく、数値を大きくするために「×245の平方根」して、年間ボラティリティを求めてます。
ソースコード
Utility、TrendCheckライブラリは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 254 255 256 257 258 259 |
# loop-type: date-only //============================== require "TIlib" require "Utility" require "TrendCheck" //============================== // 「順張り買い」戦略(斉藤正章) // 株価のブレイクアウトを利用して収益をあげる「順張り買い」戦略 // https://toushi-kyokasho.com/system-trade/ //------------------------------ // // 【買いルール】 // 1) 過去500日間のボラティリティがX%以上(10%~20%) // 2) 過去100日間のボラティリティがY%より小さい(1%~9%) // 3) 終値が過去50日間の最高値を更新(レンジを上抜けした銘柄を選ぶ) // 4) 終値が過去50日間の終値の最高値を更新した銘柄が、市場全体で50銘柄以上ある場合 // // 【手仕舞いルール】 // 1) 利食い:終値と3日移動平均乖離率が+15%以上になった場合 // 2) 損切り:終値が過去20日間の終値の最安値を更新した場合 codes = CodeList if ($code_num && $code_num != Length(codes)) Print("前回と異なる銘柄リストでは実行できません。") Dummy end $code_num = Length(codes) //グローバル変数を初期化 if (!$__INIT__) $budgetIni = 10000000 $budget = $budgetIni // 投資総額 (1000万円) $buyUnit = 1000000 // 1回の購入資金 (100万円) $MaxHoldDay = 3 // 最大保有日数 $Interest = 1 // 無制限(0) / 単利(1) / 複利(2) $reverse = 0 // 購入順序 昇順(0) / 降順(1) $ratiodate = 50 // 騰落レシオ日数 //------------------------------------------------ $BB3 = [$code_num] //------------------------------------------------ Init() $__INIT__ = 1 end def DayBefore(i) // 前日比 = (終値) ÷ 1日前の終値 return Log((float){i}Close / (float){i - 1}Close ) end //ヒストリカル・ボラティリティ def CalcHV(num) if ! (Index > num) return 0 end c = 0 while (c <= num) if ! ({(int)(-1 * c)}Close) return 0 end c = c + 1 end c = 0 a1 = 0 a2 = 0 while (c < num) a0 = DayBefore(c * -1) a1 = a0 * a0 + a1 a2 = a0 + a2 c = c + 1 end // ボラティリティ = 分散の平方根 return Sqrt(a1 / (float)num - (a2 / (float)num) * (a2 / (float)num)) * Sqrt(245) * 100 end def Main(i) //================================================== // 条件(買条件, 売条件共通部分) //================================================== //まだ上場していない銘柄は株価データがないためnullが返る if (Index == null) return end if ! ($order[(int)Code]) $order[(int)Code] = i end if ! ($BB3[i]) //Tilibのオブジェクト生成 $BB3[i] = BB_new(3) //銘柄ごとのグローバル変数を初期化する $hold[i] = 0 return end //指標の計算を1日進める BB_next($BB3[i]) // 3) 終値が過去50日間の最高値を更新(レンジを上抜けした銘柄を選ぶ) if ! (Index > 50) return end c = 1 flag = 1 while (c < 50) if {-c}High >= Close flag = 0 break end c = c + 1 end // ここまで ======================================== if (1 == PricedataExistCheck(Close)) return end //================================================== // 保有してない→購入 //================================================== if ($ratio >= 50 && ! $hold[i]) //================================================== // 売買(買い) //================================================== // 1) 過去500日間のボラティリティがX%以上(10%~20%) a1 = CalcHV(500) // 2) 過去100日間のボラティリティがY%より小さい(1%~9%) a2 = CalcHV(100) if (flag && a1 >= 15 && 9 > a2) PrintLog("購入予定") $buyflag[i][0] = 1 // 好きなパラメータをもとにソート $buyflag[i][1] = a1 $buyCnt = $buyCnt + 1 end //================================================== // 保有している→売却 //================================================== elsif ($hold[i]) if ! (Close) return end ma3 = BB_value($BB3[i]) // 指標の計算に必要な日数を経過していない場合は何もしない if ! (ma3) return end r3 = 100*(Close - ma3)/ma3 c = 1 low = Close while c < 20 if {-c}Close < low low = {-c}Close end c = c + 1 end //================================================== // 売買(売り) //================================================== // 1) 利食い:終値と3日移動平均乖離率が+15%以上になった場合 if (r3 >= 15) PrintLog("利食い") $sellflag[i] = 1 // 2) 損切り:終値が過去20日間の終値の最安値を更新した場合 elsif (low >= Close) PrintLog("手仕舞い") $sellflag[i] = 1 end end end //==================== // 買い処理 //==================== def SortBuy(i) if (PricedataExistCheck(Close)) return end $long = 0 $long = Num($buyUnit, Close) codeset = $order[(int)Code] Buying(codeset) end //==================== // 売り処理 //==================== def Sell_(i) if (PricedataExistCheck(Close)) return end if ($sellflag[i]) Selling(i) //Print($set[i]) $sellflag[i] = 0 end end //================================================== // 終値が過去50日間の終値の最高値を更新した銘柄数 //================================================== def HighMaxNum() $ratio = 0 if ! (Index > $ratiodate) return end c = 1301 while (c < 9999) cc = (string)c i = 1 flag = 1 while (i < $ratiodate) if (! {-i}{cc}Close || !{cc}Close || {-i}{cc}Close >= {cc}Close) flag = 0 break end i = i + 1 end c = c + 1 if (flag) $ratio = $ratio + 1 end end Print("-------------------------------------------------") Print("日付 = "+ Year + "/" + Month + "/" + Day) Print("過去50日間の終値の最高値を更新した銘柄数 = "+ $ratio) end //==================== // 銘柄コードを変えながらMain関数,BuySell関数を実行 //==================== SortInit() // ソート初期化 //4) 終値が過去50日間の終値の最高値を更新した銘柄が、市場全体で50銘柄以上ある場合 HighMaxNum() i = -1 while (i + 1 < $code_num) i = i + 1 {codes[i]}Main(i) end i = -1 while i + 1 < $code_num i = i + 1 {codes[i]}Sort(i) end i = -1 while i + 1 < $buyCnt i = i + 1 {$sortList2[i]}SortBuy(i) end i = -1 while i + 1 < $code_num i = i + 1 {codes[i]}Sell_(i) end |
バックテスト結果
結果は、売買銘柄が一つもありません。
「a1 >= 30 && 20 > a2」として結果を出してみました。
東証一部だけの計算に丸2日かかりました・・・
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 |
株価データ: 日足 銘柄リスト: 東証1部 1998/01/05~2019/07/23における成績です。 ---------------------------------------- 全トレード数 536 勝ちトレード数(勝率) 224(41.79%) 負けトレード数(負率) 312(58.21%) 全トレード平均利率 3.04% 勝ちトレード平均利率 13.45% 負けトレード平均損率 -4.42% 勝ちトレード最大利率 139.08% 負けトレード最大損率 -21.10% 全トレード平均期間 49.11 勝ちトレード平均期間 80.12 負けトレード平均期間 26.85 ---------------------------------------- 必要資金 ¥9,009,500 最大ポジション(簿価) ¥9,999,800 最大ポジション(時価) ¥16,104,800 純利益 ¥16,051,300 勝ちトレード総利益 ¥28,661,200 負けトレード総損失 -¥12,609,900 全トレード平均利益 ¥29,946 勝ちトレード平均利益 ¥127,952 負けトレード平均損失 -¥40,416 勝ちトレード最大利益 ¥1,401,200 負けトレード最大損失 -¥205,600 プロフィットファクター 2.27 最大ドローダウン(簿価) -¥1,354,800 最大ドローダウン(時価) -¥2,501,700 ---------------------------------------- 現在進行中のトレード数 2 ---------------------------------------- [年度別レポート] 年度 取引回数 運用損益 勝率 最大DD PF 2000年 1回 ¥12,000円 100.00% 0.00% ∞倍 2001年 8回 -¥8,800円 25.00% -7.69% 0.96倍 2003年 22回 ¥1,018,500円 59.09% -11.11% 3.67倍 2004年 22回 ¥750,300円 54.55% -8.14% 2.87倍 2005年 57回 ¥1,983,500円 43.86% -10.37% 2.69倍 2006年 12回 ¥3,212,500円 83.33% -11.34% 20.59倍 2007年 23回 -¥200,500円 30.43% -8.12% 0.57倍 2008年 3回 -¥71,600円 33.33% -6.51% 0.05倍 2009年 31回 -¥413,800円 25.81% -9.18% 0.52倍 2010年 64回 -¥592,500円 31.25% -10.24% 0.64倍 2011年 23回 ¥246,400円 39.13% -21.10% 1.22倍 2012年 40回 ¥704,500円 42.50% -9.69% 1.78倍 2013年 21回 ¥5,624,600円 61.90% -8.23% 20.71倍 2014年 53回 -¥53,800円 30.19% -11.93% 0.96倍 2015年 45回 ¥1,423,100円 48.89% -15.19% 2.31倍 2016年 9回 ¥111,100円 55.56% -5.21% 2.46倍 2017年 71回 ¥2,090,500円 45.07% -12.97% 2.39倍 2018年 29回 ¥266,300円 41.38% -7.98% 1.40倍 |
利益曲線はこちら。
【写真】ARuFa氏
こんなグラフ期待してないです・・・・。
何かが違います・・・・。
でも、もう何もかも調べる気になれません・・・・。
色々、疲れた・・・・。
まとめ
ボラティリティが下記の両方を満たす数値が見つかりません。
- 1) 過去500日間のボラティリティがX%以上(10%~20%)
- 2) 過去100日間のボラティリティがY%より小さい(1%~9%)
そして、Protraの標準関数で出来ない指標が増えてきました。
こうなると「Protra使う意味あるの?」
と思えてきます。
分足や板読みを使った株取引(日本株)であれば、マネックス証券のTradeStation一択です。
日足であれば、最近はSmart Tradeのサービスであるクオンテックス(QuantX)という、株式投資をPythonで行うための無料プラットフォームがあります。
開発したアルゴリズムはSmart Tradeのマーケットプレイスでも販売できます。
が・・・ゼロから作る方が早いかなあ…。
バグだらけの自分のプログラムだと信じれるものが何もなくなるね。
でも、protraの困りごとは次のとおり。
- 独自言語での実装で、便利なライブラリなどを使えない
- 自動パラメータ最適化ができない
- ディープラーニングなどを試せない
- ファンダメンタル分析など、新規概念を試せない
- WindowsでGUIありき
- 速度が激遅(自作の方が遅いかも・・)
今までの経験上、自作すると満足して使わず終わってるのが怖い・・
ネットネット株判定、うねり取り練習ツールなどなど。
OmegaChartは、ファンダメンタル分析も取り入れる拡張されてました。