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
2323 多因子小市值周轮动涨停管理策略、wywy1995大神机器学习策略年化提升版.py » 轻知量化 QMT、PTrade、聚宽策略分享交流平台

2323 多因子小市值周轮动涨停管理策略、wywy1995大神机器学习策略年化提升版.py

多因子小市值周轮动涨停管理策略

本策略基于多因子模型,结合小市值轮动和涨停板管理逻辑,通过筛选质量因子优异、市值较小的股票,并在每周调仓时动态管理持仓。策略的核心思想是捕捉小市值股票的成长潜力,同时通过涨停板监控控制短期风险。

2423 多因子小市值周轮动涨停管理策略、wywy1995大神机器学习策略年化提升版.py

策略核心逻辑

  1. 多因子选股模型

    • 因子选择

      • 不复权价格(反映短期市场情绪)

      • 成本费用利润率(衡量企业盈利能力)

      • 存货周转率(评估运营效率)

    • 线性加权:通过历史回测优化的权重(-6.12e-5, -0.0025, -2.19e-6)对三因子进行加权,生成综合评分。

    • 筛选规则

      • 选取评分最高的前10%股票,剔除每股收益为负的个股。

      • 按流通市值升序排序,优先选择小市值标的。

  2. 动态调仓机制

    • 周度调仓:每周一开盘时,卖出非目标股(保留昨日涨停股),买入新筛选的股票。

    • 市值控制:持仓数量固定为10只,分散配置小市值标的。

    • 尾盘风控

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

      • 早盘9:26和下午13:00补仓,确保资金利用率。

  3. 风险控制

    • 硬性过滤

      • 排除科创板、北交所、ST股、上市未满375天次新股。

      • 剔除停牌、涨停/跌停股。

    • 涨停监控:对持仓中昨日涨停的股票进行盯盘,破板即卖。

    • 防重复买入:标记当周已操作股票,避免短期重复交易。


策略特点

  • 因子驱动:结合质量类与市场类因子,兼顾基本面与技术面。

  • 小市值偏好:通过流通市值排序,捕捉小盘股超额收益。

  • 高频调仓:周度主调仓+日度辅助交易,平衡交易频率与冲击成本。

  • 自动化风控:涨停板管理与实时持仓监控,降低极端行情风险。


适用场景

  • 市场环境:适用于震荡市或结构性行情,小盘股活跃阶段表现更佳。

  • 资金规模:适合中小资金(1亿以内),小市值策略容量有限。

  • 投资者类型:适合追求相对收益、能承受中高波动的量化投资者。


风险提示

  1. 小市值风险:小盘股流动性较低,极端行情下易出现大幅波动。

  2. 因子失效:若市场风格转向大盘蓝筹,策略可能阶段性跑输基准。

  3. 涨停板依赖:涨停股次日溢价效应不稳定,需动态跟踪市场情绪。

  4. 数据依赖:依赖财务数据时效性,定期报告期可能存在信息滞后。


代码结构

模块 函数 功能说明
初始化 initialize 设定基准、交易参数,定义全局变量,绑定定时任务
数据准备 prepare_stock_list 更新持仓列表,识别昨日涨停股,预加载当日目标股池
核心选股 get_stock_list 多因子评分、市值排序、基本面过滤
周度调仓 weekly_adjustment 主调仓逻辑,卖出非目标股,买入新标的,推送交易信号
日常交易 trade_morning/afternoon 早盘补仓、尾盘涨停监控
风控模块 check_limit_up 检查涨停股状态,破板则卖出
过滤器系列 filter_* 6类过滤规则(停牌/ST/次新/涨跌停等)
交易执行 buy_security 按等金额分配买入目标股,避免单一个股过度集中

优化建议

  1. 因子迭代

    • 引入动量因子(如20日收益率)或波动率因子,提升组合弹性。

    • 测试换手率、机构持股比例等另类因子,优化评分模型。

  2. 仓位管理

    • 动态调整持仓数量(如根据市场波动率缩放)。

    • 加入止损机制(如个股回撤超10%强制平仓)。

  3. 交易优化

    • 使用VWAP/TWAP算法拆单,降低冲击成本。

    • 结合Level2盘口数据,优化涨停板判断逻辑。

  4. 风控增强

    • 设置最大回撤阈值,触发后自动降仓。

    • 监控市场广度指标(如涨跌停家数),极端市况下切换保守模式。


