Clarify IPO short-exit strategy horizon

Request:
- Emphasize that the analyst model is for selling allocated IPO shares in T2 grey market or on D1, not for long-term holding.

Changes:
- Add explicit T2/D1 sell discipline to the analyst skill.
- Update ipo_score_v0 targets and holding policy so D1 sell return is primary and T2 is the intended extension when reliable grey-market data exists.
- Clarify that D5/D20/D60 are review labels only, not planned holding targets.
- Update the model report, single-ticker report generator, README, and the 06106 report language to reflect the short-exit horizon.

Verification:
- Rebuilt the model report with the same dataset timestamp and confirmed the analysis dataset did not change.
- Ran py_compile for build_analysis_dataset.py and generate_ipo_report.py.
- Generated a 06106 dry-run report showing T2/D1 exit discipline.
- Ran git diff --check.

Next useful context:
- T2 is still disabled in v0 until archivist approves a reliable grey-market data source; D1 remains the live modeled sell label.
This commit is contained in:
2026-06-15 14:20:56 +00:00
parent bd5a06465d
commit 29ed22e476
8 changed files with 96 additions and 34 deletions
+13 -2
View File
@@ -7,7 +7,7 @@ description: Use for Hong Kong IPO subscription analysis in this project: T0/T1/
## Purpose ## Purpose
Assess Hong Kong IPO subscription candidates using the project's archived facts, scoring rules, prediction cards, and review history. This skill owns judgment: whether to subscribe, wait, avoid, hold after allocation, or revise a rule after outcomes arrive. Assess Hong Kong IPO subscription candidates using the project's archived facts, scoring rules, prediction cards, and review history. This skill owns judgment: whether to subscribe, wait, avoid, sell in grey market or on D1, or revise a rule after outcomes arrive.
Use `archivist` first when source documents, listing facts, allotment results, prices, or database snapshots need to be updated. Use `archivist` first when source documents, listing facts, allotment results, prices, or database snapshots need to be updated.
@@ -22,6 +22,16 @@ Separate the decision stage from later facts:
Do not let later facts leak into earlier prediction cards. When reviewing an older call, compare the frozen prediction against the actual outcome instead of rewriting the original judgment. Do not let later facts leak into earlier prediction cards. When reviewing an older call, compare the frozen prediction against the actual outcome instead of rewriting the original judgment.
## Trading Horizon
The analyst model is a short-exit IPO subscription model, not a long-term holding model.
- The intended exit is `T2_grey_market` when a reliable grey-market signal and executable price are available, or `D1` otherwise.
- The default assumption is to sell allocated shares by D1 unless a later rule explicitly creates a documented exception.
- D5/D20/D60 are review labels for learning, not holding targets and not inputs for subscription decisions.
- Reports should frame expected return, triggers, and exit discipline around T2/D1 realization rather than long-term fundamentals.
- Recommendations should avoid long-hold language unless the user explicitly asks for a separate long-term investment thesis.
## Project Storage Contract ## Project Storage Contract
Use repo-relative paths everywhere: Use repo-relative paths everywhere:
@@ -77,7 +87,7 @@ Generated prediction reports must remain stage-safe:
- T0 reports use only prospectus-stage fields and T0 calibration. - T0 reports use only prospectus-stage fields and T0 calibration.
- T1 reports may add allotment demand fields and T1 calibration. - T1 reports may add allotment demand fields and T1 calibration.
- D1/D5/D20/D60 returns are never shown as prediction inputs; they are reserved for later review cards. - T2/D1 is the intended sell window; D5/D20/D60 returns are never shown as prediction inputs and are reserved for later review cards.
## Output Standards ## Output Standards
@@ -96,6 +106,7 @@ Every prediction card should include:
- key risks - key risks
- triggers for upgrade/downgrade - triggers for upgrade/downgrade
- exit plan - exit plan
- explicit T2/D1 sell discipline
- source paths - source paths
Every review card should include: Every review card should include:
+6 -2
View File
@@ -9,6 +9,8 @@ The project is designed around a feedback loop:
3. Compare predictions with post-listing outcomes. 3. Compare predictions with post-listing outcomes.
4. Improve the scoring rules only from reviewed evidence. 4. Improve the scoring rules only from reviewed evidence.
The investment horizon is deliberately short. The subscription model is built for selling allocated shares in `T2_grey_market` when a reliable executable grey-market signal exists, or on `D1` otherwise. It is not a long-term holding model.
## Goals ## Goals
- Maintain a local, Git-tracked history of Hong Kong IPO data. - Maintain a local, Git-tracked history of Hong Kong IPO data.
@@ -28,6 +30,8 @@ Each IPO is evaluated by stage:
The key discipline is to avoid hindsight leakage. A T0 prediction should only use T0 information, even after the IPO has listed. The key discipline is to avoid hindsight leakage. A T0 prediction should only use T0 information, even after the IPO has listed.
The default exit discipline is T2/D1. D5/D20/D60 outcomes are kept to review whether the model missed later information, not to justify holding IPO allocations beyond the intended sell window.
## Project Skills ## Project Skills
This repository includes project-local Codex skills under `.codex/skills/`. This repository includes project-local Codex skills under `.codex/skills/`.
@@ -153,7 +157,7 @@ Use the analyst model builder to digest archived data into a stage-safe scoring
The v0 model is documented in `rules/ipo_score_v0.yaml`. It writes `data/snapshots/analysis_model_v0_dataset.csv` and `reports/2026-06-15_analysis_model_v0.md`. The v0 model is documented in `rules/ipo_score_v0.yaml`. It writes `data/snapshots/analysis_model_v0_dataset.csv` and `reports/2026-06-15_analysis_model_v0.md`.
The model separates T0 prospectus inputs from T1 allotment inputs. D1/D5/D20/D60 returns are labels for calibration and review, not prediction inputs. The model separates T0 prospectus inputs from T1 allotment inputs. Its live trading target is a T2 or D1 sale. D5/D20/D60 returns are labels for calibration and review, not prediction inputs or planned holding targets.
## Single IPO Markdown Report ## Single IPO Markdown Report
@@ -166,7 +170,7 @@ Use the analyst report generator after the archive and model dataset are current
The generator writes `reports/{date}_{ticker}_{stage}_analysis.md` by default. It auto-selects `T1_allotment` when structured allotment-demand facts exist; otherwise it generates a `T0_prospectus` report. Use `--stdout` for a dry run or `--output` to choose a specific Markdown path. The generator writes `reports/{date}_{ticker}_{stage}_analysis.md` by default. It auto-selects `T1_allotment` when structured allotment-demand facts exist; otherwise it generates a `T0_prospectus` report. Use `--stdout` for a dry run or `--output` to choose a specific Markdown path.
Prediction reports are stage-safe: T0 reports use only prospectus-stage facts and T0 calibration, while T1 reports add allotment demand and T1 calibration. Post-listing D1/D5/D20/D60 performance stays out of prediction reports and is reserved for review cards. Prediction reports are stage-safe: T0 reports use only prospectus-stage facts and T0 calibration, while T1 reports add allotment demand and T1 calibration. Reports should frame the trade as a T2/D1 exit. Post-listing D5/D20/D60 performance stays out of prediction reports and is reserved for review cards.
## Incremental Archive Sync ## Incremental Archive Sync
@@ -9,8 +9,9 @@
- Model dataset as of: `2026-06-15T14:04:34Z` - Model dataset as of: `2026-06-15T14:04:34Z`
- Rule version: `ipo_score_v0` - Rule version: `ipo_score_v0`
- Rule path: `rules/ipo_score_v0.yaml` - Rule path: `rules/ipo_score_v0.yaml`
- Strategy horizon: short IPO subscription trade; intended exit is T2 grey market if reliable, otherwise D1.
- Decision: `strong_watch` - Decision: `strong_watch`
- PM action: Strong watch at T0, still pending T1 demand confirmation. - PM action: Strong watch at T0, still pending T1 demand confirmation for a T2/D1 exit.
- T0 score: `11` - T0 score: `11`
- Score bucket: `t0_gte_8` - Score bucket: `t0_gte_8`
- Calibrated D1 positive probability: 76.4% from 72 historical D1 labels - Calibrated D1 positive probability: 76.4% from 72 historical D1 labels
@@ -35,12 +36,14 @@
| Initial public offer percentage | 5.0% | | Initial public offer percentage | 5.0% |
| Over-allotment shares | 1,574,550 | | Over-allotment shares | 1,574,550 |
## Model Inference ## Short-Exit Model Inference
- D1 positive probability: 76.4% - D1 positive probability: 76.4%
- D1 >= 10% probability: 47.2% - D1 >= 10% probability: 47.2%
- Historical average D1 return for bucket: 28.6% - Historical average D1 return for bucket: 28.6%
- Historical median D1 return for bucket: 9.6% - Historical median D1 return for bucket: 9.6%
- T2 sell return is not modeled until an approved grey-market data source exists.
- D5/D20/D60 outcomes are review labels only, not holding targets.
## Score Breakdown ## Score Breakdown
@@ -64,8 +67,8 @@
- No material negative scoring component. - No material negative scoring component.
- No required report field is blank for this stage. - No required report field is blank for this stage.
- T2 grey-market signal is not used because the project has no approved reproducible source. - T2 grey-market signal is not used yet because the project has no approved reproducible source.
- Post-listing D1/D5/D20/D60 outcomes are labels for model calibration only and are not shown as prediction inputs. - Post-listing D5/D20/D60 outcomes are labels for later review only and are not holding-period targets.
## Triggers ## Triggers
@@ -74,9 +77,10 @@
## Exit Plan ## Exit Plan
- If subscribed and allocated, reassess after allotment and before first trading session using only information available at that stage. - If subscribed and allocated, plan to sell in T2 grey market when reliable executable data is available.
- For T1 reports without approved T2 data, treat first-day liquidity and position sizing conservatively. - If T2 is unavailable or unreliable, use D1 as the default exit window.
- Record actual D1/D5/D20/D60 outcomes later as review labels, not as retroactive prediction inputs. - Do not treat D5/D20/D60 as planned holding periods for this model.
- Record D1/D5/D20/D60 outcomes later as review labels, not as retroactive prediction inputs.
## Source Paths ## Source Paths
+4 -3
View File
@@ -7,9 +7,9 @@
## What This Model Does ## What This Model Does
This is the first analyst model built from the downloaded archive. It creates a repeatable feature table, scores each IPO using stage-safe rules, and calibrates the score buckets against archived D1 outcomes. It is intentionally transparent: the output includes every score component and the archived source paths used for each ticker. This is the first analyst model built from the downloaded archive. It creates a repeatable feature table, scores each IPO using stage-safe rules, and calibrates the score buckets against archived D1 sell outcomes. It is intentionally transparent: the output includes every score component and the archived source paths used for each ticker.
The model does not use grey-market data in v0 because T2 currently has no approved reproducible source. It also does not use post-listing returns as inputs; returns are labels only. The model is built for a short IPO allocation trade: sell in T2 grey market when reliable executable data exists, or sell on D1 otherwise. It does not use grey-market data in v0 because T2 currently has no approved reproducible source. It also does not use post-listing returns as inputs; D1 is the primary sell label, while D5/D20/D60 are review labels only.
## Data Inventory ## Data Inventory
@@ -58,7 +58,8 @@ The high-conviction bucket remains clearly differentiated, but the middle and lo
1. Run `scripts/build_analysis_dataset.py` after archivist updates the database. 1. Run `scripts/build_analysis_dataset.py` after archivist updates the database.
2. Use `t0_score` for prospectus-stage watchlisting. 2. Use `t0_score` for prospectus-stage watchlisting.
3. Use `total_score`, `decision_band`, and `calibrated_d1_positive_rate` for T1-stage subscription cards. 3. Use `total_score`, `decision_band`, and `calibrated_d1_positive_rate` for T1-stage subscription cards.
4. Treat D1/D5/D20/D60 columns as review labels only, never as prediction inputs. 4. Frame live decisions around a T2 or D1 sell, not long-term holding.
5. Treat D5/D20/D60 columns as review labels only, never as prediction inputs or holding targets.
## Known Gaps ## Known Gaps
+19 -6
View File
@@ -7,14 +7,27 @@ purpose: >
Build the first transparent Hong Kong IPO subscription scoring model from Build the first transparent Hong Kong IPO subscription scoring model from
archived project data. The model is a rules-plus-calibration baseline, not a archived project data. The model is a rules-plus-calibration baseline, not a
black-box predictor. It separates T0 prospectus information from T1 allotment black-box predictor. It separates T0 prospectus information from T1 allotment
information so later facts do not leak into earlier decisions. information so later facts do not leak into earlier decisions. It is designed
for short IPO subscription exits, not long-term holding.
targets: targets:
primary: D1 return versus IPO offer price primary: D1 sell return versus IPO offer price
secondary: secondary:
- D5 return versus IPO offer price - T2 grey-market sell return versus IPO offer price, disabled until a reliable source exists
- D20 return versus IPO offer price - D1 strong-return probability
- D60 return versus IPO offer price
holding_policy:
intended_exit_window:
- T2_grey_market when a reliable grey-market price is available
- D1 otherwise
default_assumption: sell allocated shares by D1
non_targets:
- D5 return
- D20 return
- D60 return
note: >
D5/D20/D60 outcomes are retained for review and error attribution only.
They do not define the subscription model's target holding period.
stage_policy: stage_policy:
T0_prospectus: T0_prospectus:
@@ -147,4 +160,4 @@ known_limitations:
- T1 is structurally complete for listed rows, but some individual demand fields remain null when the archived source does not explicitly state them. - T1 is structurally complete for listed rows, but some individual demand fields remain null when the archived source does not explicitly state them.
- Industry labels and issuer fundamentals are not yet sufficiently structured for v0. - Industry labels and issuer fundamentals are not yet sufficiently structured for v0.
- T2 grey-market signal is intentionally disabled until a reliable source exists. - T2 grey-market signal is intentionally disabled until a reliable source exists.
- Current D1 outcomes are from archived market data and should be reviewed for extreme corporate-action or data-vendor anomalies. - Current D1 sell-return outcomes are from archived market data and should be reviewed for extreme corporate-action or data-vendor anomalies.
+24
View File
@@ -1,5 +1,29 @@
# Rule Change Log # Rule Change Log
## 2026-06-15 - Clarify short-exit IPO strategy horizon
Request:
- Emphasize that the analyst model is focused on selling allocated IPO shares in T2 grey market or on D1, not long-term holding.
Change:
- Added an explicit T2/D1 sell horizon to the analyst skill.
- Updated `ipo_score_v0` targets and holding policy to make D1 sell return the primary modeled label and T2 the intended extension when reliable grey-market data exists.
- Clarified that D5/D20/D60 are review labels only, not holding-period targets.
- Updated single-ticker reports and the report generator to show T2/D1 exit discipline.
Rationale:
- The subscription decision should optimize for immediate IPO exit execution, not a long-term equity thesis.
- This preserves stage safety while aligning report language, model targets, and review labels with the actual trading process.
Verification:
- Generated dry-run single-ticker reports after the template update.
- Rebuilt the analysis model report with the same dataset timestamp to refresh language without changing model rows.
- Ran py_compile for the modified scripts and checked Markdown/report text for the new T2/D1 discipline.
## 2026-06-15 - Refresh `ipo_score_v0` after T1 demand backfill ## 2026-06-15 - Refresh `ipo_score_v0` after T1 demand backfill
Request: Request:
+4 -3
View File
@@ -611,9 +611,9 @@ def write_report(
"", "",
"## What This Model Does", "## What This Model Does",
"", "",
"This is the first analyst model built from the downloaded archive. It creates a repeatable feature table, scores each IPO using stage-safe rules, and calibrates the score buckets against archived D1 outcomes. It is intentionally transparent: the output includes every score component and the archived source paths used for each ticker.", "This is the first analyst model built from the downloaded archive. It creates a repeatable feature table, scores each IPO using stage-safe rules, and calibrates the score buckets against archived D1 sell outcomes. It is intentionally transparent: the output includes every score component and the archived source paths used for each ticker.",
"", "",
"The model does not use grey-market data in v0 because T2 currently has no approved reproducible source. It also does not use post-listing returns as inputs; returns are labels only.", "The model is built for a short IPO allocation trade: sell in T2 grey market when reliable executable data exists, or sell on D1 otherwise. It does not use grey-market data in v0 because T2 currently has no approved reproducible source. It also does not use post-listing returns as inputs; D1 is the primary sell label, while D5/D20/D60 are review labels only.",
"", "",
"## Data Inventory", "## Data Inventory",
"", "",
@@ -652,7 +652,8 @@ def write_report(
"1. Run `scripts/build_analysis_dataset.py` after archivist updates the database.", "1. Run `scripts/build_analysis_dataset.py` after archivist updates the database.",
"2. Use `t0_score` for prospectus-stage watchlisting.", "2. Use `t0_score` for prospectus-stage watchlisting.",
"3. Use `total_score`, `decision_band`, and `calibrated_d1_positive_rate` for T1-stage subscription cards.", "3. Use `total_score`, `decision_band`, and `calibrated_d1_positive_rate` for T1-stage subscription cards.",
"4. Treat D1/D5/D20/D60 columns as review labels only, never as prediction inputs.", "4. Frame live decisions around a T2 or D1 sell, not long-term holding.",
"5. Treat D5/D20/D60 columns as review labels only, never as prediction inputs or holding targets.",
"", "",
"## Known Gaps", "## Known Gaps",
"", "",
+15 -11
View File
@@ -216,13 +216,13 @@ def action_for_decision(decision: str) -> str:
actions = { actions = {
"weak_or_avoid": "Avoid at T0 unless later T1 demand changes the setup.", "weak_or_avoid": "Avoid at T0 unless later T1 demand changes the setup.",
"neutral": "Wait for T1 allotment demand before subscribing.", "neutral": "Wait for T1 allotment demand before subscribing.",
"positive_watch": "Watch positively, but wait for T1 confirmation before sizing.", "positive_watch": "Watch positively, but wait for T1 confirmation before sizing for a T2/D1 exit.",
"strong_watch": "Strong watch at T0, still pending T1 demand confirmation.", "strong_watch": "Strong watch at T0, still pending T1 demand confirmation for a T2/D1 exit.",
"avoid": "Avoid subscription.", "avoid": "Avoid subscription.",
"avoid_or_wait": "Avoid or wait; do not size without a stronger catalyst.", "avoid_or_wait": "Avoid or wait; do not size without a stronger catalyst.",
"watch_or_small": "Small subscription only if execution constraints are favorable.", "watch_or_small": "Small subscription only if execution constraints support a T2/D1 exit.",
"selective_subscribe": "Selective subscription with disciplined sizing.", "selective_subscribe": "Selective subscription with disciplined T2/D1 sell sizing.",
"high_conviction_subscribe": "Subscribe, subject to allocation and liquidity discipline.", "high_conviction_subscribe": "Subscribe, subject to allocation, liquidity, and T2/D1 sell discipline.",
} }
return actions[decision] return actions[decision]
@@ -360,6 +360,7 @@ def build_report(record: dict[str, str], rows: list[dict[str, str]], stage: str,
f"- Model dataset as of: `{dataset_as_of}`", f"- Model dataset as of: `{dataset_as_of}`",
f"- Rule version: `{model_version}`", f"- Rule version: `{model_version}`",
f"- Rule path: `{MODEL_RULE_PATH.as_posix()}`", f"- Rule path: `{MODEL_RULE_PATH.as_posix()}`",
"- Strategy horizon: short IPO subscription trade; intended exit is T2 grey market if reliable, otherwise D1.",
f"- Decision: `{decision}`", f"- Decision: `{decision}`",
f"- PM action: {action_for_decision(decision)}", f"- PM action: {action_for_decision(decision)}",
f"- {score_label}: `{score}`", f"- {score_label}: `{score}`",
@@ -370,12 +371,14 @@ def build_report(record: dict[str, str], rows: list[dict[str, str]], stage: str,
"", "",
facts_table(record, stage), facts_table(record, stage),
"", "",
"## Model Inference", "## Short-Exit Model Inference",
"", "",
f"- D1 positive probability: {fmt_pct_rate(metric.d1_positive_rate)}", f"- D1 positive probability: {fmt_pct_rate(metric.d1_positive_rate)}",
f"- D1 >= 10% probability: {fmt_pct_rate(metric.d1_strong_rate)}", f"- D1 >= 10% probability: {fmt_pct_rate(metric.d1_strong_rate)}",
f"- Historical average D1 return for bucket: {fmt_num(metric.average_d1_return_pct, '%')}", f"- Historical average D1 return for bucket: {fmt_num(metric.average_d1_return_pct, '%')}",
f"- Historical median D1 return for bucket: {fmt_num(metric.median_d1_return_pct, '%')}", f"- Historical median D1 return for bucket: {fmt_num(metric.median_d1_return_pct, '%')}",
"- T2 sell return is not modeled until an approved grey-market data source exists.",
"- D5/D20/D60 outcomes are review labels only, not holding targets.",
"", "",
"## Score Breakdown", "## Score Breakdown",
"", "",
@@ -389,8 +392,8 @@ def build_report(record: dict[str, str], rows: list[dict[str, str]], stage: str,
"", "",
*reason_lines(components, positive=False), *reason_lines(components, positive=False),
*missing_field_lines(record, stage), *missing_field_lines(record, stage),
"- T2 grey-market signal is not used because the project has no approved reproducible source.", "- T2 grey-market signal is not used yet because the project has no approved reproducible source.",
"- Post-listing D1/D5/D20/D60 outcomes are labels for model calibration only and are not shown as prediction inputs.", "- Post-listing D5/D20/D60 outcomes are labels for later review only and are not holding-period targets.",
"", "",
"## Triggers", "## Triggers",
"", "",
@@ -399,9 +402,10 @@ def build_report(record: dict[str, str], rows: list[dict[str, str]], stage: str,
"", "",
"## Exit Plan", "## Exit Plan",
"", "",
"- If subscribed and allocated, reassess after allotment and before first trading session using only information available at that stage.", "- If subscribed and allocated, plan to sell in T2 grey market when reliable executable data is available.",
"- For T1 reports without approved T2 data, treat first-day liquidity and position sizing conservatively.", "- If T2 is unavailable or unreliable, use D1 as the default exit window.",
"- Record actual D1/D5/D20/D60 outcomes later as review labels, not as retroactive prediction inputs.", "- Do not treat D5/D20/D60 as planned holding periods for this model.",
"- Record D1/D5/D20/D60 outcomes later as review labels, not as retroactive prediction inputs.",
"", "",
"## Source Paths", "## Source Paths",
"", "",