1#![allow(dead_code)]
18#![allow(unused_variables)]
19
20use std::str::FromStr;
21
22use nautilus_core::UnixNanos;
23use nautilus_model::{
24 enums::{AssetClass, OptionKind},
25 identifiers::{InstrumentId, Symbol},
26 instruments::{
27 BettingInstrument, BinaryOption, Cfd, Commodity, CryptoFuture, CryptoFuturesSpread,
28 CryptoOption, CryptoOptionSpread, CryptoPerpetual, CurrencyPair, Equity, FuturesContract,
29 FuturesSpread, IndexInstrument, InstrumentAny, OptionContract, OptionSpread,
30 PerpetualContract, TokenizedAsset,
31 },
32 types::{Currency, Money, Price, Quantity},
33};
34use rust_decimal::Decimal;
35use sqlx::{FromRow, Row, postgres::PgRow};
36use ustr::Ustr;
37
38use crate::sql::models::{enums::AssetClassModel, read_u8, read_u64};
39
40#[derive(Debug)]
41pub struct InstrumentAnyModel(pub InstrumentAny);
42
43#[derive(Debug)]
44pub struct BettingInstrumentModel(pub BettingInstrument);
45
46#[derive(Debug)]
47pub struct BinaryOptionModel(pub BinaryOption);
48
49#[derive(Debug)]
50pub struct CryptoFutureModel(pub CryptoFuture);
51
52#[derive(Debug)]
53pub struct CryptoOptionModel(pub CryptoOption);
54
55#[derive(Debug)]
56pub struct CryptoPerpetualModel(pub CryptoPerpetual);
57
58#[derive(Debug)]
59pub struct CurrencyPairModel(pub CurrencyPair);
60
61#[derive(Debug)]
62pub struct EquityModel(pub Equity);
63
64#[derive(Debug)]
65pub struct FuturesContractModel(pub FuturesContract);
66
67#[derive(Debug)]
68pub struct FuturesSpreadModel(pub FuturesSpread);
69
70#[derive(Debug)]
71pub struct OptionContractModel(pub OptionContract);
72
73#[derive(Debug)]
74pub struct CommodityModel(pub Commodity);
75
76#[derive(Debug)]
77pub struct IndexInstrumentModel(pub IndexInstrument);
78
79#[derive(Debug)]
80pub struct CfdModel(pub Cfd);
81
82#[derive(Debug)]
83pub struct PerpetualContractModel(pub PerpetualContract);
84
85#[derive(Debug)]
86pub struct OptionSpreadModel(pub OptionSpread);
87
88#[derive(Debug)]
89pub struct CryptoFuturesSpreadModel(pub CryptoFuturesSpread);
90
91#[derive(Debug)]
92pub struct CryptoOptionSpreadModel(pub CryptoOptionSpread);
93
94#[derive(Debug)]
95pub struct TokenizedAssetModel(pub TokenizedAsset);
96
97impl<'r> FromRow<'r, PgRow> for InstrumentAnyModel {
98 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
99 let kind = row.get::<String, _>("kind");
100 if kind == "BETTING" {
101 Ok(Self(InstrumentAny::Betting(
102 BettingInstrumentModel::from_row(row).unwrap().0,
103 )))
104 } else if kind == "BINARY_OPTION" {
105 Ok(Self(InstrumentAny::BinaryOption(
106 BinaryOptionModel::from_row(row).unwrap().0,
107 )))
108 } else if kind == "CRYPTO_FUTURE" {
109 Ok(Self(InstrumentAny::CryptoFuture(
110 CryptoFutureModel::from_row(row).unwrap().0,
111 )))
112 } else if kind == "CRYPTO_FUTURES_SPREAD" {
113 Ok(Self(InstrumentAny::CryptoFuturesSpread(
114 CryptoFuturesSpreadModel::from_row(row).unwrap().0,
115 )))
116 } else if kind == "CRYPTO_OPTION" {
117 Ok(Self(InstrumentAny::CryptoOption(
118 CryptoOptionModel::from_row(row).unwrap().0,
119 )))
120 } else if kind == "CRYPTO_OPTION_SPREAD" {
121 Ok(Self(InstrumentAny::CryptoOptionSpread(
122 CryptoOptionSpreadModel::from_row(row).unwrap().0,
123 )))
124 } else if kind == "CRYPTO_PERPETUAL" {
125 Ok(Self(InstrumentAny::CryptoPerpetual(
126 CryptoPerpetualModel::from_row(row).unwrap().0,
127 )))
128 } else if kind == "CURRENCY_PAIR" {
129 Ok(Self(InstrumentAny::CurrencyPair(
130 CurrencyPairModel::from_row(row).unwrap().0,
131 )))
132 } else if kind == "EQUITY" {
133 Ok(Self(InstrumentAny::Equity(
134 EquityModel::from_row(row).unwrap().0,
135 )))
136 } else if kind == "FUTURES_CONTRACT" {
137 Ok(Self(InstrumentAny::FuturesContract(
138 FuturesContractModel::from_row(row).unwrap().0,
139 )))
140 } else if kind == "FUTURES_SPREAD" {
141 Ok(Self(InstrumentAny::FuturesSpread(
142 FuturesSpreadModel::from_row(row).unwrap().0,
143 )))
144 } else if kind == "OPTION_CONTRACT" {
145 Ok(Self(InstrumentAny::OptionContract(
146 OptionContractModel::from_row(row).unwrap().0,
147 )))
148 } else if kind == "COMMODITY" {
149 Ok(Self(InstrumentAny::Commodity(
150 CommodityModel::from_row(row).unwrap().0,
151 )))
152 } else if kind == "INDEX_INSTRUMENT" {
153 Ok(Self(InstrumentAny::IndexInstrument(
154 IndexInstrumentModel::from_row(row).unwrap().0,
155 )))
156 } else if kind == "CFD" {
157 Ok(Self(InstrumentAny::Cfd(CfdModel::from_row(row).unwrap().0)))
158 } else if kind == "OPTION_SPREAD" {
159 Ok(Self(InstrumentAny::OptionSpread(
160 OptionSpreadModel::from_row(row).unwrap().0,
161 )))
162 } else if kind == "PERPETUAL_CONTRACT" {
163 Ok(Self(InstrumentAny::PerpetualContract(
164 PerpetualContractModel::from_row(row).unwrap().0,
165 )))
166 } else if kind == "TOKENIZED_ASSET" {
167 Ok(Self(InstrumentAny::TokenizedAsset(
168 TokenizedAssetModel::from_row(row).unwrap().0,
169 )))
170 } else {
171 Err(sqlx::Error::Decode(
172 format!("Unknown instrument type: {kind}").into(),
173 ))
174 }
175 }
176}
177
178impl<'r> FromRow<'r, PgRow> for BettingInstrumentModel {
180 #[expect(
181 clippy::too_many_lines,
182 reason = "SQL row mapping mirrors the full betting instrument constructor"
183 )]
184 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
185 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
186 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
187 let event_type_id = read_u64(row, "event_type_id")?;
188 let event_type_name = row
189 .try_get::<String, _>("event_type_name")
190 .map(|res| Ustr::from(res.as_str()))?;
191 let competition_id = read_u64(row, "competition_id")?;
192 let competition_name = row
193 .try_get::<String, _>("competition_name")
194 .map(|res| Ustr::from(res.as_str()))?;
195 let event_id = read_u64(row, "event_id")?;
196 let event_name = row
197 .try_get::<String, _>("event_name")
198 .map(|res| Ustr::from(res.as_str()))?;
199 let event_country_code = row
200 .try_get::<String, _>("event_country_code")
201 .map(|res| Ustr::from(res.as_str()))?;
202 let event_open_date = row
203 .try_get::<String, _>("event_open_date")
204 .map(UnixNanos::from)?;
205 let betting_type = row
206 .try_get::<String, _>("betting_type")
207 .map(|res| Ustr::from(res.as_str()))?;
208 let market_id = row
209 .try_get::<String, _>("market_id")
210 .map(|res| Ustr::from(res.as_str()))?;
211 let market_name = row
212 .try_get::<String, _>("market_name")
213 .map(|res| Ustr::from(res.as_str()))?;
214 let market_type = row
215 .try_get::<String, _>("market_type")
216 .map(|res| Ustr::from(res.as_str()))?;
217 let market_start_time = row
218 .try_get::<String, _>("market_start_time")
219 .map(UnixNanos::from)?;
220 let selection_id = read_u64(row, "selection_id")?;
221 let selection_name = row
222 .try_get::<String, _>("selection_name")
223 .map(|res| Ustr::from(res.as_str()))?;
224 let selection_handicap = row.try_get::<f64, _>("selection_handicap")?;
225 let currency = row
226 .try_get::<String, _>("quote_currency")
227 .map(Currency::from)?;
228 let price_precision = read_u8(row, "price_precision")?;
229 let size_precision = read_u8(row, "size_precision")?;
230 let price_increment = row
231 .try_get::<String, _>("price_increment")
232 .map(Price::from)?;
233 let size_increment = row
234 .try_get::<String, _>("size_increment")
235 .map(Quantity::from)?;
236 let max_quantity = row
237 .try_get::<Option<String>, _>("max_quantity")
238 .ok()
239 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
240 let min_quantity = row
241 .try_get::<Option<String>, _>("min_quantity")
242 .ok()
243 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
244 let max_notional = row
245 .try_get::<Option<String>, _>("max_notional")
246 .ok()
247 .and_then(|res| res.map(|value| Money::from(value.as_str())));
248 let min_notional = row
249 .try_get::<Option<String>, _>("min_notional")
250 .ok()
251 .and_then(|res| res.map(|value| Money::from(value.as_str())));
252 let max_price = row
253 .try_get::<Option<String>, _>("max_price")
254 .ok()
255 .and_then(|res| res.map(|value| Price::from(value.as_str())));
256 let min_price = row
257 .try_get::<Option<String>, _>("min_price")
258 .ok()
259 .and_then(|res| res.map(|value| Price::from(value.as_str())));
260 let margin_init = row
261 .try_get::<String, _>("margin_init")
262 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
263 let margin_maint = row
264 .try_get::<String, _>("margin_maint")
265 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
266 let maker_fee = row
267 .try_get::<String, _>("maker_fee")
268 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
269 let taker_fee = row
270 .try_get::<String, _>("taker_fee")
271 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
272 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
273 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
274
275 let inst = BettingInstrument::new(
276 id,
277 raw_symbol,
278 event_type_id,
279 event_type_name,
280 competition_id,
281 competition_name,
282 event_id,
283 event_name,
284 event_country_code,
285 event_open_date,
286 betting_type,
287 market_id,
288 market_name,
289 market_type,
290 market_start_time,
291 selection_id,
292 selection_name,
293 selection_handicap,
294 currency,
295 price_precision,
296 size_precision,
297 price_increment,
298 size_increment,
299 max_quantity,
300 min_quantity,
301 max_notional,
302 min_notional,
303 max_price,
304 min_price,
305 margin_init,
306 margin_maint,
307 maker_fee,
308 taker_fee,
309 None,
310 None,
311 ts_event,
312 ts_init,
313 );
314 Ok(Self(inst))
315 }
316}
317
318impl<'r> FromRow<'r, PgRow> for BinaryOptionModel {
319 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
320 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
321 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
322 let asset_class = row
323 .try_get::<AssetClassModel, _>("asset_class")
324 .map(|res| res.0)?;
325 let currency = row
326 .try_get::<String, _>("quote_currency")
327 .map(Currency::from)?;
328 let activation_ns = row
329 .try_get::<String, _>("activation_ns")
330 .map(UnixNanos::from)?;
331 let expiration_ns = row
332 .try_get::<String, _>("expiration_ns")
333 .map(UnixNanos::from)?;
334 let price_precision = read_u8(row, "price_precision")?;
335 let size_precision = read_u8(row, "size_precision")?;
336 let price_increment = row
337 .try_get::<String, _>("price_increment")
338 .map(|res| Price::from_str(res.as_str()).unwrap())?;
339 let size_increment = row
340 .try_get::<String, _>("size_increment")
341 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
342 let outcome = row
343 .try_get::<Option<String>, _>("outcome")
344 .ok()
345 .and_then(|res| res.map(|value| Ustr::from(value.as_str())));
346 let description = row
347 .try_get::<Option<String>, _>("description")
348 .ok()
349 .and_then(|res| res.map(|value| Ustr::from(value.as_str())));
350 let max_quantity = row
351 .try_get::<Option<String>, _>("max_quantity")
352 .ok()
353 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
354 let min_quantity = row
355 .try_get::<Option<String>, _>("min_quantity")
356 .ok()
357 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
358 let max_notional = row
359 .try_get::<Option<String>, _>("max_notional")
360 .ok()
361 .and_then(|res| res.map(|value| Money::from(value.as_str())));
362 let min_notional = row
363 .try_get::<Option<String>, _>("min_notional")
364 .ok()
365 .and_then(|res| res.map(|value| Money::from(value.as_str())));
366 let max_price = row
367 .try_get::<Option<String>, _>("max_price")
368 .ok()
369 .and_then(|res| res.map(|value| Price::from(value.as_str())));
370 let min_price = row
371 .try_get::<Option<String>, _>("min_price")
372 .ok()
373 .and_then(|res| res.map(|value| Price::from(value.as_str())));
374 let margin_init = row
375 .try_get::<String, _>("margin_init")
376 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
377 let margin_maint = row
378 .try_get::<String, _>("margin_maint")
379 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
380 let maker_fee = row
381 .try_get::<String, _>("maker_fee")
382 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
383 let taker_fee = row
384 .try_get::<String, _>("taker_fee")
385 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
386 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
387 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
388
389 let inst = BinaryOption::new(
390 id,
391 raw_symbol,
392 asset_class,
393 currency,
394 activation_ns,
395 expiration_ns,
396 price_precision,
397 size_precision,
398 price_increment,
399 size_increment,
400 outcome,
401 description,
402 max_quantity,
403 min_quantity,
404 max_notional,
405 min_notional,
406 max_price,
407 min_price,
408 margin_init,
409 margin_maint,
410 maker_fee,
411 taker_fee,
412 None,
413 None,
414 ts_event,
415 ts_init,
416 );
417 Ok(Self(inst))
418 }
419}
420
421impl<'r> FromRow<'r, PgRow> for CryptoFutureModel {
422 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
423 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
424 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
425 let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
426 let quote_currency = row
427 .try_get::<String, _>("quote_currency")
428 .map(Currency::from)?;
429 let settlement_currency = row
430 .try_get::<String, _>("settlement_currency")
431 .map(Currency::from)?;
432 let is_inverse = row.try_get::<bool, _>("is_inverse")?;
433 let activation_ns = row
434 .try_get::<String, _>("activation_ns")
435 .map(UnixNanos::from)?;
436 let expiration_ns = row
437 .try_get::<String, _>("expiration_ns")
438 .map(UnixNanos::from)?;
439 let price_precision = read_u8(row, "price_precision")?;
440 let size_precision = read_u8(row, "size_precision")?;
441 let price_increment = row
442 .try_get::<String, _>("price_increment")
443 .map(|res| Price::from_str(res.as_str()).unwrap())?;
444 let size_increment = row
445 .try_get::<String, _>("size_increment")
446 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
447 let multiplier = row
448 .try_get::<String, _>("multiplier")
449 .map(|res| Quantity::from(res.as_str()))?;
450 let lot_size = row
451 .try_get::<String, _>("lot_size")
452 .map(|res| Quantity::from(res.as_str()))?;
453 let max_quantity = row
454 .try_get::<Option<String>, _>("max_quantity")
455 .ok()
456 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
457 let min_quantity = row
458 .try_get::<Option<String>, _>("min_quantity")
459 .ok()
460 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
461 let max_notional = row
462 .try_get::<Option<String>, _>("max_notional")
463 .ok()
464 .and_then(|res| res.map(|value| Money::from(value.as_str())));
465 let min_notional = row
466 .try_get::<Option<String>, _>("min_notional")
467 .ok()
468 .and_then(|res| res.map(|value| Money::from(value.as_str())));
469 let max_price = row
470 .try_get::<Option<String>, _>("max_price")
471 .ok()
472 .and_then(|res| res.map(|value| Price::from(value.as_str())));
473 let min_price = row
474 .try_get::<Option<String>, _>("min_price")
475 .ok()
476 .and_then(|res| res.map(|value| Price::from(value.as_str())));
477 let margin_init = row
478 .try_get::<String, _>("margin_init")
479 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
480 let margin_maint = row
481 .try_get::<String, _>("margin_maint")
482 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
483 let maker_fee = row
484 .try_get::<String, _>("maker_fee")
485 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
486 let taker_fee = row
487 .try_get::<String, _>("taker_fee")
488 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
489 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
490 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
491
492 let inst = CryptoFuture::new(
493 id,
494 raw_symbol,
495 underlying,
496 quote_currency,
497 settlement_currency,
498 is_inverse,
499 activation_ns,
500 expiration_ns,
501 price_precision,
502 size_precision,
503 price_increment,
504 size_increment,
505 Some(multiplier),
506 Some(lot_size),
507 max_quantity,
508 min_quantity,
509 max_notional,
510 min_notional,
511 max_price,
512 min_price,
513 margin_init,
514 margin_maint,
515 maker_fee,
516 taker_fee,
517 None,
518 None,
519 ts_event,
520 ts_init,
521 );
522 Ok(Self(inst))
523 }
524}
525
526impl<'r> FromRow<'r, PgRow> for CryptoOptionModel {
527 #[expect(
528 clippy::too_many_lines,
529 reason = "SQL row mapping mirrors the full crypto option constructor"
530 )]
531 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
532 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
533 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
534 let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
535 let quote_currency = row
536 .try_get::<String, _>("quote_currency")
537 .map(Currency::from)?;
538 let settlement_currency = row
539 .try_get::<String, _>("settlement_currency")
540 .map(Currency::from)?;
541 let is_inverse = row.try_get::<bool, _>("is_inverse")?;
542 let option_kind = row
543 .try_get::<String, _>("option_kind")
544 .map(|res| OptionKind::from_str(res.as_str()).unwrap())?;
545 let strike_price = row
546 .try_get::<String, _>("strike_price")
547 .map(|res| Price::from_str(res.as_str()).unwrap())?;
548 let activation_ns = row
549 .try_get::<String, _>("activation_ns")
550 .map(UnixNanos::from)?;
551 let expiration_ns = row
552 .try_get::<String, _>("expiration_ns")
553 .map(UnixNanos::from)?;
554 let price_precision = read_u8(row, "price_precision")?;
555 let size_precision = read_u8(row, "size_precision")?;
556 let price_increment = row
557 .try_get::<String, _>("price_increment")
558 .map(|res| Price::from_str(res.as_str()).unwrap())?;
559 let size_increment = row
560 .try_get::<String, _>("size_increment")
561 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
562 let multiplier = row
563 .try_get::<String, _>("multiplier")
564 .map(|res| Quantity::from(res.as_str()))?;
565 let lot_size = row
566 .try_get::<String, _>("lot_size")
567 .map(|res| Quantity::from(res.as_str()))?;
568 let max_quantity = row
569 .try_get::<Option<String>, _>("max_quantity")
570 .ok()
571 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
572 let min_quantity = row
573 .try_get::<Option<String>, _>("min_quantity")
574 .ok()
575 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
576 let max_notional = row
577 .try_get::<Option<String>, _>("max_notional")
578 .ok()
579 .and_then(|res| res.map(|value| Money::from(value.as_str())));
580 let min_notional = row
581 .try_get::<Option<String>, _>("min_notional")
582 .ok()
583 .and_then(|res| res.map(|value| Money::from(value.as_str())));
584 let max_price = row
585 .try_get::<Option<String>, _>("max_price")
586 .ok()
587 .and_then(|res| res.map(|value| Price::from(value.as_str())));
588 let min_price = row
589 .try_get::<Option<String>, _>("min_price")
590 .ok()
591 .and_then(|res| res.map(|value| Price::from(value.as_str())));
592 let margin_init = row
593 .try_get::<String, _>("margin_init")
594 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
595 let margin_maint = row
596 .try_get::<String, _>("margin_maint")
597 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
598 let maker_fee = row
599 .try_get::<String, _>("maker_fee")
600 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
601 let taker_fee = row
602 .try_get::<String, _>("taker_fee")
603 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
604 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
605 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
606
607 let inst = CryptoOption::new(
608 id,
609 raw_symbol,
610 underlying,
611 quote_currency,
612 settlement_currency,
613 is_inverse,
614 option_kind,
615 strike_price,
616 activation_ns,
617 expiration_ns,
618 price_precision,
619 size_precision,
620 price_increment,
621 size_increment,
622 Some(multiplier),
623 Some(lot_size),
624 max_quantity,
625 min_quantity,
626 max_notional,
627 min_notional,
628 max_price,
629 min_price,
630 margin_init,
631 margin_maint,
632 maker_fee,
633 taker_fee,
634 None,
635 None,
636 ts_event,
637 ts_init,
638 );
639 Ok(Self(inst))
640 }
641}
642
643impl<'r> FromRow<'r, PgRow> for CryptoPerpetualModel {
644 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
645 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
646 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
647 let base_currency = row
648 .try_get::<String, _>("base_currency")
649 .map(Currency::from)?;
650 let quote_currency = row
651 .try_get::<String, _>("quote_currency")
652 .map(Currency::from)?;
653 let settlement_currency = row
654 .try_get::<String, _>("settlement_currency")
655 .map(Currency::from)?;
656 let is_inverse = row.try_get::<bool, _>("is_inverse")?;
657 let price_precision = read_u8(row, "price_precision")?;
658 let size_precision = read_u8(row, "size_precision")?;
659 let price_increment = row
660 .try_get::<String, _>("price_increment")
661 .map(|res| Price::from_str(res.as_str()).unwrap())?;
662 let size_increment = row
663 .try_get::<String, _>("size_increment")
664 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
665 let multiplier = row
666 .try_get::<String, _>("multiplier")
667 .map(|res| Quantity::from(res.as_str()))?;
668 let lot_size = row
669 .try_get::<String, _>("lot_size")
670 .map(|res| Quantity::from(res.as_str()))?;
671 let max_quantity = row
672 .try_get::<Option<String>, _>("max_quantity")
673 .ok()
674 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
675 let min_quantity = row
676 .try_get::<Option<String>, _>("min_quantity")
677 .ok()
678 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
679 let max_notional = row
680 .try_get::<Option<String>, _>("max_notional")
681 .ok()
682 .and_then(|res| res.map(|res| Money::from(res.as_str())));
683 let min_notional = row
684 .try_get::<Option<String>, _>("min_notional")
685 .ok()
686 .and_then(|res| res.map(|res| Money::from(res.as_str())));
687 let max_price = row
688 .try_get::<Option<String>, _>("max_price")
689 .ok()
690 .and_then(|res| res.map(|res| Price::from(res.as_str())));
691 let min_price = row
692 .try_get::<Option<String>, _>("min_price")
693 .ok()
694 .and_then(|res| res.map(|res| Price::from(res.as_str())));
695 let margin_init = row
696 .try_get::<String, _>("margin_init")
697 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
698 let margin_maint = row
699 .try_get::<String, _>("margin_maint")
700 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
701 let maker_fee = row
702 .try_get::<String, _>("maker_fee")
703 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
704 let taker_fee = row
705 .try_get::<String, _>("taker_fee")
706 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
707 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
708 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
709 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
710
711 let inst = CryptoPerpetual::new(
712 id,
713 raw_symbol,
714 base_currency,
715 quote_currency,
716 settlement_currency,
717 is_inverse,
718 price_precision,
719 size_precision,
720 price_increment,
721 size_increment,
722 Some(multiplier),
723 Some(lot_size),
724 max_quantity,
725 min_quantity,
726 max_notional,
727 min_notional,
728 max_price,
729 min_price,
730 margin_init,
731 margin_maint,
732 maker_fee,
733 taker_fee,
734 None,
735 None,
736 ts_event,
737 ts_init,
738 );
739 Ok(Self(inst))
740 }
741}
742
743impl<'r> FromRow<'r, PgRow> for CurrencyPairModel {
744 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
745 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
746 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
747 let base_currency = row
748 .try_get::<String, _>("base_currency")
749 .map(Currency::from)?;
750 let quote_currency = row
751 .try_get::<String, _>("quote_currency")
752 .map(Currency::from)?;
753 let price_precision = read_u8(row, "price_precision")?;
754 let size_precision = read_u8(row, "size_precision")?;
755 let price_increment = row
756 .try_get::<String, _>("price_increment")
757 .map(|res| Price::from(res.as_str()))?;
758 let size_increment = row
759 .try_get::<String, _>("size_increment")
760 .map(|res| Quantity::from(res.as_str()))?;
761 let multiplier = row
762 .try_get::<Option<String>, _>("multiplier")
763 .ok()
764 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
765 let lot_size = row
766 .try_get::<Option<String>, _>("lot_size")
767 .ok()
768 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
769 let max_quantity = row
770 .try_get::<Option<String>, _>("max_quantity")
771 .ok()
772 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
773 let min_quantity = row
774 .try_get::<Option<String>, _>("min_quantity")
775 .ok()
776 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
777 let max_notional = row
778 .try_get::<Option<String>, _>("max_notional")
779 .ok()
780 .and_then(|res| res.map(|res| Money::from(res.as_str())));
781 let min_notional = row
782 .try_get::<Option<String>, _>("min_notional")
783 .ok()
784 .and_then(|res| res.map(|res| Money::from(res.as_str())));
785 let max_price = row
786 .try_get::<Option<String>, _>("max_price")
787 .ok()
788 .and_then(|res| res.map(|res| Price::from(res.as_str())));
789 let min_price = row
790 .try_get::<Option<String>, _>("min_price")
791 .ok()
792 .and_then(|res| res.map(|res| Price::from(res.as_str())));
793 let margin_init = row
794 .try_get::<String, _>("margin_init")
795 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
796 let margin_maint = row
797 .try_get::<String, _>("margin_maint")
798 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
799 let maker_fee = row
800 .try_get::<String, _>("maker_fee")
801 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
802 let taker_fee = row
803 .try_get::<String, _>("taker_fee")
804 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
805 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
806 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
807
808 let inst = CurrencyPair::new(
809 id,
810 raw_symbol,
811 base_currency,
812 quote_currency,
813 price_precision,
814 size_precision,
815 price_increment,
816 size_increment,
817 multiplier,
818 lot_size,
819 max_quantity,
820 min_quantity,
821 max_notional,
822 min_notional,
823 max_price,
824 min_price,
825 margin_init,
826 margin_maint,
827 maker_fee,
828 taker_fee,
829 None,
830 None,
831 ts_event,
832 ts_init,
833 );
834 Ok(Self(inst))
835 }
836}
837
838impl<'r> FromRow<'r, PgRow> for EquityModel {
839 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
840 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
841 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
842 let isin = row
843 .try_get::<Option<String>, _>("isin")
844 .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
845 let currency = row
846 .try_get::<String, _>("quote_currency")
847 .map(Currency::from)?;
848 let price_precision = read_u8(row, "price_precision")?;
849 let price_increment = row
850 .try_get::<String, _>("price_increment")
851 .map(|res| Price::from_str(res.as_str()).unwrap())?;
852 let lot_size = row
853 .try_get::<Option<String>, _>("lot_size")
854 .map(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()))?;
855 let max_quantity = row
856 .try_get::<Option<String>, _>("max_quantity")
857 .ok()
858 .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
859 let min_quantity = row
860 .try_get::<Option<String>, _>("min_quantity")
861 .ok()
862 .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
863 let max_price = row
864 .try_get::<Option<String>, _>("max_price")
865 .ok()
866 .and_then(|res| res.map(|s| Price::from(s.as_str())));
867 let min_price = row
868 .try_get::<Option<String>, _>("min_price")
869 .ok()
870 .and_then(|res| res.map(|s| Price::from(s.as_str())));
871 let margin_init = row
872 .try_get::<String, _>("margin_init")
873 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
874 let margin_maint = row
875 .try_get::<String, _>("margin_maint")
876 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
877 let maker_fee = row
878 .try_get::<String, _>("maker_fee")
879 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
880 let taker_fee = row
881 .try_get::<String, _>("taker_fee")
882 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
883 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
884 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
885
886 let inst = Equity::new(
887 id,
888 raw_symbol,
889 isin,
890 currency,
891 price_precision,
892 price_increment,
893 lot_size,
894 max_quantity,
895 min_quantity,
896 max_price,
897 min_price,
898 margin_init,
899 margin_maint,
900 maker_fee,
901 taker_fee,
902 None,
903 None,
904 ts_event,
905 ts_init,
906 );
907 Ok(Self(inst))
908 }
909}
910
911impl<'r> FromRow<'r, PgRow> for FuturesContractModel {
912 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
913 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
914 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
915 let asset_class = row
916 .try_get::<AssetClassModel, _>("asset_class")
917 .map(|res| res.0)?;
918 let exchange = row
919 .try_get::<Option<String>, _>("exchange")
920 .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
921 let underlying = row
922 .try_get::<String, _>("underlying")
923 .map(|res| Ustr::from(res.as_str()))?;
924 let currency = row
925 .try_get::<String, _>("quote_currency")
926 .map(Currency::from)?;
927 let activation_ns = row
928 .try_get::<String, _>("activation_ns")
929 .map(UnixNanos::from)?;
930 let expiration_ns = row
931 .try_get::<String, _>("expiration_ns")
932 .map(UnixNanos::from)?;
933 let price_precision = read_u8(row, "price_precision")?;
934 let price_increment = row
935 .try_get::<String, _>("price_increment")
936 .map(|res| Price::from(res.as_str()))?;
937 let multiplier = row
938 .try_get::<String, _>("multiplier")
939 .map(|res| Quantity::from(res.as_str()))?;
940 let lot_size = row
941 .try_get::<String, _>("lot_size")
942 .map(|res| Quantity::from(res.as_str()))?;
943 let max_quantity = row
944 .try_get::<Option<String>, _>("max_quantity")
945 .ok()
946 .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
947 let min_quantity = row
948 .try_get::<Option<String>, _>("min_quantity")
949 .ok()
950 .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
951 let max_price = row
952 .try_get::<Option<String>, _>("max_price")
953 .ok()
954 .and_then(|res| res.map(|s| Price::from(s.as_str())));
955 let min_price = row
956 .try_get::<Option<String>, _>("min_price")
957 .ok()
958 .and_then(|res| res.map(|s| Price::from(s.as_str())));
959 let margin_init = row
960 .try_get::<String, _>("margin_init")
961 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
962 let margin_maint = row
963 .try_get::<String, _>("margin_maint")
964 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
965 let maker_fee = row
966 .try_get::<String, _>("maker_fee")
967 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
968 let taker_fee = row
969 .try_get::<String, _>("taker_fee")
970 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
971 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
972 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
973
974 let inst = FuturesContract::new(
975 id,
976 raw_symbol,
977 asset_class,
978 exchange,
979 underlying,
980 activation_ns,
981 expiration_ns,
982 currency,
983 price_precision,
984 price_increment,
985 multiplier,
986 lot_size,
987 max_quantity,
988 min_quantity,
989 max_price,
990 min_price,
991 margin_init,
992 margin_maint,
993 maker_fee,
994 taker_fee,
995 None,
996 None,
997 ts_event,
998 ts_init,
999 );
1000 Ok(Self(inst))
1001 }
1002}
1003
1004impl<'r> FromRow<'r, PgRow> for FuturesSpreadModel {
1005 fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
1006 todo!("Implement FromRow for FuturesSpread")
1007 }
1008}
1009
1010impl<'r> FromRow<'r, PgRow> for OptionContractModel {
1011 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1012 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1013 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
1014 let asset_class = row
1015 .try_get::<AssetClassModel, _>("asset_class")
1016 .map(|res| res.0)?;
1017 let exchange = row
1018 .try_get::<Option<String>, _>("exchange")
1019 .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
1020 let underlying = row
1021 .try_get::<String, _>("underlying")
1022 .map(|res| Ustr::from(res.as_str()))?;
1023 let option_kind = row
1024 .try_get::<String, _>("option_kind")
1025 .map(|res| OptionKind::from_str(res.as_str()).unwrap())?;
1026 let activation_ns = row
1027 .try_get::<String, _>("activation_ns")
1028 .map(UnixNanos::from)?;
1029 let expiration_ns = row
1030 .try_get::<String, _>("expiration_ns")
1031 .map(UnixNanos::from)?;
1032 let strike_price = row
1033 .try_get::<String, _>("strike_price")
1034 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1035 let currency = row
1036 .try_get::<String, _>("quote_currency")
1037 .map(Currency::from)?;
1038 let price_precision = read_u8(row, "price_precision")?;
1039 let price_increment = row
1040 .try_get::<String, _>("price_increment")
1041 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1042 let multiplier = row
1043 .try_get::<String, _>("multiplier")
1044 .map(|res| Quantity::from(res.as_str()))?;
1045 let lot_size = row
1046 .try_get::<String, _>("lot_size")
1047 .map(|res| Quantity::from(res.as_str()))
1048 .unwrap();
1049 let max_quantity = row
1050 .try_get::<Option<String>, _>("max_quantity")
1051 .ok()
1052 .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
1053 let min_quantity = row
1054 .try_get::<Option<String>, _>("min_quantity")
1055 .ok()
1056 .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
1057 let max_price = row
1058 .try_get::<Option<String>, _>("max_price")
1059 .ok()
1060 .and_then(|res| res.map(|s| Price::from(s.as_str())));
1061 let min_price = row
1062 .try_get::<Option<String>, _>("min_price")
1063 .ok()
1064 .and_then(|res| res.map(|s| Price::from(s.as_str())));
1065 let margin_init = row
1066 .try_get::<String, _>("margin_init")
1067 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1068 let margin_maint = row
1069 .try_get::<String, _>("margin_maint")
1070 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1071 let maker_fee = row
1072 .try_get::<String, _>("maker_fee")
1073 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1074 let taker_fee = row
1075 .try_get::<String, _>("taker_fee")
1076 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1077 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1078 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1079
1080 let inst = OptionContract::new(
1081 id,
1082 raw_symbol,
1083 asset_class,
1084 exchange,
1085 underlying,
1086 option_kind,
1087 strike_price,
1088 currency,
1089 activation_ns,
1090 expiration_ns,
1091 price_precision,
1092 price_increment,
1093 multiplier,
1094 lot_size,
1095 max_quantity,
1096 min_quantity,
1097 max_price,
1098 min_price,
1099 margin_init,
1100 margin_maint,
1101 maker_fee,
1102 taker_fee,
1103 None,
1104 None,
1105 ts_event,
1106 ts_init,
1107 );
1108 Ok(Self(inst))
1109 }
1110}
1111
1112impl<'r> FromRow<'r, PgRow> for CommodityModel {
1113 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1114 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1115 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
1116 let asset_class = row
1117 .try_get::<AssetClassModel, _>("asset_class")
1118 .map(|res| res.0)?;
1119 let quote_currency = row
1120 .try_get::<String, _>("quote_currency")
1121 .map(Currency::from)?;
1122 let price_precision = read_u8(row, "price_precision")?;
1123 let size_precision = read_u8(row, "size_precision")?;
1124 let price_increment = row
1125 .try_get::<String, _>("price_increment")
1126 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1127 let size_increment = row
1128 .try_get::<String, _>("size_increment")
1129 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
1130 let lot_size = row
1131 .try_get::<Option<String>, _>("lot_size")
1132 .ok()
1133 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1134 let max_quantity = row
1135 .try_get::<Option<String>, _>("max_quantity")
1136 .ok()
1137 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1138 let min_quantity = row
1139 .try_get::<Option<String>, _>("min_quantity")
1140 .ok()
1141 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1142 let max_notional = row
1143 .try_get::<Option<String>, _>("max_notional")
1144 .ok()
1145 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1146 let min_notional = row
1147 .try_get::<Option<String>, _>("min_notional")
1148 .ok()
1149 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1150 let max_price = row
1151 .try_get::<Option<String>, _>("max_price")
1152 .ok()
1153 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1154 let min_price = row
1155 .try_get::<Option<String>, _>("min_price")
1156 .ok()
1157 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1158 let margin_init = row
1159 .try_get::<String, _>("margin_init")
1160 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1161 let margin_maint = row
1162 .try_get::<String, _>("margin_maint")
1163 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1164 let maker_fee = row
1165 .try_get::<String, _>("maker_fee")
1166 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1167 let taker_fee = row
1168 .try_get::<String, _>("taker_fee")
1169 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1170 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1171 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1172
1173 let inst = Commodity::new(
1174 id,
1175 raw_symbol,
1176 asset_class,
1177 quote_currency,
1178 price_precision,
1179 size_precision,
1180 price_increment,
1181 size_increment,
1182 lot_size,
1183 max_quantity,
1184 min_quantity,
1185 max_notional,
1186 min_notional,
1187 max_price,
1188 min_price,
1189 margin_init,
1190 margin_maint,
1191 maker_fee,
1192 taker_fee,
1193 None,
1194 None,
1195 ts_event,
1196 ts_init,
1197 );
1198 Ok(Self(inst))
1199 }
1200}
1201
1202impl<'r> FromRow<'r, PgRow> for IndexInstrumentModel {
1203 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1204 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1205 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
1206 let currency = row
1207 .try_get::<String, _>("quote_currency")
1208 .map(Currency::from)?;
1209 let price_precision = read_u8(row, "price_precision")?;
1210 let size_precision = read_u8(row, "size_precision")?;
1211 let price_increment = row
1212 .try_get::<String, _>("price_increment")
1213 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1214 let size_increment = row
1215 .try_get::<String, _>("size_increment")
1216 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
1217 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1218 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1219
1220 let inst = IndexInstrument::new(
1221 id,
1222 raw_symbol,
1223 currency,
1224 price_precision,
1225 size_precision,
1226 price_increment,
1227 size_increment,
1228 None,
1229 None,
1230 ts_event,
1231 ts_init,
1232 );
1233 Ok(Self(inst))
1234 }
1235}
1236
1237impl<'r> FromRow<'r, PgRow> for CfdModel {
1238 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1239 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1240 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
1241 let asset_class = row
1242 .try_get::<AssetClassModel, _>("asset_class")
1243 .map(|res| res.0)?;
1244 let base_currency = row
1245 .try_get::<Option<String>, _>("base_currency")
1246 .ok()
1247 .and_then(|res| res.map(Currency::from));
1248 let quote_currency = row
1249 .try_get::<String, _>("quote_currency")
1250 .map(Currency::from)?;
1251 let price_precision = read_u8(row, "price_precision")?;
1252 let size_precision = read_u8(row, "size_precision")?;
1253 let price_increment = row
1254 .try_get::<String, _>("price_increment")
1255 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1256 let size_increment = row
1257 .try_get::<String, _>("size_increment")
1258 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
1259 let lot_size = row
1260 .try_get::<Option<String>, _>("lot_size")
1261 .ok()
1262 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1263 let max_quantity = row
1264 .try_get::<Option<String>, _>("max_quantity")
1265 .ok()
1266 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1267 let min_quantity = row
1268 .try_get::<Option<String>, _>("min_quantity")
1269 .ok()
1270 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1271 let max_notional = row
1272 .try_get::<Option<String>, _>("max_notional")
1273 .ok()
1274 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1275 let min_notional = row
1276 .try_get::<Option<String>, _>("min_notional")
1277 .ok()
1278 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1279 let max_price = row
1280 .try_get::<Option<String>, _>("max_price")
1281 .ok()
1282 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1283 let min_price = row
1284 .try_get::<Option<String>, _>("min_price")
1285 .ok()
1286 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1287 let margin_init = row
1288 .try_get::<String, _>("margin_init")
1289 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1290 let margin_maint = row
1291 .try_get::<String, _>("margin_maint")
1292 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1293 let maker_fee = row
1294 .try_get::<String, _>("maker_fee")
1295 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1296 let taker_fee = row
1297 .try_get::<String, _>("taker_fee")
1298 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1299 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1300 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1301
1302 let inst = Cfd::new(
1303 id,
1304 raw_symbol,
1305 asset_class,
1306 base_currency,
1307 quote_currency,
1308 price_precision,
1309 size_precision,
1310 price_increment,
1311 size_increment,
1312 lot_size,
1313 max_quantity,
1314 min_quantity,
1315 max_notional,
1316 min_notional,
1317 max_price,
1318 min_price,
1319 margin_init,
1320 margin_maint,
1321 maker_fee,
1322 taker_fee,
1323 None,
1324 None,
1325 ts_event,
1326 ts_init,
1327 );
1328 Ok(Self(inst))
1329 }
1330}
1331
1332impl<'r> FromRow<'r, PgRow> for PerpetualContractModel {
1333 #[expect(
1334 clippy::too_many_lines,
1335 reason = "SQL row mapping mirrors the full perpetual contract constructor"
1336 )]
1337 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1338 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1339 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
1340 let underlying = row
1341 .try_get::<String, _>("underlying")
1342 .map(|res| Ustr::from(res.as_str()))?;
1343 let asset_class = row
1344 .try_get::<AssetClassModel, _>("asset_class")
1345 .map(|res| res.0)?;
1346 let base_currency = row
1347 .try_get::<Option<String>, _>("base_currency")
1348 .ok()
1349 .and_then(|res| res.map(Currency::from));
1350 let quote_currency = row
1351 .try_get::<String, _>("quote_currency")
1352 .map(Currency::from)?;
1353 let settlement_currency = row
1354 .try_get::<String, _>("settlement_currency")
1355 .map(Currency::from)?;
1356 let is_inverse = row.try_get::<bool, _>("is_inverse")?;
1357 let price_precision = read_u8(row, "price_precision")?;
1358 let size_precision = read_u8(row, "size_precision")?;
1359 let price_increment = row
1360 .try_get::<String, _>("price_increment")
1361 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1362 let size_increment = row
1363 .try_get::<String, _>("size_increment")
1364 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
1365 let multiplier = row
1366 .try_get::<String, _>("multiplier")
1367 .map(|res| Quantity::from(res.as_str()))?;
1368 let lot_size = row
1369 .try_get::<String, _>("lot_size")
1370 .map(|res| Quantity::from(res.as_str()))?;
1371 let max_quantity = row
1372 .try_get::<Option<String>, _>("max_quantity")
1373 .ok()
1374 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1375 let min_quantity = row
1376 .try_get::<Option<String>, _>("min_quantity")
1377 .ok()
1378 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1379 let max_notional = row
1380 .try_get::<Option<String>, _>("max_notional")
1381 .ok()
1382 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1383 let min_notional = row
1384 .try_get::<Option<String>, _>("min_notional")
1385 .ok()
1386 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1387 let max_price = row
1388 .try_get::<Option<String>, _>("max_price")
1389 .ok()
1390 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1391 let min_price = row
1392 .try_get::<Option<String>, _>("min_price")
1393 .ok()
1394 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1395 let margin_init = row
1396 .try_get::<String, _>("margin_init")
1397 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1398 let margin_maint = row
1399 .try_get::<String, _>("margin_maint")
1400 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1401 let maker_fee = row
1402 .try_get::<String, _>("maker_fee")
1403 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1404 let taker_fee = row
1405 .try_get::<String, _>("taker_fee")
1406 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1407 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1408 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1409
1410 let inst = PerpetualContract::new(
1411 id,
1412 raw_symbol,
1413 underlying,
1414 asset_class,
1415 base_currency,
1416 quote_currency,
1417 settlement_currency,
1418 is_inverse,
1419 price_precision,
1420 size_precision,
1421 price_increment,
1422 size_increment,
1423 Some(multiplier),
1424 Some(lot_size),
1425 max_quantity,
1426 min_quantity,
1427 max_notional,
1428 min_notional,
1429 max_price,
1430 min_price,
1431 margin_init,
1432 margin_maint,
1433 maker_fee,
1434 taker_fee,
1435 None,
1436 None,
1437 ts_event,
1438 ts_init,
1439 );
1440 Ok(Self(inst))
1441 }
1442}
1443
1444impl<'r> FromRow<'r, PgRow> for OptionSpreadModel {
1445 fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
1446 todo!("Implement FromRow for OptionSpread")
1447 }
1448}
1449
1450impl<'r> FromRow<'r, PgRow> for CryptoFuturesSpreadModel {
1451 #[expect(
1452 clippy::too_many_lines,
1453 reason = "SQL row mapping mirrors the full crypto futures spread constructor"
1454 )]
1455 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1456 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1457 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
1458 let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
1459 let quote_currency = row
1460 .try_get::<String, _>("quote_currency")
1461 .map(Currency::from)?;
1462 let settlement_currency = row
1463 .try_get::<String, _>("settlement_currency")
1464 .map(Currency::from)?;
1465 let is_inverse = row.try_get::<bool, _>("is_inverse")?;
1466 let strategy_type = row
1467 .try_get::<String, _>("strategy_type")
1468 .map(|res| Ustr::from(res.as_str()))?;
1469 let activation_ns = row
1470 .try_get::<String, _>("activation_ns")
1471 .map(UnixNanos::from)?;
1472 let expiration_ns = row
1473 .try_get::<String, _>("expiration_ns")
1474 .map(UnixNanos::from)?;
1475 let price_precision = read_u8(row, "price_precision")?;
1476 let size_precision = read_u8(row, "size_precision")?;
1477 let price_increment = row
1478 .try_get::<String, _>("price_increment")
1479 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1480 let size_increment = row
1481 .try_get::<String, _>("size_increment")
1482 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
1483 let multiplier = row
1484 .try_get::<String, _>("multiplier")
1485 .map(|res| Quantity::from(res.as_str()))?;
1486 let lot_size = row
1487 .try_get::<String, _>("lot_size")
1488 .map(|res| Quantity::from(res.as_str()))?;
1489 let max_quantity = row
1490 .try_get::<Option<String>, _>("max_quantity")
1491 .ok()
1492 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1493 let min_quantity = row
1494 .try_get::<Option<String>, _>("min_quantity")
1495 .ok()
1496 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1497 let max_notional = row
1498 .try_get::<Option<String>, _>("max_notional")
1499 .ok()
1500 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1501 let min_notional = row
1502 .try_get::<Option<String>, _>("min_notional")
1503 .ok()
1504 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1505 let max_price = row
1506 .try_get::<Option<String>, _>("max_price")
1507 .ok()
1508 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1509 let min_price = row
1510 .try_get::<Option<String>, _>("min_price")
1511 .ok()
1512 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1513 let margin_init = row
1514 .try_get::<String, _>("margin_init")
1515 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1516 let margin_maint = row
1517 .try_get::<String, _>("margin_maint")
1518 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1519 let maker_fee = row
1520 .try_get::<String, _>("maker_fee")
1521 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1522 let taker_fee = row
1523 .try_get::<String, _>("taker_fee")
1524 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1525 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1526 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1527
1528 let inst = CryptoFuturesSpread::new(
1529 id,
1530 raw_symbol,
1531 underlying,
1532 quote_currency,
1533 settlement_currency,
1534 is_inverse,
1535 strategy_type,
1536 activation_ns,
1537 expiration_ns,
1538 price_precision,
1539 size_precision,
1540 price_increment,
1541 size_increment,
1542 Some(multiplier),
1543 Some(lot_size),
1544 max_quantity,
1545 min_quantity,
1546 max_notional,
1547 min_notional,
1548 max_price,
1549 min_price,
1550 margin_init,
1551 margin_maint,
1552 maker_fee,
1553 taker_fee,
1554 None,
1555 None,
1556 ts_event,
1557 ts_init,
1558 );
1559 Ok(Self(inst))
1560 }
1561}
1562
1563impl<'r> FromRow<'r, PgRow> for CryptoOptionSpreadModel {
1564 #[expect(
1565 clippy::too_many_lines,
1566 reason = "SQL row mapping mirrors the full crypto option spread constructor"
1567 )]
1568 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1569 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1570 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
1571 let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
1572 let quote_currency = row
1573 .try_get::<String, _>("quote_currency")
1574 .map(Currency::from)?;
1575 let settlement_currency = row
1576 .try_get::<String, _>("settlement_currency")
1577 .map(Currency::from)?;
1578 let is_inverse = row.try_get::<bool, _>("is_inverse")?;
1579 let strategy_type = row
1580 .try_get::<String, _>("strategy_type")
1581 .map(|res| Ustr::from(res.as_str()))?;
1582 let activation_ns = row
1583 .try_get::<String, _>("activation_ns")
1584 .map(UnixNanos::from)?;
1585 let expiration_ns = row
1586 .try_get::<String, _>("expiration_ns")
1587 .map(UnixNanos::from)?;
1588 let price_precision = read_u8(row, "price_precision")?;
1589 let size_precision = read_u8(row, "size_precision")?;
1590 let price_increment = row
1591 .try_get::<String, _>("price_increment")
1592 .map(|res| Price::from_str(res.as_str()).unwrap())?;
1593 let size_increment = row
1594 .try_get::<String, _>("size_increment")
1595 .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
1596 let multiplier = row
1597 .try_get::<String, _>("multiplier")
1598 .map(|res| Quantity::from(res.as_str()))?;
1599 let lot_size = row
1600 .try_get::<String, _>("lot_size")
1601 .map(|res| Quantity::from(res.as_str()))?;
1602 let max_quantity = row
1603 .try_get::<Option<String>, _>("max_quantity")
1604 .ok()
1605 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1606 let min_quantity = row
1607 .try_get::<Option<String>, _>("min_quantity")
1608 .ok()
1609 .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
1610 let max_notional = row
1611 .try_get::<Option<String>, _>("max_notional")
1612 .ok()
1613 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1614 let min_notional = row
1615 .try_get::<Option<String>, _>("min_notional")
1616 .ok()
1617 .and_then(|res| res.map(|value| Money::from(value.as_str())));
1618 let max_price = row
1619 .try_get::<Option<String>, _>("max_price")
1620 .ok()
1621 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1622 let min_price = row
1623 .try_get::<Option<String>, _>("min_price")
1624 .ok()
1625 .and_then(|res| res.map(|value| Price::from(value.as_str())));
1626 let margin_init = row
1627 .try_get::<String, _>("margin_init")
1628 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1629 let margin_maint = row
1630 .try_get::<String, _>("margin_maint")
1631 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1632 let maker_fee = row
1633 .try_get::<String, _>("maker_fee")
1634 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1635 let taker_fee = row
1636 .try_get::<String, _>("taker_fee")
1637 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1638 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1639 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1640
1641 let inst = CryptoOptionSpread::new(
1642 id,
1643 raw_symbol,
1644 underlying,
1645 quote_currency,
1646 settlement_currency,
1647 is_inverse,
1648 strategy_type,
1649 activation_ns,
1650 expiration_ns,
1651 price_precision,
1652 size_precision,
1653 price_increment,
1654 size_increment,
1655 Some(multiplier),
1656 Some(lot_size),
1657 max_quantity,
1658 min_quantity,
1659 max_notional,
1660 min_notional,
1661 max_price,
1662 min_price,
1663 margin_init,
1664 margin_maint,
1665 maker_fee,
1666 taker_fee,
1667 None,
1668 None,
1669 ts_event,
1670 ts_init,
1671 );
1672 Ok(Self(inst))
1673 }
1674}
1675
1676impl<'r> FromRow<'r, PgRow> for TokenizedAssetModel {
1677 fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
1678 let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
1679 let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
1680 let asset_class = row.try_get::<String, _>("asset_class").and_then(|res| {
1681 AssetClass::from_str(res.as_str()).map_err(|e| {
1682 sqlx::Error::Decode(format!("Invalid asset class '{res}': {e}").into())
1683 })
1684 })?;
1685 let base_currency = row
1688 .try_get::<String, _>("base_currency")
1689 .map(|code| Currency::get_or_create_crypto(&code))?;
1690 let quote_currency = row
1691 .try_get::<String, _>("quote_currency")
1692 .map(Currency::from)?;
1693 let isin = row
1694 .try_get::<Option<String>, _>("isin")
1695 .ok()
1696 .and_then(|res| res.map(|s| Ustr::from(s.as_str())));
1697 let price_precision = read_u8(row, "price_precision")?;
1698 let size_precision = read_u8(row, "size_precision")?;
1699 let price_increment = row
1700 .try_get::<String, _>("price_increment")
1701 .map(|res| Price::from(res.as_str()))?;
1702 let size_increment = row
1703 .try_get::<String, _>("size_increment")
1704 .map(|res| Quantity::from(res.as_str()))?;
1705 let multiplier = row
1706 .try_get::<Option<String>, _>("multiplier")
1707 .ok()
1708 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
1709 let lot_size = row
1710 .try_get::<Option<String>, _>("lot_size")
1711 .ok()
1712 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
1713 let max_quantity = row
1714 .try_get::<Option<String>, _>("max_quantity")
1715 .ok()
1716 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
1717 let min_quantity = row
1718 .try_get::<Option<String>, _>("min_quantity")
1719 .ok()
1720 .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
1721 let max_notional = row
1722 .try_get::<Option<String>, _>("max_notional")
1723 .ok()
1724 .and_then(|res| res.map(|res| Money::from(res.as_str())));
1725 let min_notional = row
1726 .try_get::<Option<String>, _>("min_notional")
1727 .ok()
1728 .and_then(|res| res.map(|res| Money::from(res.as_str())));
1729 let max_price = row
1730 .try_get::<Option<String>, _>("max_price")
1731 .ok()
1732 .and_then(|res| res.map(|res| Price::from(res.as_str())));
1733 let min_price = row
1734 .try_get::<Option<String>, _>("min_price")
1735 .ok()
1736 .and_then(|res| res.map(|res| Price::from(res.as_str())));
1737 let margin_init = row
1738 .try_get::<String, _>("margin_init")
1739 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1740 let margin_maint = row
1741 .try_get::<String, _>("margin_maint")
1742 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1743 let maker_fee = row
1744 .try_get::<String, _>("maker_fee")
1745 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1746 let taker_fee = row
1747 .try_get::<String, _>("taker_fee")
1748 .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1749 let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1750 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1751
1752 let inst = TokenizedAsset::new(
1753 id,
1754 raw_symbol,
1755 asset_class,
1756 base_currency,
1757 quote_currency,
1758 isin,
1759 price_precision,
1760 size_precision,
1761 price_increment,
1762 size_increment,
1763 multiplier,
1764 lot_size,
1765 max_quantity,
1766 min_quantity,
1767 max_notional,
1768 min_notional,
1769 max_price,
1770 min_price,
1771 margin_init,
1772 margin_maint,
1773 maker_fee,
1774 taker_fee,
1775 None,
1776 None,
1777 ts_event,
1778 ts_init,
1779 );
1780 Ok(Self(inst))
1781 }
1782}