實例建構器
InstanceBuilder 配置並將 Navi 腳本編譯為可執行的 Instance。
基本用法
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,服務於兩個目的:
- 主 chart K 線 —
Instance::run()從中消費資料驅動逐 bar 執行 request.security()資料 — 多週期和跨標的 K 線流
Vec<Candlestick> 直接實作了 DataProvider,無需任何包裝即可傳入:
let mut instance = Instance::builder(bars, source, timeframe, "NASDAQ:AAPL")
.build().await?;
instance.run_to_end("NASDAQ:AAPL", timeframe).await?;多標的或即時資料,實作 DataProvider:
let mut instance = Instance::builder(MyProvider::new(), source, timeframe, "NASDAQ:AAPL")
.build().await?;
instance.run_to_end("NASDAQ:AAPL", timeframe).await?;標的字串作為最後一個參數傳入;標的元資料由 provider 自動解析。
檢查腳本資訊
使用獨立函數 script_info() 檢查腳本的元資料,無需建立可執行實例,也不需要資料提供者、品種或週期:
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 上可用。
| 方法 | 說明 | builder | builder_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 | 系列圖形輸出模式(Chart 或 Stream) | ✓ | ✓ |
with_jit | 啟用 JIT 編譯(需要 jit feature) | ✓ | ✓ |
with_library_loader | use 陳述式的自訂載入器,支援鏈式 fallback | ✓ | — |
腳本元資料
with_path
為腳本設定檔案路徑(用於錯誤訊息):
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
.build().await?;with_locale
設定數字與日期格式化的地區設定:
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。
// 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
控制處理哪些交易時段:
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 的欄位回退至腳本預設值。對非策略腳本無效。
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 | 佣金計算方式:percent、cash_per_contract 或 cash_per_order | percent |
commission_value | 佣金金額。percent 模式下為百分比數值(1 = 1%);cash_per_contract 或 cash_per_order 模式下為每合約或每筆訂單的絕對金額。 | 0 |
margin_long | 多頭保證金比率(%) | 100 |
margin_short | 空頭保證金比率(%) | 100 |
slippage | 每筆成交滑點,單位:tick | 0 |
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 | 優先平倉規則:FIFO 或 ANY | FIFO |
覆蓋值儲存於實例上,每次 reset() 時自動重新套用,不會因重置而遺失。
顯示與環境
with_background_color
設定自訂背景顏色:
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):
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
配置執行期限制,以防止腳本失控:
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() 時生效):
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。
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:
// 解析順序: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 構造每個實例,避免重複編譯:
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(®istry) | 替換 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 形式在事件流中發出 |
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?;詳見 執行腳本。
完整示例
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?;