# 想數均線交叉幾次？用 backtrader 餵證交所日資料，一秒列出日期

- 網址：https://blog.valenvan.uk/posts/cloudquant-backtrader.html
- 發布日期：2026-11-03
- 測試設備：Mac mini M4 Pro（12 核、64GB）
- 專案：cloudQuant/backtrader（https://github.com/cloudQuant/backtrader），GPL-3.0 授權
- 測試版本：cloudQuant/backtrader 1.9.76.123（Python 3.12）
- 實測影片：https://www.youtube.com/watch?v=xQd25YxBBCA
- 分類：金融工具區（https://blog.valenvan.uk/finance.html）

> 📌 數據僅限本工具現階段參考用。本文只示範工具怎麼安裝、設定、執行，不是投資建議，也不推薦任何股票。

## 重點摘要

- pip install -e . 約 15 秒裝好 backtrader；要畫圖還得另外 pip install -r requirements.txt（約 14 秒）。
- 餵入證交所 2330 日資料 83 列（2026-07-01 至 2026-11-02），跑 5 日線／20 日線交叉約 1 秒，列出 4 個交叉日，交易次數 2、最大回落 1.88%。
- 直接畫圖依序遇到缺 plotly、缺 dash、TKAgg 需要視窗三個錯誤；補裝 requirements、改用 Agg 並以 figs[0][0].savefig 才存出圖。
- c.plot 的 savefig=True 參數在本次實測沒有產生圖檔。
- **結論：** 算均線交叉與列日期很快，畫圖要先備好相依套件與無視窗環境設定。
- **適合：** 想用 Python 在台股日資料上自己驗證均線交叉、願意照步驟設定環境的人
- **不適合：** 不想碰命令列、需要多檔同時回測或即時資料的人（本次未測）

| 實測項目 | 結果 |
|---|---|
| 資料列數 | 83 |
| 安裝時間 | 約15秒（畫圖相依另約14秒） |
| 交叉日數 | 4 |
| 交易次數 | 2 |
| 最大回落 | 1.88% |
| 回測耗時 | 約1秒 |
| 畫圖錯誤 | 3 個（plotly、dash、TKAgg） |

想驗證一句老話：「五日線穿過二十日線」在最近幾個月到底發生過幾次？用試算表拉公式，拉到一半日期就對不上。這篇用 Python 回測框架 backtrader，餵入證交所公開的日資料，讓它把交叉日一次列出來，也順便畫圖。

測試設備：Mac mini M4 Pro（12 核、64GB）。受測版本：cloudQuant/backtrader（GPL-3.0）master，版本 1.9.76.123，Python 3.12。本文示範資料是證交所公開資料，代號 2330 只是示範輸入，資料日期到 2026-11-02。

## 下載

```bash
git clone https://github.com/cloudQuant/backtrader
cd backtrader
pip install -e .          # 實測約 15 秒
pip install -r requirements.txt   # 畫圖需要，約 14 秒
pip install requests
```

重點：只做 `pip install -e .` 不會裝到畫圖用的 plotly、dash，下面的常見問題會說明。

## 設定

資料來源是證交所的個股日成交資訊（STOCK_DAY），一次一個月。下面的腳本抓 2026 年 7 月到 11 月，存成 2330.csv（日期、開高低收、成交量），共 83 列，從 2026-07-01 到 2026-11-02。

```python
import requests, csv, time
rows=[]
for ym in ['20260701','20260801','20260901','20261001','20261101']:
    j=requests.get('https://www.twse.com.tw/exchangeReport/STOCK_DAY',
        params={'response':'json','date':ym,'stockNo':'2330'},timeout=30).json()
    for r in j.get('data',[]):
        y,m,d=r[0].split('/'); n=lambda s: float(s.replace(',',''))
        rows.append((f'{int(y)+1911}-{m}-{d}',n(r[3]),n(r[4]),n(r[5]),n(r[6]),int(n(r[1]))))
    time.sleep(3)   # 請求間隔，避免被擋
with open('2330.csv','w',newline='') as f:
    w=csv.writer(f); w.writerow(['date','open','high','low','close','volume']); w.writerows(rows)
```

## 跑起來

```python
import matplotlib
matplotlib.use('Agg')
matplotlib.use=lambda *a,**k:None   # backtrader 會強制改成 TKAgg，這行擋掉
import backtrader as bt
class Cross(bt.Strategy):
    def __init__(self):
        self.cx=bt.indicators.CrossOver(bt.indicators.SMA(self.data.close,period=5),
                                        bt.indicators.SMA(self.data.close,period=20))
    def next(self):
        d=self.data.datetime.date(0)
        if self.cx>0: print(d,'5日線向上穿過20日線'); self.buy()
        elif self.cx<0 and self.position: print(d,'5日線向下穿過20日線'); self.close()
c=bt.Cerebro()
c.adddata(bt.feeds.GenericCSVData(dataname='2330.csv',dtformat='%Y-%m-%d',datetime=0,open=1,high=2,low=3,close=4,volume=5,openinterest=-1,headers=True))
c.addstrategy(Cross)
c.addanalyzer(bt.analyzers.TradeAnalyzer,_name='t')
c.addanalyzer(bt.analyzers.DrawDown,_name='dd')
r=c.run()[0]
print('交易次數',r.analyzers.t.get_analysis().total.closed,'最大回落%',round(r.analyzers.dd.get_analysis().max.drawdown,2))
figs=c.plot(style='line',iplot=False,backend='matplotlib')
figs[0][0].set_size_inches(10,6); figs[0][0].savefig('bt2330.png',dpi=110)
```

實測輸出（跑完約 1 秒）：

![終端機輸出](https://blog.valenvan.uk/img/cloudquant-backtrader-article_1.png)

| 項目 | 結果 |
|---|---|
| 資料列數 | 83（2026-07-01 至 2026-11-02） |
| 交叉日 | 08-06 向上、09-16 向下、09-21 向上、10-16 向下 |
| 交易次數 | 2 |
| 最大回落 | 1.88% |

圖檔長這樣（收盤線、5 日與 20 日均線、成交量、下方是交叉指標）：

![backtrader 畫出的圖](https://blog.valenvan.uk/img/cloudquant-backtrader-article_2.png)

以上是工具現階段算出的欄位與日期，只示範用法，不解讀好壞。

## 常見問題

- **`ModuleNotFoundError: No module named 'plotly'`**：只裝 `pip install -e .` 就畫圖會遇到。改裝 `pip install -r requirements.txt`。
- **補裝後又出現 `No module named 'dash'`**：同一份 requirements.txt 會一起裝。
- **`ImportError: Cannot load backend 'TKAgg' which requires the 'tk' interactive framework`**：沒有視窗的環境（伺服器、容器）會遇到，backtrader 載入畫圖模組時會強制指定 TKAgg。上面腳本用 `matplotlib.use('Agg')` 加覆寫 `matplotlib.use`，並在 `c.plot` 指定 `backend='matplotlib'`。
- **`c.plot(savefig=True, figfilename=...)` 沒產生檔案**：我實測這樣沒存出圖，改用 `figs[0][0].savefig(...)` 才成功。
- **這次沒測**：多檔同時回測、參數掃描、其他資料來源與即時資料。

專案連結：https://github.com/cloudQuant/backtrader　授權：GPL-3.0
