【手把手教你】搭建自己的量化分析数据库

引 言

量化交易的分析基础是数据,包括股票历史交易数据、上市公司基本面数据、宏观和行业数据等。随着信息流量的日益膨胀,学会获取、查询和加工数据信息变得越来越重要。对于鼓捣量化交易的人来说,怎么能说不会玩数据库呢?目前常用的开源(免费)数据库有MySQL、Postgresql 、Mongodb 和 SQLite (Python自带),在2018-2019年DB-Engines 排行榜上位居前十(见下图),可见其使用量和受欢迎程度较高。这几个数据库各有自己的特点和适用环境,关于该学习哪一个或如何学习网上有很多相关资料。本文主要为大家简单介绍如何使用 Python 操作 Postgresql 数据库(其他数据库类似),利用 psycopg2 和 sqlalchemy 实现 postgresql 与 pandas 的 dataframe 进行交互,一步步搭建自己的量化分析数据库。

PostgreSQL的安装与使用

安装 PostgreSQL。到其官网选择适合自己电脑配置的版本下载安装即可(下载地址),安装过程除了设置密码,其他可选择全部默认,如实在不会可参考CSDN上的文章:PostgreSQL安装详细步骤(windows)。安装完之后在安装目录里还可以看到pgAdmin4,这个是自带的数据库图形化工具,最新版是Web 应用程序,有点类似 Python 的 Jupyter Notebook,可用来查看和操作postgresql 数据库。

Python上安装psycopg2 和 sqlalchemy 库。psycopg2 是 Python 连接PostgreSQL数据库的接口,sqlalchemy 应用更广泛,可连接数据库(MySQL, SQLite, PostgreSQL),尤其是对于 pandas 的dataframe型数据,操作起来十分方便。关于这两个 python 库的介绍网上有很多,这里不详细展开,在cmd上使用pip install xxx 进行安装即可。

实例应用

首先,使用 tushare 获取3000多只股票行情数据到本地,使用psycopg2 和 sqlalchemy 为接口,将数据存入本地PostgreSQL数据库中,方便进一步查询和操作。

#先引入后面分析、可视化等可能用到的库

importtushareasts

importpandasaspd

importnumpyasnp

importmatplotlib.pyplotasplt

#正常显示画图时出现的中文和负号

frompylabimportmpl

mpl.rcParams['font.sans-serif']=['SimHei']

mpl.rcParams['axes.unicode_minus']=False

#设置token

token='输入你的token'

pro = ts.pro_api(token)

数据获取函数,默认时间可以随时改动

#如果报错,把tushare升级到最新

defget_data(code,start='20190101',end='20190425'):

df=ts.pro_bar(ts_code=code, adj='qfq', start_date=start, end_date=end)

returndf

交易代码获取函数,获取最新交易日的代码

#获取当前交易日最新的股票代码和简称

defget_code():

codes = pro.stock_basic(list_status='L').ts_code.values

returncodes

插入PostgreSQL 数据库操作,函数里使用了try...except...pass是为了避免某些数据出错导致程序崩溃。

fromsqlalchemyimportcreate_engine

importpsycopg2

engine = create_engine('postgresql+psycopg2://postgres:123456@localhost:5432/postgres')

definsert_sql(data,db_name,if_exists='append'):

#使用try...except..continue避免出现错误,运行崩溃

try:

data.to_sql(db_name,engine,index=False,if_exists=if_exists)

#print(code+'写入数据库成功')

except:

pass

由于行情数据量庞大,下载比较慢,先下载20190101至20190425期间日交易数据,后续再不断更新。

#下载20190101-20190425数据并插入数据库stock_data

#此步骤比较耗费时间,大致25-35分钟左右

forcodeinget_code():

data=get_data(code)

insert_sql(data,'stock_data')

#读取整张表数据

df=pd.read_sql('stock_data',engine)

print(len(df))

#输出结果:270998

#选取ts_code=000001.SZ的股票数据

df=pd.read_sql("select * from stock_data where ts_code='000001.SZ'",engine)

print(len(df))

构建一个数据更新函数,可以下载和插入其他时间周期的数据。2018年1月1日至2019年4月25日,数据就已达到108万条。

#更新数据或下载其他期间数据

defupdate_sql(start,end,db_name):

fromdatetimeimportdatetime,timedelta

forcodeinget_code():

data=get_data(code,start,end)

insert_sql(data,db_name)

print(f'{start}:{end}期间数据已成功更新')

#下载20180101-20181231期间数据

#只需运行一次,不再运行后可以注释掉

#下载数据比较慢,需要20-35分钟左右

start='20180101'

end='20181231'

db_name='stock_data'

#数据下载和存入数据库

update_sql(start,end,db_name)

#使用pandas的read_sql读取数据

df_all_data=pd.read_sql('stock_data',engine)

print(len(df_all_data))

#输出结果:1087050

#查看交易代码和交易日期个数

print(len(df_all_data.ts_code.unique()))

print(len(df_all_data.trade_date.unique()))

#输出结果:3604;319

d=df_all_data.trade_date.unique()

print(d.max())

print(d.min())

2019-04-25T00:00:00.000000000

2018-01-02T00:00:00.000000000

#获取交易日2019年4月25日数据

pd.read_sql("select * from stock_data where trade_date='2019-04-25' ",engine).head()

