Skip to content

Improve implementation, validations and testing for Rust instruments #2733

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 23, 2025

Conversation

nicolad
Copy link
Member

@nicolad nicolad commented Jun 22, 2025

Area Item / Sub-task Status Key Facts & Follow-ups
Implementation Port all helper methods from Cython Instrument → Rust (make_price, make_qty, tick helpers, notional math, base-qty math) Trait-default impls complete. New ergonomic wrappers try_make_price / try_make_qty / try_calculate_base_quantity return anyhow::Result, while legacy panicking versions call their try_ cousins.
Expose info metadata in PyO3 ⛔️ Still missing – needs HashMap<String, PyObject> plus getter/setter.
Remove to_dict/from_dict from Rust All serialization now lives exclusively in the Python shim.
Tick-scheme abstraction (Fixed, Crypto0_01, …) Trait-based, enum_dispatch, case-insensitive FromStr.
Negative-price support Validation no longer forbids price < 0; tests added.
Half-even rounding retained, but internal maths replaced by rust_decimal::round_dp_with_strategy for clarity Extensive doc-comments added to explain why half-even is required in trading systems.
Variable naming cleanup (amount, currency, …) Short forms purged.
Centralised validation (validate_instrument_common) Uses ensure! + helper macro check_positive!; covers paired-field, precision and bound checks.
Option/Future-specific domain checks (underlying / strike / expiry order) ⛔️ Deferred to follow-up.
Testing Parameterised examples converted to rstest cases All scalar grids migrated; many new edge cases; names use full words.
Property-based fuzzing (proptest) for price/qty factories 10 000 cases, 0.0001 → 1e8 range.
Validation negative-path coverage 30 + panic / error-return assertions.
PyO3 ⇄ Python round-trip tests ⛔️ Blocked on info exposure.
Misc / Tooling Lint & CI (cargo clippy -D warnings, multi-OS) Clean as of latest push.
Docs (mdBook chapter on TickScheme + inverse/quanto maths) ⛔️ Draft underway; will land after API stabilises.
Helper fns now have try_* fallible variants Downstream crates can opt-in to error handling.

@nicolad nicolad requested a review from cjdsellers June 22, 2025 14:01
@nicolad nicolad force-pushed the 2630 branch 2 times, most recently from c4dff38 to 8012dd1 Compare June 22, 2025 14:10
@nicolad nicolad self-assigned this Jun 22, 2025
@nicolad nicolad added the rust Relating to the Rust core label Jun 22, 2025
@nicolad nicolad marked this pull request as ready for review June 23, 2025 02:04
Copy link
Member

@cjdsellers cjdsellers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the work including the tests @nicolad 🙏

I'll follow up on the comments after merge to establish some more patterns.
We can address the info field specifically on another pass.


/// # Errors
///
/// Returns `anyhow::Error` if the value is not finite or cannot be converted to a `Price`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to qualify the error type in the description here. "Returns an error if ..." is enough.

@@ -556,7 +636,7 @@ pub fn betting() -> BettingInstrument {
);
let selection_id = 50214;
let selection_name = Ustr::from("Kansas City Chiefs");
let selection_handicap = 0.0; // As per betting convention, no handicap
let selection_handicap = 0.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still reasonably informative.

@@ -501,7 +581,7 @@ pub fn option_spread() -> OptionSpread {
Symbol::from("UD:U$: GN 2534559"),
AssetClass::FX,
Some(Ustr::from("XCME")),
Ustr::from("SR3"), // British Pound futures (option on futures)
Ustr::from("SR3"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still reasonably informative.

@@ -464,7 +544,7 @@ pub fn option_contract_appl() -> OptionContract {
InstrumentId::from("AAPL211217C00150000.OPRA"),
Symbol::from("AAPL211217C00150000"),
AssetClass::Equity,
Some(Ustr::from("GMNI")), // Nasdaq GEMX
Some(Ustr::from("GMNI")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still reasonably informative.

#[case(2.345_5, true)]
#[case(0.000_999_999, false)]
#[case(0.000_999_999, true)]
fn quantity_rounding_grid(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good amount of cases here 👌

let step = 10f64.powi(-(price_precision as i32));
Price::new(step, price_precision)
}

macro_rules! check_positive {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This macro isn't necessary.

ensure!(price_precision >= 0, "price_precision negative");
ensure!(size_precision >= 0, "size_precision negative");
check_predicate_true(price_precision >= 0, "price_precision negative")?;
check_predicate_true(size_precision >= 0, "size_precision negative")?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have more specialized correctness functions which could be used for many of these, e.g.
https://github.com/nautechsystems/nautilus_trader/blob/develop/crates/model/src/data/trade.rs#L76

check_positive_quantity(size, stringify!(size))?;

@cjdsellers cjdsellers merged commit 598cdd8 into develop Jun 23, 2025
13 checks passed
@cjdsellers cjdsellers deleted the 2630 branch June 23, 2025 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rust Relating to the Rust core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants