Skip to content

Latest commit

 

History

History
280 lines (230 loc) · 10.3 KB

File metadata and controls

280 lines (230 loc) · 10.3 KB

【backtrader 源码解析 34】writer.py 源码注释(枯燥,仅供参考)

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

writer.py 的源码注释

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

import collections
import io
import itertools
import sys

import backtrader as bt
from backtrader.utils.py3 import (map, with_metaclass, string_types,
                                  integer_types)

# WriterBase 类
class WriterBase(with_metaclass(bt.MetaParams, object)):
    pass

# WriterFile 类
class WriterFile(WriterBase):
    """
    The system wide writer class.

    It can be parametrized with:

      - ``out`` (default: ``sys.stdout``): output stream to write to

        If a string is passed a filename with the content of the parameter will
        be used.

        If you wish to run with ``sys.stdout`` while doing multiprocess optimization, leave it as ``None``, which will
        automatically initiate ``sys.stdout`` on the child processes.

        # out 默认是 sys.stdout 如果是在多进程参数优化的过程中,想要把结果输出到标准输出中,把这个参数设置成 None,会在每个自进程中自动设置
        # 如果传了一个 filename 给 out,将会输出到这个 filename 中

      - ``close_out``  (default: ``False``)

        If ``out`` is a stream whether it has to be explicitly closed by the
        writer
        # 在 out 是一个数据流的情况下,是否需要 writer 明确的关闭

      - ``csv`` (default: ``False``)

        If a csv stream of the data feeds, strategies, observers and indicators
        has to be written to the stream during execution

        Which objects actually go into the csv stream can be controlled with
        the ``csv`` attribute of each object (defaults to ``True`` for ``data
        feeds`` and ``observers`` / False for ``indicators``)

        # 数据,策略,observers 和 indicators 的 csv 数据流可以在执行的时候写入到文件里面,如果 csv 设置的是 True 的话
        # 是会被写入进去的

      - ``csv_filternan`` (default: ``True``) whether ``nan`` values have to be
        purged out of the csv stream (replaced by an empty field)

        # 在写入 csv 文件的时候,是否清除 nan 的值

      - ``csv_counter`` (default: ``True``) if the writer shall keep and print
        out a counter of the lines actually output

        # writer 是否保存和打印实际上输出的那些 lines,默认是 True

      - ``indent`` (default: ``2``) indentation spaces for each level
        # 行首空格,默认是 2

      - ``separators`` (default: ``['=', '-', '+', '*', '.', '~', '"', '^',
        '#']``)

        Characters used for line separators across section/sub(sub)sections

        # 分隔符,默认是['=', '-', '+', '*', '.', '~', '"', '^','#']

      - ``seplen`` (default: ``79``)

        total length of a line separator including indentation

        # 包括行首的分隔符的总的长度,默认是 79

      - ``rounding`` (default: ``None``)

        Number of decimal places to round floats down to. With ``None`` no
        rounding is performed

        # 保存的小数是否四舍五入到某一位。默认不进行四舍五入。

    """
    params = (
        ('out', None),
        ('close_out', False),

        ('csv', False),
        ('csvsep', ','),
        ('csv_filternan', True),
        ('csv_counter', True),

        ('indent', 2),
        ('separators', ['=', '-', '+', '*', '.', '~', '"', '^', '#']),
        ('seplen', 79),
        ('rounding', None),
    )

    # 初始化
    def __init__(self):
        # _len 是一个计数器
        self._len = itertools.count(1)
        # headers
        self.headers = list()
        # values
        self.values = list()

    # 开始输出
    def _start_output(self):
        # open file if needed
        # 如果没有 out 属性 或者 self.out 是 None
        if not hasattr(self, 'out') or not self.out:
            # 如果 out 参数是 None,out 设置成标准输出,并且 close_out 设置成 False
            if self.p.out is None:
                self.out = sys.stdout
                self.close_out = False
            # 如果 self.p.out 是一个 string_types,以写入方式打开文件,close_out 需要设置成 True
            elif isinstance(self.p.out, string_types):
                self.out = open(self.p.out, 'w')
                self.close_out = True
            # 如果 self.p.out 既不是 None,也不是字符串格式,那么 self.out 等于 self.p.out,self.close_out 等于 self.p.close_out
            else:
                self.out = self.p.out
                self.close_out = self.p.close_out

    # 开始
    def start(self):
        # 调用 _start_output,准备好开始输出
        self._start_output()
        # 如果 csv 是 True 的话,
        if self.p.csv:
            # 写入 line 的分隔符
            self.writelineseparator()
            # 把列名写入到文件中,第一列默认是 Id
            self.writeiterable(self.headers, counter='Id')

    # 结束,如果 close_out 是 True 的话,需要关闭 self.out
    def stop(self):
        if self.close_out:
            self.out.close()

    # 如果 csv 是 True 的话,每次运行一次的时候,就 values 保存到 self.out 中,并把 self.values 设置成空列表
    def next(self):
        if self.p.csv:
            self.writeiterable(self.values, func=str, counter=next(self._len))
            self.values = list()

    # 如果 csv 是 True 的话,增加列名
    def addheaders(self, headers):
        if self.p.csv:
            self.headers.extend(headers)
    # 如果 csv 是 True 的话,如果需要过滤 nan 的话,就把 nan 替换成‘’,并把 values 添加到 self.values
    def addvalues(self, values):
        if self.p.csv:
            if self.p.csv_filternan:
                values = map(lambda x: x if x == x else '', values)
            self.values.extend(values)

    # 把可迭代的对象进行处理,写入标准输出或者 csv 文件中
    def writeiterable(self, iterable, func=None, counter=''):
        # 如果保存 csv 的 counter,在可迭代对象前加上一个 counter
        if self.p.csv_counter:
            iterable = itertools.chain([counter], iterable)
        # 如果 func 不是 None 的话,把 func 应用与可迭代对象
        if func is not None:
            iterable = map(lambda x: func(x), iterable)
        # 把可迭代对象用 csv 分隔符进行分割,形成 line
        line = self.p.csvsep.join(iterable)
        # 把 line 写入到 self.out 中
        self.writeline(line)

    # 把 line 写入到 self.out 中
    def writeline(self, line):
        self.out.write(line + '\n')

    # 把多条 line 写入到 self.out 中
    def writelines(self, lines):
        for l in lines:
            self.out.write(l + '\n')

    # 写入 line 的分隔符
    def writelineseparator(self, level=0):
        # 决定选用哪种分隔符,默认选择的是第一个分隔符"="
        sepnum = level % len(self.p.separators)
        separator = self.p.separators[sepnum]
        # 行首空格,默认是 0
        line = ' ' * (level * self.p.indent)
        # 整个 line 的内容
        line += separator * (self.p.seplen - (level * self.p.indent))
        self.writeline(line)

    # 写入字典
    def writedict(self, dct, level=0, recurse=False):
        # 如果没有递归的话,写入 line 分隔符
        if not recurse:
            self.writelineseparator(level)
        # 首行缩进多少
        indent0 = level * self.p.indent
        # 迭代字典
        for key, val in dct.items():
            # 首行空格
            kline = ' ' * indent0
            # 如果递归,加上一个字符'- '
            if recurse:
                kline += '- '
            # 增加一个 key :
            kline += str(key) + ':'
            # 判断 val 是不是一个 lineseries 的子类
            try:
                sclass = issubclass(val, bt.LineSeries)
            except TypeError:
                sclass = False
            # 如果是子类,加一个空格,增加 val 的名称
            if sclass:
                kline += ' ' + val.__name__
                self.writeline(kline)
            # 如果是字符串
            elif isinstance(val, string_types):
                # 把 val 添加到 kline 中
                kline += ' ' + val
                # 把 kline 写入到 self.out 中
                self.writeline(kline)
            # 如果是整数
            elif isinstance(val, integer_types):
                # 把 val 转化成字符串,添加到 kline 中
                kline += ' ' + str(val)
                self.writeline(kline)
            # 如果是一个浮点数
            elif isinstance(val, float):
                # 如果四舍五入不是 None 的话,就把浮点数进行四舍五入
                if self.p.rounding is not None:
                    val = round(val, self.p.rounding)
                # 把 val 转化成字符串,添加到 kline 中
                kline += ' ' + str(val)
                self.writeline(kline)
            # 如果 val 是一个字典
            elif isinstance(val, dict):
                # 如果是递归的话,写入 level
                if recurse:
                    self.writelineseparator(level=level)
                self.writeline(kline)
                # 写入字典
                self.writedict(val, level=level + 1, recurse=True)
            # 如果 val 是一个可迭代对象
            elif isinstance(val, (list, tuple, collections.Iterable)):
                # 形成 line,并保存到 self.out 中
                line = ', '.join(map(str, val))
                self.writeline(kline + ' ' + line)
            # 其他情况下,把 val 转成字符串,并保存
            else:
                kline += ' ' + str(val)
                self.writeline(kline)

# 写入 StringIO
class WriterStringIO(WriterFile):
    # 参数 out 设置成了 stringIO
    params = (('out', io.StringIO),)

    def __init__(self):
        super(WriterStringIO, self).__init__()

    def _start_output(self):
        super(WriterStringIO, self)._start_output()
        self.out = self.out()

    def stop(self):
        super(WriterStringIO, self).stop()
        # Leave the file positioned at the beginning
        self.out.seek(0)