小市值因子是很重要的选股策略,主要意思就是,小市值的股票涨幅通常会比较大。
具体执行的方法可以是:每周末(或每个月末)选出市值最小的10只股票,下周初(或下月初)平均资金买入,到下下周(或下下个月)再换10只,如此循环。
本节代码可以生成所有股票最近的交易数据,按市值排序,也可以根据设置,选出市值最小的n只股票(已剔除ST、停盘、即将退市的股票)。
思路:从新浪网站爬取所有股票的最近交易数据,按市值排序,存成csv,供参考买入、卖出。
from urllib.request import urlopen # python自带爬虫库 import pandas as pd from datetime import datetime import time import re # 正则表达式库 import os # 系统库 import json # python自带的json数据库 pd.set_option('expand_frame_repr', False) # 当列太多时不换行 pd.set_option('display.max_rows', 500) # 最多显示数据的行数 # =====函数:从网页上抓取数据,返回抓取到的网页内容 def get_content_from_internet(url, max_try_num=10, sleep_time=5): """ 使用python自带的urlopen函数,从网页上抓取数据 :param url: 要抓取数据的网址 :param max_try_num: 最多尝试抓取次数 :param sleep_time: 每次抓取失败后停顿的时间 :return: 返回抓取到的网页内容 """ get_success = False # 是否成功抓取到内容 # 抓取内容 for i in range(max_try_num): try: content = urlopen(url=url, timeout=15).read() # 使用python自带的库,从网络上获取信息 get_success = True # 成功抓取到内容 break except Exception as e: print('抓取数据报错,次数:', i+1, '报错内容:', e) time.sleep(sleep_time) # 判断是否成功抓取内容 if get_success: return content else: raise ValueError('使用urlopen抓取网页数据不断报错,达到尝试上限,停止程序,请尽快检查问题所在') # =====函数:从新浪获取指定股票(可多只,可以是指数)最近一个交易日的数据,整理成一定格式的DataFrame,返回这个DataFrame def get_today_data_from_sinajs(code_list): """ 返回一串股票最近一个交易日的相关数据 从这个网址获取股票数据:http://hq.sinajs.cn/list=sh600000,sz000002,sz300001 正常网址:https://finance.sina.com.cn/realstock/company/sh600000/nc.shtml, :param code_list: 一串股票代码的list,可以多个,例如[sh600000, sz000002, sz300001], :return: 返回一个存储股票数据的DataFrame """ # 构建url url = "http://hq.sinajs.cn/list=" + ",".join(code_list) #先用逗号把code_list中的元素连接起来,再在前面加上http:…… # 抓取数据 content = get_content_from_internet(url) content = content.decode('gbk') #用gbk解码,可解码出中文;解码前是dytes,解码后是str # 将数据转换成DataFrame content = content.strip() # 去掉文本前后的空格、回车等 data_line = content.split(' ') # 每行是一个股票的数据,生成一个列表 data_line = [i.replace('var hq_str_', '').split(',') for i in data_line] #去掉每行前面的冗余数据,并再次分割,形成2层列表 # df = pd.DataFrame(data_line, dtype='float') #dtype='float'可生成数值型数据 df = pd.DataFrame(data_line) #这里只生成DF,转float放在下面 # 对DataFrame进行整理 df[0] = df[0].str.split('="') # df[0]是取“0”列 df['stock_code'] = df[0].str[0].str.strip() df['stock_name'] = df[0].str[-1].str.strip() df['candle_end_time'] = df[30] + ' ' + df[31] # 股票市场的K线,是普遍以当跟K线结束时间来命名的 df['candle_end_time'] = pd.to_datetime(df['candle_end_time']) rename_dict = {1: 'open', 2: 'pre_close', 3: 'close', 4: 'high', 5: 'low', 6: 'buy1', 7: 'sell1', 8: 'volume', 9: 'amount', 32: 'status'} # 自己去对比数据,会有新的发现;10~29是买卖五档盘口的数据 # 其中volume单位是股,amount单位是元 df.rename(columns=rename_dict, inplace=True) #挑选需要转化的列,转化为float df[['open', 'high', 'low', 'close', 'pre_close', 'amount','volume', 'buy1', 'sell1']] = df[['open', 'high', 'low', 'close', 'pre_close', 'amount', 'volume', 'buy1', 'sell1']].astype('float') df['status'] = df['status'].str.strip('";') #在status列,去掉冗余的‘";’字符 df = df[['stock_code', 'stock_name', 'candle_end_time', 'open', 'high', 'low', 'close', 'pre_close', 'amount', 'volume', 'buy1', 'sell1', 'status']] #重新规定列顺序 return df # =====函数:从新浪获取所有股票最近一个交易日的数据,返回一个DF def get_all_today_stock_data_from_sina_marketcenter(): """ http://vip.stock.finance.sina.com.cn/mkt/#stock_hs_up 从新浪网址的上述的网址,逐页获取最近一个交易日所有股票的数据 :return: 返回一个存储股票数据的DataFrame """ # ===数据网址 raw_url = 'http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?page=%s' '&num=80&sort=symbol&asc=1&node=hs_a&symbol=&_s_r_a=sort' page_num = 1 # ===存储数据的DataFrame all_df = pd.DataFrame() # ===获取上证指数最近一个交易日的日期。此段代码在课程视频中没有,之后补上的 df = get_today_data_from_sinajs(code_list=['sh000001']) sh_date = df.iloc[0]['candle_end_time'].date() # 上证指数最近交易日 # ===开始逐页遍历,获取股票数据 while True: # 构建url url = raw_url % (page_num) print('开始抓取页数:', page_num) # 抓取数据 content = get_content_from_internet(url) content = content.decode('gbk') # 判断页数是否为空 if 'null' in content: print('抓取到页数的尽头,退出循环') break # 通过正则表达式,给key加上引号 content = re.sub(r'(?<={|,)([a-zA-Z][a-zA-Z0-9]*)(?=:)', r'"1"', content) # 将数据转换成dict格式 content = json.loads(content) #此时,content转化为list,其中的元素是dict # 将数据转换成DataFrame格式 df = pd.DataFrame(content, dtype='float') # 对数据进行整理 # 重命名;总市值单位是万元,流通值单位是万元,成交量volume单位股数,成交额amount单位是元 rename_dict = {'symbol': '股票代码', 'name': '股票名称', 'open': '开盘价', 'high': '最高价', 'low': '最低价','trade': '收盘价', 'settlement': '前收盘价', 'volume': '成交量', 'amount': '成交额', 'mktcap':'总市值','nmc':'流通值'} df.rename(columns=rename_dict, inplace=True) # 没映射的列,还是保留原来的列名,即列数不会减少 # 添加交易日期 # df['交易日期'] = pd.to_datetime(datetime.now().date()) # 课程视频中使用的是本行代码 df['交易日期'] = pd.to_datetime(sh_date) # 在课程视频中使用的是上一行代码,现在改成本行代码,程序更加稳健 # df中的字段,还包括:mktcap(总市值)、nmc(流通值)、per(本益比,类似市盈率)、pb(市净率)、turnoverratio(换手率) # 取需要的列 df = df[['股票代码', '股票名称', '交易日期', '开盘价', '最高价', '最低价', '收盘价', '前收盘价', '成交量', '成交额','总市值','流通值']] # 合并数据 all_df = all_df.append(df, ignore_index=True) # 将页数+1 page_num += 1 time.sleep(1) # ===将当天停盘的股票删除,此段代码在课程视频中没有,之后补上的 all_df = all_df[all_df['开盘价'] - 0 > 0.00001] all_df.reset_index(drop=True, inplace=True) # ===返回结果 return all_df # 以下为主程序 # ===参数设定 select_stock_num = 10 # 选股数量 # 获取今天所有的股票数据 df = get_all_today_stock_data_from_sina_marketcenter() # 函数中已删除当天停盘的股票,这里再删除ST和即将退市的股票 df = df[df['股票名称'].str.contains('ST')== False] # 删除ST股票 df = df[df['股票名称'].str.contains('退') == False] #删除即将退市股票 df['排名'] = df['总市值'].rank() # 根据总市值的排名 df.to_csv(r'C:UsersloriDesktopstockinvestproject1datadjl_data根据当前总市值选股all_stock_rank.csv', encoding='gbk',mode='w',index=False) df.sort_values(by='总市值',inplace=True) # 根据总市值排序 df_select = df[['交易日期','股票代码', '股票名称', '总市值','流通值','排名','收盘价']] df_select = df_select.iloc[:select_stock_num] df_select.to_csv(r'C:UsersloriDesktopstockinvestproject1datadjl_data根据当前总市值选股select_stock.csv', encoding='gbk',mode='w',index=False)