ある日の職場での会話。
先輩「俺は株のセンス結構あると思うんだよね。」
先輩「10億ぐらい貸してくれたら、利子つけて返せる自信があるよ。」
先輩「元手が無いからやってないけどさ。」
私「では、まず100万円を200万円にすることから始めたらどうでしょう?」
私「自信があるなら何度もやっていれば1億いきますよ?」
先輩「いや、何度も予想を当て続けるのは無理っしょ。」
何度も勝ち続けれない=再現性が無い
そんな手法はギャンブルだ。
東大生や慶応早稲田などの高学歴者が多い会社だけど、投資の世界じゃ てんでトーシローだな……。
そもそも投資は「センス」で行ったり「予想」するものではない。
徹底的に裁量を捨て再現性を追求した投資方法の一つが
システムトレード
これは、システムトレードで億万長者という安易な夢を見て
人生の大半を費やしてしまった 一人の愚かな中年男性のストーリー。
全然、儲からねーーー。
ソート部分の実装結局どうしたか?
前回、システムトレードツール「Protra」でソートの部分が遅かった事が分かった。
「遅くても別に良いや~!」
と思っていたけど、週末に手持ち無沙汰だったので「選択ソート」を使った形に作り直した。
週末を潰したけどね。
「釣り」や「ウンチク」記事って全くPVが伸びない。
一方、システムトレード関連の記事ならどんな話でも「釣り」の記事の6倍以上は訪問客が来る。
訪問客をブログに呼ぶために修正したと言っても過言ではない……。
……という訳ではなく、時間を忘れて没入して実装&検証していた。
きっと自分はこういう作業が好きなのだと思う。
「課題」まで出させておいて「施策」を実施させない、うちのような組織になっちゃ駄目だしね。
ソート修正後の、バックテスト時間はどうなった?
で、前回同様に下記のストラテジーを使って「時間測定」をしてみた。
【修正前】
- 約3時間(2時間54分52秒)
【修正後】
- 約30分(0時間30分24秒)
その差
まさかの 6倍
6倍だよ……6倍。
(C)鳥山明/ドラゴンボール
悟空がこの時「界王拳6倍」を使えていたら
楽にベジータを倒すことができていただろう。
衝撃的な速度改善。
図に表すとこうなる。
今まで、どれだけ無駄なアルゴリズムで実装していたのか……。
中身を理解せずにソースコードを拝借したら駄目だなーー。
この関数はUtility.ptに追加。
既に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 |
//================================================== // 昇順/降順($reverse = 1)で選択候補数だけ選択ソート // 引数: max 銘柄選択候補数 // 引数: selectFlag 複数ストラテジーの優先度(0 = 利用しない) // 戻値: sortList max数のソートしたCodeリスト //================================================== def SelectionSort(max, selectFlag) if ! ($buyCnt) return end buylist = [$code_num] codelist = [$maxCode] if (max > $buyCnt) max = $buyCnt end // 購入銘柄Codeのリストを作成 i = 0 m = 0 while (i < $code_num) // 購入の場合$buyflag[i][0]は0以外 if ((selectFlag && $buyflag[i][0] == selectFlag) _ || (! selectFlag && $buyflag[i][0])) buylist[m] = i // 購入順序 m = m + 1 end i = i + 1 end i = 0 while (i < $maxCode) if ($order[i] != -1) codelist[$order[i]] = i end i = i + 1 end // 選択ソート sortList = [max] i = 0 while (i < max) m = i j = i + 1 while (j < $buyCnt) if (! $reverse && $buyflag[buylist[j]][1] < $buyflag[buylist[m]][1]) m = j elsif ($reverse && $buyflag[buylist[j]][1] > $buyflag[buylist[m]][1]) m = j end j = j + 1 end min = buylist[m] buylist[m] = buylist[i] buylist[i] = min sortList[i] = (string)codelist[buylist[i]] i = i + 1 end return sortList end |
大域変数や実処理部分に「オマジナイ」的な処理を書きたくないので、これでも色々と無駄な計算が走っている。
できる限り下位互換を取るつもりだったが、過去の無駄な大域変数を消すことにした。
なので、少しアルゴリズムに修正が必要。
抜粋すると次のような書き方となる。
1 2 3 4 5 6 7 8 9 10 11 |
def Main(i) //================================================== // 条件(買条件, 売条件共通部分) //================================================== //まだ上場していない銘柄は株価データがないためnullが返る if (Index == null) return end if ($order[(int)Code] == -1) $order[(int)Code] = 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 |
//==================== // 銘柄コードを変えながらMain関数,BuySell関数を実行 //==================== Print("-------------------------------------------------") Print("日付 = "+ Year + "/" + Month + "/" + Day) $buyCnt = 0 // 購入数初期化 UpDownRatio() // 騰落レシオ計算 i = -1 while (i + 1 < $code_num) i = i + 1 {codes[i]}Main(i) end i = 0 if ($buyCnt) sortList = SelectionSort(10, 0) // 1) [デイトレサンプルスイング改良版(順位)]が[10]より[小さい(同じ含む)] 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 |
この例では、銘柄を最大10件のみ選定して選択ソートで昇順に並べている。
まぁ……別に誰も使っているライブラリでも無いし、多くの人にはどうでも良い話だろうけどね。
まとめ
ソート部分を選択ソート&必要銘柄数だけソートをするように変更することで、今回のストラテジーでは6倍の速度改善を実現した。
今後のバックテストでは、この方法を使っていく。
ただし、シグナルが極端に多いような手法に有効な高速化であり、すべての手法が6倍早くなる訳ではない。念の為。