Skip to content

執行腳本

Candlestick

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

rust
use openpine_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 openpine_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);

// 從字串解析(TradingView 格式)
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(),然後呼叫 instance.run_to_end()

rust
use openpine_vm::TimeFrame;

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

instance.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 CandlestickItemCandlestickItem::Confirmed(candle) 表示已收盤的 K 線,CandlestickItem::Realtime(candle) 表示正在形成的即時 K 線。在所有歷史 K 線之後、即時 K 線之前 yield CandlestickItem::HistoryEnd,標記歷史資料已結束。VM 會自動處理回滾和確認邏輯。

事件流

Instance::run() 回傳 Stream<Item = Result<Event, Error>>,在腳本逐 K 線執行時逐個產出事件。

事件類型

事件何時發出
BarStart(BarStartEvent)每根 K 線執行前。包含 bar_indextimestamp(epoch ms)、bar_state(見下方)。
BarEnd每根 K 線執行完成後(策略處理、腳本體等)。始終與前一個 BarStart 配對。
HistoryEnd僅一次,當 DataProvider yield CandlestickItem::HistoryEnd 時。標記從歷史回放到即時資料的轉換。不跟隨 BarStart
Log(LogEvent)K 線執行期間,腳本呼叫 log.info()log.warning()log.error() 時。在 BarStartBarEnd 之間發出。
Alert(AlertEvent)K 線執行期間,腳本呼叫 alert()alertcondition() 觸發時。在 BarStartBarEnd 之間發出。
Draw(DrawEvent)OutputMode::Stream 模式下,腳本呼叫 plot() 時。AddPlot 在首根 K 線發出一次;UpdatePlot 每根 K 線都發出。

Bar 狀態

BarStartEvent.bar_state 說明當前 K 線為何被執行:

狀態含義
History來自 CandlestickItem::Confirmed 的已收盤歷史 K 線。每根僅執行一次。執行後 bar_index 遞增。這是初始回測階段的狀態。
RealtimeNew即時 K 線的第一個 tick。出現在:HistoryEnd 後的第一個 Realtime,或時間戳晚於前一個 Realtime 的 tick(此時 VM 會先自動確認前一根 K 線)。bar_index 在確認前不會遞增。
RealtimeUpdate同一根即時 K 線的後續 tick(與前一個 Realtime 時間戳相同)。VM 回滾變數狀態到該 K 線的初始快照,然後用更新後的 OHLCV 資料重新執行腳本。bar_index 與前面的 RealtimeNew 相同。
RealtimeConfirmed當前即時 K 線被確認(收盤)。兩種情況:(1) 收到時間戳更晚的新 Realtime tick — VM 在開啟新 K 線前自動確認前一根;(2) 資料流結束時仍有未確認的即時 K 線 — VM 將其作為最後一步確認。確認後 bar_index 遞增。

事件順序

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

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

用法

rust
use std::pin::pin;
use futures_util::StreamExt;
use openpine_vm::{Event, Instance, TimeFrame};

let mut stream = pin!(instance.run("NASDAQ:AAPL", TimeFrame::days(1)));
while let Some(result) = stream.next().await {
    match result? {
        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 模式 */ }
    }
}

如不需要事件流,使用便捷方法 run_to_end(),它會在內部消費完整個流:

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

輸出模式

OutputMode 控制 plot() 資料的產出方式。透過建構器設定:

rust
use openpine_vm::{Instance, OutputMode, TimeFrame};

let mut instance = Instance::builder(provider, source, TimeFrame::days(1), "NASDAQ:AAPL")
    .with_output_mode(OutputMode::Stream)
    .build().await?;
模式plot() 行為適用場景
OutputMode::Chart(預設)寫入 instance.chart()執行完畢後讀取完整圖表
OutputMode::Stream在事件流中發出 DrawEvent串流消費繪圖資料,無需建構圖表

Stream 模式範例

OutputMode::Stream 下,每次 plot() 呼叫會發出事件而非寫入圖表:

rust
use std::pin::pin;
use futures_util::StreamExt;
use openpine_vm::{DrawEvent, Event, Instance, OutputMode, TimeFrame};

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

let mut stream = pin!(instance.run("NASDAQ:AAPL", TimeFrame::days(1)));
while let Some(result) = stream.next().await {
    match result? {
        Event::Draw(DrawEvent::AddPlot { id, title }) => {
            println!("新繪圖: id={}, title={:?}", id, title);
        }
        Event::Draw(DrawEvent::UpdatePlot { id, bar_index, timestamp, value }) => {
            println!("繪圖 {} K線 {}: {:?}", id, bar_index, value);
        }
        _ => {}
    }
}

BarStartBarEndHistoryEndLogAlert 在兩種模式下都會發出。只有 Draw 事件受輸出模式影響。

下一步

基於 MIT 許可證發佈。