1. 问题的发现
1.1 问题代码
在jupyter notebook中,我编写了下面的一段下载合约K线数据的代码:
from datetime import datetime,date,timedelta
from vnpy.trader.utility import extract_vt_symbol
from vnpy.trader.constant import Exchange,Interval
from vnpy.trader.object import HistoryRequest
from vnpy.trader.database import get_database
from vnpy.trader.datafeed import get_datafeed
df = get_datafeed()
end = datetime.now()
start = end.replace(month=11)
start,end
req = HistoryRequest(symbol='rb2201',exchange=Exchange.SHFE,start=start,end=end,interval=Interval.DAILY)
# 语句1
bars = df.query_bar_history(req)
# 语句2
db.save_bar_data(bars)
# 语句3
for bar in bars:
print(bar)
1.2 问题描述:
执行上面代码,当执行到语句3的时候,提示:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-11-f72a667896f4> in <module>
1 for bar in bars:
----> 2 print(bar)
D:\ProgramFiles\vnstudio\lib\dataclasses.py in __repr__(self)
AttributeError: 'BarData' object has no attribute 'gateway_name'
如果把语句2注释掉,执行语句3的时候有没有任何问题,可以把从米筐接口读取的rb2201.SHFE的bar全部打印出来。
2. 错误的解决方法
由于本人使用的是mysql数据库,所以需要对vnpy_mysql目录下的mysql_database.py进行修改,使用其他数据库的用户其实也有同样的问题。解决方法也是一样的,不再每个都说。
方法:
打开vnpy_mysql\mysql_database.py,在文件头部添加下面的语句:
import copy # hxxjava
对下面两个函数进行如下的修改,save_bar_data()和save_tick_data()都有同样的问题,修改之处语句在函数中注明了,错误原因是在遍历保存bars和ticks的过程中,因为数据库的要求,改变了每个bar和tick的 __dict__ 字典,保存动作做完了以后,列表bars和列表ticks的每个bar和tick已经被改得面目全非了,里面的gateway_name已经没有了。
修改过的代码后面有 # hxxjava change注释标记。
修改后的代码如下:
def save_bar_data(self, bars: List[BarData]) -> bool:
"""保存K线数据"""
# 读取主键参数
bar = bars[0]
symbol = bar.symbol
exchange = bar.exchange
interval = bar.interval
# 将BarData数据转换为字典,并调整时区
data = []
for bar in bars:
bar.datetime = convert_tz(bar.datetime)
d = copy.copy(bar.__dict__) # hxxjava change
d["exchange"] = d["exchange"].value
d["interval"] = d["interval"].value
d.pop("gateway_name")
d.pop("vt_symbol")
data.append(d)
# 使用upsert操作将数据更新到数据库中
with self.db.atomic():
for c in chunked(data, 50):
DbBarData.insert_many(c).on_conflict_replace().execute()
# 更新K线汇总数据
overview: DbBarOverview = DbBarOverview.get_or_none(
DbBarOverview.symbol == symbol,
DbBarOverview.exchange == exchange.value,
DbBarOverview.interval == interval.value,
)
if not overview:
overview = DbBarOverview()
overview.symbol = symbol
overview.exchange = exchange.value
overview.interval = interval.value
overview.start = bars[0].datetime
overview.end = bars[-1].datetime
overview.count = len(bars)
else:
overview.start = min(bars[0].datetime, overview.start)
overview.end = max(bars[-1].datetime, overview.end)
s: ModelSelect = DbBarData.select().where(
(DbBarData.symbol == symbol)
& (DbBarData.exchange == exchange.value)
& (DbBarData.interval == interval.value)
)
overview.count = s.count()
overview.save()
return True
def save_tick_data(self, ticks: List[TickData]) -> bool:
"""保存TICK数据"""
# 将TickData数据转换为字典,并调整时区
data = []
for tick in ticks:
tick.datetime = convert_tz(tick.datetime)
d = copy.copy(tick.__dict__) # hxxjava change
d["exchange"] = d["exchange"].value
d.pop("gateway_name")
d.pop("vt_symbol")
data.append(d)
# 使用upsert操作将数据更新到数据库中
with self.db.atomic():
for c in chunked(data, 50):
DbTickData.insert_many(c).on_conflict_replace().execute()
return True