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