a
    cOj|                     @   s   d Z ddlZdd ZdS )u  
退市新规风险信号计算。

信号基于 41 号 PG 的 financial_summary、balance_sheet_snapshot 和 daily_kline 计算，
不需要额外外部数据源。

当前覆盖的退市风险指标：
1. 财务类：最近年报净利润 < 0 且营业收入 < 1 亿元
2. 财务类：连续两年净利润 < 0
3. 交易类：连续 20 个交易日收盘价 < 1 元
4. 交易类：连续 20 个交易日总市值 < 3 亿元

注意：
- 审计意见非标、重大违法等规范类退市指标需要额外数据源，当前未覆盖；
- 创业板/科创板部分阈值与主板不同，本项目统一使用主板标准（营收 1 亿、市值 3 亿）。
    Nc                 C   sz  d}t j|| dtid}d}t j|| dtid}g }|js|d tjdd t|d< |dD ]L\}}|dd	}t	|d	kr|d
 dk 
 rdnd}	|||	d qft |}
d}t j|| dtid}d}t j|| dtid}|ddg  }|
js|j|
ddd}nd|d< |j|ddd}|j|ddd}g d}|D ]}|| dt||< qL|dg|  S )u   
    计算退市新规风险信号。

    返回 DataFrame：code, delist_revenue_profit_flag, delist_consecutive_loss_flag,
    delist_price_low_flag, delist_market_value_low_flag
    aV  
    SELECT DISTINCT ON (code)
        code,
        net_profit,
        revenue,
        CASE WHEN net_profit < 0 AND revenue < 100000000.0 THEN 1 ELSE 0 END AS delist_revenue_profit_flag
    FROM financial_summary
    WHERE report_date LIKE '%1231' AND net_profit IS NOT NULL AND revenue IS NOT NULL
    ORDER BY code, report_date DESC
    code)dtypez
    SELECT code, report_date, net_profit
    FROM financial_summary
    WHERE report_date LIKE '%1231' AND net_profit IS NOT NULL
    report_dateN   year   Z
net_profitr      )r   delist_consecutive_loss_flagaR  
    WITH recent AS (
        SELECT code, close,
               ROW_NUMBER() OVER (PARTITION BY code ORDER BY date DESC) AS rn
        FROM daily_kline
    )
    SELECT code,
           CASE WHEN COUNT(*) >= 20 AND bool_and(close < 1.0) THEN 1 ELSE 0 END AS delist_price_low_flag
    FROM recent
    WHERE rn <= 20
    GROUP BY code
    a  
    WITH recent AS (
        SELECT k.code, k.close, s.total_share,
               ROW_NUMBER() OVER (PARTITION BY k.code ORDER BY k.date DESC) AS rn
        FROM daily_kline k
        JOIN stock_info s ON k.code = s.code
        WHERE s.total_share IS NOT NULL AND s.total_share > 0
    )
    SELECT code,
           CASE WHEN COUNT(*) >= 20 AND bool_and(close * total_share < 300000000.0) THEN 1 ELSE 0 END AS delist_market_value_low_flag
    FROM recent
    WHERE rn <= 20
    GROUP BY code
    delist_revenue_profit_flagleft)onhowr	   )r
   r	   delist_price_low_flagdelist_market_value_low_flag)pdread_sqlstremptyastypeintgroupbysort_valuestaillenallappend	DataFramecopymergefillna)pg_connZsql_revenue_profitZdf_rpZsql_consecutive_lossZdf_lossZloss_resultsr   gZrecentflagZdf_loss_flagZ	sql_priceZdf_priceZsql_mvZdf_mvdf	flag_colscol r&   1/Users/admin/quant_ocf/ocf/signals/delist_risk.pyfetch_delist_risk   s4    
"$
r(   )__doc__pandasr   r(   r&   r&   r&   r'   <module>   s   