執行腳本
Candlestick
Candlestick 結構體表示一根 OHLCV K 線:
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 還提供衍生價格計算:
candle.hl2(); // (high + low) / 2
candle.hlc3(); // (high + low + close) / 3
candle.ohlc4(); // (open + high + low + close) / 4
candle.hlcc4(); // (high + low + close + close) / 4TimeFrame
TimeFrame 指定圖表週期。可以使用便捷方法建立或從字串解析:
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,否則使用內建預設值)。
支援的市場前綴:NYSE、NASDAQ、SHSE、SZSE、HKEX、SGX。
如需提供自訂標的元資料(描述、貨幣、最小變動單位等),實現 DataProvider::symbol_info 並回傳 PartialSymbolInfo。詳情參見實例建構器。
餵入資料
歷史回測
直接將 Vec<Candlestick> 傳給 Instance::builder(),然後呼叫 instance.run_to_end():
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:
// 從迭代器收集
let bars: Vec<Candlestick> = load_bars_from_db().collect();
let mut instance = Instance::builder(bars, source, timeframe, "NASDAQ:AAPL")
.build().await?;即時串流
實作 DataProvider 並 yield CandlestickItem:CandlestickItem::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_index、timestamp(epoch ms)、bar_state(見下方)。 |
BarEnd | 每根 K 線執行完成後(策略處理、腳本體等)。始終與前一個 BarStart 配對。 |
HistoryEnd | 僅一次,當 DataProvider yield CandlestickItem::HistoryEnd 時。標記從歷史回放到即時資料的轉換。不跟隨 BarStart。 |
Log(LogEvent) | K 線執行期間,腳本呼叫 log.info()、log.warning() 或 log.error() 時。在 BarStart 和 BarEnd 之間發出。 |
Alert(AlertEvent) | K 線執行期間,腳本呼叫 alert() 或 alertcondition() 觸發時。在 BarStart 和 BarEnd 之間發出。 |
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 ← 流結束,最終確認用法
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(),它會在內部消費完整個流:
instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await?;
// 執行完畢後透過 instance.chart()、instance.strategy_report() 等讀取結果輸出模式
OutputMode 控制 plot() 資料的產出方式。透過建構器設定:
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() 呼叫會發出事件而非寫入圖表:
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);
}
_ => {}
}
}BarStart、BarEnd、HistoryEnd、Log、Alert 在兩種模式下都會發出。只有 Draw 事件受輸出模式影響。