Skip to content

Latest commit

 

History

History
164 lines (136 loc) · 7.29 KB

File metadata and controls

164 lines (136 loc) · 7.29 KB

【backtrader 源码解析 15】csvgeneric.py 源代码注释(枯燥,对 backtrader 源代码感兴趣,可以参考)

原文:https://yunjinqi.blog.csdn.net/article/details/123457542

csvgeneric.py 源代码注释,后续基于对 backtrader 源代码的研究会重新梳理下 backtrader 的结构,让大家更好的理解 backtrader 和使用,目前源代码注释和阅读都是一个比较枯燥的过程,不感兴趣可以忽略,这部分内容的忽略不怎么影响大家使用 backtrader.

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

from datetime import datetime
import itertools

from .. import feed, TimeFrame
from ..utils import date2num
from ..utils.py3 import integer_types, string_types

class GenericCSVData(feed.CSVDataBase):
    '''Parses a CSV file according to the order and field presence defined by the
    parameters

    Specific parameters (or specific meaning):

      - ``dataname``: The filename to parse or a file-like object

      - The lines parameters (datetime, open, high ...) take numeric values

        A value of -1 indicates absence of that field in the CSV source

      - If ``time`` is present (parameter time >=0) the source contains
        separated fields for date and time, which will be combined

      - ``nullvalue``

        Value that will be used if a value which should be there is missing
        (the CSV field is empty)

      - ``dtformat``: Format used to parse the datetime CSV field. See the
        python strptime/strftime documentation for the format.

        If a numeric value is specified, it will be interpreted as follows

          - ``1``: The value is a Unix timestamp of type ``int`` representing
            the number of seconds since Jan 1st, 1970

          - ``2``: The value is a Unix timestamp of type ``float``

        If a **callable** is passed

          - it will accept a string and return a `datetime.datetime` python
            instance

      - ``tmformat``: Format used to parse the time CSV field if "present"
        (the default for the "time" CSV field is not to be present)

    '''

    # csv data 的一些常用的参数
    params = (
        ('nullvalue', float('NaN')),
        ('dtformat', '%Y-%m-%d %H:%M:%S'),
        ('tmformat', '%H:%M:%S'),

        ('datetime', 0),
        ('time', -1),
        ('open', 1),
        ('high', 2),
        ('low', 3),
        ('close', 4),
        ('volume', 5),
        ('openinterest', 6),
    )

    # 开始,根据传入的日期参数确定转换的方法
    def start(self):
        super(GenericCSVData, self).start()
        # 如果是字符串类型,就把 self._dtstr 设置成 True,否则就是默认的 False
        self._dtstr = False
        if isinstance(self.p.dtformat, string_types):
            self._dtstr = True
        # 如果是整数,那么就根据整数的不同,设置时间转换方法
        elif isinstance(self.p.dtformat, integer_types):
            idt = int(self.p.dtformat)
            if idt == 1:
                self._dtconvert = lambda x: datetime.utcfromtimestamp(int(x))
            elif idt == 2:
                self._dtconvert = lambda x: datetime.utcfromtimestamp(float(x))
        # 如果 dtformat 是可以调用的,转换方法就是它本身
        else:  # assume callable
            self._dtconvert = self.p.dtformat

    # 读取 csv 文件的 line 之后,把 line 的每个数据分割开来做成 linetokens 之后,进一步的处理
    def _loadline(self, linetokens):
        # Datetime needs special treatment
        # 首先根据 datetime 出现的顺序,取得具体的日期
        dtfield = linetokens[self.p.datetime]
        # 如果时间是字符串格式
        if self._dtstr:
            # 具体的时间格式
            dtformat = self.p.dtformat
            # 如果有 time 这个列,就把日期和时间结合到一起
            if self.p.time >= 0:
                # add time value and format if it's in a separate field
                dtfield += 'T' + linetokens[self.p.time]
                dtformat += 'T' + self.p.tmformat
            # 然后把字符串时间转化为 datetime 格式的时间
            dt = datetime.strptime(dtfield, dtformat)
        # 如果不是字符串,就调用 start 的时候设置好的时间转化函数 _dtconvert
        else:
            dt = self._dtconvert(dtfield)
        # 如果交易的时间间隔大于等于日
        if self.p.timeframe >= TimeFrame.Days:
            # check if the expected end of session is larger than parsed
            # 如果 _tzinput 是真的话,需要把日期做本地化处理,如果不是,日期还是原来的
            if self._tzinput:
                dtin = self._tzinput.localize(dt)  # pytz compatible-ized
            else:
                dtin = dt
            # 使用 date2num 把日期转化成数字
            dtnum = date2num(dtin)  # utc'ize
            # 把日期和 sessionend 结合起来,并转化成数字
            dteos = datetime.combine(dt.date(), self.p.sessionend)
            dteosnum = self.date2num(dteos)  # utc'ize
            # 如果结合 sessionend 的日期转化成的数字大于日期转化后的数字,用前面的数字作为时间
            if dteosnum > dtnum:
                self.lines.datetime[0] = dteosnum
            # 如果不大于的话,如果 self._tzinput 是真的,那么就直接把 dt 转化成时间,如果不是真的,就使用原先的 dtnum 
            else:
                # Avoid reconversion if already converted dtin == dt
                self.l.datetime[0] = date2num(dt) if self._tzinput else dtnum
        # 如果交易周期小于日,那么时间就直接转化
        else:
            self.lines.datetime[0] = date2num(dt)

        # The rest of the fields can be done with the same procedure
        # 剩下的其他的数据可以按照同样的方法去操作,循环不是 datetime 的列
        for linefield in (x for x in self.getlinealiases() if x != 'datetime'):
            # Get the index created from the passed params
            # 获取这个列名称的 index
            csvidx = getattr(self.params, linefield)
            # 如果这个列的 index 是 None 或者小于 0,代表数据是空的,设置成 NAN
            if csvidx is None or csvidx < 0:
                # the field will not be present, assignt the "nullvalue"
                csvfield = self.p.nullvalue
            # 否则直接从 linetokens 中获取
            else:
                # get it from the token
                csvfield = linetokens[csvidx]
            # 如果获取到的数据是空的字符串,把数据设置成 NAN
            if csvfield == '':
                # if empty ... assign the "nullvalue"
                csvfield = self.p.nullvalue
            # 获取这个列对应的 line,然后设置 value,没有太明白为什么使用两个 float 转化一个值,暂且认为是低效的,修改下
            # get the corresponding line reference and set the value
            line = getattr(self.lines, linefield)
            # line[0] = float(float(csvfield))  # backtrader 自带
            line[0] = float(csvfield)

        return True

class GenericCSV(feed.CSVFeedBase):
    # 类,增加一个属性 DataCls,把这个属性值设置成 GenericCSVData
    DataCls = GenericCSVData