Warning: file_exists(): open_basedir restriction in effect. File(/www/wwwroot/com.xiximiao.oa/com.cmstop/public/www/wp-content/db.php) is not within the allowed path(s): (/www/wwwroot/com.xiximiao.oa/com.cmstop/public/www/:/tmp/:/proc/:/var/log/nginx/:/www/wwwroot/com.xiximiao.oa/com.cmstop/public/:/www/wwwroot/com.xiximiao.oa/com.cmstop/vendor/:/www/wwwroot/com.xiximiao.oa/com.cmstop/ppk/) in /www/wwwroot/com.xiximiao.oa/com.cmstop/public/www/wp-includes/load.php on line 707
2501-高频小市值涨停追击策略 一个去年至今本金翻20倍的策略,无未来 53410 » 轻知量化 QMT、PTrade、聚宽策略分享交流平台

2501-高频小市值涨停追击策略 一个去年至今本金翻20倍的策略,无未来 53410

策略名:高频小市值涨停追击策略

核心逻辑:

本策略聚焦小市值涨停股日内交易,结合 T+0 高频操作与严格风控体系,构建日内交易闭环。策略运行包含三大核心模块:

 

  1. 涨停股追击引擎
  • 标的池筛选流程:
     
    # 代码核心逻辑
    1. 全市场非ST/非科创股票
    2.3日首次涨停突破
    3. 量价筛选(昨日换手>3%且成交额3-19亿)
    4. 集合竞价高开1-6%且量比>3%
    5. 国九条财务过滤(净利润>0且营收>1亿)
    
  • 分层机制:
    • 主攻:首次突破左侧压力位的涨停股
    • 备选:炸板股次日低开 3-4% 的反弹机会
  1. 日内交易系统
  • 四阶段卖出法
    时间 操作规则
    09:30 盈利股卖出 50%
    09:48-11:28 小市值股清仓 / 大市值股减仓
    13:18 未封板小票清仓
    14:48 强制清除非涨停股
  • 三重止损机制
    1. 个股止损:-6% 即时止损
    2. 大盘熔断:中小板平均跌幅 > 6% 全仓止损
    3. 量能预警:突然放量 120 日最高量 90% 触发卖出
  1. 资金风控体系
  • 动态仓位:可用资金等分制
  • 价格过滤:<2 元及> 50 元股票不交易
  • 特殊防护:Tick 级别监控(每 3 秒执行风控检查)

关键参数:

  • 核心指标:涨停封单额 > 1000 万、量比 > 3、流通市值 30-200 亿
  • 交易频率:日均操作 4-6 次
  • 成本控制:0.05% 滑点 + 万 1 佣金
  • 时间阈值:14:50 强制清算

 

策略特点:

  1. 高频特性:Tick 级数据驱动,实现秒级决策
  2. 双重收益源
    • 主攻首次突破涨停股的惯性冲高
    • 捕捉炸板股次日低开反弹机会
  3. 动态风控
    • 六道防御关卡(财务 / 流动 / 技术 / 盘口 / 系统 / 强制)
    • 滑点补偿机制应对流动性损耗
  4. 智能再平衡
    • 早盘侧重资金效率(小票优先)
    • 尾盘强调风险控制(强制清仓)

核心优化点:

  1. 订单拆分:大单分笔成交减少市场冲击
  2. 状态记忆:not_buy_again 列表防止重复交易
  3. 时序控制
    • 09:28 完成股票池计算
    • 关键时点精准卡位(09:30/11:28/14:48)
  4. 特殊行情免疫
    • 规避跳空高开 6% 以上的情绪溢价
    • 过滤次新 / ST / 高商誉等风险因子
(注:该策略特别适合量化高频交易团队,Tick 级监控需配备专业级交易系统)

from jqdata import *
from jqfactor import *
import pandas as pd
from datetime import datetime,timedelta,date
import time 

