Skip to content

實例建構器

InstanceBuilder 配置並將 Navi 腳本編譯為可執行的 Instance

基本用法

rust
use navi_vm::{Instance, TimeFrame};

let mut instance = Instance::builder(bars, source, TimeFrame::days(1), "NASDAQ:AAPL")
    .build().await?;

instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await?;

資料提供者

Instance::builder() 的第一個參數是 DataProvider,服務於兩個目的:

  1. 主 chart K 線Instance::run() 從中消費資料驅動逐 bar 執行
  2. request.security() 資料 — 多週期和跨標的 K 線流

Vec<Candlestick> 直接實作了 DataProvider,無需任何包裝即可傳入:

rust
let mut instance = Instance::builder(bars, source, timeframe, "NASDAQ:AAPL")
    .build().await?;
instance.run_to_end("NASDAQ:AAPL", timeframe).await?;

多標的或即時資料,實作 DataProvider

rust
let mut instance = Instance::builder(MyProvider::new(), source, timeframe, "NASDAQ:AAPL")
    .build().await?;
instance.run_to_end("NASDAQ:AAPL", timeframe).await?;

標的字串作為最後一個參數傳入;標的元資料由 provider 自動解析。

檢查腳本資訊

使用獨立函數 script_info() 檢查腳本的元資料,無需建立可執行實例,也不需要資料提供者、品種或週期:

rust
use navi_vm::script_info;

let info = script_info(source)?;

println!("Script type: {:?}", info.script_type);
println!("Is overlay: {}", info.overlay());
println!("Inputs: {} total", info.inputs.len());

for input in &info.inputs {
    // 檢查輸入定義
}

在建構實例之前可用此函數查詢輸入 ID(詳見輸入配置)。

建構器方法

InstanceBuilder(來自 Instance::builder)在 build() 時編譯源碼。 InstanceBuilderFromProgram(來自 Instance::builder_from_program)跳過編譯。 編譯期選項僅在 InstanceBuilder 上可用。

