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
2335 小市值市场轮动版 5年12倍 » 轻知量化 QMT、PTrade、聚宽策略分享交流平台

2335 小市值市场轮动版 5年12倍


# 导入聚宽(JoinQuant)数据模块,提供获取股票、基金、期货等金融产品数据的接口
from jqdata import *
# 导入jqfactor库中的get_factor_values函数,用于获取因子数据
from jqfactor import get_factor_values
# 导入jqlib库中的技术分析模块,提供各种技术分析指标的计算
from jqlib.technical_analysis import *
# 导入NumPy库,一个广泛使用的科学计算库,提供了大量的数学函数和操作
import numpy as np
# 导入Pandas库,一个强大的数据处理和分析工具库,提供了DataFrame和Series等数据结构
import pandas as pd
# 导入statsmodels库,一个统计模型库,用于进行统计分析和建模
import statsmodels.api as sm
# 导入datetime库,提供日期和时间处理的功能
import datetime as dt

#初始化函数 
def initialize(context):
    # 设定基准
    set_benchmark('000905.XSHG')
    # 用真实价格交易
    set_option('use_real_price', True)
    # 打开防未来函数
    set_option("avoid_future_data", True)
    # 交易量限制
    set_option('order_volume_ratio', 1)
    # 将滑点设置为0,不同滑点影响可在归因分析中查看
    set_slippage(PriceRelatedSlippage(0.002),type='stock')
    # 设置交易成本万一免五
    set_order_cost(OrderCost(open_tax=0, close_tax=0.001, open_commission=0.0001, close_commission=0.0001, close_today_commission=0, min_commission=0.1),type='fund')
    # 过滤order中低于error级别的日志
    log.set_level('order', 'error')
    #初始化全局变量
    g.stock_num = 9
    g.limit_up_list = [] #记录持仓中涨停的股票
    g.hold_list = [] #当前持仓的全部股票
    g.history_hold_list = [] #过去一段时间内持仓过的股票
    g.not_buy_again_list = [] #最近买过且涨停过的股票一段时间内不再买入
    g.limit_days = 10 #不再买入的时间段天数
    g.target_list = [] #开盘前预操作股票池
    # 设置策略每天开盘前9:05运行 prepare_stock_list 函数,用于准备股票池
    run_daily(prepare_stock_list, time='9:05', reference_security='000300.XSHG')
    # 设置策略每周的第一个交易日开盘前9:30运行 weekly_adjustment 函数,用于进行周度调仓
    run_weekly(weekly_adjustment, weekday=1, time='9:30', reference_security='000300.XSHG')
    # 设置策略每天14:00运行 check_limit_up 函数,用于检查持仓中是否有涨停股,是否需要卖出
    run_daily(check_limit_up, time='14:00', reference_security='000300.XSHG') #检查持仓中的涨停股是否需要卖出
    # 设置策略每天收盘后15:10运行 print_position_info 函数,用于打印持仓信息
    run_daily(print_position_info, time='15:10', reference_security='000300.XSHG')

def after_code_changed(context):
    unschedule_all()      # 取消所有之前设置的定时运行任务
    # 重新设置策略每天开盘前9:05运行 prepare_stock_list 函数,用于准备股票池
    run_daily(prepare_stock_list, time='9:05', reference_security='000300.XSHG')
    # 重新设置策略每周的第一个交易日开盘前9:30运行 weekly_adjustment 函数,用于进行周度调仓
    run_weekly(weekly_adjustment, weekday=1, time='9:30', reference_security='000300.XSHG')
    # 重新设置策略每天14:00运行 check_limit_up 函数,用于检查持仓中是否有涨停股,是否需要卖出
    run_daily(check_limit_up, time='14:00', reference_security='000300.XSHG')
    # 重新设置策略每天收盘后15:10运行 print_position_info 函数,用于打印持仓信息
    run_daily(print_position_info, time='15:10', reference_security='000300.XSHG')

关键函数解锁后查看:


