iT邦幫忙

2021 iThome 鐵人賽

DAY 23
0
永豐金融APIs

永豐金融APIs - 從零開始到放棄!?系列 第 23

Backtrader - 自訂 datafeeds

  • 分享至 

  • xImage
  •  

我們之前在喂歷史資料,都是先用 shioaji 下載下來,然後再用 padas 轉成 dataframe,最後再喂給 backtrader,在 backtrader 裡的 datafeeds 也是可以自訂義的,有幾個步驟:

  1. 定義一個 datafeeds 的 class 繼承 backtrader.feed.DataBase
  2. 定義需要的 params 參數:
    這邊要注意的是,backtrader.feed.DataBase 有自己的參數,要避免使用一樣的參數名稱,以下是 DataBase 的參數名稱
        params = (('dataname', None),
         ('fromdate', datetime.datetime.min),
         ('todate', datetime.datetime.max),
         ('name', ''),
         ('compression', 1),
         ('timeframe', TimeFrame.Days),
         ('sessionend', None))
    
  3. 覆寫初始化的方法 __init__(self) 或是 start(self),也可以兩個都用
  4. 如果在結束時,有需要清除的程式,可以放在 stop(self)
  5. 覆寫主要載入資料的 _load(self)

以下我就用 Shioaji 取得資料來示範

import backtrader as bt
import backtrader.feeds as btfeeds
from backtrader import date2num
import shioaji as sj
from datetime import datetime


class ShioajiFeeds(bt.feed.DataBase):
    params = (
        ("person_id", "身份證字號"),
        ("passwd", "永豐金證券密碼"),
        ("start", datetime.now().strftime("%Y-%m-%d")),
        ("end", datetime.now().strftime("%Y-%m-%d")),
        ("stock", "2330"),
        # 這個變數是 DataBase 的
        # 因為 Shioaji 取得的資料是每分鐘,所以設成 TimeFrame.Minutes
        ("timeframe", bt.TimeFrame.Minutes),
    )

    def start(self):
        self.api = sj.Shioaji()
        self.api.login(person_id = self.p.person_id, passwd = self.p.passwd)
        contract = self.api.Contracts.Stocks[self.p.stock]

        self.kbar = self.api.kbars(contract, start = self.p.start, end = self.p.end)

        self.idx = 0
        self.dataLength = len(self.kbar.ts) # 紀錄我們取得的資料有多少筆


    def _load(self):
        # 資料會一筆一筆讀進來,如果讀到最後一筆,就跳出 False
        if (self.idx == self.dataLength):
            return False
            
        dt = datetime.utcfromtimestamp(self.kbar.ts[self.idx] / 10**9)
        self.lines.datetime[0] = date2num(dt)
        
        self.lines.open[0] = self.kbar.Open[self.idx]
        self.lines.high[0] = self.kbar.High[self.idx]
        self.lines.low[0] = self.kbar.Low[self.idx]
        self.lines.close[0] = self.kbar.Close[self.idx]
        self.lines.volume[0] = self.kbar.Volume[self.idx]
                
        self.idx += 1
        
        return True


    def stop(self):
        # 結束的時候進行登出
        self.api.logout()
        self.api = None
        

# 使用自訂的 datafeed
cerebro = bt.Cerebro()

data = ShioajiFeeds(start='2021-10-01', end='2021-10-08', stock='2303')

cerebro.resampledata(data, timeframe=bt.TimeFrame.Days)

cerebro.addstrategy(bt.Strategy)

cerebro.run()

cerebro.plot()

那如果我們要載入多筆股票的資料,以這個寫法,需要進行登入登出多次,所以可以稍微改一下,把 api 當成一個參數去執行

class ShioajiFeeds(bt.feed.DataBase):
    params = (
        ("person_id", "身份證字號"),
        ("passwd", "永豐金證券密碼"),
        ("start", datetime.now().strftime("%Y-%m-%d")),
        ("end", datetime.now().strftime("%Y-%m-%d")),
        ("stock", "2330"),
        ("timeframe", bt.TimeFrame.Minutes),
        ("api", None), # 新增一個 api 參數
    )

    def start(self):
        if self.p.api is None:
            self.api = sj.Shioaji()
            self.api.login(person_id = self.p.person_id, passwd = self.p.passwd)
        else:
            self.api = self.p.api
    # ...略
    
    def stop(self):
        if self.p.api is None:
            self.api.logout()
            
        self.api = None

在執行的時候,我們就可以先初始化 api,然後把 api 當參數傳入,就可以不用一直登入登出了

api = sj.Shioaji()
api.login(person_id = "帳號", passwd = "密碼")

data = ShioajiFeeds(start = '2021-10-01', end = '2021-10-08', stock = '2330', api = api)

# ... 略

# 結束後,記得登出
api.logout()

上一篇
Backtrader - 繪圖
下一篇
Backtrader - sizer
系列文
永豐金融APIs - 從零開始到放棄!?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言