想起来很久之前实习面试某基金公司数据科学家岗位的时候,终面因为做的期货系统翻车了,现在把代码找出来,有几个点还是没改,就这么放上面引以为鉴吧
PS:需要注意到零点变为第二天时的情况,再就是能做好看点就做好看点吧,比如多画图。。。
'''
@Description:期货分钟 bar
@Author:Jiawei
@Email:
@Date:2022/3/27
@LastEditors:Jiawei
@LastEidtTime:2022/3/31
'''
import pandas as pd
from datetime import datetime
from openpyxl import Workbook
from openpyxl.utils.dataframe import dataframe_to_rows
#pandas初始化设置
#显示所有行
pd.set_option('display.max_columns', None)
#显示所有列
pd.set_option('display.max_rows', None)
#设置最大宽度
pd.set_option('display.width', 999)
#设置字段value显示长度
pd.set_option('max_colwidth', 99)
#文件初始化
#读取数据文件到pandas
data = pd.read_parquet('interview.parquet', engine='pyarrow')
#数据基础清洗,去掉有缺失值的行
data = data.dropna()
#去重后获取所有交易所的ID
ExID = data['ExchangeID'].unique()
#去重后获取所有合约的ID
InsID = data['InstrumentID'].unique()
'''数据检查'''
def data_check():
print('***************************开始进行数据检查***************************')
#统计LocalTime的单调性
print('正在统计LocalTime单调性,请稍等...')
local_time = data['LocalTime']
#遍历所有localtime,存储相邻元素之间的大小关系
monotony = [local_time[i] <= local_time[i + 1] for i in range(len(local_time) - 1)]
#如果列表中存在false则说明存在大于的情况,此时再分类判断
if False in monotony:
#如果存在false的情况下同时又存在true,则说明同时存在大于和小于等于情况,即为不单调
if True in monotony:
print('LocalTime不具有单调性')
#仅存在false则证明都为大于的情况,即为单减
else:
print('LocalTime单调递减')
else:
print('LocalTime单调递增')
#分交易所统计
for ExchangeID in ExID:
#分交易所对UpdateTime,UpdateMullisec进行遍历,查看其单调性
print('正在统计{}的(UpdateTime,UpdateMullisec)单调性,请稍等...'.format(ExchangeID))
#将原始数据保存到time_exchange
data_exchange = data[data['ExchangeID'] == ExchangeID]
#由于UpdateTime为string类,故sort后再使用字符串比对进行比较
#开辟新空间,获取一个排序后的UpdateTime,UpdateMullisec数据time_ordered
time_ordered = data_exchange.copy()
#sort_values对两列进行排序时,顺序均为升序,先UpdateTime后UpdateMillisec
time_ordered = time_ordered.sort_values(by=['UpdateTime', 'UpdateMillisec'], ascending=[True, True])
#设置计数器,确保全部数据遍历结束后再输出单调性
count = 0
#由于默认数据为实时流式接受,故默认该数据不会递减接受(若是需要考虑逆序需要增加递减序列,增加一次比对)
for i in range(len(data_exchange)):
if (data_exchange.iloc[i] != time_ordered.iloc[i]).any():
print(ExchangeID + '(UpdateTime, UpdateMyllisec)不单调')
break
count = count + 1
if (count == len(data_exchange)):
print(ExchangeID + '(UpdateTime, UpdateMyllisec)单调递增')
#探索每个交易所的tick的推送频率
#本方案关注获取的数据中平均每秒的推送频率
print('正在探索{}的tick推送频率,请稍等...'.format(ExchangeID))
num = len(data_exchange)
time = data_exchange.iloc[num - 1]["LocalTime"] - data_exchange.iloc[0]["LocalTime"]
#LocalTime推送的时间为从1970年1月1日00:00:00开始按秒计算的偏移量,查询后发现为纳秒
time = time / 1000000000
frequence = num / time
print('{}的tick推送频率为{}次/秒'.format(ExchangeID, frequence))
#探索ActionDay,TradingDay和实际交易时间的关系
#百度获知部分交易所的ActionDay,TradingDay在一天的某一时间前会出现差异,因此验证此规律并找出分界时间点
print('正在探索{}的ActionDay,TradingDay和实际交易时间的关系,请稍等...'.format(ExchangeID))
#设置标志位,如果遍历结束之后ActionDay和TradingDay的关系都没有变化,则说明一致,如果变化了则置为0
flag = 1
for i in range(len(data_exchange) - 1):
if data_exchange.iloc[i]['ActionDay'] != data_exchange.iloc[i]['TradingDay'] and \
data_exchange.iloc[i + 1]['ActionDay'] == data_exchange.iloc[i + 1]['TradingDay']:
print('{}的ActionDay,TradingDay在{}及之前不一致,之后一致'.format(ExchangeID, data_exchange.iloc[i]['UpdateTime']))
flag = 0
break
if flag:
print(ExchangeID + '的ActionDay,TradingDay一致')
#自定标准数据检查
#根据Volume和Turnover的单调性判断是否有异常数据
#根据合约号进行分类
for instrument in InsID:
data_instrument = data[data['InstrumentID'] == instrument]
volume = data_instrument['Volume']
turnover = data_instrument['Turnover']
#遍历该合约下所有Volume和Turnover,存储相邻元素之间的大小关系
for i in range(len(volume) - 1):
if volume.iloc[i] > volume.iloc[i + 1]:
print('Volume Data Exception:{}'.format(data_instrument).iloc[i + 1])
for i in range(len(turnover) - 1):
if turnover.iloc[i] > turnover.iloc[i + 1]:
print('Turnover Data Exception:{}'.format(data_instrument).iloc[i + 1])
'''分钟bar生成'''
def minute_bar_generator():
print('***************************开始生成分钟bar***************************')
#经百度查询知LocalTime为纳秒级,故经过单位转换取整后再使用strftime转换为time格式,便于后续分钟计算
data['LocalTime'] = [datetime.fromtimestamp(j // 1000000000).strftime("%Y-%m-%d %H:%M:%S") for j in data['LocalTime']]
#初始化参数
#由于字段包含映射信息,故采用字典存储结果,每个合约对应一个dataframe
result = {}
#tmp储存该分钟内价格信息
tmp = {}
#记录该合约上次交易的成交价
lastprice = {}
#用于判断成交量是否改变,如果改变证明交易发生,则把数据写入字典
prevolume = {}
#判断新送入的数据是否属于之前的时间
time_record = []
#根据合约号进行分类,定义当地时间,开盘价,最高价,最低价,收盘价
for instrument in InsID:
result[instrument] = pd.DataFrame(columns=['LocalTime', 'Opening', 'Highest', 'Lowest', 'Closing'])
tmp[instrument] = list()
ins_data = data[data["InstrumentID"] == instrument]
lastprice[instrument] = ins_data.iloc[0]['LastPrice']
prevolume[instrument] = ins_data.iloc[0]['Volume']
#根据所提供的tick数据,模拟流式的行情接收(按照LocalTime顺序一条条到达)
#问题:查看了拿到的数据之后发现成交时间并不是递增的,那么开盘价到底是推送来的第一个数据还是按照时间排序之后的第一个数据
num = len(data)
for i in range(num):
localtime = data.iloc[i]["LocalTime"]
#保证同一分钟数据时间戳相同,防止因为秒数不同后续认定为不同时间,将秒位置位00,方便后续处理
localtime = localtime[:-2]+'00'
#扫描到新的时间即新增时间,或者扫到最后一个数据
if not (localtime in time_record) or i == (num-1):
#新增时间
time_record.append(localtime)
#进入该分钟内
for instrument in InsID:
#tmp[instrument]为空
if tmp[instrument] == list():
opening = lastprice[instrument]
high_p = 0
low_p = 0
closing = lastprice[instrument]
#存在数据时
else:
opening = tmp[instrument][0]
high_p = max(tmp[instrument])
low_p = min(tmp[instrument])
closing = tmp[instrument][-1]
#将更新后的该分钟数据存入该合约的dataframe
new = pd.DataFrame({"LocalTime": [time_record[-1]], 'Opening': [opening], 'Highest': [high_p], 'Lowest': [low_p], 'Closing': [closing]})
result[instrument] = result[instrument].append(new)
#存储完后进行重置,防止后续遍历中判断出错
tmp[instrument] = list()
ins = data.iloc[i]['InstrumentID']
#如果成交量变化,证明发生过交易,则更新上次成交价
if data.iloc[i]['Volume'] - prevolume[ins] > 0:
tmp[ins].append(data.loc[i]['LastPrice'])
#更新合约的数据
lastprice[ins] = data.iloc[i]['LastPrice']
prevolume[ins] = data.iloc[i]['Volume']
print('***************************分钟bar已生成,正在写入xlsx***************************')
#利用workbook接口将数据写入xlsx
book = Workbook()
# 存储数据,每个合约对应一个sheet
for instrument in InsID:
sheet = book.create_sheet(instrument)
for row in dataframe_to_rows(result[instrument], index=False, header=True):
sheet.append(row)
#删除第一页sheet
del book["Sheet"]
book.save("MinuteBar.xlsx")
print('***************************分钟bar程序已完成***************************')
return
#本脚本可以被import到其他脚本中使用
if __name__ == '__main__':
data_check()
minute_bar_generator()