🌑️ What the market thermometer is

We collapse valuation, sentiment, and liquidity indicators onto a single 0–100 relative scale. 0 = the coldest the market has ever been within its lookback; 100 = the hottest ever. GitHub Actions recomputes on weekdays at 09:30 UTC for post-CN/HK close and daily at 22:00 UTC for post-US close; all numbers come from the silver layer and are reproducible from git history.

1

Step 1: convert each indicator to its historical percentile

Each indicator (CAPE, VIX, social financing, DXY …) is converted to its trailing 10-year rolling percentile. Monthly series (CAPE, M2, social financing) are forward-filled to daily first so the window has enough data points.

2

Step 2: direction determines hot or cold

Each indicator carries a direction in weights.yaml: + means high percentile β†’ high temperature (e.g. high CAPE = expensive = hot); - means high percentile β†’ low temperature (e.g. high VIX = fear = cold; strong DXY = tight global liquidity = cold).

3

Step 3: combine into sub-temperatures

Within each sub-temperature (valuation / sentiment / liquidity) we take a weighted average across the available indicators. Missing indicators auto-renormalise β€” e.g. when HK options PCR has no free source, HK sentiment falls back to 100% on southbound 5d instead of going blank.

4

Step 4: combine into overall temperature

The three sub-temperatures are combined per-market into the overall temperature (0–100). Bands: < 30 Cold Β· 30–70 Neutral Β· β‰₯ 70 Hot.

function Formula at a glance

indicator_pct = rolling_percentile(value, window=10y)
contribution = (direction == '+') ? indicator_pct : (100 βˆ’ indicator_pct)
sub_temp = Ξ£ (contributioni Γ— indicator_weighti) / Ξ£ available_weighti
overall = val Γ— wval + sent Γ— wsent + liq Γ— wliq
// 0 ≀ overall ≀ 100  Β·  < 30 cold Β· 30–70 mid Β· β‰₯ 70 hot

tune Per-market weights

Loaded live from config/weights.yaml β€” edit and rerun finsynapse transform run --layer temperature to apply.

πŸ‡¨πŸ‡³ CN
China A-share
CN
valuation 65% Β· sentiment 20% Β· liquidity 15%
Sub-temperature Weight Indicator Ind. weight Direction
Valuation 65%
CSI 300 trailing P/E
csi300_pe_ttm
50% + high pct β†’ hot
CSI 300 P/B
csi300_pb
50% + high pct β†’ hot
Sentiment 20%
cn_north_5d
cn_north_5d
25% + high pct β†’ hot
A-share 5-day turnover
cn_a_turnover_5d
25% + high pct β†’ hot
cn_margin_balance
cn_margin_balance
35% + high pct β†’ hot
cn_usdcny_pressure
cn_usdcny_pressure
15% βˆ’ high pct β†’ cold
Liquidity 15%
cn_m2_yoy
cn_m2_yoy
25% + high pct β†’ hot
CN social financing (12M)
cn_social_financing_12m
25% + high pct β†’ hot
cn_credit_impulse
cn_credit_impulse
25% + high pct β†’ hot
cn_dr007
cn_dr007
25% βˆ’ high pct β†’ cold
πŸ‡­πŸ‡° HK
Hong Kong
HK
valuation 60% Β· sentiment 25% Β· liquidity 15%
Sub-temperature Weight Indicator Ind. weight Direction
Valuation 60%
HK ETF TTM dividend yield
hk_ewh_yield_ttm
100% βˆ’ high pct β†’ cold
Sentiment 25%
Southbound flow (5D)
cn_south_5d
60% + high pct β†’ hot
hk_vhsi
hk_vhsi
40% βˆ’ high pct β†’ cold
Liquidity 15%
us10y_real_yield
us10y_real_yield
30% βˆ’ high pct β†’ cold
DXY
dxy
20% βˆ’ high pct β†’ cold
hk_hibor_1m
hk_hibor_1m
50% βˆ’ high pct β†’ cold
πŸ‡ΊπŸ‡Έ US
United States
US
valuation 35% Β· sentiment 45% Β· liquidity 20%
Sub-temperature Weight Indicator Ind. weight Direction
Valuation 35%
US trailing P/E
us_pe_ttm
35% + high pct β†’ hot
US CAPE (Shiller P/E)
us_cape
35% + high pct β†’ hot
us_erp
us_erp
30% βˆ’ high pct β†’ cold
Sentiment 45%
VIX
vix
40% βˆ’ high pct β†’ cold
us_hy_oas
us_hy_oas
35% βˆ’ high pct β†’ cold
us_umich_sentiment
us_umich_sentiment
25% + high pct β†’ hot
Liquidity 20%
us10y_real_yield
us10y_real_yield
25% βˆ’ high pct β†’ cold
DXY
dxy
15% βˆ’ high pct β†’ cold
us_nfci
us_nfci
35% βˆ’ high pct β†’ cold
us_walcl
us_walcl
25% + high pct β†’ hot

history Historical extremes & matched events

Below are the all-time hottest and coldest readings for each market's overall temperature, matched to the dominant macro event around that date. Today's "historical percentile" is where the current reading sits on this timeline.

"Historical percentile" = where today's overall temperature ranks against every trading day since data started. 99% means hotter than 99% of historical days.

Market Type Date Temp Matched event
πŸ‡¨πŸ‡³ CN China A-share πŸ“ Today 2026-05-14 79Β° (P79) (no labelled event in window)
πŸ‡¨πŸ‡³ CN China A-share πŸ”₯ All-time hot 2013-01-16 100Β° (no labelled event in window)
πŸ‡¨πŸ‡³ CN China A-share 🧊 All-time cold 2014-05-19 0Β° (no labelled event in window)
πŸ‡­πŸ‡° HK Hong Kong πŸ“ Today 2026-05-14 14Β° (P15) (no labelled event in window)
πŸ‡­πŸ‡° HK Hong Kong πŸ”₯ All-time hot 2021-02-23 97Β° 2021 southbound surge + tech mania peak
πŸ‡­πŸ‡° HK Hong Kong 🧊 All-time cold 2018-04-06 1Β° (no labelled event in window)
πŸ‡ΊπŸ‡Έ US United States πŸ“ Today 2026-05-14 87Β° (P70) (no labelled event in window)
πŸ‡ΊπŸ‡Έ US United States πŸ”₯ All-time hot 2018-01-30 100Β° (no labelled event in window)
πŸ‡ΊπŸ‡Έ US United States 🧊 All-time cold 2022-03-07 2Β° 2022 Fed aggressive hiking cycle

info A few things worth knowing

  • Weights are frozen once set β€” no curve fitting. Weekly attribution uses the same fixed weights.
  • The 10-year rolling window means truly unprecedented extremes get clipped at 0 or 100. By design β€” the thermometer measures "position relative to history", not absolute level.
  • The data_quality field tags rows as ok (all three sub-temps produced) or <sub>_unavailable (e.g. liquidity_unavailable when every input in that sub-temp was missing for the day). Single-indicator gaps inside a sub-temp auto-renormalize on the available weights and don't surface in this field.
  • The daily brief in gold/ is just narrative β€” LLM or template stitches these silver numbers into prose, no new calculations.