architect_api/symbology/
product.rs

1//! Product are specific assets, liabilities, tokens, etc. things that can be owned,
2//! traded, or exchanged.
3
4use super::{Symbolic, VenueId};
5use crate::{uuid_val, Str};
6use anyhow::Result;
7use bytes::Bytes;
8use chrono::{DateTime, Utc};
9#[cfg(feature = "netidx")]
10use derive::FromValue;
11#[cfg(feature = "netidx")]
12use netidx_derive::Pack;
13use rust_decimal::Decimal;
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16use std::collections::BTreeMap;
17use uuid::{uuid, Uuid};
18
19static PRODUCT_NS: Uuid = uuid!("bb25a7a7-a61c-485a-ac29-1de369a6a043");
20uuid_val!(ProductId, PRODUCT_NS);
21
22#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
23#[cfg_attr(feature = "netidx", derive(Pack, FromValue))]
24pub struct Product {
25    pub id: ProductId,
26    pub name: Str,
27    pub kind: ProductKind,
28}
29
30impl Product {
31    pub fn new(name: &str, kind: ProductKind) -> Result<Product> {
32        Ok(Product { id: ProductId::from(name), name: Str::try_from(name)?, kind })
33    }
34}
35
36impl Symbolic for Product {
37    type Id = ProductId;
38
39    fn type_name() -> &'static str {
40        "product"
41    }
42
43    fn id(&self) -> Self::Id {
44        self.id
45    }
46
47    fn name(&self) -> Str {
48        self.name
49    }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy, JsonSchema)]
53#[cfg_attr(feature = "netidx", derive(Pack))]
54#[serde(tag = "type", content = "value")]
55pub enum InstrumentType {
56    Inverse,
57    Linear,
58    Quanto,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
62#[cfg_attr(feature = "netidx", derive(Pack))]
63#[serde(tag = "type", content = "value")]
64pub enum ProductKind {
65    // CR alee: deprecate in favor of Coin, Token without params
66    Coin {
67        token_info: BTreeMap<VenueId, TokenInfo>,
68    },
69    Fiat,
70    Equity,
71    Perpetual {
72        underlying: Option<ProductId>,
73        multiplier: Option<Decimal>,
74        instrument_type: Option<InstrumentType>,
75    },
76    /// The one guarantee for [underlying] if set is that it can
77    /// be used to uniquely identify strips of related futures
78    Future {
79        underlying: Option<ProductId>,
80        multiplier: Option<Decimal>,
81        expiration: Option<DateTime<Utc>>,
82        instrument_type: Option<InstrumentType>,
83    },
84    FutureSpread {
85        same_side_leg: Option<ProductId>,
86        opp_side_leg: Option<ProductId>,
87    },
88    /// The one guarantee for [underlying] if set is that it can
89    /// be used to uniquely identify strips of related options
90    Option {
91        underlying: Option<ProductId>,
92        multiplier: Option<Decimal>,
93        expiration: Option<DateTime<Utc>>,
94        instrument_type: Option<InstrumentType>,
95    },
96    Index,
97    Commodity,
98    /// Event contracts are products akin to binary options
99    /// which settle to an outcome of a future event.
100    ///
101    /// Specific tradable event contracts are represented by
102    /// the EventContract variant, e.g. FED-2024-SEP-CUT-25-YES
103    /// and/or FED-2024-SEP-CUT-25-NO. for the YES
104    /// and NO contracts of the "Fed to cut 25 bps" outcome.
105    /// EventContract's are grouped into EventOutcome's,
106    /// which pair the YES and NO contracts of an outcome
107    /// together.  There are venues like KALSHI which have
108    /// only one YES contract per outcome (the NO contract
109    /// is implicit via short-selling the YES contract).
110    ///
111    /// EventOutcomes are grouped into Events, e.g.
112    /// FED-2024-SEP is an Event with the following mutually
113    /// exclusive outcomes:
114    ///
115    /// - FED-2024-SEP-HIKE
116    /// - FED-2024-SEP-CUT-0
117    /// - FED-2024-SEP-CUT-25
118    /// - FED-2024-SEP-CUT-ABOVE-25
119    ///
120    /// Events _may_ be grouped into EventSeries, e.g. all
121    /// FED events belong to the same series of events.
122    ///
123    /// The grouping of EventContracts into outcomes,
124    /// events, and event series are indicative and mostly
125    /// for display purposes, and don't necessarily imply
126    /// anything about the settlement of individual
127    /// event contracts.
128    EventSeries {
129        display_name: Option<String>,
130    },
131    Event {
132        series: Option<ProductId>,
133        outcomes: Vec<ProductId>,
134        mutually_exclusive: Option<bool>,
135        expiration: Option<DateTime<Utc>>,
136        display_category: Option<String>,
137        display_name: Option<String>,
138    },
139    EventOutcome {
140        contracts: EventContracts,
141        display_order: Option<u32>,
142        display_name: Option<String>,
143    },
144    // CR alee: consider making Event/EventOutcome/EventSeries
145    // first-class symbology objects, and then only have up pointers
146    // from there...would make query logic more rational
147    EventContract {
148        underlying: Option<ProductId>,
149        expiration: Option<DateTime<Utc>>,
150    },
151    #[cfg_attr(feature = "netidx", pack(other))]
152    #[serde(other)]
153    Unknown,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
157#[cfg_attr(feature = "netidx", derive(Pack))]
158pub enum EventContracts {
159    Single { yes: ProductId, yes_alias: Option<Str> },
160    Dual { yes: ProductId, yes_alias: Option<Str>, no: ProductId, no_alias: Option<Str> },
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
164#[cfg_attr(feature = "juniper", derive(juniper::GraphQLUnion))]
165#[cfg_attr(feature = "netidx", derive(Pack))]
166pub enum TokenInfo {
167    ERC20(ERC20TokenInfo),
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
171#[cfg_attr(feature = "netidx", derive(Pack))]
172pub struct ERC20TokenInfo {
173    // CR alee: don't use bytes, just use the packed ethers type
174    pub address: Bytes,
175    pub decimals: u8,
176}
177
178#[cfg(feature = "juniper")]
179#[cfg_attr(feature = "juniper", juniper::graphql_object)]
180impl ERC20TokenInfo {
181    // CR alee: resolve above CR before implementing address()
182
183    pub fn decimals(&self) -> crate::utils::graphql_scalars::U8 {
184        self.decimals.into()
185    }
186}