nautilus_model/python/data/
bet.rs1use std::{
17 collections::hash_map::DefaultHasher,
18 hash::{Hash, Hasher},
19};
20
21use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
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]
31#[pyo3_stub_gen::derive::gen_stub_pymethods]
32impl Bet {
33 #[new]
35 fn py_new(price: Decimal, stake: Decimal, side: BetSide) -> Self {
36 Self::new(price, stake, side)
37 }
38
39 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
40 match op {
41 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
42 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
43 _ => py.NotImplemented(),
44 }
45 }
46
47 fn __hash__(&self) -> isize {
48 let mut h = DefaultHasher::new();
49 self.hash(&mut h);
50 h.finish() as isize
51 }
52
53 fn __repr__(&self) -> String {
54 format!("{self:?}")
55 }
56
57 fn __str__(&self) -> String {
58 self.to_string()
59 }
60
61 #[staticmethod]
66 #[pyo3(name = "from_stake_or_liability")]
67 fn py_from_stake_or_liability(price: Decimal, volume: Decimal, side: BetSide) -> Self {
68 Self::from_stake_or_liability(price, volume, side)
69 }
70
71 #[staticmethod]
73 #[pyo3(name = "from_stake")]
74 fn py_from_stake(price: Decimal, stake: Decimal, side: BetSide) -> Self {
75 Self::from_stake(price, stake, side)
76 }
77
78 #[staticmethod]
80 #[pyo3(name = "from_liability")]
81 fn py_from_liability(price: Decimal, liability: Decimal, side: BetSide) -> Self {
82 Self::from_liability(price, liability, side)
83 }
84
85 #[getter]
87 #[pyo3(name = "price")]
88 fn py_price(&self) -> Decimal {
89 self.price()
90 }
91
92 #[getter]
94 #[pyo3(name = "stake")]
95 fn py_stake(&self) -> Decimal {
96 self.stake()
97 }
98
99 #[getter]
101 #[pyo3(name = "side")]
102 fn py_side(&self) -> BetSide {
103 self.side()
104 }
105
106 #[pyo3(name = "exposure")]
110 fn py_exposure(&self) -> Decimal {
111 self.exposure()
112 }
113
114 #[pyo3(name = "liability")]
119 fn py_liability(&self) -> Decimal {
120 self.liability()
121 }
122
123 #[pyo3(name = "profit")]
127 fn py_profit(&self) -> Decimal {
128 self.profit()
129 }
130
131 #[pyo3(name = "outcome_win_payoff")]
135 fn py_outcome_win_payoff(&self) -> Decimal {
136 self.outcome_win_payoff()
137 }
138
139 #[pyo3(name = "outcome_lose_payoff")]
143 fn py_outcome_lose_payoff(&self) -> Decimal {
144 self.outcome_lose_payoff()
145 }
146
147 #[pyo3(name = "hedging_stake")]
149 fn py_hedging_stake(&self, price: Decimal) -> Decimal {
150 self.hedging_stake(price)
151 }
152
153 #[pyo3(name = "hedging_bet")]
155 fn py_hedging_bet(&self, price: Decimal) -> Self {
156 self.hedging_bet(price)
157 }
158}
159
160#[pymethods]
161#[pyo3_stub_gen::derive::gen_stub_pymethods]
162impl BetPosition {
163 #[new]
165 fn py_new() -> Self {
166 Self::default()
167 }
168
169 fn __repr__(&self) -> String {
170 format!("{self:?}")
171 }
172
173 fn __str__(&self) -> String {
174 self.to_string()
175 }
176
177 #[getter]
179 #[pyo3(name = "price")]
180 fn py_price(&self) -> Decimal {
181 self.price()
182 }
183
184 #[getter]
188 #[pyo3(name = "side")]
189 fn py_side(&self) -> Option<BetSide> {
190 self.side()
191 }
192
193 #[getter]
195 #[pyo3(name = "exposure")]
196 fn py_exposure(&self) -> Decimal {
197 self.exposure()
198 }
199
200 #[getter]
202 #[pyo3(name = "realized_pnl")]
203 fn py_realized_pnl(&self) -> Decimal {
204 self.realized_pnl()
205 }
206
207 #[pyo3(name = "add_bet")]
209 fn py_add_bet(&mut self, bet: &Bet) {
210 self.add_bet(bet.clone());
211 }
212
213 #[pyo3(name = "as_bet")]
215 fn py_as_bet(&self) -> Option<Bet> {
216 self.as_bet()
217 }
218
219 #[pyo3(name = "unrealized_pnl")]
221 fn py_unrealized_pnl(&self, price: Decimal) -> Decimal {
222 self.unrealized_pnl(price)
223 }
224
225 #[pyo3(name = "total_pnl")]
227 fn py_total_pnl(&self, price: Decimal) -> Decimal {
228 self.total_pnl(price)
229 }
230
231 #[pyo3(name = "flattening_bet")]
233 fn py_flattening_bet(&self, price: Decimal) -> Option<Bet> {
234 self.flattening_bet(price)
235 }
236
237 #[pyo3(name = "reset")]
239 fn py_reset(&mut self) {
240 self.reset();
241 }
242}
243
244#[pyfunction]
246#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.model")]
247#[pyo3(name = "calc_bets_pnl")]
248#[expect(clippy::needless_pass_by_value)]
249pub fn py_calc_bets_pnl(bets: Vec<Bet>) -> PyResult<Decimal> {
250 Ok(calc_bets_pnl(&bets))
251}
252
253#[pyfunction]
257#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.model")]
258#[pyo3(name = "probability_to_bet")]
259pub fn py_probability_to_bet(
260 probability: Decimal,
261 volume: Decimal,
262 side: OrderSide,
263) -> PyResult<Bet> {
264 probability_to_bet(probability, volume, side.as_specified()).map_err(to_pyvalue_err)
265}
266
267#[pyfunction]
271#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.model")]
272#[pyo3(name = "inverse_probability_to_bet")]
273pub fn py_inverse_probability_to_bet(
274 probability: Decimal,
275 volume: Decimal,
276 side: OrderSide,
277) -> PyResult<Bet> {
278 inverse_probability_to_bet(probability, volume, side.as_specified()).map_err(to_pyvalue_err)
279}