GPI開発日誌 Vol.13|162円と物価指数をどう同じ0〜100にする?「Component Scoring」の仕組み

目次

はじめに

こんにちは。

GadgetStreemでは現在、ガジェット価格の上昇圧力を一つの数字で確認できる独自指数、

GPI(Gadget Pressure Index/ガジェット価格圧力指数)

を開発しています。

前回のVol.12では、Federal Reserve、BLS、New York Fedなどから取得した実データを、

Free-source Shadow Operation

として一つのPipelineへ流し始めたところをご紹介しました。(ガジェットストリーム)

ここまでで、

市場データ
↓
Collector
↓
PostgreSQL
↓
Validation
↓
Scoring
↓
Shadow Operation

という流れができています。

しかし、ここで一つ大きな疑問が残ります。

例えば、

USD/JPY:159.16円

半導体価格指数:64.1

ストレージ機器価格指数:65.056

GSCPI:1.2494

Deep Sea Freight PPI:538.265

というデータがあったとします。

数字の桁も、単位も、変動幅もまったく違います。

当然、

159.16
+64.1
+65.056
+1.2494
+538.265

と足すことはできません。

そこでGPIでは、各市場データを一度、

0〜100の共通スケール

へ変換します。

この処理を担当するのが、

Component Scoring Layer

です。


Component Scoringとは?

GadgetStreem Intelligenceの仕様では、Component Scoring Layerの役割を、

検証済みの各市場系列を、共通の「0〜100の価格上昇圧力スケール」へ変換する

ものとして分離しています。

重要なのは、この段階ではまだ5項目を合成しないことです。

Component Scoringが作るのは、

USD/JPY Score

DRAM Score

NAND Score

GPU Score

Freight Score

です。

これらへ30%、25%、20%、15%、10%のウェイトを適用してComposite GPIを作るのは、さらに次のレイヤーです。

現在の実装でもScoring Profileは、

component-scoring-v0.1

として独立して管理されています。

つまり今回のVol.13では、

市場データ → Component Score

までを扱います。

5つを合体させる話は、次回Vol.14です。


なぜ単純な前週比だけではダメなのか

最初に思いつく方法は、

前回から何%変化したか

を見ることだと思います。

例えば、

ドル円
150円 → 153円
+2%

DRAM関連指数
60 → 63
+5%

なら、

「DRAMの方が圧力が強い」

と考えることもできます。

しかし、これには問題があります。

市場によって普段の変動幅が違うからです。

例えば、

  • 普段0.5%しか動かない市場の+2%
  • 普段5%動く市場の+2%

では、同じ+2%でも意味が違います。

前者ならかなり異常な動きかもしれませんが、後者なら通常の範囲かもしれません。

GPIが知りたいのは単なる変化率ではなく、

「その市場の過去と比べて、今の動きがどの程度強いのか」

です。


まず「価格水準」ではなく「圧力」に変換する

GPIでは、データソースごとに最初のTransformを定義します。

例えば、BLSの半導体・ストレージ・Freightなどの月次価格系列では、

log(x_t / x_(t-12))

という12観測前との対数変化を使うProfileがあります。

簡単に言えば、

約1年前と比べて、価格圧力がどの程度変化したか

を見る方法です。

現在のScoring仕様でも、半導体価格プロキシやストレージ価格ベンチマークには12観測のlog_change、GSCPIにはlevelを使うProfileが定義されています。


なぜ対数変化なのか

普通の変化率でも計算はできます。

しかし、対数変化には時系列データを扱ううえで扱いやすい特徴があります。

例えば、

100 → 110

と、

200 → 220

は、絶対額では違いますが、同じ割合の変化です。

対数変化にすると、価格水準そのものよりも、

相対的にどの程度動いたのか

を扱いやすくなります。

これによって、

円

PPI

価格指数

という異なる単位を、その次の正規化処理へ持っていけます。


GSCPIだけは「そのままの水準」を使う

一方でGSCPIは少し違います。

GSCPI自体が、すでに

世界のサプライチェーン圧力が通常状態からどれくらい離れているか

を見るために作られた指数です。

そのためGSIのComponent Scoringでは、GSCPIについては、

transform:level
lag:0

というProfileを使用しています。

何でも同じ計算式へ押し込むわけではありません。

データの意味に合わせて、最初のTransformを変える

という設計です。


「上がる=悪い」とも限らない