from jqlib.technical_analysis import *
import datetime as dt

#QMT正式自动交易== 轻知量化可提供QMT跟单支持
'''
from qzqmtimport *
order = qmt_order(order)
order_target = qmt_order_target(order_target)
order_value = qmt_order_value(order_value)
order_target_value = qmt_order_target_value(order_target_value)
'''
#QMT正式自动交易end============

################################### 初始化设置 #############################################
def initialize(context):
    set_option('use_real_price', True)
    log.set_level('system', 'error')
    set_option('avoid_future_data', True)
    
def after_code_changed(context):
    g.n_days_limit_up_list = []   #重新初始化列表
    unschedule_all() # 取消所有定时运行    
    
    set_option('use_real_price', True)
    log.set_level('system', 'error')
    
    # 将滑点设置为0
    set_slippage(FixedSlippage(5/10000))
    # 设置交易成本万分之三,不同滑点影响可在归因分析中查看
    set_order_cost(OrderCost(open_tax=0, close_tax=0.001, open_commission=1.0/10000, close_commission=1.0/10000, close_today_commission=0, min_commission=5),type='stock')
    
    g.buystocks=[] #开盘先算好要买什么票,方便直接买
    g.buyzbstocks=[]#炸板高开票
    
    run_daily(get_stock_list, '09:28:00')#提前选,提前运算
    
    run_daily(sell, time='09:48', reference_security='000300.XSHG')
    run_daily(sell, time='11:28', reference_security='000300.XSHG')
    run_daily(sell, time='13:18', reference_security='000300.XSHG')
    run_daily(sell, time='14:48', reference_security='000300.XSHG')
    
    run_daily(ticksell, time='09:29:00', reference_security='000300.XSHG')
    run_daily(ticksell, time='09:50', reference_security='000300.XSHG')
    run_daily(ticksell, time='10:20', reference_security='000300.XSHG')
    run_daily(ticksell, time='11:20', reference_security='000300.XSHG')
    run_daily(ticksell, time='13:20', reference_security='000300.XSHG')
    run_daily(ticksell, time='14:00', reference_security='000300.XSHG')
    
    context.last_check_time = None  
  

## 开盘前运行函数
def before_market_open(context):

    # 输出运行时间
    log.info('函数before_market_open运行时间:'+str(context.current_dt.time()))

    trade_code_list = list(context.portfolio.positions)
    # 将所有昨日持仓的股票赋予tick权限.
    if trade_code_list:
        subscribe(trade_code_list,'tick')
        
## 定义股票池      
def set_stockpool(context):
    yesterday = context.previous_date 
    initial_list = get_all_securities('stock', yesterday).index.tolist()
    return initial_list

##################################  交易函数群 ##################################
def buy(context):
    current_data = get_current_data()
    qualified_stocks =  g.buystocks #get_stock_list(context)#9.25提前选
    if qualified_stocks:
        value = context.portfolio.available_cash / len(qualified_stocks)
        print('************************************')
        for s in qualified_stocks:
            if(current_data[s].day_open<2):#小于2元票不买
                print('小于2元票,未买入:{0} {1} 开盘价:{2}'.format(current_data[s].name,s,current_data[s].last_price))
                print('———————————————————————————————————')
                continue
            # 下单   #至少够买1手
            if context.portfolio.available_cash/current_data[s].last_price>100: 
                date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                order_value(s, value, MarketOrderStyle(current_data[s].day_open))
                print('$买入:{0} {1} ¥{2} 下单时间:{3}'.format(current_data[s].name,s,current_data[s].last_price,date_now_stime))
                print('———————————————————————————————————')
        print('************************************')
