Skip to content

策略報告

執行策略腳本後,Navi 會產生一份 StrategyReport,包含績效指標、交易歷史、權益曲線和設定詳情。

報告結構

StrategyReportinstance.strategy_report() 回傳的頂層型別:

rust
pub struct StrategyReport {
    /// 策略設定快照(初始資金、手續費等)
    pub config: StrategyConfigReport,
    /// 所有交易的綜合績效指標
    pub performance_all: PerformanceMetrics,
    /// 僅多頭交易的績效指標
    pub performance_long: PerformanceMetrics,
    /// 僅空頭交易的績效指標
    pub performance_short: PerformanceMetrics,
    /// 已平倉交易清單(依平倉時間排序)
    pub closed_trades: Vec<ClosedTradeReport>,
    /// 目前未平倉(浮動損益)的交易
    pub open_trades: Vec<OpenTradeReport>,
    /// 每根 K 線的權益值(索引 0 = 第一根 K 線)
    pub equity_curve: Vec<f64>,
    /// 每根 K 線相對峰值權益的回撤(始終 ≥ 0)
    pub drawdown_curve: Vec<f64>,
    /// 每根 K 線的買入持有權益(initial_capital × close / first_close)
    pub buy_hold_curve: Vec<f64>,
    /// 用於計算 Sharpe/Sortino 的日報酬率序列
    pub daily_returns: Vec<DailyReturnReport>,
    /// 從首次入場到最後一筆平倉的時間範圍(無已平倉交易時為 None)
    pub trading_range: Option<TradingRangeReport>,
}

策略設定

StrategyConfigReport 記錄了策略在 strategy() 中宣告的參數:

欄位型別說明
initial_capitalf64初始帳戶資金
commission_typeCommissionTypePerContractPerTradePercentOfValue
commission_valuef64對應手續費類型的手續費金額
margin_longf64多頭保證金要求(%)
margin_shortf64空頭保證金要求(%)
slippagef64訂單滑價(以 tick 為單位)
pyramidingu32同方向最大同時入場次數
risk_free_ratef64用於 Sharpe/Sortino 的年化無風險利率(%)
process_orders_on_closebool在當根 K 線收盤而非次根開盤時成交訂單
calc_on_order_fillsbool每筆訂單成交後重新執行腳本
close_entries_ruleCloseEntriesRuleFifoAny

績效指標

PerformanceMetrics 分三種維度計算:所有交易(performance_all)、僅多頭(performance_long)、僅空頭(performance_short)。

以下欄位僅在 performance_all 中有意義 — 它們基於整體權益曲線,無法按方向拆分。在 performance_longperformance_short 中這些欄位固定為 0.0None

  • max_drawdown / max_drawdown_percent
  • max_runup / max_runup_percent
  • buy_hold_return / buy_hold_return_percent
  • sharpe_ratio / sortino_ratio
  • num_even_trades

損益

