跳轉到

AETF report_data:持股、每日異動、逐股合計

適用 aetf-v8~aetf-v14。v12 將官方交易日 anchor、最後有效持股日與績效窗口完全分離,並增加持股新鮮度及績效日期;v13 排除台北今天尚未到共用資料時間的 session,不變更資料結構;v14 調整機械正文的日數去重、零/未知與自然時間文案並更新快取版本,report_data payload 不變。這是 core/mas/aetf_report_data.py 的 build_aetf_llm_payload(facts) 產生的報告專用資料,不是公開 API 的完整 facts。公開 request/response 外層 shape 保留;後端提供股數可判定性及日期證據,供 serializer 區分零與未知。user prompt 僅包含此 JSON,沒有預先寫好的報告或結論。

三組主要資料:

  1. current_holdings:目前持股明細,每檔一筆。
  2. daily_changes:依官方交易日列出有變動個股及股數/估值,保留無異動日、缺日與必要例外,供理解調整順序。
  3. stock_summary:每檔股票分別累計各日的增持、減持與淨變化,包含 5 日及 20 日各一組合計,供比較累計規模。完整且無異動的個股也提供零值合計;已退出個股仍保留歷史變動與合計。

另外附 ETF 身分、日期、分析範圍、共通語意及 ETF 績效。所有股票保留,不裁切成固定前幾名。最新一日異動在 daily_changes 最後一天,不再另複製一份 today summary。舊送模欄位 daily_trades/summary 改名僅限報告 serializer,不重新命名公開 facts 的 daily_intervals/stock_changes。

結構與欄位語意

? 表示條件出現,number | null 表示不能算時保持未知;省略不是零。

report_data
  etf: { ticker: string, name: string, market: "taiwan" }
  requested_date, anchor_date: ISO date
  holdings_as_of: ISO date | null
  holdings_freshness: {
    status: "current" | "stale" | "unknown",
    anchor_date: ISO date,
    holdings_as_of: ISO date | null,
    lag_session_count: nonnegative integer | null
  }
  scope: { included: "identified_TW_TWO_stocks", covers_entire_fund: boolean | null }
  semantics: {
    shares_unit: "股", amount_currency: "TWD",
    performance: { basis: "raw_close_price_return", is_nav: false, is_total_return: false },
    buys_sells, estimated_amount, period_boundary, overlap, zero_null,
    data_status, empty_day, stock_period, days_streak: string
  }
  periods: { today, five_day, twenty_day: Period }
  performance: { today, five_day, twenty_day: Performance }
  current_holdings: [Holding]
  daily_changes: [Day]
  stock_summary: [StockSummary]

Holding
  ticker, name: string
  shares, weight_pct: number | null
  status?: "partial"             # 只在股數/身份品質不完整時出現

Day
  date: ISO date                 # 該次相鄰快照比較的期末交易日
  data_status: "complete" | "partial" | "unavailable"
  buys, sells: [Change]
  reasons?: [string]
  all_stocks_unknown?: true       # 該日全體股數變動無法確認
  unknown_tickers?: [string]      # 僅指定個股股數變動無法確認
  exceptions?: [{ ticker, name, reason: string, weight_change_pct?: number }]

Change
  ticker, name: string
  shares: positive number
  estimated_amount: nonnegative number | null
  position_change?: "added" | "removed"

StockSummary
  ticker, name: string
  five_day: StockPeriod
  twenty_day: StockPeriod

Totals                           # 已確認範圍的合計;無操作時為零
  buy_shares, sell_shares: nonnegative number
  net_shares: number
  estimated_buy_amount, estimated_sell_amount: nonnegative number | null
  estimated_net_amount: number | null
  buy_days, sell_days: nonnegative integer

StockPeriod
  Totals 的全部欄位                # 不完整期間時全部為 null
  status?: "partial" | "unavailable"  # 省略表示 complete
  unknown_days?: nonnegative integer
  confirmed_subintervals?: Totals # 僅已確認區間的合計,不代表完整期間
  ending_streak: EndingStreak | null
  position_change?: "added" | "removed"
  latest_reversal?: "to_increase" | "to_decrease"
  weight_change_pct?: number

EndingStreak
  { direction: "increase" | "decrease", days: positive integer }
  或 { direction: "none", days: 0 }

Period
  start_date, end_date: ISO date
  interval_count: 1 | 5 | 20
  data_status: "complete" | "partial" | "unavailable"
  missing_dates?: [ISO date]

Performance
  baseline_date, end_date: ISO date
  return_pct: number | null
  status: "complete" | "partial" | "unavailable"
  has_ex_dividend, has_other_corporate_action: boolean | null

