はじめに
こんにちは。
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 Z | Component Score | 意味 |
|---|---|---|
| -3 | 0 | 非常に強い価格低下方向の圧力 |
| -1.5 | 25 | 下方向の圧力が強い |
| 0 | 50 | 過去の中心付近 |
| +1.5 | 75 | 上方向の圧力が強い |
| +3 | 100 | 非常に強い価格上昇方向の圧力 |
となります。
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_only、test_only、blockedと、技術的に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では、
- 元データをその意味に合った方法でTransformする
- 価格上昇圧力の方向を統一する
- 過去データのMedianとMADを使う
- Robust Z-scoreを計算する
- -3〜+3へClipする
- 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は承認されていません。



コメント