pineforge
CI / GitHub Actions

CI में PineScript backtests चलाएँ (GitHub Actions, GitLab, कुछ भी जो Docker चलाए)

आपके strategy repo के हर commit पर एक backtest trigger होता है। Parity assert होती है, regressions build fail कर देती हैं। वही Docker image जो आप locally चलाते हैं, आपके runner में execute होती है।

CI में parity क्यों

Strategies भी बाक़ी code की तरह drift करती हैं। एक parameter rename, helper function का refactor, एक Pine v6 API change जो आपने notice नहीं किया — कोई भी silently signals shift कर सकता है बिना किसी existing test को break किए। CI में parity gate के बिना, दो महीने पुराना commit पिछले हफ़्ते की trade list बदल सकता है और आपको पता तब चलेगा जब live P&L ग़लत दिखे।

अपनी reference trade list को strategy source के साथ commit करने की discipline वही discipline है जो software को reliable बनाती है: आप expected output describe करते हैं, और actual output diverge हो तो build fail हो जाती है। Backtest के लिए expected output trade list है — entry bar, exit bar, direction, size, fill price — एक pinned historical dataset के against।

जब वो comparison हर commit पर चले, आपको एक ratchet मिलता है: आपकी strategy का historical behaviour सिर्फ़ सुधर सकता है, accidentally regress नहीं हो सकता। आप exactly जान जाते हैं कि किस commit ने output बदला, क्योंकि उसी commit पर build fail हुई। आपकी equity curve के लिए git blame है।

ये ज़्यादातर quants जितना पहले समझते हैं उससे ज़्यादा matter करता है। ‘मैंने ये strategy test की’ और ‘मेरे पास हर historical signal का reproducible, version-controlled record है’ — दोनों के बीच का gap बड़ा है। पहला screenshot है। दूसरा audit trail है। CI वो तरीक़ा है जिससे आप दूसरा बिना manual मेहनत के maintain करते हैं।

PineForge इसे Pine strategies के लिए possible बनाता है क्योंकि runtime एक Docker image है: हर जगह जहाँ Docker चलता है वहाँ चलती है, same inputs पर deterministic output produce करती है, और एक stable JSON schema return करती है जिसे shell scripts में parse करना safe है। Backtest execution पर कोई browser नहीं, कोई authentication flow नहीं, कोई rate limit नहीं।

GitHub Actions example

Full workflow: अपनी strategy check out करें, codegen API के via shared object में transpile करें, अपनी pinned OHLCV CSV पर backtest चलाएँ, JSON report jqसे parse करें, अपनी committed baseline से compare करें, और net PnL delta आपके threshold से ज़्यादा हो तो build fail करें।

अपनी PineForge API key को एक repository secret की तरह add करें जिसका नाम 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 report baseline/report.jsonपर store कीजिए, repository में, strategy source के साथ commit की हुई। जब कोई PR strategy behaviour intentionally बदले, author PR के हिस्से के तौर पर baseline update करता है। वो diff permanent record बन जाती है कि क्या बदला और क्यों।

वही workflow GitLab CI, Bitbucket Pipelines, या किसी भी Docker-supporting runner पर identically चलता है — YAML syntax swap कीजिए, shell commands रखिए।

Exit codes और parity assert करना

PineForge runtime clean run पर exit code 0के साथ exit करता है और किसी भी engine error (malformed Pine, unsupported built-in, data file parse failure) पर non-zero। CI systems इन exit codes को automatically pick up कर लेते हैं — engine-level failures के लिए special handling की ज़रूरत नहीं।

Parity assertion अलग concern है, आपके workflow में implement होती है। JSON report schema patch versions पर stable है:

{
  "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"
    }
  ]
}

Simple numeric parity check के लिए, summary.net_pnl और summary.total_trades को baseline से compare कीजिए। Strict trade-by-trade parity के लिए, full tradesarray diff कीजिए — entry और exit bar indices, directions, और fill prices सब match होने चाहिए। नहीं हुए तो diff output exactly बता देगा कौन सा trade diverge हुआ, किस bar पर, और fill price difference क्या था।

Numeric fields के लिए practical threshold: floating-point deltas 0.01तक accept कीजिए (एक cent, या normalized series पर एक basis point) और इससे ज़्यादा पर fail कीजिए। Trade-by-trade diffs के लिए, entry_bar या exit_barमें कोई भी mismatch PnL proximity के बावजूद hard failure है — trade का अलग bar पर land होने का मतलब आपकी signal logic बदली है।

जो teams parity CI adopt करती हैं वो आम तौर पर baseline update को PR label से gated एक अलग workflow step की तरह चलाती हैं। उससे intentional strategy improvement PR timeline में document हो जाता है — baseline diff code review में Pine change के साथ-साथ दिखती है जिसने उसे cause किया।

शुरू करें