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 许可证发布。