Expose futures account runtime view

This commit is contained in:
boris
2026-04-23 20:35:32 -07:00
parent 2669350154
commit db4e385308
7 changed files with 191 additions and 9 deletions

View File

@@ -10,6 +10,7 @@ use crate::cost::ChinaAShareCostModel;
use crate::data::{DailyMarketSnapshot, DataSet, IntradayExecutionQuote, PriceBar, PriceField};
use crate::engine::BacktestError;
use crate::events::{FillEvent, OrderEvent, OrderSide, OrderStatus, ProcessEvent};
use crate::futures::FuturesAccountState;
use crate::instrument::Instrument;
use crate::portfolio::PortfolioState;
use crate::scheduler::ScheduleRule;
@@ -133,6 +134,7 @@ pub struct StrategyContext<'a> {
pub decision_index: usize,
pub data: &'a DataSet,
pub portfolio: &'a PortfolioState,
pub futures_account: Option<&'a FuturesAccountState>,
pub open_orders: &'a [OpenOrderView],
pub dynamic_universe: Option<&'a BTreeSet<String>>,
pub subscriptions: &'a BTreeSet<String>,
@@ -393,19 +395,55 @@ impl StrategyContext<'_> {
}
pub fn future_account(&self) -> Option<PortfolioRuntimeView> {
None
self.futures_account.map(|account| {
let starting_cash = account.starting_cash();
let total_value = account.total_value();
let daily_pnl = account.daily_pnl();
let static_base = total_value - daily_pnl;
let unit_net_value = safe_ratio(total_value, starting_cash);
let static_unit_net_value = safe_ratio(static_base, starting_cash);
PortfolioRuntimeView {
account_type: "FUTURE",
starting_cash,
units: 1.0,
cash: account.cash(),
available_cash: account.cash(),
frozen_cash: account.frozen_cash(),
market_value: account.market_value(),
total_value,
portfolio_value: total_value,
total_equity: total_value,
unit_net_value,
static_unit_net_value,
daily_pnl,
daily_returns: safe_ratio(daily_pnl, static_base),
total_returns: safe_ratio(total_value - starting_cash, starting_cash),
transaction_cost: account.transaction_cost(),
trading_pnl: account.trading_pnl(),
position_pnl: account.position_pnl(),
cash_liabilities: 0.0,
management_fee_rate: 0.0,
management_fees: 0.0,
}
})
}
pub fn account_by_type(&self, account_type: &str) -> Option<PortfolioRuntimeView> {
if account_type.eq_ignore_ascii_case("STOCK") {
Some(self.stock_account())
} else if account_type.eq_ignore_ascii_case("FUTURE") {
self.future_account()
} else {
None
}
}
pub fn accounts(&self) -> BTreeMap<String, PortfolioRuntimeView> {
BTreeMap::from([("STOCK".to_string(), self.stock_account())])
let mut accounts = BTreeMap::from([("STOCK".to_string(), self.stock_account())]);
if let Some(future_account) = self.future_account() {
accounts.insert("FUTURE".to_string(), future_account);
}
accounts
}
pub fn frozen_cash(&self) -> f64 {
@@ -673,6 +711,14 @@ impl StrategyContext<'_> {
}
}
fn safe_ratio(numerator: f64, denominator: f64) -> f64 {
if denominator.abs() <= f64::EPSILON {
0.0
} else {
numerator / denominator
}
}
#[derive(Debug, Clone, Default)]
pub struct StrategyDecision {
pub rebalance: bool,