欄位說明
net_profit以帳戶貨幣計的淨利潤
net_profit_percent淨利潤佔初始資金的百分比
gross_profit獲利交易的利潤總和
gross_profit_percent總利潤百分比
gross_loss虧損交易的虧損總和(正數)
gross_loss_percent總虧損百分比
profit_factor總利潤 ÷ 總虧損(無虧損交易時為 None
buy_hold_return買入持有的收益(帳戶貨幣,僅 performance_all
buy_hold_return_percent買入持有收益百分比(僅 performance_all

回撤與上升

僅在 performance_all 中有值,在 performance_long / performance_short 中始終為 0.0

欄位說明
max_drawdown最大權益回撤(帳戶貨幣)
max_drawdown_percent最大回撤百分比
max_runup最大權益上升(帳戶貨幣)
max_runup_percent最大上升百分比

風險調整收益

僅在 performance_all 中有值,在 performance_long / performance_short 中始終為 None

欄位說明
sharpe_ratio年化 Sharpe 比率(資料不足時為 None
sortino_ratio年化 Sortino 比率(資料不足時為 None

交易統計

欄位說明
total_closed_trades已完成的交易總數
total_open_trades回測結束時未平倉的交易數
num_winning_trades獲利(profit > 0)的交易數
num_losing_trades虧損(profit < 0)的交易數
num_even_trades平本(profit == 0)的交易數(僅 performance_all,其餘始終為 0
percent_profitable獲利交易數 ÷ 總交易數 × 100(無已平倉交易時為 None

平均交易

欄位說明
avg_trade每筆交易的平均損益
avg_trade_percent每筆交易的平均損益百分比
avg_winning_trade獲利交易的平均利潤
avg_winning_trade_percent獲利交易平均利潤百分比
avg_losing_trade虧損交易的平均虧損
avg_losing_trade_percent虧損交易平均虧損百分比
ratio_avg_win_loss平均獲利 ÷ 平均虧損(無虧損交易時為 None
largest_winning_trade單筆最大利潤
largest_winning_trade_percent單筆最大利潤百分比
largest_losing_trade單筆最大虧損
largest_losing_trade_percent單筆最大虧損百分比

持倉時間

欄位說明
avg_bars_in_trades所有交易的平均持倉 K 線數
avg_bars_in_winning_trades獲利交易的平均持倉 K 線數
avg_bars_in_losing_trades虧損交易的平均持倉 K 線數

其他

欄位說明
commission_paid已支付的手續費總額
max_contracts_held同時持倉的最大合約/股數
margin_calls觸發的追繳保證金次數

交易歷史

已平倉交易

report.closed_trades 中每條 ClosedTradeReport 對應一筆已完成的交易:

欄位型別說明
trade_numusize交易編號(從 0 開始)
entry_idString入場訂單 ID(如 "Long Entry"
entry_commentString入場訂單備註
entry_sideDirectionLongShort
entry_pricef64入場成交價
entry_barusize入場成交的 K 線索引
entry_timei64入場時間戳(Unix 毫秒)
exit_idString出場訂單 ID
exit_commentString出場訂單備註
exit_pricef64出場成交價
exit_barusize出場成交的 K 線索引
exit_timei64出場時間戳(Unix 毫秒)
quantityf64交易數量(合約/股數)
profitf64已實現損益(扣除手續費)
profit_percentf64已實現損益百分比(相對入場價值)
cumulative_profitf64累計損益(含本筆交易)
cumulative_profit_percentf64累計損益百分比(相對初始資金)
max_runupf64最大順向波動(帳戶貨幣)
max_runup_percentf64最大順向波動百分比
max_drawdownf64最大逆向波動(帳戶貨幣)
max_drawdown_percentf64最大逆向波動百分比
commissionf64本筆交易的手續費總額(入場 + 出場)

未平倉交易

report.open_trades 中每條 OpenTradeReport 對應一個未結倉位:

欄位型別說明
trade_numusize交易編號(從 0 開始)
entry_idString入場訂單 ID
entry_commentString入場訂單備註
entry_sideDirectionLongShort
entry_pricef64入場成交價
entry_barusize入場成交的 K 線索引
entry_timei64入場時間戳(Unix 毫秒)
quantityf64持倉數量(合約/股數)
profitf64目前浮動損益(扣除入場手續費)
profit_percentf64目前浮動損益百分比
max_runupf64迄今最大浮動獲利
max_runup_percentf64迄今最大浮動獲利百分比
max_drawdownf64迄今最大浮動虧損
max_drawdown_percentf64迄今最大浮動虧損百分比
commissionf64已支付的入場手續費

權益曲線

三組 Vec<f64> 均以 K 線為索引(索引 0 = 第一根 K 線):

欄位說明
equity_curve每根 K 線收盤時的帳戶權益
drawdown_curve每根 K 線相對峰值權益的回撤(peak - equity,始終 ≥ 0)
buy_hold_curve每根 K 線的買入持有假設權益
rust
// 找到最大回撤發生的 K 線
if let Some((bar, dd)) = report.drawdown_curve
    .iter()
    .enumerate()
    .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
{
    println!("最大回撤發生在第 {} 根 K 線:{:.2}", bar, dd);
}

日報酬率

report.daily_returns 提供用於 Sharpe/Sortino 計算的逐日報酬序列:

rust
pub struct DailyReturnReport {
    pub date: String,           // "YYYY-MM-DD"
    pub return_percent: f64,    // 當日淨損益百分比
}

交易時間範圍

report.trading_range 在至少有一筆已平倉交易時為 Some,涵蓋從首次入場到最後一筆平倉的時間段:

rust
pub struct TradingRangeReport {
    pub start_time: i64,  // Unix 毫秒
    pub end_time: i64,    // Unix 毫秒
}

完整範例

rust
use navi_vm::{Candlestick, Instance, TimeFrame};

async fn backtest(candles: Vec<Candlestick>) -> Result<(), navi_vm::Error> {
    let source = r#"
strategy("均線交叉策略",
    initial_capital = 10000,
    commission_type = strategy.commission.percent,
    commission_value = 0.1)

fast = ta.sma(close, 14)
slow = ta.sma(close, 28)

if ta.crossover(fast, slow)
    strategy.entry("Long", strategy.long)
if ta.crossunder(fast, slow)
    strategy.entry("Short", strategy.short)
"#;

    let tf = TimeFrame::days(1);
    let symbol = "NASDAQ:AAPL";
    let instance = Instance::builder(candles, source, tf, symbol)
        .build().await?
        .run_to_end(symbol, tf).await?;

    let report = instance.strategy_report();
    let p = &report.performance_all;

    println!("=== 策略報告 ===");
    println!("淨利潤:       {:.2}({:.2}%)", p.net_profit, p.net_profit_percent);
    println!("最大回撤:     {:.2}({:.2}%)", p.max_drawdown, p.max_drawdown_percent);
    println!("盈虧比:       {:.2}", p.profit_factor.unwrap_or(0.0));
    println!("Sharpe 比率:  {:.2}", p.sharpe_ratio.unwrap_or(0.0));
    println!("總交易次數:   {}", p.total_closed_trades);
    println!("勝率:         {:.1}%", p.percent_profitable.unwrap_or(0.0));
    println!("總手續費:     {:.2}", p.commission_paid);

    println!("\n=== 交易明細 ===");
    for trade in &report.closed_trades {
        println!(
            "#{:3}  {:5}  入場={:.2}  出場={:.2}  損益={:.2}({:.2}%)",
            trade.trade_num,
            if trade.entry_side == navi_vm::Direction::Long { "多頭" } else { "空頭" },
            trade.entry_price,
            trade.exit_price,
            trade.profit,
            trade.profit_percent,
        );
    }

    Ok(())
}

下一步

基於 MIT 許可證發佈。