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
2319 高股息低市盈率稳健增长的价投策略 量化自动交易策略代码分享 » 轻知量化 QMT、PTrade、聚宽策略分享交流平台

2319 高股息低市盈率稳健增长的价投策略 量化自动交易策略代码分享

策略名称:高股息低市盈率高增长价值投资策略


策略说明:

本策略基于高股息、低市盈率和高增长的价值投资理念,通过筛选分红率高、市盈率低且盈利增长稳定的股票进行投资。策略的核心思想是寻找被市场低估的优质公司,长期持有以获取稳定收益。

策略核心逻辑:

  1. 股票池筛选

    • 排除科创板、北交所、ST股和上市未满300天的股票。

    • 筛选过去3年平均分红率最高的10%的股票。

    • 筛选市盈率在0到25之间、净资产收益率大于3%、营业收入同比增长率大于5%、净利润同比增长率大于11%的股票。

  2. 交易决策

    • 每月第一个交易日开盘时,根据筛选出的股票调整持仓,买入新筛选的股票,卖出不再符合条件的股票。

    • 每日14:00检查昨日涨停的股票,若今日未涨停则卖出。

  3. 风险控制

    • 排除停牌、涨停或跌停的股票,避免无法交易的情况。

    • 设置每只股票的仓位上限,避免过度集中风险。

策略特点:

  • 高股息:通过筛选高分红率的股票,获取稳定的分红收益。

  • 低市盈率:通过筛选低市盈率的股票,寻找被市场低估的优质公司。

  • 高增长:通过筛选盈利增长稳定的股票,确保公司的持续盈利能力。

适用场景:

  • 适用于A股市场,能够有效捕捉被市场低估的优质公司。

  • 适合中长期投资者,通过长期持有获取稳定收益。

风险提示:

  • 高股息策略在牛市中可能表现不佳,需注意市场风险。

  • 策略依赖于财务数据的准确性和及时性,需定期更新数据。


