architect_api/symbology/
product.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//! Product are specific assets, liabilities, tokens, etc. things that can be owned,
//! traded, or exchanged.

use super::{Symbolic, VenueId};
use crate::{uuid_val, Str};
use anyhow::Result;
use bytes::Bytes;
use chrono::{DateTime, Utc};
#[cfg(feature = "netidx")]
use derive::FromValue;
#[cfg(feature = "netidx")]
use netidx_derive::Pack;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use uuid::{uuid, Uuid};

static PRODUCT_NS: Uuid = uuid!("bb25a7a7-a61c-485a-ac29-1de369a6a043");
uuid_val!(ProductId, PRODUCT_NS);

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "netidx", derive(Pack, FromValue))]
pub struct Product {
    pub id: ProductId,
    pub name: Str,
    pub kind: ProductKind,
}

impl Product {
    pub fn new(name: &str, kind: ProductKind) -> Result<Product> {
        Ok(Product { id: ProductId::from(name), name: Str::try_from(name)?, kind })
    }
}

impl Symbolic for Product {
    type Id = ProductId;

    fn type_name() -> &'static str {
        "product"
    }

    fn id(&self) -> Self::Id {
        self.id
    }

    fn name(&self) -> Str {
        self.name
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy)]
#[cfg_attr(feature = "netidx", derive(Pack))]
#[serde(tag = "type", content = "value")]
pub enum InstrumentType {
    Inverse,
    Linear,
    Quanto,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "netidx", derive(Pack))]
#[serde(tag = "type", content = "value")]
pub enum ProductKind {
    // CR alee: deprecate in favor of Coin, Token without params
    Coin {
        token_info: BTreeMap<VenueId, TokenInfo>,
    },
    Fiat,
    Equity,
    Perpetual {
        underlying: Option<ProductId>,
        multiplier: Option<Decimal>,
        instrument_type: Option<InstrumentType>,
    },
    /// The one guarantee for [underlying] if set is that it can
    /// be used to uniquely identify strips of related futures
    Future {
        underlying: Option<ProductId>,
        multiplier: Option<Decimal>,
        expiration: Option<DateTime<Utc>>,
        instrument_type: Option<InstrumentType>,
    },
    FutureSpread {
        same_side_leg: Option<ProductId>,
        opp_side_leg: Option<ProductId>,
    },
    /// The one guarantee for [underlying] if set is that it can
    /// be used to uniquely identify strips of related options
    Option {
        underlying: Option<ProductId>,
        multiplier: Option<Decimal>,
        expiration: Option<DateTime<Utc>>,
        instrument_type: Option<InstrumentType>,
    },
    Index,
    Commodity,
    /// Event contracts are products akin to binary options
    /// which settle to an outcome of a future event.
    ///
    /// Specific tradable event contracts are represented by
    /// the EventContract variant, e.g. FED-2024-SEP-CUT-25-YES
    /// and/or FED-2024-SEP-CUT-25-NO. for the YES
    /// and NO contracts of the "Fed to cut 25 bps" outcome.
    /// EventContract's are grouped into EventOutcome's,
    /// which pair the YES and NO contracts of an outcome
    /// together.  There are venues like KALSHI which have
    /// only one YES contract per outcome (the NO contract
    /// is implicit via short-selling the YES contract).
    ///
    /// EventOutcomes are grouped into Events, e.g.
    /// FED-2024-SEP is an Event with the following mutually
    /// exclusive outcomes:
    ///
    /// - FED-2024-SEP-HIKE
    /// - FED-2024-SEP-CUT-0
    /// - FED-2024-SEP-CUT-25
    /// - FED-2024-SEP-CUT-ABOVE-25
    ///
    /// Events _may_ be grouped into EventSeries, e.g. all
    /// FED events belong to the same series of events.
    ///
    /// The grouping of EventContracts into outcomes,
    /// events, and event series are indicative and mostly
    /// for display purposes, and don't necessarily imply
    /// anything about the settlement of individual
    /// event contracts.
    EventSeries {
        display_name: Option<String>,
    },
    Event {
        series: Option<ProductId>,
        outcomes: Vec<ProductId>,
        mutually_exclusive: Option<bool>,
        expiration: Option<DateTime<Utc>>,
        display_category: Option<String>,
        display_name: Option<String>,
    },
    EventOutcome {
        contracts: EventContracts,
        display_order: Option<u32>,
        display_name: Option<String>,
    },
    // CR alee: consider making Event/EventOutcome/EventSeries
    // first-class symbology objects, and then only have up pointers
    // from there...would make query logic more rational
    EventContract {
        underlying: Option<ProductId>,
        expiration: Option<DateTime<Utc>>,
    },
    #[cfg_attr(feature = "netidx", pack(other))]
    #[serde(other)]
    Unknown,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "netidx", derive(Pack))]
pub enum EventContracts {
    Single { yes: ProductId, yes_alias: Option<Str> },
    Dual { yes: ProductId, yes_alias: Option<Str>, no: ProductId, no_alias: Option<Str> },
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "juniper", derive(juniper::GraphQLUnion))]
#[cfg_attr(feature = "netidx", derive(Pack))]
pub enum TokenInfo {
    ERC20(ERC20TokenInfo),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "netidx", derive(Pack))]
pub struct ERC20TokenInfo {
    // CR alee: don't use bytes, just use the packed ethers type
    pub address: Bytes,
    pub decimals: u8,
}

#[cfg(feature = "juniper")]
#[cfg_attr(feature = "juniper", juniper::graphql_object)]
impl ERC20TokenInfo {
    // CR alee: resolve above CR before implementing address()

    pub fn decimals(&self) -> crate::utils::graphql_scalars::U8 {
        self.decimals.into()
    }
}