阅读建议:
该篇文章主要解读YahooFinanceCSVData类中的属性方法
从内容上看,本章与第五、六、七篇属于同一系列,应该合起来阅读
就本章而言,建议从后往前阅读,本章分为10个类,按第10、9...、1的顺序的类来看。
类图先上:
![260ef1bfeb36c2d9b82856ed53ab2254.png](https://img-blog.csdnimg.cn/img_convert/260ef1bfeb36c2d9b82856ed53ab2254.png)
代码解读:
1、
YahooFinanceCSVData
class YahooFinanceCSVData(feed.CSVDataBase):
lines = ('adjclose',)
params = (
('reverse', False),
('adjclose', True),
('adjvolume', True),
('round', True),
('decimals', 2),
('roundvolume', False),
('swapcloses', False),
)
def start(self):
def _loadline(self, linetokens):
解读:
- 在MetaLineSeries元类的__new__方法中,YahooFinanceCSVData及其父类中的lines属性会被转化为Lines类
- 在MetaLineRoot元类的__donew__方法中,YahooFinanceCSVData类在生成对象的过程中,类属性params会赋值给对象属性,同时,赋值给对象属性self.p
- start函数分别在父类CSVDataBase和祖先类AbstractDataBase中有定义,在祖先类AbstractDataBase中设置队列容器及初始化状态0,在父类CSVDataBase中打开文件self.p.dataname并赋值给self.f,在类YahooFinanceCSVData中读取self.f的内容放入内存数据并重新赋值给self.f
2、
CSVDataBase
class CSVDataBase(with_metaclass(MetaCSVDataBase, DataBase)):
f = None
params = (('headers', True), ('separator', ','),)
def start(self):
def stop(self):
def preload(self):
def _load(self):
def _getnextline(self):
解读:
- 设计:该类用于处理csv数据,包括打开文件,读取数据且格式化。子类只需覆盖_loadline方法即可使用
- start方法:打开文件并赋值给self.f, 给分隔符self.separator赋值
- _load方法:从文件句柄self.f中读取一行,内容分割后传递给方法_loadline,读取失败返回False
- _getnextline方法:从文件句柄self.f中读取一行,内容分割后返回
3、
DataBase
class DataBase(AbstractDataBase):
pass
4、
AbstractDataBase
class AbstractDataBase(with_metaclass(MetaAbstractDataBase,
dataseries.OHLCDateTime)):
params = (
('dataname', None),
('name', ''),
('compression', 1),
('timeframe', TimeFrame.Days),
('fromdate', None),
('todate', None),
('sessionstart', None),
('sessionend', None),
('filters', []),
('tz', None),
('tzinput', None),
('qcheck', 0.0), # timeout in seconds (float) to check for events
('calendar', None),
)
(CONNECTED, DISCONNECTED, CONNBROKEN, DELAYED,
LIVE, NOTSUBSCRIBED, NOTSUPPORTED_TF, UNKNOWN) = range(8)
_NOTIFNAMES = [
'CONNECTED', 'DISCONNECTED', 'CONNBROKEN', 'DELAYED',
'LIVE', 'NOTSUBSCRIBED', 'NOTSUPPORTED_TIMEFRAME', 'UNKNOWN']
@classmethod
def _getstatusname(cls, status):
return cls._NOTIFNAMES[status]
_compensate = None
_feed = None
_store = None
_clone = False
_qcheck = 0.0
_tmoffset = datetime.timedelta()
# Set to non 0 if resampling/replaying
resampling = 0
replaying = 0
_started = False
def _start_finish(self):
def _start(self):
def _timeoffset(self):
def _getnexteos(self):
def _gettzinput(self):
def _gettz(self):
def date2num(self, dt):
def num2date(self, dt=None, tz=None, naive=True):
def haslivedata(self):
def do_qcheck(self, onoff, qlapse):
def islive(self):
def put_notification(self, status, *args, **kwargs):
def get_notifications(self):
def getfeed(self):
def qbuffer(self, savemem=0, replaying=False):
def start(self):
def stop(self):
def clone(self, **kwargs):
def copyas(self, _dataname, **kwargs):
def setenvironment(self, env):
def getenvironment(self):
def addfilter_simple(self, f, *args, **kwargs):
def addfilter(self, p, *args, **kwargs):
def compensate(self, other):
def _tick_nullify(self):
def _tick_fill(self, force=False):
def advance_peek(self):
def advance(self, size=1, datamaster=None, ticks=True):
def preload(self):
def _last(self, datamaster=None):
def _check(self, forcedata=None):
def load(self):
def _load(self):
def _add2stack(self, bar, stash=False):
def _save2stack(self, erase=False, force=False, stash=False):
def _updatebar(self, bar, forward=False, ago=0):
def _fromstack(self, forward=False, stash=False):
def resample(self, **kwargs):
def replay(self, **kwargs):
解读:
- 在类属性params主要提供时间相关的参数,包括date相关,time相关,timezone相关,calendar相关
- 方法_start_finish,时间相关变量赋值给对象属性,_tz, _tzinput, fromdate, _sessionstart, _calendar等
- 方法_gettzinput和_gettz中的tzparse函数,用于给参数tz赋予替换时区的方法
- 方法date2num和num2date用于添加时区来考虑数字和日期的相互转换
- 方法put_notification和get_notifications处理通知和状态相关的变量
- 方法qbuffer用于给数据线设置qbuffer
- 方法addfileter_simple和addfilter负责给属性_filters添加过滤器
- 方法compensate用于绑定其他的对象,是信息共通
- 方法_tick_nullify用于在K线合成过程中返回None值
- 方法_tick_fill将各条线的最新值和第0条数据线的最新值赋值给对象
- 方法adcance_peek用于返回下一个时间,如果没有,则返回无穷值
- 方法advance用于数据线在不同情况下往前推进size步
- 方法next用于返回是否新的bar生成
- 方法_last和_check用于过滤器对数据添加干预
- 方法load用于循环下载数据,同时进行时间处理和过滤处理
- 方法_fromstack从属性_barstack或_barstach中获取数据并赋值给line
- 方法_add2stack和_save2stack分别往属性_barstack或_barstach中添加bar
- 方法_updatebar用于执行bar来更新数据线
- 方法resample和replay用于添加特定的过滤器
5、
OHLCDateTime
class OHLCDateTime(OHLC):
lines = (('datetime'),)
解读:
- 添加类属性lines中的'datetime'
6、
OHLC
class OHLC(DataSeries):
lines = ('close', 'low', 'high', 'open', 'volume', 'openinterest',)
解读:
- 添加类属性lines中的开高低收成交量持仓量
7、
DataSeries
class DataSeries(LineSeries):
plotinfo = dict(plot=True, plotind=True, plotylimited=True)
_name = ''
_compression = 1
_timeframe = TimeFrame.Days
Close, Low, High, Open, Volume, OpenInterest, DateTime = range(7)
LineOrder = [DateTime, Open, High, Low, Close, Volume, OpenInterest]
def getwriterheaders(self):
def getwritervalues(self):
def getwriterinfo(self):
解读:
- 该类开始专注数据线中的数值,主要用于查询各条数据线名称及最新值
- 定义默认类属性:plotinfo
- 数据线顺序LineOrder,数据线的前7条分别为日期,开、高、低、收、成交价、持仓量
- getwriteheaders/getwritervalues方法:得到所有数据线名称/最新数据
- getwritervalues方法:获取关于name,timeframe和compression相关的有序字典
8、
LineSeries
class LineSeries(with_metaclass(MetaLineSeries, LineMultiple)):
plotinfo = dict(
plot=True,
plotmaster=None,
legendloc=None,
)
csv = True
@property
def array(self):
def __getattr__(self, name):
def __len__(self):
def __getitem__(self, key):
def __setitem__(self, key, value):
def __init__(self, *args, **kwargs):
def plotlabel(self):
def _plotlabel(self):
def _getline(self, line, minusall=False):
def __call__(self, ago=None, line=-1):
def forward(self, value=NAN, size=1):
def backwards(self, size=1, force=False):
def rewind(self, size=1):
def extend(self, value=NAN, size=0):
def reset(self):
def home(self):
def advance(self, size=1):
解读:
- lineSeries很重要,从类图上看,它使用了新的元类MetaLineSeries。从功能角度思考,它最终要的是为实例属性self.lines赋予相应的Lines对象
- 通过Lines对象,来实现对数据的查询和统计功能
a. 统计功能:__len__,
b. 查询功能:__getitem__,forward/backwards/rewind/extend/reset/home/advance c. 改功能:__setitem__
9、
LineMultiple
class LineMultiple(LineRoot):
def reset(self):
def _stage1(self):
def _stage2(self):
def addminperiod(self, minperiod):
def incminperiod(self, minperiod):
def _makeoperation(self, other, operation, r=False, _ownerskip=None):
def _makeoperationown(self, operation, _ownerskip=None):
def qbuffer(self, savemem=0):
def minbuffer(self, size):
解读:
- LineMultiple继承LineRoot,由“一条数据线”变为“多条数据线”,即示例属性self.lines中保存着多条数据线,每条数据线都是一个LineRoot对象。这样,我们就有了两个概念,多条数据线集合的概念和单条数据线的概念。在该类型中,没有往集合中放数据线的方法,相信在子类中会填充相关方法。
- LineMultiple做了一件事,即对"数据线集合"设置属性(例如_stage和period)的同时,对每条"数据线”设置属性,在方法_stage1/_stage2/addminperiod/incminperiod/qbuffer/minbuffer中实现
- 从这个类中,回答在父类LineRoot解读中的遗留问题:_operation_stage1和_operation_stage2区别是什么
a. 在_opstage=2状态中,默认操作是对self[0]施加operation
b. 在_opstage=1状态下,可以重写方法_makeoperation和_makeoperationown来调整操作对象,在类LineMultiple中,重写方法,修改操作对象为self.lines[0], 即对集合中的第一条数据线进行操作,后续中加入数据线集合的数据线应该会重写方法_makeoperation和_makeoperationown
10、
LineRoot
class LineRoot(with_metaclass(MetaLineRoot, object)):
'''
Defines a common base and interfaces for Single and Multiple
LineXXX instances
Period management
Iteration management
Operation (dual/single operand) Management
Rich Comparison operator definition
'''
_OwnerCls = None
_minperiod = 1
_opstage = 1
IndType, StratType, ObsType = range(3)
def _stage1(self):
def _stage2(self):
def _operation(self, other, operation, r=False, intify=False):
def _operationown(self, operation):
def qbuffer(self, savemem=0):
def minbuffer(self, size):
def setminperiod(self, minperiod):
def updateminperiod(self, minperiod):
def addminperiod(self, minperiod):
def incminperiod(self, minperiod):
def prenext(self):
def nextstart(self):
def next(self):
def preonce(self, start, end):
def oncestart(self, start, end):
def once(self, start, end):
def _makeoperation(self, other, operation, r=False, _ownerskip=None):
raise NotImplementedError
def _makeoperationown(self, operation, _ownerskip=None):
raise NotImplementedError
def _operationown_stage1(self, operation):
'''
Operation with single operand which is "self"
'''
return self._makeoperationown(operation, _ownerskip=self)
def _roperation(self, other, operation, intify=False):
'''
Relies on self._operation to and passes "r" True to define a
reverse operation
'''
return self._operation(other, operation, r=True, intify=intify)
def _operation_stage1(self, other, operation, r=False, intify=False):
'''
Two operands' operation. Scanning of other happens to understand
if other must be directly an operand or rather a subitem thereof
'''
if isinstance(other, LineMultiple):
other = other.lines[0]
return self._makeoperation(other, operation, r, self)
def _operation_stage2(self, other, operation, r=False):
'''
Rich Comparison operators. Scans other and returns either an
operation with other directly or a subitem from other
'''
if isinstance(other, LineRoot):
other = other[0]
# operation(float, other) ... expecting other to be a float
if r:
return operation(other, self[0])
return operation(self[0], other)
def _operationown_stage2(self, operation):
return operation(self[0])
def __add__(self, other):
return self._operation(other, operator.__add__)
def __radd__(self, other):
return self._roperation(other, operator.__add__)
def __sub__(self, other):
return self._operation(other, operator.__sub__)
def __rsub__(self, other):
return self._roperation(other, operator.__sub__)
def __mul__(self, other):
return self._operation(other, operator.__mul__)
def __rmul__(self, other):
return self._roperation(other, operator.__mul__)
def __div__(self, other):
return self._operation(other, operator.__div__)
def __rdiv__(self, other):
return self._roperation(other, operator.__div__)
def __floordiv__(self, other):
return self._operation(other, operator.__floordiv__)
def __rfloordiv__(self, other):
return self._roperation(other, operator.__floordiv__)
def __truediv__(self, other):
return self._operation(other, operator.__truediv__)
def __rtruediv__(self, other):
return self._roperation(other, operator.__truediv__)
def __pow__(self, other):
return self._operation(other, operator.__pow__)
def __rpow__(self, other):
return self._roperation(other, operator.__pow__)
def __abs__(self):
return self._operationown(operator.__abs__)
def __neg__(self):
return self._operationown(operator.__neg__)
def __lt__(self, other):
return self._operation(other, operator.__lt__)
def __gt__(self, other):
return self._operation(other, operator.__gt__)
def __le__(self, other):
return self._operation(other, operator.__le__)
def __ge__(self, other):
return self._operation(other, operator.__ge__)
def __eq__(self, other):
return self._operation(other, operator.__eq__)
def __ne__(self, other):
return self._operation(other, operator.__ne__)
def __nonzero__(self):
return self._operationown(bool)
__bool__ = __nonzero__
# Python 3 forces explicit implementation of hash if
# the class has redefined __eq__
__hash__ = object.__hash__
解读:
- 这是数据线最原始的父类,所以必须要自己先从时序数据最基础的功能思考—— K线合成和数据运算,可以以tick价格序列为例: a. 价格需要需要合成K线,如何计算每一根Kbar呢?
Kbar周期:在类变量_minperiod中实现,调整周期在方法setminperiod,updateminperiod中实现
Kbar开盘价:在方法nextstart/next中实现,本质就是处理一个周期后的第一个价格
Kbar最高价最低价:在方法prenext中实现,本质就是在一个周期结束前不断比较价格
Kbar收盘价:在方法prenext中实现,本质就是在一个周期结束前不断更新价格 b. 方法nextstart/next/prenext都是针对时间戳上的价格,那引申到时间段上的价 格,就衍生出了 nextonce/once/preonce方法 c. 关于数据运算,间接通过方法_operation/_operationown来实现 - 从LineRoot的元类的__new__方法可知,LineRoot的继承类中的params参数会打包到一块
- 这个类的解读会遗留一个问题,即_operation_stage1和_operation_stage2的区别是什么