方法說明builderbuilder_from_program
with_locale數字/日期格式化的地區設定
with_input_value按 ID 覆蓋腳本輸入
with_input_sessions篩選處理的交易時段
with_strategy_config_override在實例層面覆蓋 strategy() 參數
with_background_color自訂圖表背景顏色
with_last_info最後一根 K 線的索引與時間戳
with_execution_limits迴圈迭代次數與 security 呼叫次數限制
with_mtf_wait即時階段等待 request.security() MTF 資料就緒的最大時限
with_output_mode系列圖形輸出模式(ChartStream
with_jit啟用 JIT 編譯(需要 jit feature)
with_library_loaderuse 陳述式的自訂載入器,支援鏈式 fallback

腳本元資料

with_path

為腳本設定檔案路徑(用於錯誤訊息):

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

with_locale

設定數字與日期格式化的地區設定:

rust
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_locale("zh")
    .build().await?;

輸入配置

with_input_value

按 ID 覆蓋預設輸入值。輸入 ID 從 0 開始按順序分配,與腳本中 input.*() 呼叫的出現順序一致。

如需查詢各輸入的 ID,可使用 script_info()(詳見檢查腳本資訊):inputs 是按宣告順序排列的 Vec<Input>,索引即為 ID。

rust
// Navi 腳本:
// length = input.int(14, "RSI Length")    // input id 0
// src = input.source(close, "Source")     // input id 1

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_input_value(0, 21)       // 將 RSI Length 覆蓋為 21
    .build().await?;

with_input_sessions

控制處理哪些交易時段:

rust
use navi_vm::InputSessions;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_input_sessions(InputSessions::REGULAR | InputSessions::EXTENDED)
    .build().await?;
旗標描述
InputSessions::REGULAR常規交易時段
InputSessions::EXTENDED延長/盤前/盤後交易
InputSessions::OVERNIGHT隔夜交易時段
InputSessions::ALL所有交易時段

策略配置

with_strategy_config_override

在不修改腳本原始碼的情況下,覆蓋 strategy() 的部分參數。設為 Some(…) 的欄位優先於腳本中宣告的值;設為 None 的欄位回退至腳本預設值。對非策略腳本無效。

rust
use navi_vm::{Instance, StrategyConfigOverride, TimeFrame};

let instance = Instance::builder(bars, source, TimeFrame::days(1), "NASDAQ:AAPL")
    .with_strategy_config_override(StrategyConfigOverride {
        initial_capital: Some(50_000.0),
        commission_value: Some(0.1),
        ..Default::default()
    })
    .build().await?;

StrategyConfigOverride 欄位(均為 Option<T>,預設 None):

欄位說明預設值
initial_capital初始帳戶資金1_000_000
commission_type佣金計算方式:percentcash_per_contractcash_per_orderpercent
commission_value佣金金額。percent 模式下為百分比數值(1 = 1%);cash_per_contractcash_per_order 模式下為每合約或每筆訂單的絕對金額。0
margin_long多頭保證金比率(%)100
margin_short空頭保證金比率(%)100
slippage每筆成交滑點,單位:tick0
pyramiding同方向最大開倉次數0
risk_free_rate年化無風險利率(%),用於計算 Sharpe/Sortino 比率2
process_orders_on_close在 bar 收盤價成交,而非下一根 bar 開盤價false
calc_on_order_fills訂單成交後重新執行腳本false
backtest_fill_limits_assumption限價單成交所需超出限價的額外 tick 數0
close_entries_rule優先平倉規則:FIFOANYFIFO

覆蓋值儲存於實例上,每次 reset() 時自動重新套用,不會因重置而遺失。

顯示與環境

with_background_color

設定自訂背景顏色:

rust
use navi_vm::ast::Color;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_background_color(Color::new(255, 255, 255, 255))
    .build().await?;

with_last_info

提供最後一根可用 K 線的資訊(用於 barstate.is_last):

rust
use navi_vm::LastInfo;

let last = LastInfo::new(499, 1700000000000);
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_last_info(last)
    .build().await?;

執行限制

with_execution_limits

配置執行期限制,以防止腳本失控:

rust
use navi_vm::ExecutionLimits;

let limits = ExecutionLimits::default()
    .with_max_loop_iterations_per_bar(1_000_000);

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_execution_limits(limits)
    .build().await?;

若未呼叫此方法,預設每 Bar 最多 500,000 次迴圈迭代。

還可以透過 with_max_bars_back() 控制記憶體中保留多少根歷史 Bar(預設:1000)。詳情請參閱安全與執行限制

with_mtf_wait

設定每根即時 K 線執行前的固定等待時長(腳本含 request.security() 時生效):

rust
use std::time::Duration;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_mtf_wait(Duration::from_secs(3))
    .build().await?;

即時模式下,主 chart 串流與 request.security() 子串流由不同的非同步任務驅動。新的 chart bar 到達時, 子串流資料可能因網路延遲尚未就緒。若不等待,VM 會使用最後一個快取值。

啟用 with_mtf_wait 後,VM 在每根即時 bar 執行前先等待指定時長,讓子串流有時間推送最新資料,再執行腳本。

行為
Duration::ZERO(預設值)不等待——非阻塞行為
Duration::from_secs(3)每根即時 bar 前 sleep 3 秒——適合 3 秒內到達的資料來源
其他時長固定 sleep,對所有 request.security() 子串流統一生效

腳本中沒有 request.security() 呼叫時,函式立即返回不執行 sleep,零效能開銷。

程式庫載入

with_library_loader

use 陳述式新增自訂程式庫載入器。新 loader 被插入到解析鏈的最前面,失敗後依次 回退到之前註冊的 loader,內建 prelude/stdlib 始終作為最後的 fallback。

rust
use navi_loader::LibraryLoader;

struct MyLoader;

impl LibraryLoader for MyLoader {
    type Source = String;
    type Error = String;
    fn load(&self, path: &str) -> Result<(String, String), Self::Error> {
        // 返回 (源碼, 模組名) 或 Err
        todo!()
    }
}

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_library_loader(MyLoader)
    .build().await?;

多次呼叫 with_library_loader 可建立優先順序堆疊,每個新 loader 優先於之前註冊的 loader:

rust
// 解析順序:loader_b → loader_a → 內建 prelude/stdlib
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_library_loader(loader_a)
    .with_library_loader(loader_b)
    .build().await?;

進階編譯

編譯一次,複用 Program

Instance::builder() 每次 build().await 都會重新編譯源碼。如果需要把同一個 Program 用於多個實例(例如不同的 provider 或不同的輸入值),可先編譯一次, 再用 Instance::builder_from_program 構造每個實例,避免重複編譯:

rust
use navi_compiler::CompileOptions;
use navi_vm::{Instance, NativeFuncs, TimeFrame};

// 編譯一次。
let program = CompileOptions::new(source)
    .with_library_loader(navi_builtins::builtins_loader())
    .with_load_options(navi_builtins::load_options())
    .with_native_func_registry(&NativeFuncs)
    .compile()?;

// 多個實例共享同一個 program,不再重新編譯。
let instance_a = Instance::builder_from_program(
    program.clone(), provider_a, TimeFrame::days(1), "NASDAQ:AAPL",
)
.with_input_value(0, 14)
.build().await?;

let instance_b = Instance::builder_from_program(
    program.clone(), provider_b, TimeFrame::days(1), "NASDAQ:AAPL",
)
.with_input_value(0, 21)
.build().await?;

Program 內部用 Arc 持有,複製開銷很小。注意 builder_from_program 不暴露 with_library_loader——這是編譯期選項,對已編譯的 program 無效。

CompileOptions 的 setter

直接用 navi_compiler::CompileOptions 時可以使用下列 setter —— Instance::builder 沒有暴露它們,因為它內部已經配好了合理的預設值:

Setter說明
with_load_options(opts)覆蓋 prelude/stdlib 解析設定。一般以 navi_builtins::load_options() 為起點再做客製化。
with_native_func_registry(&registry)替換 native 函式註冊表。必須包含腳本用到的所有 native 函式;傳 &navi_vm::NativeFuncs 即可使用 vm 自帶的全套。
with_locale(locale)InstanceBuilder 上同名方法行為一致。
with_library_loader(loader)同上 —— 把額外的 loader 鏈到現有 loader 之前。
with_optimize(enable)切換最佳化開關(預設 true)。

輸出模式

with_output_mode

控制 plot() 資料的產出方式。

模式行為
OutputMode::Chart(預設)系列圖形資料寫入 instance.chart()
OutputMode::Stream系列圖形資料以 DrawEvent 形式在事件流中發出
rust
use navi_vm::{Instance, OutputMode, TimeFrame};

let mut instance = Instance::builder(provider, source, TimeFrame::days(1), "NASDAQ:AAPL")
    .with_output_mode(OutputMode::Stream)
    .build().await?;

詳見 執行腳本

完整示例

rust
use navi_vm::{Instance, OutputMode, StrategyConfigOverride, TimeFrame};

let mut instance = Instance::builder(bars, source, TimeFrame::minutes(5), "NASDAQ:AAPL")
    .with_locale("en")
    .with_input_value(0, 20)        // 覆蓋第一個輸入
    .with_input_value(1, 2.5)       // 覆蓋第二個輸入
    .with_input_sessions(InputSessions::REGULAR)
    .with_last_info(LastInfo::new(999, last_time))
    .with_output_mode(OutputMode::Stream)
    // 僅策略腳本有效:覆蓋執行參數
    .with_strategy_config_override(StrategyConfigOverride {
        initial_capital: Some(50_000.0),
        commission_value: Some(0.1),
        ..Default::default()
    })
    .build().await?;

instance.run_to_end("NASDAQ:AAPL", TimeFrame::minutes(5)).await?;

下一步

基於 MIT 許可證發佈。