nautilus_infrastructure/sql/models/
instruments.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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 max_quantity = row
501            .try_get::<Option<String>, _>("max_quantity")
502            .ok()
503            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
504        let min_quantity = row
505            .try_get::<Option<String>, _>("min_quantity")
506            .ok()
507            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
508        let max_notional = row
509            .try_get::<Option<String>, _>("max_notional")
510            .ok()
511            .and_then(|res| res.map(|value| Money::from(value.as_str())));
512        let min_notional = row
513            .try_get::<Option<String>, _>("min_notional")
514            .ok()
515            .and_then(|res| res.map(|value| Money::from(value.as_str())));
516        let max_price = row
517            .try_get::<Option<String>, _>("max_price")
518            .ok()
519            .and_then(|res| res.map(|value| Price::from(value.as_str())));
520        let min_price = row
521            .try_get::<Option<String>, _>("min_price")
522            .ok()
523            .and_then(|res| res.map(|value| Price::from(value.as_str())));
524        let margin_init = row
525            .try_get::<String, _>("margin_init")
526            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
527        let margin_maint = row
528            .try_get::<String, _>("margin_maint")
529            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
530        let maker_fee = row
531            .try_get::<String, _>("maker_fee")
532            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
533        let taker_fee = row
534            .try_get::<String, _>("taker_fee")
535            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
536        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
537        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
538
539        let inst = CryptoOption::new(
540            id,
541            raw_symbol,
542            underlying,
543            quote_currency,
544            settlement_currency,
545            is_inverse,
546            option_kind,
547            strike_price,
548            activation_ns,
549            expiration_ns,
550            price_precision as u8,
551            size_precision as u8,
552            price_increment,
553            size_increment,
554            Some(multiplier),
555            max_quantity,
556            min_quantity,
557            max_notional,
558            min_notional,
559            max_price,
560            min_price,
561            margin_init,
562            margin_maint,
563            maker_fee,
564            taker_fee,
565            ts_event,
566            ts_init,
567        );
568        Ok(Self(inst))
569    }
570}
571
572impl<'r> FromRow<'r, PgRow> for CryptoPerpetualModel {
573    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
574        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
575        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
576        let base_currency = row
577            .try_get::<String, _>("base_currency")
578            .map(Currency::from)?;
579        let quote_currency = row
580            .try_get::<String, _>("quote_currency")
581            .map(Currency::from)?;
582        let settlement_currency = row
583            .try_get::<String, _>("settlement_currency")
584            .map(Currency::from)?;
585        let is_inverse = row.try_get::<bool, _>("is_inverse")?;
586        let price_precision = row.try_get::<i32, _>("price_precision")?;
587        let size_precision = row.try_get::<i32, _>("size_precision")?;
588        let price_increment = row
589            .try_get::<String, _>("price_increment")
590            .map(|res| Price::from_str(res.as_str()).unwrap())?;
591        let size_increment = row
592            .try_get::<String, _>("size_increment")
593            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
594        let multiplier = row
595            .try_get::<String, _>("multiplier")
596            .map(|res| Quantity::from(res.as_str()))?;
597        let lot_size = row
598            .try_get::<String, _>("lot_size")
599            .map(|res| Quantity::from(res.as_str()))?;
600        let max_quantity = row
601            .try_get::<Option<String>, _>("max_quantity")
602            .ok()
603            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
604        let min_quantity = row
605            .try_get::<Option<String>, _>("min_quantity")
606            .ok()
607            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
608        let max_notional = row
609            .try_get::<Option<String>, _>("max_notional")
610            .ok()
611            .and_then(|res| res.map(|res| Money::from(res.as_str())));
612        let min_notional = row
613            .try_get::<Option<String>, _>("min_notional")
614            .ok()
615            .and_then(|res| res.map(|res| Money::from(res.as_str())));
616        let max_price = row
617            .try_get::<Option<String>, _>("max_price")
618            .ok()
619            .and_then(|res| res.map(|res| Price::from(res.as_str())));
620        let min_price = row
621            .try_get::<Option<String>, _>("min_price")
622            .ok()
623            .and_then(|res| res.map(|res| Price::from(res.as_str())));
624        let margin_init = row
625            .try_get::<String, _>("margin_init")
626            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
627        let margin_maint = row
628            .try_get::<String, _>("margin_maint")
629            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
630        let maker_fee = row
631            .try_get::<String, _>("maker_fee")
632            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
633        let taker_fee = row
634            .try_get::<String, _>("taker_fee")
635            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
636        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
637        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
638        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
639
640        let inst = CryptoPerpetual::new(
641            id,
642            raw_symbol,
643            base_currency,
644            quote_currency,
645            settlement_currency,
646            is_inverse,
647            price_precision as u8,
648            size_precision as u8,
649            price_increment,
650            size_increment,
651            Some(multiplier),
652            Some(lot_size),
653            max_quantity,
654            min_quantity,
655            max_notional,
656            min_notional,
657            max_price,
658            min_price,
659            margin_init,
660            margin_maint,
661            maker_fee,
662            taker_fee,
663            ts_event,
664            ts_init,
665        );
666        Ok(Self(inst))
667    }
668}
669
670impl<'r> FromRow<'r, PgRow> for CurrencyPairModel {
671    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
672        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
673        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
674        let base_currency = row
675            .try_get::<String, _>("base_currency")
676            .map(Currency::from)?;
677        let quote_currency = row
678            .try_get::<String, _>("quote_currency")
679            .map(Currency::from)?;
680        let price_precision = row.try_get::<i32, _>("price_precision")?;
681        let size_precision = row.try_get::<i32, _>("size_precision")?;
682        let price_increment = row
683            .try_get::<String, _>("price_increment")
684            .map(|res| Price::from(res.as_str()))?;
685        let size_increment = row
686            .try_get::<String, _>("size_increment")
687            .map(|res| Quantity::from(res.as_str()))?;
688        let lot_size = row
689            .try_get::<Option<String>, _>("lot_size")
690            .ok()
691            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
692        let max_quantity = row
693            .try_get::<Option<String>, _>("max_quantity")
694            .ok()
695            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
696        let min_quantity = row
697            .try_get::<Option<String>, _>("min_quantity")
698            .ok()
699            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
700        let max_notional = row
701            .try_get::<Option<String>, _>("max_notional")
702            .ok()
703            .and_then(|res| res.map(|res| Money::from(res.as_str())));
704        let min_notional = row
705            .try_get::<Option<String>, _>("min_notional")
706            .ok()
707            .and_then(|res| res.map(|res| Money::from(res.as_str())));
708        let max_price = row
709            .try_get::<Option<String>, _>("max_price")
710            .ok()
711            .and_then(|res| res.map(|res| Price::from(res.as_str())));
712        let min_price = row
713            .try_get::<Option<String>, _>("min_price")
714            .ok()
715            .and_then(|res| res.map(|res| Price::from(res.as_str())));
716        let margin_init = row
717            .try_get::<String, _>("margin_init")
718            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
719        let margin_maint = row
720            .try_get::<String, _>("margin_maint")
721            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
722        let maker_fee = row
723            .try_get::<String, _>("maker_fee")
724            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
725        let taker_fee = row
726            .try_get::<String, _>("taker_fee")
727            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
728        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
729        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
730
731        let inst = CurrencyPair::new(
732            id,
733            raw_symbol,
734            base_currency,
735            quote_currency,
736            price_precision as u8,
737            size_precision as u8,
738            price_increment,
739            size_increment,
740            lot_size,
741            max_quantity,
742            min_quantity,
743            max_notional,
744            min_notional,
745            max_price,
746            min_price,
747            margin_init,
748            margin_maint,
749            maker_fee,
750            taker_fee,
751            ts_event,
752            ts_init,
753        );
754        Ok(Self(inst))
755    }
756}
757
758impl<'r> FromRow<'r, PgRow> for EquityModel {
759    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
760        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
761        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
762        let isin = row
763            .try_get::<Option<String>, _>("isin")
764            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
765        let currency = row
766            .try_get::<String, _>("quote_currency")
767            .map(Currency::from)?;
768        let price_precision = row.try_get::<i32, _>("price_precision")?;
769        let price_increment = row
770            .try_get::<String, _>("price_increment")
771            .map(|res| Price::from_str(res.as_str()).unwrap())?;
772        let lot_size = row
773            .try_get::<Option<String>, _>("lot_size")
774            .map(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()))?;
775        let max_quantity = row
776            .try_get::<Option<String>, _>("max_quantity")
777            .ok()
778            .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
779        let min_quantity = row
780            .try_get::<Option<String>, _>("min_quantity")
781            .ok()
782            .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
783        let max_price = row
784            .try_get::<Option<String>, _>("max_price")
785            .ok()
786            .and_then(|res| res.map(|s| Price::from(s.as_str())));
787        let min_price = row
788            .try_get::<Option<String>, _>("min_price")
789            .ok()
790            .and_then(|res| res.map(|s| Price::from(s.as_str())));
791        let margin_init = row
792            .try_get::<String, _>("margin_init")
793            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
794        let margin_maint = row
795            .try_get::<String, _>("margin_maint")
796            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
797        let maker_fee = row
798            .try_get::<String, _>("maker_fee")
799            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
800        let taker_fee = row
801            .try_get::<String, _>("taker_fee")
802            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
803        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
804        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
805
806        let inst = Equity::new(
807            id,
808            raw_symbol,
809            isin,
810            currency,
811            price_precision as u8,
812            price_increment,
813            lot_size,
814            max_quantity,
815            min_quantity,
816            max_price,
817            min_price,
818            margin_init,
819            margin_maint,
820            maker_fee,
821            taker_fee,
822            ts_event,
823            ts_init,
824        );
825        Ok(Self(inst))
826    }
827}
828
829impl<'r> FromRow<'r, PgRow> for FuturesContractModel {
830    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
831        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
832        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
833        let asset_class = row
834            .try_get::<AssetClassModel, _>("asset_class")
835            .map(|res| res.0)?;
836        let exchange = row
837            .try_get::<Option<String>, _>("exchange")
838            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
839        let underlying = row
840            .try_get::<String, _>("underlying")
841            .map(|res| Ustr::from(res.as_str()))?;
842        let currency = row
843            .try_get::<String, _>("quote_currency")
844            .map(Currency::from)?;
845        let activation_ns = row
846            .try_get::<String, _>("activation_ns")
847            .map(UnixNanos::from)?;
848        let expiration_ns = row
849            .try_get::<String, _>("expiration_ns")
850            .map(UnixNanos::from)?;
851        let price_precision = row.try_get::<i32, _>("price_precision")?;
852        let price_increment = row
853            .try_get::<String, _>("price_increment")
854            .map(|res| Price::from(res.as_str()))?;
855        let multiplier = row
856            .try_get::<String, _>("multiplier")
857            .map(|res| Quantity::from(res.as_str()))?;
858        let lot_size = row
859            .try_get::<String, _>("lot_size")
860            .map(|res| Quantity::from(res.as_str()))?;
861        let max_quantity = row
862            .try_get::<Option<String>, _>("max_quantity")
863            .ok()
864            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
865        let min_quantity = row
866            .try_get::<Option<String>, _>("min_quantity")
867            .ok()
868            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
869        let max_price = row
870            .try_get::<Option<String>, _>("max_price")
871            .ok()
872            .and_then(|res| res.map(|s| Price::from(s.as_str())));
873        let min_price = row
874            .try_get::<Option<String>, _>("min_price")
875            .ok()
876            .and_then(|res| res.map(|s| Price::from(s.as_str())));
877        let margin_init = row
878            .try_get::<String, _>("margin_init")
879            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
880        let margin_maint = row
881            .try_get::<String, _>("margin_maint")
882            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
883        let maker_fee = row
884            .try_get::<String, _>("maker_fee")
885            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
886        let taker_fee = row
887            .try_get::<String, _>("taker_fee")
888            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
889        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
890        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
891
892        let inst = FuturesContract::new(
893            id,
894            raw_symbol,
895            asset_class,
896            exchange,
897            underlying,
898            activation_ns,
899            expiration_ns,
900            currency,
901            price_precision as u8,
902            price_increment,
903            multiplier,
904            lot_size,
905            max_quantity,
906            min_quantity,
907            max_price,
908            min_price,
909            margin_init,
910            margin_maint,
911            maker_fee,
912            taker_fee,
913            ts_event,
914            ts_init,
915        );
916        Ok(Self(inst))
917    }
918}
919
920impl<'r> FromRow<'r, PgRow> for FuturesSpreadModel {
921    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
922        todo!("Implement FromRow for FuturesSpread")
923    }
924}
925
926impl<'r> FromRow<'r, PgRow> for OptionContractModel {
927    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
928        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
929        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
930        let asset_class = row
931            .try_get::<AssetClassModel, _>("asset_class")
932            .map(|res| res.0)?;
933        let exchange = row
934            .try_get::<Option<String>, _>("exchange")
935            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
936        let underlying = row
937            .try_get::<String, _>("underlying")
938            .map(|res| Ustr::from(res.as_str()))?;
939        let option_kind = row
940            .try_get::<String, _>("option_kind")
941            .map(|res| OptionKind::from_str(res.as_str()).unwrap())?;
942        let activation_ns = row
943            .try_get::<String, _>("activation_ns")
944            .map(UnixNanos::from)?;
945        let expiration_ns = row
946            .try_get::<String, _>("expiration_ns")
947            .map(UnixNanos::from)?;
948        let strike_price = row
949            .try_get::<String, _>("strike_price")
950            .map(|res| Price::from_str(res.as_str()).unwrap())?;
951        let currency = row
952            .try_get::<String, _>("quote_currency")
953            .map(Currency::from)?;
954        let price_precision = row.try_get::<i32, _>("price_precision").unwrap();
955        let price_increment = row
956            .try_get::<String, _>("price_increment")
957            .map(|res| Price::from_str(res.as_str()).unwrap())?;
958        let multiplier = row
959            .try_get::<String, _>("multiplier")
960            .map(|res| Quantity::from(res.as_str()))?;
961        let lot_size = row
962            .try_get::<String, _>("lot_size")
963            .map(|res| Quantity::from(res.as_str()))
964            .unwrap();
965        let max_quantity = row
966            .try_get::<Option<String>, _>("max_quantity")
967            .ok()
968            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
969        let min_quantity = row
970            .try_get::<Option<String>, _>("min_quantity")
971            .ok()
972            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
973        let max_price = row
974            .try_get::<Option<String>, _>("max_price")
975            .ok()
976            .and_then(|res| res.map(|s| Price::from(s.as_str())));
977        let min_price = row
978            .try_get::<Option<String>, _>("min_price")
979            .ok()
980            .and_then(|res| res.map(|s| Price::from(s.as_str())));
981        let margin_init = row
982            .try_get::<String, _>("margin_init")
983            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
984        let margin_maint = row
985            .try_get::<String, _>("margin_maint")
986            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
987        let maker_fee = row
988            .try_get::<String, _>("maker_fee")
989            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
990        let taker_fee = row
991            .try_get::<String, _>("taker_fee")
992            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
993        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
994        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
995
996        let inst = OptionContract::new(
997            id,
998            raw_symbol,
999            asset_class,
1000            exchange,
1001            underlying,
1002            option_kind,
1003            strike_price,
1004            currency,
1005            activation_ns,
1006            expiration_ns,
1007            price_precision as u8,
1008            price_increment,
1009            multiplier,
1010            lot_size,
1011            max_quantity,
1012            min_quantity,
1013            max_price,
1014            min_price,
1015            margin_init,
1016            margin_maint,
1017            maker_fee,
1018            taker_fee,
1019            ts_event,
1020            ts_init,
1021        );
1022        Ok(Self(inst))
1023    }
1024}
1025
1026impl<'r> FromRow<'r, PgRow> for OptionSpreadModel {
1027    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
1028        todo!("Implement FromRow for OptionSpread")
1029    }
1030}