Skip to main content

brokapi_alor/
cid.rs

1//! The client order id codec: ALOR keeps a comment of up to 100 characters on every order, and a full UUID
2//! fits into it.
3
4use brokapi_core::{Charset, CidCaps, CidCodec, CidError, ClientOrderId, NativeCid};
5use uuid::Uuid;
6
7/// The cid is written into the `comment` of the order as a hyphenated UUID (lossless, 128-bit). The broker
8/// returns the comment in the order list, in the order stream and in the trades, which is how a cid comes back.
9/// The same text is the `X-REQID` of the command, so a repeated command carries the same id.
10#[derive(Debug, Clone, Copy, Default)]
11pub struct AlorCidCodec;
12
13impl CidCodec for AlorCidCodec {
14    fn caps(&self) -> CidCaps {
15        CidCaps {
16            max_len: 36,
17            charset: Charset::Ascii,
18            native_bits: 128,
19            lossless: true,
20        }
21    }
22
23    fn encode(&self, cid: ClientOrderId) -> Result<NativeCid, CidError> {
24        let s = cid.hyphenated().to_string();
25        if s.len() > self.caps().max_len {
26            return Err(CidError::TooLong {
27                len: s.len(),
28                max: self.caps().max_len,
29            });
30        }
31        Ok(NativeCid::new(s))
32    }
33
34    fn decode(&self, native: &NativeCid) -> Option<ClientOrderId> {
35        Uuid::parse_str(native.as_str()).ok()
36    }
37}
38
39/// The cid of an order that carries none of ours (placed in the app of the broker, or by another program).
40pub fn cid_from_comment(comment: Option<&str>) -> Option<ClientOrderId> {
41    Uuid::parse_str(comment?.trim()).ok()
42}
43
44/// Namespace of the stable cids given to orders that are not ours.
45const FOREIGN_NS: Uuid = Uuid::from_u128(0x7bc5_2f01_53a4_4d0e_9b6a_0c8e_a102_0001);
46
47/// Stable cid for an order of this account that this connector did not place: the same order number always
48/// gives the same cid, so it can be told apart from ours and found again.
49pub fn foreign_cid(order_id: &str) -> ClientOrderId {
50    Uuid::new_v5(&FOREIGN_NS, order_id.as_bytes())
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use proptest::prelude::*;
57
58    #[test]
59    fn foreign_cids_are_stable_and_distinct() {
60        assert_eq!(foreign_cid("18995978560"), foreign_cid("18995978560"));
61        assert_ne!(foreign_cid("18995978560"), foreign_cid("18995978561"));
62        assert_eq!(foreign_cid("x").get_version_num(), 5);
63    }
64
65    #[test]
66    fn only_a_whole_uuid_in_the_comment_is_a_cid() {
67        let cid = Uuid::new_v4();
68        assert_eq!(cid_from_comment(Some(&cid.to_string())), Some(cid));
69        assert_eq!(cid_from_comment(Some("my first order")), None);
70        assert_eq!(cid_from_comment(None), None);
71    }
72
73    proptest! {
74        #[test]
75        fn alor_cid_roundtrip(bytes in any::<[u8; 16]>()) {
76            let cid = ClientOrderId::from_bytes(bytes);
77            let native = AlorCidCodec.encode(cid).expect("encode");
78            prop_assert_eq!(AlorCidCodec.decode(&native), Some(cid));
79        }
80    }
81}