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