もう一つ重要なのが、

Direction

です。

価格系のデータでは、

数値上昇
↓
コスト上昇
↓
価格上昇圧力も強い

という方向が比較的分かりやすいです。

しかし、例えば将来GPUの供給数量を使う場合、

輸入数量が減る
↓
供給が細る
↓
価格上昇圧力が強まる

という可能性があります。

この場合、数字が下がった方が価格上昇圧力は高いことになります。

実際、現在研究中のGPU Census候補では、数量系列についてdirection: negativeのShadow Profileが検討されています。ただし、これはまだProduction Scoreではありません。

そのためGPIでは、

最終的なComponent Scoreでは、数字が大きいほど「ガジェット価格を押し上げる方向」

になるよう、Directionを揃えます。


次に「過去と比べて異常か」を測る

Transformが終わったら、次に現在値を過去の分布と比較します。

ここで使う考え方が、

Z-score

です。

一般的なZ-scoreは、

Z =
(現在値 − 平均値)
÷ 標準偏差

という形です。

例えば、

Z = 0

なら平均付近。

Z = +2

なら平均からかなり上。

Z = -2

なら平均からかなり下。

という具合です。


ただしGPIでは普通のZ-scoreをそのまま使わない

市場データには、ときどき極端な値が現れます。

例えば、

  • 金融危機
  • コロナ禍
  • 半導体不足
  • 海上物流の急騰
  • 為替急変

などです。

普通の平均値と標準偏差は、こうした極端値の影響を受けます。

そこでGPIでは、

Median(中央値)

と、

MAD(Median Absolute Deviation/中央絶対偏差)

を利用したRobustな正規化を採用しています。

MADは、

各値が中央値から
どれくらい離れているか
↓
その距離の中央値

です。

NISTもMADを、標準偏差より極端値の影響を受けにくいRobustなScale指標として説明しています。(NIST)


Robust Z-scoreを計算する

GPIで使っている考え方は、NISTが紹介するModified Z-scoreと同系統です。

概念的には、

Robust Z
=
0.6745 ×
(現在値 − Median)
÷ MAD

です。

NISTでもModified Z-scoreとして、

0.6745 × (x - median) / MAD

という形が示されています。(NIST)

つまり、

現在の市場変化

を、

その市場自身の
過去の典型的な中心と変動幅

に対して比較しています。


これでドル円とPPIを比較できる

ここが重要です。

例えば、

USD/JPY
159.16円

と、

GSCPI
1.2494

は、Raw Valueではまったく比較できません。

しかしそれぞれを自分自身の過去分布と比べれば、

USD/JPY
Robust Z = -0.86

GSCPI
Robust Z = +1.32

という、単位のない共通尺度へ変換できます。

これで初めて、

「それぞれの市場で、現在の圧力が過去と比べてどの位置にあるか」

を比較できるようになります。


ただしZ-scoreをそのまま使わない

さらにGPIでは、Robust Z-scoreを、

-3 ~ +3

に制限します。

これを、

Clipping

と呼んでいます。

例えば、

Robust Z = +1.5
↓
+1.5

Robust Z = +4.4
↓
+3.0

Robust Z = -5.2
↓
-3.0

です。

実際のComponent Scoreにも、

clip_range:[-3, 3]

が記録されています。


なぜ極端な値を切るのか

例えば、一つの市場で歴史的なショックが起きて、

Z = +12

になったとします。

その値をそのままCompositeへ持ち込むと、一つの市場だけがGPI全体を支配する可能性があります。

GPIでは極端なショックを無視するのではなく、

「非常に強い上昇圧力」として上限100に飽和させる

設計にしています。

また、100への張り付きが頻発していないかについても、Shadow研究では、

  • clipping count
  • clipping rate
  • 100相当のsaturation
  • 連続clipped期間

まで確認します。

100がたくさん出れば良いわけではありません。

むしろ頻発するなら、

Scoring Profileが市場の変動幅に合っていないのではないか

という検証対象になります。


最後に0〜100へ変換

Clipped Z-scoreを、読者にも分かりやすい0〜100へ変換します。

GPIのComponent Scoringでは、概念的に、

Score
=
50
+
Clipped Z ÷ 3 × 50

という変換になります。

その結果、

Clipped ZComponent Score意味
-30非常に強い価格低下方向の圧力
-1.525下方向の圧力が強い
050過去の中心付近
+1.575上方向の圧力が強い
+3100非常に強い価格上昇方向の圧力

