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