iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 23
1
Software Development

Python 程式交易 30 天新手入門系列 第 23

Day-23 回測系統:歷史資料準備

回測系統

如果沒有完整歷史資料,那就只能實單回測,對於小資族而言,相當於是在賭身家,所以擁有完整的歷史 Tick 資料進行回測,對於確認策略的可能性及基礎有效性,是相當關鍵的一件事情。本系列將不會提及如何取得完整的歷史 Tick 資料,原因待有興趣的朋友進了圈子後自會理解。

技術整合

前情提要

請詳細閱讀前面各日所提及的各種技術與指標,回測系統需要整合已提過的相關技術。

歷史資料

大型台指期 2019/01/02 ~ 2019/09/20 Tick 資料
共 11341351 筆資料

TXF1-Ticks-2019-01-02~2019-09-20.csv

Date Time Price Volume
2019/01/02 08:45:00.000 9760.000000 910
2019/01/02 08:45:00.000 9759.000000 1
2019/01/02 08:45:00.000 9759.000000 1
2019/01/02 08:45:00.000 9759.000000 1
2019/01/02 08:45:00.000 9759.000000 1
2019/01/02 08:45:00.000 9758.000000 1
2019/01/02 08:45:00.000 9758.000000 1
2019/01/02 08:45:00.000 9758.000000 1
2019/01/02 08:45:00.000 9758.000000 1
2019/01/02 08:45:00.000 9760.000000 2
2019/01/02 08:45:00.000 9760.000000 1
... ... ... ...
2019/09/20 13:44:57.992 10900.000000 5
2019/09/20 13:44:58.665 10899.000000 1
2019/09/20 13:44:58.761 10900.000000 2
2019/09/20 13:44:58.955 10900.000000 1
2019/09/20 13:44:59.020 10901.000000 2
2019/09/20 13:44:59.206 10901.000000 6
2019/09/20 13:44:59.659 10901.000000 6
2019/09/20 13:44:59.667 10901.000000 1
2019/09/20 13:44:59.731 10900.000000 1
2019/09/20 13:44:59.742 10900.000000 4
2019/09/20 13:44:59.947 10901.000000 6

匯入資料

在以下規格電腦中共花費約 13 分鐘匯入

  1. CPU
    Intel Xeon E5-2630 v3 2.40GHz X 2(16 核 32 緒)
  2. RAM
    64GB
  3. SSD
    Crucial MX300
  4. OS
    Microsoft Windows 10 Enterprise
import csv
import datetime

import loguru
import munch
import pymongo

def main():
    client = pymongo.MongoClient('mongodb://%s:%s@%s:%s/' % (
        'root',
        'root',
        'localhost',
        '27017'
    ))
    
    db = client.backtest

    with open('TXF1-Ticks-2019-01-02~2019-09-20.csv') as f:
        reader = csv.DictReader(f)
        ticks = []
        for row in reader:
            # 透過 munchify 轉換為 stdObject 以方便在 formatted string 中取用屬性
            row = munch.munchify(row)
            t = datetime.datetime.strptime(f'{row.Date} {row.Time}+0800', '%Y/%m/%d %H:%M:%S.%f%z')
            # 不做夜盤,僅保留日盤資料
            if t.hour < 8 or t.hour > 13:
                continue
            ticks.append({
                'time': t,
                'price': float(row.Price),
                'volume': int(row.Volume),
                'created': datetime.datetime.now()
            })
            # 每 10000 筆資料再批次匯入,以提升匯入速度
            if len(ticks) > 10000:
                db.ticks.insert(ticks)
                ticks = []

    client.close()

if __name__ == '__main__':
    loguru.logger.add(
        f'{datetime.date.today():%Y%m%d}.log',
        rotation='1 day',
        retention='7 days',
        level='DEBUG'
    )
    main()

計算分 K 並保存

在以下規格電腦中共花費約 50 分鐘計算並匯入

  1. CPU
    Intel Xeon E5-2630 v3 2.40GHz X 2(16 核 32 緒)
  2. RAM
    64GB
  3. SSD
    Crucial MX300
  4. OS
    Microsoft Windows 10 Enterprise
import datetime

import loguru
import pandas
import pymongo

def main():
    client = pymongo.MongoClient('mongodb://%s:%s@%s:%s/' % (
        'root',
        'root',
        'localhost',
        '27017'
    ))
    
    db = client.backtest

    # 針對 2019 年每天執行計算
    day = datetime.date(2019, 1, 1)
    end = datetime.date(2020, 1, 1)

    while day != end:
        t = day
        day = day + datetime.timedelta(days=1)

        # 從 MongoDB 的 ticks 集合取出指定日期的全部 Tick
        s = datetime.datetime(t.year, t.month, t.day, 0, 0, 0)
        t = t + datetime.timedelta(days=1)
        e = datetime.datetime(t.year, t.month, t.day, 0, 0, 0)

        cursor = db.ticks.find({
            'time': {
                '$gte': s,
                '$lt': e
            }
        }).sort([('created', 1)])
        if cursor.count() == 0:
            continue

        # 將取出的 Tick 放入 DataFrame
        docs = list(cursor)
        TXF_TICKS = pandas.DataFrame(docs)
        TXF_TICKS = TXF_TICKS.set_index('time')

        # 計算 1 分 K,並將資料寫入 k1min 集合
        TXF_1MINK = TXF_TICKS['price'].resample('1MIN').ohlc()
        kbars = []
        for index, row in TXF_1MINK.iterrows():
            kbars.append({
                'time': index,
                'open': row['open'],
                'high': row['high'],
                'low': row['low'],
                'close': row['close'],
                'created': datetime.datetime.now()
            })
        db.k1min.insert(kbars)

        # 計算 5 分 K,並將資料寫入 k5min 集合
        TXF_5MINK = TXF_TICKS['price'].resample('5MIN').ohlc()
        kbars = []
        for index, row in TXF_5MINK.iterrows():
            kbars.append({
                'time': index,
                'open': row['open'],
                'high': row['high'],
                'low': row['low'],
                'close': row['close'],
                'created': datetime.datetime.now()
            })
        db.k5min.insert(kbars)

if __name__ == '__main__':
    loguru.logger.add(
        f'{datetime.date.today():%Y%m%d}.log',
        rotation='1 day',
        retention='7 days',
        level='DEBUG'
    )
    main()

透過修改以上方式,一樣能對均線等其他指標進行計算,並保存於 MongoDB 的集合中。


團隊系列文:

CSScoke - 金魚都能懂的這個網頁畫面怎麼切 - 金魚都能懂了你還怕學不會嗎
Clarence - LINE bot 好好玩 30 天玩轉 LINE API
Hina Hina - 陣列大亂鬥
King Tzeng - IoT沒那麼難!新手用JavaScript入門做自己的玩具
Vita Ora - 好 Js 不學嗎 !? JavaScript 入門中的入門。
TaTaMo - 用Python開發的網頁不能放到Github上?Lektor說可以!!


上一篇
Day-22 資料保存:MongoDB
下一篇
Day-24 回測系統:設計架構
系列文
Python 程式交易 30 天新手入門30

尚未有邦友留言

立即登入留言