def ticksell(context):
    current_data = get_current_data()
    #print('ticksell')
    
    #分批减仓卖出
    for s in list(context.portfolio.positions):  #有利润就跑
        current_position = context.portfolio.positions[s].total_amount  # 获取当前持仓数量
        #print(s)
        #print(current_position)
        if(context.portfolio.positions[s].closeable_amount != 0 and current_data[s].last_price < current_data[s].high_limit):#
            print("股票:{0},股数:{1},持仓成本:{2},开盘价:{3},涨停价:{4},当前价:{5}"\
            .format(s,context.portfolio.positions[s].closeable_amount,\
            context.portfolio.positions[s].avg_cost,\
            current_data[s].day_open,\
            current_data[s].high_limit,\
            current_data[s].last_price))
            
            avg_cost=context.portfolio.positions[s].avg_cost
           
            ############################################################
            if(current_data[s].last_price<=avg_cost*0.94):#跌超6个点
                print(current_data[s].name+'ticksell-跌超6个点止损')
                #log.info(current_data[s].name+'ticksell-跌超6个点止损')
                date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                order_target_value(s, 0)
                print('tick止损卖出100%的' + current_data[s].name +' 止损时间:'+date_now_stime)
            
def sell(context):
    stime = context.current_dt.strftime("%H%M")
    current_data = get_current_data()
    # 根据时间执行不同的卖出策略
    #开盘先卖一半
    if stime == '0930':
        for s in list(context.portfolio.positions):  #开盘有利润就跑1/2
            current_position = context.portfolio.positions[s].total_amount  # 获取当前持仓数量
            sell_amount = current_position // 2  # 计算要卖出的数量(向下取整)
            if ((context.portfolio.positions[s].closeable_amount != 0) and (current_data[s].last_price < current_data[s].high_limit) and (current_data[s].last_price > 1*context.portfolio.positions[s].avg_cost)):#avg_cost当前持仓成本
                order(s, -sell_amount)  # 卖出指定数量的股票,-sell_amount表示卖出
                date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                print('卖出:{0} {1} 买价:{2} 下单时间:{3}'.format(current_data[s].name,s,current_data[s].last_price,date_now_stime))
    elif stime == '0953':
        for s in list(context.portfolio.positions):  #上午有利润就跑
            current_position = context.portfolio.positions[s].total_amount  # 获取当前持仓数量
            sell_amount = current_position // 2  # 计算要卖出的数量(向下取整)
            if ((context.portfolio.positions[s].closeable_amount != 0) and (current_data[s].last_price < current_data[s].high_limit) and (current_data[s].last_price > 1*context.portfolio.positions[s].avg_cost)):#avg_cost当前持仓成本
                order_target_value(s, sell_amount)
                date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                print('卖出:{0} {1} 买价:{2} 下单时间:{3}'.format(current_data[s].name,s,current_data[s].last_price,date_now_stime))
   
    elif stime == '0948':
        for s in list(context.portfolio.positions):  #上午有利润就跑
            if ((context.portfolio.positions[s].closeable_amount != 0) and (current_data[s].last_price < current_data[s].high_limit) and (current_data[s].last_price > 1*context.portfolio.positions[s].avg_cost)):#avg_cost当前持仓成本
                if(current_data[s].day_open<50):#小票
                    order_target_value(s, 0)
                    date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    print('卖出:{0} {1} 买价:{2} 下单时间:{3}'.format(current_data[s].name,s,current_data[s].last_price,date_now_stime))
    elif stime == '1128':
        for s in list(context.portfolio.positions):  #上午有利润就跑
            if ((context.portfolio.positions[s].closeable_amount != 0) and (current_data[s].last_price < current_data[s].high_limit) and (current_data[s].last_price > 1*context.portfolio.positions[s].avg_cost)):#avg_cost当前持仓成本
                if(current_data[s].day_open>=120):#大票
                    order_target_value(s, 0)
                    date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    print('卖出:{0} {1} 买价:{2} 下单时间:{3}'.format(current_data[s].name,s,current_data[s].last_price,date_now_stime))
    elif stime == '1318':
        for s in list(context.portfolio.positions):
            if ((context.portfolio.positions[s].closeable_amount != 0) and (current_data[s].last_price < current_data[s].high_limit)):#closeable_amount可卖出的仓位
                if(current_data[s].day_open<50):#小票
                    order_target_value(s, 0)
                    date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    print('卖出:{0} {1} 买价:{2} 下单时间:{3}'.format(current_data[s].name,s,current_data[s].last_price,date_now_stime))
    elif stime == '1448':
        for s in list(context.portfolio.positions):
            if ((context.portfolio.positions[s].closeable_amount != 0) and (current_data[s].last_price < current_data[s].high_limit)):#closeable_amount可卖出的仓位
                #if(current_data[s].day_open>=50):#大票
                order_target_value(s, 0)
                date_now_stime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                print('卖出:{0} {1} 买价:{2} 下单时间:{3}'.format(current_data[s].name,s,current_data[s].last_price,date_now_stime))
    #ticksell(context)
    
