python tushare量化股票大数据分析整合版

前言:对前面的代码功能进行一次简要整合,简要添加了多线程功能:
1.tushare股票数据本地csv格式。
2.csv格式文件mysql化。
3.tushare股票数据分析word文档化。
代码如下:

import os
import docx
import time
import pymysql
import datetime
import warnings
import pandas as pd
import tushare as ts
import threading
import dateutil.parser
from docx.shared import Cm, Inches
from docx.shared import RGBColor,Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
warnings.filterwarnings('ignore')

#pd.set_option()就是pycharm输出控制显示的设置
pd.set_option('expand_frame_repr', False)#True就是可以换行显示。设置成False的时候不允许换行
pd.set_option('display.max_columns', None)# 显示所有列
#pd.set_option('display.max_rows', None)# 显示所有行
pd.set_option('colheader_justify', 'centre')# 显示居中

#os.chdir()用于改变当前工作目录到指定的路径
#此路径必须改为放数据的路径且中间的不能缺失任何一天数据,例如get_analysis_stockdata('20200101', '20200106'),
#那么你放数据文件夹内不能缺少任何一个这段时期内的交易数据文件,否则报错
#tushare的流通市值跟总市值单位是‘万元’,成交额单位是‘千元’,代码都将他们转化成亿单位,方便阅读分析
os.chdir('D:/stock_data/')  #保存的绝对路径,需要自己修改跟创建,就是切换默认的工作目录到你设置的路径
pro = ts.pro_api('要到tushare官网注册个账户然后将token复制到这里,可以的话请帮个忙用文章末我分享的链接注册,谢谢')

#df_basic = pro.stock_basic() 获取基础信息数据,包括股票代码、名称、上市日期、退市日期等
#df_daily = pro.daily()  获取所有股票日行情信息,或通过通用行情接口获取数据,包含了前后复权数据,停牌期间不提供数据
#df_daily_basic = pro.daily_basic()获取全部股票每日重要的基本面指标,可用于选股分析、报表展示等。

def get_all_stockdata(st_date, ed_date):
    trade_d = pro.trade_cal(exchange='SSE', is_open='1',start_date=st_date,end_date=ed_date,fields='cal_date')
    for date in trade_d['cal_date'].values:
        df_basic = pro.stock_basic(exchange='', list_status='L')    #再获取所有股票的基本信息
        df_daily = pro.daily(trade_date=date)    # 先获得所有股票的行情数据,成交额单位是千元,成交量是手
        df_daily_basic = pro.daily_basic(ts_code='', trade_date=date,fields='ts_code, turnover_rate, turnover_rate_f,'
                                                                            ' volume_ratio, pe, pe_ttm, pb, ps, ps_ttm,'
                                                                            ' dv_ratio, dv_ttm, total_share, float_share,'
                                                                            ' free_share, total_mv, circ_mv ')    #获取每日指标,单位是万股,万元
        #基本数据跟行情数据合并,再跟每日指标数据合并生成一个csv数据文件
        df_first = pd.merge(left=df_basic, right=df_daily, on='ts_code', how='outer')    #on='ts_code'以ts_code为索引,合并数据,how='outer',取并集
        df_all = pd.merge(left=df_first, right=df_daily_basic, on='ts_code', how='outer')
        #数据清洗,删除symbol列数据,跟ts_code数据重复
        df_all = df_all.drop('symbol', axis=1)
        for w in ['name', 'area', 'industry', 'market']:    #在'name', 'area', 'industry', 'market'列内循环填充NaN值
            df_all[w].fillna('问题股', inplace=True)
        df_all['amount'] = df_all['amount']/100000    #千转亿
        df_all['circ_mv'] = df_all['circ_mv']/10000    #万转亿
        df_all['total_mv'] = df_all['total_mv']/10000    #万转亿

        df_all['ts_code'] = df_all['ts_code'].astype(str)    #强制转换成str字符串格式
        df_all['list_date'] = pd.to_datetime(df_all['list_date'])    #本地储存前一定要先转化成日期格式先
        df_all['trade_date'] = pd.to_datetime(df_all['trade_date'])

        df_all.to_csv(str(date) + '_ts.csv', index=False, encoding='gbk')    #保存数据,不保存索引,如果index=True,则保存索引会多出一列
        #print(df_all.dtypes)
        print('%s download complete.' % (str(date)))
    return df_all

