BigQuant使用文档

106a-新国九条改良版微盘策略

由bq5bun29创建,最终由small_q 被浏览 103 用户

策略介绍

本策略是根据新国九条进行改良的新版微盘策略从而更好筛选需要的股票。

自从2024年新国九条出来后,小市值策略逐渐失效,部分小票退市概率变大,我们先看看国九条中关于股票ST的内容:

可能影响股票被ST或退市的关键因子,这些因子可以作为投资者避免潜在风险的参考:

1、分红情况:如果公司最近一个会计年度净利润为正值,但三年分红低于年均净利润的30%,且累计分红金额低于5,000万元,可能会被实施ST。

2、财务造假:如果公司一年虚假记载金额达到2亿元以上,占比超过30%,或连续两年虚假记载金额达到3亿元以上,占比超过20%,或连续三年及以上年度存在虚假记载,可能会被退市。

3、资金占用:如果公司存在资金占用问题,且余额达到最近一期经审计净资产绝对值30%以上,或金额超过2亿元以上,在规定期限内未改正,可能会被退市。

4、内部控制问题:如果公司连续两年内部控制审计报告为无法表示意见或否定意见,或未按规定披露内部控制审计报告,且第三年仍为非无保留意见,可能会被退市。

5、控制权争夺:如果公司存在控制权无序争夺,导致投资者无法获取公司有效信息,可能会被退市。

6、财务状况:利润总额、净利润或扣除非经常性损益后的净利润为负值,且营业收入低于3亿元,可能会被退市。

7、市值:如果公司连续20个交易日的每日股票收盘总市值均低于5亿元,可能会被退市。

8、信息披露问题:如果公司存在信息披露或规范运作的重大缺陷,可能会被退市。

9、监管要求:如果公司未在法定期限内披露年度报告或半年度报告,或董事无法保证报告的真实性、准确性、完整性,可能会被ST或退市。

10、违法违规行为:如果公司存在欺诈发行、重大信息披露违法或其他严重损害证券市场秩序的行为,可能会被退市。


因此本例,尽量规避可能退市的小票,具体如何规避呢,就是按照上述内容进行过滤剔除。本例要用到分红数据、股本数据、净利润数据,我们分别是从cn_stock_dividend、cn_stock_capital、cn_stock_financial_ly_shif

原始表读取数据,然后结合外部合并、缺失值向前填充,最终计算出一些复杂的财务因子,比如最近三年分红总额、近三年年均净利润,小伙伴可以基于此熟悉dai的方法,然后按需加工更加复杂的财务因子。

\

画布板块

在原有画布基础上增加了DAI数据连接、DAI数据过滤、Python函数板块。m7模版是python代码模块,其内容依赖上游纯代码单元的运行。

输入特征板块

在表达式特征中增加了net_profit_deducted_lf

