株のシステムトレードをしよう - 1から始める株自動取引システムの作り方

株式をコンピュータに売買させる仕組みを少しずつ作っていきます。できあがってから公開ではなく、書いたら途中でも記事として即掲載して、後から固定ページにして体裁を整える方式で進めていきます。

Backtrader で KABU+ のデータを読めるようにする その4

昨日の記事では転けている箇所とその行数、およびその行が所属するメソッドを特定した。

how-to-make-stock-trading-system.dogwood008.com

今回は、標準で bt.feeds.YahooFinanceCSVData に用意されているパーサである _loadline() メソッドを上書きするための前準備として、そっくりそのままコピーしてきて、想定する場所で転けるかを確認する。

コピーする内容は、 bt.feeds.YahooFinanceCSVData_loadline() メソッドの定義全てである。これを KabuPlusJPCSVData クラス内で再定義する。

import itertools
from datetime import date, datetime
from backtrader.utils import date2num
class KabuPlusJPCSVData(bt.feeds.YahooFinanceCSVData):
    '''
    Parses pre-downloaded Yahoo Japan CSV Data Feeds (or locally generated if they
    comply to the Yahoo formatg)
    Specific parameters:
      - ``dataname``: The filename to parse or a file-like object
      - ``reverse`` (default: ``True``)
        It is assumed that locally stored files have already been reversed
        during the download process
      - ``adjclose`` (default: ``True``)
        Whether to use the dividend/split adjusted close and adjust all
        values according to it.
      - ``adjvolume`` (default: ``True``)
        Do also adjust ``volume`` if ``adjclose`` is also ``True``
      - ``round`` (default: ``True``)
        Whether to round the values to a specific number of decimals after
        having adjusted the close
      - ``roundvolume`` (default: ``0``)
        Round the resulting volume to the given number of decimals after having
        adjusted it
      - ``decimals`` (default: ``2``)
        Number of decimals to round to
      - ``swapcloses`` (default: ``False``)
        [2018-11-16] It would seem that the order of *close* and *adjusted
        close* is now fixed. The parameter is retained, in case the need to
        swap the columns again arose.
    '''
    params = (
        ('reverse', True),
        ('adjclose', True),
        ('adjvolume', True),
        ('round', True),
        ('decimals', 2),
        ('roundvolume', False),
        ('swapcloses', False),
    )

    def _loadline(self, linetokens):
        while True:
            nullseen = False
            for tok in linetokens[1:]:
                if tok == 'null':
                    nullseen = True
                    linetokens = self._getnextline()  # refetch tokens
                    if not linetokens:
                        return False  # cannot fetch, go away

                    # out of for to carry on wiwth while True logic
                    break

            if not nullseen:
                break  # can proceed

        i = itertools.count(0)

        dttxt = linetokens[next(i)]
        dt = date(int(dttxt[0:4]), int(dttxt[5:7]), int(dttxt[8:10]))
        dtnum = date2num(datetime.combine(dt, self.p.sessionend))

        self.lines.datetime[0] = dtnum
        o = float(linetokens[next(i)])
        h = float(linetokens[next(i)])  # ここで失敗するはず
        l = float(linetokens[next(i)])
        c = float(linetokens[next(i)])
        self.lines.openinterest[0] = 0.0

        # 2018-11-16 ... Adjusted Close seems to always be delivered after
        # the close and before the volume columns
        adjustedclose = float(linetokens[next(i)])
        try:
            v = float(linetokens[next(i)])
        except:  # cover the case in which volume is "null"
            v = 0.0

        if self.p.swapcloses:  # swap closing prices if requested
            c, adjustedclose = adjustedclose, c

        adjfactor = c / adjustedclose

        # in v7 "adjusted prices" seem to be given, scale back for non adj
        if self.params.adjclose:
            o /= adjfactor
            h /= adjfactor
            l /= adjfactor
            c = adjustedclose
            # If the price goes down, volume must go up and viceversa
            if self.p.adjvolume:
                v *= adjfactor

        if self.p.round:
            decimals = self.p.decimals
            o = round(o, decimals)
            h = round(h, decimals)
            l = round(l, decimals)
            c = round(c, decimals)

        v = round(v, self.p.roundvolume)

        self.lines.open[0] = o
        self.lines.high[0] = h
        self.lines.low[0] = l
        self.lines.close[0] = c
        self.lines.volume[0] = v
        self.lines.adjclose[0] = adjustedclose

        return True
if __name__ == '__main__':
    cerebro = bt.Cerebro()

    data = KabuPlusJPCSVData(
        dataname=path_to_csv,
        fromdate=datetime(2020, 1, 1),
        todate=datetime(2020, 11, 30),
        reverse=False)

    cerebro.adddata(data)
    cerebro.run()

実行結果:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-5-60db8dbbc6e2> in <module>()
      9 
     10     cerebro.adddata(data)
---> 11     cerebro.run()

5 frames
<ipython-input-4-791391b01568> in _loadline(self, linetokens)
     63         self.lines.datetime[0] = dtnum
     64         o = float(linetokens[next(i)])
---> 65         h = float(linetokens[next(i)])  # ここで失敗するはず
     66         l = float(linetokens[next(i)])
     67         c = float(linetokens[next(i)])

ValueError: could not convert string to float: 'SGホールディングス'

実際に実行すると、コメントを付けた場所できちんと失敗している。これにより、正しく上書きできたことが確認された。

次回は _loadline() メソッドを修正していく。

(C) 2020 dogwood008 禁無断転載 不許複製 Reprinting, reproducing are prohibited.