A-share ST/*ST risk predictionDependencies: This skill must be used in conjunction with tushare skill , and when data is missing, fall back to akshare skill。
Scope of application
- A-shares onlyMain Board / ChiNext / STAR Market. Hong Kong stocks, US stocks, crypto, commodities,Beijing Stock Exchange(
8xxxxx.BJwhose regulatory rules are inconsistent with this skill's thresholds) are all not applicable, and this skill directly refuses. - Only makes forward-looking predictions of "whether it will be ST/*ST in the next fiscal year," and does not predict financial fraud.
- Outputs a dual-axis result:Threshold-triggering risk level + Prediction credibility。
Trigger conditions
User questions in the form of:
- "Analyze the ST risk of 000729.SZ"
- "Will 600xxx be ST at the next annual report"
- "Help me check whether 300xxx has delisting risk"
When receiving such requests, execute according to the "Analysis Process" below; do not proactively enable this skill in other scenarios.
Data acquisition specification (tushare priority → akshare fallback)
Iron rule:
- All financial/fundamental/dividend/audit/ST status data must first call tushare skill, using the interface names specified in the table below,
ts_code、periodto fetch. - When the tushare interface returns empty due to insufficient points, missing token, orfall back to akshare skill the corresponding function.
- akshare fallback data must be explicitly noted in the "Remarks" of the final output:
数据源:akshare(非官方聚合,不保证完整性,建议核对原始公告)。It is not allowed to give a "high credibility" prediction using only akshare— the credibility of red-line items sourced from akshare fallback is mandatorily lowered by one level (high→medium-high, medium-high→medium, medium→low). - Regulatory penalties (E2) use this skill's built-in scripts/fetch_sina_penalties.py, and do not go through tushare/akshare.
Data requirement mapping table
| Data requirement | tushare interface (preferred) | Fetch parameters / time window | akshare fallback | akshare notes |
|---|---|---|---|---|
| Current ST status | stock_st |
Most recent trading day | stock_zh_a_st_em() |
Only returns the current ST/delisting consolidation board list; history is not queryable |
| Historical name changes | namechange |
ts_code full history |
stock_zh_a_new_em()(new stocks only, no name changes) |
akshare has no complete name-change interface; skip if necessary |
| Stock basic information (board determination) | stock_basic |
list_status='L' |
stock_individual_info_em(symbol) |
Field names differ, mapping must be done manually |
| Earnings forecast | forecast |
ts_code + the most recent 2 periods ({Y}0630/{Y}0930/{Y}1231) |
stock_yjyg_em(date='YYYYMMDD') |
Look up by report period date; must first pull the whole market by period and then filter by ts_code |
| Earnings flash report | express |
Same as above, the most recent 2 periods | stock_yjbb_em(date='YYYYMMDD') |
Same as above, whole-market snapshot needs filtering |
| Income statement | income |
ts_code + the most recent 5 periods (annual reports for the past 4 years {Y}1231 + the latest interim/third-quarter report) |
stock_financial_report_sina(stock='sh600000', symbol='利润表') |
Units are inconsistent (ten thousand yuan vs yuan), normalization is required; missing quarterly reports are common |
| Balance Sheet | balancesheet |
Same as above | stock_financial_report_sina(stock='sh600000', symbol='资产负债表') |
Same as above |
| Cash Flow Statement | cashflow |
Same as above | stock_financial_report_sina(stock='sh600000', symbol='现金流量表') |
Same as above |
| Financial Indicators (Net profit excluding non-recurring items, etc.) | fina_indicator |
ts_code + most recent 5 periods |
stock_financial_analysis_indicator(symbol='600000') |
Field names in Chinese; the non-recurring-excluded field is named "Net profit after deducting non-recurring gains and losses" |
| Audit Opinion | fina_audit |
ts_code + most recent 2 annual report periods |
akshare has no corresponding interface | When missing, annotate "Audit opinion data missing" in the output,must not skip——must prompt the user to check the official website |
| Dividends | dividend |
ts_code + most recent 5 years div_proc='实施' |
stock_history_dividend_detail(symbol='600000', indicator='分红') |
Field names in Chinese; need to multiply the "dividend" amount × total share capital to get the total cash dividend |
| Daily quotes (1 yuan delisting warning) | daily |
ts_code + most recent 30 trading days |
stock_zh_a_hist(symbol='600000', period='daily', adjust='') |
Must useunadjustedprice to judge the 1 yuan delisting line |
| Daily indicators (market value) | daily_basic |
ts_code + most recent trading day |
stock_zh_a_spot_em() Full-market snapshot filter 代码=600000 |
Field name is "Total market value" (Unit: yuan, Eastmoney push2 original specification, akshare does not rescale); to convert to 100 million yuan, need /1e8. ⚠️ Do not confuse with tushare daily_basic.total_mv(10 thousand yuan)——this column is the akshare fallback specification |
| Financial report disclosure date | disclosure_date |
ts_code + current year |
akshare has no corresponding interface | When missing, estimate based on experience (4/30 annual report, 8/31 interim report, 10/31 third-quarter report) |
| Regulatory penalties | — | — | Use this skill's built-in sina script | See section E2 |
Unified time window specifications
Handle separately by data type,do not use a single 60-day empirical value to uniformly align:
- Financial statements (income / balancesheet / cashflow / fina_indicator): based on
disclosure_dateactual announcement date; when this interface is unavailable, use the statutory deadlines (annual report 4/30, interim report 8/31, third-quarter report 10/31, first-quarter report 4/30) as fallback. Only from announcement date +1 day can this period be regarded as "latest available". - "Past N years' annual reports": always
period = {Y}1231, do not use calendar year start and end. - Earnings forecast forecast / earnings express express: only take
ann_date ≥ 当前年度 1 月 1 日records; sort byann_datein descending order and take the latest one; forecasts from old periods are invalid for prediction. - dividend: by
(end_date, ann_date, cash_div_tax)tripleforced deduplication——the same dividend in tushare will appear in 3-4 rows (proposal/shareholders' meeting approval/implementation); directly summing will cause triple miscalculation.
Step 1: Current status check (M0)
Determine whether the company is currently under risk warning, and adjust the analysis direction accordingly (do not end with a one-size-fits-all approach):
Call order:
- Prioritize calling
stock_st(tushare skill, 3000 points), query by the current most recent trading day; recordtype/type_name。 - If points are insufficient, fall back to
namechange(free): fetchts_codeall historical name change records, take bystart_datethe latest sorted item; determinenameprefix. - Use
stock_basic.nameas a fallback to confirm the name.
Branch handling:
| Current status | Analysis direction | Whether to continue |
|---|---|---|
| Normal stock | Predict whether it will be ST/*ST in the next fiscal year | Yes(default process) |
| Already ST (ordinary) | Change to predicting: whether it will further become *ST or be delisted; the R1-R4 thresholds need to be stricter (the loss chain threshold is lowered by one year) | Yes |
| Already *ST | Change to predicting: whether it will be forcibly delisted; focus on R1 revenue + R2 net assets + E1 audit opinion | Yes |
| Already in delisting consolidation period / already delisted | End directly | No |
Step 2: Board threshold table (determines the red line)
First use the ts_code prefix to determine the board, then apply the corresponding thresholds. Do not mix them.
| Board | Code identification | Revenue red line | Market cap delisting line | Three-year cumulative dividend threshold |
|---|---|---|---|---|
| Main Board | 60xxxx.SH / 00xxxx.SZ(except those starting with 30 / 688) |
Net profit is negative and revenue < 300 million | Market cap < 500 million | Cumulative < 50 million and < 30% of average annual net profit |
| ChiNext | 30xxxx.SZ |
Net profit is negative and revenue < 100 million | Market cap < 300 million | Cumulative < 30 million and < 30% of average annual net profit (R&D exemption: R&D investment accounts for ≥ 5% of revenue or cumulative over the past three years ≥ 60 million) |
| STAR Market | 688xxx.SH |
Net profit is negative and revenue < 100 million | Market cap < 300 million | Cumulative < 30 million and < 30% of average annual net profit (R&D exemption: R&D investment accounts for ≥ 5% of revenue or cumulative over the past three years ≥ 60 million) |
Step 3: Forecast point-in-time selection
Determine the baseline point in time according to the "latest available evidence" principle, selecting the best from bottom to top:
| Baseline point in time | Data interface | Credibility baseline | Applicable season |
|---|---|---|---|
| Earnings forecast forecast | forecast |
High | Late January / mid-July / late October peak period |
| Earnings express express | express |
High | January-April, before the annual report |
| Third-quarter report | income/balancesheet/cashflow (period=YYYYMMDD, end of September) |
Medium-high | After disclosure in late October until the next year's annual report |
| Interim report | Same as above, end of June period | Medium | After disclosure in late August until the third-quarter report |
| Last year's annual report | Same as above, end of December period | Low | Only used as a baseline reference |
Evidence priority(when there are multiple sources for the same indicator, the higher one overrides the lower one):
forecast / express > 三季报 > 中报 > 去年年报 > 经验外推
When outputtingyou must explicitly state for each forecast item which level of evidence was used. If there is a full-year net profit range given by forecast,it is prohibited to mechanically annualize and override it using the third-quarter report。
Step 4: Four Predictable Red Lines
R1 Revenue + Net Profit Red Line
Objective: Predict full-year revenue, net profit attributable to the parent, andnet profit excluding non-recurring items, and judge using the regulatory standard of "the lower of the two, before and after excluding non-recurring items."
Key to the standard: The original regulatory text is min(n_income, profit_dedt) < 0 and revenue < 板块阈值 —— must also predict net profit excluding non-recurring items, and cannot use only the parent-attributable figure. Looking only at the parent-attributable figure would miss shell companies where "the parent-attributable figure is positive but the ex-non-recurring figure is negative."
Forecasting Methods (choose according to the base time point, at least one):
- forecast direct extraction method: If
forecastprovidesnet_profit_min/net_profit_max, directly take the range as the full-year parent-attributable forecast; for the ex-non-recurring figure, convert using the ex-non-recurring rate from the past two years (the median ofprofit_dedt / n_income); credibility = high. The revenue red line still needs to be extrapolated in combination with the latest financial statements. - express direct extraction method: If
expresshas already been disclosed,revenue/n_incomeis the full-year express report value; the ex-non-recurring figure is likewise converted using the rate from the past two years (express reports generally have no ex-non-recurring field); credibility = high. - Q4 single-quarter recovery method(Q3 report base):
Q1Q3_revenue= operating revenue in the Q3 reportQ4_revenue_est= median of Q4 revenue as a proportion of full-year revenue in the past two years × current annualized estimaterevenue_full_year_est = Q1Q3_revenue + Q4_revenue_estn_income/profit_dedthandled the same way- credibility = medium-high
- H2 scenario extrapolation method(interim report base, mandatory for strongly seasonal companies):
- Take the average of H2 revenue as a proportion of full-year revenue in the past two years, and give pessimistic/base/optimistic scenarios
revenue_full_year_est = H1_revenue / H1_share_avg- credibility = medium
- Year-on-year continuation method(interim report base, usable for weakly seasonal companies):
revenue_full_year_est = H1_revenue × (last_year_full_revenue / last_year_H1_revenue)- credibility = medium
Prohibited: Directly multiplying H1 by 2, directly multiplying Q1-Q3 by 4/3. Any mechanical annualizationmust be downgraded to "low" credibilityand noted in the output.
Threshold breach determination(standard:worst_profit = min(n_income_est, profit_dedt_est)):
- High risk:
worst_profit < 0andrevenue_est < 板块阈值 - Medium risk:
worst_profit < 0but revenue meets the standard; or revenue is close to the threshold (< threshold × 1.2) - Low risk: none of the above are met
Explanation of coefficients: In R1, the 1.2 for medium risk is a warning buffer for the forecast item (the forecast itself has error and requires a narrow buffer); in R4, the 1.5 for high risk is a buffer for the two-year linkage determination (only when both years are losses and revenue is close to the threshold is it a true delisting signal, requiring a wide buffer). The two coefficients are intentionally different,do not incorrectly unify them。
R2 Year-end Net Assets Red Line
Objective: Determine whether year-end parent-attributable shareholders' equity may turn negative.
Method:
- Take the latest period
balancesheet.total_hldr_eqy_exc_min_int(latest interim report or Q3 report), denoted ascurrent_eq。 - Calculate
eq_year_end_est = current_eq + 剩余季度净利润预测 − 当前年度已实施现金分红总额。- remaining-quarter net profit: for the interim report base, use R1's H2 forecast; for the Q3 report base, use R1's Q4 forecast.
- Total dividends already implemented: see R3's deduplication + dimensional formula
cash_div_tax / 10 × total_share。
Threshold breach determination(unified relative threshold, unit: yuan):
- High risk:
current_eq < 0oreq_year_end_est < 0 - Medium risk:
0 ≤ eq_year_end_est < 1e8(100 million yuan) - Low risk:
eq_year_end_est ≥ 1e8
Note: Goodwill impairment and long-term equity investment impairment are recognized in a concentrated manner in Q3/Q4, and a note should indicate "if a large impairment is recognized, net assets may decline further", butimpairment must not be recognized without the basis of an announcement。
R3 Dividend Compliance Outlook
Objective: Determine whether "cumulative cash dividends over the past three years < 30% of average annual net profit and < the cumulative threshold for the board" will both be met at the next annual assessment (regulatory text: the two conditionssimultaneouslytrigger a warning only if met).
Single Dividend Amount Dimensional Formula(This formula must be used, do not use cash_div_tax × base_share):
cash_dividend_total = cash_div_tax / 10 × total_share
where cash_div_tax comes from dividend(cash amount per 10 shares, tax included),total_share comes from daily_basic.total_share(latest total share capital, in ten thousand shares → needs ×1e4 to convert to shares) or stock_basic。tushare dividend.base_share field is None for many companies,direct use is prohibited。
Method:
- Use
dividendto pull thets_coderecords for the past five years, by the(end_date, ann_date, cash_div_tax)triplemandatory deduplication—tushare returns 3-4 rows for the same dividend: "proposal/shareholders' meeting approved/implemented". - Conversion coefficient (by
div_procstatus):实施=1.0/股东大会通过=0.7/预案=0.5; for the sameend_datetake the row with the highest coefficient. - Use
incometo pull the full-year net profit attributable to the parent for the past three years (period=YYYY1231). - Calculation assessment window =
[当前年-2, 当前年], i.e. "dividends already implemented in the past two years + estimated dividends for the current year". - Estimated dividends for the current year:
- If
dividenda current-year record has been found: include it according to the conversion coefficient above. - Otherwise use "average dividend payout ratio over the past two years" × R1 forecast full-year net profit (base scenario) for estimation, and lower the confidence by one level.
- If
- Compare with average annual net profit:
- Condition A:
cumulative_div_3y < 30% × avg_n_income_3y - Condition B:
cumulative_div_3y < 板块累计阈值
- Condition A:
- For ChiNext / STAR Market, if the R&D investment ratio meets the exemption conditions, note "meeting R&D requirements may exempt".
Threshold Breach Determination:
- High risk: A and B are met simultaneously and net profit was positive in at least one of the past three years (i.e. "failed to meet the standard in a profitable year"—regulation uses profitable years as the benchmark).
- Medium risk: only one condition is met; or dividends for the current year have not yet been announced.
- Low risk: cumulative dividends have steadily met the standard; or cumulative net profit over the past three years ≤ 0 (no basis for dividends, linked per R1).
Prohibited: when dividends for the current year have not yet been announced, do not assert "certainly will not meet the standard"; only give a probability.
R4 Consecutive Losses / Non-Recurring Loss Chain
Objective: Determine whether it will trigger "the lower of net profit before and after non-recurring items is negative for two consecutive years" → *ST.
Key Scope: the regulatory text istwo consecutive years(not three years), and the statutory condition for *ST financial mandatory delisting is "loss + revenue < board threshold" must be met simultaneously—looking only at the loss chain will systematically overestimate risk (empirical example: Wingtech Technology lost money in 2024 but had revenue of over one hundred billion, so it will not be *ST).
Method:
- Take the
n_income、fina_indicator.profit_dedtfrom the last full annual report, recordworst_last = min(n_income, profit_dedt)、revenue_last。 - Using the
worst_profit_estandrevenue_full_year_estalready predicted by R1 (baseline scenario), corresponding to the current year. - Check the two-year
worst < 0combination,and overlay the revenue linkage condition。
Threshold-line determination(dual condition, loss chain + revenue linkage):
- High risk:
worst_last < 0andworst_profit_est < 0,and(R1 current year hits high risk orrevenue_full_year_est < 板块阈值 × 1.5) — i.e., losses in both years + revenue in the delisting risk range. - Medium risk: (a) losses in two consecutive years but revenue far above the threshold (≥ threshold × 1.5) — the loss chain holds but financial-category *ST would not be triggered, though ST may still result from deteriorating operations; (b) only
worst_profit_est < 0(current year forecast loss, profitable last year, no consecutive losses formed); (c) onlyworst_last < 0but the current year is forecast positive and close to 0 (< board threshold × 0.5). - Low risk:
worst_last ≥ 0andworst_profit_est ≥ 0。
Why overlay revenue: The statutory condition for A-share *ST financial-category mandatory delisting is "net profit before and after non-recurring items, whichever is lower < 0 and revenue < board threshold", both of which must be satisfied simultaneously. A company with only losses that does not reach the revenue threshold (such as a large cyclical stock with short-term losses) would be regarded by the market and regulators as a "cyclical loss" rather than "going-concern doubt".
Step 5: Three factual/critical evidence surfaces (not involved in forecast credibility)
E1 Audit opinion (fact)
Call fina_audit, take the most recent 2 complete annual reports:
- High risk: the latest annual report
audit_resultcontains a "disclaimer of opinion" or "adverse opinion";or a single occurrence of a "qualified opinion"(empirically supported: recent SSE regulatory-category *ST cases mostly triggered by a single qualified opinion). - Medium risk: the same negative conclusion for two consecutive years but corrected in the current period; or an unqualified opinion with an emphasis of matter paragraph involving going concern.
- Low risk: standard unqualified opinion.
Predicting the next audit opinion is prohibited— auditing is an ex-post act.
Manual verification path when akshare fallback is missing(fina_audit has no akshare substitute interface, a clickable URL must be given):
| Source | URL template | Query method |
|---|---|---|
| CNINFO (original announcements, authoritative) | http://www.cninfo.com.cn/new/disclosure/stock?orgId=&stockCode={code6} |
After entering the page, filter "Periodic Reports → Annual Reports", download the PDF and check 审计意见类型 |
| CNINFO advanced search (by keyword) | http://www.cninfo.com.cn/new/fulltextSearch/full?searchkey={code6}+审计报告&sdate=&edate=&isfulltext=false&sortName=time&sortType=desc |
Directly hits announcements with "audit report" in the title |
| SSE information disclosure | http://www.sse.com.cn/disclosure/listedinfo/regular/?stockCode={code6} |
Limited to 60xxxx / 688xxx |
| SZSE information disclosure | http://www.szse.cn/disclosure/listed/notice/index.html?stock={code6} |
Limited to 00xxxx / 30xxxx |
Where {code6} is the 6-digit stock code (remove the .SH / .SZ suffix).Mandatory requirement: when fina_audit returns empty, the "Remarks" of the output report must list one of the two CNINFO URLs above, to facilitate the user's manual verification; it must not merely write "audit opinion data missing".
E2 Regulatory penalties (fact, dual window: previous fiscal year + past 12 months)
This is the key evidence surface of this skill.
Window rules (dual-window strategy, single fetch sliced in memory):
- Window A —
[当前年-1 的 1 月 1 日, 当前年-1 的 12 月 31 日](the last complete fiscal year): used for reason_normalized single-record rating. - Window B —
[分析日 - 365 天, 分析日](trailing 12 months): used forfrequency enhancement rule。 - If the analysis point in time is already after May and the latest annual report has been fully disclosed, Window A may be changed to
[最新年报 end_date - 365 天, 最新年报 end_date]。
Call method (recommended: single full fetch + in-memory slicing):
python agent/src/skills/ashare-pre-st-filter/scripts/fetch_sina_penalties.py \
--ts-code 000729.SZ --stock-name 燕京啤酒 --no-filter
After the full page records are returned, the LLM slices two copies in memory by Window A and Window B. You can also use --start-date / --end-date to fetch separately, but within the same analysisavoid issuing two HTTP requests。
Output a JSON list, fields:ann_date / event_type / title / reason / reason_normalized / content / issuer / issuer_normalized / subject_normalized / target_relevance / e2_countable / source_url。
Ironclad relevance rule: when calling the script, you must pass --stock-name {股票简称}(from stock_basic.name), and if necessary use --alias to supplement former names/abbreviations. Sina pages may include records of "the target stock appearing in some person's securities account trading list"; such records only indicate that the target stock was traded,not a penalty on the listed company/shareholder/director, supervisor, or senior manager. The script will mark them as target_relevance=security_mention_only and e2_countable=false, and E2 frequency statistics must exclude them.
subject_normalized field(used for frequency weight conversion):
company— the penalized subject is the company itself (default; classified here when no personal-identity keyword is present)shareholder— controlling shareholder / actual controller / shareholder holding 5% or moreofficer— director / supervisor / senior manager (chairman, general manager, board secretary, CFO, etc.)
Risk level determination:
Hit reason_normalized |
Level contribution |
|---|---|
| Financial fraud / false statements / information disclosure violations | High (strong warning) |
| Illegal guarantees / misappropriation of funds | High (directly touches the ST red line) |
| Insider trading / market manipulation / illegal share reduction | Medium |
| Other/unknown | Low (background only) |
Frequency enhancement rule (governance quality warning, Window B):
Regulatory letters themselves reflect corporate governance and internal control quality, and dense occurrences are a strong leading indicator of "compliance-category ST" (qualified opinion, non-standard internal control, major information disclosure defects). However,different penalized subjects have different governance implications: shareholder share-reduction violations and short-swing trading by directors, supervisors, and senior managers reflect personal compliance issues and should not be equated with "corporate governance disorder".
Subject-weighted count(after conversion by subject_normalized , then look up the table):
加权条数 = Σ 单条权重
其中:subject_normalized == "company" → 权重 1.0
subject_normalized == "officer" → 权重 0.5
subject_normalized == "shareholder" → 权重 0.5
| Regulatory letters/inquiries/warning letters in the past 12 monthsWeighted count | Frequency level |
|---|---|
| ≥ 5.0 | Extremely high |
| 3.0 - 4.5 | High |
| 2.0 - 2.5 | Medium |
| < 2.0 | Low |
Determination source: only when e2_countable=true , the event_type ∈ {警示, 问讯, 监管关注, 监管函} or issuer_normalized ∈ {上交所, 深交所, 北交所, 证监会, 地方证监局} output by the script is counted toward the count; the count is converted by subject_normalized before looking up the table.target_relevance=security_mention_only records must be excluded.
Output requirement: both the "raw count" and the "weighted count" must be given, so the user can review the conversion logic. For example:窗口 B 命中 6 条(公司 2 条 + 股东 3 条 + 高管 1 条 → 加权 4.0 条),频次等级:高。
E2 composite level synthesis rule(Resolving the issue of a single rating overlapping with frequency):
E2_单条等级 = 窗口 A 内所有 reason_normalized 单条等级的最大值
E2_频次等级 = 窗口 B 频次表查得
E2_综合等级 = max(E2_单条等级, E2_频次等级)
Overlay bonus: if both E2_频次等级 ≥ 高 and E2_单条等级 ≥ 高are satisfied (including financial fraud/false statements/illegal guarantees/misappropriation of funds) → directly elevated to extremely high。
Why this rule is needed: purely quantitative red lines can only capture "financial ST" and cannot provide early warning of "regulatory ST". The frequency rule incorporates "governance disorder" as a forward-looking signal.
Note: penalty evidenceonly raises the risk level and does not affect forecast credibility—this is factual evidence, not a forecast item.
E3 Trading-Related Critical Early Warning (Fact)
Call daily(last 30 trading days,must adjust='' be unadjusted) and daily_basic(latest day, unit: ten thousand yuan):
1 yuan delisting warning(using daily.close,unadjusted):
- High risk: in the last 20 trading days, the number of days with a closing price < 1 yuan is ≥ 10
- Medium risk: ≥ 1 but < 10
- Low risk: 0 days
Market value delisting warning(using daily_basic.total_mv, unit ten thousand yuan → ×1e4 converted to yuan → /1e8 converted to hundred million yuan):
| Board | High-risk threshold | Medium-risk threshold |
|---|---|---|
| Main board | < 500 million | < 5 × 1.5 = 750 million |
| ChiNext / STAR Market | < 300 million | < 3 × 1.5 = 450 million |
Step 6: Dual-Axis Output Template
Each analysis must be output according to the template below.Both dimensions must be given: risk level + credibility。
## ST 风险预测:{name}({ts_code})
### 当前状态
- 是否已 ST/*ST:{是 / 否}
- 板块:{主板 / 创业板 / 科创板}
- 基准预测时点:{forecast / express / 三季报(period) / 中报(period)}
### 量化红线预测(双轴)
| 红线 | 基准情景结论 | 风险等级 | 可信度 | 主要证据 |
|------|-------------|---------|-------|---------|
| R1 营收+净利润 | 全年营收预测 X 亿,净利润 Y 亿 | 高/中/低 | 高/中高/中/低 | forecast/三季报/... |
| R2 年末净资产 | 当前 X 亿,年末预计 Y 亿 | 高/中/低 | 高/中高/中/低 | balancesheet + R1 |
| R3 分红达标 | 三年累计预计 X,对应阈值 Y | 高/中/低 | 中/低 | dividend + income |
| R4 连续亏损链 | 过去两年 + 当前年是否均亏 | 高/中/低 | 同 R1 | income + fina_indicator |
### 事实证据面(不参与预测可信度)
| 证据 | 结论 | 影响 |
|------|------|------|
| E1 审计意见 | 最新结论 | 抬升/无影响 |
| E2 监管处罚(窗口 A 上一财年 / 窗口 B 过去 12 个月) | A 命中 Na 条(最高单条等级:…),B 原始 Nb_raw 条 / 加权 Nb_w 条(公司 a + 股东 b + 高管 c → a×1.0+b×0.5+c×0.5),频次等级:…,E2_综合等级:… | 抬升/无影响 |
| E3 交易类临界 | 收盘价/市值情况 | 抬升/无影响 |
### 综合结论
- **下一财年被 ST/*ST 风险等级**:{极高 / 高 / 中 / 低}
- **预测综合可信度**:{高 / 中高 / 中 / 低}
- **关键风险点**(最多 3 条):...
- **缓解信号**(如有):...
### 备注
- 数据时点:{所用最新报告期}
- 已忽略的不可预测项:财务造假、未来审计意见
- 季节性提醒(如适用):...
Prohibitions
- Do not predict financial fraud (unknowable in advance).
- Do not predict the next audit opinion (it is issued only after the fact).
- Do not arbitrarily recognize impairment without announcement-based grounds.
- Do not mix board thresholds (the revenue/market value/dividend thresholds differ between the main board and ChiNext/STAR Market).
- Do not disguise mechanical annualization (H1×2, Q1Q3×4/3) as "high credibility".
- Do not enable this skill on Hong Kong stocks / U.S. stocks / crypto / Beijing Stock Exchange(
8xxxxx.BJ, where regulatory rules are inconsistent with this skill's thresholds). - Do not count "violation penalty records" toward forecast credibility (they only raise the risk level).
- Do not use
cash_div_tax × base_sharein the R3 dividend calculation (base_share is often None, which would yield 0); you must usecash_div_tax / 10 × total_share, and deduplicate by the(end_date, ann_date, cash_div_tax)triple. - Do not use only
n_incomein R1 threshold determination; you must usemin(n_income, profit_dedt), otherwise shell companies will be missed. - Do not write R4 as "three consecutive years"; the regulatory text says "two consecutive years".And R4 high risk must be overlaid with the revenue linkage condition—"two consecutive years of losses" alone does not constitute *ST financial mandatory delisting; it must also satisfy "revenue < board threshold × 1.5" or R1 has already hit high risk, otherwise only medium risk can be given.
- Do not count E2 regulatory penalties "by entity in a one-size-fits-all way"—you must use
subject_normalizedweighting (company ×1.0 / shareholder ×0.5 / directors, supervisors, and senior management ×0.5), otherwise companies with shareholder turmoil will be systematically overestimated.
List of dependent tushare interfaces
| Purpose | Interface | Remarks |
|---|---|---|
| Current ST status | stock_st |
3000 points; fallback when unavailable namechange |
| Historical name changes | namechange |
Free |
| Board/sector determination | stock_basic |
Free |
| Income statement | income |
2000 points, fetched for multiple periods |
| Balance sheet | balancesheet |
2000 points |
| Financial indicators (non-recurring, etc.) | fina_indicator |
2000 points |
| Earnings forecast | forecast |
2000 points |
| Earnings flash report | express |
2000 points |
| Audit opinion | fina_audit |
500 points |
| Dividends | dividend |
2000 points |
| Daily bars | daily |
Free |
| Daily indicators (market cap) | daily_basic |
Free |
| Financial report disclosure date | disclosure_date |
500 points |
Bundled script
- scripts/fetch_sina_penalties.py — Scrapes the Sina Finance vGP_GetOutOfLine penalty page, implemented with stdlib, no external dependencies; reason / issuer are standardized.
Usage:
# 单次拉全量(推荐:本 skill 双窗口策略由 LLM 在内存中切片)
python agent/src/skills/ashare-pre-st-filter/scripts/fetch_sina_penalties.py \
--ts-code 000729.SZ --stock-name 燕京啤酒 --no-filter
# 或指定单一窗口
python agent/src/skills/ashare-pre-st-filter/scripts/fetch_sina_penalties.py \
--ts-code 000729.SZ --stock-name 燕京啤酒 --start-date 2025-01-01 --end-date 2025-12-31
Under normal conditions, outputs JSON to stdout; on network/parsing failure or invalid parameters, returns {"source": "unavailable", "error": "..."} to stdout and exits with a non-zero exit code (without throwing a traceback), making it easy for the LLM to handle as a fallback.
End-to-end call pseudocode
Used to reduce LLM call order drift, organized according to the following skeleton:
import os, json, subprocess
import tushare as ts
ts_code = '000729.SZ'
pro = ts.pro_api(os.environ['TUSHARE_TOKEN'])
# === Step 0: M0 当前状态 ===
basic = pro.stock_basic(ts_code=ts_code, fields='ts_code,name,market').to_dict('records')[0]
st_hit = pro.stock_st() # 失败则回退 namechange
name_chg = pro.namechange(ts_code=ts_code).sort_values('start_date', ascending=False)
# === Step 1: 板块判断 ===
board = '科创板' if ts_code.startswith('688') else '创业板' if ts_code.startswith('30') else '主板'
threshold_revenue = 1e8 if board != '主板' else 3e8
threshold_mv = 3e8 if board != '主板' else 5e8
# === Step 2: 拉财务(最新 5 期 + 过去 4 年年报) ===
income = pro.income(ts_code=ts_code).sort_values('end_date', ascending=False).drop_duplicates('end_date')
balance = pro.balancesheet(ts_code=ts_code).sort_values('end_date', ascending=False).drop_duplicates('end_date')
fina_ind = pro.fina_indicator(ts_code=ts_code).sort_values('end_date', ascending=False).drop_duplicates('end_date')
forecast = pro.forecast(ts_code=ts_code).sort_values('ann_date', ascending=False)
express = pro.express(ts_code=ts_code).sort_values('ann_date', ascending=False)
# === Step 3: R1-R4 计算(按各章节口径,注意 min(n_income, profit_dedt)) ===
# ... 略
# === Step 4: E1 审计 ===
audit = pro.fina_audit(ts_code=ts_code).sort_values('end_date', ascending=False).head(2)
# === Step 5: E2 监管处罚(双窗口 + 主体加权)===
from datetime import date, timedelta
result = subprocess.run([
'python', 'agent/src/skills/ashare-pre-st-filter/scripts/fetch_sina_penalties.py',
'--ts-code', ts_code, '--stock-name', basic['name'], '--no-filter',
], capture_output=True, text=True)
penalties = json.loads(result.stdout).get('records', [])
# 在内存中切双窗口(避免两次 HTTP)
this_year = date.today().year
win_a_start, win_a_end = f'{this_year-1}-01-01', f'{this_year-1}-12-31'
win_b_start = (date.today() - timedelta(days=365)).isoformat()
win_b_end = date.today().isoformat()
def _in(rec, s, e):
d = rec.get('ann_date') or ''
return bool(d) and s <= d <= e
win_a = [r for r in penalties if _in(r, win_a_start, win_a_end)]
win_b = [r for r in penalties if _in(r, win_b_start, win_b_end)]
# 窗口 A:单条等级取最高
REASON_LEVEL = {
'财务造假': 3, '虚假陈述': 3, '信息披露违规': 3,
'违规担保': 3, '占用资金': 3,
'内幕交易': 2, '市场操纵': 2, '违规减持': 2,
}
levels_a = [REASON_LEVEL.get(r['reason_normalized'], 1) for r in win_a]
e2_single = max(levels_a) if levels_a else 0
# 窗口 B:原始条数 + 主体加权条数
FREQ_EVENT_TYPES = {'警示', '问讯', '监管关注', '监管函', '警示函'}
FREQ_ISSUERS = {'上交所', '深交所', '北交所', '证监会', '地方证监局'}
SUBJECT_WEIGHT = {'company': 1.0, 'shareholder': 0.5, 'officer': 0.5}
freq_pool = [r for r in win_b
if r.get('e2_countable', True)
and (r['event_type'] in FREQ_EVENT_TYPES
or r['issuer_normalized'] in FREQ_ISSUERS)]
nb_raw = len(freq_pool)
nb_weighted = round(sum(SUBJECT_WEIGHT.get(r['subject_normalized'], 1.0)
for r in freq_pool), 1)
# 频次等级(基于加权条数)
if nb_weighted >= 5.0: e2_freq = 4 # 极高
elif nb_weighted >= 3.0: e2_freq = 3 # 高
elif nb_weighted >= 2.0: e2_freq = 2 # 中
else: e2_freq = 1 # 低
e2_overall = max(e2_single, e2_freq)
# 叠加加成:频次高 + 单条高同时命中 → 直升极高
if e2_freq >= 3 and e2_single >= 3:
e2_overall = 4
# === Step 6: E3 交易临界 ===
daily = pro.daily(ts_code=ts_code).sort_values('trade_date', ascending=False).head(30)
daily_basic = pro.daily_basic(ts_code=ts_code).sort_values('trade_date', ascending=False).head(1).iloc[0]
total_mv_yi = daily_basic['total_mv'] * 1e4 / 1e8 # 万元 → 亿元
# === Step 7: 双轴合成输出 ===
# 风险等级 = max(R1, R2, R3, R4, E1, E2_综合, E3)
# 可信度 = 加权平均(R1, R2, R3, R4 各项可信度),akshare 兜底项降一档