总结

本策略通过多因子模型精选小市值标的,结合周频调仓与实时风控,在控制风险的前提下捕捉小盘股超额收益。策略逻辑清晰,模块化设计便于迭代,适合作为量化多头策略的核心组件。实盘前需重点测试因子稳定性与小市值流动性风险。

1. 去掉了创业板

因为我是新手,今年才开的证券账号,所以没有创业板的权限,所以再股票过滤中,去掉 3开头的创业板股票。

2.涨停打开卖出后,立即换股买入

原策略涨停打开是会卖出的,卖出后资金就空闲到下周一,再次买入新的股票,再这周中间资金是闲置的。我增加 卖出后立即买入的代码

3.周一买入失败后,第二天继续买入

再实盘的第二周就出现,一个股票开盘后2分钟就涨停,导致聚宽的模拟交易和我本人的实盘中,都未能买入。因为实盘是有时间延迟的(聚宽中普通用户的模拟交易估计有1分钟左右的延迟),没办法。所以我就加入第二天继续买入,和将买入时间提早到9:26的逻辑。

4.每天发邮件总结

因为不可能时刻盯着,聚宽的模拟交易也没有APP可以参考,就增加邮件发送代码,每天收盘后,把今天的情况发到邮箱。

回测结果

最终再不调整策略的参数和因子的情况下,策略的年化收益率从52%->87%,提升了35pt

2423 多因子小市值周轮动涨停管理策略、wywy1995大神机器学习策略年化提升版.py

策略代码实现:

# 风险及免责提示:该策略由聚宽用户在聚宽社区分享,仅供学习交流使用。
# 标题:wywy1995大神机器学习策略年化提升35pt


#导入函数库
from jqdata import *            # 导入聚宽量化平台的函数库
from jqfactor import *          # 导入聚宽因子库
import numpy as np              # 导入numpy库,用于数据处理
import pandas as pd             # 导入pandas库,用于数据处理
import statsmodels.api as sm    # 导入statsmodels库,用于统计分析

# 初始化函数,在策略开始前运行一次
def initialize(context):
    # 设定沪深300指数为策略的基准
    set_benchmark('000300.XSHG')
    # 使用真实价格进行交易,即不使用前复权价格
    set_option('use_real_price', True)
    # 打开避免未来数据的选项,防止策略中出现未来函数
    set_option("avoid_future_data", True)
    # 设置滑点为0,即假设交易没有滑点
    set_slippage(FixedSlippage(0))
    # 设置交易成本,包括印花税和佣金
    set_order_cost(OrderCost(open_tax=0, close_tax=0.001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5),type='stock')
    # 设置日志级别,只记录error级别的日志,避免太多无用信息
    log.set_level('order', 'error')
    #初始化全局变量
    g.stock_num = 10  # 策略持仓的股票数量
    g.hold_list = []  # 当前持仓的全部股票列表
    g.target_list = []  # 目标股票列表,可能用于调仓
    g.yesterday_HL_list = []  # 记录昨日涨停的股票
    g.not_buy_again = []  # 记录不再买入的股票
    g.factor_list = [
        'price_no_fq', # 不复权价格因子,用于技术分析
        'total_profit_to_cost_ratio',  # 成本费用利润率,质量类因子
        'inventory_turnover_rate'  # 存货周转率,质量类因子
        ]
    # 设置交易运行时间
    run_daily(prepare_stock_list, time='9:05', reference_security='000300.XSHG') # 设置交易运行时间,每天9:05运行prepare_stock_list函数
    # 为了实盘的时候,买掉股票后可以立马成交,从而可以买入新股票,所以选择9:24
    run_weekly(weekly_adjustment, 1, time='9:30', reference_security='000300.XSHG') # 每周第一个交易日9:30运行weekly_adjustment函数,用于周级别的调仓
    run_daily(trade_morning,time='9:26',reference_security='000300.XSHG') # 每个交易日9:26运行trade_morning函数,用于早盘交易
    run_daily(trade_afternoon, time='13:00', reference_security='000300.XSHG')  # 每个交易日13:00运行trade_afternoon函数,用于下午检查持仓中的涨停股是否需要卖出
    #run_daily(print_position_info, time='15:10', reference_security='000300.XSHG')

