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

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

戦略:前日比x%以下で買い、前日比y%以上で売り その3

昨日に引き続き戦略を作成していく。但し未だ完成していないので途中経過を添付する。

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

import backtrader as bt
from logging import getLogger, StreamHandler, Formatter, DEBUG, INFO

if USE_BOKEH:
    from backtrader_plotting import Bokeh
    from backtrader_plotting.schemes import Tradimo

# for jupyter
if 'PercentageBuySellStrategyWithLogger' in globals():
    del PercentageBuySellStrategyWithLogger

# Create a Stratey
class PercentageBuySellStrategyWithLogger(bt.Strategy):
    params = (
        ('default_unit_size', 100),  # デフォルトの単元株の株数
        ('buy_under_percentage', 5),  # 前日終値と比較し本日始値が▲x%の場合に買い注文
        ('sell_over_percentage', 5),  # 前日終値と比較し本日始値が+y%の場合に売り注文
        ('min_order_price', 10 * 10000),  # 最低購入金額(円)
        ('max_order_price', 50 * 10000),  # 最高購入金額(円)
        ('smaperiod', 5),
    )

    def _log(self, txt, dt=None):
        ''' Logging function for this strategy '''
        dt = dt or self.datas[0].datetime.date(0)
        self._logger.debug('%s, %s' % (dt.isoformat(), txt))

    def __init__(self, loglevel):
        # Keep a reference to the "close" line in the data[0] dataseries
        self._dataopen = self.datas[0].open
        self._datahigh = self.datas[0].high
        self._datalow = self.datas[0].low
        self._dataclose = self.datas[0].close
        self._dataadjclose = self.datas[0].adjclose
        self._datavolume = self.datas[0].volume
        self._logger = getLogger(__name__)
        self.handler = StreamHandler()
        self.handler.setLevel(loglevel)
        self._logger.setLevel(loglevel)
        self._logger.addHandler(self.handler)
        self._logger.propagate = False
        self.handler.setFormatter(
                Formatter('[%(levelname)s] %(message)s'))
        self.sma = bt.indicators.SimpleMovingAverage(
            self.datas[0], period=self.params.smaperiod)                

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return

        if order.status in [order.Completed]:
            if order.isbuy():
                buy_or_sell = 'BUY'
            elif order.issell():
                buy_or_sell = 'SELL'
            else:
                buy_or_sell = 'UNDEFINED'
            self._log('{b_s:%4s} EXECUTED, {price:07.2f}'.format(
                b_s=buy_or_sell,
                price=order.executed.price))

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self._debug('Order Canceled/Margin/Rejected')
        elif order.status in [order.Expired]:
            self._debug('Expired: {b_s:s} ¥{sum:,d} (@{price:.2f} * {unit:4d})'.format( \
                sum=int(order.price * order.size),
                b_s=order.ordtypename(), price=order.price, unit=order.size))
        else:
            self._debug(order.getstatusname())
        

        
    def _log(self, txt, loglevel=INFO, dt=None):
        ''' Logging function for this strategy '''
        dt = dt or self.datas[0].datetime.date(0)
        self._logger.log(loglevel, '%s, %s' % (dt.isoformat(), txt))

    def _debug(self, txt, dt=None):
        self._log(txt, DEBUG, dt)

    def _info(self, txt, dt=None):
        self._log(txt, INFO, dt)

    def size(self) -> int:
        # TODO: min_order_price <= 購入金額 <= max_order_price になるように調整して返す
        return self.p.default_unit_size
        
    def next(self):
        open_today = self._dataopen[0]
        close_yesterday = self._dataclose[-1]

        if open_today * (100.0 - self.p.buy_under_percentage) / 100.0 <= close_yesterday:
            size = self.size()
            self._info('BUY CREATE @{price:.2f}, #{unit:4d}'.format(price=open_today, unit=size))
            self._debug('(o, h, l, c) = ({o:}, {h:}, {l:}, {c:})'.format(
                o=self._dataopen[0], h=self._datahigh[0], l=self._datalow[0], c=self._dataclose[0]))
            self.buy(size=size, price=open_today, exectype=bt.Order.Stop, valid=bt.Order.DAY)

        elif close_yesterday * (100.0 + self.p.sell_over_percentage) / 100.0 <= open_today:
            size = None  # 自動的に建玉全数が指定される
            self._info('CLOSE (SELL) CREATE @{price:.2f}, #all'.format(price=open_today))
            self.close(size=size, price=open_today, exectype=bt.Order.Stop, valid=bt.Order.DAY)


if USE_BOKEH:
    del PercentageBuySellStrategyWithLogger
    from percentage_buy_sell_strategy_with_logger import PercentageBuySellStrategyWithLogger

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