Skip to content

Commit e8a7692

Browse files
committed
Add horizon scoring and market confirmation
1 parent 8c62fb7 commit e8a7692

18 files changed

Lines changed: 484 additions & 69 deletions

.github/workflows/monthly_advisory_review.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ on:
3232
required: false
3333
default: "data/output/theme_momentum_snapshot.json"
3434
type: string
35+
market_confirmation_path:
36+
description: "Optional market confirmation CSV path inside advisor repository. Empty disables this input."
37+
required: false
38+
default: ""
39+
type: string
3540
schedule:
3641
- cron: "20 13 1 * *"
3742

@@ -68,6 +73,7 @@ jobs:
6873
POLITICAL_WATCHLIST_PATH: ${{ github.event.inputs.political_watchlist_path || 'data/live/political_watchlist.csv' }}
6974
AI_SIGNAL_PATH: ${{ github.event.inputs.ai_signal_path || 'data/output/latest_signal.json' }}
7075
THEME_MOMENTUM_PATH: ${{ github.event.inputs.theme_momentum_path || 'data/output/theme_momentum_snapshot.json' }}
76+
MARKET_CONFIRMATION_PATH: ${{ github.event.inputs.market_confirmation_path || '' }}
7177
run: |
7278
set -euo pipefail
7379
AS_OF="${INPUT_AS_OF:-$(date -u +%F)}"
@@ -76,13 +82,18 @@ jobs:
7682
if [ -n "${THEME_MOMENTUM_PATH}" ] && [ -f "../research-signal-context/${THEME_MOMENTUM_PATH}" ]; then
7783
THEME_ARGS=(--theme-momentum "../research-signal-context/${THEME_MOMENTUM_PATH}")
7884
fi
85+
MARKET_ARGS=()
86+
if [ -n "${MARKET_CONFIRMATION_PATH}" ] && [ -f "${MARKET_CONFIRMATION_PATH}" ]; then
87+
MARKET_ARGS=(--market-confirmation "${MARKET_CONFIRMATION_PATH}")
88+
fi
7989
python scripts/build_advisory_report.py \
8090
--as-of "${AS_OF}" \
8191
--cadence monthly \
8292
--political-events "../political-events/${POLITICAL_EVENTS_PATH}" \
8393
--political-watchlist "../political-events/${POLITICAL_WATCHLIST_PATH}" \
8494
--ai-signal "../research-signal-context/${AI_SIGNAL_PATH}" \
8595
"${THEME_ARGS[@]}" \
96+
"${MARKET_ARGS[@]}" \
8697
--output-json "data/output/monthly_advisory_review/advisory_report_${AS_OF}.json" \
8798
--output-md "data/output/monthly_advisory_review/advisory_report_${AS_OF}.md"
8899
REVIEW_ARGS=(

.github/workflows/publish_advisory_site.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ on:
3232
required: false
3333
default: "data/output/theme_momentum_snapshot.json"
3434
type: string
35+
market_confirmation_path:
36+
description: "Optional market confirmation CSV path inside advisor repository. Empty disables this input."
37+
required: false
38+
default: ""
39+
type: string
3540
schedule:
3641
- cron: "0 13 * * 6"
3742

@@ -77,6 +82,7 @@ jobs:
7782
POLITICAL_WATCHLIST_PATH: ${{ github.event.inputs.political_watchlist_path || 'data/live/political_watchlist.csv' }}
7883
AI_SIGNAL_PATH: ${{ github.event.inputs.ai_signal_path || 'data/output/latest_signal.json' }}
7984
THEME_MOMENTUM_PATH: ${{ github.event.inputs.theme_momentum_path || 'data/output/theme_momentum_snapshot.json' }}
85+
MARKET_CONFIRMATION_PATH: ${{ github.event.inputs.market_confirmation_path || '' }}
8086
run: |
8187
set -euo pipefail
8288
AS_OF="${INPUT_AS_OF:-$(date -u +%F)}"
@@ -85,13 +91,18 @@ jobs:
8591
if [ -n "${THEME_MOMENTUM_PATH}" ] && [ -f "../research-signal-context/${THEME_MOMENTUM_PATH}" ]; then
8692
THEME_ARGS=(--theme-momentum "../research-signal-context/${THEME_MOMENTUM_PATH}")
8793
fi
94+
MARKET_ARGS=()
95+
if [ -n "${MARKET_CONFIRMATION_PATH}" ] && [ -f "${MARKET_CONFIRMATION_PATH}" ]; then
96+
MARKET_ARGS=(--market-confirmation "${MARKET_CONFIRMATION_PATH}")
97+
fi
8898
python scripts/build_advisory_report.py \
8999
--as-of "${AS_OF}" \
90100
--cadence weekly \
91101
--political-events "../political-events/${POLITICAL_EVENTS_PATH}" \
92102
--political-watchlist "../political-events/${POLITICAL_WATCHLIST_PATH}" \
93103
--ai-signal "../research-signal-context/${AI_SIGNAL_PATH}" \
94104
"${THEME_ARGS[@]}" \
105+
"${MARKET_ARGS[@]}" \
95106
--output-json "data/output/published/advisory_report_${AS_OF}.json" \
96107
--output-md "data/output/published/advisory_report_${AS_OF}.md"
97108
python scripts/publish_advisory_site.py \

.github/workflows/weekly_advisory_review.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ on:
2727
required: false
2828
default: "data/output/theme_momentum_snapshot.json"
2929
type: string
30+
market_confirmation_path:
31+
description: "Optional market confirmation CSV path inside advisor repository. Empty disables this input."
32+
required: false
33+
default: ""
34+
type: string
3035
schedule:
3136
- cron: "30 12 * * 6"
3237

@@ -62,6 +67,7 @@ jobs:
6267
POLITICAL_WATCHLIST_PATH: ${{ github.event.inputs.political_watchlist_path || 'data/live/political_watchlist.csv' }}
6368
AI_SIGNAL_PATH: ${{ github.event.inputs.ai_signal_path || 'data/output/latest_signal.json' }}
6469
THEME_MOMENTUM_PATH: ${{ github.event.inputs.theme_momentum_path || 'data/output/theme_momentum_snapshot.json' }}
70+
MARKET_CONFIRMATION_PATH: ${{ github.event.inputs.market_confirmation_path || '' }}
6571
run: |
6672
set -euo pipefail
6773
AS_OF="${INPUT_AS_OF:-$(date -u +%F)}"
@@ -70,13 +76,18 @@ jobs:
7076
if [ -n "${THEME_MOMENTUM_PATH}" ] && [ -f "../research-signal-context/${THEME_MOMENTUM_PATH}" ]; then
7177
THEME_ARGS=(--theme-momentum "../research-signal-context/${THEME_MOMENTUM_PATH}")
7278
fi
79+
MARKET_ARGS=()
80+
if [ -n "${MARKET_CONFIRMATION_PATH}" ] && [ -f "${MARKET_CONFIRMATION_PATH}" ]; then
81+
MARKET_ARGS=(--market-confirmation "${MARKET_CONFIRMATION_PATH}")
82+
fi
7383
python scripts/build_advisory_report.py \
7484
--as-of "${AS_OF}" \
7585
--cadence weekly \
7686
--political-events "../political-events/${POLITICAL_EVENTS_PATH}" \
7787
--political-watchlist "../political-events/${POLITICAL_WATCHLIST_PATH}" \
7888
--ai-signal "../research-signal-context/${AI_SIGNAL_PATH}" \
7989
"${THEME_ARGS[@]}" \
90+
"${MARKET_ARGS[@]}" \
8091
--output-json "data/output/weekly_advisory_review/advisory_report_${AS_OF}.json" \
8192
--output-md "data/output/weekly_advisory_review/advisory_report_${AS_OF}.md"
8293
- name: Upload model recommendation artifact

README.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Advisor is the final composition layer. Source ownership by horizon is:
4545
- Medium term (`2-12 weeks`): `theme_momentum_snapshot.json` from `ResearchSignalContextPipelines`, now explicitly marked as `medium_horizon_theme_context`.
4646
- Long term (`1-3 years`): `latest_signal.json` / `signal_history/*.json` from `ResearchSignalContextPipelines` as AI shadow context.
4747

48-
Final recommendations are still deterministic Advisor outputs. The signal context repository does not directly produce short-term recommendations or replace the final decision engine.
48+
Final recommendations are still deterministic Advisor outputs. The signal context repository does not directly produce short-term recommendations or replace the final decision engine. Advisor now records separate short/medium/long horizon scores for each final pick; public pages keep the simpler final recommendation layout.
4949

5050
## Boundary
5151

@@ -244,14 +244,13 @@ into executable strategy targets.
244244

245245
## Source Mode
246246

247-
Reports built from `examples/` inputs are marked `source_mode=fixture` and the
248-
individual HTML report displays a fixture warning. Scheduled weekly/monthly and
249-
Pages workflows now default to `data/live/*` inputs from
250-
`PoliticalEventTrackingResearch`, so published reports should be
251-
`source_mode=operator_supplied` and should not show the fixture warning.
247+
Reports built from `examples/` inputs are still marked `source_mode=fixture` in
248+
JSON, but public HTML/RSS/Telegram output no longer displays fixture or source-mode
249+
badges. Scheduled weekly/monthly and Pages workflows default to `data/live/*`
250+
inputs from `PoliticalEventTrackingResearch`, so published reports should be
251+
`source_mode=operator_supplied` in the audit artifact.
252252

253-
`source_mode` remains in the JSON contract for auditability, but the public
254-
index, RSS, and Telegram summaries do not expose that internal field.
253+
`source_mode` remains in the JSON contract for auditability only.
255254

256255
## Regulatory Boundary
257256

@@ -280,16 +279,19 @@ python scripts/build_advisory_report.py \
280279
--political-watchlist examples/political_watchlist.example.csv \
281280
--ai-signal examples/research_signal_context.example.json \
282281
--theme-momentum examples/theme_momentum_snapshot.example.json \
282+
--market-confirmation examples/market_confirmation.example.csv \
283283
--output-json data/output/advisory_report.example.json \
284284
--output-md data/output/advisory_report.example.md
285285
```
286286

287287
Theme momentum is explanation-first context: it highlights strong themes and
288288
creates `theme_first_candidates[]` for JSON/Markdown audit material. The public
289289
HTML, RSS, and Telegram outputs stay focused on final recommendations only, so
290-
theme candidates are not mistaken for a buy list. Theme momentum does not change
291-
recommendation ratings, scores, allocations, or execution policy. Workflows skip
292-
this context when the snapshot file is absent.
290+
theme candidates are not mistaken for a buy list. The base `recommendations[]`
291+
rating still comes from event/watchlist/AI evidence; `final_decisions` can use
292+
medium-horizon theme momentum plus optional market confirmation to rank the final
293+
public list. It never changes allocation or execution policy. Workflows skip this
294+
context when the snapshot file is absent.
293295

294296
Yahoo chart downloads are only a temporary fallback. Do not rely on random free
295297
proxy pools for the stable pipeline; prefer audited price snapshots, cache files,

README.zh-CN.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Advisor 是最终合成层,三个周期的输入分工如下:
3333
- 中线(2-12 周):`ResearchSignalContextPipelines``theme_momentum_snapshot.json`,现在明确标记为 `medium_horizon_theme_context`
3434
- 长线(1-3 年):`ResearchSignalContextPipelines``latest_signal.json` / `signal_history/*.json`,作为 AI shadow 背景。
3535

36-
最终推荐仍由本仓库确定性合成。信号上下文仓库不直接输出短线推荐,也不替代本仓库的最终决策。
36+
最终推荐仍由本仓库确定性合成。信号上下文仓库不直接输出短线推荐,也不替代本仓库的最终决策。本仓库会为最终推荐记录短/中/长线独立评分,但公开页面仍保持简洁,只展示最终列表、背景、理由和风险。
3737

3838
## 当前 MVP
3939

@@ -77,9 +77,9 @@ manifest 会记录 JSON/Markdown 的 SHA256、`as_of`、cadence、来源 artifac
7777

7878
## 来源模式
7979

80-
如果报告输入来自 `examples/`输出会标记 `source_mode=fixture`单篇 HTML 报告会显示 fixture 警告,避免把合成样例误认为真实推荐。周度/月度和 Pages 发布 workflow 现在默认读取 `PoliticalEventTrackingResearch``data/live/*`因此正式发布应为 `source_mode=operator_supplied`,页面不应出现 fixture 警告
80+
如果报告输入来自 `examples/`JSON 里仍会标记 `source_mode=fixture`但公开 HTML/RSS/Telegram 不再显示 fixture 或来源模式标签。周度/月度和 Pages 发布 workflow 默认读取 `PoliticalEventTrackingResearch``data/live/*`因此正式发布的审计 artifact 应为 `source_mode=operator_supplied`
8181

82-
`source_mode` 继续保留在 JSON 契约里用于审计,但公开首页、RSS 和 Telegram 摘要不再展示这个内部字段
82+
`source_mode` 继续保留在 JSON 契约里用于审计,不作为公开页面文案
8383

8484
## 边界
8585

@@ -190,13 +190,14 @@ python scripts/build_advisory_report.py \
190190
--political-watchlist examples/political_watchlist.example.csv \
191191
--ai-signal examples/research_signal_context.example.json \
192192
--theme-momentum examples/theme_momentum_snapshot.example.json \
193+
--market-confirmation examples/market_confirmation.example.csv \
193194
--output-json data/output/advisory_report.example.json \
194195
--output-md data/output/advisory_report.example.md
195196
```
196197

197-
主题动量会生成 `theme_first_candidates[]`但它现在只作为 JSON/Markdown 中的解释和审计材料。公开页面、RSS 和 Telegram 摘要默认只显示最终推荐,避免把候选池误读为买入清单。
198+
主题动量会生成 `theme_first_candidates[]`作为 JSON/Markdown 中的解释和审计材料。公开页面、RSS 和 Telegram 摘要默认只显示最终推荐,避免把候选池误读为买入清单。
198199

199-
候选池仍会保留行业/主题背景、入选原因、事件确认状态和主要风险;但它不直接改变推荐评级、分数、仓位或执行状态
200+
基础 `recommendations[]` 评级仍来自事件、watchlist 和 AI 背景;`final_decisions` 会用中线主题动量和可选市场确认对最终公开列表排序。候选池不改变仓位或执行状态
200201
线上 workflow 如果找不到 `data/output/theme_momentum_snapshot.json`,会自动跳过这个展示区块。
201202

202203
Yahoo chart 下载只作为临时 fallback。不要把随机免费代理 IP 池作为稳定生产方案;它有稳定性、数据污染、封禁、隐私和合规风险。更稳的做法是使用本组织已有价格快照、缓存文件,或可审计的自有代理/数据源。

docs/advisory_contract.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ source_artifacts: object
1515
summary: object
1616
recommendations: list
1717
theme_first_candidates: list, optional
18+
final_decisions: object, optional
1819
policy: object
1920
```
2021

@@ -114,7 +115,8 @@ Allowed strategy styles:
114115
- `mixed_research`
115116

116117
The contract intentionally rejects account-action fields such as `target_weight`,
117-
`target_quantity`, `shares`, `order_type`, `broker`, and `account_id`.
118+
`target_quantity`, `shares`, `order_type`, `broker`, and `account_id`. The same
119+
restriction applies to final picks and theme-first candidates.
118120

119121
## Theme-first Candidate
120122

@@ -144,6 +146,44 @@ reasons
144146
risk_notes
145147
```
146148

149+
## Final Decisions
150+
151+
`final_decisions` is the public recommendation layer. It keeps the simple public
152+
list while preserving audit details in JSON:
153+
154+
```text
155+
recommendations[]
156+
watchlist[]
157+
horizon_buckets.short | medium | long
158+
horizon_rankings.short | medium | long
159+
```
160+
161+
Each final pick may carry:
162+
163+
```text
164+
combined_score
165+
source_score
166+
momentum_score
167+
medium_context_score
168+
long_context_score
169+
horizon_scores.short|medium|long
170+
supporting_context.short|medium|long
171+
selection_trace[]
172+
business_summary
173+
prospect_summary
174+
why_selected[]
175+
risk_summary
176+
```
177+
178+
Scoring intent by horizon:
179+
180+
- short: event/source evidence first, optionally confirmed by recent market behavior;
181+
- medium: theme momentum and individual momentum first, with event/news and optional market confirmation as supporting inputs;
182+
- long: saved AI shadow context and durable theme context first, with event/news as supporting evidence.
183+
184+
These fields are audit metadata. Public HTML/RSS/Telegram renderers still show
185+
only final recommendations, stock background, recommendation reasons, and risks.
186+
147187
## Artifact Manifest
148188

149189
Every CLI-generated JSON report also writes:
@@ -176,7 +216,7 @@ generated_at
176216

177217
`summary.source_mode` is:
178218

179-
- `fixture`: one or more inputs came from `examples/`; the individual HTML report should show a fixture warning and scheduled publication should avoid this mode.
219+
- `fixture`: one or more inputs came from `examples/`; scheduled publication should avoid this mode.
180220
- `operator_supplied`: inputs did not come from fixture paths. This is the expected mode for scheduled public publication.
181221

182-
`summary.data_quality_warnings` carries source-mode warnings for renderers. Public index, RSS, and Telegram summaries intentionally do not display `source_mode`; it remains an audit field in JSON.
222+
`summary.data_quality_warnings` carries source-mode warnings for audit tooling. Public HTML, index, RSS, and Telegram summaries intentionally do not display `source_mode`; it remains an audit field in JSON.

docs/data_factor_roadmap.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ must not directly generate orders, target weights, or account actions.
5858

5959
Owns final non-personalized model recommendations:
6060

61-
- inputs: event CSV, watchlist CSV, saved AI shadow JSON, optional theme momentum;
61+
- inputs: event CSV, watchlist CSV, saved AI shadow JSON, optional theme momentum, optional market confirmation CSV;
6262
- outputs: JSON, Markdown, HTML, RSS, and optional Telegram summary;
6363
- contract blocks orders, target weights, target share quantities, broker routing,
6464
account information, and suitability claims.
@@ -89,7 +89,9 @@ Priority order:
8989
releases, SEC/EDGAR, company IR, policy/procurement sources, alias maps, and
9090
source registry coverage.
9191
3. Add optional market confirmation CSVs while keeping report generation working
92-
when the data is absent.
92+
when the data is absent. The CSV should carry point-in-time returns, relative
93+
returns, abnormal volume, drawdown, and volatility; it should not contain
94+
target weights or trade instructions.
9395
4. Add event review inputs for 1/5/20/60 trading-day follow-up.
9496
5. Add fundamentals/valuation snapshots for risk explanation, not execution.
9597
6. Only then consider read-only references from existing snapshot repositories.

docs/data_factor_roadmap.zh-CN.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353

5454
当前负责最终非个性化模型推荐:
5555

56-
- 输入:事件 CSV、watchlist CSV、AI shadow JSON。
56+
- 输入:事件 CSV、watchlist CSV、AI shadow JSON、可选主题动量、可选市场确认 CSV
5757
- 输出:`model_recommendations` JSON、Markdown、HTML、RSS。
5858
- 推荐字段:推荐等级、推荐层级、适合周期、周期说明、来源可信度、理由、风险、复核清单。
5959
- 合约禁止:目标仓位、目标股数、订单、券商、账户信息。
@@ -178,8 +178,8 @@ Crypto 可作为跨资产风险情绪参考,但暂时不应混入 US equity
178178
- X / Truth Social / 社区内容暂不作为稳定默认源。
179179

180180
3.`QuantAdvisorResearch` 增加 market confirmation 输入,但保持可选:
181-
- `symbol,as_of,close,volume,benchmark_close,sector_etf_close`
182-
- 如果没有市场确认数据,报告继续生成,但降级提示数据缺口
181+
- 当前 CSV 字段为 `symbol,as_of,return_5d,return_20d,return_63d,relative_return_20d,relative_return_63d,volume_zscore,drawdown_63d,volatility_21d`
182+
- 如果没有市场确认数据,报告继续生成;该输入只影响 `final_decisions` 的短/中/长线审计评分,不包含目标仓位或交易指令
183183

184184
4. 增加事件复盘结果输入:
185185
- `event_id,symbol,event_date,window,absolute_return,benchmark_relative_return`

docs/system_design.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,17 @@ The public HTML/RSS/Telegram outputs should stay direct:
9494
and risks;
9595
- hide internal tags such as `source_mode`, mode labels, audience labels, and
9696
repository names;
97-
- keep `theme_first_candidates[]` in JSON/Markdown as explanation and audit
98-
material, not as a public buy list;
97+
- keep `theme_first_candidates[]`, `horizon_scores`, and `selection_trace` in
98+
JSON/Markdown as explanation and audit material, not as public page clutter;
99+
- use theme momentum and optional market confirmation only inside deterministic
100+
`final_decisions` ranking;
99101
- never show orders, target weights, target share quantities, account suitability,
100102
or account-specific allocation advice.
101103

102104
## Fixture vs Live Inputs
103105

104106
Reports built from `examples/` are `source_mode=fixture` and are suitable for
105-
local tests only. Scheduled workflows default to `data/live/*` inputs from
106-
`PoliticalEventTrackingResearch`, so published reports should be
107-
`source_mode=operator_supplied` and should not show fixture warnings.
107+
local tests only. The public renderers no longer display fixture/source-mode
108+
badges. Scheduled workflows default to `data/live/*` inputs from
109+
`PoliticalEventTrackingResearch`, so published audit artifacts should be
110+
`source_mode=operator_supplied`.

0 commit comments

Comments
 (0)