# 1-3 准备股票池
def prepare_stock_list(context):
    yesterday = context.previous_date  # 获取前一个交易日的日期
    #获取已持有列表
    g.hold_list= []
    for position in list(context.portfolio.positions.values()):
        # 提取每只股票的代码
        stock = position.security
        # 将股票代码添加到持有列表中
        g.hold_list.append(stock)
    # 将当前持有的股票列表添加到历史持有列表的记录中
    g.history_hold_list.append(g.hold_list)
    
    # 如果历史持有列表的记录天数超过了设定的限制天数 g.limit_days
    # 则保留最近 g.limit_days 天的记录
    if len(g.history_hold_list) >= g.limit_days:
        g.history_hold_list = g.history_hold_list[-g.limit_days:]
    temp_set = set()   # 创建一个临时集合用于存储历史上曾经持有过的股票
    # 遍历历史持有列表中的每一个持有列表
    for hold_list in g.history_hold_list:
        # 将每个持有列表中的股票添加到临时集合中
        for stock in hold_list:
            temp_set.add(stock)
    # 将临时集合转换为列表,并赋值给全局变量 g.not_buy_again_list
    # 这个列表用于记录那些在历史中曾经持有过,短期内不再考虑买入的股票
    g.not_buy_again_list = list(temp_set)
    # 如果当前持有列表不为空,即持有至少一只股票
    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.high_limit_list
        g.high_limit_list = list(df.code)
    else:
        g.high_limit_list = []  # 如果当前没有持有任何股票,则涨停列表为空
    # 如果涨停列表不为空,则记录涨停股票的信息
    if len(g.high_limit_list):
        log.info("昨日涨停:[%s]" % (g.high_limit_list))
        
    '''
    # 每日潜池变化提醒
    get_stock_prepare_list = get_stock_list(context)
    sg_list = get_stock_prepare_list[0][:5]
    ms_list = get_stock_prepare_list[1][:5]
    peg_list = get_stock_prepare_list[2][:5]
    union_list = list(set(sg_list).union(set(ms_list)).union(set(peg_list)))
    q = query(valuation.code,valuation.circulating_market_cap).filter(valuation.code.in_(union_list)).order_by(valuation.circulating_market_cap.asc())
    df = get_fundamentals(q)
    get_prepare_list = list(df.code)
    for stock in get_prepare_list:
        stock_market_cap = get_factor_values(stock,'circulating_market_cap', end_date=yesterday, count=1)['circulating_market_cap'].iloc[0]
        stock_name = get_security_info(stock, date=yesterday).display_name
        log.info("潜力股:%s,代码:%s,流通市值:%f" ,stock_name, stock, stock_market_cap/100_000_000)
'''

# 1-5 执行周度调仓操作
def weekly_adjustment(context):
    # 获取股票池列表,该列表由不同策略筛选出的股票组成
    all_list = get_stock_list(context)
    # 从不同策略筛选出的股票中各取前5个作为初步买入候选
    sg_list = all_list[0][:5]  # 策略1选出的股票
    ms_list = all_list[1][:5]  # 策略2选出的股票
    peg_list = all_list[2][:5]  # 策略3选出的股票
    # 将三个策略选出的股票合并,并去重
    union_list = list(set(sg_list).union(set(ms_list)).union(set(peg_list)))
    # 查询合并后股票列表的流通市值,并按市值升序排列
    q = query(valuation.code,valuation.circulating_market_cap).filter(valuation.code.in_(union_list)).order_by(valuation.circulating_market_cap.asc())
    df = get_fundamentals(q)
    # 将查询结果的股票代码列表作为目标持仓列表
    g.target_list = list(df.code)
    # 过滤掉停牌的股票
    g.target_list = filter_paused_stock(g.target_list)
    # 过滤掉涨停的股票
    g.target_list = filter_limitup_stock(context, g.target_list)
    # 过滤掉跌停的股票
    g.target_list = filter_limitdown_stock(context, g.target_list)
    #过滤最近买过且涨停过的股票
    recent_limit_up_list = get_recent_limit_up_stock(context, g.target_list, g.limit_days)
    black_list = list(set(g.not_buy_again_list).intersection(set(recent_limit_up_list)))
    g.target_list = [stock for stock in g.target_list if stock not in black_list]
    # 根据设定的最大持仓数截取目标股票列表
    g.target_list = g.target_list[:min(g.stock_num, len(g.target_list))]
    #调仓卖出
    for stock in g.hold_list:
        if (stock not in g.target_list) and (stock not in g.high_limit_list):
            log.info("卖出[%s]" % (stock))
            position = context.portfolio.positions[stock]
            close_position(position)
        else:
            log.info("已持有[%s]" % (stock))
    # 执行调仓卖出操作
    position_count = len(context.portfolio.positions)
    target_num = len(g.target_list)
    if target_num > position_count:
        # 计算每只股票的买入金额
        value = context.portfolio.cash / (target_num - position_count)
        for stock in g.target_list:
            # 如果股票不在当前持仓中,尝试买入
            if context.portfolio.positions[stock].total_amount == 0:
                if open_position(stock, value):
                    # 如果买入后持仓数达到目标持仓数,停止买入
                    if len(context.portfolio.positions) == target_num:
                        break

#1-6 调整昨日涨停股票
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_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))    # 如果仍然涨停,则记录继续持有的信息

