Skip to content

執行腳本

Candlestick

Candlestick 結構體表示一根 OHLCV K 線:

rust
use navi_vm::{Candlestick, market::TradeSession};

let candle = Candlestick::new(
    1700000000000,          // time: 毫秒級時間戳
    150.0,                  // open
    155.0,                  // high
    149.0,                  // low
    153.0,                  // close
    1_000_000.0,            // volume
    0.0,                    // turnover
    TradeSession::Regular,  // 交易時段
);

Candlestick 還提供衍生價格計算:

rust
candle.hl2();    // (high + low) / 2
candle.hlc3();   // (high + low + close) / 3
candle.ohlc4();  // (open + high + low + close) / 4
candle.hlcc4();  // (high + low + close + close) / 4

TimeFrame

TimeFrame 指定圖表週期。可以使用便捷方法建立或從字串解析:

rust
use navi_vm::TimeFrame;

// 便捷建構函式
let tf = TimeFrame::days(1);
let tf = TimeFrame::minutes(5);
let tf = TimeFrame::weeks(1);
let tf = TimeFrame::months(1);
let tf = TimeFrame::seconds(30);

// 從字串簡寫解析
let tf: TimeFrame = "D".parse().unwrap();    // 1 天
let tf: TimeFrame = "5".parse().unwrap();    // 5 分鐘
let tf: TimeFrame = "60".parse().unwrap();   // 60 分鐘(1 小時)
let tf: TimeFrame = "W".parse().unwrap();    // 1 週
let tf: TimeFrame = "3M".parse().unwrap();   // 3 個月

// 檢查
let seconds = tf.in_seconds();  // Option<u64>
let display = tf.to_string();   // "D", "5", "W" 等

SymbolInfo

SymbolInfo 攜帶交易標的的元資料。你不再需要手動構造它——只需將標的字串傳給 Instance::builder,建構器會自動解析(若配置了 provider 則呼叫 DataProvider::symbol_info,否則使用內建預設值)。

支援的市場前綴:NYSENASDAQSHSESZSEHKEXSGX

如需提供自訂標的元資料(描述、貨幣、最小變動單位等),實現 DataProvider::symbol_info 並回傳 PartialSymbolInfo。詳情參見實例建構器

提供資料

歷史回測

直接將 Vec<Candlestick> 傳給 Instance::builder(),呼叫 run_to_end() 時 VM 會主動從中拉取資料:

rust
use navi_vm::TimeFrame;

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

對於惰性載入/串流資料,先收集為 Vec<Candlestick>,或實作自訂 DataProvider

rust
// 從迭代器收集
let bars: Vec<Candlestick> = load_bars_from_db().collect();
let mut instance = Instance::builder(bars, source, timeframe, "NASDAQ:AAPL")
    .build().await?;

即時串流

實作 DataProvider 並 yield CandlestickItem::Bar(candle)HistoryEnd 之前的所有 bar 必須是已完全收盤的歷史 K 線——HistoryEnd 是一條硬邊界,意味著「所有歷史資料均已確認」。在所有已確認的歷史 bar 發送完畢後 yield CandlestickItem::HistoryEndHistoryEnd 之後的 Bar 為即時 K 線——時間戳可能與最後一根歷史 bar 相同(若查詢時該週期仍在形成中),也可能是更晚的新時間戳(新 bar)。VM 會自動處理回滾和確認邏輯。

事件流

Instance::run() 回傳 RunHandle,在腳本逐 K 線執行時逐個產出事件。

RunHandle

RunHandle 在執行期間持有 Instance。你可以透過 handle.next_event().await 逐個迭代事件,並藉由 DerefMut 直接在 handle 上呼叫實例方法;執行結束後再透過 handle.into_instance() 取回實例。

當實時 K 線發生 rollover(先收到 Bar(tA),再收到時間戳更晚的 Bar(tB),其中 tB > tA),流保持與之前相同的事件順序——先確認 tA,再開始 tB——但這兩步分開暴露,以便呼叫方在確認邊界處執行 checkpoint。

事件類型