となります。


50点は「価格が変わらない」という意味ではない

ここは誤解されやすいポイントです。

Score = 50

は、

来週のガジェット価格が変わらない

という予測ではありません。

意味は、

そのComponentの現在の変化が、参照している過去分布の中心付近にある

です。

同様に、

Score = 75

だから、

価格が75%上昇する

わけでもありません。

0〜100は、

価格変化率でも、上昇確率でもありません。


では「100点」は何を意味するのか

これも重要です。

GPIのComponent Scoreが100になったからといって、

これ以上価格が上がる余地がない

という意味ではありません。

また、

100%の確率で値上がりする

という意味でもありません。

正確には、

現在のTransform値が、過去の参照分布に対して非常に強い上昇圧力領域にあり、Robust Zが+3以上だったため上限100へClipされた

という意味です。


実際に100になった例もある

GSI内部の過去のScoring Runでは、ストレージ機器価格ベンチマークについて、

Raw Value:
65.056

Transformed Value:
0.265628...

Robust Z:
4.437489...

Clipped Z:
3.0

Component Score:
100.0

という結果が実際に出ています。

ここで、

Robust Z = 4.44

をそのままスコアへ伸ばさず、

Clipped Z = 3.0
↓
Score = 100

で止めています。

つまり、この100は、

「歴史的な参照分布と比べて非常に強い圧力」

を表しています。


他のComponentではどうなる?

同じ内部Scoring Runでは、例えばGSCPIは、

Robust Z:
1.3187

Score:
71.98

でした。

また別の内部SnapshotではUSD/JPYが、

Robust Z:
-0.8568

Score:
35.72

となっています。

これを見ると、

159.16円

1.2494

65.056

という直接比較できなかった数字が、

USD/JPY:約36

GSCPI:約72

Storage Benchmark:100

という同じ尺度へ変わることが分かります。

ただし、これは過去の内部検証時点の実行例です。

現在公開しているGPI Status Previewの数値ではありません。


「スコアを計算できる」と「GPIに使える」は別

Vol.7以降、何度も出てきたルールがここでも重要です。

GSIでは、Scoring結果とProduction Eligibilityを分離しています。

現在、系列は例えば、

eligible

benchmark_only

test_only

blocked

という状態を持ちます。

benchmark_onlyでも技術的には0〜100を計算できます。

しかし、通常のProduction ScopeではScoreが計算できたという理由だけでウェイトを与えません。

ADRでも、

benchmark_onlytest_onlyblockedと、技術的にScoringできることを分離する

という契約を固定しています。

つまり、

Score = 100

でも、

Production Eligible = false

は普通にあり得ます。


データ不足なら点数を作らない

もう一つ重要なのが、

Minimum Samples

です。

十分な過去データがない状態で、

過去3か月だけある
↓
とりあえずScore 82

とはしません。

月次系列の多くでは、現在のScoring Profileで少なくとも、

36 transformed samples

を要求しています。

過去データが足りなければ、

NOT_READY

やGovernance上のBlockとして扱います。


GPUが現在23 / 36なのはこのため

この仕組みは、現在のGPU開発でも実際に効いています。

直近の確認済みProduction状態では、

GPU transformed samples:
23

Required:
36

です。

したがってGPUは、

HISTORY_ACCUMULATION

の状態です。

15%を無理に埋めるために、

必要36
↓
23しかない
↓
基準を23へ下げよう

とはしません。

現在のPublic Status Previewでも、

  • Production Coverage:85%
  • Required Coverage:100%
  • GPU:23 / 36
  • Composite GPI:WITHHELD

と表示する設計になっています。


85%を100%へ換算しないのと同じ考え方

Vol.12では、

Ready Weight 85%を100%へRenormalizeしない

と説明しました。(ガジェットストリーム)

Component Scoringでも考え方は同じです。

データが足りないなら、

足りないこと自体を情報として残す

ようにします。

不足しているからといって、

  • サンプル条件を下げる
  • 他系列を黙って代入する
  • 別Componentへウェイトを回す
  • 仮の値を入れる

ことはしません。


月次データを週次GPIへどう合わせる?

GPIは週次更新を想定しています。

一方、

  • BLS
  • GSCPI
  • GPUの一部候補

には月次や四半期のデータがあります。

