年化96%的小市值策略的选股逻辑源码来了

之前写了一篇文章是《分享一个年化96%的小市值策略》 代码是基于聚宽平台写的,后期有粉丝朋友想让花姐改成基于xtquant+MiniQMT版本,这不就来了。

这里先回顾下这个策略:

选股逻辑

1、选取中小板 399101 指数成份股作为初始池

2、剔除里面的ST股票、次新股、停牌的股、涨停跌停股、北交所和科创板的股票

3、选取流通市值最小的前100个股票

4、再选取每股收益大于0的保留前100个

5、最后在这100只中,每个行业选出一只股票,然后取市值最小的股票N个用于交易

调仓逻辑

调仓逻辑也很简单,每周二会按照选股逻辑选出6个股票,然后卖出之前持有的,买入最新的6个股票。当然如果持有的股票涨停了就会继续持有,把剩余的仓位买入新调入的股票。

止盈止损

每天在10点去根据条件判断止盈止损,这里我设置了几个条件 1、持有的股票收益>100%止盈 2、跌破成本的91%就无条件止损 3、如果中小板 399101 指数平均跌幅 > 5%,全部清仓

接下来就是这篇文章的重头戏了

选股逻辑源码

量化系统框架还没搭建好的可以先参考这篇教程,后面的代码都是基于这套框架来运行的,是必备条件。

从0到1打造一套小白也能跑得起来的量化框架_图文教程

1、获取中小综指成份股

这个还是很方便的,直接调用xtquant里的函数就可以获取了代码如下:

python 复制代码
initial_list = xtdata.get_stock_list_in_sector('中小综指') #获取指数成份股

返回值是一个包含股票代码的列表['000001.SZ', '000002.SZ', '000063.SZ', '000069.SZ',...]

2、排除 4、8、68开头的股票

这里也是基本的列表操作

python 复制代码
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

3、过滤次新股 上市不足1年的直接去掉

python 复制代码
def filter_new_stock(stock_list,instrument_detail_list):
    out_list =[]
    data_list = instrument_detail_list
    for stock in stock_list:
        OpenDate = data_list[stock]['OpenDate']
        date_obj = datetime.strptime(OpenDate, "%Y%m%d")
        # 获取当前日期(不包含时间部分)
        today = datetime.today().date()
        # 只取日期部分比较
        date_obj_only = date_obj.date()

        # 计算差值
        delta_days = (today - date_obj_only).days
        if delta_days>=365:
            out_list.append(stock)

    return out_list

4、过滤ST股票 这里在接口文档没有找到可以直接获取ST股票列表的接口,所以直接根据股票名称来判断是不是ST股

python 复制代码
def filter_st_stock(stock_list,instrument_detail_list):
    out_list =[]
    data_list = instrument_detail_list
    for stock in stock_list:
        InstrumentName = data_list[stock]['InstrumentName']
        if 'st' not in InstrumentName \
        and 'ST' not in InstrumentName \
        and '*' not in InstrumentName  \
        and '退' not in InstrumentName:
            out_list.append(stock)
    return out_list

5、过滤停牌股

xtquant接口中InstrumentStatus表示合约停牌状态,<=0:正常交易

python 复制代码
def filter_paused_stock(stock_list,instrument_detail_list):
    out_list =[]
    data_list = instrument_detail_list
    for stock in stock_list:
        InstrumentStatus = data_list[stock]['InstrumentStatus']
        if InstrumentStatus<=0:
            out_list.append(stock)
    return out_list

6、获取流通市值最小的200个股票 这个方法里我同时过滤了涨跌停的股票

