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