事件壓縮器
EventCompactor 是 Instance::run() 產出的 Event 流的增量過濾器與生命週期追蹤器。專為服務端整合設計:可在即時 K 線確認(收盤)時通知呼叫方,並可選過濾 Log/Alert 雜訊,再持久化事件。
適用場景
以下場景適合使用 EventCompactor:
- 持久化事件時剔除即時中間態。每根即時 K 線在每個 tick 都會產生一套完整的事件週期(
BarStart→ 繪圖事件 →BarEnd)。大多數持久化場景只需要最終狀態;event_count精確告知緩衝區末尾有多少條屬於剛剛確認的 K 線,可據此覆蓋或丟棄之前的中間態記錄。 - 將事件持久化到資料庫或訊息佇列,需要明確的每根即時 K 線提交點。
- 向下游消費者轉發事件,希望剔除
Log/Alert雜訊。 - 需要知道即時 K 線何時收盤,以便觸發批次操作。
如果只是本地簡單使用,無需 EventCompactor,直接消費 RunHandle 流即可。
核心型別
EventCompactionRules
rust
pub struct EventCompactionRules {
/// 丟棄 `Event::Log` 事件,不轉發。預設:`false`。
pub filter_log: bool,
/// 丟棄 `Event::Alert` 事件,不轉發。預設:`false`。
pub filter_alert: bool,
}RealtimeConfirmReason
rust
pub enum RealtimeConfirmReason {
/// 收到時間戳不同的新即時 K 線,上一根自動閉合。
Rollover,
/// 流結束時仍有未確認的即時 K 線,在 `finish()` 時閉合。
StreamEnd,
}RealtimeConfirmedInfo
即時 K 線確認時傳遞給 EventCompactionSink::on_realtime_confirmed:
rust
pub struct RealtimeConfirmedInfo {
/// 已確認 K 線的從 0 開始的索引。
pub bar_index: usize,
/// 已確認 K 線的 Unix 毫秒時間戳。
pub bar_time: i64,
/// K 線確認的原因。
pub reason: RealtimeConfirmReason,
/// 自本根 K 線進入 `RealtimeNew` 階段起,compactor 轉發給 `on_event`
/// 的事件數量(不含 `ScriptInfo`、`Warning`、`HistoryEnd` 等流級別事件)。
pub event_count: usize,
}event_count 是管理中間狀態的核心指標,詳見使用場景。
EventCompactionSink
rust
pub trait EventCompactionSink {
/// 每個通過過濾規則的事件都會呼叫此方法。
fn on_event(&mut self, event: Event);
/// 即時 K 線確認時呼叫此方法。
fn on_realtime_confirmed(&mut self, info: RealtimeConfirmedInfo);
}建立 Compactor
rust
use navi_vm::{EventCompactor, EventCompactionRules};
let rules = EventCompactionRules {
filter_log: true, // 過濾 log.*() 雜訊
filter_alert: false, // 保留 alert() 事件
};
let mut compactor = EventCompactor::new(rules);推送事件
對 RunHandle 的每個事件呼叫 push,流結束後呼叫 finish:
rust
use navi_vm::{Event, EventCompactor, EventCompactionRules, EventCompactionSink,
RealtimeConfirmedInfo};
struct MySink {
buffer: Vec<Event>,
}
impl EventCompactionSink for MySink {
fn on_event(&mut self, event: Event) {
self.buffer.push(event);
}
fn on_realtime_confirmed(&mut self, info: RealtimeConfirmedInfo) {
println!(
"K 線 {} 已確認({:?}),共 {} 個事件",
info.bar_index, info.reason, info.event_count
);
// 提交或歸檔 self.buffer 中最後 event_count 條記錄
}
}
let mut sink = MySink { buffer: Vec::new() };
let mut compactor = EventCompactor::new(EventCompactionRules::default());
let mut handle = instance.run(symbol, tf);
while let Some(result) = handle.next_event().await {
compactor.push(result?, &mut sink);
}
compactor.finish(&mut sink);事件路由
compactor 按如下規則路由事件:
| 事件 | 是否轉發 | 是否計入 event_count |
|---|---|---|
ScriptInfo | 始終轉發 | 否——流級別 |
Warning | 始終轉發 | 否——流級別 |
HistoryEnd | 始終轉發 | 否——流級別 |
BarStart(History) | 始終轉發 | 否 |
BarStart(RealtimeNew/Update/Confirmed) | 始終轉發 | 是 |
BarEnd | 始終轉發 | 是(即時階段時) |
Draw | 始終轉發 | 是(即時階段時) |
Strategy | 始終轉發 | 是(即時階段時) |
Log | 僅 filter_log = false 時 | 轉發時計入 |
Alert | 僅 filter_alert = false 時 | 轉發時計入 |
Rollover 行為
當收到 bar_state = RealtimeNew 且時間戳不同於當前即時 K 線的 BarStart 時,compactor:
- 以
reason = Rollover呼叫on_realtime_confirmed,通知前一根 K 線已確認。 - 開始追蹤新 K 線。
這與 VM 自身的 rollover 邏輯一致:時間戳更晚的新 tick 會隱式關閉前一根 K 線。
push BarStart(N, RealtimeNew, t=T0) → on_event,開始追蹤 K 線 N
push BarEnd → on_event,event_count = 2
push BarStart(N, RealtimeUpdate, t=T0) → on_event,event_count = 3
push BarEnd → on_event,event_count = 4
push BarStart(N+1, RealtimeNew, t=T1) → on_realtime_confirmed(K 線 N, Rollover, count=4)
on_event,開始追蹤 K 線 N+1使用場景
清理中間狀態
向緩衝區追加事件時,event_count 告知末尾有多少條屬於剛剛確認的 K 線,可將其刪除或覆蓋,只保留最終狀態:
rust
fn on_realtime_confirmed(&mut self, info: RealtimeConfirmedInfo) {
let keep_from = self.buffer.len().saturating_sub(info.event_count);
// 丟棄中間更新,只保留已確認 K 線的事件
self.buffer.drain(..keep_from);
}資料庫批次提交
確認後原子提交最後 event_count 行:
rust
fn on_realtime_confirmed(&mut self, info: RealtimeConfirmedInfo) {
let start = self.pending.len().saturating_sub(info.event_count);
let confirmed_rows = &self.pending[start..];
db.commit_batch(confirmed_rows, info.bar_index, info.bar_time);
self.pending.truncate(start);
}稽核追蹤
使用 bar_index 和 event_count 按 K 線分區儲存事件,便於後續回放或偵錯。
與 ChartAccumulator 組合
EventCompactor 和 ChartAccumulator 可自然組合——在 sink 中驅動 accumulator 的同時追蹤生命週期:
rust
use navi_vm::{
Event, EventCompactor, EventCompactionRules, EventCompactionSink, RealtimeConfirmedInfo,
visuals::{ChartAccumulator, ChartEvent},
};
struct ChartSink {
acc: ChartAccumulator,
}
impl EventCompactionSink for ChartSink {
fn on_event(&mut self, event: Event) {
if let Ok(chart_event) = ChartEvent::try_from(event) {
self.acc.apply(chart_event);
}
}
fn on_realtime_confirmed(&mut self, info: RealtimeConfirmedInfo) {
println!("即時 K 線 {} 已收盤({:?})", info.bar_index, info.reason);
}
}