#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 获取最近N个交易日内有涨停的股票
def get_recent_limit_up_stock(context, stock_list, recent_days):
    stat_date = context.previous_date  # 获取前一个交易日的日期
    new_list = []  # 初始化一个空列表,用于存储筛选出的有涨停记录的股票代码
    for stock in stock_list:  # 遍历传入的股票列表
        # 获取每只股票在最近N个交易日的收盘价和涨停价
        df = get_price(stock, end_date=stat_date, frequency='daily', fields=['close','high_limit'], count=recent_days, panel=False, fill_paused=False)
        df = df[df['close'] == df['high_limit']]  # 筛选出收盘价等于涨停价的行,即股票发生涨停的交易日
        if len(df) > 0:             # 如果存在股票涨停的记录(即筛选后的 DataFrame 不为空)
            new_list.append(stock)  # 将该股票代码添加到筛选结果列表中
    return new_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_kcb_stock(context, stock_list):
    return [stock for stock in stock_list  if stock[0:3] != '688']

#2-7 过滤次新股
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-8 筛选热门行业成分股
def get_hot_industry_stock(context, count=10, number=24):
    # 获取前一个交易日的日期
    end_date = context.previous_date  # 获取昨日日期
    # 获取前 count+20 个交易日的日期列表,并取第一个日期
    by_date = get_trade_days(end_date=end_date, count=count+20)[0]
    # 获取在 count+20 个交易日之前就已经上市的所有股票列表
    stock_list = get_all_securities(date=by_date).index.tolist()
    # 获取这些股票在前 count+20 个交易日内的收盘价,并按时间、股票代码进行整理
    df_close = get_price(stock_list,end_date=end_date, count=count+20, fields='close', panel=False).pivot(index='time', values='close', columns='code')
    # 计算收盘价在过去 20 个交易日的均值,然后判断每个收盘价是否大于均值
    df_bias = df_close.iloc[20:] > df_close.rolling(20).mean().iloc[20:]
    # 获取申万一级行业分类
    df_industries = get_industries('sw_l1', date=end_date)
    # 将行业分类 DataFrame 的索引(行业代码)转换为列表
    df_industries['code'] = list(df_industries.index)
    # 初始化一个空的 DataFrame 用于存储每个行业的热门股票比例
    df = pd.DataFrame()
    # 获取所有股票代码的集合
    columns = set(df_bias.columns)
    for idx, row in df_industries.iterrows():  # 遍历每个行业分类
        # 获取该行业的成分股
        ind_stocks = set(get_industry_stocks(idx, date=end_date))
        # 找出在 df_bias 表中存在的成分股
        ind_avail_stocks = list(columns & ind_stocks) # 成分股 在df_bias表中存在的
        if ind_avail_stocks:
            # 计算该行业成分股收盘价大于过去 20 个交易日均值的百分比
            df[row['code']] = (100*(df_bias[ind_avail_stocks].sum(axis=1))/len(ind_avail_stocks)).astype(int)
    df.sort_index(ascending=False, inplace=True)  # 按行业热门股票比例降序排序
    # 选择前 count 行,计算这些行的均值,并降序排序
    sr = df.iloc[0:count,:].mean().sort_values(ascending=False, inplace=False)
    # 选择均值最高的前 number 个行业
    sr = list(sr[0:number].index)
    # 初始化一个空集合用于存储热门行业的成分股
    stocks_set = set()
    # 遍历选出的行业
    for s in sr:
        # 获取该行业的成分股
        ind_stocks = set(get_industry_stocks(s, date=end_date))
        # 将这些成分股添加到集合中
        stocks_set.update(ind_stocks)
    # 返回热门行业的成分股列表
    return list(stocks_set)

#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 adjust_position(context, buy_stocks, stock_num):
	for stock in context.portfolio.positions:              # 遍历当前账户持仓中的所有股票
		if stock not in buy_stocks:                        # 如果股票不在应买入列表中
			log.info("[%s]不在应买入列表中" % (stock))     # 日志记录不在买入列表中的股票
			position = context.portfolio.positions[stock]  # 获取股票的持仓信息
			close_position(position)                       # 执行平仓操作
		# 如果股票已经在持仓中
		else:
			log.info("[%s]已经持有无需重复买入" % (stock)) # 日志记录已持有的股票,无需重复买入

	position_count = len(context.portfolio.positions)      # 计算当前持仓的股票数量
	if stock_num > position_count:                         # 如果需要持有的股票数量大于当前持仓数量
		value = context.portfolio.cash / (stock_num - position_count)  # 计算每只股票应分配的资金
		for stock in buy_stocks:                           # 遍历应买入的股票列表
			if context.portfolio.positions[stock].total_amount == 0:   # 如果当前股票不在持仓中
				if open_position(stock, value):            # 执行开仓操作
					if len(context.portfolio.positions) == stock_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('———————————————————————————————————————分割线————————————————————————————————————————')
2025-02-21
⚠️
本站资源大多来自网络,仅供网友学习交流,未经作者或上传书面授权,请勿作他用。
站长 vx: xiangyin615 或者 留言反馈 ,我们将尽快处理。
Notice: When you of the legal rights be violate, please stir to vx: xiangyin615
个人中心
购物车
优惠劵
搜索