#1-1 准备股票池
def prepare_stock_list(context):
    #获取已持有列表
    g.hold_list= []   # 初始化持仓列表,g.hold_list 用于存储当前持仓的股票代码
    for position in list(context.portfolio.positions.values()):   # 遍历当前持仓的每个持仓价值
        stock = position.security    # 获取股票代码
        g.hold_list.append(stock)    # 将股票代码添加到持仓列表
    #获取昨日涨停列表
    if g.hold_list != []:            # 如果当前持仓列表不为空,即存在持仓
        # 获取每只股票的昨日收盘价和涨停价
        df = get_price(g.hold_list, end_date=context.previous_date, frequency='daily', fields=['close','high_limit'], count=1, panel=False, fill_paused=False)
        df = df[df['close'] == df['high_limit']]  # 筛选出昨日收盘价等于涨停价的股票,即昨日涨停的股票
        g.yesterday_HL_list = list(df.code)       # 更新昨日涨停列表,存储为股票代码列表
    else:
        g.yesterday_HL_list = []                  # 如果没有持仓,昨日涨停列表为空
    g.target_list = get_stock_list(context)       # 提早准备今日股票list

关键函数选股模块,解锁后查看:

#1-3 整体调整持仓
def weekly_adjustment(context):
    g.not_buy_again = []    # 清空不再买入的股票列表
    target_list = g.target_list    # 从全局变量中获取目标股票列表
    # 获取应买入列表,并确保不超过最大持仓数,取目标列表的前 g.stock_num 个股票,或者列表本身的全部(如果目标列表股票数量不足 g.stock_num)
    target_list = target_list[:min(g.stock_num, len(target_list))]
    #调仓卖出
    for stock in g.hold_list:      # 遍历当前持仓列表 g.hold_list
        if (stock not in target_list) and (stock not in g.yesterday_HL_list):  # 如果股票不在目标列表中,并且不是昨天涨停的股票
            log.info("卖出[%s]" % (stock))      # 记录卖出操作
            position = context.portfolio.positions[stock]      # 获取该股票的持仓信息
            close_position(position)    # 卖出该股票的持仓
        else:
            log.info("持有[%s]" % (stock))      # 如果股票在目标列表中或昨天涨停,记录持有操作
    # 调仓买入
    buy_security(context,target_list)
    #记录已买入的股票
    for position in list(context.portfolio.positions.values()):    # 遍历当前所有持仓
        stock = position.security    # 获取股票代码
        g.not_buy_again.append(stock)  # 将买入的股票添加到不再买入列表中
    #微信通知 
    if context.run_params == 'sim_trade':  # 如果策略是在模拟交易环境中运行,发送微信通知
        # 构造微信通知文本,卖出股票和买入股票的列表,使用 set 运算来确定
        weixin_message_text = '\n卖出:'.join(list(set(g.hold_list).difference(set(target_list))))\
            +'\n买入:'.join(list(set(target_list).difference(set(g.hold_list))))
        send_message(weixin_message_text,channel='weixin')    # 发送微信消息
        #send_email(list(set(g.hold_list).difference(set(target_list))),list(set(target_list).difference(set(g.hold_list))))

#1-4 调整昨日涨停股票
def check_limit_up(context):
    now_time = context.current_dt   # 获取当前时间
    if g.yesterday_HL_list != []:   # 检查昨日涨停股票列表是否为空
        #对昨日涨停股票观察到尾盘如不涨停则提前卖出,如果涨停即使不在应买入列表仍暂时持有
        for stock in g.yesterday_HL_list:  # 遍历昨日涨停股票列表
            # 获取当前股票的最新价格和涨停价
            current_data = get_price(stock, end_date=now_time, frequency='1m', fields=['close','high_limit'], skip_paused=False, fq='pre', count=1, panel=False, fill_paused=True)
            if current_data.iloc[0,0] <    current_data.iloc[0,1]:   # 检查当前价格是否小于涨停价,即判断今日是否打开涨停
                log.info("[%s]涨停打开,卖出" % (stock))             # 如果打开涨停,则记录卖出信息
                position = context.portfolio.positions[stock]        # 获取股票当前的持仓情况
                close_position(position)                             # 卖出股票持仓
            else:
                log.info("[%s]涨停,继续持有" % (stock))             # 如果仍然涨停,则记录继续持有的信息