ここで、

6月:100

7月:104

だったから、

6月第2週:101
6月第3週:102
6月第4週:103

という値を作ることはしません。

それは市場で観測された数字ではありません。


「観測値」と「Checkpoint」を分ける

現在のMethodology設計では、

Canonical Observation

と、

Weekly Checkpointで利用可能だった情報

を分けて考えています。

例えば一つの月次値が、

6月発表値

として存在している場合、それを毎週コピーして、

6月第1週の新観測
6月第2週の新観測
6月第3週の新観測

とはしません。

同じ一つのObservationです。

週次Checkpointでは、

その時点で既に公表されていて、かつFreshness条件を満たす最新のObservationは何か

を参照する考え方です。

現在のMixed-frequency設計でも、同じ四半期・月次観測を週ごとに複製して観測数を水増しすることや、線形補間、未来情報の使用、Missing Weightの再配分は禁止されています。


「最新値を使う」と「未来を知っていたことにする」は違う

バックテストでは特に重要です。

例えば、

7月10日に6月データが公表

されたとします。

7月5日のGPIを再現するときに、

後から分かった6月データ

を使ってはいけません。

当時はまだ利用できなかったからです。

これを、

Future-information leakage

と呼びます。

GPIでは、過去のCheckpointを再現する場合、

その日時点で実際に知ることができたデータ

だけを使用します。

そうしなければ、バックテストだけ異常に優秀な指数になってしまいます。


Scoreは必ずValidationの後

Component Scoringだけが独立して好きなデータを読むこともありません。

処理順は、

Observation
↓
Validation
↓
Eligibility
↓
Component Scoring

です。

例えば、

  • Freshnessに問題がある
  • Revision整合性が取れていない
  • GovernanceでBlocked
  • Source Rightsが未承認

なら、

Score = ○○

をProduction結果として出しません。

実際のScoring Engineでも、

ValidationまたはGovernanceがBlockしている場合はComponent ScoringをBlockする

という動作になっています。


Component Scoreは「市場予測」ではない

ここも明確にしておきます。

例えば、

DRAM Score:80

だったとしても、

来週DRAM価格が上がる確率80%

ではありません。

意味は、

DRAM Componentとして採用されたTransform値が、その過去分布と比べて強い上方向の価格圧力を示している

です。

GPIは将来的な価格変動との関係を検証していきますが、

Component Scoreそのものは予測確率ではありません。


現在のGPI開発は、この記事よりさらに先へ進んでいる

ここで、Vol.12と同じく時系列について補足します。

Component Scoring Layer自体は、この記事を書いている今、初めて作っている機能ではありません。

GSIリポジトリにはすでに、

  • Scoring Engine
  • Versioned Scoring Profile
  • Component Score保存
  • Historical Backfill
  • Validationとの連携
  • Composite Calculationとの連携

が実装されています。関連するScoring Layer、Historical Backfill、Composite Calculation、Backtestingは、それぞれ独立したIssue・ADR・仕様として管理されています。

この記事シリーズでは、読者が理解しやすい順番で紹介しています。


現在はProduction Coverage 85%

そして実際のGPI開発は、初期Shadow Cycleよりさらに進んでいます。

直近の確認済みPublic Statusでは、

USD/JPY:
INCLUDED

DRAM:
INCLUDED

NAND:
INCLUDED
※Storage-device price proxy disclosure付き

Freight:
INCLUDED
※Deep-sea freight price-pressure proxy disclosure付き

GPU:
HISTORY_ACCUMULATION
23 / 36

Production Coverage:
85%

Required Coverage:
100%

となっています。

Vol.7〜Vol.10で「まだ正式採用していない」と紹介した各候補のうち、NANDやFreightなどは、その後のMethodology・Qualification・Production Activation工程を経て開発が前進しています。

これは過去記事との矛盾ではなく、

各記事で紹介したGateを、その後一つずつ通過してきた結果

です。


それでも公開Component Scoreは出していない

ここが現在の重要な境界です。

Public Status Preview自体はすでにLiveです。

しかし公開Payloadでは、

Composite Value:
null

Component Scores:
非公開

Contributions:
非公開

Raw Observations:
非公開

としています。

公開しているのは、

現在どこまでProduction準備ができているか

というStatusです。

直近の公開実行記録でも、

Public Status Preview:LIVE

Production Coverage:85%

Composite Numeric GPI:
WITHHELD