# 编写tick级别运行函数的逻辑每3秒执行一次    
def handle_tick(context, tick):
    #time.sleep(60)  # 等待3秒 
    #print('tick***')
    # 获取当前时间戳并转换为datetime对象  
    #current_dt = datetime.fromtimestamp(get_datetime().timestamp())  
      #Tick(code: 600995.XSHG, datetime: 2023-09-22 14:55:01, open: 9.41, current: 9.56, high: 9.64, low: 9.4, volume: 7217681, money: 68710378.0, a1_p: 9.56, a2_p: 9.57, a3_p: 9.58, a4_p: 9.59, a5_p: 9.6, a1_v: 72800, a2_v: 37100, a3_v: 88600, a4_v: 69200, a5_v: 84700, b1_p: 9.55, b2_p: 9.54, b3_p: 9.53, b4_p: 9.52, b5_p: 9.51, b1_v: 27800, b2_v: 40000, b3_v: 28400, b4_v: 69100, b5_v: 13800)
    #if datetime.time(9, 30) < context.current_dt.time() <= datetime.time(14, 59):
    #if datetime.time(9, 30) < current_dt.time() <= datetime.time(14, 59):
    ticksell(context)
        
##################################  选股函数群 ##################################   
# 提前选股

关键函数解锁后查看:


###################################  其它函数群 ##################################
# 处理日期相关函数
def transform_date(date, date_type):
    if type(date) == str:
        str_date = date
        dt_date = dt.datetime.strptime(date, '%Y-%m-%d')
        d_date = dt_date.date()
    elif type(date) == dt.datetime:
        str_date = date.strftime('%Y-%m-%d')
        dt_date = date
        d_date = dt_date.date()
    elif type(date) == dt.date:
        str_date = date.strftime('%Y-%m-%d')
        dt_date = dt.datetime.strptime(str_date, '%Y-%m-%d')
        d_date = date
    dct = {'str':str_date, 'dt':dt_date, 'd':d_date}
    return dct[date_type]
    
# 筛选出某一日涨停的股票
def get_hl_stock2(stock_list, date1,days):
    if not stock_list:return []
    h_s = get_price(stock_list, end_date=date1, frequency='daily', fields=['close', 'high_limit', 'paused'],
                  count=days, panel=False, fill_paused=False, skip_paused=True
                  ).query('close==high_limit and paused==0').groupby('code').size()
    return h_s.index.tolist()
    
# 计算涨停数  蒋老师优化(暂未使用!)
def get_hl_stock(stock_list, date1, days):
    # 获取watch_days的数据
    h_s = get_price(stock_list, end_date=date1, fields=['low', 'close', 'high_limit','paused'], 
               count=days, panel=False).query('close==high_limit and paused==0').groupby('code').size()
    return h_s.index.tolist()
    
    
# 过滤函数
def filter_new_stock(initial_list, date, days=50):
    return [stock for stock in initial_list if get_security_info(stock).start_date < date - timedelta(days=days)]

