增加通用期货策略动作并修正组合净值
This commit is contained in:
@@ -9,11 +9,12 @@ use fidc_core::{
|
||||
BenchmarkSnapshot, BrokerSimulator, CandidateEligibility, ChinaAShareCostModel,
|
||||
ChinaEquityRuleHooks, DailyFactorSnapshot, DailyMarketSnapshot, DataSet, ExecutionQuoteRequest,
|
||||
FuturesAccountState, FuturesCommissionType, FuturesContractSpec, FuturesDirection,
|
||||
FuturesOrderIntent, FuturesTradingParameter, FuturesValidationConfig, Instrument,
|
||||
IntradayExecutionQuote, IntradayOrderBookDepthLevel, MatchingType, OpenOrderView, OrderIntent,
|
||||
OrderSide, OrderStatus, PlatformExprStrategy, PlatformExprStrategyConfig, PortfolioState,
|
||||
PriceField, ProcessEvent, ProcessEventBus, ProcessEventKind, ScheduleRule, ScheduleStage,
|
||||
ScheduleTimeRule, Strategy, StrategyContext, StrategyDecision,
|
||||
FuturesOrderIntent, FuturesPositionEffect, FuturesTradingParameter, FuturesValidationConfig,
|
||||
Instrument, IntradayExecutionQuote, IntradayOrderBookDepthLevel, MatchingType, OpenOrderView,
|
||||
OrderIntent, OrderSide, OrderStatus, PlatformExprStrategy, PlatformExprStrategyConfig,
|
||||
PlatformTradeAction, PortfolioState, PriceField, ProcessEvent, ProcessEventBus,
|
||||
ProcessEventKind, ScheduleRule, ScheduleStage, ScheduleTimeRule, Strategy, StrategyContext,
|
||||
StrategyDecision,
|
||||
};
|
||||
|
||||
fn d(year: i32, month: u32, day: u32) -> NaiveDate {
|
||||
@@ -1475,6 +1476,73 @@ fn engine_executes_futures_order_intents_against_future_account() {
|
||||
assert!((futures_account.cash() - 355_988.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_runtime_actions_execute_generic_futures_open_and_close() {
|
||||
let mut cfg = PlatformExprStrategyConfig::generic();
|
||||
cfg.signal_symbol = "000001.SZ".to_string();
|
||||
cfg.benchmark_symbol = "000300.SH".to_string();
|
||||
cfg.rotation_enabled = false;
|
||||
cfg.benchmark_short_ma_days = 1;
|
||||
cfg.benchmark_long_ma_days = 1;
|
||||
cfg.explicit_actions = vec![
|
||||
PlatformTradeAction::Futures {
|
||||
symbol: "IF2501".to_string(),
|
||||
direction: FuturesDirection::Long,
|
||||
effect: FuturesPositionEffect::Open,
|
||||
quantity_expr: "1".to_string(),
|
||||
limit_price_expr: None,
|
||||
transaction_cost_expr: None,
|
||||
when_expr: Some("decision_date == \"2025-01-02\"".to_string()),
|
||||
reason: "generic futures open".to_string(),
|
||||
},
|
||||
PlatformTradeAction::Futures {
|
||||
symbol: "IF2501".to_string(),
|
||||
direction: FuturesDirection::Long,
|
||||
effect: FuturesPositionEffect::Close,
|
||||
quantity_expr: "1".to_string(),
|
||||
limit_price_expr: None,
|
||||
transaction_cost_expr: None,
|
||||
when_expr: Some("decision_date == \"2025-01-03\"".to_string()),
|
||||
reason: "generic futures close".to_string(),
|
||||
},
|
||||
];
|
||||
let broker = BrokerSimulator::new_with_execution_price(
|
||||
ChinaAShareCostModel::default(),
|
||||
ChinaEquityRuleHooks::default(),
|
||||
PriceField::Open,
|
||||
);
|
||||
let mut engine = BacktestEngine::new(
|
||||
two_day_futures_data(),
|
||||
PlatformExprStrategy::new(cfg),
|
||||
broker,
|
||||
BacktestConfig {
|
||||
initial_cash: 100_000.0,
|
||||
benchmark_code: "000300.SH".to_string(),
|
||||
start_date: Some(d(2025, 1, 2)),
|
||||
end_date: Some(d(2025, 1, 3)),
|
||||
decision_lag_trading_days: 0,
|
||||
execution_price_field: PriceField::Open,
|
||||
},
|
||||
)
|
||||
.with_futures_initial_cash(500_000.0);
|
||||
|
||||
let result = engine.run().expect("generic futures actions execute");
|
||||
|
||||
let futures_fills = result
|
||||
.fills
|
||||
.iter()
|
||||
.filter(|fill| fill.symbol == "IF2501")
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(futures_fills.len(), 2);
|
||||
assert!((futures_fills[0].price - 4000.0).abs() < 1e-12);
|
||||
assert!((futures_fills[0].commission - 2.5).abs() < 1e-12);
|
||||
assert!((futures_fills[1].price - 3988.0).abs() < 1e-12);
|
||||
assert!((futures_fills[1].commission - 2.0).abs() < 1e-12);
|
||||
let futures_account = engine.futures_account().expect("future account");
|
||||
assert!(futures_account.positions().is_empty());
|
||||
assert!((futures_account.total_cash() - 496_395.5).abs() < 1e-12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn engine_settles_configured_futures_expiration_at_settlement() {
|
||||
let date = d(2025, 1, 2);
|
||||
@@ -1550,7 +1618,9 @@ fn engine_aggregates_futures_account_into_nav_and_metrics() {
|
||||
|
||||
assert_eq!(result.metrics.initial_cash, 600_000.0);
|
||||
assert!((result.equity_curve[0].total_equity - 599_988.0).abs() < 1e-6);
|
||||
assert!((result.equity_curve[0].unit_nav - 0.99998).abs() < 1e-12);
|
||||
assert!((result.metrics.total_assets - 599_988.0).abs() < 1e-6);
|
||||
assert!((result.metrics.total_return + 0.00002).abs() < 1e-12);
|
||||
assert_eq!(result.analyzer_report().trades.len(), result.fills.len());
|
||||
assert_eq!(result.analyzer_report().monthly_returns.len(), 1);
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user