#将csv导入本地mysql数据库
#连接 Mysql 数据库
def output_mysql():
    try:
        conn = pymysql.connect(host='localhost', user='root', password='你的密码', db='你创建的数据库', charset='utf8')
        cur = conn.cursor()
        print('数据库连接成功!')
        print(' ')
    except:
        print('数据库连接失败!')

    #读取任意文件夹下的csv文件
    #获取程序所在路径及该路径下所有文件名称
    os.chdir('D:/stock_data/')
    path = os.getcwd()
    files = os.listdir(path)

    #遍历所有文件
    i = 0
    for file in files:
        #判断文件是不是csv文件,file.split('.')[-1]获取‘.’后的csv字符串
        if file.split('.')[-1] in ['csv']:
            i += 1
            #构建一个表名称,供后期SQL语句调用,file.split('.')[0]获取的是.csv前面的名字
            filename = file.split('.')[0]
            filename = 'tab_' + filename

            #使用pandas库读取csv文件的所有内容,结果f是一个数据框,保留了表格的数据存储方式,是pandas的数据存储结构。
            f = pd.read_csv(file, encoding='gbk')

            #获取数据框的标题行(即字段名称),将来作为sql语句中的字段名称。
            columns = f.columns.tolist()
            #print(columns)

            #将csv文件中的字段类型转换成mysql中的字段类型,只有跟mysql的数据格式一致才能导入
            #获取f数据类型
            types = f.dtypes
            #print(types)
            field = [] #用来接收字段名称的列表
            table = [] #用来接收字段名称和字段类型的列表
            char = []
            for item in range(len(columns)):
                #print(types[item])
                #print(columns[item])
                if 'object' == str(types[item]):
                    char = '`' + columns[item] + '`' + ' VARCHAR(255)' #必须加上`这个点,否则在写入mysql是会报错
                elif 'int64' == str(types[item]):
                    char = '`' + columns[item] + '`' + ' INT'
                elif 'float64' == str(types[item]):
                    char = '`' + columns[item] + '`' + ' FLOAT'
                elif 'datetime64[ns]' == str(types[item]):
                    char = '`' + columns[item] + '`' + ' DATETIME'
                else:
                    char = '`' + columns[item] + '`' + ' VARCHAR(255)'
                table.append(char)
                field.append('`' + columns[item] + '`') #必须加上`这个点,否则在写入mysql是会报错
                #print(char)
                #print(field)

            #构建SQL语句片段
            #将table列表中的元素用逗号连接起来,组成table_sql语句中的字段名称和字段类型片段,用来创建表。
            tables = ','.join(table)
            #print(tables)

            #将field列表中的元素用逗号连接起来,组成insert_sql语句中的字段名称片段,用来插入数据。
            fields = ','.join(field) #字段名
            #print(fields)

            #创建数据库表
            #如果数据库表已经存在,首先删除它
            cur.execute('drop table if exists {};'.format(filename))
            #print(filename)
            conn.commit()

            #构建创建表的SQL语句
            table_sql = 'CREATE TABLE IF NOT EXISTS ' + filename + '(' + 'id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,' + tables + ');'
            #print('table_sql is: ' + table_sql)

            #开始创建数据库表
            print('表:' + filename + ',开始创建…………')
            cur.execute(table_sql)
            conn.commit()
            print('表:' + filename + ',创建成功!')

            #向数据库表中插入数据
            print('表:' + filename + ',开始插入数据…………')

            #将数据框的数据读入列表。每行数据是一个列表,所有数据组成一个大列表。也就是列表中的列表,将来可以批量插入数据库表中。
            f_sql = f.astype(object).where(pd.notnull(f), None)
            values = f_sql.values.tolist() #所有的数据
            #print(values)

            #计算数据框中总共有多少个字段,每个字段用一个 %s 替代。
            s = ','.join(['%s' for _ in range(len(f.columns))])
            #print(s)

            #构建插入数据的SQL语句
            insert_sql = 'insert into {}({}) values({})'.format(filename,fields,s)
            #print('insert_sql is:' + insert_sql)

            #开始插入数据
            cur.executemany(insert_sql, values) #使用 executemany批量插入数据
            conn.commit()
            print('表:' + filename + ',数据插入完成!')
            print(' ')
    cur.close()
    conn.close()
    print('任务完成!共导入 {} 个CSV文件。'.format(i))

