- 前言
- 项目主要步骤
- 数据来源
- 计算结果的准确性:(与choice比较)
- 一、环境配置
- 二、项目步骤
- 1. 定义计算MACD指标的函数
- 2. 定义格式转换与数据处理的函数
- 3. main函数
- 三、踩的坑&经验小结
- 1. 关于 使用其他复权类型数据来计算
- 2. 关于 空值的处理办法
- 3. 关于 dfToSQL()
- 4. 关于数据库:
- 5. dataframe按时间排列的顺序
前言
今天分享本人写论文时做的一个小项目,花了好几天时间写的(论文最后竟然还没用到勞)
本文项目依据沪深300指数的300只成分股所有历史日线数据,计算出其全部历史MACD指标值,计算结果存为本地csv文件或存入数据库,以备后续使用。
指标值包括:EMA(12),EMA(26),DIF, DEA ,MACD;
示例为价格前复权,有需要的朋友可以自行改动为其他复权类型,要修改的地方文内有提示。
- 从本地csv文件 list_hushen300.csv 获取股票列表
- 从tushare拿到300只股票的数据,分别存入mysql,
- 从mysql提取,计算指标值,最后再存入mysql。
本文还提供了:将拿到的tushare日线数据、计算出的指标值存为本地csv文件的函数。
数据来源数据来源是tushare社区,在此感谢社区为广大学生提供的免费优质服务,我个人使用体验很好,也推荐大家从这里拿数据。
计算结果的准确性:(与choice比较)(任选一只进行对比)
计算结果:
choice:
一、环境配置
IDE:pycharm
数据库:mysql, navicat
计算结果检查:choice金融终端
数据来源:tushare(需满足积分条件)
使用到的python库:
import tushare as ts import pymysql as mdb import numpy as np import pandas as pd import time import datetime import copy import sqlalchemy from sqlalchemy import create_engine import sys二、项目步骤 1. 定义计算MACD指标的函数
代码如下:
def ema(data, period=0, column='close', name='ema'):
for i in range(len(data)):
if i == 0:
data.loc[i, name] = data.loc[i, column]
else:
data.loc[i, name] = (2*data.loc[i, column]+(period-1)*data.loc[i-1, name])/(period+1)
return data
def macd(data, period_long=26, period_short=12, period_signal=9, column='close'):
data = ema(data, period_long, column, 'ema'+str(period_long))
data = ema(data, period_short, column, 'ema'+str(period_short))
data['macd_dif'] = data['ema'+str(period_short)] - data['ema'+str(period_long)]
data = ema(data, period_signal, 'macd_dif', 'macd_dea')
data['macd_macd'] = 2 * (data['macd_dif'] - data['macd_dea'])
return data
2. 定义格式转换与数据处理的函数
def getAStockDaily(ts_code, adj):
"""
本函数实现从通用行情接口获取单只股票的所有历史日线数据,价格前复权,返回dataframe类型的data
本函数须和dfToCsv一起用
"""
if adj == "qfq":
data = ts.pro_bar(ts_code=ts_code, adj='qfq', start_date='', end_date='') # 不需要用format()
if adj == "hfq":
data = ts.pro_bar(ts_code=ts_code, adj='hfq', start_date='', end_date='')
if adj == "None":
data = ts.pro_bar(ts_code=ts_code, adj='None', start_date='', end_date='')
return data
def dfToSQL(ts_code, df, sqlEngine):
"""
本函数将dataframe存入mysql
"""
df = df.loc[:, ["ts_code", "trade_date", "close"]]
df = df.fillna(0.0, inplace=True)
df.to_sql(name=ts_code.lower(), con=sqlEngine, index=False, if_exists='replace') # 在函数内将ts_code转为小写再作表名
return 0
def dfmacdToCsv(ts_code,df, file):
"""
本函数专门将计算得出的macd的dateframe存到本地csv文件中。具有专用性
"""
df.to_csv(file + "{}.csv".format(ts_code),
columns=["ts_code", "trade_date", "close", "ema26", "ema12", "macd_dif", "macd_dea", "macd_macd"],
encoding='utf_8_sig')
# 未设置参数na_rep=0.0
def dfToCsv(ts_code, df, file):
"""
本函数专门实现将从tushare通用行情接口获取的dataframe以csv存储到本地file文件中。具有专用性
本函数必须和getAStockDaily一起用;
"""
df.to_csv(file + "{}.csv".format(ts_code),
columns=["ts_code", "trade_date", "open", "low", "high", "close"],
encoding='utf_8_sig',
na_rep=0.0)
return 0
3. main函数
file_localCsvQfq = "data/qfq/"
file_macd = "data/stockmacd/"
""" 取沪深300列表 存入list """
list_hushen300 = pd.read_csv("../data/list_hushen300.csv")
rowNum = len(list_hushen300.index)
list300 = list(list_hushen300.iloc[1:rowNum,2])
print(list300)
""" 计算沪深300的MACD,然后存到数据库 """
''' 读取沪深300列表 ,存入list'''
list_hushen300 = pd.read_csv("data/list_hushen300.csv")
rowNum = len(list_hushen300.index)
list300 = list(list_hushen300.iloc[0:rowNum, 2]) # 应该是iloc[0:rowNum,2]),详细原因见dataframe用法
''' 建立数据库连接 '''
conn = mdb.connect(host="localhost", port=3306, user='root', passwd='123456', db='stockdate_qfq', charset='utf8')
engine = create_engine("mysql+mysqldb://username:password@localhost:3306/stockdate_qfq?charset=utf8")
engine2 = create_engine("mysql+mysqldb://username:password@localhost:3306/stockmacd?charset=utf8")
cursor = conn.cursor() # 游标其实没有用到
''' 循环执行 一直到计算macd后存到MySQL'''
#使用两层循环来控制节奏
for i in range(10):
print("第"+str(i)+"批执行中,共10批") # 打算用三次
# list_period = list300[10*i:10*i+10] # 第一次
# list_period = list300[10*i+100:10*i+110] # 第二次
list_period = list300[10*i+200:10*i+210] # 第三次
for ts_code in list_period:
# 访问tushare接口获取dateframe:
data =getAStockDaily(ts_code, "qfq")
''' 转为本地csv: '''
dfToCsv(ts_code, data, file_localCsvQfq)
''' dateframe转为SQL'''
# df_toSQL()前的准备工作:
data = data.loc[:, ["ts_code", "trade_date", "close"]]
data.fillna(0.0, inplace=True)
# 转为SQL:
get_StocksDataFunc.dfToSQL(ts_code, data, engine)
'''计算MACD值'''
sql1 = "SELECT ts_code, trade_date,`close` FROM `{}` where ts_code='{}' order by trade_date asc".format(ts_code, ts_code)
df = pd.read_sql(sql1, conn)
df_macd = df[['ts_code', 'trade_date', 'close']]
df = macd(df_macd, 26, 12, 9, 'close')
# print(df_macd)
''' 将计算的MACD值存入本地CSV '''
dfmacdToCsv(ts_code, df_macd, file_macd)
''' 将计算的MACD值存入SQL'''
dfToSQL(ts_code, df_macd, engine2)
''' 等待10s '''
print("{}已执行完毕,10s后继续执行".format(ts_code))
time.sleep(5)
time.sleep(20)
三、踩的坑&经验小结
这里对文章进行总结:
1. 关于 使用其他复权类型数据来计算在使用getgetAStockDaily()函数时传入所需的复权类型并调整相应文件夹命名即可
2. 关于 空值的处理办法产生空值的原因:tushare获取的数据中,部分股票较早年份的数据是没有的
影响:导致macd指标值计算有小的偏差(时间越早,影响越小),但在可接受的范围内;由于传回的日线数据dataframe中缺失数据标记是NaN,这导致计算前必须把NaN转为0.0,
处理办法:
dfToSQL()中对data使用dataframe的函数fillna(0.0, inplace=True)
dfToCsv()中,添加to_csv()函数的参数 na_rep=0.0, 将所有空值NaN转为0
3. 关于 dfToSQL()使用df=df.loc[:, [“ts_code”, “trade_date”, “close”]] 来选择要保存的字段,字段的类型自动完成(也可以选择在函数外部来完成这一任务);
使用fillna() 将NaN值转化为0.0 , 类型是默认的,暂时不需要关心(如果不使用参数来实现转化,可以用SQL语句);
dfToSQL()中为什么有name=ts_code.lower() ?
存入数据库后数据表的表名会变为(显示为)小写,同时在做select时不区分大小写也都可以,但df格式使engine对表的大小写敏感,所以用df.to_sql(file_basename.lower()…) 将tushare传回的name无论大小写都转为小写再作表名
dfToSQL()中控制字段类型与长度等时,可以用to_sql()的dtype参数,但十分不推荐,因为看着就不好写,哈哈,感兴趣的同学可以尝试一下;
4. 关于数据库:为什么建了两个数据库的engine ?
如果存入mysql与取出mysql中数据用了同一个engine 会报错,因为赶时间,我就建了两个来用,没有做其他测试;
从tushare基础日线接口与通用行情接口取到的数据的顺序都是:2022-1998 新到旧,但是本文的计算方法需要从旧到新开始计算,需要调整,本文采取了sql的方法来解决,感觉很好用:做select时 用一下order by trade_date asc
文章到这里就结束了,仓促写就,希望能帮到大家。本人第一篇博文,还希望大家给点个赞哈哈