事件何時發出
ScriptInfo(Arc<ScriptInfo>)流開始:腳本元資料(標題、overlay 旗標、輸入、宣告的序列、告警)。在任何 K 線之前發出一次(僅 Stream 模式)。
Warning(Warnings)編譯警告,在 ScriptInfo 之後、任何 K 線之前發出一次(有警告時)。
BarStart(BarStartEvent)每根 K 線執行前。包含 bar_indexcandlestick(完整 OHLCV)以及 bar_stateHistoryRealtimeNewRealtimeUpdateRealtimeConfirmed)。
BarEnd每根 K 線執行完成後(策略處理、腳本體等)。始終與前一個 BarStart 配對。
HistoryEnd僅一次,當 DataProvider yield CandlestickItem::HistoryEnd 時。標記從歷史回放到即時資料的轉換。不跟隨 BarStart
Log(LogEvent)K 線執行期間,腳本呼叫 log.info()log.warning()log.error() 時。在 BarStartBarEnd 之間發出。
Alert(AlertEvent)K 線執行期間,腳本呼叫 alert()alert_condition() 觸發時。在 BarStartBarEnd 之間發出。
Draw(DrawEvent)OutputMode::Stream 模式下。包裝 SeriesEventGraphEventFilledOrder。詳見讀取輸出
Strategy(StrategyEvent)OutputMode::Stream 模式下。訂單、成交、權益快照等。詳見讀取輸出

事件順序

典型的歷史 + 即時會話中,流的產出順序:

BarStart(0) → Log/Alert/Draw... → BarEnd   ← 歷史 K 線 0
BarStart(1) → Log/Alert/Draw... → BarEnd   ← 歷史 K 線 1
...
HistoryEnd
BarStart(N) → ... → BarEnd    ← K 線 N 的第一個 tick(RealtimeNew)
BarStart(N) → ... → BarEnd    ← 同時間戳 tick 更新(RealtimeUpdate)
BarStart(N) → ... → BarEnd    ← K 線 N 確認(RealtimeConfirmed)
BarStart(N+1) → ... → BarEnd  ← K 線 N+1 的第一個 tick(RealtimeNew)
BarStart(N+1) → ... → BarEnd  ← 流結束,最終確認(RealtimeConfirmed)

用法

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

let mut handle = instance.run("NASDAQ:AAPL", TimeFrame::days(1));
while let Some(result) = handle.next_event().await {
    match result? {
        Event::ScriptInfo(info) => {
            println!("腳本類型:{:?}", info.script_type);
        }
        Event::BarStart(bs) => {
            println!("K 線 {} 開始,階段為 {:?}", bs.bar_index, bs.bar_state);
        }
        Event::BarEnd => { /* K 線結束 */ }
        Event::HistoryEnd => {
            println!("歷史回放完成,後續為即時 K 線");
        }
        Event::Log(log) => println!("[{:?}] {}", log.level, log.message),
        Event::Alert(alert) => println!("ALERT: {}", alert.message),
        Event::Draw(draw) => { /* 繪圖資料,僅 Stream 模式 */ }
        Event::Strategy(strat) => { /* 策略事件,僅 Stream 模式 */ }
        _ => {}
    }
}

如不需要事件流,使用便捷方法 run_to_end(),它會在內部消費完整個流並回傳執行完成後的實例:

rust
let instance = instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await?;
// 執行完畢後透過 instance.chart()、instance.strategy_report() 等讀取結果

重置狀態

instance.reset() 丟棄所有執行時狀態,將實例恢復到建構完成後的初始狀態——就好像從未執行過任何 K 線——而無需重新編譯腳本或重新建立物件。

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

// 重置:清除所有 K 線、圖表、策略狀態等
instance.reset();

// 用相同的腳本和資料源從第 0 根 K 線重新執行
let instance = instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await?;

何時用 reset() vs. 新建 Instance

  • reset() — 適合在同一編譯程式和資料源上反覆執行(例如參數掃描、調整輸入後重跑)。省去重新編譯和 Arena 分配的開銷。
  • 新建 Instance — 腳本原始碼、標的或週期變化時必須新建,或需要在建構時傳入不同的輸入值覆蓋時使用。
  • 快照 — 希望後續啟動時跳過歷史回放、保留已累積狀態時使用。參見快照

注意事項

透過建構器 .with_input_value() 設定的輸入值覆蓋在 build() 時寫入 GC Arena,之後不再保存在實例上。reset() 後,輸入會恢復為腳本中宣告的預設值(例如 input.int(14, "Length") 恢復為 14)。

下一步

基於 MIT 許可證發佈。