# 过滤函数
def filter_new_stock_zb(initial_list, date, days=50):
    d_date = transform_date(date, 'd')
    return [stock for stock in initial_list if d_date - get_security_info(stock).start_date > dt.timedelta(days=days)]
    
def filter_st_paused_stock(initial_list, date):
    current_data = get_current_data()
    return [stock for stock in initial_list if not (
            current_data[stock].is_st or 
            current_data[stock].paused or 
            'ST' in current_data[stock].name or
            '*' in current_data[stock].name or
            '证券' in current_data[stock].name or
            '财' in current_data[stock].name or
            '退' in current_data[stock].name)]


def filter_kcbj_stock(initial_list):
    return [stock for stock in initial_list if stock[0] != '4' and stock[0] != '8'  and stock[:2] != '68'] #and stock[0] != '3'

def filter_price_stock(initial_list, date):#小于2元票不买
    current_data = get_current_data()
    return [stock for stock in initial_list if (
            current_data[stock].day_open>=2)]

#######################################炸板相关####################################
# 每日初始股票池
def prepare_stock_list_zb(date): 
    initial_list = get_all_securities('stock', date).index.tolist()
    initial_list = filter_kcbj_stock(initial_list)
    initial_list = filter_new_stock_zb(initial_list, date)
    initial_list = filter_st_paused_stock(initial_list, date)
    #initial_list = filter_paused_stock(initial_list, date)
    return initial_list

# 选炸板高开票
def get_zb_stocks(context):
    
    # 文本日期
    date = context.previous_date
    date = transform_date(date, 'str')
    date_1=get_shifted_date(date, -1, 'T')
    date_2=get_shifted_date(date, -2, 'T')

    # 初始列表
    initial_list = prepare_stock_list_zb(date)
    # 前日曾涨停
    h1_list = get_ever_hl_stock_zb(initial_list, date)
    # 上上个交易日涨停过滤
    #elements_to_remove = get_hl_stock_zb(initial_list, date_1)
    
    # 过滤上上个交易日涨停、曾涨停
    #all_list = [stock for stock in h1_list if stock not in elements_to_remove]
    all_list = [stock for stock in h1_list]
    
    target_list = all_list
    
    
    qualified_stocks = [] 
    current_data = get_current_data()
    date_now = context.current_dt.strftime("%Y-%m-%d")
    mid_time1 = ' 09:15:00'
    end_times1 =  ' 09:30:01'
    start = date_now + mid_time1
    end = date_now + end_times1
    for s in target_list:
        
        #是否为跳空高开
        prev_data = attribute_history(s, 2, '1d', fields=['close', 'open'], skip_paused=True)
        yesterday_open=prev_data['open'][-1]
        before_yesterday_close=prev_data['close'][-2]
        
        # 判断昨天是否为跳空高开  
        if yesterday_open >= before_yesterday_close*1.06:  
            #log.info(f"{s} 前天收盘价{before_yesterday_close},昨天开盘价{yesterday_open},为跳空高开,不能买!")  
            continue
        
        
        # 条件一:均价,金额,市值,换手率
        prev_day_data = attribute_history(s, 1, '1d', fields=['close', 'volume', 'money'], skip_paused=True)
        avg_price_increase_value = prev_day_data['money'][0] / prev_day_data['volume'][0] / prev_day_data['close'][0]  - 1
        if avg_price_increase_value < -0.04 or prev_day_data['money'][0] < 3e8 or prev_day_data['money'][0] > 19e8:
            continue
        turnover_ratio_data=get_valuation(s, start_date=context.previous_date, end_date=context.previous_date, fields=['turnover_ratio', 'market_cap','circulating_market_cap'])
        if turnover_ratio_data.empty or turnover_ratio_data['market_cap'][0] < 70  or turnover_ratio_data['circulating_market_cap'][0] > 520 :
            continue
        
        
        # 条件二:左压
        zyts = calculate_zyts(s, context)
        volume_data = attribute_history(s, zyts, '1d', fields=['volume'], skip_paused=True)
        if len(volume_data) < 2 or volume_data['volume'][-1] <= max(volume_data['volume'][:-1]) * 0.9:
            continue
        
        # 条件三:高开,开比
        
        auction_data = get_call_auction(s, start_date=start, end_date=end, fields=['time','volume', 'current'])
        
        if auction_data.empty or auction_data['volume'][0] / volume_data['volume'][-1] < 0.03:
            continue
        current_ratio = auction_data['current'][0] / (current_data[s].high_limit/1.1)
        if current_ratio<=0.98 or current_ratio>=1.09:
            continue
        
        # 如果股票满足所有条件,则添加到列表中  
        qualified_stocks.append(s)
    g.buyzbstocks=qualified_stocks


