2013年頃から「逆指値」の売買システムトレードがトレンドのようです。
逆指値とは、通常の指値注文とは異なり「ある価格以上になったら(指値、または成行で)買い」という注文です。
このため、どうしてもスリッページ(注文を行ったときの値段と、約定したときの値段とのズレ)が発生します。
短所がありますが、資金を徐々に投入する必要があるヘッジファンドには真似出来ない手法です。
トレシズ氏など、多くのトレーダーが採用しています。
トレード日記側のポートフォリオを全て逆指値戦略で構成。
スリッページという欠点を超える長所があるため。(1)逆指値では、当日上昇(または下落)している銘柄をとらえることができる
(2)どちらかというと市場暴落時に強い場合が多い
(3)高値や期間高値などの節目を超えた瞬間に仕掛けることができる逆指値を語るシリーズ(11)
301 Moved Permanently
今回の日記は、2年前は難解で実現が無理だったcosisin氏の手法のバックテスト第三段です。
cosisin氏の平滑移動平均乖離率を使った逆張り手法の有効性検証
cosisin氏は、2012年〜2014年頃にブログを書かれていたイザナミのシステムトレーダーです。
前日比、期間高値、期間安値、移動平均乖離率、期間上昇率、4本値などを利用した手法を得意としています。
複雑な手仕舞いやシグナル数により購入銘柄数を変えたり、複数の逆指値を分岐させたり、ダウ平均を使ったり・・と、今のイザナミで販売されている手法に影響を与えたのは間違いありません。
今回実現するのは、2013年09月23日の日記です。6年前です。
仕掛けるストラテジーを作ってみました。ギャップアップして陰線を引いたもので、過去にかなりの上昇実績があるものに次の日仕掛けます。仕掛け位置は3通りに分けてみました。前日の高値が遠い場合は、s高の1ティック手前で仕掛けます。
平滑移動平均乖離率を利用した大きな意味はありません。移動平均乖離率5日より少し期待値が良かったから使ってみました。
との事です。
【買いルール】
- 1) [始値→終値(率)]が[0]より[小さい]
- 2) [翌日寄付ギャップ(率)(1日前)]が[0]より[大きい]
- 3) [平滑移動平均乖離率(6)]が[0]より[大きい]
- 4) 過去[75]日間に[移動平均乖離率(高値)(10)]が[40]より[大きい(同じ含む)]日が[1]日以上[存在する]
- 5) [高値]が[値幅制限上限]より[大きい(同じ含む)]
- IF(5) 6) [翌日逆指値(終日)][値幅制限上限(-1Tick)]で[買い]を仕掛ける
- IF(Not 5) 7) [終値→高値(率)]が[5]より[小さい]
- IF(7) [翌日逆指値(終日)][終値(+5.00%)]で[買い]を仕掛ける
- IF(Not 7) [翌日逆指値(終日)][高値(+1Tick)]で[買い]を仕掛ける
【手仕舞いルール】
- 1) [翌日逆指値(終日)][終値(-1Tick)]で手仕舞いする
複雑過ぎる・・・。
イザナミを使った事がないので、推測しながらロジックを組み、各手法の計算方法を理解した上で実装・・・・。
全画面がキャプチャされているので、イザナミで実装するなら10分程度で可能だと思います。
Protraで実装する場合は次のような追加実装が必要です。
- 「値幅制限」
- 「移動平均乖離率(高値)」(TIlib.ptに追加)
- 「始値→終値(率)」
- 「終値→高値(率)」
- 呼び値を使った動的な買い
過去にはTIlib.pt に関数追加する事ですら、大騒ぎでしたが、それが霞んで見えるほど複雑です。
丸半日を使いました。
なお、今回は手仕舞いは「翌日の寄付」としています(さらに複雑になるので)。
ソースコード
TIlib、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 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# loop-type: date-only //============================== require "TIlib" require "Utility" require "TrendCheck" // ====================================== // cosisin氏の平滑移動平均乖離率を使った逆張り手法 // ====================================== // //【基本設定】 // 1) 株価の低い場合はランキングしない[50]万円以下 // 2) 売買代金の少ない場合はランキングしない(売買代金の[3]日間平均が[5]千万円以下の場合) // 3) 単利利用、通年 // 4) 全ての銘柄対象 // //【買いルール】 // 1) [始値→終値(率)]が[0]より[小さい] // 2) [翌日寄付ギャップ(率)(1日前)]が[0]より[大きい] // 3) [平滑移動平均乖離率(6)]が[0]より[大きい] // 4) 過去[75]日間に[移動平均乖離率(高値)(10)]が[40]より[大きい(同じ含む)]日が[1]日以上[存在する] // 5) [高値]が[値幅制限上限]より[大きい(同じ含む)] // IF(5) 6) [翌日逆指値(終日)][値幅制限上限(-1Tick)]で[買い]を仕掛ける // IF(Not 5) 7) [終値→高値(率)]が[5]より[小さい] // IF(7) [翌日逆指値(終日)][終値(+5.00%)]で[買い]を仕掛ける // IF(Not 7) [翌日逆指値(終日)][高値(+1Tick)]で[買い]を仕掛ける // //【手仕舞いルール】 // // 1) [翌日逆指値(終日)][終値(-1Tick)]で手仕舞いする codes = CodeList if ($code_num && $code_num != Length(codes)) Print("前回と異なる銘柄リストでは実行できません。") Dummy end $code_num = Length(codes) //グローバル変数を初期化 if (!$__INIT__) $budgetIni = 10000000 $buyUnit = 1000000 // 1回の購入資金 (100万円) $MaxHoldDay = 1 // 最大保有日数 $Interest = 1 // 無制限(0) / 単利(1) / 複利(2) $reverse = 1 // 購入順序 昇順(0) / 降順(1) $udcount = 0 // 騰落レシオ利用数 Init() // テクニカル指標初期化 -------------------------- $pastMA = [$code_num] $pastMAflag = [$code_num] i = 0 while (i < $code_num) $pastMA[i] = [75] $pastMAflag[i] = 0 i = i + 1 end $EMA = [$code_num] $HMA = [$code_num] // 騰落レシオ初期値代入($udcountの数) ---------- //------------------------------------------------ InitDone() // 騰落レシオ初期化 $__INIT__ = 1 end //================================================== // 翌日寄付ギャップダウン率(%) //================================================== def GapDownRatio() return ((float)Open - (float){-1}Close) / (float){-1}Close * 100 end // 4) 過去[75]日間に[移動平均乖離率(高値)(10)]が[40]より[大きい(同じ含む)]日が[1]日以上[存在する] def ChekPastMA(code, value, num, flag) $pastMA[code][$pastMAflag[code]] = value $pastMAflag[code] = ($pastMAflag[code] + 1) % num i = 0 sum = 0 while (i < num) if ! ($pastMA[code][i]) return 0 end if ($pastMA[code][i] >= flag) sum = sum + 1 end i = i + 1 end return sum end def Main(i) //================================================== // 条件(買条件, 売条件共通部分) //================================================== //まだ上場していない銘柄は株価データがないためnullが返る if (Index == null) return end if ! ($order[(int)Code]) $order[(int)Code] = i end if ! ($EMA[i] && $HMA[i]) //Tilibのオブジェクト生成 $EMA[i] = DiffEMA_new(6) // 平滑移動平均乖離率 $HMA[i] = DiffHighMA_new(10) // 移動平均乖離率(高値) //銘柄ごとのグローバル変数を初期化する $hold[i] = 0 return end //指標の計算を1日進める DiffEMA_next($EMA[i]) DiffHighMA_next($HMA[i]) if ($HMA) dhma = DiffHighMA_value($HMA[i]) end ret = 0 if (dhma) ret = ChekPastMA(i, dhma, 75, 40) end if ! (Index > 75) return end //================================================== // 保有してない→購入 //================================================== if (! $hold[i]) if ! ($EMA[i][1] && $HMA[i] && Close && {-1}Close) return end dema = DiffEMA_value($EMA[i]) if ! (dema) return end //================================================== // 売買(買い) //================================================== // 1) 株価の低い場合はランキングしない[50]万円以下 // 2) 売買代金の少ない場合はランキングしない(売買代金の[3]日間平均が[5]千万円以下の場合) if ! (SalesValue()/3 + {-1}SalesValue()/3 + {-2}SalesValue()/3 > 50000 && Close > 50) return end // 1) [始値→終値(率)]が[0]より[小さい] check1 = 0 > OpenToClose() // 2) [翌日寄付ギャップ(率)(1日前)]が[0]より[大きい] check2 = GapDownRatio() > 0 // 3) [平滑移動平均乖離率(6)]が[0]より[大きい] check3 = dema > 0 // 4) 過去[75]日間に[移動平均乖離率(高値)(10)]が[40]より[大きい(同じ含む)]日が[1]日以上[存在する] check4 = ret >= 1 // 5) [高値]が[値幅制限上限]より[大きい(同じ含む)] check5 = High > {-1}Close + DailyPriceLimit({-1}Close) if (check1 && check2 && check3 && check4) if (check5) // 6) [翌日逆指値(終日)][値幅制限上限(-1Tick)]で[買い]を仕掛ける PrintLog("購入予定1") $buyflag[i][0] = 1 // 好きなパラメータをもとにソート $buyflag[i][1] = dema $buyCnt = $buyCnt + 1 else // 7) [終値→高値(率)]が[5]より[小さい] check7 = 5 > CloseToHigh() if (check7) // [翌日逆指値(終日)][終値(+5.00%)]で[買い]を仕掛ける PrintLog("購入予定2") $buyflag[i][0] = 2 // 好きなパラメータをもとにソート $buyflag[i][1] = dema $buyCnt = $buyCnt + 1 else // [翌日逆指値(終日)][高値(+1Tick)]で[買い]を仕掛ける PrintLog("購入予定3") $buyflag[i][0] = 3 // 好きなパラメータをもとにソート $buyflag[i][1] = dema $buyCnt = $buyCnt + 1 end end end //================================================== // 保有している→売却 //================================================== elsif ($hold[i]) if ($set[i] < 1) $set[i] = 1 return end $set[i] = $set[i] + 1 //================================================== // 売買(売り) //================================================== // 1) [翌日逆指値(終日)][終値(-1Tick)]で手仕舞いする if ($set[i] >= $MaxHoldDay) PrintLog("手仕舞い") $sellflag[i] = 1 $set[i] = 0 end end end //================================================== // [買い]を仕掛ける //================================================== def BuyingLimitedPrice(i,d,t) //資金が不足している場合は何もしない if ($long == 0) return end //予算を超えない場合だけ買う if ($long * t <= $budget) if ($Interest == 1 || $Interest == 2) $budget = $budget - $long * t end $hold[i] = $long $buy[i] = t {1}Buy((int)t, $hold[i]) Print("B 予算残り = " + $budget) $set[i] = 0 else PrintLog("予算を超過しています") end end //================================================== // [翌日逆指値(終日)][値幅制限上限(-1Tick)] //================================================== def StopOrderPriceLimitTick() m = DailyPriceLimit(Close) t = Close - Yobine(Close + m, 0) if ! ({1}High > t && t > {1}Low) return 0 end return t end //================================================== // [翌日逆指値(終日)][終値(+5.00%)] //================================================== def StopOrderClose() t = Close * 1.05 if ! ({1}High > t && t > {1}Low) return 0 end return t end //================================================== // [翌日逆指値(終日)][高値(+1Tick)] //================================================== def StopOrderHighTick() t = High + Yobine(High, 0) if ! ({1}High > t && t > {1}Low) return 0 end return t end //================================================== // [翌日逆指値(終日)][終値(-1Tick)] //================================================== def StopOrderCloseTick() t = Close - Yobine(Close, 0) if ! ({1}High > t && t > {1}Low) return 0 end return t end //================================================== // 買い(カスタマイズ条件) //================================================== def Buying2(i) if (0 == PricedataExistCheck({1}Open)) if ($buyflag[i][0] == 2) t = StopOrderClose() elsif ($buyflag[i][0] == 3) t = StopOrderHighTick() else t = StopOrderPriceLimitTick() end if (t) BuyingLimitedPrice(i,1,t) end end end //==================== // 買い処理 //==================== def SortBuy(i) if (PricedataExistCheck(Close)) return end $long = 0 $long = Num($buyUnit, Close) codeset = $order[(int)Code] Buying2(codeset) end //================================================== // 売り(カスタマイズ条件) //================================================== def Selling2(i) if (0 == PricedataExistCheck({1}Open)) SellingOpen(i,1) else if (1 == PricedataExistCheck({2}Open)) SellingClose(i,0) else SellingOpen(i,2) end end end //==================== // 売り処理 //==================== def Sell_(i) if ($sellflag[i]) Selling2(i) $sellflag[i] = 0 end // 使用した$buyflag 配列を初期化 if ($buyflag[i][0]) $buyflag[i][0] = 0 $buyflag[i][1] = 0 end end //==================== // 銘柄コードを変えながらMain関数,BuySell関数を実行 //==================== Print("-------------------------------------------------") Print("日付 = "+ Year + "/" + Month + "/" + Day) SortInit() // ソート初期化 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 if ($buyCnt) while i + 1 < $buyCnt i = i + 1 {$sortList2[i]}SortBuy(i) end end i = -1 while i + 1 < $code_num i = i + 1 {codes[i]}Sell_(i) end |
バックテスト結果
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 |
株価データ: 日足 銘柄リスト: 全銘柄 1998/01/05~2019/09/13における成績です。 ---------------------------------------- 全トレード数 1156 勝ちトレード数(勝率) 588(50.87%) 負けトレード数(負率) 568(49.13%) 全トレード平均利率 2.39% 勝ちトレード平均利率 10.11% 負けトレード平均損率 -5.60% 勝ちトレード最大利率 77.10% 負けトレード最大損率 -100.00% 全トレード平均期間 2.85 勝ちトレード平均期間 2.90 負けトレード平均期間 2.79 ---------------------------------------- 必要資金 ¥2,264,600 最大ポジション(簿価) ¥9,988,900 最大ポジション(時価) ¥10,765,300 純利益 ¥26,818,270 勝ちトレード総利益 ¥58,155,980 負けトレード総損失 -¥31,337,700 全トレード平均利益 ¥23,199 勝ちトレード平均利益 ¥98,905 負けトレード平均損失 -¥55,172 勝ちトレード最大利益 ¥777,700 負けトレード最大損失 -¥1,046,400 プロフィットファクター 1.86 最大ドローダウン(簿価) -¥1,309,600 最大ドローダウン(時価) -¥1,235,320 ---------------------------------------- 現在進行中のトレード数 1 ---------------------------------------- 平均年利 59.21% 平均年利(直近5年) 20.04% 最大連勝 9回 最大連敗 8回 ---------------------------------------- [年度別レポート] 年度 取引回数 運用損益 年利 勝率 PF 最大DD 2019年 33回 -¥352,500円 -15.57% 45.45% 0.71倍 -33.62% 2018年 69回 ¥342,979円 15.15% 43.48% 1.15倍 -27.06% 2017年 108回 ¥1,800,400円 79.50% 43.52% 1.60倍 -22.78% 2016年 95回 -¥315,400円 -13.93% 45.26% 0.90倍 -22.55% 2015年 74回 ¥793,800円 35.05% 36.49% 1.31倍 -14.88% 2014年 129回 ¥3,428,200円 151.38% 50.39% 1.93倍 -23.48% 2013年 187回 ¥7,663,120円 338.39% 58.82% 2.81倍 -19.07% 2012年 48回 ¥1,812,500円 80.04% 60.42% 2.38倍 -18.87% 2011年 43回 ¥1,982,500円 87.54% 55.81% 2.69倍 -26.94% 2010年 49回 ¥549,945円 24.28% 46.94% 1.42倍 -15.12% 2009年 68回 ¥1,692,917円 74.76% 54.41% 1.65倍 -100.00% 2008年 28回 ¥739,600円 32.66% 39.29% 2.21倍 -11.67% 2007年 21回 ¥852,500円 37.64% 61.90% 3.71倍 -7.95% 2006年 19回 ¥490,900円 21.68% 63.16% 2.22倍 -12.91% 2005年 39回 ¥354,608円 15.66% 51.28% 1.58倍 -8.29% 2004年 51回 ¥1,640,900円 72.46% 62.75% 2.63倍 -20.62% 2003年 54回 ¥2,348,700円 103.71% 62.96% 3.70倍 -11.58% 2002年 6回 ¥142,300円 6.28% 83.33% 3.96倍 -4.63% 2001年 21回 ¥491,500円 21.70% 52.38% 1.80倍 -16.97% 2000年 14回 ¥358,800円 15.84% 57.14% 1.73倍 -24.80% |
利益曲線は次のとおりです。
近年は負けが続いています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[2019年] 月度 取引回数 運用損益 勝率 PF 最大DD 9月 2回 -¥3,200円 50.00% 0.94倍 -5.38% 8月 6回 -¥30,300円 50.00% 0.86倍 -13.46% 7月 3回 -¥166,400円 0.00% 0.00倍 -11.68% 6月 3回 -¥203,200円 66.67% 0.39倍 -33.62% 5月 5回 -¥50,200円 40.00% 0.70倍 -7.83% 4月 1回 -¥19,200円 0.00% 0.00倍 -1.86% 3月 5回 -¥31,400円 40.00% 0.59倍 -3.94% 2月 0回 ¥0円 NaN NaN倍 0.00% 1月 8回 ¥151,400円 62.50% 1.91倍 -8.48% [2018年] 月度 取引回数 運用損益 勝率 PF 最大DD 12月 4回 -¥82,500円 25.00% 0.55倍 -10.06% 11月 4回 -¥97,300円 25.00% 0.33倍 -7.36% 10月 4回 -¥24,700円 25.00% 0.84倍 -10.14% 9月 5回 -¥74,300円 40.00% 0.40倍 -9.09% |
平均年利が50%越えているのは驚異ですが、2016年頃から利益曲線が寝ています。
必要資金が200万円程度と少ないのが特徴です。逆指値としているので、売買が成立しない事があるためだと言う理解です。
まとめ
ここまで複雑にしないと勝てないのかなあ・・・と言うのが正直な感想です。
今まで多くの先人達の手法を実現しましたが、近年の結果を見る限り結局は資金曲線寝てるし・・・。
ただし、cosisin氏の手法はニッパー氏や先端シストレ研究等のブログで紹介されて有名なため、イザナミトレーダーはこのレベルのロジックがボトムだと考える必要があります。
イザナミ自体も難解になり過ぎだよね。システムトレードで勝つのが難しくなった証拠かな。