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, CryptoPerpetual, CurrencyPair, Equity,
28        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
38pub struct InstrumentAnyModel(pub InstrumentAny);
39pub struct BettingInstrumentModel(pub BettingInstrument);
40pub struct BinaryOptionModel(pub BinaryOption);
41pub struct CryptoFutureModel(pub CryptoFuture);
42pub struct CryptoPerpetualModel(pub CryptoPerpetual);
43pub struct CurrencyPairModel(pub CurrencyPair);
44pub struct EquityModel(pub Equity);
45pub struct FuturesContractModel(pub FuturesContract);
46pub struct FuturesSpreadModel(pub FuturesSpread);
47pub struct OptionContractModel(pub OptionContract);
48pub struct OptionSpreadModel(pub OptionSpread);
49
50// TBD
51impl<'r> FromRow<'r, PgRow> for InstrumentAnyModel {
52    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
53        let kind = row.get::<String, _>("kind");
54        if kind == "BETTING" {
55            Ok(InstrumentAnyModel(InstrumentAny::Betting(
56                BettingInstrumentModel::from_row(row).unwrap().0,
57            )))
58        } else if kind == "BINARY_OPTION" {
59            Ok(InstrumentAnyModel(InstrumentAny::BinaryOption(
60                BinaryOptionModel::from_row(row).unwrap().0,
61            )))
62        } else if kind == "CRYPTO_FUTURE" {
63            Ok(InstrumentAnyModel(InstrumentAny::CryptoFuture(
64                CryptoFutureModel::from_row(row).unwrap().0,
65            )))
66        } else if kind == "CRYPTO_PERPETUAL" {
67            Ok(InstrumentAnyModel(InstrumentAny::CryptoPerpetual(
68                CryptoPerpetualModel::from_row(row).unwrap().0,
69            )))
70        } else if kind == "CURRENCY_PAIR" {
71            Ok(InstrumentAnyModel(InstrumentAny::CurrencyPair(
72                CurrencyPairModel::from_row(row).unwrap().0,
73            )))
74        } else if kind == "EQUITY" {
75            Ok(InstrumentAnyModel(InstrumentAny::Equity(
76                EquityModel::from_row(row).unwrap().0,
77            )))
78        } else if kind == "FUTURES_CONTRACT" {
79            Ok(InstrumentAnyModel(InstrumentAny::FuturesContract(
80                FuturesContractModel::from_row(row).unwrap().0,
81            )))
82        } else if kind == "FUTURES_SPREAD" {
83            Ok(InstrumentAnyModel(InstrumentAny::FuturesSpread(
84                FuturesSpreadModel::from_row(row).unwrap().0,
85            )))
86        } else if kind == "OPTION_CONTRACT" {
87            Ok(InstrumentAnyModel(InstrumentAny::OptionContract(
88                OptionContractModel::from_row(row).unwrap().0,
89            )))
90        } else if kind == "OPTION_SPREAD" {
91            Ok(InstrumentAnyModel(InstrumentAny::OptionSpread(
92                OptionSpreadModel::from_row(row).unwrap().0,
93            )))
94        } else {
95            panic!("Unknown instrument type")
96        }
97    }
98}
99
100// TODO: New/updated schema required to support betting instrument loading
101impl<'r> FromRow<'r, PgRow> for BettingInstrumentModel {
102    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
103        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
104        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
105        let event_type_id = row.try_get::<i64, _>("event_type_id")? as u64;
106        let event_type_name = row
107            .try_get::<String, _>("event_type_name")
108            .map(|res| Ustr::from(res.as_str()))?;
109        let competition_id = row.try_get::<i64, _>("competition_id")? as u64;
110        let competition_name = row
111            .try_get::<String, _>("competition_name")
112            .map(|res| Ustr::from(res.as_str()))?;
113        let event_id = row.try_get::<i64, _>("event_id")? as u64;
114        let event_name = row
115            .try_get::<String, _>("event_name")
116            .map(|res| Ustr::from(res.as_str()))?;
117        let event_country_code = row
118            .try_get::<String, _>("event_country_code")
119            .map(|res| Ustr::from(res.as_str()))?;
120        let event_open_date = row
121            .try_get::<String, _>("event_open_date")
122            .map(UnixNanos::from)?;
123        let betting_type = row
124            .try_get::<String, _>("betting_type")
125            .map(|res| Ustr::from(res.as_str()))?;
126        let market_id = row
127            .try_get::<String, _>("market_id")
128            .map(|res| Ustr::from(res.as_str()))?;
129        let market_name = row
130            .try_get::<String, _>("market_name")
131            .map(|res| Ustr::from(res.as_str()))?;
132        let market_type = row
133            .try_get::<String, _>("market_type")
134            .map(|res| Ustr::from(res.as_str()))?;
135        let market_start_time = row
136            .try_get::<String, _>("market_start_time")
137            .map(UnixNanos::from)?;
138        let selection_id = row.try_get::<i64, _>("selection_id")? as u64;
139        let selection_name = row
140            .try_get::<String, _>("selection_name")
141            .map(|res| Ustr::from(res.as_str()))?;
142        let selection_handicap = row.try_get::<f64, _>("selection_handicap")?;
143        let currency = row
144            .try_get::<String, _>("quote_currency")
145            .map(Currency::from)?;
146        let price_precision = row.try_get::<i32, _>("price_precision")? as u8;
147        let size_precision = row.try_get::<i32, _>("size_precision")? as u8;
148        let price_increment = row
149            .try_get::<String, _>("price_increment")
150            .map(Price::from)?;
151        let size_increment = row
152            .try_get::<String, _>("size_increment")
153            .map(Quantity::from)?;
154        let max_quantity = row
155            .try_get::<Option<String>, _>("max_quantity")
156            .ok()
157            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
158        let min_quantity = row
159            .try_get::<Option<String>, _>("min_quantity")
160            .ok()
161            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
162        let max_notional = row
163            .try_get::<Option<String>, _>("max_notional")
164            .ok()
165            .and_then(|res| res.map(|value| Money::from(value.as_str())));
166        let min_notional = row
167            .try_get::<Option<String>, _>("min_notional")
168            .ok()
169            .and_then(|res| res.map(|value| Money::from(value.as_str())));
170        let max_price = row
171            .try_get::<Option<String>, _>("max_price")
172            .ok()
173            .and_then(|res| res.map(|value| Price::from(value.as_str())));
174        let min_price = row
175            .try_get::<Option<String>, _>("min_price")
176            .ok()
177            .and_then(|res| res.map(|value| Price::from(value.as_str())));
178        let margin_init = row
179            .try_get::<String, _>("margin_init")
180            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
181        let margin_maint = row
182            .try_get::<String, _>("margin_maint")
183            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
184        let maker_fee = row
185            .try_get::<String, _>("maker_fee")
186            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
187        let taker_fee = row
188            .try_get::<String, _>("taker_fee")
189            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
190        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
191        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
192
193        let inst = BettingInstrument::new(
194            id,
195            raw_symbol,
196            event_type_id,
197            event_type_name,
198            competition_id,
199            competition_name,
200            event_id,
201            event_name,
202            event_country_code,
203            event_open_date,
204            betting_type,
205            market_id,
206            market_name,
207            market_type,
208            market_start_time,
209            selection_id,
210            selection_name,
211            selection_handicap,
212            currency,
213            price_precision,
214            size_precision,
215            price_increment,
216            size_increment,
217            max_quantity,
218            min_quantity,
219            max_notional,
220            min_notional,
221            max_price,
222            min_price,
223            margin_init,
224            margin_maint,
225            maker_fee,
226            taker_fee,
227            ts_event,
228            ts_init,
229        );
230        Ok(BettingInstrumentModel(inst))
231    }
232}
233
234impl<'r> FromRow<'r, PgRow> for BinaryOptionModel {
235    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
236        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
237        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
238        let asset_class = row
239            .try_get::<AssetClassModel, _>("asset_class")
240            .map(|res| res.0)?;
241        let currency = row
242            .try_get::<String, _>("quote_currency")
243            .map(Currency::from)?;
244        let activation_ns = row
245            .try_get::<String, _>("activation_ns")
246            .map(UnixNanos::from)?;
247        let expiration_ns = row
248            .try_get::<String, _>("expiration_ns")
249            .map(UnixNanos::from)?;
250        let price_precision = row.try_get::<i32, _>("price_precision")? as u8;
251        let size_precision = row.try_get::<i32, _>("size_precision")? as u8;
252        let price_increment = row
253            .try_get::<String, _>("price_increment")
254            .map(|res| Price::from_str(res.as_str()).unwrap())?;
255        let size_increment = row
256            .try_get::<String, _>("size_increment")
257            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
258        let outcome = row
259            .try_get::<Option<String>, _>("outcome")
260            .ok()
261            .and_then(|res| res.map(|value| Ustr::from(value.as_str())));
262        let description = row
263            .try_get::<Option<String>, _>("description")
264            .ok()
265            .and_then(|res| res.map(|value| Ustr::from(value.as_str())));
266        let max_quantity = row
267            .try_get::<Option<String>, _>("max_quantity")
268            .ok()
269            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
270        let min_quantity = row
271            .try_get::<Option<String>, _>("min_quantity")
272            .ok()
273            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
274        let max_notional = row
275            .try_get::<Option<String>, _>("max_notional")
276            .ok()
277            .and_then(|res| res.map(|value| Money::from(value.as_str())));
278        let min_notional = row
279            .try_get::<Option<String>, _>("min_notional")
280            .ok()
281            .and_then(|res| res.map(|value| Money::from(value.as_str())));
282        let max_price = row
283            .try_get::<Option<String>, _>("max_price")
284            .ok()
285            .and_then(|res| res.map(|value| Price::from(value.as_str())));
286        let min_price = row
287            .try_get::<Option<String>, _>("min_price")
288            .ok()
289            .and_then(|res| res.map(|value| Price::from(value.as_str())));
290        let margin_init = row
291            .try_get::<String, _>("margin_init")
292            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
293        let margin_maint = row
294            .try_get::<String, _>("margin_maint")
295            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
296        let maker_fee = row
297            .try_get::<String, _>("maker_fee")
298            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
299        let taker_fee = row
300            .try_get::<String, _>("taker_fee")
301            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
302        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
303        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
304
305        let inst = BinaryOption::new(
306            id,
307            raw_symbol,
308            asset_class,
309            currency,
310            activation_ns,
311            expiration_ns,
312            price_precision,
313            size_precision,
314            price_increment,
315            size_increment,
316            outcome,
317            description,
318            max_quantity,
319            min_quantity,
320            max_notional,
321            min_notional,
322            max_price,
323            min_price,
324            margin_init,
325            margin_maint,
326            maker_fee,
327            taker_fee,
328            ts_event,
329            ts_init,
330        );
331        Ok(BinaryOptionModel(inst))
332    }
333}
334
335impl<'r> FromRow<'r, PgRow> for CryptoFutureModel {
336    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
337        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
338        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
339        let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
340        let quote_currency = row
341            .try_get::<String, _>("quote_currency")
342            .map(Currency::from)?;
343        let settlement_currency = row
344            .try_get::<String, _>("settlement_currency")
345            .map(Currency::from)?;
346        let is_inverse = row.try_get::<bool, _>("is_inverse")?;
347        let activation_ns = row
348            .try_get::<String, _>("activation_ns")
349            .map(UnixNanos::from)?;
350        let expiration_ns = row
351            .try_get::<String, _>("expiration_ns")
352            .map(UnixNanos::from)?;
353        let price_precision = row.try_get::<i32, _>("price_precision")?;
354        let size_precision = row.try_get::<i32, _>("size_precision")?;
355        let price_increment = row
356            .try_get::<String, _>("price_increment")
357            .map(|res| Price::from_str(res.as_str()).unwrap())?;
358        let size_increment = row
359            .try_get::<String, _>("size_increment")
360            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
361        let multiplier = row
362            .try_get::<String, _>("multiplier")
363            .map(|res| Quantity::from(res.as_str()))?;
364        let lot_size = row
365            .try_get::<String, _>("lot_size")
366            .map(|res| Quantity::from(res.as_str()))?;
367        let max_quantity = row
368            .try_get::<Option<String>, _>("max_quantity")
369            .ok()
370            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
371        let min_quantity = row
372            .try_get::<Option<String>, _>("min_quantity")
373            .ok()
374            .and_then(|res| res.map(|value| Quantity::from(value.as_str())));
375        let max_notional = row
376            .try_get::<Option<String>, _>("max_notional")
377            .ok()
378            .and_then(|res| res.map(|value| Money::from(value.as_str())));
379        let min_notional = row
380            .try_get::<Option<String>, _>("min_notional")
381            .ok()
382            .and_then(|res| res.map(|value| Money::from(value.as_str())));
383        let max_price = row
384            .try_get::<Option<String>, _>("max_price")
385            .ok()
386            .and_then(|res| res.map(|value| Price::from(value.as_str())));
387        let min_price = row
388            .try_get::<Option<String>, _>("min_price")
389            .ok()
390            .and_then(|res| res.map(|value| Price::from(value.as_str())));
391        let margin_init = row
392            .try_get::<String, _>("margin_init")
393            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
394        let margin_maint = row
395            .try_get::<String, _>("margin_maint")
396            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
397        let maker_fee = row
398            .try_get::<String, _>("maker_fee")
399            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
400        let taker_fee = row
401            .try_get::<String, _>("taker_fee")
402            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
403        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
404        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
405
406        let inst = CryptoFuture::new(
407            id,
408            raw_symbol,
409            underlying,
410            quote_currency,
411            settlement_currency,
412            is_inverse,
413            activation_ns,
414            expiration_ns,
415            price_precision as u8,
416            size_precision as u8,
417            price_increment,
418            size_increment,
419            Some(multiplier),
420            Some(lot_size),
421            max_quantity,
422            min_quantity,
423            max_notional,
424            min_notional,
425            max_price,
426            min_price,
427            margin_init,
428            margin_maint,
429            maker_fee,
430            taker_fee,
431            ts_event,
432            ts_init,
433        );
434        Ok(CryptoFutureModel(inst))
435    }
436}
437
438impl<'r> FromRow<'r, PgRow> for CryptoPerpetualModel {
439    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
440        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
441        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
442        let base_currency = row
443            .try_get::<String, _>("base_currency")
444            .map(Currency::from)?;
445        let quote_currency = row
446            .try_get::<String, _>("quote_currency")
447            .map(Currency::from)?;
448        let settlement_currency = row
449            .try_get::<String, _>("settlement_currency")
450            .map(Currency::from)?;
451        let is_inverse = row.try_get::<bool, _>("is_inverse")?;
452        let price_precision = row.try_get::<i32, _>("price_precision")?;
453        let size_precision = row.try_get::<i32, _>("size_precision")?;
454        let price_increment = row
455            .try_get::<String, _>("price_increment")
456            .map(|res| Price::from_str(res.as_str()).unwrap())?;
457        let size_increment = row
458            .try_get::<String, _>("size_increment")
459            .map(|res| Quantity::from_str(res.as_str()).unwrap())?;
460        let multiplier = row
461            .try_get::<String, _>("multiplier")
462            .map(|res| Quantity::from(res.as_str()))?;
463        let lot_size = row
464            .try_get::<String, _>("lot_size")
465            .map(|res| Quantity::from(res.as_str()))?;
466        let max_quantity = row
467            .try_get::<Option<String>, _>("max_quantity")
468            .ok()
469            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
470        let min_quantity = row
471            .try_get::<Option<String>, _>("min_quantity")
472            .ok()
473            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
474        let max_notional = row
475            .try_get::<Option<String>, _>("max_notional")
476            .ok()
477            .and_then(|res| res.map(|res| Money::from(res.as_str())));
478        let min_notional = row
479            .try_get::<Option<String>, _>("min_notional")
480            .ok()
481            .and_then(|res| res.map(|res| Money::from(res.as_str())));
482        let max_price = row
483            .try_get::<Option<String>, _>("max_price")
484            .ok()
485            .and_then(|res| res.map(|res| Price::from(res.as_str())));
486        let min_price = row
487            .try_get::<Option<String>, _>("min_price")
488            .ok()
489            .and_then(|res| res.map(|res| Price::from(res.as_str())));
490        let margin_init = row
491            .try_get::<String, _>("margin_init")
492            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
493        let margin_maint = row
494            .try_get::<String, _>("margin_maint")
495            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
496        let maker_fee = row
497            .try_get::<String, _>("maker_fee")
498            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
499        let taker_fee = row
500            .try_get::<String, _>("taker_fee")
501            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
502        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
503        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
504        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
505
506        let inst = CryptoPerpetual::new(
507            id,
508            raw_symbol,
509            base_currency,
510            quote_currency,
511            settlement_currency,
512            is_inverse,
513            price_precision as u8,
514            size_precision as u8,
515            price_increment,
516            size_increment,
517            Some(multiplier),
518            Some(lot_size),
519            max_quantity,
520            min_quantity,
521            max_notional,
522            min_notional,
523            max_price,
524            min_price,
525            margin_init,
526            margin_maint,
527            maker_fee,
528            taker_fee,
529            ts_event,
530            ts_init,
531        );
532        Ok(CryptoPerpetualModel(inst))
533    }
534}
535
536impl<'r> FromRow<'r, PgRow> for CurrencyPairModel {
537    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
538        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
539        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
540        let base_currency = row
541            .try_get::<String, _>("base_currency")
542            .map(Currency::from)?;
543        let quote_currency = row
544            .try_get::<String, _>("quote_currency")
545            .map(Currency::from)?;
546        let price_precision = row.try_get::<i32, _>("price_precision")?;
547        let size_precision = row.try_get::<i32, _>("size_precision")?;
548        let price_increment = row
549            .try_get::<String, _>("price_increment")
550            .map(|res| Price::from(res.as_str()))?;
551        let size_increment = row
552            .try_get::<String, _>("size_increment")
553            .map(|res| Quantity::from(res.as_str()))?;
554        let lot_size = row
555            .try_get::<Option<String>, _>("lot_size")
556            .ok()
557            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
558        let max_quantity = row
559            .try_get::<Option<String>, _>("max_quantity")
560            .ok()
561            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
562        let min_quantity = row
563            .try_get::<Option<String>, _>("min_quantity")
564            .ok()
565            .and_then(|res| res.map(|res| Quantity::from(res.as_str())));
566        let max_notional = row
567            .try_get::<Option<String>, _>("max_notional")
568            .ok()
569            .and_then(|res| res.map(|res| Money::from(res.as_str())));
570        let min_notional = row
571            .try_get::<Option<String>, _>("min_notional")
572            .ok()
573            .and_then(|res| res.map(|res| Money::from(res.as_str())));
574        let max_price = row
575            .try_get::<Option<String>, _>("max_price")
576            .ok()
577            .and_then(|res| res.map(|res| Price::from(res.as_str())));
578        let min_price = row
579            .try_get::<Option<String>, _>("min_price")
580            .ok()
581            .and_then(|res| res.map(|res| Price::from(res.as_str())));
582        let margin_init = row
583            .try_get::<String, _>("margin_init")
584            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
585        let margin_maint = row
586            .try_get::<String, _>("margin_maint")
587            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
588        let maker_fee = row
589            .try_get::<String, _>("maker_fee")
590            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
591        let taker_fee = row
592            .try_get::<String, _>("taker_fee")
593            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
594        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
595        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
596
597        let inst = CurrencyPair::new(
598            id,
599            raw_symbol,
600            base_currency,
601            quote_currency,
602            price_precision as u8,
603            size_precision as u8,
604            price_increment,
605            size_increment,
606            lot_size,
607            max_quantity,
608            min_quantity,
609            max_notional,
610            min_notional,
611            max_price,
612            min_price,
613            margin_init,
614            margin_maint,
615            maker_fee,
616            taker_fee,
617            ts_event,
618            ts_init,
619        );
620        Ok(CurrencyPairModel(inst))
621    }
622}
623
624impl<'r> FromRow<'r, PgRow> for EquityModel {
625    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
626        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
627        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::from)?;
628        let isin = row
629            .try_get::<Option<String>, _>("isin")
630            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
631        let currency = row
632            .try_get::<String, _>("quote_currency")
633            .map(Currency::from)?;
634        let price_precision = row.try_get::<i32, _>("price_precision")?;
635        let price_increment = row
636            .try_get::<String, _>("price_increment")
637            .map(|res| Price::from_str(res.as_str()).unwrap())?;
638        let lot_size = row
639            .try_get::<Option<String>, _>("lot_size")
640            .map(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()))?;
641        let max_quantity = row
642            .try_get::<Option<String>, _>("max_quantity")
643            .ok()
644            .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
645        let min_quantity = row
646            .try_get::<Option<String>, _>("min_quantity")
647            .ok()
648            .and_then(|res| res.map(|s| Quantity::from_str(s.as_str()).unwrap()));
649        let max_price = row
650            .try_get::<Option<String>, _>("max_price")
651            .ok()
652            .and_then(|res| res.map(|s| Price::from(s.as_str())));
653        let min_price = row
654            .try_get::<Option<String>, _>("min_price")
655            .ok()
656            .and_then(|res| res.map(|s| Price::from(s.as_str())));
657        let margin_init = row
658            .try_get::<String, _>("margin_init")
659            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
660        let margin_maint = row
661            .try_get::<String, _>("margin_maint")
662            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
663        let maker_fee = row
664            .try_get::<String, _>("maker_fee")
665            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
666        let taker_fee = row
667            .try_get::<String, _>("taker_fee")
668            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
669        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
670        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
671
672        let inst = Equity::new(
673            id,
674            raw_symbol,
675            isin,
676            currency,
677            price_precision as u8,
678            price_increment,
679            lot_size,
680            max_quantity,
681            min_quantity,
682            max_price,
683            min_price,
684            margin_init,
685            margin_maint,
686            maker_fee,
687            taker_fee,
688            ts_event,
689            ts_init,
690        );
691        Ok(EquityModel(inst))
692    }
693}
694
695impl<'r> FromRow<'r, PgRow> for FuturesContractModel {
696    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
697        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
698        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
699        let asset_class = row
700            .try_get::<AssetClassModel, _>("asset_class")
701            .map(|res| res.0)?;
702        let exchange = row
703            .try_get::<Option<String>, _>("exchange")
704            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
705        let underlying = row
706            .try_get::<String, _>("underlying")
707            .map(|res| Ustr::from(res.as_str()))?;
708        let currency = row
709            .try_get::<String, _>("quote_currency")
710            .map(Currency::from)?;
711        let activation_ns = row
712            .try_get::<String, _>("activation_ns")
713            .map(UnixNanos::from)?;
714        let expiration_ns = row
715            .try_get::<String, _>("expiration_ns")
716            .map(UnixNanos::from)?;
717        let price_precision = row.try_get::<i32, _>("price_precision")?;
718        let price_increment = row
719            .try_get::<String, _>("price_increment")
720            .map(|res| Price::from(res.as_str()))?;
721        let multiplier = row
722            .try_get::<String, _>("multiplier")
723            .map(|res| Quantity::from(res.as_str()))?;
724        let lot_size = row
725            .try_get::<String, _>("lot_size")
726            .map(|res| Quantity::from(res.as_str()))?;
727        let max_quantity = row
728            .try_get::<Option<String>, _>("max_quantity")
729            .ok()
730            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
731        let min_quantity = row
732            .try_get::<Option<String>, _>("min_quantity")
733            .ok()
734            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
735        let max_price = row
736            .try_get::<Option<String>, _>("max_price")
737            .ok()
738            .and_then(|res| res.map(|s| Price::from(s.as_str())));
739        let min_price = row
740            .try_get::<Option<String>, _>("min_price")
741            .ok()
742            .and_then(|res| res.map(|s| Price::from(s.as_str())));
743        let margin_init = row
744            .try_get::<String, _>("margin_init")
745            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
746        let margin_maint = row
747            .try_get::<String, _>("margin_maint")
748            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
749        let maker_fee = row
750            .try_get::<String, _>("maker_fee")
751            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
752        let taker_fee = row
753            .try_get::<String, _>("taker_fee")
754            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
755        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
756        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
757
758        let inst = FuturesContract::new(
759            id,
760            raw_symbol,
761            asset_class,
762            exchange,
763            underlying,
764            activation_ns,
765            expiration_ns,
766            currency,
767            price_precision as u8,
768            price_increment,
769            multiplier,
770            lot_size,
771            max_quantity,
772            min_quantity,
773            max_price,
774            min_price,
775            margin_init,
776            margin_maint,
777            maker_fee,
778            taker_fee,
779            ts_event,
780            ts_init,
781        );
782        Ok(FuturesContractModel(inst))
783    }
784}
785
786impl<'r> FromRow<'r, PgRow> for FuturesSpreadModel {
787    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
788        todo!("Implement FromRow for FuturesSpread")
789    }
790}
791
792impl<'r> FromRow<'r, PgRow> for OptionContractModel {
793    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
794        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
795        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
796        let asset_class = row
797            .try_get::<AssetClassModel, _>("asset_class")
798            .map(|res| res.0)?;
799        let exchange = row
800            .try_get::<Option<String>, _>("exchange")
801            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
802        let underlying = row
803            .try_get::<String, _>("underlying")
804            .map(|res| Ustr::from(res.as_str()))?;
805        let option_kind = row
806            .try_get::<String, _>("option_kind")
807            .map(|res| OptionKind::from_str(res.as_str()).unwrap())?;
808        let activation_ns = row
809            .try_get::<String, _>("activation_ns")
810            .map(UnixNanos::from)?;
811        let expiration_ns = row
812            .try_get::<String, _>("expiration_ns")
813            .map(UnixNanos::from)?;
814        let strike_price = row
815            .try_get::<String, _>("strike_price")
816            .map(|res| Price::from_str(res.as_str()).unwrap())?;
817        let currency = row
818            .try_get::<String, _>("quote_currency")
819            .map(Currency::from)?;
820        let price_precision = row.try_get::<i32, _>("price_precision").unwrap();
821        let price_increment = row
822            .try_get::<String, _>("price_increment")
823            .map(|res| Price::from_str(res.as_str()).unwrap())?;
824        let multiplier = row
825            .try_get::<String, _>("multiplier")
826            .map(|res| Quantity::from(res.as_str()))?;
827        let lot_size = row
828            .try_get::<String, _>("lot_size")
829            .map(|res| Quantity::from(res.as_str()))
830            .unwrap();
831        let max_quantity = row
832            .try_get::<Option<String>, _>("max_quantity")
833            .ok()
834            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
835        let min_quantity = row
836            .try_get::<Option<String>, _>("min_quantity")
837            .ok()
838            .and_then(|res| res.map(|s| Quantity::from(s.as_str())));
839        let max_price = row
840            .try_get::<Option<String>, _>("max_price")
841            .ok()
842            .and_then(|res| res.map(|s| Price::from(s.as_str())));
843        let min_price = row
844            .try_get::<Option<String>, _>("min_price")
845            .ok()
846            .and_then(|res| res.map(|s| Price::from(s.as_str())));
847        let margin_init = row
848            .try_get::<String, _>("margin_init")
849            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
850        let margin_maint = row
851            .try_get::<String, _>("margin_maint")
852            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
853        let maker_fee = row
854            .try_get::<String, _>("maker_fee")
855            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
856        let taker_fee = row
857            .try_get::<String, _>("taker_fee")
858            .map(|res| Some(Decimal::from_str(res.as_str()).unwrap()))?;
859        let ts_event = row.try_get::<String, _>("ts_event").map(UnixNanos::from)?;
860        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
861
862        let inst = OptionContract::new(
863            id,
864            raw_symbol,
865            asset_class,
866            exchange,
867            underlying,
868            option_kind,
869            strike_price,
870            currency,
871            activation_ns,
872            expiration_ns,
873            price_precision as u8,
874            price_increment,
875            multiplier,
876            lot_size,
877            max_quantity,
878            min_quantity,
879            max_price,
880            min_price,
881            margin_init,
882            margin_maint,
883            maker_fee,
884            taker_fee,
885            ts_event,
886            ts_init,
887        );
888        Ok(OptionContractModel(inst))
889    }
890}
891
892impl<'r> FromRow<'r, PgRow> for OptionSpreadModel {
893    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
894        todo!("Implement FromRow for OptionSpread")
895    }
896}