# 计算左压天数
def calculate_zyts(s, context):
    high_prices = attribute_history(s, 101, '1d', fields=['high'], skip_paused=True)['high']
    prev_high = high_prices.iloc[-1]
    zyts_0 = next((i-1 for i, high in enumerate(high_prices[-3::-1], 2) if high >= prev_high), 100)
    zyts = zyts_0 + 5
    return zyts
    
# 筛选出某一日涨停的股票
def get_hl_stock_zb(initial_list, date):
    df = get_price(initial_list, end_date=date, frequency='daily', fields=['close','high_limit'], count=1, panel=False, fill_paused=False, skip_paused=False)
    df = df.dropna() #去除停牌
    df = df[df['close'] == df['high_limit']]
    hl_list = list(df.code)
    return hl_list

# 筛选曾涨停
def get_ever_hl_stock_zb(initial_list, date):
    df = get_price(initial_list, end_date=date, frequency='daily', fields=['close','high','high_limit'], count=1, panel=False, fill_paused=False, skip_paused=False)
    df = df.dropna() #去除停牌
    cd1 = df['high'] == df['high_limit'] 
    cd2 = df['close'] != df['high_limit']
    df = df[cd1 & cd2]
    hl_list = list(df.code)
    return hl_list

# 计算涨停数
def get_hl_count_df(hl_list, date, watch_days):
    # 获取watch_days的数据
    df = get_price(hl_list, end_date=date, frequency='daily', fields=['close','high_limit','low'], count=watch_days, panel=False, fill_paused=False, skip_paused=False)
    df.index = df.code
    #计算涨停与一字涨停数,一字涨停定义为最低价等于涨停价
    hl_count_list = []
    extreme_hl_count_list = []
    for stock in hl_list:
        df_sub = df.loc[stock]
        hl_days = df_sub[df_sub.close==df_sub.high_limit].high_limit.count()
        extreme_hl_days = df_sub[df_sub.low==df_sub.high_limit].high_limit.count()
        hl_count_list.append(hl_days)
        extreme_hl_count_list.append(extreme_hl_days)
    #创建df记录
    df = pd.DataFrame(index=hl_list, data={'count':hl_count_list, 'extreme_count':extreme_hl_count_list})
    return df



# 计算昨涨幅
def get_index_increase_ratio(index_code, context):
    # 获取指数昨天和前天的收盘价
    close_prices = attribute_history(index_code, 2, '1d', fields=['close'], skip_paused=True)
    if len(close_prices) < 2:
        return 0  # 如果数据不足,返回0
    day_before_yesterday_close = close_prices['close'][0]
    yesterday_close = close_prices['close'][1]
    
    # 计算涨幅
    increase_ratio = (yesterday_close - day_before_yesterday_close) / day_before_yesterday_close
    return increase_ratio
    