在表达式过滤条件中,我们在BigQuant平台内数据平台中的数据表cn_stock_prefactors与cn_stock_factors_financial_indicators提取字段进行过滤条件的编写。

  1. 按上市天数进行过滤,将次新股剔除:
    • list_days > 270:表示上市天数大于270天,这个条件用来过滤掉上市时间较短的次新股。
  2. 剔除ST股:
    • st_status = 0:表示剔除特别处理的股票,通常是财务或其他方面存在问题的股票。、
  3. 非停牌股:
    • suspended = 0:表示只选择没有停牌的股票。
  4. 不属于北交所:
    • list_sector < 4:表示只选择不属于北京证券交易所的股票。
  5. 过滤市值连续20个交易日低于5亿元的公司:
    • m_min(total_market_cap,20) > 500000000:表示在过去20个交易日中,市值的最小值必须大于5亿元。m_min为时间序列函数, 求d 天内的最小值可以在BigQuant数据平台的函数列表中找到。
  6. 过滤财务状况不达标的公司(利润总额、净利润或扣非净利润为负值,且营业收入低于3亿元):
    • ((total_profit_ttm < 0 OR net_profit_deducted_ttm < 0 OR net_profit_to_parent_deducted_ttm < 0) AND operating_revenue_yoy_ttm < 300000000):表示总利润、扣非净利润或归母扣非净利润为负值,并且营业收入低于3亿元的公司将被过滤掉。total_profit_ttm < 0该表达式表示总利润(TTM,即过去12个月的总利润)小于0,表示公司在过去一年中总体上是亏损的。 net_profit_deducted_ttm < 0扣非净利润(TTM,即过去12个月的扣非净利润)小于0,表示公司在过去一年中扣除非经常性损益后的净利润是负值,即主营业务亏损。net_profit_to_parent_deducted_ttm < 0表示母扣非净利润(TTM,即过去12个月的归属于母公司股东的扣非净利润)小于0,表示母公司在过去一年中扣除非经常性损益后的净利润是负值,即母公司主营业务亏损。operating_revenue_yoy_ttm < 300000000`营业收入(TTM,即过去12个月的营业收入)小于3亿元,表示公司在过去一年中的总收入较低。

Python函数板块

这段SQL代码通过窗口函数last获取每个 instrument分组中 avg_net_profit和dividend_amount的最新非空值,并新增一个名为 y3_dividend_amount的列,该列使用 m_sum函数计算 dividend_amount在750天窗口内的滑动求和,过滤日期范围在 2010-01-01至2024-05-31之间的记录,最终将结果写入新的数据源并返回。这里,merge_df是代码加工好的数据。

DAI数据分析板块

设置关联列和连接方式。

DAI数据过滤板块

这个布尔条件 (net_profit_deducted_lf > 0) & (y3_dividend_amount > avg_net_profit * 0.3) & (y3_dividend_amount > 5000) 用于筛选那些满足以下三个条件的记录:

  1. 净利润(net_profit_deducted_lf)为正,即公司是盈利的。
  2. 750天窗口内的滑动股息总额(y3_dividend_amount)大于平均净利润的30%,表明股息支付相对较高。
  3. 750天窗口内的滑动股息总额(y3_dividend_amount)大于5000,确保股息支付达到一定的绝对值水平。

这些条件共同作用,筛选出盈利且股息支付水平较高的公司记录。

代码板块

本例也使用了一些cell进行纯代码的运行,旨在帮助用户更好理解dai的使用。

加工分红数据

import dai

raw_sql = """ with 
t1 as (SELECT  total_shares, publish_date as date, instrument 
FROM  cn_stock_capital where change_date = date), 

t2 as (SELECT date,instrument,cash_before_tax from cn_stock_dividend),

t3 as (select date, instrument, t2.cash_before_tax as cash_before_tax, t1.total_shares as  total_shares  from t2 
full join t1 using(date, instrument)
order by date, instrument),

t4 as (select date, instrument,  
    last(columns([cash_before_tax, total_shares]) ignore nulls) 
        over (partition by instrument order by date rows between 
        unbounded preceding and current row) as 'columns(*)',
FROM t3)

select date, instrument, cash_before_tax * total_shares as dividend_amount  
from t4 order by date ,instrument

"""
devidend_df = dai.query(raw_sql, filters={"date": ["2010-01-01", "2024-05-31"],  "publish_date": ["2010-01-01", "2024-02-01"]}).df()

这段 SQL 代码通过一系列的公共表表达式(CTE)将股票的总股数和分红数据进行全连接(FULL JOIN),并使用窗口函数(如 last)填充缺失值。窗口函数在每个股票代码(instrument)分组内,按日期(date)排序,获取当前行及之前行的最后一个非空值。最终计算每个股票在特定日期的分红金额(dividend_amount),并按日期和股票代码排序输出结果。


加工净利润均值数据

sql = """
select date ,
instrument, 
avg(net_profit_ly) as avg_net_profit from  cn_stock_financial_ly_shift where  shift <=2
group by instrument, date
order by date, instrument
""" 
net_profit_df=dai.query(sql,filters={"date": ["2010-01-01", "2024-05-31"]}).df()

这段代码的主要目的是从 cn_stock_financial_ly_shift 表中提取和计算每个证券在每个日期的净利润平均值,并对结果进行过滤和排序。最终结果将存储在 net_profit_df 这个 Pandas DataFrame 中。


数据合并

sql = """ 
select date, instrument , avg_net_profit,  devidend_df.dividend_amount from  net_profit_df 
full join devidend_df using(date, instrument)
full join cn_stock_prefactors using(date, instrument)
order by date, instrument
"""
merge_df = dai.query(sql, filters={"date": ["2010-01-01", "2024-05-31"]}).df()

这段代码的目的是从 net_profit_df、devidend_df 和 cn_stock_prefactors 表中提取数据,并基于 date 和 instrument 字段进行合并。最终结果将存储在 merge_df 这个 Pandas DataFrame 中,并按日期和证券代码的顺序进行。


数据填充为日频数据

这个代码注释了,无需运行,因为m7函数模块就是这个代码逻辑。

# sql = """  
# with t0 as (
# select date, instrument,  
#     last(columns([avg_net_profit, dividend_amount]) ignore nulls) 
#         over (partition by instrument order by date rows between 
#         unbounded preceding and current row) as 'columns(*)',
# FROM merge_df ) 


# select date, instrument, avg_net_profit, m_sum(dividend_amount, 750) as y3_dividend_amount from t0 order by date, instrument
# """

# fillna_df = dai.query(sql, filters={"date": ["2010-01-01", "2024-05-31"]}).df()

这段代码的主要目的是:

  1. 使用窗口函数填充数据中的缺失值。
  2. 计算一个滚动窗口的dividend_amount总和。
  3. 对结果按日期和证券代码进行排序。

最终结果将存储在 fillna_df 这个 Pandas DataFrame 中。

https://bigquant.com/codesharev3/72d4aaf8-0778-4c60-8a06-25b0000a7132

{link}