nautechsystems/nautilus_trader · error

{e}

Error message

{e}

What it means

The Python fee model wrapper calls the user-provided Python object's `get_commission` via pyo3 and converts the result. The inner `map_err(|e| anyhow!("{e}"))` surfaces any pyo3 conversion/call error (Python exception or failed result extraction) with just the exception's text as this error message.

Source

Thrown at crates/execution/src/python/fee.rs:193

impl FeeModel for PythonFeeModel {
    fn get_commission(
        &self,
        order: &OrderAny,
        fill_quantity: Quantity,
        fill_px: Price,
        instrument: &InstrumentAny,
    ) -> anyhow::Result<Money> {
        Python::attach(|py| -> anyhow::Result<Money> {
            let order = order_any_to_pyobject(py, order.clone())?;
            let instrument = instrument_any_to_pyobject(py, instrument.clone())?;
            self.obj
                .bind(py)
                .call_method1(
                    "get_commission",
                    (order, fill_quantity, fill_px, instrument),
                )?
                .extract()
                .map_err(|e| anyhow::anyhow!("{e}"))
        })
        .map_err(|e| anyhow::anyhow!("Python FeeModel.get_commission failed: {e}"))
    }

    fn get_commission_with_context(
        &self,
        order: &OrderAny,
        fill_quantity: Quantity,
        fill_px: Price,
        instrument: &InstrumentAny,
        underlying_px: Option<Price>,
    ) -> anyhow::Result<Money> {
        Python::attach(|py| -> anyhow::Result<Money> {
            let obj = self.obj.bind(py);
            if !has_method_override_before_base(py, obj, "get_commission_with_context")? {
                let order = order_any_to_pyobject(py, order.clone())?;
                let instrument = instrument_any_to_pyobject(py, instrument.clone())?;
                return obj

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the wrapped `{e}` text — it is the original Python exception — and fix the Python fee model accordingly.
  2. Ensure the Python `get_commission(self, order, fill_quantity, fill_px, instrument)` signature matches exactly and returns a Money-compatible value.
  3. Test the Python model standalone with representative order/instrument objects to reproduce the exception.

Example fix

# before: wrong signature/return
def get_commission(self, order, qty, px):
    return None
# after
def get_commission(self, order, fill_quantity, fill_px, instrument):
    return Money(fill_quantity * fill_px * self.rate, instrument.quote_currency)
Defensive patterns

Strategy: try-catch

Validate before calling

import inspect
sig = inspect.signature(fee_model.get_commission)
assert list(sig.parameters) == ['self', 'order', 'fill_quantity', 'fill_px', 'instrument']

Type guard

def is_valid_fee_model(obj) -> bool:
    return callable(getattr(obj, 'get_commission', None)) and \
           len(inspect.signature(obj.get_commission).parameters) == 4

Try / catch

try:
    fee = fee_model.get_commission(order, qty, px, instrument)
except Exception as e:
    logger.exception("FeeModel.get_commission failed: %s", e)
    fee = None

Prevention

When it happens

Trigger: Calling `get_commission` on a Python FeeModel whose `get_commission` raises a Python exception, returns an object not extractable as the expected type, or accepts different arguments than `(order, fill_quantity, fill_px, instrument)`.

Common situations: Custom Python fee model with a wrong method signature; returning `None` or a non-Money value; exception inside user Python code (e.g. KeyError on instrument dict); passing objects not convertible between Rust/Python (version/type mismatch of bindings).

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/36a601349fb8aa67. Report an issue: GitHub.