Skip to content

錯誤處理

openpine_vm::Error 列舉涵蓋了編譯時和執行時的錯誤。

錯誤類型

rust
pub enum Error {
    Compile(CompileErrors),          // 解析、類型檢查或編譯錯誤
    MissingScriptType,               // 沒有 indicator()/strategy()/library() 呼叫
    InputValueNotFound(usize),       // with_input_value 中的無效輸入 ID
    SetInputValue(usize, String),    // 輸入值類型不匹配
    UnsupportedTimeFrame(TimeFrame), // 腳本不支援此時間週期
    SessionNotAllowed(TradeSession), // 交易時段類型未啟用
    DataProvider(String),            // DataProvider 回傳的錯誤
    Exception(Exception),            // 執行時異常(如 runtime.error())
    LibraryScriptNotExecutable,      // 嘗試執行程式庫
}

處理錯誤

模式配對

rust
async fn run(source: &str) {
    let mut instance = match Instance::builder(source, TimeFrame::days(1), "NASDAQ:AAPL")
        .build().await
    {
        Ok(inst) => inst,
        Err(Error::Compile(errors)) => {
            // 帶原始碼位置顯示編譯錯誤
            eprintln!("{}", errors);
            return;
        }
        Err(Error::MissingScriptType) => {
            eprintln!("Script must call indicator(), strategy(), or library()");
            return;
        }
        Err(Error::Exception(exception)) => {
            // 全域範疇執行時錯誤(如頂層程式碼中的 runtime.error())
            eprintln!("Init exception: {}", exception.display());
            return;
        }
        Err(e) => {
            eprintln!("Build error: {}", e);
            return;
        }
    };

    // 執行
    match instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await {
        Ok(()) => {}
        Err(Error::Exception(exception)) => {
            // 執行時錯誤(如除以零、runtime.error())
            eprintln!("Runtime exception: {}", exception.display());
        }
        Err(e) => {
            eprintln!("Execution error: {}", e);
        }
    }
}

編譯錯誤

編譯錯誤包含原始碼 span,用於精確的錯誤報告:

rust
match Instance::builder(source, tf, "NASDAQ:AAPL").build().await {
    Err(Error::Compile(errors)) => {
        // CompileErrors 實現了 Display
        // 顯示帶行號/列號的格式化錯誤
        eprintln!("{}", errors);
    }
    _ => {}
}

執行時異常

執行時異常發生在腳本執行期間(如 runtime.error() 呼叫、索引越界):

rust
match instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await {
    Err(Error::Exception(exception)) => {
        // 顯示帶原始檔案上下文的異常
        eprintln!("{}", exception.display());

        // 取得錯誤的原始碼 span
        if let Some(span) = exception.first_span() {
            eprintln!("  at line {}", span.start.line);
        }
    }
    _ => {}
}

編譯警告

警告不會阻止執行,但指示潛在問題:

rust
let instance = Instance::builder(source, tf, "NASDAQ:AAPL").build().await?;

for warning in instance.warnings() {
    eprintln!("Warning: {}", warning.display_as_warning());
}

最佳實踐

  1. 始終處理編譯錯誤 — 向使用者顯示帶原始碼上下文的錯誤
  2. 也要處理 build() 的異常build() 執行全域範疇以發現腳本元資料(indicator()input.*() 等),因此頂層程式碼中的任何執行時錯誤(如 runtime.error()、無效的函式參數)都會在執行開始之前以 Error::Exception 的形式出現
  3. 遇到執行時錯誤立即停止 — Pine Script 逐 K 線計算,每根 K 線的結果依賴前一根的狀態(var 變數、series 歷史參照如 close[1] 等)。如果某根 K 線計算出錯,後續所有結果都將無效,因此應立即終止
  4. 處理事件流中的事件 — 日誌事件可能包含有用的診斷資訊;消費 run() 回傳的流或使用 run_to_end() 丟棄事件
  5. 建構後檢查警告 — 它們通常指示已棄用的功能或潛在的 bug
rust
use std::pin::pin;
use futures_util::StreamExt;

async fn run_safely(
    instance: &mut Instance,
    symbol: &str,
    timeframe: TimeFrame,
) -> Result<(), Error> {
    for w in instance.warnings() {
        eprintln!("[warn] {}", w.display_as_warning());
    }

    let mut stream = pin!(instance.run(symbol, timeframe));
    while let Some(result) = stream.next().await {
        match result? {
            Event::Log(log) => {
                eprintln!("[{:?}] {}", log.level, log.message);
            }
            Event::Alert(alert) => {
                println!("Alert: {}", alert.message);
            }
            _ => {}
        }
    }
    Ok(())
}

下一步

基於 MIT 許可證發佈。