#1-5 如果昨天有股票卖出或者买入失败,剩余的金额今天早上买入
def check_remain_amount(context):
    g.hold_list= []    # 清空当前持仓列表
    for position in list(context.portfolio.positions.values()):    # 遍历当前策略的持仓
        stock = position.security     # 获取每只股票的股票代码
        g.hold_list.append(stock)        # 将股票代码添加到持仓列表中
    if len(g.hold_list) < g.stock_num:  # 如果当前持仓的股票数量小于预设的最大持仓数 g.stock_num
        print('有余额可用,多买入一只'+str(context.portfolio.cash))    # 打印信息,提示有余额可用,并显示当前的现金余额
        target_list = g.target_list     # 获取目标股票列表
        #剔除本周一曾买入的股票,不再买入
        target_list = filter_not_buy_again(target_list)      # 从目标列表中剔除本周一曾尝试买入但失败的股票
        target_list = target_list[:min(g.stock_num, len(target_list))]        # 确定最终要购买的股票列表,数量不超过预设的最大持仓数和目标列表的长度
        buy_security(context,target_list)   # 调用 buy_security 函数来执行买入操作

# 1-6 下午检查交易
def trade_afternoon(context):
    check_limit_up(context)   # 调用 check_limit_up 函数,检查持仓中是否有涨停股票需要处理
    check_remain_amount(context)  # 调用 check_remain_amount 函数,检查是否有剩余资金可用于购买股票

# 用于每个交易日早上执行的操作
def trade_morning(context):
    # 周一不执行,因为周一主交易代码,这里只是补充,0代表周一  
    if context.current_dt.weekday() in (2,3,4,5):   # 这里 if 语句的条件是检查是否为周二至周五,因为周一的主要交易代码已经执行,这里不需要重复执行
        check_remain_amount(context)  # 如果不是周一,则调用 check_remain_amount 函数,检查是否有剩余资金可用于购买股票

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

# 2-2 过滤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]

# 2-3 过滤科创北交股票
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

# 2-4 过滤涨停的股票
def filter_limitup_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 last_prices[stock][-1] < current_data[stock].high_limit]

# 2-5 过滤跌停的股票
def filter_limitdown_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 last_prices[stock][-1] > current_data[stock].low_limit]

# 2-6 过滤次新股
def filter_new_stock(context,stock_list):
    yesterday = context.previous_date  # 获取昨日日期
    # 使用列表推导式,返回上市时间超过375日的股票列表
    return [stock for stock in stock_list if not yesterday - get_security_info(stock).start_date < datetime.timedelta(days=375)]

# 2-7 删除本周一买入的股票
def filter_not_buy_again(stock_list):
    return [stock for stock in stock_list if stock not in g.not_buy_again]

# 3-1 交易模块-自定义下单
def order_target_value_(security, value):
    if value == 0:   # 检查目标价值是否为0,即是否需要清仓
        log.debug("Selling out %s" % (security)) # 日志记录清仓信息
    else:
        log.debug("Order %s to value %f" % (security, value)) # 日志记录下单到特定价值的信息
    return order_target_value(security, value)   # 调用聚宽平台的order_target_value函数执行交易

# 3-2 交易模块-开仓
def open_position(security, value):
    order = order_target_value_(security, value)  # 尝试对指定股票进行下单到特定价值
    if order != None and order.filled > 0:        # 检查订单是否创建成功且有成交
        return True  # 开仓成功
    return False  # 开仓失败

# 3-3 交易模块-平仓
def close_position(position):
    security = position.security  # 获取持仓的股票代码
    order = order_target_value_(security, 0)  # 下单清仓 ;可能会因停牌失败
    if order != None:  # 检查订单是否创建成功
        if order.status == OrderStatus.held and order.filled == order.amount:  # 检查订单状态是否为全部成交且订单数量等于下单数量
            return True  # 平仓成功
    return False  # 平仓失败

# 3-4 用于执行买入操作
def buy_security(context,target_list):
    #调仓买入
    position_count = len(context.portfolio.positions)    # 获取当前持仓的数量
    target_num = len(target_list)    # 获取目标股票列表的长度
    if target_num > position_count:    # 如果目标股票的数量大于当前持仓的数量,表示需要增加持仓
        # 计算每个股票应该分配的资金。这里将总现金除以需要买入的股票数量(target_num - position_count)
        value = context.portfolio.cash / (target_num - position_count)
        for stock in target_list:   # 遍历目标股票列表
            if stock not in context.portfolio.positions:     # 检查该股票是否已经在持仓中
                if open_position(stock, value):    # 尝试开仓买入该股票,传入股票代码和计划投入的资金
                    log.info("买入[%s](%s元)" % (stock,value))     # 如果买入成功,记录此次操作
                    g.not_buy_again.append(stock)       # 将该股票添加到不再买入清单中,避免在后续操作中重复买入
                    if len(context.portfolio.positions) == target_num:     # 如果当前持仓数量已经达到目标数量,停止买入更多的股票
                        break

