nautilus_model/python/data/
bet.rs1use std::{
17 collections::hash_map::DefaultHasher,
18 hash::{Hash, Hasher},
19};
20
21use nautilus_core::python::IntoPyObjectNautilusExt;
22use pyo3::{basic::CompareOp, prelude::*};
23use rust_decimal::Decimal;
24
25use crate::{
26 data::bet::{Bet, BetPosition, calc_bets_pnl, inverse_probability_to_bet, probability_to_bet},
27 enums::{BetSide, OrderSide},
28};
29
30#[pymethods]
31impl Bet {
32 #[new]
33 fn py_new(price: Decimal, stake: Decimal, side: BetSide) -> PyResult<Self> {
34 Ok(Bet::new(price, stake, side))
35 }
36
37 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
38 match op {
39 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
40 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
41 _ => py.NotImplemented(),
42 }
43 }
44
45 fn __hash__(&self) -> isize {
46 let mut h = DefaultHasher::new();
47 self.hash(&mut h);
48 h.finish() as isize
49 }
50
51 fn __repr__(&self) -> String {
52 format!("{self:?}")
53 }
54
55 fn __str__(&self) -> String {
56 self.to_string()
57 }
58
59 #[staticmethod]
61 #[pyo3(name = "from_stake_or_liability")]
62 fn py_from_stake_or_liability(
63 price: Decimal,
64 volume: Decimal,
65 side: BetSide,
66 ) -> PyResult<Self> {
67 Ok(Bet::from_stake_or_liability(price, volume, side))
68 }
69
70 #[staticmethod]
72 #[pyo3(name = "from_stake")]
73 fn py_from_stake(price: Decimal, stake: Decimal, side: BetSide) -> PyResult<Self> {
74 Ok(Bet::from_stake(price, stake, side))
75 }
76
77 #[staticmethod]
81 #[pyo3(name = "from_liability")]
82 fn py_from_liability(price: Decimal, liability: Decimal, side: BetSide) -> PyResult<Self> {
83 Ok(Bet::from_liability(price, liability, side))
84 }
85
86 #[getter]
88 #[pyo3(name = "price")]
89 fn py_price(&self) -> Decimal {
90 self.price()
91 }
92
93 #[getter]
95 #[pyo3(name = "stake")]
96 fn py_stake(&self) -> Decimal {
97 self.stake()
98 }
99
100 #[getter]
102 #[pyo3(name = "side")]
103 fn py_side(&self) -> BetSide {
104 self.side()
105 }
106
107 #[pyo3(name = "exposure")]
109 fn py_exposure(&self) -> Decimal {
110 self.exposure()
111 }
112
113 #[pyo3(name = "liability")]
115 fn py_liability(&self) -> Decimal {
116 self.liability()
117 }
118
119 #[pyo3(name = "profit")]
121 fn py_profit(&self) -> Decimal {
122 self.profit()
123 }
124
125 #[pyo3(name = "outcome_win_payoff")]
127 fn py_outcome_win_payoff(&self) -> Decimal {
128 self.outcome_win_payoff()
129 }
130
131 #[pyo3(name = "outcome_lose_payoff")]
133 fn py_outcome_lose_payoff(&self) -> Decimal {
134 self.outcome_lose_payoff()
135 }
136
137 #[pyo3(name = "hedging_stake")]
139 fn py_hedging_stake(&self, price: Decimal) -> Decimal {
140 self.hedging_stake(price)
141 }
142
143 #[pyo3(name = "hedging_bet")]
145 fn py_hedging_bet(&self, price: Decimal) -> Self {
146 self.hedging_bet(price)
147 }
148}
149
150#[pymethods]
151impl BetPosition {
152 #[new]
153 fn py_new() -> Self {
154 Self::default()
155 }
156
157 fn __repr__(&self) -> String {
158 format!("{self:?}")
159 }
160
161 fn __str__(&self) -> String {
162 self.to_string()
163 }
164
165 #[getter]
167 #[pyo3(name = "price")]
168 fn py_price(&self) -> Decimal {
169 self.price()
170 }
171
172 #[getter]
174 #[pyo3(name = "side")]
175 fn py_side(&self) -> Option<BetSide> {
176 self.side()
177 }
178
179 #[getter]
181 #[pyo3(name = "exposure")]
182 fn py_exposure(&self) -> Decimal {
183 self.exposure()
184 }
185
186 #[getter]
188 #[pyo3(name = "realized_pnl")]
189 fn py_realized_pnl(&self) -> Decimal {
190 self.realized_pnl()
191 }
192
193 #[pyo3(name = "add_bet")]
195 fn py_add_bet(&mut self, bet: &Bet) {
196 self.add_bet(bet.clone());
197 }
198
199 #[pyo3(name = "as_bet")]
201 fn py_as_bet(&self) -> Option<Bet> {
202 self.as_bet()
203 }
204
205 #[pyo3(name = "unrealized_pnl")]
207 fn py_unrealized_pnl(&self, price: Decimal) -> Decimal {
208 self.unrealized_pnl(price)
209 }
210
211 #[pyo3(name = "total_pnl")]
213 fn py_total_pnl(&self, price: Decimal) -> Decimal {
214 self.total_pnl(price)
215 }
216
217 #[pyo3(name = "flattening_bet")]
219 fn py_flattening_bet(&self, price: Decimal) -> Option<Bet> {
220 self.flattening_bet(price)
221 }
222
223 #[pyo3(name = "reset")]
225 fn py_reset(&mut self) {
226 self.reset();
227 }
228}
229
230#[pyfunction]
231#[pyo3(name = "calc_bets_pnl")]
232pub fn py_calc_bets_pnl(bets: Vec<Bet>) -> PyResult<Decimal> {
233 Ok(calc_bets_pnl(&bets))
234}
235
236#[pyfunction]
237#[pyo3(name = "probability_to_bet")]
238pub fn py_probability_to_bet(
239 probability: Decimal,
240 volume: Decimal,
241 side: OrderSide,
242) -> PyResult<Bet> {
243 Ok(probability_to_bet(probability, volume, side.as_specified()))
244}
245
246#[pyfunction]
247#[pyo3(name = "inverse_probability_to_bet")]
248pub fn py_inverse_probability_to_bet(
249 probability: Decimal,
250 volume: Decimal,
251 side: OrderSide,
252) -> PyResult<Bet> {
253 Ok(inverse_probability_to_bet(
254 probability,
255 volume,
256 side.as_specified(),
257 ))
258}