Add remaining RQAlpha extension helpers

This commit is contained in:
boris
2026-04-23 21:44:42 -07:00
parent beb9c7a7ae
commit ed8ac385e4
7 changed files with 689 additions and 21 deletions

View File

@@ -4,12 +4,13 @@ use std::rc::Rc;
use chrono::{NaiveDate, NaiveDateTime};
use fidc_core::{
BacktestConfig, BacktestEngine, BenchmarkSnapshot, BrokerSimulator, CandidateEligibility,
ChinaAShareCostModel, ChinaEquityRuleHooks, DailyFactorSnapshot, DailyMarketSnapshot, DataSet,
FuturesAccountState, FuturesCommissionType, FuturesContractSpec, FuturesDirection,
FuturesOrderIntent, FuturesTradingParameter, Instrument, IntradayExecutionQuote, OpenOrderView,
OrderIntent, OrderSide, OrderStatus, PortfolioState, PriceField, ProcessEventKind,
ScheduleRule, ScheduleStage, ScheduleTimeRule, Strategy, StrategyContext, StrategyDecision,
BacktestConfig, BacktestEngine, BacktestProcessMod, BenchmarkSnapshot, BrokerSimulator,
CandidateEligibility, ChinaAShareCostModel, ChinaEquityRuleHooks, DailyFactorSnapshot,
DailyMarketSnapshot, DataSet, FuturesAccountState, FuturesCommissionType, FuturesContractSpec,
FuturesDirection, FuturesOrderIntent, FuturesTradingParameter, Instrument,
IntradayExecutionQuote, OpenOrderView, OrderIntent, OrderSide, OrderStatus, PortfolioState,
PriceField, ProcessEvent, ProcessEventBus, ProcessEventKind, ScheduleRule, ScheduleStage,
ScheduleTimeRule, Strategy, StrategyContext, StrategyDecision,
};
fn d(year: i32, month: u32, day: u32) -> NaiveDate {
@@ -1335,11 +1336,16 @@ fn engine_aggregates_futures_account_into_nav_and_metrics() {
assert!((result.equity_curve[0].total_equity - 599_988.0).abs() < 1e-6);
assert!((result.metrics.total_assets - 599_988.0).abs() < 1e-6);
assert_eq!(result.analyzer_report().trades.len(), result.fills.len());
assert_eq!(result.analyzer_report().monthly_returns.len(), 1);
assert_eq!(
result.analyzer_report().risk_summary.total_return,
result.metrics.total_return
);
assert!(
result
.analyzer_report_json()
.expect("report json")
.contains("\"trades\"")
.contains("\"monthly_returns\"")
);
}
@@ -2813,6 +2819,61 @@ fn engine_dispatches_process_events_to_external_bus_listeners() {
);
}
struct AnyEventCountingMod {
sink: Rc<RefCell<Vec<String>>>,
}
impl BacktestProcessMod for AnyEventCountingMod {
fn name(&self) -> &str {
"any-event-counter"
}
fn install(&mut self, bus: &mut ProcessEventBus) {
let sink = self.sink.clone();
bus.add_any_listener(move |event: &ProcessEvent| {
sink.borrow_mut()
.push(format!("{:?}:{}", event.kind, event.detail));
});
}
}
#[test]
fn engine_installs_process_mods_on_event_bus() {
let date = d(2025, 1, 2);
let data = single_day_anchor_data(date);
let broker = BrokerSimulator::new_with_execution_price(
ChinaAShareCostModel::default(),
ChinaEquityRuleHooks::default(),
PriceField::DayOpen,
);
let mut engine = BacktestEngine::new(
data,
HookProbeStrategy {
log: Rc::new(RefCell::new(Vec::new())),
},
broker,
BacktestConfig {
initial_cash: 100_000.0,
benchmark_code: "000300.SH".to_string(),
start_date: Some(date),
end_date: Some(date),
decision_lag_trading_days: 0,
execution_price_field: PriceField::DayOpen,
},
);
let sink = Rc::new(RefCell::new(Vec::new()));
let mut module = AnyEventCountingMod { sink: sink.clone() };
engine.install_process_mod(&mut module);
engine.run().expect("backtest run");
assert!(
sink.borrow()
.iter()
.any(|item| { item.starts_with("PreBeforeTrading:before_trading:pre") })
);
}
#[test]
fn engine_applies_dynamic_universe_and_subscription_directives() {
let dates = [d(2025, 1, 2), d(2025, 1, 3), d(2025, 1, 6)];