pineforge
開始使用
CI / GitHub Actions

在 CI 跑 PineScript 回測(GitHub Actions、GitLab、凡支援 Docker 者)

策略 repo 每次 commit 觸發回測。對齊被主張,迴歸就擋 build。與本機同一個 Docker 映像,在 runner 執行。

為何在 CI 做對齊

策略跟任何程式碼一樣會漂移。改參數名、重構 helper、沒注意到的 Pine v6 API 變更 — 都可能悄悄挪動訊號卻不打破既有測試。CI 沒對齊門檻時,兩個月前的 commit 可能改掉上週成交列表,你要等實盤損益怪了才發現。

把參考成交列表跟策略原始碼一起 commit 的紀律,就是讓軟體可靠的同一套:描述預期輸出,實際一偏 build 就炸。對回測而言預期輸出就是成交列表 — 進場棒、出場棒、方向、部位、成交價 — 對釘死的歷史資料集。

每次 commit 都比對,就形成棘輪:策略歷史行為只能改善,不會意外退步。你能 exactly 哪個 commit 改了輸出,因為該 commit build 失敗。權益曲線也有 git blame。

多數量化人一開始低估這件事。「我測過這策略」與「這策略每個歷史訊號都有可重現、版控紀錄」差距巨大。前者是截圖;後者是稽核軌跡。CI 讓你在每次 push 不必手動維持後者。

PineForge 讓 Pine 策略做得到,因為 runtime 是 Docker 映像:Docker 能跑就能跑、同輸入給決定性輸出、JSON schema 穩定適合 shell 解析。無瀏覽器、無認證流程、回測執行本身無流量上限。

GitHub Actions 範例

完整流程:checkout 策略、經 codegen API 轉成共享物件、對釘死的 OHLCV CSV 跑回測、用 jq 解析 JSON 報告,與已 commit 的 baseline 比較,若淨損益差超過門檻就讓 build 失敗。

把 PineForge API key 設成 repo secret,名稱 PINEFORGE_API_KEY,再建立 .github/workflows/backtest.yml

name: Backtest parity

on:
  push:
    branches: [main, "feat/**"]
  pull_request:

jobs:
  backtest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Transpile strategy to .so
        run: |
          curl -s https://api.pineforge.io/v1/codegen \
            -H "Authorization: Bearer ${{ secrets.PINEFORGE_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"source": "'"$(cat strategy.pine | jq -Rs .)"'"}' \
            | jq -r '.artifact_url' \
            | xargs curl -sL -o strategy.so

      - name: Pull PineForge runtime
        run: docker pull ghcr.io/pineforge/runtime:latest

      - name: Run backtest
        run: |
          docker run --rm \
            -v "$PWD/strategy.so":/strategy.so \
            -v "$PWD/data/ohlcv.csv":/data.csv \
            ghcr.io/pineforge/runtime:latest \
            run /strategy.so --data /data.csv --output json \
            > report.json

      - name: Assert parity
        run: |
          ACTUAL=$(jq '.summary.net_pnl' report.json)
          BASELINE=$(jq '.summary.net_pnl' baseline/report.json)
          DELTA=$(echo "$ACTUAL $BASELINE" | awk '{d=$1-$2; print (d<0)?-d:d}')
          THRESHOLD="0.01"
          if awk "BEGIN{exit !($DELTA > $THRESHOLD)}"; then
            echo "Parity drift: net_pnl delta $DELTA exceeds threshold $THRESHOLD"
            diff <(jq '.trades' baseline/report.json) \
                 <(jq '.trades' report.json)
            exit 1
          fi
          echo "Parity OK — delta $DELTA (threshold $THRESHOLD)"

把 baseline 報告放在 repo 的 baseline/report.json,與策略原始碼一起 commit。PR 若刻意改行為,作者在 PR 內更新 baseline。該 diff 成為永久紀錄:改了什麼、為什麼。

同一流程在 GitLab CI、Bitbucket Pipelines 或任何支援 Docker 的 runner 等同運行 — 換 YAML 語法,shell 指令保留。

退出碼與對齊主張

PineForge runtime 乾淨執行時退出碼為 0;若發生引擎錯誤(畸形 Pine、不支援內建、資料解析失敗)則為非零。CI 會自動讀取這些退出碼 — 引擎級失敗無需額外處理。

對齊主張另議,由你的工作流實作。JSON 報告 schema 在 patch 版本間穩定:

{
  "summary": {
    "total_trades": 47,
    "net_pnl": 12483.50,
    "max_drawdown": 3201.00,
    "profit_factor": 1.82,
    "sharpe_ratio": 1.34,
    "win_rate": 0.617
  },
  "trades": [
    {
      "entry_bar": 142,
      "exit_bar": 156,
      "direction": "long",
      "size": 1.0,
      "entry_price": 42310.5,
      "exit_price": 44820.0,
      "pnl": 2509.50,
      "exit_reason": "strategy.close"
    }
  ]
}

簡單數值對齊可比較 summary.net_pnl summary.total_trades 對 baseline。嚴格逐筆對齊則對完整 trades 陣列 diff — 進出場棒索引、方向、成交價皆應匹配。不匹配時 diff 會指出哪筆成交偏了、在哪根棒、價差多少。

數值欄位實務門檻:浮點差容忍到 0.01(一美分,或正規化序列上一個 bp)以上才失敗。逐筆 diff 時, entry_bar exit_bar 任一不匹配即硬失敗,無論損益多近 — 成交落在不同棒代表訊號邏輯已變。

採用對齊 CI 的團隊通常把 baseline 更新做成獨立 workflow 步驟,並用 PR label 控制。刻意的策略改善會留在 PR 時間軸 — baseline diff 與造成變更的 Pine 一起在 code review 現身。

開始使用