polyoxide_venue/lib.rs
1//! # polyoxide-venue
2//!
3//! The vocabulary every polyoxide crate shares, whichever venue it talks to.
4//!
5//! - [`Class`] sorts every failure into one of eight classes, and [`Classify`],
6//! implemented by every public error type in the workspace, reports an
7//! error's class, whether it is a fault, and how long the server asked the
8//! caller to wait. [`ClassifiedError`] carries any of them.
9//! - [`class_for_status`], [`class_for_close_code`] and
10//! [`class_for_handshake_status`] are the one status and close-code table,
11//! and [`impl_ws_classification!`] expands the one socket table for
12//! `polyoxide_ws::WsError` in the venue crate that wraps it.
13//! - [`parse_retry_after`] is the one `Retry-After` parser, and
14//! [`retry_delay`] lets it only lengthen the client's own backoff.
15//! - [`Secret`] holds a value its `Debug` never prints, and [`UnixMillis`] is
16//! the time venues stamp things with.
17//! - [`wire_enum!`] and [`open_enum!`] declare an enum whose variants each have
18//! one wire spelling; a closed one refuses an unknown spelling with
19//! [`UnknownVariant`], and an open one keeps it in `Other`.
20//! - With the `decimal` feature, `positional` reads and writes rows sent as
21//! bare arrays of decimal strings.
22//! - With the `keys` feature, `MarketKey` names one instrument as
23//! `venue.product:id`. Only a product's `KeySpace` makes one, so two keys
24//! are equal exactly when they name the same instrument; `RawKey` is text
25//! split into its parts and checked by no product yet. Each `KeySpace`
26//! declares its product's `ProductClass`, which every key it makes carries.
27//! - With the `market` feature, the traits a venue's products implement so
28//! market data reads the same on every venue: `MarketData`, which every
29//! product implements, and the capability traits `Instruments`, `Trades`,
30//! `Candles`, `Funding` and `EventGroups`, each with a `Dyn` wrapper. The
31//! class bundles `PerpetualMarket`, `EventContractMarket` and `SpotMarket`
32//! name what every product of a class offers. The traits return the records
33//! market data is read into (`Instrument`, `Quote`, `Book`, `Trade`, `Candle`,
34//! `FundingRate` and `EventGroup`), each carrying `Extensions`, a venue's own
35//! facts on a shared record, one value per type.
36//! - With the `trading` feature, `Trading` places and cancels orders, reads
37//! open orders, positions and balances, and streams a venue's
38//! acknowledgements and fills as `TradingEvents`, which begins with a
39//! `Resync` and carries another after every reconnect. It works in
40//! `OrderRequest`, `Order`, `Fill`, `Position` and `Balance`, with the opaque
41//! `OrderId`, `FillId` and `ClientOrderId`, each record carrying
42//! `Extensions`; `Extensions::ensure_declared` is how a venue refuses an
43//! option it does not declare.
44//!
45//! By default the crate depends on nothing (on wasm32, only `web-time`, which
46//! reads the browser's wall clock), so a credential-free socket crate can use
47//! it without building an HTTP or signing stack. The enum macros name
48//! `::serde`, so a crate that invokes them depends on serde itself, with
49//! serde's `derive` feature for `wire_enum!`, which derives the two traits;
50//! `open_enum!` implements them by hand.
51//!
52//! ```
53//! use polyoxide_venue::{class_for_status, Class};
54//!
55//! let class = class_for_status(503).unwrap().with_code("dependency_unavailable");
56//! assert!(class.is_retriable());
57//! assert_eq!(class.code(), Some("dependency_unavailable"));
58//! assert_eq!(class_for_status(451), Some(Class::Restricted));
59//! ```
60
61#![warn(missing_docs)]
62
63#[cfg(doctest)]
64#[doc = include_str!("../README.md")]
65struct ReadmeDoctests;
66
67mod class;
68#[cfg(feature = "market")]
69mod extensions;
70#[cfg(feature = "trading")]
71mod ids;
72#[cfg(feature = "keys")]
73mod key;
74#[cfg(feature = "market")]
75mod market;
76#[cfg(feature = "decimal")]
77pub mod positional;
78#[cfg(feature = "market")]
79mod records;
80mod retry_after;
81mod secret;
82mod socket;
83mod status;
84mod time;
85#[cfg(feature = "trading")]
86mod trading;
87mod wire;
88mod ws_table;
89
90pub use class::{Class, ClassifiedError, Classify};
91#[cfg(feature = "market")]
92pub use extensions::{DuplicateExtension, Extensions, UndeclaredExtension};
93#[cfg(feature = "trading")]
94pub use ids::{ClientOrderId, FillId, OrderId};
95#[cfg(feature = "keys")]
96pub use key::{KeyError, KeySpace, MarketKey, ProductClass, ProductId, RawKey, VenueId};
97#[cfg(feature = "market")]
98pub use market::{
99 Candles, DynCandles, DynEventGroups, DynFunding, DynInstruments, DynMarketData, DynTrades,
100 EventContractMarket, EventGroups, Funding, Instruments, MarketData, PerpetualMarket,
101 SpotMarket, Trades,
102};
103#[cfg(feature = "market")]
104pub use records::{
105 Book, Candle, CandleInterval, CandleQuery, EventGroup, EventOutcome, FundingRate, Instrument,
106 Level, NotListed, Quote, Side, SizeUnit, Trade, TradeId, UnservedInterval,
107};
108pub use retry_after::{parse_retry_after, retry_delay};
109pub use secret::Secret;
110pub use socket::{class_for_close_code, class_for_handshake_status};
111pub use status::class_for_status;
112pub use time::UnixMillis;
113#[cfg(feature = "trading")]
114pub use trading::{
115 Balance, DynTrading, Fill, KillReason, Liquidity, Order, OrderRequest, OrderStatus, Position,
116 TimeInForce, Trading, TradingEvent, TradingEvents,
117};
118pub use wire::UnknownVariant;
119
120// Every public error type implements `Classify`; one without it fails the
121// build here. `.github/scripts/tests/test_classify_coverage.py` fails when a
122// public error type is missing from this list.
123const _: fn() = || {
124 fn is<T: crate::Classify>() {}
125 is::<UnknownVariant>();
126 #[cfg(feature = "keys")]
127 is::<KeyError>();
128 #[cfg(feature = "market")]
129 is::<DuplicateExtension>();
130 #[cfg(feature = "market")]
131 is::<NotListed>();
132 #[cfg(feature = "market")]
133 is::<UndeclaredExtension>();
134 #[cfg(feature = "market")]
135 is::<UnservedInterval>();
136};