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