【宽客学院】如何选出符合一定条件的股票

pandas
宽客学院
标签: #<Tag:0x00007f0851b123e8> #<Tag:0x00007f0851b122a8>

(iQuant) #1

导语:在开发股票量化投资策略的时候,其中非常重要的一步就是选股。因此本文的目的是希望大家阅读以后,能够更为快速、高效、便捷地在BigQuant平台上选出符合一定条件的股票。


作者: bigquant
阅读时间:15分钟
本文由BigQuant宽客学院推出,难度标签:☆☆☆

符合一定条件的股票可以这样理解,这类股票具有同样的特征、属性,比如属于同一个板块,或者相似的财务指标,或者是存在相似的图表形态。

那么,怎样快速地选出符合一定条件的股票呢。主要因素为下面两点:

  • BigQuant数据以及数据API的了解
  • Pandas数据分析技巧

扩展阅读

1.BigQuant数据API详解
2.10分钟学会Pandas
3.Pandas使用小技巧
4.Pandas基础操作技能get!强烈推荐!

完整代码如下:

克隆策略

参数

In [2]:
start_date = '2016-01-01'
end_date = '2017-09-14'
instrument = D.instruments(start_date=start_date, end_date=end_date)

1.基本信息层面

获取指数成分股列表

In [3]:
# 沪深300指数成分
df = D.history_data(instrument, start_date, end_date, ['in_csi300'])
instruments = list(set(df[df['in_csi300']==1]['instrument']))
print('沪深300指数成分股预览10只股票:', instruments[:10])
沪深300指数成分股预览10只股票: ['600098.SHA', '000555.SZA', '000826.SZA', '600111.SHA', '600271.SHA', '601899.SHA', '002736.SZA', '600871.SHA', '000738.SZA', '601127.SHA']

获取某个行业股票列表

In [4]:
# (举例)获取国防军工行业股票列表
df = D.history_data(instrument, start_date, end_date, ['industry_sw_level1'])
instruments = list(set(df[df['industry_sw_level1'] == 650000]['instrument']))
D.history_data(instruments, '2017-07-27', '2017-07-27', ['company_name']).head() 
Out[4]:
date company_name instrument
441666 2017-07-27 中兵红箭股份有限公司 000519.SZA
441690 2017-07-27 航天工业发展股份有限公司 000547.SZA
441833 2017-07-27 中国航发动力控制股份有限公司 000738.SZA
441849 2017-07-27 中航飞机股份有限公司 000768.SZA
442030 2017-07-27 中航工业机电系统股份有限公司 002013.SZA

返回特定时间段某个指数的股票

In [15]:
start_date='2017-05-23'
end_date = '2018-01-01'
df = D.history_data(D.instruments(start_date, end_date),start_date,end_date,fields=['in_csi300'])
# 获取沪深300的成分股股票列表  中证500:in_csi500,中证800:in_csi800  参考文档说明
instruments = list(set(df[df['in_csi300']==1]['instrument'])) 
print('特定时间段内沪深300指数成分股预览10只股票:',instruments[:10])
特定时间段内沪深300指数成分股预览10只股票: ['000555.SZA', '000826.SZA', '600111.SHA', '600271.SHA', '601899.SHA', '002736.SZA', '600871.SHA', '000738.SZA', '601127.SHA', '601211.SHA']

获取某个概念、板块的股票列表

In [6]:
df = D.history_data(instrument, '2017-08-23', '2017-08-23', ['concept']).dropna()   # 'concept' 是股票的概念字段
df['is_ai'] = df['concept'].map(lambda x: '人工智能' in x)  # 以人工智能为例,找到相关概念股票
st = list(df[df['is_ai'] == True]['instrument'])
D.history_data(st,'2017-08-23','2017-08-23',['name', 'concept']).head()
Out[6]:
date instrument name concept
0 2017-08-23 000977.SZA 浪潮信息 云计算;IPV6;大数据;网络安全;去IOE;人工智能;雄安新区;融资融券;融资融券标的
1 2017-08-23 002049.SZA 紫光国芯 物联网;重组;智能IC卡;芯片国产化;国家队;高校;人工智能;融资融券;融资融券标的
2 2017-08-23 002073.SZA 软控股份 机器人;工业4.0;国家队;人工智能;融资融券;融资融券标的
3 2017-08-23 002184.SZA 海得控制 智能电网;浦东新区;机器人;工业4.0;军民融合;人工智能
4 2017-08-23 002226.SZA 江南化工 重组;人工智能;预增;并购阶段(需定增)

获取每日创业板次新股股票列表

次新股的定义为上市90天以内

