# 競馬プログラミング - 複勝およびワイドを利用し、回収率100%越えを目指す。(単勝では回収率100%超え達成) - できる限り、的中する馬券のみを選択。取りこぼさないようにするのではなく、(理想は)購入したものが全て的中するような選択をする。 - 満足のいく出力が出来次第、アプリ化も検討(最終目標) ## 現状タスク - 進行中(2022.9.16) - goal - Outline - X, yという訓練データを用いて、\_dateに開催される全レースの着順の予測を行う - input: - X: 説明変数 - y: 目的変数 - \_date: 日付 - output: - recommended_places: 購入すべき(おすすめの)複勝馬券 - Excelへの自動書き込み or 表示 - 概略 - rank_predictor: ある日付に開催されたレースの予測 - prediction_extractor: 予測結果から必要なデータの取得 - prediction_handler: 予測結果の処理を行う - タスク - rank_predictor, prediction_extractorの作成(途中) - 不備 - [x] predict_dfに星マークが付与されていない。(prediction_result_extracton.py l.235 \_\_attach_star2proba) - [x] SettingWithCopyWarningの発生(prediction_result_extracton.py l.118 \_\_determine_fukusho_odds) - ExcelWriterの作成(未着手) - other refactoring - [ ] Returnの引数をrace_id_dictからrace_id_lsに変更。 - [ ] horse_idならびにrace_idの取得をResultからRaceCardへ変更。 - [x] 一回の実行で同じtableに複数回SESSIONしてしまうことを解消。 - [ ] mergeの処理が遅いので、早くできる方法を模索する。(特に、jockeyは謎に遅くなってしまった。) - [ ] \_\_init__.pyが雑に記入されているので、再度勉強。(Import Error: partially initialized module) - [ ] rank_predictorにて予測する際のpbarの表示を綺麗に。 - [ ] ディレクトリ構想の見直し(特に、\_class内部を分割) ## ER図 (更新: 2022.8.20) <img src="https://i.imgur.com/9AU5TFc.png" width="500"> ## 開発予定技術 - Excel - ~~Excelクラスの作成~~ - excelへの書き出しの際、1着馬が複数のレースにも対応できるようにする。 - 例) 1/9 中京12R - 除外や中止、取消など、最終的に着順が決定しなかった馬を表から削除。 - 精度、回収率向上のために - 血統データのprocessing(難) - ensemble学習 - (コードによる)特徴量削減([参考文献](https://qiita.com/shimopino/items/5fee7504c7acf044a521#%E7%89%B9%E5%BE%B4%E9%87%8F%E9%81%B8%E6%8A%9E%E6%89%8B%E6%B3%95%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81)) - パラメータチューニング([参考文献](https://knknkn.hatenablog.com/entry/2021/06/29/125226#boostingboosting_type)) - フォーメーション自動作成(難) - 複勝 - ~~1着予想の馬に対する複勝の回収率~~ - 買うべき馬を出力する関数 - 新特徴量候補 - 前レースと騎手が同じかどうか(diff_jockey) - 前レースの斤量との差(diff_weights) - 開催地が海外のレースを訓練データから削除 - trainer_table, owner_table, breeder_table, jockey_tableを作成(**最優先**) - 以前はそれぞれのidを直接特徴量に入れたが種類が多く変に学習し断念。 - 理由はidは数値なので、数値が近しいid同士などに本来には関係ない関係性を見出されてしまうため。 - one-hot encodingの実装も考えたが、種類が多くふさわしくないと思いこちらも断念。 - 新たな提案として、idではなくそのレースに出走する馬に関わるjockeyなどの全体の1着率、2着率、3着率、圏外率、総レース数辺りを特徴量として追加。 - 例) ルメール騎手の過去戦績(1着率x_1%, 2着率x_2%...) - テーブルが複数に増えるためdbなどを作成するのもあり。 - テストコード - フォーメーション自動作成&美味しい複勝馬券に対応した回収率の計算を行う関数の作成。 - 現在は、閾値による簡易版のみ。 - Webapi - フロントエンド - React - テンプレートの自動作成 - `npx create-react-app <name> ` - npxとは - Node.jsのパッケージランナーツール - 似たものにnpmがある。([参考文献](https://www.investor-daiki.com/it/npm-npx-difference#:~:text=%E4%B8%80%E6%96%B9%E3%81%A7npx%E3%81%AFNode,%E3%81%8C%E4%B8%BB%E3%81%AA%E8%B2%AC%E5%8B%99%E3%81%A7%E3%81%99%E3%80%82)) - バックエンド - fast api - ([参考文献](https://zenn.dev/sawao/articles/15a9cf0e3360a7)) - その他 - 見やすい変数名への変更([参考文献](https://qiita.com/Ted-HM/items/7dde25dcffae4cdc7923#%E7%9C%81%E7%95%A5%E3%81%97%E3%81%9F%E8%A8%80%E8%91%89)) - ~~予想の際、入力変数を日付のみにする(race_idの入力を省略)~~ - 自動化([参考文献](https://t.co/tvF5pHufc2)) - 最新情報のスクレイピング - 結果の出力 - 馬券購入 ## ブレスト ### フォーメーション自動作成 - ==自動で決定する馬券はワイドに絞る== - 他は難しそう。作成するとしても、優先順位はかなり低め。 - 何を基準にして買い馬を決定するか。 - オッズxスコアから得られる指標 - 予測にオッズは用いない。購入するかどうかの意思決定のみにオッズを使用。 - スコアが高く硬い馬を軸に、穴馬をオッズが良い馬に限定 - トリガミはほとんどないようにする。どのように? - トリガミが生じない程度の候補数にする。 - トリガミが生じる馬券に対しての賭け金を増やす。 - トリガミが生じる馬券を候補から除外する。 - **上位n件の馬の中から、不必要な馬(低オッズかつ低スコア)をフォーメーションから削除。** - 不必要な馬の定義が必要 - **優位性のある馬(高スコア)は軸馬として考える。** - すなわち、購入馬券に必ず軸馬を絡めるようにする。 - 軸馬の定義が必要 - ワイドの場合、硬い馬x穴馬の組み合わせが理想 ### 買うべき馬を出力する関数 - tansho_binaryの結果の通り、当たりやすい馬ばかりを買うと、ほとんど回収が見込めない。 - 例) オッズ1.2の複勝馬券の場合、6回中5回以上の的中率(=約85%)が求められる。これは、リスキーな割に、利益に繋がりにくい。 - ここで言う買うべき馬は、当たりやすい馬ではなく、**回収率の良い馬**。 - ~~外した時のリスクが大きすぎるため、オッズが1.5未満は購入しない。~~ 計算結果次第では購入あり。(オッズが1.1の場合、的中率90%超えが必須であるため、自動的に省かれるはず。) - 複勝オッズの最大値は優に100を超える。 - スコアは負の値も含む。 - (リークが起きていなければ、)現在のモデルは、スコアが約2.25以上の単勝馬券を買い続けて、回収率100%前後 - 大体1, 2頭/会場(=約5頭/日)ほどになるようにしたい。 - 但し、回収率100%超えは前提。回収率の条件が達成できそうでなければ、希望頭数の変更はやむなし - ~~美味しい複勝馬券の基準候補 **i) スコア x オッズ >= 4.5** **ii) オッズ >= 1.5** (候補数が多すぎる or 少なすぎるで基準を変更の余地あり)~~ - 具体例 - オッズ1.5の場合、約67%の的中率が必須 - ~~スコア>=3?~~ - オッズ2.0の場合、50%以上の的中率が必須 - ~~スコア>=2.25?~~ - **==(採用)スコアに対しての複勝的中率の期待値を出力する関数が必須==** - 上記の役割とはたす関数を`hit_fukusho(score)`とする。 - 購入すべき馬券の条件 - ==`1/hit_fukusho(predict(horse)) > fukusho_odds_df(horse, race)`== (但し、`fukusho_odds_df(x)`とは、ある馬(horse)が出走するあるレース(race)に与えられている複勝オッズを引っ張ってくる関数) ### hit_fukusho関数 - スコア帯ごとの的中率を洗い出す。 - 最大値と最小値が必要 - 幅はいくつ? - 0.1程度が妥当な気がする - 幅を狭くしすぎると、一区画に対するデータ数が少なくなり、正しい的中率が得られない恐れがある。 - 幅を広くしすぎると、同じスコア帯の最大値と最小値で的中率の差が大きくなる。 - スコアでソートした全データをn分割し、それぞれグループA1, A2, ..., Anとする。すると、hit_fukushoは以下のように書ける。また、得られたスコアがどのスコア帯に属するかを出力する関数を`get_A(score)`とする。 ==`hit_fukusho(get_A(predict(horse))) = (実データにより算出された的中率)`== - `get_A(score)`の概要は、scoreと各グループの要素の平均値との差の絶対値`(|score-Avg(Ai)|)`が最も小さいものを採択 - 各グループ(Ai)に最低1000件ほど欲しい ### DDD - ディレクトリは以下の4種 - adapters - controllers - usecaseが動作部分を示すのに対して、controllersはusecaseの結果を取得する。その際、利用しやすい形に変形する。 - パスオペレーション関数の内容と同値。 - usecaseから想定外の結果が得られた場合などの例外処理も行う。 - db_gateways - dbのmodel定義及び、crudを記述。引数にengineを持つため、最もdbに近いディレクトリ - applications - usecase - このappの必要なusecaseを網羅する。 - dbが既に存在する前提の記述となる。すなわち、db_gatewayを用いる。 - domains - schemas - dbの一要素分のdataを保持するclass - 上記のclassをまとめて一括で処理するclass(=repository) - builder.py - 各primary_key一つに対応するデータの作成(?) - repository.py - db_gatewayを実際に使うもの?実際に、builder.pyで作成したものを複数所持しているもの。saveやselectができる。 - infrastructures - db_drivers - base.py - declarative_baseを定義(Pythonで宣言したclassとdbをマッピングするためのベースとなるclass) - servers - route.py - パスオペレーション関数をデコレータを用いて記述。作成したrouterを返り値とする。 ### DB(postgresql) - 便利ツール - [SQLpad](https://itnews.org/news_resources/310204): databaseを可視化するWebUI ### 血統データのプロセッシング - [fastText](https://qiita.com/KTaskn/items/e76551191214593c278e)の利用 - 利用したいとは思っているが、全く案が思い付かない。 - word2vec的なものもあるらしい。 ## 未修正バグ - ~~月ごとの集計を行う関数(make_BoP_sheet, make_hit_sheet)が、複勝回収率を加えたことにより、座標がずれ、意図していない集計がなされている。~~ - ~~列名指定で値の集計を行うように改善~~ - 5/8 修正完了 - ~~過去の実データに対しての予測を行う際も、現状スクレイピングしている全データを用いて予測をおこなっている。~~ - ~~未来のデータを用いていることになるので、訓練用データを一部削除する関数が必要。~~ - 5/9 修正完了 - 複勝回収率がランク毎になっている。 - 実際に購入する馬券は本命馬だけでないため、本命馬ランクを用いた分割はできない。 - 単勝も複勝同様のDataFrame(tansho_odds_df)を作成し、新たなランク帯を作成する - fukusho_odds_dfのデータの分割をし忘れている。 - trainとtestに分割しなければならないが、現状は全データから全データを予測する状況になっており、正しい分布が得られていない。 - 現在の幅は0.05にしているが、データ数が約5分の1になるため、 再検討が必要。 - 三連複、三連単流し回収率がうまく機能していない - 原因は未探索 - 予測不可能なレース(主に新馬戦)に星マーク(☆)がついている。 - 単純に見づらいし、今後悪影響が出そうなので削除したい。 ## 現在のモデル (更新: 2022.7.8) - params([参考文献](https://knknkn.hatenablog.com/entry/2021/06/29/125226#bagging_fractio%E3%81%A8bagging_freq%E3%82%92%E3%81%A4%E3%81%8B%E3%81%A3%E3%81%A6bagging%E3%81%AE%E8%AA%BF%E6%95%B4%E3%82%92%E3%81%99%E3%82%8B)) | 変数名 | 値 | 説明 | | ---- | ---- | ---- | | objective | regression | 計算手法 | | metric | rmse(二乗平均平方根誤差) | 誤差の計算方式 | | feature_pre_filter | False | (検索中) | | boosting_type | gdbt(勾配ブースティング) | boostingアルゴリズム | | lambda_l1 | 6.6377046563499 | L1正則化項の係数 (重要じゃない特徴量を削ぐ役割) | | lambda_l2 | 0.009493220785902975 | L2正則化項の係数(重要じゃない特徴量の影響力を小さくする) | | num_leaves | 31 | 木の葉(=ノード)の数 | | feature_fraction | 0.748 | 各機を作成する際、使用する特徴量の割合 | | bagging_fraction | 1.0 | baggingを行う際、使用する特徴量の割合 | | bagging_freq | 0 | baggingを行う頻度 | | min_child_samples | 20 | 葉が有するデータの最小数 | - bagging_fractionおよびbagging_freqから分かる通り、baggingは行っていない。 - 回収率のグラフ(縦軸:回収率、横軸:閾値) - tansho_binary: 複勝馬の予想 - tansho_regression: オッズと着順両方を加味 - tansho_lambdarank: ランキング学習 ![](https://i.imgur.com/omDhqqi.png)