讀取輸出
執行 K 線後,你可以通過 instance.chart() 檢查視覺輸出,通過 instance.script_info() 檢查腳本元資料。
有關完整的圖表資料模型(所有欄位、列舉和渲染演算法),請參閱圖表渲染。
存取圖形
圖表包含序列圖(逐 K 線資料)和圖形(時間點繪圖物件):
let chart = instance.chart();
// 序列圖:每根 K 線一個值
for (id, graph) in chart.series_graphs() {
match graph {
SeriesGraph::Plot(plot) => { /* 線條、面積、柱狀圖等 */ }
SeriesGraph::PlotCandle(candle) => { /* OHLC K 線疊加 */ }
SeriesGraph::PlotShape(shape) => { /* 形狀標記 */ }
SeriesGraph::PlotArrow(arrow) => { /* 箭頭標記 */ }
SeriesGraph::PlotBar(bar) => { /* OHLC 條形圖 */ }
SeriesGraph::PlotChar(ch) => { /* 字元標記 */ }
SeriesGraph::BackgroundColors(bg) => { /* 背景顏色 */ }
SeriesGraph::Fill(fill) => { /* 繪圖之間的填充 */ }
}
}
// 時間點圖形
for (id, graph) in chart.graphs() {
match graph {
Graph::Label(label) => { /* 文字標註 */ }
Graph::Line(line) => { /* 線段 */ }
Graph::Box(bx) => { /* 矩形 */ }
Graph::Table(table) => { /* 儲存格網格 */ }
Graph::Hline(hline) => { /* 水平線 */ }
Graph::LineFill(fill) => { /* 兩條線之間的填充 */ }
Graph::Polyline(poly) => { /* 多點線 */ }
}
}你也可以通過 ID 查找單個圖形:
if let Some(sg) = chart.series_graph(id) {
// ...
}
if let Some(g) = chart.graph(id) {
// ...
}腳本資訊
檢查已編譯腳本的元資料:
let info = instance.script_info();
match &info.script_type {
ScriptType::Indicator(ind) => {
println!("Indicator: {}", ind.title);
println!("Overlay: {}", ind.overlay);
}
ScriptType::Strategy(strat) => {
println!("Strategy: {}", strat.title);
println!("Initial capital: {}", strat.initial_capital);
}
ScriptType::Library(lib) => {
println!("Library: {}", lib.title);
}
}
// 列出所有輸入
for input in &info.inputs {
// 每個輸入變體有:id、title、tooltip、default_value 等
}
// 警報條件
for alert in &info.alert_conditions {
// 警報條件定義
}輸出模式
Navi 支援兩種輸出模式:
Chart 模式(預設)
Chart 模式下,所有輸出累積在 Chart 結構體中。執行完成後,透過 instance.chart() 和 instance.strategy_report() 讀取結果。完整的報告結構請參閱策略報告。
適用場景:本地圖表渲染、批量回測、一次性分析。
let instance = instance.run_to_end(symbol, tf).await?;
let chart = instance.chart();
let report = instance.strategy_report(); // 策略腳本Stream 模式
Stream 模式下,繪圖指令和策略事件作為 Event 變體透過 instance.run() 回傳的 RunHandle 發出。建構實例時使用 OutputMode::Stream:
let instance = Instance::builder(candles, source, tf, symbol)
.with_output_mode(OutputMode::Stream)
.build().await?;適用場景:即時推送、遠端客戶端、事件驅動架構、僅需計算結果。
事件
事件在執行期間產生,透過 handle.next_event().await 逐個取得(handle 由 instance.run() 回傳,並在執行期間持有 Instance)。
| 事件 | 說明 |
|---|---|
ScriptInfo(Arc<ScriptInfo>) | 流開始時發出的腳本元資料(僅 Stream 模式),包含宣告的序列、輸入、告警和 overlay 設定。 |
Warning(Warnings) | 編譯警告(在 ScriptInfo 之後、第一根 K 線之前發出一次,有警告時)。 |
BarStart(BarStartEvent) | K 線開始。包含 bar_index 和 candlestick(完整 OHLCV)。 |
BarEnd | K 線執行結束。 |
HistoryEnd | 歷史與即時 K 線的分界標記。 |
Log(LogEvent) | 日誌訊息,含級別(Info、Warning、Error)和內容。 |
Alert(AlertEvent) | 警報,含可選 id 和訊息。 |
Draw(DrawEvent) | 繪圖指令(僅 Stream 模式)。包裝 SeriesEvent 或 GraphEvent。 |
Strategy(StrategyEvent) | 策略事件(僅 Stream 模式)。訂單、成交、權益快照等。 |
BarStart、BarEnd、HistoryEnd、Log、Alert、Warning 在兩種模式下均發出。Draw 和 Strategy 僅在 Stream 模式下發出。
繪圖事件
DrawEvent 有四個變體:
DrawEvent::NewBar { bar_index }— 新 K 線開始的信號。每根新 K 線(歷史或實時新開)發出一次,早於該 K 線的所有Series事件。ChartAccumulator用此事件擴展內部系列緩衝區。DrawEvent::Series(SeriesEvent)— 逐 K 線計算值,來自plot()、bg_color()、fill()、plot_arrow()、plot_bar()、plot_candle()、plot_char()、plot_shape()、bar_color()。靜態配置來自Event::ScriptInfo,後續SeriesEvent只攜帶逐 bar 更新。DrawEvent::Graph(GraphEvent)— 時間點繪圖物件(label、line、box、table、hline、linefill、polyline)。Add*攜帶完整結構體,Update*使用屬性級列舉,Remove表示刪除或淘汰。DrawEvent::FilledOrder(FilledOrder)— 已執行的成交訂單,包含order_id、price、quantity。
SeriesEvent 變體
| 事件 | Navi 函式 | 說明 |
|---|---|---|
UpdatePlot | plot() | 線條、面積、柱狀圖等。攜帶 value 和 color。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdateBgColor | bg_color() | 背景顏色帶。攜帶 color。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdateFill | fill() | 兩個 plot 或 hline 之間的填充。攜帶 color(純色或漸層)。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdatePlotArrow | plot_arrow() | 箭頭標記。攜帶 value、up_color、down_color。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdatePlotBar | plot_bar() | OHLC 條形圖。攜帶 bar(OHLC)和 color。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdatePlotCandle | plot_candle() | OHLC K 線疊加。攜帶 bar、color、wick_color、border_color。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdatePlotChar | plot_char() | 字元標記。攜帶 value、color、text_color。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdatePlotShape | plot_shape() | 形狀標記。攜帶 value、color、text_color。靜態宣告來自 ScriptInfo.series_declarations。 |
UpdateBarColor | bar_color() | K 線著色。攜帶 color。靜態宣告來自 ScriptInfo.bar_color_declaration。 |
Event::ScriptInfo 攜帶腳本宣告的全部 series_declarations(按從 0 開始的 series graph id 順序排列),以及可選的 bar_color_declaration。在 stream 模式下,消費端可以先根據 ScriptInfo 建立宣告索引,再把後續 Update* 事件視為按索引更新。
所有公開的 draw event id 現在都以 u64 序列化。對 series 事件來說,這個 u64 就是 series_declarations 中對應的、從 0 開始的宣告索引。
GraphEvent 變體
| 事件 | Navi 函式 | 說明 |
|---|---|---|
AddHline | hline() | 水平線。建立後不可變。 |
AddLabel / UpdateLabel | label.new() | 文字標註。Update 使用 LabelAttr 列舉。 |
AddLine / UpdateLine | line.new() | 線段。Update 使用 LineAttr 列舉。 |
AddBox / UpdateBox | box.new() | 矩形。Update 使用 BoxAttr 列舉。 |
AddTable / UpdateTableAttr / UpdateTableCell / UpdateTableCellAttr | table.new() | 儲存格網格。三種更新粒度:表級屬性、整個儲存格、儲存格屬性。 |
AddLineFill / UpdateLineFill | linefill.new() | 兩條線之間的填充。 |
AddPolyline | polyline.new() | 多點線。建立後不可變。 |
Remove | *.delete() / 淘汰 | 表示刪除或超限淘汰。 |
策略事件
| 事件 | 說明 |
|---|---|
Config | 策略配置快照(初始資金、佣金、保證金等)。首根 K 線發出一次。 |
OrderSubmitted | 透過 strategy.entry() 或 strategy.order() 提交訂單。 |
OrderFilled | 訂單成交。包含 id、direction、price、quantity、commission。 |
OrderCancelled | 訂單取消。 |
TradeOpened | 成交後新倉位開立。包含入場成交的 bar_index 和 bar_time(Unix 毫秒時間戳)。 |
TradeClosed | 倉位關閉。包含盈虧、最大浮盈、最大回撤、佣金,以及出場成交的 bar_index 和 bar_time(Unix 毫秒時間戳)。 |
OpenTradeUpdated | 逐 K 線更新持倉的盈虧和風險指標。 |
EquitySnapshot | 逐 K 線權益和買入持有權益。 |
DailyReturn | 每日回報率,用於 Sharpe/Sortino 計算。 |
MarginCall | 保證金追繳事件。 |
RiskFlatten | 風控觸發平倉。 |
基本事件消費
use navi_vm::{Event, LogLevel};
let mut handle = instance.run(symbol, tf);
while let Some(result) = handle.next_event().await {
match result? {
Event::Log(log) => {
match log.level {
LogLevel::Info => println!("INFO: {}", log.message),
LogLevel::Warning => println!("WARN: {}", log.message),
LogLevel::Error => println!("ERROR: {}", log.message),
}
}
Event::Alert(alert) => {
println!("ALERT [id={:?}]: {}", alert.id, alert.message);
}
Event::HistoryEnd => {
println!("History replay complete");
}
_ => {}
}
}提取計算值(Stream 模式)
大多數整合場景裡,更推薦直接使用 Instance::plot_rows()。這是專門為 stream 模式提取 plot() 值準備的迭代器,只暴露 plot 欄。它要求 OutputMode::Stream,否則會 panic。
推薦:plot_rows()
use navi_vm::{Instance, OutputMode};
let instance = Instance::builder(candles, source, tf, symbol)
.with_output_mode(OutputMode::Stream)
.build().await?;
let mut stream = instance.plot_rows(symbol, tf);
let columns = stream.columns().to_vec();
let mut rows = Vec::<Vec<Option<f64>>>::new();
while let Some(row) = stream.next_row().await {
rows.push(row?.to_vec());
}
let macd_index = columns
.iter()
.position(|title| title.as_deref() == Some("MACD"));
let macd_values: Vec<Option<f64>> = macd_index
.map(|index| rows.iter().map(|row| row[index]).collect())
.unwrap_or_default();手動處理 stream 事件
如果你需要更底層的控制,也可以繼續手動處理事件流來提取 plot() 值:
use navi_vm::{Event, OutputMode, DrawEvent};
use navi_vm::visuals::SeriesEvent;
let instance = Instance::builder(candles, source, tf, symbol)
.with_output_mode(OutputMode::Stream)
.build().await?;
let macd_id = instance.script_info().series_id_by_title("MACD");
let mut macd_values: Vec<Option<f64>> = Vec::new();
let mut new_bar = false;
let mut handle = instance.run(symbol, tf);
while let Some(result) = handle.next_event().await {
match result? {
Event::Draw(DrawEvent::NewBar { .. }) => {
new_bar = true;
}
Event::Draw(DrawEvent::Series(SeriesEvent::UpdatePlot { id, value, .. })) => {
if macd_id == Some(id) {
if new_bar {
// 新 K 線:追加槽位
macd_values.push(value);
new_bar = false;
} else {
// 同一根 K 線的即時重算——替換最後一個值
if let Some(last) = macd_values.last_mut() {
*last = value;
}
}
}
}
_ => {}
}
}這種寫法會先透過 script_info() 解析一次 series id,再在整個 stream 生命週期裡重用,開銷更低。
從事件重建 Chart
使用 ChartAccumulator 從事件流重建完整的 Chart。透過 TryFrom 將事件轉為 ChartEvent——處理 ScriptInfo、HistoryEnd、NewBar、Series、Graph 和 FilledOrder。非圖表事件(如 Log、Alert、Strategy)返回 Err,可單獨處理:
use navi_vm::{Event, visuals::{ChartAccumulator, ChartEvent}};
let mut acc = ChartAccumulator::new();
let mut handle = instance.run(symbol, tf);
while let Some(result) = handle.next_event().await {
match ChartEvent::try_from(result?) {
Ok(chart_event) => acc.apply(chart_event),
Err(_other) => { /* Log、Alert、Strategy、Warning、BarStart、BarEnd */ }
}
}
let chart = acc.into_chart();從事件重建策略報告
使用 StrategyAccumulator 從策略事件重建 StrategyReport。報告的完整結構和所有可用欄位請參閱策略報告。
use navi_vm::{Event, StrategyAccumulator};
let mut acc = StrategyAccumulator::new();
let mut handle = instance.run(symbol, tf);
while let Some(result) = handle.next_event().await {
if let Event::Strategy(e) = result? {
acc.apply(e);
}
}
let report = acc.report();如不需要逐個處理事件,使用 run_to_end():
let instance = instance.run_to_end(symbol, tf).await?;
// 事件被丟棄;透過 instance.chart() 等讀取結果完整範例
use navi_vm::{Candlestick, Event, Instance, TimeFrame};
async fn run(candles: Vec<Candlestick>) -> Result<(), navi_vm::Error> {
let source = r#"
indicator("RSI Monitor")
rsi = ta.rsi(close, 14)
plot(rsi, "RSI")
hline(70, "Overbought")
hline(30, "Oversold")
alert_condition(ta.crossover(rsi, 70), "RSI Overbought")
"#;
let tf = TimeFrame::days(1);
let symbol = "NASDAQ:AAPL";
let instance = Instance::builder(candles, source, tf, symbol)
.build().await?;
// 消費事件流
let mut handle = instance.run(symbol, tf);
while let Some(result) = handle.next_event().await {
if let Event::Alert(alert) = result? {
println!("Alert: {}", alert.message);
}
}
let instance = handle.into_instance();
// 列印最終 RSI 值
for (_id, graph) in instance.chart().series_graphs() {
if let Some(plot) = graph.as_plot() {
if plot.title.as_deref() == Some("RSI") {
if let Some(last) = plot.series.last() {
println!("Final RSI: {:?}", last);
}
}
}
}
Ok(())
}