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 许可证发布。