use nautilus_core::{UnixNanos, python::to_pyvalue_err};
use nautilus_model::{
enums::{OrderSide, OrderType},
identifiers::{AccountId, ClientOrderId, InstrumentId, PositionId, TradeId, VenueOrderId},
python::instruments::pyobject_to_instrument_any,
reports::ExecutionMassStatus,
types::{Price, Quantity},
};
use pyo3::{
IntoPyObjectExt,
prelude::*,
types::{PyDict, PyTuple},
};
use rust_decimal::Decimal;
use crate::reconciliation::{
calculate_reconciliation_price, create_inferred_reconciliation_trade_id,
create_position_reconciliation_venue_order_id, process_mass_status_for_reconciliation,
};
#[pyfunction(name = "adjust_fills_for_partial_window")]
#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.execution")]
#[pyo3(signature = (mass_status, instrument, tolerance=None))]
pub fn py_adjust_fills_for_partial_window(
py: Python<'_>,
mass_status: &Bound<'_, PyAny>,
instrument: Py<PyAny>,
tolerance: Option<String>,
) -> PyResult<Py<PyTuple>> {
let instrument_any = pyobject_to_instrument_any(py, instrument)?;
let mass_status_obj: ExecutionMassStatus = mass_status.extract()?;
let tol = tolerance
.map(|s| Decimal::from_str_exact(&s).map_err(to_pyvalue_err))
.transpose()?;
let result = process_mass_status_for_reconciliation(&mass_status_obj, &instrument_any, tol)
.map_err(to_pyvalue_err)?;
let orders_dict = PyDict::new(py);
for (id, order) in result.orders {
orders_dict.set_item(id.to_string(), order.into_py_any(py)?)?;
}
let fills_dict = PyDict::new(py);
for (id, fills) in result.fills {
let fills_list: Result<Vec<_>, _> = fills.into_iter().map(|f| f.into_py_any(py)).collect();
fills_dict.set_item(id.to_string(), fills_list?)?;
}
Ok(PyTuple::new(
py,
[orders_dict.into_py_any(py)?, fills_dict.into_py_any(py)?],
)?
.into())
}
#[pyfunction(name = "calculate_reconciliation_price")]
#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.execution")]
#[pyo3(signature = (current_position_qty, current_position_avg_px, target_position_qty, target_position_avg_px))]
pub fn py_calculate_reconciliation_price(
current_position_qty: Decimal,
current_position_avg_px: Option<Decimal>,
target_position_qty: Decimal,
target_position_avg_px: Option<Decimal>,
) -> Option<Decimal> {
calculate_reconciliation_price(
current_position_qty,
current_position_avg_px,
target_position_qty,
target_position_avg_px,
)
}
#[pyfunction(name = "create_inferred_reconciliation_trade_id")]
#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.execution")]
#[pyo3(signature = (account_id, instrument_id, client_order_id, venue_order_id, order_side, order_type, filled_qty, last_qty, last_px, position_id, ts_last))]
#[expect(clippy::too_many_arguments)]
pub fn py_create_inferred_reconciliation_trade_id(
account_id: AccountId,
instrument_id: InstrumentId,
client_order_id: ClientOrderId,
venue_order_id: Option<VenueOrderId>,
order_side: OrderSide,
order_type: OrderType,
filled_qty: Quantity,
last_qty: Quantity,
last_px: Price,
position_id: PositionId,
ts_last: u64,
) -> TradeId {
create_inferred_reconciliation_trade_id(
account_id,
instrument_id,
client_order_id,
venue_order_id,
order_side,
order_type,
filled_qty,
last_qty,
last_px,
position_id,
UnixNanos::from(ts_last),
)
}
#[pyfunction(name = "create_position_reconciliation_venue_order_id")]
#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.execution")]
#[pyo3(signature = (account_id, instrument_id, order_side, order_type, quantity, price=None, venue_position_id=None, ts_last=0, tag=None))]
#[expect(clippy::needless_pass_by_value, clippy::too_many_arguments)]
pub fn py_create_position_reconciliation_venue_order_id(
account_id: AccountId,
instrument_id: InstrumentId,
order_side: OrderSide,
order_type: OrderType,
quantity: Quantity,
price: Option<Price>,
venue_position_id: Option<PositionId>,
ts_last: u64,
tag: Option<String>,
) -> VenueOrderId {
create_position_reconciliation_venue_order_id(
account_id,
instrument_id,
order_side,
order_type,
quantity,
price,
venue_position_id,
tag.as_deref(),
UnixNanos::from(ts_last),
)
}