python 复制代码
def get_ltsz_top200(stock_list,instrument_detail_list):
    # 排除 涨停 跌停 然后获取流通市值最小的200个股票
    # 获取昨天的日期
    yesterday = datetime.today() - timedelta(days=10)
    data_list = instrument_detail_list
    temp_list=[]
    # 格式化为 YYYYMMDD
    yesterday_str = yesterday.strftime("%Y%m%d")
    for i in range(0,len(stock_list),500):
        stock_list_window = stock_list[i:i+500]
        xtdata.download_history_data2(stock_list=stock_list_window,start_time=yesterday_str,period='1d',incrementally=True)
        
    
        hq_list = xtdata.get_market_data_ex(['open','high','low','close','volume','amount','preClose'],
                                             stock_list_window, period='1d', count=1, 
                                             dividend_type='front',start_time=yesterday_str,fill_data=False)
        for stock in stock_list_window:
            FloatVolume = data_list[stock]['FloatVolume'] #流通股本
            UpStopPrice = data_list[stock]['UpStopPrice'] # 涨停价
            DownStopPrice = data_list[stock]['DownStopPrice'] # 跌停价
            hq = hq_list[stock]
            if len(hq)>0:
                close_price = hq.iloc[-1]['close']
                if close_price>DownStopPrice and close_price<UpStopPrice:
                    temp_list.append({"stock":stock,"float_amount":close_price*FloatVolume})
        
    df_temp = pd.DataFrame(temp_list)
    top_200_smallest = df_temp.nsmallest(200, 'float_amount')
    # print(top_200_smallest)
    return top_200_smallest

7、基本每股收益>0的股票

一开始下载财务数据的时候我用的是download_financial_data接口,一直没响应,有人知道是什么原因吗?后来改成download_financial_data2就没问题了。

python 复制代码
def get_eps_up_0(stock_list):
    out_list =[]
    # 取数据前请确保已下载所需要的财务数据
    xtdata.download_financial_data2(stock_list, table_list=["Income"])
    data_list = xtdata.get_financial_data(stock_list,table_list=["Income"])
    for stock in stock_list:
        data = data_list[stock]['Income']
        if len(data)>0:
            s_fa_eps_basic = data.iloc[-1]['s_fa_eps_basic'] #基本每股收益
            if s_fa_eps_basic>0:
                out_list.append(stock)
                
    return out_list

8、获取每个行业市值最小的股票

这里分了2步写的,首先是获取股票对应的申万二级行业

python 复制代码
def get_stock_industry(stock_list):
    sector_list = xtdata.get_sector_list()
    sw2_list = [s for s in sector_list if s[:3].lower()=='sw2' and '加权' not in s] #获取申万二级行业
    result_list =[]
    for sw2 in sw2_list:
        s_list = xtdata.get_stock_list_in_sector(sw2)
        for stock in stock_list:
            if stock in s_list:
                result_list.append({'stock':stock,'industry':sw2})
    
    df_industry = pd.DataFrame(result_list)
    # df_industry.to_csv("个股行业.csv")
    return df_industry

接下来就是把每个行业市值最小的股票取出来

python 复制代码
def get_top_1_industry(df_industry,df_ltsz):
    df_merge = pd.merge(df_industry,df_ltsz,how='inner',on='stock')
    result  = df_merge.loc[df_merge.groupby('industry')['float_amount'].idxmin()]
    return result

最后我们用result = df_top1_industry.nsmallest(stock_count, 'float_amount')获取市值最小的N个股票用于交易

以上就是整个策略的选股逻辑核心代码了。

相关推荐
Victor35627 分钟前
Redis(28)Redis的持久化文件可以跨平台使用吗?
后端
Victor35629 分钟前
Redis(29)如何手动触发Redis的RDB快照?
后端
乘乘凉2 小时前
Python中函数的闭包和装饰器
前端·数据库·python
爱隐身的官人6 小时前
爬虫基础学习-爬取网页项目(二)
前端·爬虫·python·学习
快乐就是哈哈哈8 小时前
《一文带你搞懂ElasticSearch:从零到上手搜索引擎》
后端·elasticsearch
刘恒1234567898 小时前
Pycharm
ide·python·pycharm
大鸡腿同学8 小时前
身弱:修炼之路
后端
bobz9658 小时前
cpu 调度 和 gpu 调度
后端
AirMan8 小时前
深入揭秘 ConcurrentHashMap:JDK7 到 JDK8 并发优化的演进之路
后端·面试
bobz9659 小时前
Linux CPU 调度模型
后端