In [7]:
df = D.history_data(instrument, start_date, end_date, ['list_date', 'list_board'])
from datetime import timedelta
df[(df['list_board']=='创业板')&(df['date']<=df['list_date']+timedelta(days=20))].head(10)
Out[7]:
date list_date instrument list_board
294534 2017-05-23 2017-05-05 300643.SZA 创业板
294536 2017-05-23 2017-05-03 300647.SZA 创业板
294538 2017-05-23 2017-05-05 300649.SZA 创业板
294539 2017-05-23 2017-05-03 300650.SZA 创业板
294540 2017-05-23 2017-05-09 300651.SZA 创业板
294541 2017-05-23 2017-05-16 300652.SZA 创业板
294542 2017-05-23 2017-05-16 300653.SZA 创业板
294543 2017-05-23 2017-05-23 300655.SZA 创业板
294544 2017-05-23 2017-05-19 300656.SZA 创业板
294545 2017-05-23 2017-05-23 300657.SZA 创业板

2.财务信息层面

选出满足以下条件的股票

  • 市盈率小于15倍
  • 市净率小于1.5倍
In [8]:
 # 获取市盈率、市净率、成交额数据
history_data = D.history_data(instrument, start_date=start_date, end_date= end_date, fields=[ 'pb_lf', 'pe_ttm','amount'])
result = history_data[(history_data['pb_lf'] < 1.5)
                                       & (history_data['pe_ttm'] < 15) 
                                       & (history_data['amount'] > 0) 
                                       & (history_data['pb_lf'] > 0)
                                       & (history_data['pe_ttm'] > 0)]