#分析csv数据并生成docx文档,存储至本地D盘D:/stock_analysis/
def get_analysis_stockdata(st_date, ed_date):
    trade_d = pro.trade_cal(exchange='SSE', is_open='1',start_date=st_date,end_date=ed_date,fields='cal_date')    #获取st_date,ed_date时间段内的交易日期
    for date_now in trade_d['cal_date'].values:    #将以上获取时间段的交易日期赋值给date_now
        df = pd.read_csv('{}_ts.csv'.format(str(date_now)), encoding='gbk')    #读取时间段内每日的股票数据
        df.fillna(0, inplace=True)    #fillna填充缺失数据,传入inplace=True直接修改原对象

        #astype强制将涨幅,PE,总市值,流通市值转换成float格式,ts_code转化成str后,NAN也变成nan str格式
        df[['change', 'pe', 'total_mv', 'circ_mv']] = df[['change', 'pe', 'total_mv', 'circ_mv']].astype(float)
        df['ts_code'] = df['ts_code'].astype(str)
        df['list_date'] = pd.to_datetime(df['list_date'])

        # 添加交易所列
        df.loc[df['ts_code'].str.startswith('3'), 'exchange'] = 'CY'
        df.loc[df['ts_code'].str.startswith('6'), 'exchange'] = 'SH'
        df.loc[df['ts_code'].str.startswith('0'), 'exchange'] = 'SZ'

        #此change表示的是价格变化的多少,不是指上涨下跌的百分比
        df_up = df[df['change'] > 0.00]    #找出上涨的股票
        df_even = df[df['change'] == 0.00]    #找出走平的股票
        df_down = df[df['change'] < 0.00]    #找出下跌的股票

        # 找出涨停的股票
        limit_up = df[df['change']/df['pre_close'] >= 0.097]
        #print(limit_up['list_date'])
        limit_down = df[df['change']/df['pre_close'] <= -0.0970]

        # 涨停股数中的未封板股,上市日期小于15天
        limit_up_new = limit_up[pd.to_datetime(date_now) - limit_up['list_date'] <= pd.Timedelta(days=15)]
        #print(pd.to_datetime(date_now))
        # 涨停股数中次新股,上市日期小于1年
        limit_up_fresh = limit_up[pd.to_datetime(date_now) - limit_up['list_date'] <= pd.Timedelta(days=365)]

        # 涨停股数中的未封板股,上市日期小于15天
        limit_down_new = limit_down[pd.to_datetime(date_now) - limit_down['list_date'] <= pd.Timedelta(days=15)]
        # 涨停股数中次新股,上市日期小于1年
        limit_down_fresh = limit_down[pd.to_datetime(date_now) - limit_down['list_date'] <= pd.Timedelta(days=365)]
        #df_up.shape[0]获取上涨的行数,df.shape[1]获取列数,df.values获取值
        # print('A股上涨个数: %d, A股下跌个数: %d, A股走平个数: %d。' % (df_up.shape[0], df_down.shape[0], df_even.shape[0]))
        # print('A股总成交额:%d 亿, 总成交量:%d 手' % (df['amount'].sum(), df['vol'].sum()))
        # print('A股平均市盈率:%.2f, 平均流通市值 %.2f 亿, 平均总市值 %.2f 亿' % (df['pe'].mean(), df['circ_mv'].mean(), df['total_mv'].mean()))
        # print('涨停数量:%d 个, 涨停中上市日期小于15天的:%d, 涨停中上市日期小于1年的:%d' % (limit_up.shape[0], limit_up_new.shape[0], limit_up_fresh.shape[0]))
        # print('跌停数量:%d 个, 涨停中上市日期小于15天的:%d, 涨停中上市日期小于1年的:%d' % (limit_down.shape[0], limit_down_new.shape[0], limit_down_fresh.shape[0]))


        file = docx.Document()
        #设置总标题,居中
        headb = file.add_heading('%s中国股市今日收盘分析报告' % (date_now), level=0).alignment = WD_ALIGN_PARAGRAPH.CENTER
        head1 = file.add_heading('股市基本概况:',level=1)    #设置一级标题
        #添加段落内容
        text1 = file.add_paragraph() #首先创建一个空的段落,然后再往里面加文字,这样方便设置文字格式字体等设置,另外一种写法,缺点不能单独设置字体属性
        # text1 = file.add_paragraph('A股上涨个数: %d, A股下跌个数: %d, A股走平个数: %d。' % (df_up.shape[0], df_down.shape[0], df_even.shape[0]))
        text1.add_run('A股上涨个数:').bold = True    #添加文字并设置粗体
        text1.add_run('{}      '.format(str(df_up.shape[0]))).font.color.rgb = RGBColor(255, 0, 0)    #添加变量
        text1.add_run('A股下跌个数:').bold = True
        text1.add_run('{}      '.format(str(df_down.shape[0]))).font.color.rgb = RGBColor(0, 255, 0)
        text1.add_run('A股走平个数:').bold = True
        text1.add_run('{}      '.format(str(df_even.shape[0]))).font.color.rgb = RGBColor(0, 0, 255)
        text1.line_spacing = Pt(25)    #设置段落行距
        text1.style = 'List Bullet'    # 设置项目符号列表

        text2 = file.add_paragraph()
        text2.add_run('A股总成交额:').bold = True
        text2.add_run('{}'.format(str(round(df['amount'].sum(),2)))).font.color.rgb = RGBColor(128, 0, 128)
        text2.add_run('亿      ')
        text2.add_run('总成交量:').bold = True
        text2.add_run('{}'.format(str(round(df['vol'].sum(),2)))).font.color.rgb = RGBColor(128, 0, 128)
        text2.add_run('手      ')
        text2.line_spacing = Pt(25)
        text2.style = 'List Bullet'

        text3 = file.add_paragraph()
        text3.add_run('A股平均市盈率:').bold = True
        text3.add_run('{}   '.format(str(round(df['pe'].mean())))).font.color.rgb = RGBColor(128, 0, 128)
        text3.add_run('平均流通市值:').bold = True
        text3.add_run('{}'.format(str(round(df['circ_mv'].mean(),2)))).font.color.rgb = RGBColor(128, 0, 128)
        text3.add_run('亿')
        text3.add_run('\n')
        text3.add_run('平均总市值:').bold = True
        text3.add_run('{}'.format(str(round(df['total_mv'].mean(),2)))).font.color.rgb = RGBColor(128, 0, 128)
        text3.add_run('亿      ')
        text3.line_spacing = Pt(25)
        text3.style = 'List Bullet'
        text3.add_run('\n')

        text4 = file.add_paragraph()
        text4.add_run('涨停数量:').bold = True
        text4.add_run('{}'.format(str(limit_up.shape[0]))).font.color.rgb = RGBColor(255, 0, 0)
        text4.add_run('个      ')
        text4.add_run('涨停中上市日期小于15天的:').bold = True
        text4.add_run('{}'.format(str(limit_up_new.shape[0]))).font.color.rgb = RGBColor(255, 0, 0)
        text4.add_run('个      ')
        text4.add_run('\n')
        text4.add_run('涨停中上市日期小于1年的:').bold = True
        text4.add_run('{}'.format(str(limit_up_fresh.shape[0]))).font.color.rgb = RGBColor(255, 0, 0)
        text4.add_run('个      ')
        text4.line_spacing = Pt(25)
        text4.style = 'List Bullet'

        text5 = file.add_paragraph()
        text5.add_run('跌停数量:').bold = True
        text5.add_run('{}'.format(str(limit_down.shape[0]))).font.color.rgb = RGBColor(0, 255, 0)
        text5.add_run('个      ')
        text5.add_run('跌停中上市日期小于15天的:').bold = True
        text5.add_run('{}'.format(str(limit_down_new.shape[0]))).font.color.rgb = RGBColor(0, 255, 0)
        text5.add_run('个      ')
        text5.add_run('\n')
        text5.add_run('跌停中上市日期小于1年的:').bold = True
        text5.add_run('{}'.format(str(limit_down_fresh.shape[0]))).font.color.rgb = RGBColor(0, 255, 0)
        text5.add_run('个      ')
        text5.line_spacing = Pt(25)
        text5.style = 'List Bullet'

        file.add_page_break()    #添加分页符

        def get_output(df, columns='_industry', name='_limit_up'):  #自定义计算平均市盈率,平均流通市值等函数,方便后面调用
            # df.copy(deep= False)和df.copy()都是浅拷贝,是复制了旧对象的内容,然后重新生成一个新对象,改变旧对象不会影响新对象。
            df = df.copy()
            output = pd.DataFrame()

            #df.groupby(columns)根据列值分组数据,并根据股票代码统计数据
            output = pd.DataFrame(df.groupby(columns)['ts_code'].count())

            output['平均市盈率'] = round(df.groupby(columns)['pe'].mean(),2)

            output['平均流通市值(亿)'] = round(df.groupby(columns)['circ_mv'].mean(),2)

            output['平均总市值(亿)'] = round(df.groupby(columns)['total_mv'].mean(),2)

            output['平均成交量(手)'] = round(df.groupby(columns)['vol'].mean(),2)

            output['平均成交额(亿)'] = round(df.groupby(columns)['amount'].mean(),2)
            #依据ts_code进行降序,排序后的数据集替换原来的数据
            output.sort_values('ts_code', ascending=False, inplace=True)
            #改列值名字,将ts_code改成name+‘_count’的形式
            output.rename(columns={
    
    'ts_code': name + '合计'}, inplace=True)
            return output

        for i in ['industry', 'exchange', 'area']:
            # 对涨停的股票分析
            output_limit_up = get_output(limit_up, columns=i, name='涨停').reset_index()
            # 对跌停的股票分析
            output_limit_down = get_output(limit_down, columns=i, name='跌停').reset_index()
            # 对全量的股票分析
            output_total = get_output(df, columns=i, name='全部').reset_index()
            #添加表格开头类别说明
            tabletext = file.add_paragraph()
            tabletext.add_run('类别:').bold = True
            tabletext.add_run('{}      '.format(str(i))).font.color.rgb = RGBColor(222, 125, 44)

            # print(output_limit_up)
            # print(output_limit_down)
            # print(output_total)
            for j in [output_limit_up, output_limit_down]:   #, output_total
                #之所以生成文件慢是因为output_total这个统计需要长时间计算,如果需要速度快你可以试着把output_total去掉,当然你也可以在上面的列表内加上,变成for j in [output_limit_up, output_limit_down, output_total]
                tb = file.add_table(rows=len(j.index)+1, cols=len(j.columns),style='Medium Grid 3 Accent 1')
                tb.autofit = True  #关闭表格行宽自适应
                for x in range(len(j.columns)):
                    tb.cell(0, x).text = j.columns[x]  #添加表列头
                    #tb.cell(0, x).width = Inches(1.2)  #设置行宽
                    tb.cell(0, x).paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER  #文字居中
                for row in range(len(j.index)):
                    for col in range(len(j.columns)):
                        tb.cell(row+1, col).text = str(j.iloc[row, col]) #设置行宽
                        tb.cell(row+1, col).paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER #文字居中
                file.add_paragraph('\n')    #表格换行
            file.add_page_break()    #每制成一个类别表格分一次页面
        #之所以生成文件慢是因为output_total这个统计需要长时间计算,如果需要速度快你可以试着把output_total去掉
        #生成一个docx文件我的电脑需要3到4分钟左右
        file.save('D:\\stock_analysis\\{}_分析报告.docx'.format(str(date_now)))
        print('{}_分析报告分析完成'.format(str(date_now)))

if __name__=="__main__":
    t1 = threading.Thread(target=get_all_stockdata, args=(20200401, 20200425))
    time.sleep(1)
    t1.start()
    t1.join()

    t2 = threading.Thread(target=output_mysql)
    time.sleep(1)
    t2.start()

    t3 = threading.Thread(target=get_analysis_stockdata, args=(20200401, 20200425))
    t3.start()

tushare注册链接:link

猜你喜欢

转载自blog.csdn.net/Wilburzzz/article/details/107024280