构建数据查询和可视化函数:

defplot_data(condition,title):

frompyechartsimportBar

fromsqlalchemyimportcreate_engine

engine = create_engine('postgresql+psycopg2://postgres:123456@localhost:5432/postgres')

data=pd.read_sql("select * from stock_data where+"+ condition,engine)

count_=data.groupby('trade_date')['ts_code'].count()

attr=count_.index

v1=count_.values

bar=Bar(title,title_text_size=15)

bar.add('',attr,v1,is_splitline_show=False,linewidth=2)

returnbar

查询股价低于2元个股数据分布

c1="close<2"

t1="股价低于2元个股时间分布"

plot_data(c1,t1)

查询股价日涨幅超过9.5%个股数据分布:

c2="pct_chg>9.5"

t2="股价涨幅超过9.5%个股时间分布"

plot_data(c2,t2)

查询股价日跌幅超过-9.5%个股数据分布:

c3="pct_chg<-9.5"

t3="股价跌幅超过-9.5%个股时间分布"

plot_data(c3,t3)

结合选股策略对数据库进行查询和提取数据:

#筛选代码

#获取当前交易的股票代码和名称

defget_new_code(date):

#获取当前所有交易股票代码

df0 = pro.stock_basic(exchange='', list_status='L')

df1 =pro.daily_basic(trade_date=date)

df=pd.merge(df0,df1,on='ts_code')

#剔除2017年以后上市的新股次新股

df=df[df['list_date'].apply(int).values<20170101]

#剔除st股

df=df[-df['name'].apply(lambdax:x.startswith('*ST'))]

#剔除动态市盈率为负的

df=df[df.pe_ttm>0]

#剔除大市值股票

df=df[df.circ_mv<10**5]

#剔除价格高于20元股票

#df=df[df.close<20]

codes=df.ts_code.values

returncodes

len(get_new_code('20190425'))

#输出结果:46

importtalibasta

#20日均线交易策略

deffind_stock(date):

f_code=[]

forcodeinget_new_code(date):

try:

data=df_all_data.loc[df_all_data.ts_code==code].copy()

data.index=pd.to_datetime(data.trade_date)

data=data.sort_index()

data['ma_20']=ta.MA(data.close,timeperiod=20)

ifdata.iloc[-1]['close']>data.iloc[-1]['ma_20']:

f_code.append(code)

except:

pass

returnf_code

fs=find_stock('20190305')

print(f'筛选出的股票个数:{len(fs)}')

iffs:

df_find_stocks=pd.DataFrame(fs,columns=['ts_code'])

#将选出的股票存入数据库,如果表已存在,替换掉,相当于每次更新

insert_sql(df_find_stocks,'find_stocks',if_exists='replace')

print('筛选的股票已入库')

筛选出的股票个数:9

筛选的股票已入库

#查看数据库中筛选的股票池

codes=pd.read_sql('find_stocks',engine)

codes=codes.values.tolist()

codes=[c[0]forcincodes]

#print(codes)

对筛选的股票作进一步分析:

select_data=pd.DataFrame()

forcodeincodes:

try:

df_= df_all_data[df_all_data.ts_code.values==code]

df_.index=pd.to_datetime(df_.trade_date)

df_=df_.sort_index()

select_data[code]=df_.close

except:

pass

select_data.fillna(method='ffill',inplace=True)

select_data.tail()

ret=select_data.apply(lambdax:x/x.shift(1)-1)

ret=ret.dropna()

ret.tail()

prod_ret=ret.apply(lambdax:(1+x).cumprod())

prod_ret.plot(figsize=(12,5))

plt.xlabel('',fontsize=15)

plt.title('股票池累计净值',size=15)

ax = plt.gca()

ax.spines['right'].set_color('none')

ax.spines['top'].set_color('none')

plt.show()

#根据代码从数据库中获取数据

defget_data_from_sql(code):

fromsqlalchemyimportcreate_engine

engine = create_engine('postgresql+psycopg2://postgres:123456@localhost:5432/postgres')

data=pd.read_sql(f"select * from stock_data where ts_code='{code}'",engine)

data.index=pd.to_datetime(data.trade_date)

data=data.sort_index()

#计算20日均线

data['ma20']=data.close.rolling(20).mean()

returndata

利用20日均线交易策略,搭建数据查询和可视化函数kline_plot(),完整代码将分享在知识星球上。对选出的股票日K线、20日均线、成交量、买入(buy)和卖出(sell)信号进行可视化。

kline_plot('002790.SZ')

kline_plot('300573.SZ')

结 语

数据库操作其实要学的东西还很多,本文旨在抛砖引玉,简单介绍使用Python对PostgreSQL数据库与dataframe型数据进行交互,一步步搭建自己的量化分析数据库。由于文中用到的数据仅为百万条左右,实际上使用excel的csv来读写也很快,并且比较直观,但随着数据的不断增多,要建立自己完善的量化分析系统,数据库的学习就显得尤为重要了。注意,文中所提及选股方式和股票代码仅作为示例应用,不构成任何投资建议。

关于Python金融量化

专注于分享Python在金融量化领域的应用。加入知识星球,可以免费获取30多g的量化投资视频资料、公众号文章Python完整源码、量化投资前沿分析框架,与博主直接交流、结识圈内朋友等。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容