daily_buy_stock = result.groupby('date').apply(lambda df:list(df.sort_values(['pe_ttm','pb_lf']).instrument)[:30])
daily_buy_stock.head()
Out[8]:
date
2017-05-23    [600015.SHA, 600016.SHA, 601818.SHA, 601166.SH...
2017-05-24    [600015.SHA, 600016.SHA, 601818.SHA, 601166.SH...
2017-05-25    [600015.SHA, 601818.SHA, 601288.SHA, 600016.SH...
2017-05-26    [600015.SHA, 600016.SHA, 601818.SHA, 601288.SH...
2017-05-31    [600015.SHA, 600016.SHA, 601818.SHA, 601288.SH...
dtype: object

选出满足以下条件的股票

  • 市净率小于8
  • 市销率大于0.4
  • 资产周转率大于0.4
  • 市值最小的10只股票
In [9]:
stock_num = 10
# 通过history_data接口获取历史数据
history_data = D.history_data(instrument, start_date, end_date, ['pb_lf', 'ps_ttm', 'market_cap', 'amount'])
# 通过feature接口获取财务数据
financial = D.features(instrument, start_date=start_date, end_date=end_date,
                        fields=['fs_operating_revenue_ttm_0','fs_current_assets_0','fs_non_current_assets_0']) 
# 总资产=流动资产+非流动资产
financial['total_assets'] = financial['fs_current_assets_0'] + financial['fs_non_current_assets_0']
# 资产周转率=营业收入/总资产
financial['asset_turnover'] = financial['fs_operating_revenue_ttm_0'] / financial['total_assets']
financial_data = financial[['date','instrument','asset_turnover']] 
# 两个DataFrame:历史数据、财务数据 合并
result = history_data.merge(financial_data, on=['date','instrument'], how='outer')  
# 按照选股法则选出股票
daily_buy_stock = result.groupby('date').apply(lambda df:list(df[(df['ps_ttm']>0.4) & (df['pb_lf']<8) & (df['asset_turnover']>0.4)
                                         & (df['amount']>100)].sort_values('market_cap')['instrument'])[:stock_num])
daily_buy_stock.head()
Out[9]:
date
2017-05-23    [300321.SZA, 300483.SZA, 300417.SZA, 300243.SZ...
2017-05-24    [300321.SZA, 300483.SZA, 300417.SZA, 600847.SH...
2017-05-25    [300321.SZA, 300483.SZA, 300417.SZA, 600847.SH...
2017-05-26    [300321.SZA, 300483.SZA, 300417.SZA, 300243.SZ...
2017-05-31    [300321.SZA, 300483.SZA, 300417.SZA, 300243.SZ...
dtype: object

3.技术指标层面

选出满足以下条件的股票

  • 7日均线上穿63日均线
  • 收盘价突破ATR上轨
In [10]:
df= D.history_data(instrument, start_date, end_date, ['open', 'high', 'low', 'close'])

import talib as ta
from numpy import float as f

def seek_stocks(df):
    df['ma_7'] = ta.SMA(df.close.map(f).values, 7)
    df['ma_63'] = ta.SMA(df.close.map(f).values, 63)
    try:
        df['atr'] = ta.ATR(df.high.map(f).values, df.low.map(f).values, df.close.map(f).values, 14)
    except Exception as e:
        df['atr'] = np.nan
    df['upperline'] = df.close.rolling(14).mean() + df['atr'] 
    return df[(df['ma_7']>df['ma_63'])&(df['close']>=df['upperline'])].drop('instrument', axis=1)

result = df.groupby('instrument').apply(seek_stocks).reset_index()
daily_buy_stock = result.groupby('date').apply(lambda df:list(df['instrument']))
daily_buy_stock.head()
Out[10]:
date
2017-08-21    [000008.SZA, 000016.SZA, 000018.SZA, 000021.SZ...
2017-08-22    [000008.SZA, 000018.SZA, 000021.SZA, 000040.SZ...
2017-08-23    [000001.SZA, 000021.SZA, 000040.SZA, 000410.SZ...
2017-08-24    [000001.SZA, 000040.SZA, 000402.SZA, 000408.SZ...
2017-08-25    [000001.SZA, 000016.SZA, 000020.SZA, 000040.SZ...
dtype: object

选出满足以下条件的股票

  • 股价创60日最高点
  • 3日线上穿5日均线,5日均线上穿10日均线
  • 当日成交额是昨日成交额的1.4倍
  • macd柱状图处于红色区域
In [11]:
df =D.history_data(instrument, start_date, end_date, ['close', 'amount','high'])

def seek_stocks(df):
    df['highest_60'] = df['high'].rolling(60).max()  # 计算60天最高点
    df['ma3_cross_ma5'] = df['close'].rolling(3).mean() - df['close'].rolling(5).mean()  > 0  # 3日均线上穿5日均线
    df['ma5_cross_ma10'] = df['close'].rolling(5).mean() - df['close'].rolling(10).mean()  > 0  # 5日均线上穿10日均线
    df['amount_cond'] = df['amount'] / df['amount'].shift(1) - 1 >= 0.4   # 当日成交量比前一日成交量大40%
    prices = df['close'].map(np.float)  # 转化成float格式
    # macd:diff线 信号线:dea 柱状图:diff-dea
    macd, signal, hist = ta.MACD(np.array(prices), 12, 26, 9)  # 计算macd各个指标
    df['is_highest'] = df['close'] == df['highest_60']  # 该列是布尔型变量,表明是否是60日最高点
    df['hist_is_red'] =  hist > 0   # macd柱状图是否在红色区域
    return df
  
managed_df = df.groupby('instrument').apply(seek_stocks).reset_index()
result= managed_df[
    (managed_df['hist_is_red'])&   # macd在红色区域
    (managed_df['is_highest'])&   # 是60日最高点
    (managed_df['ma3_cross_ma5'])&  # 3日均线上穿5日均线
    (managed_df['ma5_cross_ma10'])&   # 5日均线上穿10日均线
    (managed_df['amount_cond'])]   # 满足成交量条件
    
# 整理出每日符合买入条件的列表               
daily_buy_stock = result.groupby('date').apply(lambda df:list(df.instrument)).reset_index().rename(columns={0:'stocks'})
daily_buy_stock.head()
Out[11]:
date stocks
0 2017-08-16 [002128.SZA, 300377.SZA, 600797.SHA]
1 2017-08-17 [000922.SZA, 000990.SZA, 002128.SZA, 002380.SZ...
2 2017-08-18 [000554.SZA, 002545.SZA, 300295.SZA, 600275.SH...
3 2017-08-21 [000720.SZA, 002298.SZA, 300310.SZA, 300353.SZ...
4 2017-08-22 [000593.SZA, 000595.SZA, 002037.SZA, 300229.SZ...

小结:之所以将“如何选出符合条件的股票”单独作为一篇帖子,是因为选股在开发量化策略的过程中是及其重要的一步,尤其是当大家在开发基于一些逻辑、想法的量化选股策略。我们希望您能在BigQuant平台快速实现您的选股思想。


   本文由BigQuant宽客学院推出,版权归BigQuant所有,转载请注明出处。



基于AI排序算法的指数增强策略
如何按流通盘大小选标的
请教:如何选出高市值的标的
策略研究常用功能
(maxchen) #2

市现率 写错了,应为市销率


(iQuant) #3

嗯嗯,我们的疏忽,谢谢哈。


(hkr__) #4

这里的判断语句不是上市20天以内吗?


(fslong) #5

请问,seek_socks(df)的df参数是怎样传递进去的?