
之前写了一篇文章是《分享一个年化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%,全部清仓
接下来就是这篇文章的重头戏了
选股逻辑源码
量化系统框架还没搭建好的可以先参考这篇教程,后面的代码都是基于这套框架来运行的,是必备条件。
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个股票用于交易
以上就是整个策略的选股逻辑核心代码了。