formula_screening は、stock_db が保持する日本株データを読み出し、戦略ファイルで定義した条件に基づいて銘柄を抽出し、stock_web_ui に結果を渡してブラウザ表示する薄いアプリケーション層です。
rust/ には現在のスクリーニング中核を置く。TOML 戦略の解釈、指標計算、スクリーニング、JSON payload 生成を Rust で実装し、データ読み取りは stock_db の公開 API と Rust screening API を使う。通常の運用入口は python -m formula_screening screen で、Python CLI が stock_db の汎用価格更新と stock_web_ui 連携を担当し、Rust binding formula_screening._core が payload を生成する。
処理の主経路は次のとおりです。
- Python CLI が TOML 戦略ファイルと対象銘柄を受け取る
stock_dbAPI で前営業日終値の鮮度を確認し、古ければ Stooq + Yahoo Finance JP 補完で更新するformula_screening._core.run_screening_payload_py()がstock_db/rust::screening::load_default_screening_stocks()から財務データ・株価・履歴を読み込む- Rust core が派生指標を計算し、TOML 戦略の条件を評価して JSON payload を返す
- CLI が
docs/assets/screening.jsonと任意の--json出力を書き出す --json未指定時はweb.pyが/api/screeningを配信し、src_ts/app.tsがテーブル表示する
- Python 3.13 以上
uv../stock_dbと../stock_web_uiが同じ親ディレクトリ配下にあることstock_db側に価格・財務データが投入済みであること- Python package は
src/formula_screeningに置き、pyproject.tomlのtool.maturin.python-source = "src"で editable install からも import できるようにする
uv run python -m formula_screening screen \
-s strategies/net_cash_fcf.toml -t 1867 --json /tmp/screening.jsonRust バイナリ単体でも同じスクリーニング core を実行できます。この経路は stock_db の Rust crate edinet-xbrl から screening read model を読むため、DB パスや内部テーブルを formula_screening 側の公開契約にしません。
cargo run --manifest-path rust/Cargo.toml --bin formula-screening -- \
screen -s strategies/net_cash_fcf.toml -t 1867 --json /tmp/screening.json複数銘柄、全銘柄、範囲、CSV も指定できます。
uv run python -m formula_screening screen \
-s strategies/net_cash_fcf.toml -t 1867 7203 --json /tmp/screening.json
uv run python -m formula_screening screen \
-s strategies/net_cash_fcf.toml -t all --json /tmp/screening.json
uv run python -m formula_screening screen \
-s strategies/net_cash_fcf.toml -t 1000-2000 --json /tmp/screening.json
printf "1867\n7203\n" > /tmp/formula_tickers.csv
uv run python -m formula_screening screen \
-s strategies/net_cash_fcf.toml -t csv:/tmp/formula_tickers.csv --json /tmp/screening.json静的配信用 JSON を更新する場合:
uv run python -m formula_screening screen \
-s strategies/net_cash_fcf.toml -t all --json docs/assets/screening.jsonこの出力は DB と最新株価に依存するスナップショットであり、再生成時は通過銘柄数、並び順、各指標値の差分が docs/assets/screening.json に集約されます。
GitHub Pages 用の更新をコミットする場合もこの Python CLI 経路を使い、Saved to docs/assets/screening.json まで完了した JSON をコミット対象にします。
公開項目の欠損診断は対象銘柄ごとの ERROR ログとして出ますが、これは UI 上で - になる項目の可視化であり、コマンドが終了コード 0 で完了した場合はスナップショット生成自体は成功です。
- 通常運用の
screenサブコマンドを提供する Python facade --tickerの単一値、複数値、範囲、all、csv:path.csvを解決する- スクリーニング前に
formula_screening.stock_db_compat.ensure_prices_fresh()で前営業日終値の鮮度を確認し、必要なら Stooq + Yahoo Finance JP 補完を実行する - 個別銘柄の株価が取得できない場合もスクリーニングは継続し、行 payload の
price_dateと metadata のtarget_price_dateで古い株価を UI が判定できるようにする formula_screening._core.run_screening_payload_with_diagnostics_py()を呼び出し、Rust が生成した payload を JSON 保存または Web 配信へ渡す- 全スクリーニング対象銘柄について UI 上で
-表示になる公開項目を診断し、欠損がある銘柄は欠損フィールド一覧付きのERRORログを出す --workersは互換用に残っているが、現在の Rust-backed 経路では並列数の制御には使っていない
formula-screeningバイナリが TOML 戦略を読み、stock_dbの Rust screening API から財務データを取得するlib.rsが metrics / indicators / preferred-share 判定 / JSON payload / 公開項目の欠損診断を担当する- PyO3 モジュール
formula_screening._coreがcompute_all_stock_metrics()を公開し、下流 Python repo からも Rust 実装を利用する main.rsが Rust 単体実行用のscreenサブコマンド、静的 JSON 保存、stock_web_ui_core::serve()連携を担当する- Rust 単体実行も
stock_dbの Rust screening API 経由で価格鮮度を確認する。DB path は受け取らず、STOCK_DB_VAR_DIRが設定されていればそこからstock_dbの内部 DB を解決する。server host/port などはmain.rs内の固定値を使う。--json未指定時は共有 Rust サーバーが既存の待受ポートを解放してから起動し、既定ブラウザを開く
- Python 側の比較用実装として TOML 戦略ファイルを読み込む
- TOML 戦略の
filters/sort/columnsを実行可能な関数に変換する formula_screening.stock_db_compat.load_screening_stocks()から EDINET XBRL 財務、四季報予想、価格、発行済株式数、履歴 CF / PL / dividend を読み出し、戦略評価用のstock辞書を組み立てる- SQLite connection 注入は公開 contract から外しており、渡された場合は明示的に
TypeErrorにする
- Python 比較経路で PL / BS / CF と現在株価から派生指標を計算する。通常の CLI と
compute_all_stock_metrics()は同等ロジックの Rust 実装を使う market_cap,per_actual,per,per_next,pbr,dividend_yield,total_payout_ratio,retained_earnings_ratio,equity_ratio,free_cf,interest_bearing_debt,net_cash,net_cash_ratioなどをmetricsに詰めるinterest_bearing_debtはshort_term_debt + long_term_debtで計算し、欠損項目は0として扱う(XBRLに概念が存在しない=債務ゼロ)per_actualはmarket_cap / pl.net_income、perはmarket_cap / forecast.net_income_current(四季報今期予想純利益)、per_nextはmarket_cap / forecast.net_income_next(四季報来期予想純利益)。純利益予想の単一ソースはjapan_company_handbook(stock_dbのsource=shikiho)- BS / PL / CF の単一ソースは
stock_dbのsource=edinet_xbrl、dividend yield 用の DPS はsource=shikiho(四季報)。総還元性向は EDINET XBRL 由来のdividend.dividend_paymentとcf.treasury_stock_purchaseを使う total_payout_ratioは過去10年分(config/magic_numbers.tomlのpayout_years)の配当支払額と自己株式取得額を絶対値で合算し、現在の時価総額で割る。配当支払額はdividend_historyの各期dividend_payment、自己株式取得額はcf_historyの各期treasury_stock_purchaseから取得する。全期間で両方欠損またはmarket_cap <= 0/ 欠損ではNoneretained_earnings_ratioは BS のretained_earnings(利益剰余金)を現在の時価総額で割る。stock_dbの XBRL パーサーが J-GAAPRetainedEarningsと IFRSRetainedEarningsIFRSをbs.retained_earningsとして正規化する
sum(abs(dividend_payment) for each period) + sum(abs(treasury_stock_purchase) for each period)
/ market_cap * 100
net_cashは次の式で求める
current_assets - inventories + investment_securities * 0.7
- current_liabilities - non_current_liabilities
- Python 比較実装と Rust core の unit test は、
net_cash_ratioが流動負債・固定負債を控除することを回帰テストとして固定する
fcf.py: 過去 N 期の平均 FCF Yield を計算する。既定の N はconfig/magic_numbers.tomlのfcf_years = 10。各期の FCF を現在の時価総額で割る。ライブスクリーニング向けであり、バックテスト用途には先読みバイアスがある。上場年数が N 年未満で有効期間数が不足する銘柄では警告ログを出力しNoneを返す(スクリプト全体は継続する)。10期未満の原因確認はscripts/diagnose_fcf_history.pyを使う。この診断はformula_screening.stock_db_compat.get_screening_tickers()とformula_screening.stock_db_compat.load_screening_stocks()だけを使い、CF期間数不足、期間内のfree_cf/operating_cf/investing_cf欠損、CF履歴なしを分類する。fcf_growth.py: 過去 N 期の FCF 成長率を 3 つの方法で計算する。回帰対象年数はfcf_years、SMA 窓幅はfcf_sma_window(既定 3)。fcf_cagr: 全期間 FCF > 0 の場合は自然対数をとり最小二乗法で線形回帰した傾き β からe^β - 1を%で返す(指数回帰 CAGR)。FCF に 0 以下が含まれる場合は線形回帰の傾きを平均絶対 FCF で正規化したslope / |mean| * 100にフォールバックする。データ不足または平均がゼロの場合はNone。fcf_cagr_r2: 全期間 FCF > 0 の場合は指数回帰の決定係数 R² を返す。FCF に 0 以下が含まれる場合は生値の線形回帰 R² にフォールバックする(0.0〜1.0)。1 に近いほど安定した成長トレンド。fcf_sma_cagr: 各年のfcf_sma_window年単純移動平均を計算し、最初と最後の SMA 値で成長率を求める。SMA 両端が正の場合は複利 CAGR(last/first)^(1/n) - 1、それ以外は線形成長率(last-first)/|first|/nにフォールバックする。first が 0 またはデータ不足の場合はNone。
croic.py:free_cf / (stockholders_equity + interest_bearing_debt)を計算する。interest_bearing_debtはmetrics.pyがshort_term_debt + long_term_debtから導出する。これらのBS項目はstock_dbの XBRL パーサーが JPPFS(ShortTermLoansPayable/LongTermLoansPayable等)と IFRS(BorrowingsNCLIFRS/BondsAndBorrowingsCLIFRS等)の両概念名を候補としてパースする。peg.py: Trailing PEG(peg_trailing)と独自ブレンドPEG(peg_blended_2f)を計算する。いずれもEPSベース(stock_dbのcompute_epsで計算済み)。peg_trailing(stock, years): 過去years期間の実績EPS CAGRを使い、per_actual / CAGR%を返す。5年CAGRには6データポイントが必要(years+1)。peg_blended_2f(stock, actual_years): 過去actual_years期間の実績EPS + 今期予想EPS + 来期予想EPS の独自ブレンドCAGRを使い、per_next / CAGR%を返す。標準Forward PEGではない。
/api/screeningを返す API ルートを作る/api/stock-price-metaでformula_screening.stock_db_compat.get_stock_price_metadata()の{ "price_date": "YYYY-MM-DD", "target_price_date": "YYYY-MM-DD" }を返すstock_web_uiのserve()にdocs/assets、IndexPage、API ルートを渡す- handbook 参照用に
../japan_company_handbook/dataをyazi_base_dirとして渡す - 外部利用向けの
compute_all_stock_metrics()は Rust bindingformula_screening._coreを呼び、has_preferred_sharesも返す - Python
stock辞書向けのcreate_screening_api()/save_screening_json()と、Rust payload 向けのcreate_screening_payload_api()/save_screening_payload_json()を持つ - GitHub Pages 用に
docs/assets/stock-price-meta.jsonも生成する
screenサブコマンドはスクリーニング実行後、常にdocs/assets/screening.json(GitHub Pages 用)を自動生成する- 同時に
docs/assets/stock-price-meta.jsonを生成し、UI のステータス欄に株価基準日を表示できるようにする - 同時に
docs/assets/column-config.jsonを生成し、TOML[[columns]]の Web 表示用設定をフロントエンドに提供する screenサブコマンドは実行前に前営業日終値が揃っているか確認する。古い銘柄があればformula_screening.stock_db_compat.ensure_prices_fresh()経由で Stooq 更新と Yahoo Finance JP 補完を実行する。JPX 休日定義はstock_db側のconfig/jpx_market_holidays.tomlを使う。補完後も古い株価が残る場合はprice_date付きの行として出力し、共通 UI が目立ちにくい表示にする--json <path>オプションで追加の JSON 保存先を指定できる(Web サーバーを起動しない)--json未指定時は従来どおり Web サーバーを起動する- JSON 保存後に
_auto_push_json()がjj diffで変更を検知し、jj commit+jj git pushで JSON のみを自動コミット・プッシュする
stock_web_uiのStockTableランタイムとStockColumnsカラムビルダーを読み込む- ローカル時は
/api/column-config、GitHub Pages 時はassets/column-config.jsonを fetch してカラム設定を取得する - カラム型レジストリ(
code/name/price/num/metric_num/peg/bool)に基づいてColumnDef[]を動的構築する - ローカル時は
/api/screening、GitHub Pages 時はassets/screening.jsonを fetch する metadataUrlとしてローカル時は/api/stock-price-meta、GitHub Pages 時はassets/stock-price-meta.jsonを渡す- 閾値色分けは
COMMON_THRESHOLDSにpbrとdividend_yieldを追加して定義する
戦略ファイルは TOML で定義します。具体例は
strategies/net_cash_fcf.toml を参照してください。
required_sources: 戦略が前提とするデータソース名。現在は strategy metadata として保持し、runtime のデータ存在チェックには使っていないsort: 並び順に使う登録済み指標キー[[filters]]:source,operator,threshold[[columns]]:header(optional),source,format(optional), および Web 表示用の任意プロパティ(type,decimals,scale,suffix,title,toggleable,status_source,metric_key)
source は登録済み指標キーだけを受け付けます。Python callable は使いません。
operator は >, >=, <, <=, between を使えます。
between の threshold は [lo, hi] です。Python 比較経路ではロード後の戦略に
screen(stock) / columns(stock) が組み立てられ、columns には共通リンク列が自動マージされます。Rust-backed CLI の Web/API payload は現在固定形状で、TOML の columns は validation 対象ですが表示列の生成には使っていません。
[[columns]] の type フィールドは Web UI のカラム型を指定します。code, name, price, bool は Web 専用カラムであり、CLI テキスト出力ではスキップされます。num は行直アクセス、metric_num は row.metrics.* 経由アクセス、peg はステータスフォールバック付き数値表示です。組み込みカラムでは header / format は省略可能です。
Python 比較経路で戦略に渡す辞書は、build_stock_dict() が構築します。Rust-backed 経路では stock_db_core::screening::ScreeningStock から Rust 側の Stock を構築します。
{
"ticker": str,
"name": str,
"price": float | None,
"shares_outstanding": int | None,
"pl": dict[str, float | None],
"bs": dict[str, float | None],
"cf": dict[str, float | None],
"dividend": dict[str, float | None],
"forecast": dict[str, float | None],
"metrics": dict[str, float | None],
"cf_history": list[tuple[str, dict[str, float | None]]],
"pl_history": list[tuple[str, dict[str, float | None]]],
"dividend_history": list[tuple[str, dict[str, float | None]]],
}Python 比較経路の screen_output.py は共通リンク列として少なくとも次を追加します。
monexsikiho
Web UI 側では会社名列に handbook 連携用の yazi リンクも使います。
Python 比較経路では create_screening_api() が通過銘柄の stock 辞書をフロントエンド向け JSON に変換する。通常の CLI 経路では Rust core が同じ形状の payload を作り、create_screening_payload_api() が /api/screening で返します。返却形状は次のキーを中心に構成されます。
codenamepriceprice_datemetrics.net_cash_ratiometrics.per_actualmetrics.permetrics.per_nextmetrics.equity_ratiometrics.dividend_yieldmetrics.total_payout_ratiometrics.retained_earnings_ratiometrics.pbrmetrics.market_capfcf_yield_avgpeg_trailing_5peg_trailing_5_statuspeg_blended_5y_actual_2fpeg_blended_5y_actual_2f_statushas_preferred_sharescroicfcf_cagrfcf_cagr_r2fcf_sma_cagr
下流プロジェクト向けには formula_screening.web.run_screening_strategy_payload(strategy_path, tickers=None, return_all=False)
を公開する。この関数は Rust-backed な run_screening_payload_py() を呼び、TOML戦略の通過銘柄 payload を返す。
下流側はこの payload を自分のドメインデータへ合流し、formula_screening 側には下流固有データへの依存を追加しない。
PyO3 の互換 API として run_screening_payload_py() は従来どおり payload 配列だけを返します。通常 CLI は run_screening_payload_with_diagnostics_py() を使い、payload に加えて diagnostics と column_config を受け取ります。diagnostics は全スクリーニング対象銘柄を対象に、公開 payload で None になり UI 上 - 表示になる項目を code, name, missing_fields で返します。column_config は TOML [[columns]] を JSON シリアライズした配列で、フロントエンドが動的カラム構築に使います。欠損診断はログ用途であり、結果生成自体は継続します。
フロントエンド資産は次の分担です。
stock_web_ui.page.IndexPage: ローカルサーバー起動時の HTML テンプレート入力docs/index.html: 静的配信用のページ骨格docs/assets/app.js: ビルド済みフロントエンドsrc_ts/app.ts: TypeScript ソース
app.ts は TOML 駆動のカラム設定を column-config.json(ローカル時は /api/column-config、GitHub Pages 時は assets/column-config.json)から fetch し、動的に ColumnDef[] を構築します。カラム型レジストリが code/name/price(組み込み)、num(行直アクセス数値)、metric_num(row.metrics.* 経由数値)、peg(ステータスフォールバック付き数値)、bool(yes/no テキスト)の各タイプを StockColumns の既存ビルダーと独自レンダラーで解決します。column-config.json の取得に失敗した場合は最小限のデフォルトカラム(code, name)にフォールバックします。既定ソートは net_cash_ratio 降順です。PEG 列は未算出理由を missing_input -> miss, insufficient_history -> hist, non_positive_per -> per-, non_positive_eps -> eps-, non_positive_growth -> growth- と表示し、未知 status / status なしは - を表示します。PER、PBR、配当利回り、自己資本比率、FCF Yield、CROIC に閾値ベースの色付けを行います。共通閾値は COMMON_THRESHOLDS を利用し、pbr と div% のみプロジェクト固有で追加しています。
Rust-backed 経路への移行漏れは、tests/test_rust_migration_contract.py の一時 SQLite DB E2E で検知します。このテストは formula_screening._core.run_screening_payload_py() を実行し、旧 Python 経路で UI/API に必要だった payload キー、派生指標、return_all の挙動が欠落していないことを固定します。
XBRL タグから canonical financial item への取り込み漏れは stock_db 側の責務です。stock_db/rust/src/financials.rs の unit test は、main 時点の BS / PL / CF / dividend / shares / forecast 候補タグ一覧を静的スナップショットとして持ち、現在実装がそれらを最低条件として包含していることを確認します。テスト実行時に main ブランチを読み取らず、7203 型の IFRS 負債タグなど追加タグは許容します。DB 投入前の具体的な parse 回帰は ../stock_db/tests/sources/test_xbrl_financials_parser.py で固定します。
設定値は config/ 配下の TOML で管理します。
magic_numbers.toml:fcf_years,fcf_sma_window,workers,peg_trailing_years,peg_blended_actual_years,payout_yearscli_defaults.toml: CLI 既定値path.toml: データ・ログ系パス
DB パスは formula_screening の設定では扱わず、stock_db の Rust crate と edinet-xbrl downstream-* JSON CLI が内部で解決します。path.toml は formula_screening 自身の data/ と logs/ の管理に使われます。
formula_screening.stock_db_compat は、株価更新、社名、株価 metadata、screening stock、BS 履歴を stock_db の edinet-xbrl downstream-* JSON CLI 経由で取得する境界モジュールです。stock_db.api は使いません。