Emit futures order process events
This commit is contained in:
@@ -1538,6 +1538,7 @@ where
|
|||||||
.position_events
|
.position_events
|
||||||
.extend(report.position_events.clone());
|
.extend(report.position_events.clone());
|
||||||
result.account_events.extend(report.account_events.clone());
|
result.account_events.extend(report.account_events.clone());
|
||||||
|
result.process_events.extend(report.process_events.clone());
|
||||||
report
|
report
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2284,6 +2285,7 @@ fn merge_futures_report(target: &mut BrokerExecutionReport, incoming: FuturesExe
|
|||||||
target.fill_events.extend(incoming.fill_events);
|
target.fill_events.extend(incoming.fill_events);
|
||||||
target.position_events.extend(incoming.position_events);
|
target.position_events.extend(incoming.position_events);
|
||||||
target.account_events.extend(incoming.account_events);
|
target.account_events.extend(incoming.account_events);
|
||||||
|
target.process_events.extend(incoming.process_events);
|
||||||
target.diagnostics.extend(incoming.diagnostics);
|
target.diagnostics.extend(incoming.diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
use crate::events::{AccountEvent, FillEvent, OrderEvent, OrderSide, OrderStatus, PositionEvent};
|
use crate::events::{
|
||||||
|
AccountEvent, FillEvent, OrderEvent, OrderSide, OrderStatus, PositionEvent, ProcessEvent,
|
||||||
|
ProcessEventKind,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum FuturesDirection {
|
pub enum FuturesDirection {
|
||||||
@@ -137,6 +140,7 @@ pub struct FuturesExecutionReport {
|
|||||||
pub fill_events: Vec<FillEvent>,
|
pub fill_events: Vec<FillEvent>,
|
||||||
pub position_events: Vec<PositionEvent>,
|
pub position_events: Vec<PositionEvent>,
|
||||||
pub account_events: Vec<AccountEvent>,
|
pub account_events: Vec<AccountEvent>,
|
||||||
|
pub process_events: Vec<ProcessEvent>,
|
||||||
pub diagnostics: Vec<String>,
|
pub diagnostics: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,8 +518,32 @@ impl FuturesAccountState {
|
|||||||
) -> FuturesExecutionReport {
|
) -> FuturesExecutionReport {
|
||||||
let mut report = FuturesExecutionReport::default();
|
let mut report = FuturesExecutionReport::default();
|
||||||
let side = intent.side();
|
let side = intent.side();
|
||||||
|
push_futures_process_event(
|
||||||
|
&mut report,
|
||||||
|
date,
|
||||||
|
ProcessEventKind::OrderPendingNew,
|
||||||
|
order_id,
|
||||||
|
&intent.symbol,
|
||||||
|
side,
|
||||||
|
format!(
|
||||||
|
"requested_quantity={} direction={} effect={} reason={}",
|
||||||
|
intent.quantity,
|
||||||
|
intent.direction.as_str(),
|
||||||
|
intent.effect.as_str(),
|
||||||
|
intent.reason
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if intent.quantity == 0 || !intent.price.is_finite() || intent.price <= 0.0 {
|
if intent.quantity == 0 || !intent.price.is_finite() || intent.price <= 0.0 {
|
||||||
|
push_futures_process_event(
|
||||||
|
&mut report,
|
||||||
|
date,
|
||||||
|
ProcessEventKind::OrderCreationReject,
|
||||||
|
order_id,
|
||||||
|
&intent.symbol,
|
||||||
|
side,
|
||||||
|
"invalid futures order",
|
||||||
|
);
|
||||||
report.order_events.push(OrderEvent {
|
report.order_events.push(OrderEvent {
|
||||||
date,
|
date,
|
||||||
order_id,
|
order_id,
|
||||||
@@ -611,6 +639,24 @@ impl FuturesAccountState {
|
|||||||
intent.effect.as_str()
|
intent.effect.as_str()
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
push_futures_process_event(
|
||||||
|
&mut report,
|
||||||
|
date,
|
||||||
|
ProcessEventKind::OrderCreationPass,
|
||||||
|
order_id,
|
||||||
|
&intent.symbol,
|
||||||
|
side,
|
||||||
|
"futures order passed account checks",
|
||||||
|
);
|
||||||
|
push_futures_process_event(
|
||||||
|
&mut report,
|
||||||
|
date,
|
||||||
|
ProcessEventKind::Trade,
|
||||||
|
order_id,
|
||||||
|
&intent.symbol,
|
||||||
|
side,
|
||||||
|
format!("filled_quantity={} price={}", intent.quantity, intent.price),
|
||||||
|
);
|
||||||
report.position_events.push(PositionEvent {
|
report.position_events.push(PositionEvent {
|
||||||
date,
|
date,
|
||||||
symbol: intent.symbol.clone(),
|
symbol: intent.symbol.clone(),
|
||||||
@@ -658,6 +704,15 @@ impl FuturesAccountState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
|
push_futures_process_event(
|
||||||
|
&mut report,
|
||||||
|
date,
|
||||||
|
ProcessEventKind::OrderCreationReject,
|
||||||
|
order_id,
|
||||||
|
&intent.symbol,
|
||||||
|
side,
|
||||||
|
reason.clone(),
|
||||||
|
);
|
||||||
report.order_events.push(OrderEvent {
|
report.order_events.push(OrderEvent {
|
||||||
date,
|
date,
|
||||||
order_id,
|
order_id,
|
||||||
@@ -725,6 +780,7 @@ impl FuturesAccountState {
|
|||||||
combined.fill_events.extend(report.fill_events);
|
combined.fill_events.extend(report.fill_events);
|
||||||
combined.position_events.extend(report.position_events);
|
combined.position_events.extend(report.position_events);
|
||||||
combined.account_events.extend(report.account_events);
|
combined.account_events.extend(report.account_events);
|
||||||
|
combined.process_events.extend(report.process_events);
|
||||||
combined.diagnostics.extend(report.diagnostics);
|
combined.diagnostics.extend(report.diagnostics);
|
||||||
}
|
}
|
||||||
combined.diagnostics.push(format!(
|
combined.diagnostics.push(format!(
|
||||||
@@ -759,3 +815,22 @@ impl FuturesAccountState {
|
|||||||
cash_delta
|
cash_delta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_futures_process_event(
|
||||||
|
report: &mut FuturesExecutionReport,
|
||||||
|
date: NaiveDate,
|
||||||
|
kind: ProcessEventKind,
|
||||||
|
order_id: Option<u64>,
|
||||||
|
symbol: &str,
|
||||||
|
side: OrderSide,
|
||||||
|
detail: impl Into<String>,
|
||||||
|
) {
|
||||||
|
report.process_events.push(ProcessEvent {
|
||||||
|
date,
|
||||||
|
kind,
|
||||||
|
order_id,
|
||||||
|
symbol: Some(symbol.to_string()),
|
||||||
|
side: Some(side),
|
||||||
|
detail: detail.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -971,6 +971,9 @@ fn engine_executes_futures_order_intents_against_future_account() {
|
|||||||
assert!(result.fills.iter().any(|fill| {
|
assert!(result.fills.iter().any(|fill| {
|
||||||
fill.symbol == "IF2501" && fill.quantity == 1 && (fill.commission - 12.0).abs() < 1e-6
|
fill.symbol == "IF2501" && fill.quantity == 1 && (fill.commission - 12.0).abs() < 1e-6
|
||||||
}));
|
}));
|
||||||
|
assert!(result.process_events.iter().any(|event| {
|
||||||
|
event.symbol.as_deref() == Some("IF2501") && event.kind == ProcessEventKind::Trade
|
||||||
|
}));
|
||||||
let futures_account = engine.futures_account().expect("future account");
|
let futures_account = engine.futures_account().expect("future account");
|
||||||
let position = futures_account
|
let position = futures_account
|
||||||
.position("IF2501", FuturesDirection::Long)
|
.position("IF2501", FuturesDirection::Long)
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ fn futures_order_execution_splits_close_between_old_and_today_quantity() {
|
|||||||
|
|
||||||
assert_eq!(report.order_events.len(), 1);
|
assert_eq!(report.order_events.len(), 1);
|
||||||
assert_eq!(report.order_events[0].status, OrderStatus::Filled);
|
assert_eq!(report.order_events[0].status, OrderStatus::Filled);
|
||||||
|
assert!(
|
||||||
|
report
|
||||||
|
.process_events
|
||||||
|
.iter()
|
||||||
|
.any(|event| event.kind == fidc_core::ProcessEventKind::Trade)
|
||||||
|
);
|
||||||
assert_eq!(report.fill_events[0].quantity, 4);
|
assert_eq!(report.fill_events[0].quantity, 4);
|
||||||
assert!((report.fill_events[0].net_cash_flow - 19_196.0).abs() < 1e-6);
|
assert!((report.fill_events[0].net_cash_flow - 19_196.0).abs() < 1e-6);
|
||||||
let position = account
|
let position = account
|
||||||
@@ -120,6 +126,12 @@ fn futures_close_today_rejects_when_today_quantity_is_insufficient() {
|
|||||||
|
|
||||||
assert_eq!(report.order_events.len(), 1);
|
assert_eq!(report.order_events.len(), 1);
|
||||||
assert_eq!(report.order_events[0].status, OrderStatus::Rejected);
|
assert_eq!(report.order_events[0].status, OrderStatus::Rejected);
|
||||||
|
assert!(
|
||||||
|
report
|
||||||
|
.process_events
|
||||||
|
.iter()
|
||||||
|
.any(|event| event.kind == fidc_core::ProcessEventKind::OrderCreationReject)
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
report.order_events[0]
|
report.order_events[0]
|
||||||
.reason
|
.reason
|
||||||
@@ -174,6 +186,14 @@ fn futures_expiration_settlement_closes_all_contract_directions() {
|
|||||||
|
|
||||||
assert_eq!(report.order_events.len(), 2);
|
assert_eq!(report.order_events.len(), 2);
|
||||||
assert_eq!(report.fill_events.len(), 2);
|
assert_eq!(report.fill_events.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
report
|
||||||
|
.process_events
|
||||||
|
.iter()
|
||||||
|
.filter(|event| event.kind == fidc_core::ProcessEventKind::Trade)
|
||||||
|
.count(),
|
||||||
|
2
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
report
|
report
|
||||||
.order_events
|
.order_events
|
||||||
|
|||||||
Reference in New Issue
Block a user