久しぶりにさくらレンタルサーバを触ると、次のようなエラーが出るようになっていました。
1 |
urlopen error eof occurred in violation of protocol (_ ssl c 777 |
エラーメッセージから推測するに、 OpenSSLがらみの問題で使えないようだ。この前のように、Python 3.6.3 をバージョンアップして Python 3.6.5 にしたのだが、pip3 が正常に使えない。
それじゃ、ということで、OpenSSL あたりはどなっているのか調べてみる。
1 2 3 4 5 6 7 8 9 |
[www ~]$ openssl version OpenSSL 1.0.2o-freebsd 27 Mar 2018 [www ~]$ ~/bin/python3.7/bin/python3 Python 3.7.4rc1 (default, Jun 29 2019, 21:56:56) [GCC 7.3.0] on freebsd11 Type "help", "copyright", "credits" or "license" for more information. >>> import ssl >>> print(ssl.OPENSSL_VERSION) OpenSSL 1.0.2o-freebsd 27 Mar 2018 |
あれれ。なぜか、せっかくインストールした Python 3.6.5 は古いバージョンのOpenSSLを呼んでくるみたいだ。
1 2 3 4 5 6 7 8 9 10 |
[www ~]$ ~/.pyenv/shims/python3 Python 3.6.6 (default, Oct 14 2018, 13:13:00) [GCC 4.2.1 20070831 patched [FreeBSD]] on freebsd9 Type "help", "copyright", "credits" or "license" for more information. >>> import ssl Traceback (most recent call last): File " File ".pyenv/versions/3.6.6/lib/python3.6/ssl.py", line 101, in <module> import _ssl # if we can't import it, let the error propagate ImportError: .pyenv/versions/3.6.6/lib/python3.6/lib-dynload/_ssl.so: Undefined symbol "SSLv2_method" |
configure するとき、/usr/local/ssl を使うようにオプションを追加してコンパイルしたら、新しいOpenSSLを使うようになり、pip3もちゃんと動くようになった。
1 2 3 4 5 6 |
$ wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4rc1.tgz $ tar zxvf Python-3.7.4rc1.tgz $ cd Python-3.7.4rc1 $ ./configure CPPFLAGS="-I/usr/local/ssl/include" LDFLAGS="-L/usr/local/ssl/lib" --prefix=$HOME/bin/python3.7 $ make $ make install |
さくらレンタルサーバーでは、高負荷なバッチは自動的にKillされます。
この事を知らなかったので、昔、株のシグナル公開サイトを立ち上げようとしましたが、断念しました。
実際に、問い合わせすると公式に次のような返答がありました。
さくらのレンタルサーバではサーバの負荷対策のため、一定以上のメモリを使用しているプロセスや、一定時間以上起動し続けているプロセスは自動的に強制終了を行うよう設定を行っております。
なるほど。
cosisin氏の勝率90%以上、期待値20%以上の有効性検証
今回のバックテストは、cosisin氏の2013年09月08日「勝率90%以上、期待値20%以上の」の日記です。
ストラテジーを作ってみました。なんとかショックみたいな暴落のときに、反発したのを確認してから、次の日寄指で仕掛けます。3年に1度くらいしかシグナル発生しません。まあ、かなり長い間待ってばかりの戦略です。相場情報使わなくても、最適分散の仕掛け設定でもできます。
【基本設定】
- 1) 株価の低い場合はランキングしない[50]万円以下
- 2) 単利利用、通年
- 3) 全ての銘柄対象
【買いルール】
- 1) [前日比(率)]が[5]より[大きい]
- 2) [始値→終値(率)]が[5]より[大きい]
- 3) [期間安値高値の中間値(2)]が[期間安値高値の中間値(5)(-10.00%)]より[小さい(同じ含む)]
- 4) [集計した銘柄数]が[50]より[大きい(同じ含む)]
- 5) [翌日指値(寄付)][終値]で[買い]を仕掛ける
【手仕舞いルール】
- 1) [翌日逆指値(終値)][終値(-1Tick)]で手仕舞いする
ソースコード
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 |
# loop-type: date-only //============================== require "TIlib" require "Utility" require "TrendCheck" // ====================================== // cosisin氏の勝率90%以上、期待値20%以上 // ====================================== // //【基本設定】 // 1) 株価の低い場合はランキングしない[50]万円以下 // 2) 単利利用、通年 // 3) 全ての銘柄対象 // //【買いルール】 // 1) [前日比(率)]が[5]より[大きい] // 2) [始値→終値(率)]が[5]より[大きい] // 3) [期間安値高値の中間値(2)]が[期間安値高値の中間値(5)(-10.00%)]より[小さい(同じ含む)] // 4) [集計した銘柄数]が[50]より[大きい(同じ含む)] // 5) [翌日指値(寄付)][終値]で[買い]を仕掛ける // //【手仕舞いルール】 // 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 = 0 // 最大保有日数 $Interest = 1 // 無制限(0) / 単利(1) / 複利(2) $reverse = 1 // 購入順序 昇順(0) / 降順(1) $udcount = 0 // 騰落レシオ利用数 Init() // テクニカル指標初期化 -------------------------- $HLB2 = [Length(CodeList)] $HLB5 = [Length(CodeList)] //------------------------------------------------ InitDone() // 騰落レシオ初期化 $__INIT__ = 1 end // 1) [期間上昇(率)]=[当日終値(現在値)]÷[指定期間前の終値] def PeriodUpRate(i) if ! ({-1 * i}Close) return 0 end return (float)Close / (float){-1 * i}Close * 100 end def Main(i) //================================================== // 条件(買条件, 売条件共通部分) //================================================== //まだ上場していない銘柄は株価データがないためnullが返る if (Index == null) return end if ! ($order[(int)Code]) $order[(int)Code] = i end if ! ($HLB2[i] && $HLB5[i] ) //Tilibのオブジェクト生成 $HLB2[i] = HLBand_new(2) $HLB5[i] = HLBand_new(5) //銘柄ごとのグローバル変数を初期化する $hold[i] = 0 return end HLBand_next($HLB2[i]) HLBand_next($HLB5[i]) if ! (Index > 75 && {1}Open) return end //================================================== // 保有してない→購入 //================================================== if (! $hold[i]) //================================================== // 売買(買い) //================================================== // 1) 株価の低い場合はランキングしない[50]万円以下 if ! (Close && {-1}Close && $HLB5[i][1] && $HLB5[i][3] && Close > 50) return end // 1) [前日比(率)]が[5]より[大きい] check1 = {0}DayBfrRatio() > 5 // 2) [始値→終値(率)]が[5]より[大きい] check2 = OpenToClose() > 5 // 3) [期間安値高値の中間値(2)]が[期間安値高値の中間値(5)(-10.00%)]より[小さい(同じ含む)] check3 = (($HLB5[i][1] + $HLB5[i][3]) / 2) * 0.9 >= ($HLB2[i][1] + $HLB2[i][2]) / 2 if (check1 && check2 && check3) // 5) [翌日指値(寄付)][終値]で[買い]を仕掛ける PrintLog("購入予定") $buyflag[i][0] = 1 $buyflag[i][1] = check1 // 好きなパラメータをもとにソート $buyCnt = $buyCnt + 1 end //================================================== // 保有している→売却 //================================================== elsif ($hold[i]) if ($set[i] < 1) $set[i] = 1 return end $set[i] = $set[i] + 1 //================================================== // 売買(売り) //================================================== // 3) [保有日数]が[1]より[大きい(同じ含む)] if ($set[i] >= $MaxHoldDay) // 1) [翌日逆指値(終値)][終値(-1Tick)]で[手仕舞いする] 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 //================================================== // [翌日逆指値(終日)][高値(+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 //================================================== // [翌日指値(寄付)][終値(num%)]で[買い]を仕掛ける //================================================== def StopOrderClose(num) t = Close * num if ! ({1}High > t && t > {1}Low) return 0 end return t end //================================================== // [翌日逆指値(終日)][高値(+1Tick)] //================================================== def StopOrderOpenTick() t = {1}Open + Yobine({1}Open, 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] == 1) // 5) [翌日指値(寄付)][終値(-1.50%)]で[買い]を仕掛ける t = StopOrderClose(0.985) elsif ($buyflag[i][0] == 2) // 6) [翌日逆指値(終日)][翌日始値(+1Tick)]で[買い]を仕掛ける t = StopOrderOpenTick() 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 ($sellflag[i] == 1) // 2) [当日指定値][高値]で手仕舞いする if (0 == PricedataExistCheck(High)) Sell(High, $hold[i]) $budget = $budget + $buy[i] * $hold[i] $hold[i] = 0 Print("S 予算残り = " + $budget) end elsif ($sellflag[i] == 2) // 4) [当日引け]で手仕舞いする Selling(i) 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 // 4) [集計した銘柄数]が[50]より[大きい(同じ含む)] if ($buyCnt >= 50) 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.5時間です。
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 |
株価データ: 日足 銘柄リスト: 全銘柄 1998/01/05~2019/09/20における成績です。 ---------------------------------------- 全トレード数 40 勝ちトレード数(勝率) 30(75.00%) 負けトレード数(負率) 10(25.00%) 全トレード平均利率 11.08% 勝ちトレード平均利率 16.64% 負けトレード平均損率 -5.60% 勝ちトレード最大利率 50.00% 負けトレード最大損率 -8.63% 全トレード平均期間 3.00 勝ちトレード平均期間 3.57 負けトレード平均期間 1.30 ---------------------------------------- 必要資金 ¥10,266,400 最大ポジション(簿価) ¥9,745,300 最大ポジション(時価) ¥12,170,600 純利益 ¥4,306,800 勝ちトレード総利益 ¥4,835,300 負けトレード総損失 -¥528,500 全トレード平均利益 ¥107,670 勝ちトレード平均利益 ¥161,177 負けトレード平均損失 -¥52,850 勝ちトレード最大利益 ¥488,700 負けトレード最大損失 -¥81,600 プロフィットファクター 9.15 最大ドローダウン(簿価) -¥521,100 最大ドローダウン(時価) -¥521,100 ---------------------------------------- 現在進行中のトレード数 0 ---------------------------------------- 平均年利 13.98% 平均年利(直近5年) 8.39% 最大連勝 10回 最大連敗 2回 ---------------------------------------- [年度別レポート] 年度 取引回数 運用損益 年利 勝率 PF 最大DD 2014年 10回 ¥842,000円 8.20% 100.00% ∞倍 0.00% 2011年 10回 ¥1,560,600円 15.20% 100.00% ∞倍 0.00% 2008年 20回 ¥1,904,200円 18.55% 50.00% 4.60倍 -8.63% |
利益曲線は次のとおりです。
勝率は75%で、利益は14%なので、タイトル通りの結果にはなりませんでした。
まとめ
シグナルが出ているタイミングは下記です。
- 2014年02月 チャイナ・ショック
- 2011年03月 大震災ショック
- 2008年10月 リーマンショック
このタイミングの暴落を検出する程度であれば、他の「逆張り手法」の条件を厳しくすれば同じことができそうです。
また、「集計した銘柄数」を「20」にすれば、下記のような結果になり、昨年末のトランプ・ショックもシグナルを出すことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
平均年利 17.96% 平均年利(直近5年) 21.99% 最大連勝 15回 最大連敗 3回 ---------------------------------------- [年度別レポート] 年度 取引回数 運用損益 年利 勝率 PF 最大DD 2019年 10回 ¥346,300円 3.56% 70.00% 6.06倍 -4.26% 2014年 10回 ¥842,000円 8.65% 100.00% ∞倍 0.00% 2013年 21回 ¥2,009,800円 20.64% 80.95% 33.31倍 -3.07% 2011年 20回 ¥4,392,200円 45.11% 100.00% ∞倍 0.00% 2008年 40回 ¥3,116,510円 32.01% 67.50% 5.85倍 -9.94% 2006年 11回 ¥1,509,300円 15.50% 100.00% ∞倍 0.00% 2003年 11回 ¥22,000円 0.23% 54.55% 1.16倍 -6.25% |
今までに「逆張りストラテジー」を何種類も作ったことがある人であれば、特に新鮮さは感じない手法かと思います。