#4-1 打印每日持仓信息
def print_position_info(context):
    #打印当天成交记录
    trades = get_trades()  # 获取当天所有成交记录
    for _trade in trades.values():  # 遍历成交记录字典的值(即成交记录对象)
        print('成交记录:'+str(_trade))
    #打印账户信息
    for position in list(context.portfolio.positions.values()):  # 遍历账户中所有持仓的股票信息
        securities=position.security  # 获取股票代码
        cost=position.avg_cost        # 获取股票当前持仓成本
        price=position.price          # 获取股票最新价
        ret=100*(price/cost-1)        # 计算股票收益率
        value=position.value          # 获取股票市值
        amount=position.total_amount  # 获取股票持股数量
        print('代码:{}'.format(securities))
        print('成本价:{}'.format(format(cost,'.2f')))
        print('现价:{}'.format(price))
        print('收益率:{}%'.format(format(ret,'.2f')))
        print('持仓(股):{}'.format(amount))
        print('市值:{}'.format(format(value,'.2f')))
        print('———————————————————————————————————')
    print('———————————————————————————————————————分割线————————————————————————————————————————')

# 4-2 发送邮件通知
def send_email(sell_list,buy_list):
    # 导入所需的库
    import smtplib 
    from email.mime.text import MIMEText 
    from email.header import Header
    
    sender = 'ch@sina.com'   # 发件人的邮箱地址
    receiver = 'si@sina.com' # 收件人的邮箱地址(注:测试中发送其他邮箱会提示错误) 
    smtpserver = 'smtp.sina.com'  # SMTP服务器地址
    username = 'ch3@sina.com' # 发件人的邮箱用户名
    password = 'xx'     # 发件人的邮箱密码
    subject = '量化交易信号'   # 邮件主题
    message = sell_list.to_json()+'\n'+buy_list.to_json  # 邮件内容,将卖出和买入的股票列表转换为JSON格式
    msg = MIMEText(message,'plain','utf-8')  # 创建一个MIMEText邮件对象,指定邮件内容、类型和编码
    msg['Subject'] = Header(subject, 'utf-8') # 创建邮件主题,并指定编码
    msg['from'] = sender # 设置发件人
    # 创建SMTP_SSL对象并连接到SMTP服务器
    smtp = smtplib.SMTP_SSL(smtpserver, 465) 
    try: 
        smtp.login(username, password)  # 登录邮箱
        smtp.sendmail(sender, receiver, msg.as_string())  # 发送邮件
        log.info('邮件发送成功')   # 如果发送成功,记录日志
    except smtplib.SMTPException: 
        # 如果发送失败,记录日志并打印错误信息
        log.info('邮件发送失败')  
        print("Error: 无法发送邮件")
    smtp.quit()    # 关闭SMTP连接
    
def after_code_changed(context):
    # 取消所有定时运行
    unschedule_all()
    # 设置交易运行时间
    run_daily(prepare_stock_list, time='9:05', reference_security='000300.XSHG')
    # 为了实盘的时候,买掉股票后可以立马成交,从而可以买入新股票,所以选择9:24卖出,26买入
    run_weekly(weekly_adjustment, 1, time='9:30', reference_security='000300.XSHG')
    run_daily(trade_morning,time='9:26',reference_security='000300.XSHG')
    run_daily(trade_afternoon, time='13:00', reference_security='000300.XSHG') #检查持仓中的涨停股是否需要卖出
    #run_daily(print_position_info, time='15:10', reference_security='000300.XSHG')
2025-02-21
⚠️
本站资源大多来自网络,仅供网友学习交流,未经作者或上传书面授权,请勿作他用。
站长 vx: xiangyin615 或者 留言反馈 ,我们将尽快处理。
Notice: When you of the legal rights be violate, please stir to vx: xiangyin615
个人中心
购物车
优惠劵
搜索