策略代码结构:

  1. 初始化函数(initialize

    • 设定基准指数(沪深300)。

    • 配置交易手续费和滑点。

    • 定义每日运行的函数(开盘前、开盘时、收盘后)。

  2. 开盘前运行函数(before_trading_start

    • 筛选符合条件的股票,更新持仓列表和历史持仓列表。

  3. 开盘时运行函数(handle_data

    • 每月第一个交易日开盘时,根据筛选出的股票调整持仓,买入新筛选的股票,卖出不再符合条件的股票。

    • 每日14:00检查昨日涨停的股票,若今日未涨停则卖出。

  4. 交易函数(my_Trader

    • 根据筛选出的股票调整持仓,买入新筛选的股票,卖出不再符合条件的股票。

  5. 收盘后运行函数(check_limit_up

    • 检查昨日涨停的股票,若今日未涨停则卖出。

  6. 辅助函数

    • filter_kcbj_stock:过滤科创板和北交所股票。

    • filter_paused_stock:过滤停牌股票。

    • filter_st_stock:过滤ST股。

    • filter_limit_stock:过滤涨停股票。

    • get_dividend_ratio_filter_list:根据过去3年分红率筛选股票。

    • filter_new_stock:过滤上市未满300天的股票。

    • choice_try_A:根据市盈率、净资产收益率、营业收入同比增长率、净利润同比增长率等因子筛选股票。


策略优化建议:

  1. 参数优化

    • 可对分红率、市盈率、净资产收益率等筛选条件进行优化,以适应不同的市场周期。

    • 调整持仓数量(如10只),以平衡风险与收益。

  2. 风险控制

    • 增加最大回撤限制,当回撤超过一定比例时暂停交易。

    • 设置单笔交易的最大亏损比例,避免单笔交易亏损过大。

  3. 多因子结合

    • 可结合其他财务指标(如市净率、资产负债率等)进行综合筛选,提高策略的稳定性。


总结:

本策略通过高股息、低市盈率和高增长的价值投资理念,筛选被市场低估的优质公司,适合中长期投资者在A股市场中获取稳定收益。策略逻辑清晰,适合有一定编程基础的投资者进行进一步优化和实盘测试。

策略源代码

# 风险及免责提示:该策略由聚宽用户在聚宽社区分享,仅供学习交流使用。
# 标题:高股息低市盈率高增长的价投策略
# 原回测时间选择分钟,如果选择每天,回测速度就会更快

'''
0、股票筛选所有股票,去科创板和北交所,去ST,去上市未满300天;
1、股票筛选和排序:最近3年平均分红股息率最大10%的股票,按分红/股价 从大到小排序;(股价上涨,会导致排名下跌)
2、股票筛选:PEG在0.08-2之间,市盈率:0~20之间,净资产收益率>3%,营业收入同比增长率>5%,净资产同比增长>11%;(股价上涨到PE超过25就会跌出股票池)
3、选取的股票:去停牌,去昨天涨停的,取前10个股票做为买入股票池(股价)
4、执行频率每月执行一次;
5、每天执行的是:如果昨天是涨停的,今天没有涨停,则卖出;
'''
import pandas as pd   # 导入pandas库并设置别名pd
from jqdata import *  # 从jqdata模块导入所有内容

def initialize(context):
    set_benchmark('000300.XSHG')    # 设定基准
    log.set_level('order', 'error')    # 过滤order中低于error级别的日志
    set_option('use_real_price', True)    # 用真实价格交易
    set_option('avoid_future_data', True)    # 打开防未来函数
    set_slippage(FixedSlippage(0.02))     # 设置滑点    
    g.stock_num = 10  # 设置要持有的股票数量
    g.month=context.current_dt.month-1  # 获取上一个月份

# 开盘前运行,在每个交易日的开盘前运行,用于执行一些开盘前需要准备的工作
def before_trading_start(context):
    prepare_stock_list(context)   # 调用 prepare_stock_list 函数
    print(context.run_params.type)   # 打印策略运行参数中的类型,了解策略是在进行回测还是模拟交易

# 开盘时运行函数
def handle_data(context,data):
    hour=context.current_dt.hour          # 读取当前时间的小时部分
    minute=context.current_dt.minute      # 读取当前时间的分钟部分
    #选股程序每月执行一次,检查是否到达新的月份,并且当前时间是9:30(开盘时)
    if context.current_dt.month !=g.month and hour==9 and minute==30:
        my_Trader(context)       # 调用 my_Trader 函数执行选股和交易逻辑
        g.month=context.current_dt.month     # 更新全局变量 g.month 为当前月份,这样选股程序下个月才会再次执行
    #-----------------------执行频率的设置和股票筛选程序----------------------
    if hour==14 and minute==0:    # 检查当前时间是否是14:00
        check_limit_up(context)   # 调用 check_limit_up 函数,卖出昨天涨停今天开板的股票
    # 显示可动用现金与总资产的比例,这里记录的是现金占总资产的比例
    record(cash=context.portfolio.available_cash/context.portfolio.total_value*100)

关键函数解锁后查看:

#显示筛查出股票的:名称,代码,市值
def slist(context,stock_list):    
    current_data = get_current_data()   # 获取当前所有股票的数据
    for stock in stock_list:    # 遍历股票列表中的每只股票
        # 获取单只股票的财务数据,这里只获取了估值表(valuation)的数据,且筛选条件是股票代码等于当前遍历到的股票代码
        df = get_fundamentals(query(valuation).filter(valuation.code == stock))
        # 打印股票的详细信息,包括:股票代码:使用格式化字符串中的占位符 {0} 来表示;# 股票名称:调用 get_security_info 函数获取股票信息对象,然后通过 display_name 属性获取股票的名称;总市值:从 df(DataFrame)中获取 market_cap 列的值,索引为 [0],表示获取第一行的数据
        # 流通市值:从 df 中获取 circulating_market_cap 列的值;PE(市盈率):从 df 中获取 pe_ratio 列的值;股价:从 current_data 中获取 last_price 属性,表示当前时间点的股票最新价格;.2f 是格式化说明符,表示浮点数保留两位小数
        print('股票代码:{0},  名称:{1},  总市值:{2:.2f},  流通市值:{3:.2f},  PE:{4:.2f},股价:{5:.2f}'.format(stock,get_security_info(stock).display_name,df['market_cap'][0],df['circulating_market_cap'][0],df['pe_ratio'][0],current_data[stock].last_price))

#1-1 准备股票池
# 如果持有的股票昨日涨停,则将其加入涨停列表,以便在今日监控是否打开涨停,一旦打开涨停就卖出,这个每天执行
def prepare_stock_list(context):
    g.hold_list= []    # 初始化全局变量 g.hold_list 为空列表,用于存储当前持有的股票列表
    g.high_limit_list=[]    # 初始化全局变量 g.high_limit_list 为空列表,用于存储昨日涨停的股票列表
    for position in list(context.portfolio.positions.values()):    # 遍历当前投资组合中的所有持仓
        stock = position.security          # 获取持仓的股票代码
        g.hold_list.append(stock)         # 将股票代码添加到 g.hold_list 列表中
    #获取昨日涨停列表
    if g.hold_list != []:    # 如果 g.hold_list 不为空,即有持仓股票
        for stock in g.hold_list:        # 遍历 g.hold_list 中的每只股票
            # 获取该股票昨日的收盘价和涨停价,
            df = get_price(stock, end_date=context.previous_date, frequency='daily', fields=['close','high_limit'], count=1)
            if df['close'][0] >= df['high_limit'][0]*0.98:  # 检查昨日的收盘价是否接近涨停价(这里使用 0.98 作为阈值)
                g.high_limit_list.append(stock)     # 将昨日涨停的股票添加到 g.high_limit_list 列表中
    
#1-5 调整昨日涨停股票
def check_limit_up(context):
    now_time = context.current_dt  # 获取当前时间
    if g.high_limit_list != []:   # 检查昨日涨停股票列表是否为空
        #对昨日涨停股票观察到尾盘如不涨停则提前卖出,如果涨停即使不在应买入列表仍暂时持有
        for stock in g.high_limit_list:   # 遍历昨日涨停股票列表
            current_data = get_current_data()   # 获取当前所有股票的数据
            if current_data[stock].last_price <   current_data[stock].high_limit:  # 检查当前价格是否小于涨停价,即判断今日是否打开涨停
                log.info("[%s]涨停打开,卖出" % (stock))     # 如果打开涨停,则记录卖出信息
                order_target(stock, 0)     # 卖出股票持仓
            else:
                log.info("[%s]涨停,继续持有" % (stock))    # 如果仍然涨停,则记录继续持有的信息
 
# 过滤科创北交股票
def filter_kcbj_stock(stock_list):
    for stock in stock_list[:]:  # 遍历股票列表,过滤掉科创板和北交所的股票
        if stock[0] == '4' or stock[0] == '8' or stock[:2] == '68':  # 根据股票代码判断是否属于科创板或北交所
            stock_list.remove(stock)  # 从列表中移除该股票
    return stock_list

# 过滤停牌股票
def filter_paused_stock(stock_list):
	current_data = get_current_data()   # 获取当前所有股票的数据
	return [stock for stock in stock_list if not current_data[stock].paused]   # 使用列表推导式,返回不在停牌状态的股票列表

# 过滤ST及其他具有退市标签的股票
def filter_st_stock(stock_list):
	current_data = get_current_data()       # 获取当前所有股票的数据
	return [stock for stock in stock_list   # 使用列表推导式,返回不是ST股且名称中不包含特定退市标签的股票列表
			if not current_data[stock].is_st
			and 'ST' not in current_data[stock].name
			and '*' not in current_data[stock].name
			and '退' not in current_data[stock].name]

# 过滤涨停的股票
def filter_limit_stock(context, stock_list):
	last_prices = history(1, unit='1m', field='close', security_list=stock_list)  # 获取最近一分钟的收盘价
	current_data = get_current_data()  # 获取当前所有股票的数据
	# 已存在于持仓的股票即使涨停也不过滤,避免此股票再次可买,但因被过滤而导致选择别的股票
	return [stock for stock in stock_list if stock in context.portfolio.positions.keys()
			or current_data[stock].low_limit < last_prices[stock][-1] < current_data[stock].high_limit]

#1-1 根据最近三年分红除以当前总市值计算股息率并筛选
def get_dividend_ratio_filter_list(context, stock_list, sort, p1, p2):
    time1 = context.previous_date    # 获取策略运行的上一个日期
    time0 = time1 - datetime.timedelta(days=365*3)      # 计算三年前日期,用于获取分红数据
    #获取分红数据,由于finance.run_query最多返回4000行,以防未来数据超限,最好把stock_list拆分后查询再组合
    interval = 1000 #某只股票可能一年内多次分红,导致其所占行数大于1,所以interval不要取满4000
    list_len = len(stock_list)
    # 初始化查询分红数据的 Query 对象
    #               股票代码                        股权登记日                     每股分红金额(人民币)
    q = query(finance.STK_XR_XD.code, finance.STK_XR_XD.a_registration_date, finance.STK_XR_XD.bonus_amount_rmb
    ).filter(
        finance.STK_XR_XD.a_registration_date >= time0, # 股权登记日大于等于三年前
        finance.STK_XR_XD.a_registration_date <= time1, # 股权登记日小于等于上一个日期
        finance.STK_XR_XD.code.in_(stock_list[:min(list_len, interval)]))  # 股票代码在列表中
    df = finance.run_query(q)    # 执行查询,获取分红数据
    #对interval的部分分别查询并拼接
    if list_len > interval:      # 如果股票列表长度大于设定的查询间隔,则分批查询
        df_num = list_len // interval
        for i in range(df_num):
            q = query(finance.STK_XR_XD.code, finance.STK_XR_XD.a_registration_date,  finance.STK_XR_XD.bonus_amount_rmb
            ).filter(
                finance.STK_XR_XD.a_registration_date >= time0, # 股权登记日大于等于三年前
                finance.STK_XR_XD.a_registration_date <= time1, # 股权登记日小于等于上一个日期
                finance.STK_XR_XD.code.in_(stock_list[interval*(i+1):min(list_len,interval*(i+2))]))
            temp_df = finance.run_query(q)
            df = df.append(temp_df)
    # 处理分红数据,填充空值为0,并按股票代码分组求和
    dividend = df.fillna(0) # df.fillna() 是一个 Pandas 数据处理库中的函数,它可以用来填充数据框中的空值
    dividend = dividend.groupby('code').sum()
    temp_list = list(dividend.index) #query查询不到无分红信息的股票,所以temp_list长度会小于stock_list
    #获取市值相关数据 股票代码      总市值
    q = query(valuation.code,valuation.market_cap).filter(valuation.code.in_(temp_list))
    cap = get_fundamentals(q, date=time1)  # 执行查询,获取市值数据
    cap = cap.set_index('code')  # 将市值数据设置为以股票代码为索引的 DataFrame
    # 计算股息率(分红金额除以总市值)
    cap['dividend_ratio']=(dividend['bonus_amount_rmb']/10000)/cap['market_cap']
    # 根据股息率进行排序并筛选
    cap = cap.sort_values(by=['dividend_ratio'], ascending=sort)
    final_list = list(cap.index)[int(p1*len(cap)):int(p2*len(cap))] # 计算筛选比例,p1 和 p2 为筛选的上下界比例
    return final_list      # 返回筛选后的股票列表
	
# 过滤次新股
def filter_new_stock(context, stock_list):
    # 使用列表推导式来构建一个新的列表,包含满足特定条件的股票
     # 对于 stock_list 中的每只股票,如果满足以下条件,则将其包含在新列表中:条件是:(策略运行的上一个日期 - 300天) 必须大于该股票的上市日期
    return [stock for stock in stock_list if (context.previous_date - datetime.timedelta(days=300)) > get_security_info(stock).start_date]

关键函数解锁后查看:

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