Public Numeric Release:
NOT_PUBLISHABLE

Full GPI Production Release:
NO_GO

となっています。

つまり、

Component Scoreを計算できることと、それを一般公開してよいことは別

です。

ここでもGPIのFail-closed方針を維持しています。


Component Scoringを一枚でまとめると

今回の処理を一つの流れにすると、

Raw Market Data
159.16円 / PPI 538.265 / GSCPI 1.2494 …
        ↓

① Transform
log change / level など
        ↓

② Directionを統一
大きいほど「価格上昇圧力」
        ↓

③ 過去分布と比較
Median + MAD
        ↓

④ Robust Z-score
現在が過去からどれくらい離れているか
        ↓

⑤ -3 ~ +3へClip
極端な値の影響を制限
        ↓

⑥ 0 ~ 100へMapping
        ↓

Component Score

となります。


0〜100の意味

最後にもう一度整理します。

0
↓
非常に強い価格低下方向の圧力

25
↓
下方向の圧力

50
↓
過去の中心付近

75
↓
強い価格上昇方向の圧力

100
↓
非常に強い価格上昇方向の圧力

です。

これは、

価格の上昇率

でも、

価格が上がる確率

でもありません。

過去と比較した「価格圧力の位置」

です。


次のVol.14でいよいよ5つを合体させる

ここまでで、

USD/JPY
↓
0〜100

DRAM
↓
0〜100

NAND
↓
0〜100

GPU
↓
0〜100

Freight
↓
0〜100

という共通のComponent Scoreを作る仕組みを説明しました。

では次に、

30%
25%
20%
15%
10%

をどう適用するのでしょうか。

次回のGPI開発日誌 Vol.14では、

GPI Composite Engine

を取り上げます。

具体的には、

  • Component Score × Weightとは何か
  • Weighted Contributionの計算
  • なぜウェイト合計を100%に固定するのか
  • Componentが欠けたらどうするのか
  • なぜ85%を100%へRenormalizeしないのか
  • Composite GPIを保存する条件
  • WITHHELDになる条件
  • Publishability Gate
  • なぜ現在85%なのにGPI数値を公開していないのか

を解説します。

いよいよ、

5つの市場データが一つのGPIへ合成される仕組み

に入ります。


まとめ

Vol.12では、無料・公的データを使ってGPI全体をShadow環境で動かし始めました。(ガジェットストリーム)

今回のVol.13では、その中間にある、

Component Scoring

の仕組みをご紹介しました。

GPIでは、

  1. 元データをその意味に合った方法でTransformする
  2. 価格上昇圧力の方向を統一する
  3. 過去データのMedianとMADを使う
  4. Robust Z-scoreを計算する
  5. -3〜+3へClipする
  6. 0〜100へ変換する

という流れで、単位の異なる市場データを共通スケールへ変換します。

MADは極端値の影響を受けにくいRobustなScale指標であり、Modified Z-scoreではMedianとMADを用いて現在値の位置を測れます。(NIST)

そして重要なのは、

100点=価格が100%上がる、ではない

ことです。

100は、

過去と比べて非常に強い価格上昇圧力領域へ入っている

という意味です。

反対に50も、

価格が変わらない

という予測ではなく、過去分布の中心付近を示します。

さらに、Scoreを計算できても、

  • データ利用権
  • Semantic Fit
  • Freshness
  • Historical Coverage
  • Production Eligibility

を通過していなければ、Production GPIには入りません。

現在のGPIはProduction Coverage 85%まで進みましたが、GPUは23/36 transformed samplesの履歴蓄積中であり、Composite Numeric GPIは今もWITHHELDです。

ようやく、

市場データを集める

ところから、

市場データを同じ言語へ翻訳する

ところまで来ました。

次回は、その5つの「同じ言語になった数字」を一つのGPIへ合成します。


※GPIは現在開発中の実験的な市場分析指標です。本記事で説明する0〜100のComponent Scoreは価格変化率、価格上昇確率、投資リターン予測ではありません。また、記事中の具体的なComponent Scoreは過去の内部検証結果を説明するための例であり、現在の公開GPI値ではありません。現在公開されているのはGPI Public Status Previewであり、Composite Numeric GPIおよびComponent ScoreのPublic Numeric Releaseは承認されていません。

この記事が気に入ったら
フォローしてね!

コメント

コメントする

目次