Expose account runtime metrics

This commit is contained in:
boris
2026-04-23 20:03:49 -07:00
parent 9f10afddec
commit e0a5d0c945
7 changed files with 367 additions and 19 deletions

View File

@@ -6,8 +6,9 @@ use chrono::{NaiveDate, NaiveDateTime};
use fidc_core::{
BacktestConfig, BacktestEngine, BenchmarkSnapshot, BrokerSimulator, CandidateEligibility,
ChinaAShareCostModel, ChinaEquityRuleHooks, DailyFactorSnapshot, DailyMarketSnapshot, DataSet,
Instrument, IntradayExecutionQuote, OrderIntent, PriceField, ProcessEventKind, ScheduleRule,
ScheduleStage, ScheduleTimeRule, Strategy, StrategyContext, StrategyDecision,
Instrument, IntradayExecutionQuote, OpenOrderView, OrderIntent, OrderSide, OrderStatus,
PortfolioState, PriceField, ProcessEventKind, ScheduleRule, ScheduleStage, ScheduleTimeRule,
Strategy, StrategyContext, StrategyDecision,
};
fn d(year: i32, month: u32, day: u32) -> NaiveDate {
@@ -1226,6 +1227,79 @@ fn strategy_context_exposes_final_order_runtime_view() {
);
}
#[test]
fn strategy_context_exposes_rqalpha_style_account_runtime_view() {
let prev_date = d(2025, 1, 2);
let date = d(2025, 1, 3);
let data = DataSet::from_components(
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
vec![BenchmarkSnapshot {
date,
benchmark: "000300.SH".to_string(),
open: 100.0,
close: 100.0,
prev_close: 99.0,
volume: 1_000_000,
}],
)
.expect("dataset");
let mut portfolio = PortfolioState::new(10_000.0);
portfolio
.position_mut("000001.SZ")
.buy(prev_date, 100, 10.0);
portfolio.begin_trading_day();
portfolio.position_mut("000001.SZ").buy(date, 50, 11.0);
portfolio
.position_mut("000001.SZ")
.sell(40, 12.0)
.expect("sell");
portfolio.position_mut("000001.SZ").record_trade_cost(3.0);
let open_orders = vec![OpenOrderView {
order_id: 7,
symbol: "000002.SZ".to_string(),
side: OrderSide::Buy,
requested_quantity: 100,
filled_quantity: 0,
remaining_quantity: 50,
unfilled_quantity: 50,
status: OrderStatus::Pending,
avg_price: 0.0,
transaction_cost: 0.0,
limit_price: 12.0,
reason: "pending_buy".to_string(),
}];
let subscriptions = BTreeSet::new();
let ctx = StrategyContext {
execution_date: date,
decision_date: date,
decision_index: 0,
data: &data,
portfolio: &portfolio,
open_orders: &open_orders,
dynamic_universe: None,
subscriptions: &subscriptions,
process_events: &[],
active_process_event: None,
active_datetime: None,
order_events: &[],
fills: &[],
};
let account = ctx.account();
assert!((account.starting_cash - 10_000.0).abs() < 1e-6);
assert!((account.frozen_cash - 600.0).abs() < 1e-6);
assert!((account.available_cash - 9_400.0).abs() < 1e-6);
assert!((account.transaction_cost - 3.0).abs() < 1e-6);
assert!((account.daily_pnl - 247.0).abs() < 1e-6);
assert!((account.daily_returns - portfolio.daily_returns()).abs() < 1e-6);
assert!((account.total_returns - portfolio.total_returns()).abs() < 1e-6);
assert!((ctx.available_cash() - account.available_cash).abs() < 1e-6);
}
#[test]
fn engine_rejects_pending_limit_orders_at_market_close() {
let date1 = d(2025, 1, 2);