概要
親子上場解消TOB候補を機械的に抽出するスクリーニングシステムを開発している。前回までに42点満点の基本スコアと30点満点の親子上場スコアを実装し、過去事例6銘柄中2銘柄を事前検出(33%的中)した結果を得ていた。
しかしこの「33%」はTop評価のみのチェリーピッキングであり、システム全体の真の予測精度は不明だった。今回、過去のTOB事例48件を収集してバックテストを実装し、検証可能な31件に対してシステムを再適用した。
結果として、yfinance版での初期検出率は16.1%にとどまったが、データソースをJ-Quantsに移行し、3つのロジック改善を施した結果、51.6%まで引き上げることができた。本記事はその技術的記録である。
検証の動機
過去の記事で報告した「6銘柄中2銘柄がTOB済み」という結果は印象的な数字だったが、サンプル選定が恣意的だった。実用可能なシステムかを判断するには、母集団に対する事前検出率を定量化する必要がある。
評価設計として以下を採用した:
- 過去事例: 2024年10月〜2026年5月発表の親子上場解消TOB事例
- 評価時点: TOB発表日の3ヶ月前と6ヶ月前
- 評価項目: フィルタ通過判定、スコア値、除外理由
バックテスト用データ収集
ChatGPTを用いて2024年10月以降の親子上場解消TOB事例を収集し、48件の事例を historical_tobs.yaml として整備した。スキーマは以下のとおり。
historical_tobs:
- announcement_date: "2026-05-25"
subsidiary_ticker: "1948"
subsidiary_name: "弘電社"
subsidiary_market: "スタンダード"
parent_ticker: "6503"
parent_name: "三菱電機"
parent_ratio_before: 51.36
acquirer_name: "きんでん"
tob_price: 11501
tob_premium_close_pct: 81.12
tob_pattern: "C"
48件のうちannouncement_dateが確定している有効事例は31件。パターン内訳は以下のとおり。
- Pattern A(親会社による現金TOB): 18件
- Pattern B(株式交換による完全子会社化): 10件
- Pattern C(第三者TOBによる親子上場解消): 3件
第1段階: yfinance版バックテストの結果
最初のバックテストエンジンは既存のスクリーニング基盤をそのまま転用し、過去株価をyfinanceの Ticker.history() で取得する設計とした。
def get_historical_data(ticker, target_date, months_before):
eval_date = target_date - relativedelta(months=months_before)
stock = yf.Ticker(f"{ticker}.T")
hist = stock.history(start=eval_date - timedelta(days=30),
end=eval_date + timedelta(days=1))
if hist.empty:
return None # data_unavailable
...
31件×2評価時点=62ケースを実行した結果は衝撃的だった。
| 指標 | 3ヶ月前 | 6ヶ月前 |
|---|---|---|
| データ取得成功 | 8/31(25.8%) | 8/31(25.8%) |
| フィルタ通過 | 5/31(16.1%) | 6/31(19.4%) |
| スコア25以上 | 3/31 | 3/31 |
| スコア40以上 | 1/31 | 2/31 |
31件中23件がdata_unavailable。原因はyfinanceの構造的制約だった。
LIBRARY ERROR: possibly delisted; no timezone found
yfinance(Yahoo Finance)は上場廃止後の銘柄について過去データを保持しない。バックテスト対象の23件は既に上場廃止済みであり、データ取得が不可能だった。残った8件は2026年TOB発表で「まだ上場している」銘柄のみ。
スコア40以上に該当した銘柄(5660神鋼鋼線工業 6mo 46点、1948弘電社 6mo 44点、5660神鋼鋼線工業 3mo 44点)は全て実際にTOB発表済みであり、システムの高スコア判定そのものは機能していた。しかし母集団の74%が評価不能では予測精度を語れない。
第2段階: J-Quantsへの移行
代替データソースとして以下を検討した。
- Stooq(無料、廃止銘柄カバー、ただし配当・財務指標が薄い)
- J-Quants Free(無料、廃止銘柄カバー不明)
- J-Quants Light(月1,089円)
- EDINET API(無料、株価データなし、実装難)
J-Quants Freeで上場廃止銘柄のデータが取得できるかが論点だった。実際にイオンディライト(2025-07-17廃止)、山陽特殊製鋼(2025-04-23廃止)、NTTデータグループ(2025-09-26廃止)の3銘柄でテスト取得したところ、全件廃止前日まで完全に取得できた。
銘柄 廃止日 取得件数 最終取得日
9787 イオンディライト 2025-07-17 131日 2025-07-16
5481 山陽特殊製鋼 2025-04-23 136日 2025-04-22
9613 NTTデータグループ 2025-09-26 141日 2025-09-25
財務指標も get_fin_summary() で売上、営業利益、純資産、自己資本比率(EqAR)、BPS、EPS、発行済株式数(ShOutFY)が取得可能。Freeプランで月額0円のまま運用できる。
backtest_jquants.py として実装を切り出し、データ取得部分のみJ-Quants V2 APIに置き換えた。
def get_historical_data_jquants(ticker, target_date, months_before):
eval_date = target_date - relativedelta(months=months_before)
# 株価取得
prices = client.get_eq_bars_daily(
code=ticker,
from_=eval_date - timedelta(days=30),
to=eval_date + timedelta(days=1)
)
# 財務指標(評価日直前の決算期)
fin = client.get_fin_summary(code=ticker)
latest_fin = filter_by_date(fin, before=eval_date)
return {
"price": close_price,
"bps": latest_fin["BPS"],
"eps": latest_fin["EPS"],
"equity_ratio": latest_fin["EqAR"],
"shares_outstanding": latest_fin["ShOutFY"],
"pbr": close_price / latest_fin["BPS"],
"per": close_price / latest_fin["EPS"],
"market_cap": close_price * latest_fin["ShOutFY"]
}
J-Quants版での再実行結果は以下のとおり。
| 指標 | 3ヶ月前 | 6ヶ月前 |
|---|---|---|
| データ取得成功 | 30/31(96.8%) | 30/31(96.8%) |
| フィルタ通過 | 12/31(38.7%) | 10/31(32.3%) |
| スコア25以上 | 3/31 | 5/31 |
| スコア40以上 | 1/31 | 1/31 |
data_unavailableが23件から1件に減少。3847パシフィックシステムのみJ-Quantsでも取得できなかった。
ここで興味深い発見があった。yfinance版では「フィルタ通過」していた銘柄が、J-Quants版では「営業赤字」で除外されるケースが複数発生した。
1776 三井住建道路 3mo JQ除外理由: 営業赤字
1776 三井住建道路 6mo JQ除外理由: 営業赤字
4367 広栄化学 3mo JQ除外理由: 営業赤字
4974 タカラバイオ 3mo JQ除外理由: 営業赤字
J-QuantsのfinSummary APIは四半期決算データを返す。「最新四半期の営業利益」で判定すると、季節性のある業種で特定四半期だけ赤字の場合に誤除外が発生する。建設業(三井住建道路)や医薬品(タカラバイオ)のような季節変動の大きい業種で顕著だった。
第3段階: 3つのロジック改善
J-Quants移行で母集団は確保できたが、フィルタ通過率は約40%にとどまった。改善余地として以下3点を同時実装した。
改善①: 営業利益判定のTTM化
最新四半期ではなく直近4四半期累計(TTM: Trailing Twelve Months)で判定する。
J-QuantsのOPフィールドは「会計年度内の累計値」として返される(Q2なら上半期累計)。そのためTTMの計算式は単純な4四半期合計ではなく、以下になる。
TTM = 当期累計OP + 前期通期OP − 前期同期累計OP
具体例として三井住建道路(1776)の2025年11月時点(評価日3ヶ月前)を計算すると:
当期2Q累計OP = -90M円(2025年4〜9月)
前期通期OP = +224M円(FY2024/25)
前期2Q累計OP = -266M円(2024年4〜9月)
TTM = -90M + 224M - (-266M) = +400M円 → 黒字 ✓
最新四半期(-90M)だけ見ると赤字に見えるが、TTMで計算すると+400M円の黒字であることがわかる。実装はutils.pyに共通関数として切り出した。
def calculate_ttm_operating_income(fs_before) -> float | None:
"""J-Quants fin_summary からTTM(直近12ヶ月累計)営業利益を計算。"""
# ...最新四半期の期種を特定
latest_period = str(latest.get("CurPerType", ""))
if latest_period == "FY":
return latest_op # 通期ならそのまま
# TTM = 当期累計 + 前期FY - 前期同期累計
return latest_op + prev_fy_op - prev_same_op
改善②: 時価総額上限の引き上げ
1,500億円から3,000億円へ。PALTAC(2,775億円)、三菱食品(2,105億円)等の大型親子上場案件を捕捉するため。
primary_filter:
market_cap_max: 300000000000 # 2026-06-18変更: 大型親子上場をカバー
market_cap_min: 3000000000
4,000億円超のイオンモール、SCSK(1.1兆円)、NTTデータグループ(4.3兆円)は依然として範囲外。これらは別カテゴリの大型株として判断した。
改善③: 高スコア閾値の引き下げ
40点から35点へ。改善前の評価可能サンプルで、37点の銘柄(弘電社6mo、山陽特殊製鋼3mo/6mo)が全て実際にTOB済みだったため、35-40点のレンジも実用的なシグナルと判断した。
scoring:
thresholds:
high_score: 35 # 旧40
watch_score: 25
改善後の結果
| 指標 | yfinance版 | JQ版(改善前) | JQ版(改善後) |
|---|---|---|---|
| データ取得成功 | 8/31 | 30/31 | 30/31 |
| フィルタ通過 [3mo] | 5/31(16.1%) | 12/31(38.7%) | 13/31(41.9%) |
| フィルタ通過 [6mo] | 6/31(19.4%) | 10/31(32.3%) | 14/31(45.2%) |
| スコア25以上 [3mo] | 3/31 | 3/31 | 3/31 |
| スコア25以上 [6mo] | 3/31 | 5/31 | 5/31 |
| スコア35以上 [3mo] | 1/31 | 1/31 | 2/31 |
| スコア35以上 [6mo] | 2/31 | 1/31 | 3/31 |
| ユニーク通過銘柄 | 5/31 | 13/31 | 16/31(51.6%) |
少なくとも1評価時点でフィルタ通過した銘柄が31件中16件。検出率51.6%を達成した。
Top 5銘柄(改善後)
| 順位 | ticker | 銘柄名 | 評価時点 | スコア | フィルタ |
|---|---|---|---|---|---|
| 1 | 5660 | 神鋼鋼線工業 | 3mo | 48 | ✓ |
| 2 | 5660 | 神鋼鋼線工業 | 6mo | 46 | ✓ |
| 3 | 1948 | 弘電社 | 6mo | 37 | ✓ |
| 4 | 5481 | 山陽特殊製鋼 | 3mo | 37 | ✓ |
| 5 | 5481 | 山陽特殊製鋼 | 6mo | 37 | ✓ |
スコア35以上の銘柄は全て実際にTOBが発表済み。100%的中である。サンプル数は5件と少ないが、強力なシグナルであることが確認できた。
パターン別の精度
| パターン | 説明 | 通過/総件数 | 通過率 |
|---|---|---|---|
| A | 親子上場の現金TOB | 20/36 | 55.6% |
| B | 株式交換による完全子会社化 | 5/20 | 25.0% |
| C | 第三者TOB | 2/6 | 33.3% |
Pattern Aが圧倒的に高い通過率。親子上場スコア(最大30点)が効いており、本システムは親子上場案件の検出に特化したツールとして機能していることが確認できた。
Pattern Bが低いのは、株式交換による完全子会社化では親会社保有比率が50%未満のケースも多く、親子上場スコアの加算が十分でないため。
弘電社の事例: 早期検出の威力
特に印象的だったのは1948弘電社の評価時点による差である。
| 評価時点 | 株価 | PBR | スコア | フィルタ |
|---|---|---|---|---|
| 6ヶ月前(2025-11-25) | 2,919円 | 1.17 | 37 | ✓ |
| 3ヶ月前(2026-02-25) | 6,920円 | 2.77 | 24 | ✗(PBR超過) |
| TOB価格 | 11,501円 | — | — | — |
6ヶ月前に検出していれば2,919円 → TOB価格11,501円で約3.9倍のリターン(+294%)。早期検出の経済的価値が定量化された事例である。
残された課題
PBR欠損/上限超過(7〜8件)
TOB期待で株価が先行上昇したケース。弘電社の3ヶ月前評価(PBR 2.77)、東邦チタニウム(PBR 1.5〜1.9)等。対策は「6ヶ月前評価を重視する」運用で部分的に解決可能。
時価総額範囲外(6〜7件)
3,000億円上限でも届かない大型株。SCSK(1.1兆円)、NTTデータグループ(4.3兆円)、イオンモール(4,500億円)等。これらは本システムの設計思想(中小型割安株への特化)から外れるため、将来的に別系統スクリーニングを検討する。
営業赤字(1〜4件)
TTM化後も依然として赤字の銘柄。広栄化学(FY2025/26累計赤字継続)等は正当な除外と判断。
結論
31件中16件(51.6%)の検出率を達成した。
見方を変えれば「31件のTOBのうち16件を発表前6ヶ月の時点で抽出できた」ということでもある。投資判断の起点として、実用的な精度といえる。特にスコア35以上の5件は全て実際のTOB事例であり、高スコア判定の信頼性は高い。
次のステップとして、親会社別のグループ整理動向スキャンを実装する。本バックテストで判明したように、特定の親会社が連続的に子会社整理を進めるパターン(イオン3件、住友化学2件、日本製鉄2件)がある。これを定量化することで、次に整理される可能性が高い銘柄をさらに絞り込めると期待している。