Skip to main content

qs_instruments/
economics.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    EconomicsImplementationId, EconomicsModelId, InstrumentEconomics, ResolvedInstrumentRef,
7};
8
9/// Economic operation that must be supplied by executable code.
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum EconomicOperation {
13    PositionValue,
14    RealizedPnl,
15    UnrealizedPnl,
16    Inventory,
17    Fees,
18    Funding,
19    Margin,
20    Liquidation,
21}
22
23/// Executable economics capability supplied by a runtime implementation.
24pub trait EconomicsCapabilityProvider: Send + Sync {
25    fn implementation_id(&self) -> &EconomicsImplementationId;
26
27    fn supports(&self, model: &EconomicsModelId, operation: EconomicOperation) -> bool;
28}
29
30/// One operation bound to a concrete implementation and model.
31#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct BoundEconomicCapability {
34    pub operation: EconomicOperation,
35    pub model: EconomicsModelId,
36    pub implementation: EconomicsImplementationId,
37}
38
39/// Fail-closed executable capability binding for one resolved instrument.
40#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
41#[serde(deny_unknown_fields)]
42pub struct EconomicsBinding {
43    pub instrument: ResolvedInstrumentRef,
44    pub capabilities: BTreeMap<EconomicOperation, BoundEconomicCapability>,
45}
46
47/// Bind every requested economic operation to a registered implementation.
48pub fn bind_economics(
49    instrument: &ResolvedInstrumentRef,
50    economics: &InstrumentEconomics,
51    required: impl IntoIterator<Item = EconomicOperation>,
52    providers: &[&dyn EconomicsCapabilityProvider],
53) -> Result<EconomicsBinding, EconomicsCapabilityError> {
54    let mut capabilities = BTreeMap::new();
55    for operation in required {
56        let model = model_for_operation(economics, operation)?.clone();
57        let mut implementations = providers
58            .iter()
59            .filter(|provider| provider.supports(&model, operation))
60            .map(|provider| provider.implementation_id().clone())
61            .collect::<Vec<_>>();
62        implementations.sort();
63        implementations.dedup();
64        let implementation = match implementations.as_slice() {
65            [] => {
66                return Err(EconomicsCapabilityError::Unsupported {
67                    operation,
68                    model: model.clone(),
69                });
70            }
71            [implementation] => implementation.clone(),
72            _ => {
73                return Err(EconomicsCapabilityError::Ambiguous {
74                    operation,
75                    model: model.clone(),
76                    implementations,
77                });
78            }
79        };
80        capabilities.insert(
81            operation,
82            BoundEconomicCapability {
83                operation,
84                model,
85                implementation,
86            },
87        );
88    }
89    Ok(EconomicsBinding {
90        instrument: instrument.clone(),
91        capabilities,
92    })
93}
94
95fn model_for_operation(
96    economics: &InstrumentEconomics,
97    operation: EconomicOperation,
98) -> Result<&EconomicsModelId, EconomicsCapabilityError> {
99    match operation {
100        EconomicOperation::PositionValue
101        | EconomicOperation::RealizedPnl
102        | EconomicOperation::UnrealizedPnl
103        | EconomicOperation::Inventory => Ok(&economics.pnl_model),
104        EconomicOperation::Fees => economics
105            .fee_model
106            .as_ref()
107            .ok_or(EconomicsCapabilityError::MissingModel { operation }),
108        EconomicOperation::Funding => economics
109            .funding_model
110            .as_ref()
111            .ok_or(EconomicsCapabilityError::MissingModel { operation }),
112        EconomicOperation::Margin | EconomicOperation::Liquidation => economics
113            .margin_model
114            .as_ref()
115            .ok_or(EconomicsCapabilityError::MissingModel { operation }),
116    }
117}
118
119/// Failures while binding declarative economics to executable capabilities.
120#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
121pub enum EconomicsCapabilityError {
122    #[error("instrument economics do not declare a model for {operation:?}")]
123    MissingModel { operation: EconomicOperation },
124    #[error("no implementation supports {operation:?} for model {model}")]
125    Unsupported {
126        operation: EconomicOperation,
127        model: EconomicsModelId,
128    },
129    #[error("multiple implementations support {operation:?} for model {model}")]
130    Ambiguous {
131        operation: EconomicOperation,
132        model: EconomicsModelId,
133        implementations: Vec<EconomicsImplementationId>,
134    },
135}