nautilus_infrastructure/sql/models/
instruments.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16// Under development
17#![allow(dead_code)]
18#![allow(unused_variables)]
19
20use std::str::FromStr;
21
22use nautilus_core::UnixNanos;
23use nautilus_model::{
24    enums::OptionKind,
25    identifiers::{InstrumentId, Symbol},
26    instruments::{
27        BettingInstrument, BinaryOption, CryptoFuture, CryptoOption, CryptoPerpetual, CurrencyPair,
28        Equity, FuturesContract, FuturesSpread, InstrumentAny, OptionContract, OptionSpread,
29    },
30    types::{Currency, Money, Price, Quantity},
31};
32use rust_decimal::Decimal;
33use sqlx::{FromRow, Row, postgres::PgRow};
34use ustr::Ustr;
35
36use crate::sql::models::enums::AssetClassModel;
37
38#[derive(Debug)]
39pub struct InstrumentAnyModel(pub InstrumentAny);
40
41#[derive(Debug)]
42pub struct BettingInstrumentModel(pub BettingInstrument);
43
44#[derive(Debug)]
45pub struct BinaryOptionModel(pub BinaryOption);
46
47#[derive(Debug)]
48pub struct CryptoFutureModel(pub CryptoFuture);
49
50#[derive(Debug)]
51pub struct CryptoOptionModel(pub CryptoOption);
52
53#[derive(Debug)]
54pub struct CryptoPerpetualModel(pub CryptoPerpetual);
55
56#[derive(Debug)]
57pub struct CurrencyPairModel(pub CurrencyPair);
58
59#[derive(Debug)]
60pub struct EquityModel(pub Equity);
61
62#[derive(Debug)]
63pub struct FuturesContractModel(pub FuturesContract);
64
65#[derive(Debug)]
66pub struct FuturesSpreadModel(pub FuturesSpread);
67
68#[derive(Debug)]
69pub struct OptionContractModel(pub OptionContract);
70
71#[derive(Debug)]
72pub struct OptionSpreadModel(pub OptionSpread);
73
74impl<'r> FromRow<'r, PgRow> for InstrumentAnyModel {
75    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
76        let kind = row.get::<String, _>("kind");
77        if kind == "BETTING" {
78            Ok(Self(InstrumentAny::Betting(
79                BettingInstrumentModel::from_row(row).unwrap().0,
80            )))
81        } else if kind == "BINARY_OPTION" {
82            Ok(Self(InstrumentAny::BinaryOption(
83                BinaryOptionModel::from_row(row).unwrap().0,
84            )))
85        } else if kind == "CRYPTO_FUTURE" {
86            Ok(Self(InstrumentAny::CryptoFuture(
87                CryptoFutureModel::from_row(row).unwrap().0,
88            )))
89        } else if kind == "CRYPTO_OPTION" {
90            Ok(Self(InstrumentAny::CryptoOption(
91                CryptoOptionModel::from_row(row).unwrap().0,
92            )))
93        } else if kind == "CRYPTO_PERPETUAL" {
94            Ok(Self(InstrumentAny::CryptoPerpetual(
95                CryptoPerpetualModel::from_row(row).unwrap().0,
96            )))
97        } else if kind == "CURRENCY_PAIR" {
98            Ok(Self(InstrumentAny::CurrencyPair(
99                CurrencyPairModel::from_row(row).unwrap().0,
100            )))
101        } else if kind == "EQUITY" {
102            Ok(Self(InstrumentAny::Equity(
103                EquityModel::from_row(row).unwrap().0,
104            )))
105        } else if kind == "FUTURES_CONTRACT" {
106            Ok(Self(InstrumentAny::FuturesContract(
107                FuturesContractModel::from_row(row).unwrap().0,
108            )))
109        } else if kind == "FUTURES_SPREAD" {
110            Ok(Self(InstrumentAny::FuturesSpread(
111                FuturesSpreadModel::from_row(row).unwrap().0,
112            )))
113        } else if kind == "OPTION_CONTRACT" {
114            Ok(Self(InstrumentAny::OptionContract(
115                OptionContractModel::from_row(row).unwrap().0,
116            )))
117        } else if kind == "OPTION_SPREAD" {
118            Ok(Self(InstrumentAny::OptionSpread(
119                OptionSpreadModel::from_row(row).unwrap().0,
120            )))
121        } else {
122            panic!("Unknown instrument type")
123        }
124    }
125}
126
127// TODO: New/updated schema required to support betting instrument loading
128impl<'r> FromRow<'r, PgRow> for BettingInstrumentModel {
129    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
130        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
131        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
132        let event_type_id = row.try_get::<i64, _>("event_type_id")? as u64;
133        let event_type_name = row
134            .try_get::<String, _>("event_type_name")
135            .map(|res| Ustr::from(res.as_str()))?;
136        let competition_id = row.try_get::<i64, _>("competition_id")? as u64;
137        let competition_name = row
138            .try_get::<String, _>("competition_name")
139            .map(|res| Ustr::from(res.as_str()))?;
140        let event_id = row.try_get::<i64, _>("event_id")? as u64;
141        let event_name = row
142            .try_get::<String, _>("event_name")
143            .map(|res| Ustr::from(res.as_str()))?;
144        let event_country_code = row
145            .try_get::<String, _>("event_country_code")
146            .map(|res| Ustr::from(res.as_str()))?;
147        let event_open_date = row
148            .try_get::<String, _>("event_open_date")
149            .map(UnixNanos::from)?;
150        let betting_type = row
151            .try_get::<String, _>("betting_type")
152            .map(|res| Ustr::from(res.as_str()))?;
153        let market_id = row
154            .try_get::<String, _>("market_id")
155            .map(|res| Ustr::from(res.as_str()))?;
156        let market_name = row
157            .try_get::<String, _>("market_name")
158            .map(|res| Ustr::from(res.as_str()))?;
159        let market_type = row
160            .try_get::<String, _>("market_type")
161            .map(|res| Ustr::from(res.as_str()))?;
162        let market_start_time = row
163            .try_get::<String, _>("market_start_time")
164            .map(UnixNanos::from)?;
165        let selection_id = row.try_get::<i64, _>("selection_id")? as u64;
166        let selection_name = row
167            .try_get::<String, _>("selection_name")
168            .map(|res| Ustr::from(res.as_str()))?;
169        let selection_handicap = row.try_get::<f64, _>("selection_handicap")?;
170        let currency = row
171            .try_get::<String, _>("quote_currency")
172            .map(Currency::from)?;
173        let price_precision = row.try_get::<i32, _>("price_precision")? as u8;
174        let size_precision = row.try_get::<i32, _>("size_precision")? as u8;
175        let price_increment = row
176            .try_get::<String, _>("price_increment")
177            .map(Price::from)?;
178        let size_increment = row
179            .try_get::<String, _>("size_increment")
180            .map(Quantity::from)?;
181        let max_quantity = row
182            .try_get::<Option<String>, _>("max_quantity")
183            .ok()
184            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
185        let min_quantity = row
186            .try_get::<Option<String>, _>("min_quantity")
187            .ok()
188            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
189        let max_notional = row
190            .try_get::<Option<String>, _>("max_notional")
191            .ok()
192            .and_then(|res| res.map(|value| Money::from(value.as_str())));
193        let min_notional = row
194            .try_get::<Option<String>, _>("min_notional")
195            .ok()
196            .and_then(|res| res.map(|value| Money::from(value.as_str())));
197        let max_price = row
198            .try_get::<Option<String>, _>("max_price")
199            .ok()
200            .and_then(|res| res.map(|value| Price::from(value.as_str())));
201        let min_price = row
202            .try_get::<Option<String>, _>("min_price")
203            .ok()
204            .and_then(|res| res.map(|value| Price::from(value.as_str())));
205        let margin_init = row
206            .try_get::<String, _>("margin_init")
207            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
208        let margin_maint = row
209            .try_get::<String, _>("margin_maint")
210            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
211        let maker_fee = row
212            .try_get::<String, _>("maker_fee")
213            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
214        let taker_fee = row
215            .try_get::<String, _>("taker_fee")
216            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
217        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
218        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
219
220        let inst = BettingInstrument::new(
221            id,
222            raw_symbol,
223            event_type_id,
224            event_type_name,
225            competition_id,
226            competition_name,
227            event_id,
228            event_name,
229            event_country_code,
230            event_open_date,
231            betting_type,
232            market_id,
233            market_name,
234            market_type,
235            market_start_time,
236            selection_id,
237            selection_name,
238            selection_handicap,
239            currency,
240            price_precision,
241            size_precision,
242            price_increment,
243            size_increment,
244            max_quantity,
245            min_quantity,
246            max_notional,
247            min_notional,
248            max_price,
249            min_price,
250            margin_init,
251            margin_maint,
252            maker_fee,
253            taker_fee,
254            ts_event,
255            ts_init,
256        );
257        Ok(Self(inst))
258    }
259}
260
261impl<'r> FromRow<'r, PgRow> for BinaryOptionModel {
262    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
263        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
264        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
265        let asset_class = row
266            .try_get::<AssetClassModel, _>("asset_class")
267            .map(|res| res.0)?;
268        let currency = row
269            .try_get::<String, _>("quote_currency")
270            .map(Currency::from)?;
271        let activation_ns = row
272            .try_get::<String, _>("activation_ns")
273            .map(UnixNanos::from)?;
274        let expiration_ns = row
275            .try_get::<String, _>("expiration_ns")
276            .map(UnixNanos::from)?;
277        let price_precision = row.try_get::<i32, _>("price_precision")? as u8;
278        let size_precision = row.try_get::<i32, _>("size_precision")? as u8;
279        let price_increment = row
280            .try_get::<String, _>("price_increment")
281            .map(|res| Price::from_str(res.as_str()).unwrap())?;
282        let size_increment = row
283            .try_get::<String, _>("size_increment")
284            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
285        let outcome = row
286            .try_get::<Option<String>, _>("outcome")
287            .ok()
288            .and_then(|res| res.map(|value| Ustr::from(value.as_str())));
289        let description = row
290            .try_get::<Option<String>, _>("description")
291            .ok()
292            .and_then(|res| res.map(|value| Ustr::from(value.as_str())));
293        let max_quantity = row
294            .try_get::<Option<String>, _>("max_quantity")
295            .ok()
296            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
297        let min_quantity = row
298            .try_get::<Option<String>, _>("min_quantity")
299            .ok()
300            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
301        let max_notional = row
302            .try_get::<Option<String>, _>("max_notional")
303            .ok()
304            .and_then(|res| res.map(|value| Money::from(value.as_str())));
305        let min_notional = row
306            .try_get::<Option<String>, _>("min_notional")
307            .ok()
308            .and_then(|res| res.map(|value| Money::from(value.as_str())));
309        let max_price = row
310            .try_get::<Option<String>, _>("max_price")
311            .ok()
312            .and_then(|res| res.map(|value| Price::from(value.as_str())));
313        let min_price = row
314            .try_get::<Option<String>, _>("min_price")
315            .ok()
316            .and_then(|res| res.map(|value| Price::from(value.as_str())));
317        let margin_init = row
318            .try_get::<String, _>("margin_init")
319            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
320        let margin_maint = row
321            .try_get::<String, _>("margin_maint")
322            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
323        let maker_fee = row
324            .try_get::<String, _>("maker_fee")
325            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
326        let taker_fee = row
327            .try_get::<String, _>("taker_fee")
328            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
329        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
330        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
331
332        let inst = BinaryOption::new(
333            id,
334            raw_symbol,
335            asset_class,
336            currency,
337            activation_ns,
338            expiration_ns,
339            price_precision,
340            size_precision,
341            price_increment,
342            size_increment,
343            outcome,
344            description,
345            max_quantity,
346            min_quantity,
347            max_notional,
348            min_notional,
349            max_price,
350            min_price,
351            margin_init,
352            margin_maint,
353            maker_fee,
354            taker_fee,
355            ts_event,
356            ts_init,
357        );
358        Ok(Self(inst))
359    }
360}
361
362impl<'r> FromRow<'r, PgRow> for CryptoFutureModel {
363    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
364        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
365        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
366        let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
367        let quote_currency = row
368            .try_get::<String, _>("quote_currency")
369            .map(Currency::from)?;
370        let settlement_currency = row
371            .try_get::<String, _>("settlement_currency")
372            .map(Currency::from)?;
373        let is_inverse = row.try_get::<bool, _>("is_inverse")?;
374        let activation_ns = row
375            .try_get::<String, _>("activation_ns")
376            .map(UnixNanos::from)?;
377        let expiration_ns = row
378            .try_get::<String, _>("expiration_ns")
379            .map(UnixNanos::from)?;
380        let price_precision = row.try_get::<i32, _>("price_precision")?;
381        let size_precision = row.try_get::<i32, _>("size_precision")?;
382        let price_increment = row
383            .try_get::<String, _>("price_increment")
384            .map(|res| Price::from_str(res.as_str()).unwrap())?;
385        let size_increment = row
386            .try_get::<String, _>("size_increment")
387            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
388        let multiplier = row
389            .try_get::<String, _>("multiplier")
390            .map(|res| Quantity::from(res.as_str()))?;
391        let lot_size = row
392            .try_get::<String, _>("lot_size")
393            .map(|res| Quantity::from(res.as_str()))?;
394        let max_quantity = row
395            .try_get::<Option<String>, _>("max_quantity")
396            .ok()
397            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
398        let min_quantity = row
399            .try_get::<Option<String>, _>("min_quantity")
400            .ok()
401            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
402        let max_notional = row
403            .try_get::<Option<String>, _>("max_notional")
404            .ok()
405            .and_then(|res| res.map(|value| Money::from(value.as_str())));
406        let min_notional = row
407            .try_get::<Option<String>, _>("min_notional")
408            .ok()
409            .and_then(|res| res.map(|value| Money::from(value.as_str())));
410        let max_price = row
411            .try_get::<Option<String>, _>("max_price")
412            .ok()
413            .and_then(|res| res.map(|value| Price::from(value.as_str())));
414        let min_price = row
415            .try_get::<Option<String>, _>("min_price")
416            .ok()
417            .and_then(|res| res.map(|value| Price::from(value.as_str())));
418        let margin_init = row
419            .try_get::<String, _>("margin_init")
420            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
421        let margin_maint = row
422            .try_get::<String, _>("margin_maint")
423            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
424        let maker_fee = row
425            .try_get::<String, _>("maker_fee")
426            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
427        let taker_fee = row
428            .try_get::<String, _>("taker_fee")
429            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
430        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
431        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
432
433        let inst = CryptoFuture::new(
434            id,
435            raw_symbol,
436            underlying,
437            quote_currency,
438            settlement_currency,
439            is_inverse,
440            activation_ns,
441            expiration_ns,
442            price_precision as u8,
443            size_precision as u8,
444            price_increment,
445            size_increment,
446            Some(multiplier),
447            Some(lot_size),
448            max_quantity,
449            min_quantity,
450            max_notional,
451            min_notional,
452            max_price,
453            min_price,
454            margin_init,
455            margin_maint,
456            maker_fee,
457            taker_fee,
458            ts_event,
459            ts_init,
460        );
461        Ok(Self(inst))
462    }
463}
464
465impl<'r> FromRow<'r, PgRow> for CryptoOptionModel {
466    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
467        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
468        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
469        let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
470        let quote_currency = row
471            .try_get::<String, _>("quote_currency")
472            .map(Currency::from)?;
473        let settlement_currency = row
474            .try_get::<String, _>("settlement_currency")
475            .map(Currency::from)?;
476        let is_inverse = row.try_get::<bool, _>("is_inverse")?;
477        let option_kind = row
478            .try_get::<String, _>("option_kind")
479            .map(|res| OptionKind::from_str(res.as_str()).unwrap())?;
480        let strike_price = row
481            .try_get::<String, _>("strike_price")
482            .map(|res| Price::from_str(res.as_str()).unwrap())?;
483        let activation_ns = row
484            .try_get::<String, _>("activation_ns")
485            .map(UnixNanos::from)?;
486        let expiration_ns = row
487            .try_get::<String, _>("expiration_ns")
488            .map(UnixNanos::from)?;
489        let price_precision = row.try_get::<i32, _>("price_precision")?;
490        let size_precision = row.try_get::<i32, _>("size_precision")?;
491        let price_increment = row
492            .try_get::<String, _>("price_increment")
493            .map(|res| Price::from_str(res.as_str()).unwrap())?;
494        let size_increment = row
495            .try_get::<String, _>("size_increment")
496            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
497        let multiplier = row
498            .try_get::<String, _>("multiplier")
499            .map(|res| Quantity::from(res.as_str()))?;
500        let lot_size = row
501            .try_get::<String, _>("lot_size")
502            .map(|res| Quantity::from(res.as_str()))?;
503        let max_quantity = row
504            .try_get::<Option<String>, _>("max_quantity")
505            .ok()
506            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
507        let min_quantity = row
508            .try_get::<Option<String>, _>("min_quantity")
509            .ok()
510            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
511        let max_notional = row
512            .try_get::<Option<String>, _>("max_notional")
513            .ok()
514            .and_then(|res| res.map(|value| Money::from(value.as_str())));
515        let min_notional = row
516            .try_get::<Option<String>, _>("min_notional")
517            .ok()
518            .and_then(|res| res.map(|value| Money::from(value.as_str())));
519        let max_price = row
520            .try_get::<Option<String>, _>("max_price")
521            .ok()
522            .and_then(|res| res.map(|value| Price::from(value.as_str())));
523        let min_price = row
524            .try_get::<Option<String>, _>("min_price")
525            .ok()
526            .and_then(|res| res.map(|value| Price::from(value.as_str())));
527        let margin_init = row
528            .try_get::<String, _>("margin_init")
529            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
530        let margin_maint = row
531            .try_get::<String, _>("margin_maint")
532            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
533        let maker_fee = row
534            .try_get::<String, _>("maker_fee")
535            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
536        let taker_fee = row
537            .try_get::<String, _>("taker_fee")
538            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
539        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
540        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
541
542        let inst = CryptoOption::new(
543            id,
544            raw_symbol,
545            underlying,
546            quote_currency,
547            settlement_currency,
548            is_inverse,
549            option_kind,
550            strike_price,
551            activation_ns,
552            expiration_ns,
553            price_precision as u8,
554            size_precision as u8,
555            price_increment,
556            size_increment,
557            Some(multiplier),
558            Some(lot_size),
559            max_quantity,
560            min_quantity,
561            max_notional,
562            min_notional,
563            max_price,
564            min_price,
565            margin_init,
566            margin_maint,
567            maker_fee,
568            taker_fee,
569            ts_event,
570            ts_init,
571        );
572        Ok(Self(inst))
573    }
574}
575
576impl<'r> FromRow<'r, PgRow> for CryptoPerpetualModel {
577    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
578        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
579        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
580        let base_currency = row
581            .try_get::<String, _>("base_currency")
582            .map(Currency::from)?;
583        let quote_currency = row
584            .try_get::<String, _>("quote_currency")
585            .map(Currency::from)?;
586        let settlement_currency = row
587            .try_get::<String, _>("settlement_currency")
588            .map(Currency::from)?;
589        let is_inverse = row.try_get::<bool, _>("is_inverse")?;
590        let price_precision = row.try_get::<i32, _>("price_precision")?;
591        let size_precision = row.try_get::<i32, _>("size_precision")?;
592        let price_increment = row
593            .try_get::<String, _>("price_increment")
594            .map(|res| Price::from_str(res.as_str()).unwrap())?;
595        let size_increment = row
596            .try_get::<String, _>("size_increment")
597            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
598        let multiplier = row
599            .try_get::<String, _>("multiplier")
600            .map(|res| Quantity::from(res.as_str()))?;
601        let lot_size = row
602            .try_get::<String, _>("lot_size")
603            .map(|res| Quantity::from(res.as_str()))?;
604        let max_quantity = row
605            .try_get::<Option<String>, _>("max_quantity")
606            .ok()
607            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
608        let min_quantity = row
609            .try_get::<Option<String>, _>("min_quantity")
610            .ok()
611            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
612        let max_notional = row
613            .try_get::<Option<String>, _>("max_notional")
614            .ok()
615            .and_then(|res| res.map(|res| Money::from(res.as_str())));
616        let min_notional = row
617            .try_get::<Option<String>, _>("min_notional")
618            .ok()
619            .and_then(|res| res.map(|res| Money::from(res.as_str())));
620        let max_price = row
621            .try_get::<Option<String>, _>("max_price")
622            .ok()
623            .and_then(|res| res.map(|res| Price::from(res.as_str())));
624        let min_price = row
625            .try_get::<Option<String>, _>("min_price")
626            .ok()
627            .and_then(|res| res.map(|res| Price::from(res.as_str())));
628        let margin_init = row
629            .try_get::<String, _>("margin_init")
630            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
631        let margin_maint = row
632            .try_get::<String, _>("margin_maint")
633            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
634        let maker_fee = row
635            .try_get::<String, _>("maker_fee")
636            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
637        let taker_fee = row
638            .try_get::<String, _>("taker_fee")
639            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
640        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
641        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
642        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
643
644        let inst = CryptoPerpetual::new(
645            id,
646            raw_symbol,
647            base_currency,
648            quote_currency,
649            settlement_currency,
650            is_inverse,
651            price_precision as u8,
652            size_precision as u8,
653            price_increment,
654            size_increment,
655            Some(multiplier),
656            Some(lot_size),
657            max_quantity,
658            min_quantity,
659            max_notional,
660            min_notional,
661            max_price,
662            min_price,
663            margin_init,
664            margin_maint,
665            maker_fee,
666            taker_fee,
667            ts_event,
668            ts_init,
669        );
670        Ok(Self(inst))
671    }
672}
673
674impl<'r> FromRow<'r, PgRow> for CurrencyPairModel {
675    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
676        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
677        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
678        let base_currency = row
679            .try_get::<String, _>("base_currency")
680            .map(Currency::from)?;
681        let quote_currency = row
682            .try_get::<String, _>("quote_currency")
683            .map(Currency::from)?;
684        let price_precision = row.try_get::<i32, _>("price_precision")?;
685        let size_precision = row.try_get::<i32, _>("size_precision")?;
686        let price_increment = row
687            .try_get::<String, _>("price_increment")
688            .map(|res| Price::from(res.as_str()))?;
689        let size_increment = row
690            .try_get::<String, _>("size_increment")
691            .map(|res| Quantity::from(res.as_str()))?;
692        let multiplier = row
693            .try_get::<Option<String>, _>("multiplier")
694            .ok()
695            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
696        let lot_size = row
697            .try_get::<Option<String>, _>("lot_size")
698            .ok()
699            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
700        let max_quantity = row
701            .try_get::<Option<String>, _>("max_quantity")
702            .ok()
703            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
704        let min_quantity = row
705            .try_get::<Option<String>, _>("min_quantity")
706            .ok()
707            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
708        let max_notional = row
709            .try_get::<Option<String>, _>("max_notional")
710            .ok()
711            .and_then(|res| res.map(|res| Money::from(res.as_str())));
712        let min_notional = row
713            .try_get::<Option<String>, _>("min_notional")
714            .ok()
715            .and_then(|res| res.map(|res| Money::from(res.as_str())));
716        let max_price = row
717            .try_get::<Option<String>, _>("max_price")
718            .ok()
719            .and_then(|res| res.map(|res| Price::from(res.as_str())));
720        let min_price = row
721            .try_get::<Option<String>, _>("min_price")
722            .ok()
723            .and_then(|res| res.map(|res| Price::from(res.as_str())));
724        let margin_init = row
725            .try_get::<String, _>("margin_init")
726            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
727        let margin_maint = row
728            .try_get::<String, _>("margin_maint")
729            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
730        let maker_fee = row
731            .try_get::<String, _>("maker_fee")
732            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
733        let taker_fee = row
734            .try_get::<String, _>("taker_fee")
735            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
736        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
737        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
738
739        let inst = CurrencyPair::new(
740            id,
741            raw_symbol,
742            base_currency,
743            quote_currency,
744            price_precision as u8,
745            size_precision as u8,
746            price_increment,
747            size_increment,
748            multiplier,
749            lot_size,
750            max_quantity,
751            min_quantity,
752            max_notional,
753            min_notional,
754            max_price,
755            min_price,
756            margin_init,
757            margin_maint,
758            maker_fee,
759            taker_fee,
760            ts_event,
761            ts_init,
762        );
763        Ok(Self(inst))
764    }
765}
766
767impl<'r> FromRow<'r, PgRow> for EquityModel {
768    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
769        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
770        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
771        let isin = row
772            .try_get::<Option<String>, _>("isin")
773            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
774        let currency = row
775            .try_get::<String, _>("quote_currency")
776            .map(Currency::from)?;
777        let price_precision = row.try_get::<i32, _>("price_precision")?;
778        let price_increment = row
779            .try_get::<String, _>("price_increment")
780            .map(|res| Price::from_str(res.as_str()).unwrap())?;
781        let lot_size = row
782            .try_get::<Option<String>, _>("lot_size")
783            .map(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()))?;
784        let max_quantity = row
785            .try_get::<Option<String>, _>("max_quantity")
786            .ok()
787            .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
788        let min_quantity = row
789            .try_get::<Option<String>, _>("min_quantity")
790            .ok()
791            .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
792        let max_price = row
793            .try_get::<Option<String>, _>("max_price")
794            .ok()
795            .and_then(|res| res.map(|s| Price::from(s.as_str())));
796        let min_price = row
797            .try_get::<Option<String>, _>("min_price")
798            .ok()
799            .and_then(|res| res.map(|s| Price::from(s.as_str())));
800        let margin_init = row
801            .try_get::<String, _>("margin_init")
802            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
803        let margin_maint = row
804            .try_get::<String, _>("margin_maint")
805            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
806        let maker_fee = row
807            .try_get::<String, _>("maker_fee")
808            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
809        let taker_fee = row
810            .try_get::<String, _>("taker_fee")
811            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
812        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
813        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
814
815        let inst = Equity::new(
816            id,
817            raw_symbol,
818            isin,
819            currency,
820            price_precision as u8,
821            price_increment,
822            lot_size,
823            max_quantity,
824            min_quantity,
825            max_price,
826            min_price,
827            margin_init,
828            margin_maint,
829            maker_fee,
830            taker_fee,
831            ts_event,
832            ts_init,
833        );
834        Ok(Self(inst))
835    }
836}
837
838impl<'r> FromRow<'r, PgRow> for FuturesContractModel {
839    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
840        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
841        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
842        let asset_class = row
843            .try_get::<AssetClassModel, _>("asset_class")
844            .map(|res| res.0)?;
845        let exchange = row
846            .try_get::<Option<String>, _>("exchange")
847            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
848        let underlying = row
849            .try_get::<String, _>("underlying")
850            .map(|res| Ustr::from(res.as_str()))?;
851        let currency = row
852            .try_get::<String, _>("quote_currency")
853            .map(Currency::from)?;
854        let activation_ns = row
855            .try_get::<String, _>("activation_ns")
856            .map(UnixNanos::from)?;
857        let expiration_ns = row
858            .try_get::<String, _>("expiration_ns")
859            .map(UnixNanos::from)?;
860        let price_precision = row.try_get::<i32, _>("price_precision")?;
861        let price_increment = row
862            .try_get::<String, _>("price_increment")
863            .map(|res| Price::from(res.as_str()))?;
864        let multiplier = row
865            .try_get::<String, _>("multiplier")
866            .map(|res| Quantity::from(res.as_str()))?;
867        let lot_size = row
868            .try_get::<String, _>("lot_size")
869            .map(|res| Quantity::from(res.as_str()))?;
870        let max_quantity = row
871            .try_get::<Option<String>, _>("max_quantity")
872            .ok()
873            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
874        let min_quantity = row
875            .try_get::<Option<String>, _>("min_quantity")
876            .ok()
877            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
878        let max_price = row
879            .try_get::<Option<String>, _>("max_price")
880            .ok()
881            .and_then(|res| res.map(|s| Price::from(s.as_str())));
882        let min_price = row
883            .try_get::<Option<String>, _>("min_price")
884            .ok()
885            .and_then(|res| res.map(|s| Price::from(s.as_str())));
886        let margin_init = row
887            .try_get::<String, _>("margin_init")
888            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
889        let margin_maint = row
890            .try_get::<String, _>("margin_maint")
891            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
892        let maker_fee = row
893            .try_get::<String, _>("maker_fee")
894            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
895        let taker_fee = row
896            .try_get::<String, _>("taker_fee")
897            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
898        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
899        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
900
901        let inst = FuturesContract::new(
902            id,
903            raw_symbol,
904            asset_class,
905            exchange,
906            underlying,
907            activation_ns,
908            expiration_ns,
909            currency,
910            price_precision as u8,
911            price_increment,
912            multiplier,
913            lot_size,
914            max_quantity,
915            min_quantity,
916            max_price,
917            min_price,
918            margin_init,
919            margin_maint,
920            maker_fee,
921            taker_fee,
922            ts_event,
923            ts_init,
924        );
925        Ok(Self(inst))
926    }
927}
928
929impl<'r> FromRow<'r, PgRow> for FuturesSpreadModel {
930    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
931        todo!("Implement FromRow for FuturesSpread")
932    }
933}
934
935impl<'r> FromRow<'r, PgRow> for OptionContractModel {
936    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
937        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
938        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
939        let asset_class = row
940            .try_get::<AssetClassModel, _>("asset_class")
941            .map(|res| res.0)?;
942        let exchange = row
943            .try_get::<Option<String>, _>("exchange")
944            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
945        let underlying = row
946            .try_get::<String, _>("underlying")
947            .map(|res| Ustr::from(res.as_str()))?;
948        let option_kind = row
949            .try_get::<String, _>("option_kind")
950            .map(|res| OptionKind::from_str(res.as_str()).unwrap())?;
951        let activation_ns = row
952            .try_get::<String, _>("activation_ns")
953            .map(UnixNanos::from)?;
954        let expiration_ns = row
955            .try_get::<String, _>("expiration_ns")
956            .map(UnixNanos::from)?;
957        let strike_price = row
958            .try_get::<String, _>("strike_price")
959            .map(|res| Price::from_str(res.as_str()).unwrap())?;
960        let currency = row
961            .try_get::<String, _>("quote_currency")
962            .map(Currency::from)?;
963        let price_precision = row.try_get::<i32, _>("price_precision").unwrap();
964        let price_increment = row
965            .try_get::<String, _>("price_increment")
966            .map(|res| Price::from_str(res.as_str()).unwrap())?;
967        let multiplier = row
968            .try_get::<String, _>("multiplier")
969            .map(|res| Quantity::from(res.as_str()))?;
970        let lot_size = row
971            .try_get::<String, _>("lot_size")
972            .map(|res| Quantity::from(res.as_str()))
973            .unwrap();
974        let max_quantity = row
975            .try_get::<Option<String>, _>("max_quantity")
976            .ok()
977            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
978        let min_quantity = row
979            .try_get::<Option<String>, _>("min_quantity")
980            .ok()
981            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
982        let max_price = row
983            .try_get::<Option<String>, _>("max_price")
984            .ok()
985            .and_then(|res| res.map(|s| Price::from(s.as_str())));
986        let min_price = row
987            .try_get::<Option<String>, _>("min_price")
988            .ok()
989            .and_then(|res| res.map(|s| Price::from(s.as_str())));
990        let margin_init = row
991            .try_get::<String, _>("margin_init")
992            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
993        let margin_maint = row
994            .try_get::<String, _>("margin_maint")
995            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
996        let maker_fee = row
997            .try_get::<String, _>("maker_fee")
998            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
999        let taker_fee = row
1000            .try_get::<String, _>("taker_fee")
1001            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
1002        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
1003        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
1004
1005        let inst = OptionContract::new(
1006            id,
1007            raw_symbol,
1008            asset_class,
1009            exchange,
1010            underlying,
1011            option_kind,
1012            strike_price,
1013            currency,
1014            activation_ns,
1015            expiration_ns,
1016            price_precision as u8,
1017            price_increment,
1018            multiplier,
1019            lot_size,
1020            max_quantity,
1021            min_quantity,
1022            max_price,
1023            min_price,
1024            margin_init,
1025            margin_maint,
1026            maker_fee,
1027            taker_fee,
1028            ts_event,
1029            ts_init,
1030        );
1031        Ok(Self(inst))
1032    }
1033}
1034
1035impl<'r> FromRow<'r, PgRow> for OptionSpreadModel {
1036    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
1037        todo!("Implement FromRow for OptionSpread")
1038    }
1039}