範圍、期間與品質

  • scope.included 指可識別的台灣上市/上櫃股票。covers_entire_fund=false 表示有範圍外資產;true 僅在快照完整且未發現範圍外資產時成立;不足以判定為 null。這不是個股資料完整度。
  • periods.*.data_status 指指定範圍內股數變動是否足以判定。排除現金、非股票或海外部位本身不使範圍內股票降級;缺快照、股數/識別/公司行動證據不足才限制判讀。缺個股價格不否定已確認股數,改以金額 null 及 missing_price 例外揭露。
  • start_date 是基準快照日,累計嚴格使用 start_date < daily_changes.date <= end_date。interval_count 是官方交易區間數,不是日曆天數,也不表示實際資料已齊全;完整度另看 data_status。
  • anchor_date 是 requested date 當日或以前最近的合格官方交易日;僅 requested date 等於台北今天且現在早於共用 DAILY_STOCK_UPDATE_TIME(15:30)時排除今天,15:30 起納入。歷史請求不受今天時間影響;週末/休市不額外退一個 session。holdings_as_of 是最後一個 success、非空且列數相符的持股快照日。periods 的 2/6/21 個快照日結束於 holdings_as_of,不會被較新的績效日期拉動。
  • holdings_freshness.lag_session_count 是 (holdings_as_of, anchor_date] 之間的官方交易日數,不是日曆天數。同日為 0;週末/休市的 requested date 不額外增加 lag。無法驗證時為 unknown/lag 為 null,不猜測或當成 0。
  • 例如近 5 日基準日為 2026-10-05 時,不包含 2026-10-05 當日的異動。缺日仍保留在完整官方交易日序列,不能跳過後續接成連續。
  • 每日 all_stocks_unknown=true 表示該日所有個股都不能用空清單推論零;局部不足則以 unknown_tickers 指出受影響個股。exceptions 補充已知具體原因或僅權重變化;正常資料不逐筆重複警語。

零、未知與連續日數

  • buys/sells 是快照股數增減,不是成交紀錄,可能包含申贖。股數一律正值,方向由清單決定;net_shares = buy_shares - sell_shares。
  • daily_changes 與 stock_summary 來自同一份已標準化變動;彙總直接累加明細中的股數、估值與操作日數,不重新定價。逐日估值是股數差乘同日未還原收盤價;兩側各自為正值,淨額為增持減減持。缺個股價格時保留股數,沿用該股期間估值全有或未知的口徑:任一變動缺價,該期間三個估值合計都為 null。
  • 0 是可確認的零;null 是未知或不可計算。淨零但正負兩邊不為零,表示有操作而相抵。
  • 每日 buys=[]、sells=[] 只有在 data_status=complete 且無例外時,才表示範圍內確認無異動。
  • 已確認整段無異動的個股提供零值 Totals;沒出現 action 並非足夠證據。某個期間未提供,預設不能解讀成無異動。
  • 整體「確認未見持股股數變化」還要求 stock_summary 至少有一檔可分析股票。最新一日須同時是完整 daily_changes、無 unknown/exception,且 buys/sells 皆空;近 5/20 日則要求每檔對應期間的 buy_shares/sell_shares 都是明確的 0。空 stock_summary 表示缺少可分析的台股持股,不能用空集合宣稱無異動。
  • buy_days/sell_days 是期間內有增減持的交易日數;ending_streak 才是該期間截至最新一日的連續區間數。最後一天確認無異動為 none/0,可確認連續增減持為方向/正整數,無法確認為 null。無異動日中斷連續;缺漏不能跨越,若缺漏讓連續長度不確定則保留未知。
  • 無 status 的 StockPeriod 表示該股票完整期間合計。partial/unavailable 時,八個完整期間合計欄位全部為 null;有已確認區間才提供 confirmed_subintervals,不得稱完整期間結果。即使全窗口有其他個股例外,完整個股仍可保有完整合計。
  • 只有權重變化不推論股數,留在例外或 weight_change_pct。新增/退出只由後端已驗證事件提供,不能由淨股數正負或權重推論;weight_pct=0 不代表 shares=0。
  • 每日明細與期間合計是同一批變動的不同呈現,不能重複相加;5 日又包含於 20 日內,兩期間也不能相加。

績效與 prompt 使用

performance.*.return_pct 使用未還原收盤價漲跌;共通 semantics.performance.basis=raw_close_price_return,非 NAV、非含息總報酬。績效獨立使用結束於 official anchor_date 的 2/6/21 個官方交易日,不回退到舊持股日;每期的 baseline_date、end_date 直接保留在 performance 物件。指定日價格缺失時回 partial|unavailable 與 null,不改用其他日價格。公司行動旗標為三態:true 有已知事件,false 僅在相應覆蓋完整且無事件時成立,缺覆蓋為 null。

prompt 保留三段、約 250~400 字與精簡寫作要求;current_holdings 用於理解目前部位,daily_changes 用於理解調整順序而非逐日流水帳,stock_summary 用於引用期間累計與比較規模。連續操作以 ending_streak 為準,正文寫「截至最新一日」;只有近 20 日完整方向證據支持的位置比較才寫「20 個交易日前」。完整度與缺值限制判讀,只有實質影響主要結論時才用白話寫入正文。

實際樣本與驗證

實際樣本使用已保存的唯讀來源 source-00981A-2026-10-13.json.gz:主動統一台股增長(00981A.TW),21 個官方交易日,2026-09-10 至 2026-10-13。資料不代表目前即時持股。

task worktree 的 .codex/aetf-actual-llm-input-00981A-2026-10-13.txt 是可整份貼給 LLM 的 prompt+JSON;同名 aetf-actual-llm-data-…json 為展開格式資料;request manifest 記錄實際生成呼叫邊界、來源 hash 與筆數。擷取不呼叫 provider、不存取 DB。

驗證入口為 test_aetf_agent.py 與 test_aetf_agent_real_fixture.py:從原始快照及同日價格核對每日與逐股合計,並檢查基準日排除、零與未知、正負相抵、無異動/缺日中斷、範圍排除及零權重持股。實際執行結果與完整生成樣本以本次 task evidence 為準;本文件不宣稱尚未執行的測試通過。