def get_shifted_date(date, days, days_type='T'):
    #获取上一个自然日
    d_date = transform_date(date, 'd')
    yesterday = d_date + dt.timedelta(-1)
    #移动days个自然日
    if days_type == 'N':
        shifted_date = yesterday + dt.timedelta(days+1)
    #移动days个交易日
    if days_type == 'T':
        all_trade_days = [i.strftime('%Y-%m-%d') for i in list(get_all_trade_days())]
        #如果上一个自然日是交易日,根据其在交易日列表中的index计算平移后的交易日        
        if str(yesterday) in all_trade_days:
            shifted_date = all_trade_days[all_trade_days.index(str(yesterday)) + days + 1]
        #否则,从上一个自然日向前数,先找到最近一个交易日,再开始平移
        else:
            for i in range(100):
                last_trade_date = yesterday - dt.timedelta(i)
                if str(last_trade_date) in all_trade_days:
                    shifted_date = all_trade_days[all_trade_days.index(str(last_trade_date)) + days + 1]
                    break
    return str(shifted_date)
##############################################################################################
    

#2.0 #国九
def filter_gjt(context,target_list):
    final_list = []
    # 国九更新:过滤近一年净利润为负且营业收入小于1亿的
    # 国九更新:过滤近一年期末净资产为负的 (经查询没有为负数的,所以直接pass这条)
    # 国九更新:过滤近一年审计建议无法出具或者为负面建议的 (经过净利润等筛选,审计意见几乎不会存在异常)
    q = query(
        valuation.code,
        valuation.market_cap,  # 总市值 circulating_market_cap/market_cap
        income.np_parent_company_owners,  # 归属于母公司所有者的净利润
        income.net_profit,  # 净利润
        income.operating_revenue  # 营业收入
        #security_indicator.net_assets
    ).filter(
        valuation.code.in_(target_list),
        #valuation.market_cap.between(g.min_mv,g.max_mv),
        #income.np_parent_company_owners > 0,
        #income.net_profit > 0,
        #income.operating_revenue > 1e8,
        #indicator.roe>0,#股票净资产收益率
        #indicator.roa>0,#资产回报率
      
    )#.order_by(valuation.market_cap.asc()).limit(100)
    
    df = get_fundamentals(q)
    final_list = list(df.code)
    
    # 过滤审计意见
    #if g.filter_audit:
    final_list = filter_audit(context,final_list)
    
    #过滤红利股
    #if g.filter_bonus:
    #final_list = bonus_filter(context,final_list)
        
    if len(final_list) == 0:
        # 由于有时候选股条件苛刻,所以会没有股票入选,这时买入银华日利ETF
        log.info('无适合股票')
    return final_list
    
#2.1 筛选审计意见
'''
审计意见类型编码
类型编码 审计意见类型
1 	     无保留
2 	     无保留带解释性说明
3        保留意见
4        拒绝/无法表示意见
5        否定意见
6 	     未经审计
7 	     保留带解释性说明
10 	     经审计(不确定具体意见类型)
11       无保留带持续经营重大不确定性
'''
def filter_audit(context,code_list):
    # 获取审计意见,近三年内如果有不合格(report_type为3、4、5、7)的审计意见则返回False,否则返回True
    final_list = []
    expection_Audit_list = []
    for stock in code_list:
        lstd = context.previous_date
        last_year = (lstd.replace(year=lstd.year - 3, month=1, day=1)).strftime('%Y-%m-%d')
        q=query(finance.STK_AUDIT_OPINION.code,finance.STK_AUDIT_OPINION.pub_date,finance.STK_AUDIT_OPINION).filter(
                                finance.STK_AUDIT_OPINION.code==stock,finance.STK_AUDIT_OPINION.pub_date>=last_year)
        df=finance.run_query(q)
        # print('\n%s'%df)
        values_to_check = [3, 4, 5, 7]
        contains_unwanted_values = df['opinion_type_id'].isin(values_to_check).any()
        if not contains_unwanted_values:
            final_list.append(stock)
        else:
            expection_Audit_list.append(stock)
    print('★★★★ 去除近三年内存在审计问题的%s只 ★★★★'%(len(expection_Audit_list)))
    print('★★★★ 存在审计问题的: %s  '%(expection_Audit_list))

    return  final_list  # 返回剔除审计意见异常后的list

