價量支撐壓力(high_volume_support_resistance)Integration Guide — Frontend
Backend branch: feat/chip-high-volume-support-resistance → cortex/dev
Endpoints:GET /api/agent/chip/{ticker}、GET /api/signals/stock/{ticker}(chip 段)
生效時間:merge 後 cortex-api --reload 立即生效(顯示名/新 index 欄位);每日 15:30 cortex-scheduler 重啟後,新寫入的籌碼快照才會帶 high_volume_support_resistance 區塊,之前的快照回 {}。無 migration。
Consumer:讀這兩支端點籌碼段的前端(個股頁籌碼區塊)。本次盤點 docs/integration/ 只有前端 consumer 紀錄;若有未登記的 consumer,請補一份對應檔案。
相容性:純新增。既有欄位
indexes.support_resistance內容不變;既有 signal/event/story 代碼都不變,只有顯示名改了(見 §2),前端若以name字串比對會壞,以*_code比對不受影響。
1. 欄位 diff 表
兩支端點的籌碼 indexes 物件各新增兩個 key;其餘欄位不變。
| endpoint | 欄位 | 型別 | 變動 | 語意 |
|---|---|---|---|---|
| 兩支 | indexes.high_volume_support_resistance |
HighVolumeSupportResistance \| {} \| null |
新增 | 價量支撐壓力區塊,原樣回傳快照 evidence 的頂層同名區塊。本功能上線前寫入的快照為 {};/api/agent/chip 在 analysis_status = "pending_source"(籌碼來源未到齊)時為 null(與 support_resistance 同) |
| 兩支 | indexes.institutional_support_resistance |
同 support_resistance |
新增(別名) | 法人支撐壓力,與既有 indexes.support_resistance 同一個物件;兩個 key 都保留 |
| 兩支 | indexes.support_resistance |
— | 不變 | — |
| 兩支 | signals[]/events[]/stories[] |
— | 新增代碼+改顯示名 | 見 §2 |
HighVolumeSupportResistance 欄位(status 不是 available 時,thresholds 為 {}、所有 level 為 null、reason 有值):
| 欄位 | 型別 | 說明 |
|---|---|---|
status |
"available" \| "insufficient_data" \| "compute_failed" |
只有 available 才有 level |
reason |
string \| null |
非 available 的原因(例 rows=50 < 61、RuntimeError: …) |
basis |
"raw" |
原始價 |
stages |
number[] |
[60, 120, 240],三階視窗(含當日) |
stddev_multiplier |
string |
"2":大量日=量 ≥ 視窗平均+2×樣本標準差 |
thresholds |
{ [stage: string]: { mean, stddev, volume } } |
只含實際用到的階(兩側都在 60 根內找滿就只有 "60";不可假設三個 key 都在);volume 為門檻(股數) |
window_start / window_end |
string(YYYY-MM-DD) \| null |
實際搜尋的最大視窗起訖 |
closing_price |
number \| null |
當日收盤(快照存值) |
support / support2 |
Level \| null |
第一/第二支撐(低於現價的大量日收盤,依與現價距離近→遠) |
resistance / resistance2 |
Level \| null |
第一/第二壓力(高於現價) |
previous_support / previous_resistance |
{ date, close, as_of } \| null |
以前一交易日當「今天」重算的第一支撐/壓力(跌破/突破事件的比較基準) |
Level = { date: string, close: number, volume: number(股), z_score: number, stage: 60|120|240, distance_pct: number };distance_pct 恆為正:支撐=現價高於支撐的百分比((close/support − 1)×100),壓力=現價低於壓力的百分比((resistance − close)/resistance×100)。
2. 行為改變對照表
| 項目 | 代碼(不變) | 修前顯示名 | 修後顯示名 |
|---|---|---|---|
| signal | chip_support_resistance |
籌碼支撐壓力 | 法人支撐壓力 |
| event | chip_support_nearby |
籌碼支撐接近 | 法人支撐接近 |
| event | chip_resistance_nearby |
籌碼壓力接近 | 法人壓力接近 |
| event | chip_support_break |
籌碼支撐跌破 | 法人支撐跌破 |
| story | cost_defense |
成本防守 | 法人成本防守 |
| 項目 | 新代碼 | 顯示名 | direction | 成立條件 |
|---|---|---|---|---|
| signal | chip_high_volume_support_resistance |
價量支撐壓力 | state ∈ support/risk/bullish/neutral |
顯示 state 優先序:支撐跌破 risk > 壓力突破 bullish > 壓力接近 risk > 支撐接近 support |
| event | chip_high_volume_support_nearby |
支撐接近 | support | 收盤在第一支撐上方 3% 以內(含) |
| event | chip_high_volume_support_break |
支撐跌破 | risk | 收盤 < 前一日第一支撐(嚴格) |
| event | chip_high_volume_resistance_nearby |
壓力接近 | risk | 收盤在第一壓力下方 3% 以內(含) |
| event | chip_high_volume_resistance_break |
壓力突破 | bullish | 收盤 > 前一日第一壓力(嚴格) |
| story | high_volume_cost_defense |
成本防守 | support | 支撐接近成立且支撐跌破不成立(priority 301,排在 cost_defense 300 之後) |
規則:支撐側(跌破>接近)與壓力側(突破>接近)各自只出一個事件,兩側可同日並存(例:2603 同日「支撐接近」+「壓力突破」)。法人那組事件與 story 的規則不變,只改名;margin_call_capitulation/margin_call_risk 仍只參考法人支撐跌破。
| 情境 | 修前 | 修後 |
|---|---|---|
| 快照已帶區塊 | 無此 key | indexes.high_volume_support_resistance = 區塊(§4-1) |
| 快照是本功能上線前寫的 | 無此 key | {}(§4-3) |
| 日 K 不足 61 根/最後一根不是當日/收盤與快照存值不符 | — | status = "insufficient_data"(§4-2) |
| 計算例外 | — | status = "compute_failed",其他區塊照常(§4-5) |
analysis_status = "pending_source"(只有 /api/agent/chip 有此形狀) |
support_resistance: null |
兩個新 key 也是 null(§4-4) |
| story 顯示上限 | 每域 top-3(既有 ses_display.story_top_n_per_domain) |
#2114 起改為全部回傳:這兩支端點的 stories 不再每域 top-3 修剪。high_volume_cost_defense 排在 priority 301,同檔若已有 ≥3 個更前面的 story(例 1219:foreign_entry/institutional_accumulation/cost_defense),它排第 4 以後但仍會出現;前端預設顯示前 3 個、展開看完整(見 §4-7 與 docs/integration/stock-stories-full-list-frontend.md)。列表端點 GET /api/signals/latest 仍為 top-N |
3. TypeScript type diff
// 新增
export type HighVolumeLevel = {
date: string; // YYYY-MM-DD
close: number;
volume: number; // 股
z_score: number; // (volume − mean) / stddev,2 位小數
stage: 60 | 120 | 240;
distance_pct: number; // 恆為正,見 §1
};
export type HighVolumePreviousLevel = { date: string; close: number; as_of: string };
export type HighVolumeSupportResistance = {
status: "available" | "insufficient_data" | "compute_failed";
reason: string | null;
basis: "raw";
stages: number[]; // [60, 120, 240]
stddev_multiplier: string; // "2"
thresholds: Record<string, { mean: number; stddev: number; volume: number }>; // 只含實際用到的階,可能是 {}
window_start: string | null;
window_end: string | null;
closing_price: number | null;
support: HighVolumeLevel | null;
support2: HighVolumeLevel | null;
resistance: HighVolumeLevel | null;
resistance2: HighVolumeLevel | null;
previous_support: HighVolumePreviousLevel | null;
previous_resistance: HighVolumePreviousLevel | null;
};
// 兩支端點的籌碼 indexes
export type ChipIndexes = {
// ...既有欄位不變
support_resistance: ChipSupportResistance | Record<string, never> | null; // 不變
+ institutional_support_resistance: ChipSupportResistance | Record<string, never> | null; // === support_resistance
+ high_volume_support_resistance: HighVolumeSupportResistance | Record<string, never> | null;
};
// 判斷方式
export const isHighVolumeBlock = (v: unknown): v is HighVolumeSupportResistance =>
!!v && typeof v === "object" && "status" in (v as object); // {} 與 null 都不是
event/story 代碼 union 各加:"chip_high_volume_support_nearby" | "chip_high_volume_support_break" | "chip_high_volume_resistance_nearby" | "chip_high_volume_resistance_break"、"high_volume_cost_defense";signal 代碼加 "chip_high_volume_support_resistance"。
4. 真實 response(拋棄式 parity DB,13 檔 live 輸入唯讀匯出,交易日 2026-10-30)
4-1 有資料(3105.TWO,GET /api/agent/chip/3105.TWO)
indexes.high_volume_support_resistance:
{
"basis": "raw",
"reason": null,
"stages": [
60,
120,
240
],
"status": "available",
"support": {
"date": "2026-09-01",
"close": 492.0,
"stage": 60,
"volume": 55247000,
"z_score": 2.37,
"distance_pct": 17.48
},
"support2": {
"date": "2026-08-31",
"close": 447.5,
"stage": 60,
"volume": 50920000,
"z_score": 2.02,
"distance_pct": 29.16
},
"resistance": {
"date": "2026-10-29",
"close": 594.0,
"stage": 60,
"volume": 56374000,
"z_score": 2.46,
"distance_pct": 2.69
},
"thresholds": {
"60": {
"mean": 26388800.0,
"stddev": 12165761.95695199,
"volume": 50720323.91390398
},
"120": {
"mean": 25479941.666666668,
"stddev": 10736803.439842435,
"volume": 46953548.54635154
},
"240": {
"mean": 26185370.833333332,
"stddev": 12991282.47362846,
"volume": 52167935.78059025
}
},
"window_end": "2026-10-30",
"resistance2": {
"date": "2026-04-21",
"close": 617.0,
"stage": 240,
"volume": 53504000,
"z_score": 2.1,
"distance_pct": 6.32
},
"window_start": "2025-11-03",
"closing_price": 578.0,
"previous_support": {
"date": "2026-09-01",
"as_of": "2026-10-29",
"close": 492.0
},
"stddev_multiplier": "2",
"previous_resistance": {
"date": "2026-04-21",
"as_of": "2026-10-29",
"close": 617.0
}
}
{
"signals": [
{
"signal_code": "chip_support_resistance",
"state": "risk",
"direction": "risk/support",
"score": 0.7,
"name": "法人支撐壓力"
},
{
"signal_code": "chip_high_volume_support_resistance",
"state": "risk",
"direction": "bullish/risk/support",
"score": 0.7,
"name": "價量支撐壓力"
}
],
"events": [
{
"event_code": "chip_support_break",
"signal_code": "chip_support_resistance",
"state": "active",
"direction": "risk",
"score": 0.7,
"name": "法人支撐跌破"
},
{
"event_code": "chip_high_volume_resistance_nearby",
"signal_code": "chip_high_volume_support_resistance",
"state": "active",
"direction": "risk",
"score": 0.7,
"name": "壓力接近"
}
],
"stories": [
{
"story_code": "institutional_distribution",
"state": "active",
"direction": "bearish",
"score": 0.38695,
"name": "法人派發",
"suppressed_by": null
},
{
"story_code": "ownership_accumulation",
"state": "active",
"direction": "support",
"score": 0.58,
"name": "籌碼集中",
"suppressed_by": null
}
]
}
4-2 日 K 不足(6488.TWO,只剩 50 根)— insufficient_data
{
"basis": "raw",
"reason": "rows=50 < 61",
"stages": [
60,
120,
240
],
"status": "insufficient_data",
"support": null,
"support2": null,
"resistance": null,
"thresholds": {},
"window_end": null,
"resistance2": null,
"window_start": null,
"closing_price": 756.0,
"previous_support": null,
"stddev_multiplier": "2",
"previous_resistance": null
}
4-3 本功能上線前寫入的快照(2412.TW)— {}
{}
4-4 籌碼來源未到齊(1301.TW,當日融資餘額未到)— pending_source,兩個新 key 為 null
{
"ticker": "1301.TW",
"trading_date": "2026-10-30",
"analysis_status": "pending_source",
"indexes": {
"raw_chip_summary": "…",
"institutional_flow": "…",
"margin": "…",
"short": "…",
"ownership": "…",
"cost_zones": null,
"support_resistance": null,
"institutional_support_resistance": null,
"high_volume_support_resistance": null
}
}
4-5 計算例外(2382.TW,計算函式被強制丟出例外)— compute_failed,其他區塊照常
{
"basis": "raw",
"reason": "RuntimeError: sample: forced high-volume compute failure",
"stages": [
60,
120,
240
],
"status": "compute_failed",
"support": null,
"support2": null,
"resistance": null,
"thresholds": {},
"window_end": null,
"resistance2": null,
"window_start": null,
"closing_price": null,
"previous_support": null,
"stddev_multiplier": "2",
"previous_resistance": null
}
4-6 story「成本防守」(2454.TW,GET /api/signals/stock/2454.TW?qryDate=2026-10-30 的 chip 段節錄)
{
"indexes.high_volume_support_resistance.support": {
"date": "2026-08-04",
"close": 3865.0,
"stage": 60,
"volume": 17318591,
"z_score": 2.26,
"distance_pct": 1.29
},
"closing_price": 3915.0,
"events": [
{
"event_code": "chip_high_volume_support_nearby",
"name": "支撐接近",
"direction": "support"
}
],
"stories": [
{
"story_code": "high_volume_cost_defense",
"name": "成本防守",
"direction": "support",
"score": 0.7999999999999999,
"source_event_codes": [
"chip_high_volume_support_nearby",
"consensus_buy",
"institutional_dominant"
]
}
]
}
4-7 同檔同時有「法人成本防守」與「成本防守」(1219.TW):ledger 兩列都在,端點也全部回(#2114 起)
ledger(taiwan.ses_story_daily,2026-10-30):
story_code | direction | score | suppressed_by
cost_defense | support | 0.8 | -
foreign_entry | bullish | 0.56045 | -
high_volume_cost_defense | support | 0.8 | -
institutional_accumulation | bullish | 0.68545 | foreign_entry
ownership_accumulation | support | 0.072 | foreign_entry
high_volume_cost_defense,dev 實測見 stock-stories-full-list-frontend.md §4),GET /api/agent/chip/1219.TW 的 stories 自 #2114 起依引擎排序(priority 優先,同 priority 再比 score)回全部 5 個:foreign_entry、institutional_accumulation、cost_defense、high_volume_cost_defense、ownership_accumulation(priority 280/290/300/301/310,見 investment-agent/backend/config/replacement_story_definitions.py)。#2114 之前只回下列前 3 個;前端預設顯示 3 個時,high_volume_cost_defense 落在展開區:
[
{
"story_code": "foreign_entry",
"name": "外資成本區進場",
"direction": "bullish",
"score": 0.56045,
"suppressed_by": null
},
{
"story_code": "institutional_accumulation",
"name": "法人吸籌",
"direction": "bullish",
"score": 0.68545,
"suppressed_by": "foreign_entry"
},
{
"story_code": "cost_defense",
"name": "法人成本防守",
"direction": "support",
"score": 0.7999999999999999,
"suppressed_by": null
}
]
5. 前端遷移 checklist
- [ ] 以
*_code比對 signal/event/story,不要用name:五個顯示名改了(§2),代碼沒變 - [ ] 讀
indexes.high_volume_support_resistance前先isHighVolumeBlock():{}(舊快照)、null(pending_source)、status !== "available"(insufficient_data/compute_failed)三種都沒有 level,各給對應文案 - [ ]
thresholds只含實際用到的階,不可假設"60"/"120"/"240"都在 - [ ] 每側最多兩個 level(
support/support2、resistance/resistance2),任一可為null - [ ]
volume是股數;顯示張數請 ÷1000 - [ ] 若要區分「法人」與「價量」支撐壓力:法人讀
institutional_support_resistance(=support_resistance),價量讀high_volume_support_resistance - [ ] 四個新事件與新 story 加進代碼 union/文案表;壓力接近(risk)與壓力突破(bullish)方向不同
- [ ] 知悉 story 顯示:#2114 起這兩支端點回全部 story,前端預設顯示前 3 個、提供展開看完整;
high_volume_cost_defense可能排在第 4 以後(§4-7)
6. 參考
- API 規格:docs/api-reference.md(§14.5、§15 新增兩個 index 欄位)
- SESI 名冊:docs/sesi-databook.zh-TW.html(本次重生,245 events/85 stories)
- OpenAPI:docs/assets/openapi.json(兩支端點回傳自由 dict,本次未變)
- 規則核可:2026-11-02(三階 60/120/240、平均+2σ、每側兩個、前一日基準);issue #2082