pub struct ServerApp(pub Exchange);Expand description
Transparent newtype around Exchange that carries the
Application trait impl. Exists solely so the impl can live in
melin-server (the wiring crate) without violating the orphan rule —
neither Application (in melin-app) nor Exchange (in
melin-exchange-core) is local to melin-server, but ServerApp is.
The inner field is pub because the server frequently constructs an
Exchange directly (Exchange::with_capacity) and wraps it; making
the wrap explicit at every construction site is
cheaper than introducing a parallel set of constructors here.
Tuple Fields§
§0: ExchangeImplementations§
Methods from Deref<Target = Exchange>§
Sourcepub fn cancel_replace(
&mut self,
symbol: Symbol,
account: AccountId,
order_id: OrderId,
new_price: Price,
new_quantity: Quantity,
reports: &mut Vec<ExecutionReport>,
)
pub fn cancel_replace( &mut self, symbol: Symbol, account: AccountId, order_id: OrderId, new_price: Price, new_quantity: Quantity, reports: &mut Vec<ExecutionReport>, )
Atomically amend a resting limit order’s price and/or quantity.
Validation order (all checks before any mutation):
- Instrument exists
- Order exists on the book (resting limit only — not stops, not market)
- Circuit breaker: halted or price band violation
- Risk limits: max qty, max notional
- Price-would-cross check: reject if new price crosses the spread
- Reservation adjustment: compute new required amount, check balance
If any check fails, the original order remains untouched.
Time priority rules:
- Same price, qty decrease → keep priority
- Same price, qty increase → lose priority
- Price change → lose priority
Sourcepub fn execute(
&mut self,
symbol: Symbol,
order: Order,
reports: &mut Vec<ExecutionReport>,
)
pub fn execute( &mut self, symbol: Symbol, order: Order, reports: &mut Vec<ExecutionReport>, )
Submit an order to the matching engine for the given instrument.
Validates the instrument exists, reserves funds, then executes. On fill, balances are updated. On reject/cancel, reserves are released.
Under feature = "skip-order-exec" the body is short-circuited
to a single Rejected{NoLiquidity} push, used by the server’s
transport-only benchmark build to isolate transport throughput
from matching cost. Same wire shape — bench clients still see
one response per SubmitOrder — but no order book / account
state touched.
Sourcepub fn snapshot_key_hwm(&self) -> Vec<(u64, u64)>
pub fn snapshot_key_hwm(&self) -> Vec<(u64, u64)>
Snapshot per-key request sequence HWMs for serialization.
Sourcepub fn instrument_specs(&self) -> impl Iterator<Item = &InstrumentSpec>
pub fn instrument_specs(&self) -> impl Iterator<Item = &InstrumentSpec>
Iterate over instrument specs (for snapshot serialization).
Sourcepub fn snapshot_order_sides(&self) -> Vec<((AccountId, OrderId), Side)>
pub fn snapshot_order_sides(&self) -> Vec<((AccountId, OrderId), Side)>
Snapshot the order-side map as a Vec for serialization. Only serializes the side; reservation slots are ephemeral and reassigned on restore.
Sourcepub fn snapshot_risk_limits(&self) -> Vec<(Symbol, RiskLimits)>
pub fn snapshot_risk_limits(&self) -> Vec<(Symbol, RiskLimits)>
Snapshot the per-instrument risk limits for serialization.
Sourcepub fn snapshot_circuit_breakers(&self) -> Vec<(Symbol, CircuitBreakerConfig)>
pub fn snapshot_circuit_breakers(&self) -> Vec<(Symbol, CircuitBreakerConfig)>
Snapshot the per-instrument circuit breaker configs for serialization.
Sourcepub fn prefault_seed(&mut self, num_accounts: usize, num_instruments: usize)
pub fn prefault_seed(&mut self, num_accounts: usize, num_instruments: usize)
Pre-allocate collections for a known bulk-seed workload.
Sizes the balance map to num_accounts × num_instruments × 2
(base + quote per instrument per account) so the seed phase
doesn’t hit multi-hundred-ms rehash stalls as the map grows.
Populates the instrument pool with one OrderBook per expected
instrument (indexed by symbol). add_instrument pulls from
this pool instead of allocating fresh, avoiding the 5-11 ms
first-touch + mlock spike during seed (matching thread runs
under MCL_FUTURE so any new allocation faults thousands of
pages at once).
Sourcepub fn set_max_open_orders_per_account(&mut self, max: u32)
pub fn set_max_open_orders_per_account(&mut self, max: u32)
Configure the per-account open-order cap (0 = unlimited). See the
field doc on max_open_orders_per_account for semantics and the
primary/replica determinism constraint.
Sourcepub fn max_open_orders_per_account(&self) -> u32
pub fn max_open_orders_per_account(&self) -> u32
Read back the configured per-account open-order cap. Test/admin only.
Sourcepub fn set_max_orders_per_second(&mut self, rate: u32, burst: u32)
pub fn set_max_orders_per_second(&mut self, rate: u32, burst: u32)
Configure the per-account order-submission rate limit (SEC-04).
Argument semantics (active values, 0 = disabled, etc.) live on
the max_orders_per_second / max_orders_burst field docs above.
Bucket-clearing rule: existing per-account bucket state is
cleared only when transitioning between two active
configurations whose (rate, burst) values differ — the online-
reconfig case where tokens credited at the old rate could over-
credit under the new one. All other transitions preserve buckets:
- Initial activation (previous config was
(0, _)or(_, 0), i.e. limiter was disabled): buckets that exist on the map can only have come from a snapshot restore viarestore_order_buckets, and that is exactly the state we need to preserve to close the SEC-04 divergence window. A fresh engine with no restored buckets is unaffected. - Deactivation (new config is
(0, _)or(_, 0)): the limiter is off, so bucket contents are unobserved. Keeping them is harmless and avoids losing state if the operator later re-enables with the same values. - No-op reapply (values unchanged): obviously preserve.
Determinism: must match across primary and replicas — see the field
docs on max_orders_per_second / max_orders_burst.
Side effect (online-reconfig path only): clearing buckets resets every account to a full burst at next first-touch. Operators should treat online reconfiguration as a rare, audit-logged change — frequent re-tuning is effectively a throttle bypass. Engine library users embedding the matching core should gate the call behind their own auth path.
Sourcepub fn max_orders_per_second(&self) -> (u32, u32)
pub fn max_orders_per_second(&self) -> (u32, u32)
Read back the configured rate limit (rate_per_sec, burst).
Test/admin only.
Sourcepub fn set_current_event_ts_ns(&mut self, now_ns: u64)
pub fn set_current_event_ts_ns(&mut self, now_ns: u64)
Stash the current event’s now_ns so per-event methods (execute,
cancel, …) can read a deterministic clock without each method
taking a now_ns parameter. Called by Application::apply exactly
once per event before dispatch.
Sourcepub fn open_order_count(&self, account: AccountId) -> u32
pub fn open_order_count(&self, account: AccountId) -> u32
Current count of open orders (resting limits + pending stops +
in-flight) for account, across all instruments. Returns 0 if
the account has never traded. Used by proptests and admin queries
to inspect the same counter the SEC-03 cap reads.
Sourcepub fn order_bucket_count(&self) -> usize
pub fn order_bucket_count(&self) -> usize
Live rate-limiter bucket count. Used by the server’s startup path to detect a primary↔replica config mismatch after snapshot restore (non-empty buckets paired with a disabled limiter indicates the operator forgot to wire the rate-limit config) and by tests to assert bucket-eviction behaviour.
Sourcepub fn drain_due_scheduled_tasks(
&mut self,
now_ns: u64,
reports: &mut Vec<ExecutionReport>,
)
pub fn drain_due_scheduled_tasks( &mut self, now_ns: u64, reports: &mut Vec<ExecutionReport>, )
Drain every scheduled task whose fire_ns <= now_ns. Called at the
head of every event the matching stage processes, so time-driven work
runs in lockstep with the journal. Tombstones — tasks that point to
orders that have already been cancelled or filled — are silently
dropped via the find_gtd_expiry lookup.
Sourcepub fn check_request_seq(&mut self, key_hash: u64, request_seq: u64) -> bool
pub fn check_request_seq(&mut self, key_hash: u64, request_seq: u64) -> bool
Check per-key request sequence for idempotency dedup. Returns true if this is a new request (should be processed). Returns false if duplicate (caller should reject with DuplicateRequest). Exempt when key_hash == 0 (internal/seed events with no authenticated key).
Sourcepub fn request_seq_hwm(&self, key_hash: u64) -> u64
pub fn request_seq_hwm(&self, key_hash: u64) -> u64
Current request_seq HWM for key_hash, or 0 if no event has
ever been accepted from that key. Read-only; safe to call from
the matching stage at any point. Used by the QueryRequestSeq
query handler so reconnecting clients can resume their outbound
seq past whatever the engine has already seen.
Sourcepub fn instrument_count(&self) -> usize
pub fn instrument_count(&self) -> usize
Number of active instruments (for diagnostics).
Sourcepub fn set_risk_limits(&mut self, symbol: Symbol, limits: RiskLimits)
pub fn set_risk_limits(&mut self, symbol: Symbol, limits: RiskLimits)
Set fat finger risk limits for an instrument. No-op if the instrument doesn’t exist (matches previous behavior).
Sourcepub fn set_circuit_breaker(
&mut self,
symbol: Symbol,
config: CircuitBreakerConfig,
)
pub fn set_circuit_breaker( &mut self, symbol: Symbol, config: CircuitBreakerConfig, )
Set circuit breaker configuration for an instrument. No-op if the instrument doesn’t exist (matches previous behavior).
Sourcepub fn set_fee_schedule(
&mut self,
symbol: Symbol,
schedule: FeeSchedule,
reports: &mut Vec<ExecutionReport>,
)
pub fn set_fee_schedule( &mut self, symbol: Symbol, schedule: FeeSchedule, reports: &mut Vec<ExecutionReport>, )
Set the maker/taker fee schedule for an instrument.
When the effective max fee rate changes, all affected buy-side orders have their reservations adjusted:
- Resting limit buys and pending stop-limit buys: reservation topped up from available balance, or cancelled if insufficient.
- Pending stop-market buys:
quote_budgetrecalculated so the fill leaves room for the new fee.
No-op if the instrument doesn’t exist, or if either rate is outside the documented ±10_000 bps range (±100%) — see below.
Sourcepub fn prefault(&mut self)
pub fn prefault(&mut self)
Touch all pre-allocated HashMap pages so page faults happen at startup, not on the hot path. Call once after adding instruments, before accepting orders. Skips maps that already contain data — their pages are already faulted from the insertions that populated them.
Sourcepub fn add_instrument(&mut self, spec: InstrumentSpec)
pub fn add_instrument(&mut self, spec: InstrumentSpec)
Register a new instrument with its currency pair specification. Grows the instrument Vec if needed (admin operation, not hot path).
Sourcepub fn deposit(&mut self, account: AccountId, currency: CurrencyId, amount: u64)
pub fn deposit(&mut self, account: AccountId, currency: CurrencyId, amount: u64)
Deposit funds into an account.
Sourcepub fn provision_account(&mut self, account: AccountId, amount: u64)
pub fn provision_account(&mut self, account: AccountId, amount: u64)
Provision an account with amount deposited in every currency of
every registered instrument. Replaces O(instruments) individual
Deposit calls with a single operation for bulk seeding.
Sourcepub fn accounts(&self) -> &AccountManager
pub fn accounts(&self) -> &AccountManager
Get the account manager (for balance queries).
Sourcepub fn cancel_all(
&mut self,
account: AccountId,
reports: &mut Vec<ExecutionReport>,
)
pub fn cancel_all( &mut self, account: AccountId, reports: &mut Vec<ExecutionReport>, )
Cancel all resting orders and pending stops for an account across all instruments (kill switch). Releases all associated reservations.
Sourcepub fn end_of_day(&mut self, reports: &mut Vec<ExecutionReport>)
pub fn end_of_day(&mut self, reports: &mut Vec<ExecutionReport>)
Cancel all resting orders and pending stops with TimeInForce::Day
across all instruments. Called at end-of-session.
Sourcepub fn disable_instrument(
&mut self,
symbol: Symbol,
reports: &mut Vec<ExecutionReport>,
)
pub fn disable_instrument( &mut self, symbol: Symbol, reports: &mut Vec<ExecutionReport>, )
Disable an instrument: reject future orders and cancel all resting orders and pending stops. Idempotent — disabling an already-disabled instrument is a no-op (no reports emitted).
Sourcepub fn enable_instrument(
&mut self,
symbol: Symbol,
reports: &mut Vec<ExecutionReport>,
)
pub fn enable_instrument( &mut self, symbol: Symbol, reports: &mut Vec<ExecutionReport>, )
Re-enable a previously disabled instrument, allowing new orders.
Sourcepub fn remove_instrument(
&mut self,
symbol: Symbol,
reports: &mut Vec<ExecutionReport>,
)
pub fn remove_instrument( &mut self, symbol: Symbol, reports: &mut Vec<ExecutionReport>, )
Permanently remove a disabled instrument, reclaiming memory. Only succeeds if the instrument is disabled and has no resting orders (which disable guarantees). Active instruments must be disabled first.
Sourcepub fn cancel(
&mut self,
symbol: Symbol,
account: AccountId,
order_id: OrderId,
reports: &mut Vec<ExecutionReport>,
)
pub fn cancel( &mut self, symbol: Symbol, account: AccountId, order_id: OrderId, reports: &mut Vec<ExecutionReport>, )
Cancel a resting order on the given instrument.
Sourcepub fn best_bid(&self, symbol: Symbol) -> Option<Price>
pub fn best_bid(&self, symbol: Symbol) -> Option<Price>
Best (highest) bid on symbol’s book, or None if the bid side is
empty or the symbol is not registered. Read-only book introspection
(market-data / audit queries); not on the matching hot path.
Sourcepub fn best_ask(&self, symbol: Symbol) -> Option<Price>
pub fn best_ask(&self, symbol: Symbol) -> Option<Price>
Best (lowest) ask on symbol’s book, or None if the ask side is
empty or the symbol is not registered.
Sourcepub fn depth_at(&self, symbol: Symbol, price: Price, side: Side) -> u64
pub fn depth_at(&self, symbol: Symbol, price: Price, side: Side) -> u64
Total resting quantity at one exact price level on symbol’s book,
or 0 if the level does not exist or the symbol is not registered.
Sourcepub fn withdraw(
&mut self,
account: AccountId,
currency: CurrencyId,
amount: u64,
) -> Result<(), RejectReason>
pub fn withdraw( &mut self, account: AccountId, currency: CurrencyId, amount: u64, ) -> Result<(), RejectReason>
Withdraw funds from an account. Rejects if the account has resting
orders (must CancelAll first) or insufficient available balance.
Removes the balance entry if it reaches zero (memory cleanup).
Sourcepub fn clone_via_snapshot(&self) -> Exchange
pub fn clone_via_snapshot(&self) -> Exchange
Create a deep copy of this Exchange by round-tripping through the snapshot representation. Used by the shadow snapshot stage to obtain an independent replica of the exchange state at startup.
Not suitable for the hot path — allocates extensively.
Trait Implementations§
Source§impl Application for ServerApp
impl Application for ServerApp
Source§const APP_VERSION: u16 = engine_snapshot::PAYLOAD_VERSION
const APP_VERSION: u16 = engine_snapshot::PAYLOAD_VERSION
Schema version for the snapshot payload. Tracks the underlying
snapshot module’s PAYLOAD_VERSION — any change there forces a
bump here too, surfaced through the transport-owned frame.
Source§fn apply(
&mut self,
event: Self::Event,
ctx: &ApplyCtx,
out: &mut Vec<Self::Report>,
) -> Option<Self::QueryResponse>
fn apply( &mut self, event: Self::Event, ctx: &ApplyCtx, out: &mut Vec<Self::Report>, ) -> Option<Self::QueryResponse>
Thin dispatcher over TradingEvent. Marked #[inline] so the
matching stage’s monomorphised hot loop can see through to each
concrete Exchange method: the inner methods (execute, cancel,
…) own the real work and keep their own inlining attrs.
Source§fn prefault(&mut self)
fn prefault(&mut self)
Route through Exchange::prefault, which walks the pre-allocated
slabs and indices so the first hot-path access after startup
doesn’t soft-fault. Avoids the default snapshot-round-trip
implementation on a cold allocator.
Source§fn clone_via_snapshot(&self) -> Result<Self>
fn clone_via_snapshot(&self) -> Result<Self>
Exchange exposes an in-memory clone_via_snapshot that skips
the byte serialisation — faster than the default
serialise-then-deserialise path. Keep the optimisation for the
shadow-snapshot stage.
Source§fn snapshot<W: Write>(&self, w: &mut W) -> Result<()>
fn snapshot<W: Write>(&self, w: &mut W) -> Result<()>
Writes the engine payload bytes verbatim. The transport stores
APP_VERSION in its frame and rejects mismatching files before
restore is ever called, so duplicating the version in the
payload would be unreachable. If multi-version migration ever
lands, drop the transport-side APP_VERSION check and reintroduce
an in-payload version prefix here.
Source§type Event = TradingEvent
type Event = TradingEvent
Source§type Report = ExecutionReport
type Report = ExecutionReport
Copy keeps the output ring
buffer allocation-free.Source§fn check_request_seq(&mut self, key_hash: u64, seq: u64) -> bool
fn check_request_seq(&mut self, key_hash: u64, seq: u64) -> bool
true if seq is strictly
greater than the previously seen sequence for key_hash (and
the high-water mark has been advanced), false on a duplicate. Read moreSource§fn build_reject(
event: &Self::Event,
reason: TransportRejectReason,
) -> Self::Report
fn build_reject( event: &Self::Event, reason: TransportRejectReason, ) -> Self::Report
apply has observed the event.
No access to &self — the reject must be constructible from the
event alone (plus the transport’s reason).