#2.2 #获取红利列表
def bonus_filter(context,stock_list):
    #print(f'进入红利筛选前,共{len(stock_list)}只股票')
    year=context.previous_date.year
    start_date=datetime.date(year, 1, 1)
    end_date=context.previous_date
    if end_date.month in g.Expected_bonus:
        q = query(finance.STK_XR_XD.code,finance.STK_XR_XD.company_name, finance.STK_XR_XD.board_plan_pub_date,finance.STK_XR_XD.bonus_amount_rmb,finance.STK_XR_XD.bonus_ratio_rmb
            ).filter(               
                #finance.STK_XR_XD.bonus_type !='年度分红',
                finance.STK_XR_XD.board_plan_pub_date>start_date,
                finance.STK_XR_XD.implementation_pub_date<=end_date,
                #finance.STK_XR_XD.a_xr_date < context.previous_date,
                finance.STK_XR_XD.bonus_ratio_rmb>0,
                finance.STK_XR_XD.code.in_(stock_list))
        Expected_bonus_df = finance.run_query(q)
        
        if len(Expected_bonus_df)>0:
            bonus_list=Expected_bonus_df['code'].unique().tolist()
            price_df=history(1, unit='1d', field='close', security_list=bonus_list, df=True, skip_paused=False, fq='pre')
            price_df=price_df.T
            price_df.rename(columns={price_df.columns[0]:'Close_now'},inplace=True)
            price_df['code']=price_df.index
            Expected_bonus_df=pd.merge(Expected_bonus_df,price_df,on=('code'),how='left')
            Expected_bonus_df['bonus_ratio']=(Expected_bonus_df['bonus_ratio_rmb'])/Expected_bonus_df['Close_now']
            Expected_bonus_df=Expected_bonus_df.sort_values(by='bonus_ratio',ascending=True)
            bonus_list=Expected_bonus_df['code'].unique().tolist()
        else:
            bonus_list=[]
    else:
        reprot_date = datetime.date(year-1, 12, 31)
        q = query(finance.STK_XR_XD.code,finance.STK_XR_XD.company_name,finance.STK_XR_XD.a_registration_date, finance.STK_XR_XD.bonus_amount_rmb,finance.STK_XR_XD.bonus_ratio_rmb
            ).filter(
                finance.STK_XR_XD.report_date ==reprot_date,         
                finance.STK_XR_XD.bonus_type=='年度分红' ,
                finance.STK_XR_XD.implementation_pub_date<=end_date,
                finance.STK_XR_XD.board_plan_bonusnote=='不分配不转增',
                finance.STK_XR_XD.code.in_(stock_list))
    
        no_year_bonus = finance.run_query(q)
        no_year_bonus_list=no_year_bonus['code'].unique().tolist()
        #排除今年不分红的股票
        bonus_list=[code for code in stock_list if code not in no_year_bonus_list]
        bonus_list=short_by_market_cap(context,bonus_list)
       
    print(f'进行实际红利筛选后,原有{len(stock_list)}只股票,筛选后剩余{len(bonus_list)}只股票')
    
    if len(bonus_list)< g.stock_num:
        bonus_list.extend([x for x in short_by_market_cap(context,stock_list) if x not in bonus_list ][:g.stock_num-len(bonus_list)])
    return bonus_list

### end ###

最后更新: 2025-09-3 06:52

⚠️
本站资源大多来自网络,仅供网友学习交流,未经作者或上传书面授权,请勿作他用。
站长 vx: xiangyin615 或者 留言反馈 ,我们将尽快处理。
Notice: When you of the legal rights be violate, please stir to vx: xiangyin615
个人中心
购物车
优惠劵
搜索