Skip to content

Refactor execution message handling in Rust #2291

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 9 commits into from
Feb 9, 2025

Conversation

filipmacek
Copy link
Member

@filipmacek filipmacek commented Feb 7, 2025

Pull Request

  • moved models and matching_engine from backtest to execution crate
  • consolidated all order management and execution components into separate modules in execution crate
  • created fully typed and statically defined all OrderMatchingCore handlers: FillMarketOrderHandler, FillLimitOrderHandler and TriggerStopOrderHandler with two variants of implementation for OrderMatchingEngine and OrderEmulator
  • redesigned OrderMatchingCore from closures to these handlers with proper setter functions
  • introduce handler management objects called adapters which take care of hooking up predefined functions in both msgbus and handler objects. They connect all order execution-related components and take care of the lifecycle of handlers functions
  • OrderMatchingEngine now fills resting limit orders from OrderMatchingCore when iterated if the market bid/ask moved
  • OrderEmulator completely refactored with new handler pattern, component cleaner and checked, and state moved from OrderEmulatorState to main struct which simplifies component state change and improves code quality and makes it easier to read

@filipmacek filipmacek added the rust Relating to the Rust core label Feb 7, 2025
@filipmacek filipmacek requested a review from cjdsellers February 7, 2025 07:38
@filipmacek filipmacek self-assigned this Feb 7, 2025
@cjdsellers cjdsellers changed the title Redesign order matching in Rust Refactor execution message handling in Rust Feb 9, 2025
@cjdsellers cjdsellers merged commit 7359b7a into develop Feb 9, 2025
5 checks passed
@cjdsellers cjdsellers deleted the order-matching-refactor-rust branch February 9, 2025 23:01
Copy link
Collaborator

@twitu twitu left a comment

Choose a reason for hiding this comment

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

I'm noticing that using the shared constructs and message handler style traits is not as ergonomic as it could be. There's a lot of cognitive overhead to Rc, RefCell, explicit borrows and wrapper enums that make it hard to understand the core logic.

I believe this is mostly because of the SharedMessageHandler is defined. I'll look into how it can be made a little more flexible either a type level or using proc macros. What do you think?

Comment on lines +44 to +48
pub fn new(
clock: Rc<RefCell<dyn Clock>>,
cache: Rc<RefCell<Cache>>,
msgbus: Rc<RefCell<MessageBus>>,
) -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

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

A lot constructs are wrapped by Rc RefCell but it adds to the cognitive load. Perhaps all such constructs can defined a wrapper type alias to make it easier to read.

pub type SharedCache = Rc<RefCell>;


fn initialize_modify_order_handler(emulator: Rc<RefCell<OrderEmulator>>) {
let handler = ModifyOrderHandlerAny::OrderEmulator(emulator.clone());
emulator.borrow_mut().set_modify_order_handler(handler);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again a common pattern emerging from the Rc RefCell shared nature of the constructs. For core constructs, perhaps it's better to define a new wrapper struct and expose AsRef and AsMutRef on them. So that the explicit borrows can be reduced.

struct SharedOrderEmulator(Rc<RefCell<OrderEmulator>>)

impl AsRef<OrderEmulator> for SharedOrderEmulator {
   pub fn ... {
       self.0.borrow()
   }
}

Comment on lines +29 to +45
pub enum FillMarketOrderHandlerAny {
OrderMatchingEngine(Rc<RefCell<OrderMatchingEngine>>),
OrderEmulator(Rc<RefCell<OrderEmulator>>),
}

impl FillMarketOrderHandler for FillMarketOrderHandlerAny {
fn fill_market_order(&mut self, order: &OrderAny) {
match self {
FillMarketOrderHandlerAny::OrderMatchingEngine(engine) => {
engine.borrow_mut().fill_market_order(&mut order.clone());
}
FillMarketOrderHandlerAny::OrderEmulator(emulator) => {
emulator.borrow_mut().fill_market_order(&mut order.clone());
}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Depending on this the FillMarketOrderHandler is consumed it might be better to implement the FillMarketOrderHandler trait for the OrderMatchingEngine and OrderEmulator individually and store it as as Box or Rc that will reduce the number of enums and wrapper types created for this functionality.

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.

3 participants