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
2316 大市值价值投资从2005年至今超额稳定.py » 轻知量化 QMT、PTrade、聚宽策略分享交流平台

2316 大市值价值投资从2005年至今超额稳定.py

回测结果:回测区间 2023-01-01 ~ 2025-02-02 、策略收益 40.06% 、 年化19%

天天想着年化N倍的圣杯策略,到最后发现都是白折腾,徒耗光阴,蓦然回首还是价值投资才是最终的归宿?毕竟这条路上有无数成功的先例。

思路:

1、选上市200天以上主板成熟个股;
2、价格低于价值(pb< 1);
3、公司真的在不断赚钱,赚真钱,主要用到:
(1)经营现金流:这个比利润真实,不像利润容易作假;
(2)扣非净利润:别靠做账做出假利润;
(3)总资产收益率:别靠负债做高ROE;
(4)净利润同比增长:说明经营良好; 再加上排序指标,取前5支,每月调仓。
随着股市波动,在大牛市顶端会自动空仓留住利润,因为那个位置选不出有价值的票了。
ps,回测过从2005年至今(2023.4.11),超额收益一直稳定上升。

策略说明:

本策略基于价值投资理念,通过筛选低市净率(PB < 1)、高资产回报率(ROA > 0.15)、净利润同比增长的股票,构建投资组合。策略的核心思想是寻找被市场低估的优质公司,长期持有以获取超额收益。

策略核心逻辑:

  1. 股票池筛选

    • 筛选上市200天以上的主板股票,排除创业板、ST股、停牌股、涨停或跌停的股票。

    • 筛选市净率(PB)小于1的股票,确保价格低于账面价值。

    • 筛选经营活动现金流净额大于1亿、扣非净利润大于1亿、资产回报率(ROA)大于0.15、净利润同比增长的股票。

  2. 交易决策

    • 每月第一个交易日开盘前筛选符合条件的股票,按资产回报率(ROA)排序,选取前5只股票。

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

  3. 风险控制

    • 设置止损条件,当持仓股票亏损超过5%时,卖出止损。

    • 通过跌停数量判断市场风险,若跌停数量较多,则暂停买入。

策略特点:

  • 价值投资:通过筛选低市净率、高资产回报率的股票,寻找被市场低估的优质公司。

  • 长期持有:每月调仓一次,适合中长期投资者。

  • 动态仓位管理:限制持仓数量,避免过度集中风险。

适用场景:

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

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

风险提示:

  • 价值投资策略在牛市中可能表现不佳,需注意市场风险。

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


策略代码结构:

  1. 初始化函数(initialize

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

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

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

  2. 开盘前运行函数(before_market_open

    • 筛选符合条件的股票,按资产回报率(ROA)排序,选取前5只股票。

  3. 开盘时运行函数(my_trade

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

  4. 收盘后运行函数(after_market_close

    • 记录当天的成交记录,并输出日志。

  5. 辅助函数

    • order_target_value_:自定义下单函数,记录买入或卖出操作。

    • open_position:买入指定价值的股票。

    • close_position:卖出指定持仓的股票。

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

    • get_name:获取股票的名称。


策略优化建议:

  1. 参数优化

    • 可对市净率(PB)、资产回报率(ROA)等筛选条件进行优化,以适应不同的市场周期。

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

  2. 风险控制

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

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

  3. 多因子结合

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


总结:

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

策略源码

# 风险及免责提示:该策略由聚宽用户在聚宽社区分享,仅供学习交流使用。
# 标题:大市值价值投资,从2005年至今超额稳定

from jqdata import *  # 从jqdata模块导入所有内容

# 初始化函数,设定基准等等
def initialize(context):
	# 设定沪深300作为基准
	set_benchmark('000300.XSHG')
	# 开启动态复权模式(真实价格)
	set_option('use_real_price', True)
	#防止未来函数
	set_option("avoid_future_data", True)
	# 输出内容到日志 log.info()
	log.info('初始函数开始运行且全局只运行一次')
	# 过滤掉order系列API产生的比error级别低的log
	log.set_level('order', 'error')
	
	g.buy_stock_count = 5  # 定义全局变量,用于指定策略希望持有的股票数量是5只
	g.check_out_lists = []  # 定义全局变量,用于存储策略筛选出来的股票代码列表;初始化为空列表,表示开始时没有选定任何股票
	
	# 设置交易成本,          卖出时印花税      买入时印花税               卖出时佣金            最低佣金,不包含印花税
	set_order_cost(OrderCost(close_tax=0.001, open_commission=0.00012, close_commission=0.00012, min_commission=5),
				   type='stock')  # type: 股票
	
	run_monthly(before_market_open, 1, time='5:00', reference_security='000300.XSHG')	# 每月第一个交易日开盘前 5:00 运行 before_market_open
	run_monthly(my_trade, 1, time='9:30', reference_security='000300.XSHG')             # 每月第一个交易日开盘时 9:30 运行 my_trade
	run_daily(after_market_close, time='after_close', reference_security='000300.XSHG') # 收盘后 after_close 运行 after_market_close

# 开盘时运行函数
def my_trade(context):
	# 调用adjust_position函数来调整当前的投资组合,的是在开盘时根据预设的股票列表调整持仓,执行买入或卖出操作
	adjust_position(context, g.check_out_lists)

# 收盘后运行函数
def after_market_close(context):
	positions_dict = context.portfolio.positions      # 获取当前投资组合中所有持仓的字典对象
	for position in list(positions_dict.values()):      # 遍历投资组合中的每个持仓项
	    # 使用log.info打印每个持仓的详细信息,格式化字符串,输出持仓的股票名称、数量、市值、盈利百分比和建仓时间
	    log.info("当前持仓:{0}, 数量:{1}, 市值:{2}, 盈利:{3}%, 建仓时间:{4}".format(get_name(position.security), position.total_amount, round(position.value,0), round((position.value-(position.avg_cost*position.total_amount))/(position.avg_cost*position.total_amount)*100,1), position.init_time))
	log.info('#########################################################################################\n\n')

开平仓关键函数解锁后查看:

# 交易函数,用于调整持仓
def adjust_position(context, buy_stocks):
	for stock in context.portfolio.positions:      # 遍历当前投资组合中的所有持仓股票
		current_data = get_current_data()          # 获取当前的股票数据
		nosell_1 = context.portfolio.positions[stock].price >= current_data[stock].high_limit   # 检查是否因为涨停而不能卖出
		sell_2 = stock not in buy_stocks    # 检查该股票是否不在买入列表中
		if sell_2 and not nosell_1:     # 如果股票不在买入列表中且不是因为涨停而不能卖出,则卖出该股票
			log.info("调出平仓:[%s]" % (stock)) 
			position = context.portfolio.positions[stock]     # 获取该股票的持仓信息
			close_position(position)      # 调用close_position函数尝试卖出该股票
		else:
			log.info("已持仓,本次不买入:[%s]" % (stock))     # 如果股票仍然在买入列表中或因涨停不能卖出,则不进行操作
	# 根据股票数量分仓,此处只根据可用金额平均分配购买,不能保证每个仓位平均分配
	position_count = len(context.portfolio.positions)
	if g.buy_stock_count > position_count:     # 如果当前持仓数量少于预设的股票数量
		value = context.portfolio.cash / (g.buy_stock_count - position_count)     # 计算每只股票的购买价值
		for stock in buy_stocks:        # 遍历买入列表中的股票
			if stock not in context.portfolio.positions:     # 如果该股票不在当前持仓中
				if open_position(stock, value):      # 调用open_position函数尝试买入该股票
					if len(context.portfolio.positions) == g.buy_stock_count:     # 如果买入成功且持仓数量达到预设的股票数量,则停止买入
						break

# 通过代码返回股票名称
def get_name(stk):
    return get_security_info(stk).display_name+':'+stk[:6]  # get_security_info 可以获取股票等的信息,包括代码,中文名称等

# 开盘前运行函数
def before_market_open(context):
    g.check_out_lists = []    # 清空前一个交易日的股票池列表
    current_data = get_current_data()    # 获取当前的交易数据
    check_date = context.previous_date - datetime.timedelta(days=200)    # 设置查询日期为前一个交易日减去200天,用于获取足够历史数据的证券列表
    all_stocks = list(get_all_securities(date=check_date).index)    # 获取指定日期的所有证券列表
    # 过滤掉不符合条件的股票:创业板、ST股、停牌、当日涨停或跌停的股票
    all_stocks = [stock for stock in all_stocks if not (
            (current_data[stock].day_open == current_data[stock].high_limit) or  # 涨停开盘
            (current_data[stock].day_open == current_data[stock].low_limit) or  # 跌停开盘
            current_data[stock].paused or  # 停牌
            current_data[stock].is_st or  # ST
            ('ST' in current_data[stock].name) or
            ('*' in current_data[stock].name) or
            ('退' in current_data[stock].name) or
            (stock.startswith('30')) or  # 创业
            (stock.startswith('68')) or  # 科创
            (stock.startswith('8')) or  # 北交
            (stock.startswith('4'))   # 北交
    )]
    # 构建查询对象,用于筛选符合条件的股票
    q = query(
        valuation.code, valuation.market_cap, valuation.pe_ratio, income.total_operating_revenue
        ).filter(
        valuation.pb_ratio < 1,    # 市净率小于1
        cash_flow.subtotal_operate_cash_inflow > 1e6,  # 经营活动产生的现金流量净额大于1亿
        indicator.adjusted_profit > 1e6,    # 调整后的净利润大于1亿
        indicator.roa > 0.15,    # 资产回报率大于0.15
        indicator.inc_net_profit_year_on_year > 0,    # 净利润同比增长大于0
    	valuation.code.in_(all_stocks)  # 股票代码在过滤后的列表中
    	).order_by(
    	indicator.roa.desc()  # 按资产回报率降序排列
    ).limit(
    	g.buy_stock_count * 3  # 限制查询结果数量为预设买入股票数量的3倍
    )
    check_out_lists = list(get_fundamentals(q).code)      # 执行查询并获取符合条件的股票列表
    check_out_lists = check_out_lists[:g.buy_stock_count]  # 取需要的股票数量,即预设买入股票数量
    g.check_out_lists = check_out_lists    # 更新全局变量check_out_lists为新的股票池
    log.info("今日股票池:%s" % g.check_out_lists)      # 记录今日股票池到日志


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